main v1.1
Benjamin Kraft 2 years ago
commit 93c8cd4fd8
  1. 2
      .gitignore
  2. 6
      project.json
  3. BIN
      public/data/images/ball.png
  4. BIN
      public/data/images/box.png
  5. BIN
      public/data/images/favicon.ico
  6. 138
      public/data/scripts/js/background.js
  7. 1
      public/data/scripts/js/background.js.map
  8. 30
      public/data/scripts/js/ball.js
  9. 1
      public/data/scripts/js/ball.js.map
  10. 146
      public/data/scripts/js/chain.js
  11. 1
      public/data/scripts/js/chain.js.map
  12. 67
      public/data/scripts/js/container.js
  13. 1
      public/data/scripts/js/container.js.map
  14. 167
      public/data/scripts/js/decoration.js
  15. 1
      public/data/scripts/js/decoration.js.map
  16. 27
      public/data/scripts/js/events.js
  17. 1
      public/data/scripts/js/events.js.map
  18. 219
      public/data/scripts/js/game.js
  19. 1
      public/data/scripts/js/game.js.map
  20. 143
      public/data/scripts/js/init.js
  21. 1
      public/data/scripts/js/init.js.map
  22. 40
      public/data/scripts/js/loader.js
  23. 1
      public/data/scripts/js/loader.js.map
  24. 9
      public/data/scripts/js/online.js
  25. 1
      public/data/scripts/js/online.js.map
  26. 72
      public/data/scripts/js/star.js
  27. 1
      public/data/scripts/js/star.js.map
  28. 223
      public/data/scripts/js/tree.js
  29. 1
      public/data/scripts/js/tree.js.map
  30. 189
      public/data/scripts/ts/background.ts
  31. 39
      public/data/scripts/ts/ball.ts
  32. 167
      public/data/scripts/ts/chain.ts
  33. 84
      public/data/scripts/ts/container.ts
  34. 194
      public/data/scripts/ts/decoration.ts
  35. 12050
      public/data/scripts/ts/definitions/p5.d/p5.d.ts
  36. 6423
      public/data/scripts/ts/definitions/p5.d/p5.global-mode.d.ts
  37. 30
      public/data/scripts/ts/events.ts
  38. 261
      public/data/scripts/ts/game.ts
  39. 168
      public/data/scripts/ts/init.ts
  40. 54
      public/data/scripts/ts/loader.ts
  41. 10
      public/data/scripts/ts/online.ts
  42. 89
      public/data/scripts/ts/star.ts
  43. 256
      public/data/scripts/ts/tree.ts
  44. 7
      public/data/settings/libraries.json
  45. 202
      public/data/settings/settings.json
  46. 88
      public/data/styles/color_picker.css
  47. BIN
      public/data/styles/fonts/Elronet/Elronmonospace.ttf
  48. 93
      public/data/styles/fonts/Tajawal/OFL.txt
  49. BIN
      public/data/styles/fonts/Tajawal/Tajawal-Black.ttf
  50. BIN
      public/data/styles/fonts/Tajawal/Tajawal-Bold.ttf
  51. BIN
      public/data/styles/fonts/Tajawal/Tajawal-ExtraBold.ttf
  52. BIN
      public/data/styles/fonts/Tajawal/Tajawal-ExtraLight.ttf
  53. BIN
      public/data/styles/fonts/Tajawal/Tajawal-Light.ttf
  54. BIN
      public/data/styles/fonts/Tajawal/Tajawal-Medium.ttf
  55. BIN
      public/data/styles/fonts/Tajawal/Tajawal-Regular.ttf
  56. 89
      public/data/styles/range_input.css
  57. 67
      public/index.html
  58. 86
      public/styles.css
  59. BIN
      public/thumbnail.png
  60. 9
      tsconfig.json

2
.gitignore vendored

@ -0,0 +1,2 @@
.idea

@ -0,0 +1,6 @@
{
"display_name": "Christmas Tree",
"info_text": "With this little nice tool you can have fun decorating your own Christmas Trees.",
"visible": true,
"tags": ["Tool"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

@ -0,0 +1,138 @@
"use strict";
let snowflake_max_r = 12;
let snowflake_min_r = 7;
let snowflake_max_count = 500;
class Background {
constructor() {
this.bgColor = p.color(100, 100, 255);
this.wind_direction = 0;
this.ground = new Ground();
this.snowflakes = [];
for (let i = 0; i < snowflake_max_count; i++) {
this.snowflakes.push(this.getNewSnowflake());
}
this.snowflakes.sort((a, b) => {
if (a.radius < b.radius) {
return -1;
}
if (a.radius > b.radius) {
return 1;
}
return 0;
});
this.backgroundSnowflakes = this.snowflakes.slice(0, snowflake_max_count * 2 / 3);
this.foregroundSnowflakes = this.snowflakes.slice(snowflake_max_count * 2 / 3, snowflake_max_count);
this.updateSettings();
}
getNewSnowflake() {
return new Snowflake(p.random(p.width), p.random(-snowflake_max_r, p.height), p.random(snowflake_min_r, snowflake_max_r));
}
updateSettings() {
let allowed_snowflakes_count = $('#snow_intensity').val();
let visible_snowflakes = p.shuffle(this.snowflakes.filter(s => !s.hidden));
let hidden_snowflakes = p.shuffle(this.snowflakes.filter(s => s.hidden));
let snowflakes_to_add = allowed_snowflakes_count - visible_snowflakes.length;
if (snowflakes_to_add > 0) {
for (let i = 0; i < snowflakes_to_add; i++) {
hidden_snowflakes[i].hidden = false;
}
}
if (snowflakes_to_add < 0) {
for (let i = 0; i < p.abs(snowflakes_to_add); i++) {
visible_snowflakes[i].hidden = true;
}
}
this.bgColor.setBlue(p.map(allowed_snowflakes_count, 0, snowflake_max_count, 255, 170));
}
update() {
this.ground.update();
this.wind_direction = p.sin(p.frameCount / 1000);
this.snowflakes.forEach(s => s.update(this.wind_direction));
}
display(asForeground) {
if (asForeground) {
this.foregroundSnowflakes.forEach(s => s.display());
}
else {
p.background(this.bgColor);
this.ground.display();
this.backgroundSnowflakes.forEach(s => s.display());
}
}
}
class Snowflake {
constructor(x, y, r) {
this.rotation = 0;
this.pos = p.createVector(x, y);
this.radius = r;
this.hidden = false;
this.alpha = 0;
this.rotateDir = p.random([-1, 1]) * p.random(0.01, 0.03);
this.peakCount = p.random([4, 5, 6]);
}
update(wind_direction) {
let dir = p.createVector(p.map(this.radius, snowflake_min_r, snowflake_max_r, 0, 1) * wind_direction, p.map(this.radius, snowflake_min_r, snowflake_max_r, 0.5, 1) * 2);
this.pos.add(dir);
this.rotation += this.rotateDir;
if (this.pos.y > p.height + this.radius) {
this.pos.y = -this.radius;
}
if (this.pos.x > p.width + this.radius) {
this.pos.x = -this.radius;
}
else if (this.pos.x < -this.radius) {
this.pos.x = p.width + this.radius;
}
if (this.hidden && this.alpha > 0) {
this.alpha -= 0.015;
}
else if (this.alpha < 1) {
this.alpha += 0.015;
}
}
display() {
p.push();
p.strokeWeight(1);
p.stroke(255, this.alpha * 255);
p.fill(255, this.alpha * 255);
p.translate(this.pos);
p.rotate(this.rotation % p.TWO_PI);
for (let rad = 0; rad < p.PI; rad += p.PI / this.peakCount) {
let x1 = p.cos(rad) * this.radius;
let y1 = p.sin(rad) * this.radius;
let x2 = p.cos(rad + p.PI) * this.radius;
let y2 = p.sin(rad + p.PI) * this.radius;
p.line(x1, y1, x2, y2);
}
p.pop();
}
}
class Ground {
constructor() {
this.createPoints();
}
createPoints() {
this.points = [];
for (let x = 0; x < p.width; x++) {
let vector = p.createVector(x, p.noise(x / 300) * 150);
this.points.push(vector);
}
}
update() {
}
display() {
p.push();
p.fill(220, 220, 255);
p.stroke(50, 50, 150);
p.strokeWeight(2);
p.beginShape();
for (let point of this.points) {
p.vertex(point.x, point.y + p.height / 2);
}
p.vertex(p.width, p.height);
p.vertex(0, p.height);
p.endShape(p.CLOSE);
p.pop();
}
}
//# sourceMappingURL=background.js.map

File diff suppressed because one or more lines are too long

@ -0,0 +1,30 @@
"use strict";
class Ball extends Decoration {
constructor(properties) {
super(properties);
this.createGraphics();
}
createGraphics() {
let img = images['ball'];
this.graphics = p.createGraphics(img.width, img.height);
this.updateColor();
}
updateColor() {
let img = images['ball'];
this.graphics.clear();
this.graphics.tint(this.colors[0]);
this.graphics.image(img, 0, 0);
}
display(pos, dim) {
super.display(pos, dim);
p.image(this.graphics, 0, 0, this.radius * 2, this.radius * 2);
if (this.mouseIsOver) {
this.brightness(70);
}
p.pop();
}
update() {
super.update();
}
}
//# sourceMappingURL=ball.js.map

@ -0,0 +1 @@
{"version":3,"file":"ball.js","sourceRoot":"","sources":["../ts/ball.ts"],"names":[],"mappings":";AAAA,MAAM,IAAK,SAAQ,UAAU;IAIzB,YAAY,UAAkB;QAC1B,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,CAAC,cAAc,EAAE,CAAC;IAC1B,CAAC;IAED,cAAc;QACV,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;IAED,WAAW;QACP,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;IAGD,OAAO,CAAC,GAAe,EAAE,GAAe;QACpC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAExB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,IAAI,CAAC,WAAW,EAAC;YACjB,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;SACvB;QAED,CAAC,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;IAED,MAAM;QACF,KAAK,CAAC,MAAM,EAAE,CAAC;IACnB,CAAC;CAEJ"}

@ -0,0 +1,146 @@
"use strict";
var has = Reflect.has;
class Chain extends Decoration {
constructor(properties) {
super(properties);
this.p1 = p.createVector(-0.65, 0);
this.p2 = p.createVector(0.65, 0);
this.p1IsBoundToMouse = false;
this.p2IsBoundToMouse = false;
this.placedOnce = false;
this.points = [];
this.lights = [];
this.generate();
}
get properties() {
let obj = super.properties;
obj['p1'] = this.p1;
obj['p2'] = this.p2;
return obj;
}
generate() {
let d = p.dist(this.p1.x, this.p1.y, this.p2.x, this.p2.y);
let m = p.abs((this.p2.y - this.p1.y) / (this.p2.x - this.p1.x));
let a = -1 / d - m / 10;
let b = (this.p1.y - this.p2.y + a * p.pow(this.p2.x, 2) - a * p.pow(this.p1.x, 2)) /
(this.p1.x - this.p2.x);
if (this.p1.x - this.p2.x === 0)
b = 0;
let c = this.p1.y - a * p.pow(this.p1.x, 2) - b * this.p1.x;
let func = x => a * x * x + b * x + c;
let detail = 40;
let points = [];
for (let i = 0; i <= detail; i++) {
let point = { x: 0, y: 0 };
point.x = this.p1.x + (this.p2.x - this.p1.x) / detail * i;
point.y = func(point.x);
points.push(point);
}
let lightDist = 0.35;
let lastLight = { x: Infinity, y: Infinity };
let lights = [];
detail = 300;
for (let i = 0; i <= detail; i++) {
let point = { x: 0, y: 0, hasLight: false };
point.x = this.p1.x + (this.p2.x - this.p1.x) / detail * i;
point.y = func(point.x);
if (p.dist(point.x, point.y, lastLight.x, lastLight.y) >= lightDist) {
lights.push(point);
lastLight = point;
}
}
this.points = points;
this.lights = lights;
}
display(pos, dim) {
super.display(pos, dim);
p.strokeWeight(0.05);
p.stroke(0);
p.noFill();
p.beginShape();
for (let point of this.points)
p.vertex(point.x, point.y);
p.endShape();
p.strokeWeight(0.01);
let i = 0;
for (let point of this.lights) {
let colorIndex = (i + p.int(p.frameCount / 30)) % this.colors.length;
p.fill(this.colors[colorIndex]);
p.ellipse(point.x, point.y, 0.2, 0.2);
i++;
}
p.pop();
}
get hasValidPosition() {
return game.tree.containsPosition(this.realPosByAnchor(this.p1), true)
&& game.tree.containsPosition(this.realPosByAnchor(this.p2), true);
}
realPosByAnchor(anchor) {
return p5.Vector.add(p5.Vector.mult(p5.Vector.add(this.pos, anchor), game.tree.sizeMultiplier), game.center);
}
realPointDistToAnchor(point, anchor) {
let pos = this.realPosByAnchor(anchor);
return p.dist(pos.x, pos.y, point.x, point.y);
}
isMouseOverAnchor(anchor) {
return this.realPointDistToAnchor(p.createVector(p.mouseX, p.mouseY), anchor) <= 0.5 * game.tree.sizeMultiplier;
}
get mouseIsOver() {
return this.isTaken && (this.isMouseOverAnchor(this.p1) || this.isMouseOverAnchor(this.p2));
}
//Mouse is over this
mousePressed() {
super.mousePressed();
if (this.isMouseOverAnchor(this.p1)) {
this.p1IsBoundToMouse = true;
}
else if (this.isMouseOverAnchor(this.p2)) {
this.p2IsBoundToMouse = true;
}
}
updateFromPoint(point, other, x, y) {
if (this.placedOnce) {
let pointRelative = p.createVector(x - this.pos.x, y - this.pos.y);
point.set(pointRelative);
let pointReal = p5.Vector.add(this.pos, point);
let otherReal = p5.Vector.add(this.pos, other);
let newPos = p5.Vector.div(p5.Vector.add(pointReal, otherReal), 2);
point.set(p5.Vector.sub(pointReal, newPos));
other.set(p5.Vector.mult(point, -1));
this.pos.set(newPos);
}
else {
this.pos.set(x - point.x, y - point.y);
}
}
update() {
let x = (p.mouseX - game.center.x) / game.tree.sizeMultiplier, y = (p.mouseY - game.center.y) / game.tree.sizeMultiplier;
if (this.isBoundToMouse) {
if (this.p1IsBoundToMouse) {
this.updateFromPoint(this.p1, this.p2, x, y);
}
if (this.p2IsBoundToMouse) {
this.updateFromPoint(this.p2, this.p1, x, y);
}
this.generate();
}
else {
super.update();
}
if (this.isTaken && !this.isBoundToMouse) {
this.placedOnce = true;
}
}
toJSON(index) {
let list = [
"p1",
"p2"
];
let obj = super.toJSON(index);
for (let item of list) {
obj[item] = this[item];
}
return obj;
}
}
//# sourceMappingURL=chain.js.map

File diff suppressed because one or more lines are too long

@ -0,0 +1,67 @@
"use strict";
class Container {
constructor(index, content) {
this.pos = p.createVector();
this.dim = p.createVector();
this.index = index;
this.content = content;
}
get mouseIsOver() {
return p.mouseX > this.pos.x && p.mouseX < this.pos.x + this.dim.x &&
p.mouseY > this.pos.y && p.mouseY < this.pos.y + this.dim.y;
}
get center() {
return p5.Vector.add(this.pos, p5.Vector.div(this.dim, 2));
}
display() {
let w = this.dim.x, h = this.dim.y;
let x = this.pos.x + w / 2, y = this.pos.y + h / 2;
p.image(images['box'], x, y, w, h);
if (this.content !== game.tree) {
this.content.display();
}
if (this.mouseIsOver) {
p.fill(0, 50);
p.noStroke();
p.rect(x - w / 2, y - h / 2, w, h);
}
}
update() {
let currentCount = game.currentContainers.length;
let w = p.width / currentCount, h = w;
let x = w * this.index, y = p.height - h;
if (this.content instanceof Decoration) {
let otherCount = game.containers.filter(c => game.currentContainers.find(ic => ic == c) == null).length;
w = h = p.width / otherCount;
x = w * this.index;
y = p.height - h;
x += p.width / 2 - w * currentCount / 2;
}
this.pos.set(x, y);
this.dim.set(w, h);
if (this.content !== game.tree)
this.content.update();
}
mousePressed() {
if (this.content instanceof Tree) {
game.tree.animationDirection = -1;
if (this.content === game.tree) {
game.tree = new Tree(null);
}
else {
game.tree = this.content;
game.tree.animationDirection = 1;
}
}
if (this.content instanceof Decoration && !game.tree.isPlaceholder) {
let decoration = Decoration.Create(this.content.properties);
decoration.isTaken = true;
decoration.isBoundToMouse = true;
game.tree.decorations.push(decoration);
if (decoration instanceof Chain) {
decoration.p1IsBoundToMouse = true;
}
}
}
}
//# sourceMappingURL=container.js.map

@ -0,0 +1 @@
{"version":3,"file":"container.js","sourceRoot":"","sources":["../ts/container.ts"],"names":[],"mappings":"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"}

@ -0,0 +1,167 @@
"use strict";
class Decoration {
constructor(properties) {
this.pos = p.createVector();
this.isTaken = false;
this.isBoundToMouse = false;
this.isToDelete = false;
this.needsValidation = false;
this.isSelected = false;
for (let key in properties) {
if (properties.hasOwnProperty(key)) {
if (Array.isArray(properties[key])) {
this[key] = arrayCopy(properties[key]);
}
else {
this[key] = properties[key];
}
}
}
}
get properties() {
let colors = [];
for (let i = 0; i < this.colors.length; i++) {
let color = [];
for (let c of this.colors[i]) {
color.push(c);
}
colors.push(color);
}
return {
radius: this.radius,
colors: colors,
type: this.type
};
}
get mouseIsOver() {
if (this.isTaken) {
let pos = this.realPos;
return p.dist(pos.x, pos.y, p.mouseX, p.mouseY) <= this.radius * game.tree.sizeMultiplier;
}
return false;
}
get hasValidPosition() {
return game.tree.containsPosition(this.realPos, true);
}
get realPos() {
return p5.Vector.add(p5.Vector.mult(this.pos, game.tree.sizeMultiplier), game.center);
}
static Create(properties) {
switch (properties['type']) {
case 'ball':
return new Ball(properties);
case 'star':
return new Star(properties);
case 'chain':
return new Chain(properties);
}
}
display(pos, dim) {
let sizeMultiplier = this.sizeMultiplier;
if (pos || dim) {
sizeMultiplier = dim.y * 0.6;
}
else {
pos = this.pos;
}
p.push();
p.translate(pos);
p.scale(sizeMultiplier);
}
brightness(value) {
p.noStroke();
p.fill(0, 0, 0, value);
p.ellipse(0, 0, this.radius * 2, this.radius * 2);
}
update() {
if (!this.isTaken) {
this.pos = this.container.center;
this.sizeMultiplier = this.container.dim.y * 0.6;
}
else {
this.sizeMultiplier = 1;
}
if (this.isBoundToMouse) {
let x = (p.mouseX - game.center.x) / game.tree.sizeMultiplier, y = (p.mouseY - game.center.y) / game.tree.sizeMultiplier;
this.pos.set(x, y);
}
}
//Assumes mouse is over this
mousePressed() {
this.isBoundToMouse = true;
}
//Always called
mouseReleased() {
}
toJSON(index) {
let list = [
"pos",
"colors",
"radius",
"type"
];
let obj = {};
for (let item of list) {
obj[item] = this[item];
}
return obj;
}
static restoreFrom(rawDeco) {
let props = {};
for (let key in rawDeco) {
if (['pos', 'p1', 'p2'].find(s => s === key) === undefined) {
props[key] = rawDeco[key];
}
}
let deco = Decoration.Create(props);
deco.isTaken = true;
deco.pos.x = rawDeco['pos'].x;
deco.pos.y = rawDeco['pos'].y;
if (deco instanceof Chain) {
deco.p1.set(rawDeco['p1'].x, rawDeco['p1'].y);
deco.p2.set(rawDeco['p2'].x, rawDeco['p2'].y);
deco.generate();
}
return deco;
}
}
function darker(c) {
let newC = [];
for (let v of c) {
newC.push(p.constrain(v * 0.6, 0, 255));
}
return newC;
}
function arrayCopy(a) {
let newArr = [];
for (let c of a) {
let copy = c;
if (Array.isArray(c)) {
copy = arrayCopy(c);
}
newArr.push(copy);
}
return newArr;
}
function equals(a1, a2) {
// if the other array is a falsy value, return
if (!a1)
return false;
// compare lengths - can save a lot of time
if (a2.length != a1.length)
return false;
for (let i = 0, l = a2.length; i < l; i++) {
// Check if we have nested arrays
if (a2[i] instanceof Array && a1[i] instanceof Array) {
// recurse into the nested arrays
if (!a2[i].equals(a1[i]))
return false;
}
else if (a2[i] != a1[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
//# sourceMappingURL=decoration.js.map

File diff suppressed because one or more lines are too long

@ -0,0 +1,27 @@
'use strict';
p.keyPressed = () => {
switch (p.keyCode) {
//Ctrl + D
case 68:
if (p.keyIsDown(17)) {
debug = !debug;
let msg = 'Debug mode turned ' + (debug ? 'ON' : 'OFF');
console.info(msg);
return false;
}
break;
}
};
p.mousePressed = () => {
game.mousePressed();
released = false;
};
p.mouseReleased = () => {
game.mouseReleased();
released = true;
};
p.windowResized = () => {
let w = $('#canvas_holder').width(), h = $('#canvas_holder').height();
p.resizeCanvas(w, h);
};
//# sourceMappingURL=events.js.map

@ -0,0 +1 @@
{"version":3,"file":"events.js","sourceRoot":"","sources":["../ts/events.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,CAAC,CAAC,UAAU,GAAG,GAAG,EAAE;IAChB,QAAQ,CAAC,CAAC,OAAO,EAAC;QACd,UAAU;QACV,KAAK,EAAE;YACH,IAAI,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,EAAC;gBAChB,KAAK,GAAG,CAAC,KAAK,CAAC;gBACf,IAAI,GAAG,GAAG,oBAAoB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBACxD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAClB,OAAO,KAAK,CAAC;aAChB;YACD,MAAM;KACb;AACL,CAAC,CAAA;AAED,CAAC,CAAC,YAAY,GAAG,GAAG,EAAE;IAClB,IAAI,CAAC,YAAY,EAAE,CAAC;IACpB,QAAQ,GAAG,KAAK,CAAC;AACrB,CAAC,CAAA;AACD,CAAC,CAAC,aAAa,GAAG,GAAG,EAAE;IACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IACrB,QAAQ,GAAG,IAAI,CAAC;AACpB,CAAC,CAAA;AAED,CAAC,CAAC,aAAa,GAAG,GAAG,EAAE;IACnB,IAAI,CAAC,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC,KAAK,EAAE,EACnC,CAAC,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACzB,CAAC,CAAA"}

@ -0,0 +1,219 @@
"use strict";
class Game {
constructor() {
this.trees = [];
this.tree = new Tree(null);
this.decorations = [];
this.containers = [];
this.isTreeMode = true;
this.isDecorationMode = false;
this.switchButton = new Button('switch');
this.downloadButton = new Button('download');
this.restoreButton = new Button('restore');
this.background = new Background();
settings.game.trees.forEach((s) => this.trees.push(new Tree(s)));
let red = settings.game.colors.red;
this.decorations = [
Decoration.Create({
type: "ball", radius: 0.45, colors: [red]
}),
Decoration.Create({
type: "star", radius: 0.45, colors: [red, darker(red)]
}),
Decoration.Create({
type: "chain", radius: 0.45, colors: [
settings.game.colors.gold,
settings.game.colors.silver
]
}),
];
$('#gold, #silver').attr('checked', 'checked');
this.trees.forEach((t, i) => {
let c = new Container(i, t);
this.containers.push(c);
t.container = c;
});
this.decorations.forEach((d, i) => {
let c = new Container(i, d);
this.containers.push(c);
d.container = c;
});
this.updateDecorationSettings();
this.updateStarSettings();
}
updateDecorationSettings() {
this.decorations.filter(d => d instanceof Ball || d instanceof Star).forEach(d => {
d.radius = $('#radius').val() / 100 * 0.7;
let colors = [];
colors.push(JSON.parse($('#color').val()));
if (d instanceof Ball) {
d.colors = colors;
d.updateColor();
}
if (d instanceof Star) {
d.innerRadius = $('#inner_radius').val() * d.radius;
colors.push(darker(colors[0]));
d.colors = colors;
d.createTriangles();
}
});
}
updateStarSettings() {
this.decorations.filter(d => d instanceof Star).forEach((s) => {
s.innerRadius = $('#inner_radius').val() * s.radius;
s.raysCount = parseInt($('#rays_count').val());
s.createTriangles();
});
}
updateChainSettings(element) {
let chain = this.decorations.find(d => d instanceof Chain);
let colorName = element.attr('id');
let colorValue = settings.game.colors[colorName];
let cvIndex = chain.colors.findIndex(c => equals(c, colorValue));
if (cvIndex >= 0) {
chain.colors.splice(cvIndex, 1);
}
else {
chain.colors.push(colorValue);
}
if (chain.colors.length == 0) {
chain.colors.push(colorValue);
return false;
}
return true;
}
updateBackgroundSettings() {
this.background.updateSettings();
}
get center() {
return p.createVector(p.width / 2, this.height / 2);
}
get height() {
return p.height - this.containers[0].dim.y;
}
get currentContainers() {
return this.containers.filter(c => {
if (this.isTreeMode)
return c.content instanceof Tree;
if (this.isDecorationMode)
return c.content instanceof Decoration;
});
}
update() {
this.background.update();
this.switchButton.update();
this.downloadButton.update();
this.restoreButton.update();
this.currentContainers.forEach(c => c.update());
this.tree.update();
}
display() {
this.background.display();
this.switchButton.display();
this.downloadButton.display();
this.restoreButton.display();
this.currentContainers.forEach(c => c.display());
this.tree.display();
this.background.display(true);
}
//Always called
mousePressed() {
this.tree.mousePressed();
this.currentContainers.forEach(c => c.mouseIsOver ? c.mousePressed() : null);
this.switchButton.mouseIsOver ? this.switchButton.mousePressed() : null;
this.downloadButton.mouseIsOver ? this.downloadButton.mousePressed() : null;
this.restoreButton.mouseIsOver ? this.restoreButton.mousePressed() : null;
}
//Always called
mouseReleased() {
this.tree.mouseReleased();
}
restoreTrees(rawTrees) {
this.trees.forEach((t, i) => {
t.restoreFrom(rawTrees[i]);
});
}
}
class Button {
constructor(type) {
this.pos = p.createVector();
this.dim = p.createVector();
this.type = type;
}
get mouseIsOver() {
return p.mouseX > this.pos.x && p.mouseX < this.pos.x + this.dim.x &&
p.mouseY > this.pos.y && p.mouseY < this.pos.y + this.dim.y;
}
get center() {
return p5.Vector.add(this.pos, p5.Vector.div(this.dim, 2));
}
display() {
let w = this.dim.x, h = this.dim.y;
let x = this.pos.x + w / 2, y = this.pos.y + h / 2;
p.image(images['box'], x, y, w, h);
if (this.type === 'switch') {
if (game.isTreeMode) {
game.decorations[0].display(this.center, this.dim);
}
if (game.isDecorationMode) {
let tree = game.tree;
if (tree.isPlaceholder)
tree = game.trees[0];
tree.display(this.center, this.dim);
}
}
if (this.type === 'download') {
p.textAlign(p.CENTER, p.CENTER);
p.textSize(25);
p.fill(0);
p.text("Save", this.pos.x, this.pos.y, this.dim.x, this.dim.y);
}
if (this.type === 'restore') {
p.textAlign(p.CENTER, p.CENTER);
p.textSize(20);
p.fill(0);
p.text("Restore", this.pos.x, this.pos.y, this.dim.x, this.dim.y);
}
if (this.mouseIsOver) {
p.fill(0, 50);
p.noStroke();
p.rect(x - w / 2, y - h / 2, w, h);
}
}
update() {
let w, h, x, y;
if (this.type === 'switch') {
w = game.containers[0].dim.x / 2;
h = w;
x = 0;
y = game.containers[0].pos.y - h;
}
if (this.type === 'download') {
w = game.containers[0].dim.x / 2;
h = w;
x = p.width - w;
y = game.containers[0].pos.y - h;
}
if (this.type === 'restore') {
w = game.containers[0].dim.x / 2;
h = w;
x = p.width - 2 * w;
y = game.containers[0].pos.y - h;
}
this.pos.set(x, y);
this.dim.set(w, h);
}
mousePressed() {
if (this.type === 'switch') {
game.isDecorationMode = !game.isDecorationMode;
game.isTreeMode = !game.isTreeMode;
}
if (this.type === 'download') {
downloadTrees();
}
if (this.type === 'restore') {
$('#file_browser')[0].click();
}
}
}
//# sourceMappingURL=game.js.map

File diff suppressed because one or more lines are too long

@ -0,0 +1,143 @@
'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
let debug = false, font, settings, loader;
//Only for online games
let socket;
let antiCacheQuery = '?_=' + new Date().getTime();
let game;
let released = false;
let images = {};
const p = new p5((p) => {
p.preload = () => {
settings = p.loadJSON('data/settings/settings.json' + antiCacheQuery, {}, 'json', (json) => {
console.log('Local settings loaded: ', json);
}, (error) => {
console.log('Local settings failed: ', error);
});
font = p.loadFont('data/styles/fonts/Tajawal/Tajawal-Regular.ttf' + antiCacheQuery, (font) => {
console.log('Local font loaded: ', font);
}, (error) => {
console.log('Local font failed: ', error);
});
images['ball'] = p.loadImage('data/images/ball.png' + antiCacheQuery, img => {
console.log('Ball image loaded: ', img);
}, error => {
console.log('Ball image failed: ', error);
});
images['box'] = p.loadImage('data/images/box.png' + antiCacheQuery, img => {
console.log('Box image loaded: ', img);
}, error => {
console.log('Box image failed: ', error);
});
},
p.setup = () => {
interfaceSetup();
canvasSetup();
//loader = new Loader(p.createVector(p.width / 2, p.height / 2), Math.min(p.width, p.height) / 2);
loadDynamicScripts().then(() => {
//Load other stuff, then =>
//loader = null;
game = new Game();
game.trees[0].container.mousePressed();
});
},
p.draw = () => {
p.clear();
if (game) {
game.update();
game.display();
}
if (loader) {
loader.update();
loader.display();
}
if (debug)
debugInformation();
};
});
function debugInformation() {
}
function interfaceSetup() {
let files_elem = $('#file_browser');
files_elem.on('change', () => {
let file = files_elem.get(0).files[0];
file.text().then(text => {
game.restoreTrees(JSON.parse(text)[0]);
});
});
let colorSelect = $('#color');
let possibleColors = $('#possible_colors');
for (let colorName in settings.game.colors) {
if (!settings.game.colors.hasOwnProperty(colorName))
continue;
let publicName = colorName.substr(0, 1).toUpperCase() + colorName.substr(1);
let option = $('<option></option>');
option.html(publicName);
option.val("[" + settings.game.colors[colorName] + "]");
colorSelect.append(option);
let checkbox = $('<input/>');
checkbox.attr({
type: 'checkbox',
id: colorName
});
checkbox.on('click', event => {
if (game) {
if (!game.updateChainSettings($(event.target))) {
console.info('failed');
event.preventDefault();
}
}
});
let label = $('<label></label>');
label.html(publicName);
label.attr('for', colorName);
possibleColors.append(checkbox, label);
}
}
function downloadTrees() {
let str = JSON.stringify(game.trees, (key, value) => {
if (value instanceof p5.Vector) {
return {
x: value.x,
y: value.y
};
}
return value;
});
let data = 'data:application/json;charset=utf-8,[' + encodeURIComponent(str) + "]";
$("#save_link").attr("href", data);
$("#save_link")[0].click();
}
function canvasSetup() {
p.frameRate(60);
let w = $('#canvas_holder').width(), h = $('#canvas_holder').height();
let canvas = p.createCanvas(w, h);
canvas.parent('canvas_holder');
p.textFont(font);
p.imageMode(p.CENTER);
}
function loadDynamicScripts() {
return __awaiter(this, void 0, void 0, function* () {
const json = yield p.httpGet('data/settings/libraries.json' + antiCacheQuery, 'json');
let requests = [];
for (let script in json) {
if (json[script]) {
let url = '/lib/benjocraeft/' + script + '.js';
requests.push($.getScript(url, () => {
console.log('Successfully loaded script: ', url);
}));
}
}
yield $.when(...requests);
console.log('All dynamic scripts have been loaded!');
});
}
//# sourceMappingURL=init.js.map

File diff suppressed because one or more lines are too long

@ -0,0 +1,40 @@
"use strict";
class Loader {
constructor(pos, radius) {
this.pos = pos;
this.c = p.createGraphics(radius * 2, radius * 2);
this.radius = radius;
this.center = p.createVector(radius, radius);
this.message = 'Loading...';
this.progress = 0;
this.angle = 0;
}
update() {
this.angle += Math.PI / 10;
}
display() {
let c = this.c;
c.clear();
c.noFill();
c.stroke(20, 100, 200);
c.strokeWeight(this.radius * 0.025);
//c.arc(this.center.x, this.center.y, this.radius * 2, this.radius * 2, this.angle, this.angle + Math.PI * 1.5);
c.strokeWeight(2);
c.rect(this.center.x - this.radius + 5, this.center.y - 25, this.radius * 2 - 50, 50);
c.fill(50, 150, 255);
c.rect(this.center.x - this.radius + 5, this.center.y - 25, (this.radius - 50) * this.progress, 50);
c.textAlign(p.CENTER, p.CENTER);
c.textSize(25);
c.fill(220);
c.strokeWeight(4);
c.text(this.message, this.radius, this.radius * 1.5);
c.textSize(40);
c.text(Math.floor(this.progress * 100) + '%', this.radius, this.radius / 2);
p.imageMode(p.CENTER);
p.image(c, this.pos.x, this.pos.y);
}
destroy() {
this.c.remove();
}
}
//# sourceMappingURL=loader.js.map

@ -0,0 +1 @@
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../ts/loader.ts"],"names":[],"mappings":";AAAA,MAAM,MAAM;IAUR,YAAY,GAAc,EAAE,MAAc;QACtC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,MAAM;QACF,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO;QACH,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACf,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,CAAC,CAAC,MAAM,EAAE,CAAC;QACX,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACvB,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;QACpC,gHAAgH;QAChH,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QACtF,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAEpG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACf,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;QAErD,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACf,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAE5E,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO;QACH,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACpB,CAAC;CAEJ"}

@ -0,0 +1,9 @@
'use strict';
function socketConnect(project, name = "noone") {
let urlQueries = '?game=' + project.name + '&name=' + name;
let port = 3000;
let url = location.hostname + ":" + port + urlQueries;
socket = io.connect(url);
socket.on('connect', () => console.log('Connected to ', url));
}
//# sourceMappingURL=online.js.map

@ -0,0 +1 @@
{"version":3,"file":"online.js","sourceRoot":"","sources":["../ts/online.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,SAAS,aAAa,CAAC,OAAO,EAAE,IAAI,GAAG,OAAO;IAC1C,IAAI,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC3D,IAAI,IAAI,GAAG,IAAI,CAAA;IACf,IAAI,GAAG,GAAG,QAAQ,CAAC,QAAQ,GAAG,GAAG,GAAG,IAAI,GAAG,UAAU,CAAC;IAEtD,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC;AAClE,CAAC"}

@ -0,0 +1,72 @@
"use strict";
class Star extends Decoration {
constructor(properties) {
super(properties);
this.createTriangles();
}
get properties() {
let obj = super.properties;
obj['raysCount'] = this.raysCount;
obj['innerRadius'] = this.innerRadius;
return obj;
}
createTriangles() {
this.triangles = [];
let increment = p.TWO_PI / this.raysCount / 2, firstOuter = true;
for (let i = 0; i < p.TWO_PI; i += increment) {
let x1 = p.sin(i), y1 = -p.cos(i), x2 = p.sin(i + increment), y2 = -p.cos(i + increment);
if (firstOuter) {
x1 *= this.radius,
y1 *= this.radius,
x2 *= this.innerRadius,
y2 *= this.innerRadius;
}
else {
x2 *= this.radius,
y2 *= this.radius,
x1 *= this.innerRadius,
y1 *= this.innerRadius;
}
firstOuter = !firstOuter;
this.triangles.push([
p.createVector(x1, y1),
p.createVector(x2, y2),
p.createVector(0, 0)
]);
}
}
brightness(value) {
this.triangles.forEach((t, i) => {
p.fill(0, 0, 0, value);
p.triangle(t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y);
});
}
display(pos, dim) {
super.display(pos, dim);
p.noStroke();
this.triangles.forEach((t, i) => {
let color = this.colors[i % this.colors.length];
p.fill(color);
p.triangle(t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y);
});
if (this.mouseIsOver) {
this.brightness(70);
}
p.pop();
}
update() {
super.update();
}
toJSON(index) {
let list = [
"raysCount",
"innerRadius"
];
let obj = super.toJSON(index);
for (let item of list) {
obj[item] = this[item];
}
return obj;
}
}
//# sourceMappingURL=star.js.map

@ -0,0 +1 @@
{"version":3,"file":"star.js","sourceRoot":"","sources":["../ts/star.ts"],"names":[],"mappings":"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"}

@ -0,0 +1,223 @@
"use strict";
class Tree {
constructor(properties) {
this.pos = p.createVector();
this.sizeMultiplier = 1;
this.animationProgress = 0;
this.animationDirection = -1;
this.decorations = [];
if (properties == null) {
this.isPlaceholder = true;
}
else {
for (let key in properties) {
this[key] = properties[key];
}
}
}
//Always called
mousePressed() {
for (let d of [...this.decorations].reverse()) {
if (d.mouseIsOver) {
d.mousePressed();
let index = this.decorations.findIndex(deco => deco == d);
this.decorations[index] = this.decorations[this.decorations.length - 1];
this.decorations[this.decorations.length - 1] = d;
break;
}
}
}
//Always called
mouseReleased() {
for (let d of this.decorations) {
d.mouseReleased();
}
}
get smoothProgress() {
return this.smoothStep(0, 1, this.animationProgress);
}
containsPosition(pos, excludeStem) {
let size = game.height, x = (p.width - size) / 2;
let px = pos.x, py = pos.y;
let pixelColor = p.get(px, py).slice(0, 3);
let colorIsValid = game.tree.getAllColors(excludeStem).find(c => equals(c, pixelColor)) != null;
return colorIsValid &&
px > x && px < x + size &&
py > 0 && py < size - 20;
}
update() {
this.animationProgress += 0.05 * this.animationDirection;
this.animationProgress = this.animationProgress > 1 ? 1 : this.animationProgress < 0 ? 0 : this.animationProgress;
if (this.isPlaceholder) {
this.pos = game.center;
this.sizeMultiplier = game.height * 0.05;
}
else {
this.pos = p5.Vector.lerp(this.container.center, game.center, this.smoothProgress);
this.sizeMultiplier = p.lerp(this.container.dim.y * 0.05, game.height * 0.05, this.smoothProgress);
}
this.decorations.forEach((d, i) => {
if (d.isToDelete) {
this.decorations.splice(i, 1);
if (d instanceof Ball) {
d.graphics.remove();
}
}
});
this.decorations.forEach(d => d.update());
}
display(pos, dim) {
let sizeMultiplier = this.sizeMultiplier;
if (pos || dim) {
sizeMultiplier = dim.y * 0.05;
}
else {
pos = this.pos;
}
p.push();
p.translate(pos.x, pos.y);
p.scale(sizeMultiplier);
if (this.isPlaceholder) {
p.fill(0);
p.stroke(100, 220, 100);
p.strokeWeight(0.05);
p.textSize(1);
p.textAlign(p.CENTER, p.CENTER);
p.text('Select your Christmas Tree!', 0, 0);
}
else {
this.drawTree();
this.drawDecorations();
if (debug && this === game.tree) {
p.stroke(255, 0, 0, 80);
p.strokeWeight(0.05);
for (let i = -10; i <= 10; i++) {
p.line(-10, i, 10, i);
p.line(i, -10, i, 10);
}
}
}
p.pop();
}
drawTree() {
p.push();
p.translate(0, 1);
p.fill(this.stemColor);
p.stroke(0);
p.strokeWeight(0.05);
p.rect(-this.stemRadius, 0, this.stemRadius * 2, this.stemHeight);
let drawLeaf = (r, h) => {
if (this.isSmooth) {
let cps = [
p.createVector(40, 70),
p.createVector(60, 90),
p.createVector(100, 130),
p.createVector(-100, 130),
p.createVector(-60, 90),
p.createVector(-40, 70)
];
cps.forEach(c => {
c.x *= r / 100;
c.y *= h / 100;
});
p.beginShape();
p.vertex(0, 0);
p.bezierVertex(cps[0].x, cps[0].y, cps[1].x, cps[1].y, r, h);
p.bezierVertex(cps[2].x, cps[2].y, cps[3].x, cps[3].y, -r, h);
p.bezierVertex(cps[4].x, cps[4].y, cps[5].x, cps[5].y, 0, 0);
p.endShape();
if (debug) {
p.stroke(255, 0, 0);
cps.forEach(c => p.ellipse(c.x, c.y, 0));
}
}
else {
p.triangle(0, 0, r, h, -r, h);
}
};
for (let i = 0; i < this.leafCount; i++) {
let y = -i * this.leafHeight / 2;
let lr = this.leafRadius * (this.leafCount - i) / this.leafCount;
p.push();
p.translate(0, y);
p.fill(this.leafColors[i % this.leafColors.length]);
drawLeaf(lr, this.leafHeight);
p.pop();
}
p.pop();
}
drawDecorations() {
for (let d of this.decorations) {
if (d.needsValidation) {
if (!d.hasValidPosition) {
d.isToDelete = true;
}
d.needsValidation = false;
}
}
for (let d of this.decorations) {
if (released && d.isBoundToMouse) {
d.needsValidation = true;
d.isBoundToMouse = false;
if (d instanceof Chain) {
d.p1IsBoundToMouse = false;
d.p2IsBoundToMouse = false;
}
}
d.display();
}
}
getAllColors(withoutStem) {
let colors = [];
colors.push(...this.leafColors, [0, 0, 0]);
if (!withoutStem) {
colors.push(this.stemColor);
}
return colors;
}
smoothStep(start, end, t) {
function clamp(x, lowLimit, upLimit) {
if (x < lowLimit)
x = lowLimit;
if (x > upLimit)
x = upLimit;
return x;
}
t = clamp((t - start) / (end - start), 0, 1);
return t * t * t * (t * (t * 6 - 15) + 10);
}
toJSON(index) {
let list = [
"stemRadius",
"leafCount",
"leafColors",
"stemColor",
"stemHeight",
"leafHeight",
"leafRadius",
"isSmooth",
"isPlaceholder",
"decorations"
];
let obj = {};
for (let item of list) {
obj[item] = this[item];
}
return obj;
}
restoreFrom(rawTree) {
for (let key in rawTree) {
if (key === 'decorations') {
this[key] = [];
rawTree[key].forEach(rawDeco => {
let deco = Decoration.restoreFrom(rawDeco);
this[key].push(deco);
});
}
else {
this[key] = rawTree[key];
}
}
}
}
//# sourceMappingURL=tree.js.map

File diff suppressed because one or more lines are too long

@ -0,0 +1,189 @@
let snowflake_max_r = 12;
let snowflake_min_r = 7;
let snowflake_max_count = 500;
class Background{
bgColor: p5.Color = p.color(100, 100, 255);
snowflakes: Snowflake[]
foregroundSnowflakes: Snowflake[]
backgroundSnowflakes: Snowflake[]
wind_direction: number = 0
ground: Ground = new Ground()
constructor() {
this.snowflakes = [];
for (let i = 0; i < snowflake_max_count; i++){
this.snowflakes.push(this.getNewSnowflake());
}
this.snowflakes.sort((a, b) => {
if (a.radius < b.radius){
return -1;
}
if (a.radius > b.radius){
return 1;
}
return 0;
});
this.backgroundSnowflakes = this.snowflakes.slice(0, snowflake_max_count * 2 / 3);
this.foregroundSnowflakes = this.snowflakes.slice(snowflake_max_count * 2 / 3, snowflake_max_count);
this.updateSettings();
}
getNewSnowflake(){
return new Snowflake(
p.random(p.width),
p.random(-snowflake_max_r, p.height),
p.random(snowflake_min_r, snowflake_max_r),
);
}
updateSettings(){
let allowed_snowflakes_count = $('#snow_intensity').val() as number;
let visible_snowflakes = p.shuffle(this.snowflakes.filter(s => !s.hidden));
let hidden_snowflakes = p.shuffle(this.snowflakes.filter(s => s.hidden));
let snowflakes_to_add = allowed_snowflakes_count - visible_snowflakes.length;
if (snowflakes_to_add > 0){
for (let i = 0; i < snowflakes_to_add; i++){
hidden_snowflakes[i].hidden = false;
}
}
if (snowflakes_to_add < 0){
for (let i = 0; i < p.abs(snowflakes_to_add); i++){
visible_snowflakes[i].hidden = true;
}
}
this.bgColor.setBlue(
p.map(
allowed_snowflakes_count,
0, snowflake_max_count,
255, 170
)
);
}
update(){
this.ground.update();
this.wind_direction = p.sin(p.frameCount / 1000);
this.snowflakes.forEach(s => s.update(this.wind_direction));
}
display(asForeground?: boolean){
if (asForeground){
this.foregroundSnowflakes.forEach(s => s.display());
} else {
p.background(this.bgColor);
this.ground.display();
this.backgroundSnowflakes.forEach(s => s.display());
}
}
}
class Snowflake{
pos: p5.Vector
radius: number
alpha: number
hidden: boolean
rotateDir: number
rotation: number = 0
peakCount: number
constructor(x, y, r) {
this.pos = p.createVector(x, y);
this.radius = r;
this.hidden = false;
this.alpha = 0;
this.rotateDir = p.random([-1, 1]) * p.random(0.01, 0.03);
this.peakCount = p.random([4, 5, 6]);
}
update(wind_direction: number){
let dir = p.createVector(
p.map(this.radius, snowflake_min_r, snowflake_max_r, 0, 1) * wind_direction,
p.map(this.radius, snowflake_min_r, snowflake_max_r, 0.5, 1) * 2
);
this.pos.add(dir);
this.rotation += this.rotateDir;
if (this.pos.y > p.height + this.radius){
this.pos.y = -this.radius;
}
if (this.pos.x > p.width + this.radius){
this.pos.x = -this.radius;
} else if (this.pos.x < -this.radius){
this.pos.x = p.width + this.radius;
}
if (this.hidden && this.alpha > 0){
this.alpha -= 0.015;
} else if (this.alpha < 1){
this.alpha += 0.015;
}
}
display(){
p.push();
p.strokeWeight(1);
p.stroke(255, this.alpha * 255);
p.fill(255, this.alpha * 255);
p.translate(this.pos);
p.rotate(this.rotation % p.TWO_PI);
for (let rad = 0; rad < p.PI; rad += p.PI / this.peakCount){
let x1 = p.cos(rad) * this.radius;
let y1 = p.sin(rad) * this.radius;
let x2 = p.cos(rad + p.PI) * this.radius;
let y2 = p.sin(rad + p.PI) * this.radius;
p.line(x1, y1, x2, y2);
}
p.pop();
}
}
class Ground{
points: p5.Vector[]
constructor() {
this.createPoints();
}
createPoints(){
this.points = [];
for (let x = 0; x < p.width; x++){
let vector = p.createVector(
x,
p.noise(x / 300) * 150
);
this.points.push(vector);
}
}
update(){
}
display(){
p.push();
p.fill(220, 220, 255);
p.stroke(50, 50, 150);
p.strokeWeight(2);
p.beginShape();
for (let point of this.points){
p.vertex(point.x, point.y + p.height / 2);
}
p.vertex(p.width, p.height);
p.vertex(0, p.height);
p.endShape(p.CLOSE);
p.pop();
}
}

@ -0,0 +1,39 @@
class Ball extends Decoration{
graphics: p5.Graphics
constructor(properties: object){
super(properties);
this.createGraphics();
}
createGraphics(){
let img = images['ball'];
this.graphics = p.createGraphics(img.width, img.height);
this.updateColor();
}
updateColor(){
let img = images['ball'];
this.graphics.clear();
this.graphics.tint(this.colors[0]);
this.graphics.image(img, 0, 0);
}
display(pos?: p5.Vector, dim?: p5.Vector){
super.display(pos, dim);
p.image(this.graphics, 0, 0, this.radius * 2, this.radius * 2);
if (this.mouseIsOver){
this.brightness(70);
}
p.pop();
}
update(){
super.update();
}
}

@ -0,0 +1,167 @@
import has = Reflect.has;
class Chain extends Decoration{
p1: p5.Vector = p.createVector(-0.65, 0)
p2: p5.Vector = p.createVector(0.65, 0)
p1IsBoundToMouse: boolean = false
p2IsBoundToMouse: boolean = false
placedOnce: boolean = false
points: {x: number, y: number}[] = []
lights: {x: number, y: number}[] = []
constructor(properties: object){
super(properties);
this.generate();
}
get properties(): object{
let obj = super.properties;
obj['p1'] = this.p1;
obj['p2'] = this.p2;
return obj;
}
generate(){
let d = p.dist(this.p1.x, this.p1.y, this.p2.x, this.p2.y);
let m = p.abs((this.p2.y - this.p1.y) / (this.p2.x - this.p1.x));
let a = -1 / d - m / 10;
let b = (this.p1.y - this.p2.y + a * p.pow(this.p2.x, 2) - a * p.pow(this.p1.x, 2)) /
(this.p1.x - this.p2.x);
if (this.p1.x - this.p2.x === 0)
b = 0;
let c = this.p1.y - a * p.pow(this.p1.x, 2) - b * this.p1.x;
let func = x => a * x * x + b * x + c;
let detail = 40;
let points = [];
for (let i = 0; i <= detail; i++){
let point = {x: 0, y: 0};
point.x = this.p1.x + (this.p2.x - this.p1.x) / detail * i;
point.y = func(point.x);
points.push(point);
}
let lightDist = 0.35;
let lastLight = {x: Infinity, y: Infinity};
let lights = []
detail = 300;
for (let i = 0; i <= detail; i++){
let point = {x: 0, y: 0, hasLight: false};
point.x = this.p1.x + (this.p2.x - this.p1.x) / detail * i;
point.y = func(point.x);
if (p.dist(point.x, point.y, lastLight.x, lastLight.y) >= lightDist){
lights.push(point);
lastLight = point;
}
}
this.points = points;
this.lights = lights;
}
display(pos?: p5.Vector, dim?: p5.Vector){
super.display(pos, dim);
p.strokeWeight(0.05);
p.stroke(0);
p.noFill();
p.beginShape();
for (let point of this.points)
p.vertex(point.x, point.y);
p.endShape();
p.strokeWeight(0.01);
let i = 0;
for (let point of this.lights){
let colorIndex = (i + p.int(p.frameCount / 30)) % this.colors.length;
p.fill(this.colors[colorIndex]);
p.ellipse(point.x, point.y, 0.2, 0.2);
i++;
}
p.pop();
}
get hasValidPosition(): boolean{
return game.tree.containsPosition(this.realPosByAnchor(this.p1), true)
&& game.tree.containsPosition(this.realPosByAnchor(this.p2), true);
}
realPosByAnchor(anchor): p5.Vector{
return p5.Vector.add(p5.Vector.mult(p5.Vector.add(this.pos, anchor), game.tree.sizeMultiplier), game.center);
}
realPointDistToAnchor(point, anchor){
let pos = this.realPosByAnchor(anchor);
return p.dist(pos.x, pos.y, point.x, point.y);
}
isMouseOverAnchor(anchor){
return this.realPointDistToAnchor(p.createVector(p.mouseX, p.mouseY), anchor) <= 0.5 * game.tree.sizeMultiplier;
}
get mouseIsOver(): boolean{
return this.isTaken && (this.isMouseOverAnchor(this.p1) || this.isMouseOverAnchor(this.p2));
}
//Mouse is over this
mousePressed() {
super.mousePressed();
if (this.isMouseOverAnchor(this.p1)){
this.p1IsBoundToMouse = true;
} else if (this.isMouseOverAnchor(this.p2)){
this.p2IsBoundToMouse = true;
}
}
updateFromPoint(point, other, x, y){
if (this.placedOnce){
let pointRelative = p.createVector(x - this.pos.x, y - this.pos.y);
point.set(pointRelative);
let pointReal = p5.Vector.add(this.pos, point);
let otherReal = p5.Vector.add(this.pos, other);
let newPos = p5.Vector.div(p5.Vector.add(pointReal, otherReal), 2);
point.set(p5.Vector.sub(pointReal, newPos));
other.set(p5.Vector.mult(point, -1));
this.pos.set(newPos);
} else {
this.pos.set(x - point.x, y - point.y);
}
}
update(){
let x = (p.mouseX - game.center.x) / game.tree.sizeMultiplier,
y = (p.mouseY - game.center.y) / game.tree.sizeMultiplier;
if (this.isBoundToMouse){
if (this.p1IsBoundToMouse){
this.updateFromPoint(this.p1, this.p2, x, y);
}
if (this.p2IsBoundToMouse){
this.updateFromPoint(this.p2, this.p1, x, y);
}
this.generate();
} else {
super.update();
}
if (this.isTaken && !this.isBoundToMouse){
this.placedOnce = true;
}
}
toJSON(index){
let list = [
"p1",
"p2"
]
let obj = super.toJSON(index);
for (let item of list){
obj[item] = this[item];
}
return obj;
}
}

@ -0,0 +1,84 @@
class Container{
index: number
pos: p5.Vector = p.createVector()
dim: p5.Vector = p.createVector()
content: Tree | Decoration
constructor(index: number, content: Tree | Decoration){
this.index = index;
this.content = content;
}
get mouseIsOver(): boolean{
return p.mouseX > this.pos.x && p.mouseX < this.pos.x + this.dim.x &&
p.mouseY > this.pos.y && p.mouseY < this.pos.y + this.dim.y;
}
get center(): p5.Vector{
return p5.Vector.add(this.pos, p5.Vector.div(this.dim, 2));
}
display(){
let w = this.dim.x,
h = this.dim.y;
let x = this.pos.x + w / 2,
y = this.pos.y + h / 2;
p.image(images['box'], x, y, w, h);
if (this.content !== game.tree){
this.content.display();
}
if (this.mouseIsOver){
p.fill(0, 50);
p.noStroke();
p.rect(x - w / 2, y - h / 2, w , h);
}
}
update(){
let currentCount = game.currentContainers.length;
let w = p.width / currentCount,
h = w;
let x = w * this.index,
y = p.height - h;
if (this.content instanceof Decoration){
let otherCount = game.containers.filter(c => game.currentContainers.find(ic => ic == c) == null).length;
w = h = p.width / otherCount;
x = w * this.index;
y = p.height - h;
x += p.width / 2 - w * currentCount / 2;
}
this.pos.set(x, y);
this.dim.set(w, h);
if (this.content !== game.tree)
this.content.update();
}
mousePressed(){
if (this.content instanceof Tree){
game.tree.animationDirection = -1;
if (this.content === game.tree){
game.tree = new Tree(null);
} else {
game.tree = this.content;
game.tree.animationDirection = 1;
}
}
if (this.content instanceof Decoration && !game.tree.isPlaceholder){
let decoration = Decoration.Create(this.content.properties);
decoration.isTaken = true;
decoration.isBoundToMouse = true;
game.tree.decorations.push(decoration);
if (decoration instanceof Chain){
decoration.p1IsBoundToMouse = true;
}
}
}
}

@ -0,0 +1,194 @@
class Decoration{
pos: p5.Vector = p.createVector()
colors: number[][]
radius: number
sizeMultiplier: number
container: Container
isTaken: boolean = false
isBoundToMouse: boolean = false;
isToDelete: boolean = false;
needsValidation: boolean = false;
isSelected: boolean = false;
type: string
constructor(properties: object){
for (let key in properties){
if (properties.hasOwnProperty(key)){
if (Array.isArray(properties[key])){
this[key] = arrayCopy(properties[key]);
} else {
this[key] = properties[key];
}
}
}
}
get properties(): object{
let colors = [];
for (let i = 0; i < this.colors.length; i++){
let color = [];
for (let c of this.colors[i]){
color.push(c);
}
colors.push(color);
}
return {
radius: this.radius,
colors: colors,
type: this.type
};
}
get mouseIsOver(): boolean{
if (this.isTaken){
let pos = this.realPos;
return p.dist(pos.x, pos.y, p.mouseX, p.mouseY) <= this.radius * game.tree.sizeMultiplier;
}
return false;
}
get hasValidPosition(): boolean{
return game.tree.containsPosition(this.realPos, true);
}
get realPos(): p5.Vector{
return p5.Vector.add(p5.Vector.mult(this.pos, game.tree.sizeMultiplier), game.center);
}
static Create(properties: object): Decoration{
switch (properties['type']){
case 'ball':
return new Ball(properties);
case 'star':
return new Star(properties);
case 'chain':
return new Chain(properties);
}
}
display(pos?: p5.Vector, dim?: p5.Vector){
let sizeMultiplier = this.sizeMultiplier;
if (pos || dim){
sizeMultiplier = dim.y * 0.6;
} else {
pos = this.pos;
}
p.push();
p.translate(pos);
p.scale(sizeMultiplier);
}
brightness(value: number){
p.noStroke();
p.fill(0, 0, 0, value);
p.ellipse(0, 0, this.radius * 2, this.radius * 2);
}
update(){
if (!this.isTaken){
this.pos = this.container.center;
this.sizeMultiplier = this.container.dim.y * 0.6;
} else {
this.sizeMultiplier = 1;
}
if (this.isBoundToMouse){
let x = (p.mouseX - game.center.x) / game.tree.sizeMultiplier,
y = (p.mouseY - game.center.y) / game.tree.sizeMultiplier;
this.pos.set(x, y);
}
}
//Assumes mouse is over this
mousePressed(){
this.isBoundToMouse = true;
}
//Always called
mouseReleased(){
}
toJSON(index){
let list = [
"pos",
"colors",
"radius",
"type"
]
let obj = {};
for (let item of list){
obj[item] = this[item];
}
return obj;
}
static restoreFrom(rawDeco: object){
let props = {};
for (let key in rawDeco){
if (['pos', 'p1', 'p2'].find(s => s === key) === undefined){
props[key] = rawDeco[key];
}
}
let deco = Decoration.Create(props);
deco.isTaken = true;
deco.pos.x = rawDeco['pos'].x;
deco.pos.y = rawDeco['pos'].y;
if (deco instanceof Chain){
deco.p1.set(rawDeco['p1'].x, rawDeco['p1'].y);
deco.p2.set(rawDeco['p2'].x, rawDeco['p2'].y);
deco.generate();
}
return deco;
}
}
function darker(c){
let newC = []
for (let v of c){
newC.push(p.constrain(v * 0.6, 0, 255));
}
return newC;
}
function arrayCopy(a){
let newArr = [];
for (let c of a){
let copy = c;
if (Array.isArray(c)){
copy = arrayCopy(c);
}
newArr.push(copy);
}
return newArr;
}
function equals(a1, a2){
// if the other array is a falsy value, return
if (!a1)
return false;
// compare lengths - can save a lot of time
if (a2.length != a1.length)
return false;
for (let i = 0, l=a2.length; i < l; i++) {
// Check if we have nested arrays
if (a2[i] instanceof Array && a1[i] instanceof Array) {
// recurse into the nested arrays
if (!a2[i].equals(a1[i]))
return false;
}
else if (a2[i] != a1[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,30 @@
'use strict';
p.keyPressed = () => {
switch (p.keyCode){
//Ctrl + D
case 68:
if (p.keyIsDown(17)){
debug = !debug;
let msg = 'Debug mode turned ' + (debug ? 'ON' : 'OFF');
console.info(msg);
return false;
}
break;
}
}
p.mousePressed = () => {
game.mousePressed();
released = false;
}
p.mouseReleased = () => {
game.mouseReleased();
released = true;
}
p.windowResized = () => {
let w = $('#canvas_holder').width(),
h = $('#canvas_holder').height();
p.resizeCanvas(w, h);
}

@ -0,0 +1,261 @@
class Game{
trees: Tree[] = []
tree: Tree = new Tree(null)
decorations: Decoration[] = []
containers: Container[] = []
isTreeMode: boolean = true
isDecorationMode: boolean = false
barHeight: number
switchButton: Button = new Button('switch')
downloadButton: Button = new Button('download')
restoreButton: Button = new Button('restore')
background: Background = new Background()
constructor(){
settings.game.trees.forEach((s: object) => this.trees.push(new Tree(s)));
let red = settings.game.colors.red;
this.decorations = [
Decoration.Create({
type: "ball", radius: 0.45, colors: [red]
}),
Decoration.Create({
type: "star", radius: 0.45, colors: [red, darker(red)]
}),
Decoration.Create({
type: "chain", radius: 0.45, colors: [
settings.game.colors.gold,
settings.game.colors.silver
]
}),
];
$('#gold, #silver').attr('checked', 'checked');
this.trees.forEach((t, i) => {
let c = new Container(i, t);
this.containers.push(c);
t.container = c;
});
this.decorations.forEach((d, i) => {
let c = new Container(i, d);
this.containers.push(c);
d.container = c;
});
this.updateDecorationSettings();
this.updateStarSettings();
}
updateDecorationSettings(){
this.decorations.filter(d => d instanceof Ball || d instanceof Star).forEach(d => {
d.radius = $('#radius').val() as number / 100 * 0.7;
let colors = [];
colors.push(JSON.parse($('#color').val() as string));
if (d instanceof Ball){
d.colors = colors;
d.updateColor();
}
if (d instanceof Star){
d.innerRadius = $('#inner_radius').val() as number * d.radius;
colors.push(darker(colors[0]));
d.colors = colors;
d.createTriangles();
}
});
}
updateStarSettings(){
this.decorations.filter(d => d instanceof Star).forEach((s: Star) => {
s.innerRadius = $('#inner_radius').val() as number * s.radius;
s.raysCount = parseInt($('#rays_count').val() as string);
s.createTriangles();
});
}
updateChainSettings(element: JQuery): boolean{
let chain = this.decorations.find(d => d instanceof Chain);
let colorName = element.attr('id');
let colorValue = settings.game.colors[colorName];
let cvIndex = chain.colors.findIndex(c => equals(c, colorValue));
if (cvIndex >= 0){
chain.colors.splice(cvIndex, 1);
} else {
chain.colors.push(colorValue);
}
if (chain.colors.length == 0){
chain.colors.push(colorValue);
return false;
}
return true;
}
updateBackgroundSettings(){
this.background.updateSettings();
}
get center(): p5.Vector{
return p.createVector(p.width / 2, this.height / 2);
}
get height(): number{
return p.height - this.containers[0].dim.y;
}
get currentContainers(): Container[]{
return this.containers.filter(c => {
if (this.isTreeMode)
return c.content instanceof Tree
if (this.isDecorationMode)
return c.content instanceof Decoration
});
}
update(){
this.background.update();
this.switchButton.update();
this.downloadButton.update();
this.restoreButton.update();
this.currentContainers.forEach(c => c.update());
this.tree.update();
}
display(){
this.background.display();
this.switchButton.display();
this.downloadButton.display();
this.restoreButton.display();
this.currentContainers.forEach(c => c.display());
this.tree.display();
this.background.display(true);
}
//Always called
mousePressed(){
this.tree.mousePressed();
this.currentContainers.forEach(c => c.mouseIsOver ? c.mousePressed() : null);
this.switchButton.mouseIsOver ? this.switchButton.mousePressed() : null;
this.downloadButton.mouseIsOver ? this.downloadButton.mousePressed() : null;
this.restoreButton.mouseIsOver ? this.restoreButton.mousePressed() : null;
}
//Always called
mouseReleased(){
this.tree.mouseReleased();
}
restoreTrees(rawTrees: object[]){
this.trees.forEach((t, i) => {
t.restoreFrom(rawTrees[i]);
});
}
}
class Button{
pos: p5.Vector = p.createVector()
dim: p5.Vector = p.createVector()
type: string
constructor(type: string){
this.type = type;
}
get mouseIsOver(): boolean{
return p.mouseX > this.pos.x && p.mouseX < this.pos.x + this.dim.x &&
p.mouseY > this.pos.y && p.mouseY < this.pos.y + this.dim.y;
}
get center(): p5.Vector{
return p5.Vector.add(this.pos, p5.Vector.div(this.dim, 2));
}
display(){
let w = this.dim.x,
h = this.dim.y;
let x = this.pos.x + w / 2,
y = this.pos.y + h / 2;
p.image(images['box'], x, y, w, h);
if (this.type === 'switch'){
if (game.isTreeMode){
game.decorations[0].display(this.center, this.dim);
}
if (game.isDecorationMode){
let tree = game.tree;
if (tree.isPlaceholder)
tree = game.trees[0];
tree.display(this.center, this.dim);
}
}
if (this.type === 'download'){
p.textAlign(p.CENTER, p.CENTER);
p.textSize(25);
p.fill(0);
p.text("Save", this.pos.x, this.pos.y, this.dim.x, this.dim.y);
}
if (this.type === 'restore'){
p.textAlign(p.CENTER, p.CENTER);
p.textSize(20);
p.fill(0);
p.text("Restore", this.pos.x, this.pos.y, this.dim.x, this.dim.y);
}
if (this.mouseIsOver){
p.fill(0, 50);
p.noStroke();
p.rect(x - w / 2, y - h / 2, w , h);
}
}
update(){
let w, h, x, y;
if (this.type === 'switch'){
w = game.containers[0].dim.x / 2;
h = w;
x = 0;
y = game.containers[0].pos.y - h;
}
if (this.type === 'download'){
w = game.containers[0].dim.x / 2;
h = w;
x = p.width - w;
y = game.containers[0].pos.y - h;
}
if (this.type === 'restore'){
w = game.containers[0].dim.x / 2;
h = w;
x = p.width - 2 * w;
y = game.containers[0].pos.y - h;
}
this.pos.set(x, y);
this.dim.set(w, h);
}
mousePressed(){
if (this.type === 'switch'){
game.isDecorationMode = !game.isDecorationMode;
game.isTreeMode = !game.isTreeMode;
}
if (this.type === 'download'){
downloadTrees();
}
if (this.type === 'restore'){
$('#file_browser')[0].click();
}
}
}

@ -0,0 +1,168 @@
'use strict';
let debug = false,
font,
settings: any,
loader: any;
//Only for online games
let socket: SocketIOClient.Socket;
let antiCacheQuery = '?_=' + new Date().getTime();
let game: Game;
let released = false;
let images: object = {
};
const p = new p5((p: p5) => {
p.preload = () => {
settings = p.loadJSON('data/settings/settings.json' + antiCacheQuery, {}, 'json', (json: any) => {
console.log('Local settings loaded: ', json);
}, (error: any) => {
console.log('Local settings failed: ', error);
});
font = p.loadFont('data/styles/fonts/Tajawal/Tajawal-Regular.ttf' + antiCacheQuery, (font) => {
console.log('Local font loaded: ', font);
}, (error: any) => {
console.log('Local font failed: ', error);
});
images['ball'] = p.loadImage('data/images/ball.png' + antiCacheQuery, img => {
console.log('Ball image loaded: ', img);
}, error => {
console.log('Ball image failed: ', error);
});
images['box'] = p.loadImage('data/images/box.png' + antiCacheQuery, img => {
console.log('Box image loaded: ', img);
}, error => {
console.log('Box image failed: ', error);
});
},
p.setup = () => {
interfaceSetup();
canvasSetup();
//loader = new Loader(p.createVector(p.width / 2, p.height / 2), Math.min(p.width, p.height) / 2);
loadDynamicScripts().then(() => {
//Load other stuff, then =>
//loader = null;
game = new Game();
game.trees[0].container.mousePressed();
});
},
p.draw = () => {
p.clear();
if (game){
game.update();
game.display();
}
if (loader){
loader.update();
loader.display();
}
if (debug) debugInformation();
}
})
function debugInformation(){
}
function interfaceSetup(){
let files_elem = $('#file_browser');
files_elem.on('change', () => {
let file = (files_elem.get(0) as HTMLInputElement).files[0];
file.text().then(text => {
game.restoreTrees(JSON.parse(text)[0]);
});
});
let colorSelect = $('#color');
let possibleColors = $('#possible_colors');
for (let colorName in settings.game.colors){
if (!settings.game.colors.hasOwnProperty(colorName))
continue;
let publicName = colorName.substr(0, 1).toUpperCase() + colorName.substr(1);
let option = $('<option></option>');
option.html(publicName);
option.val("[" + settings.game.colors[colorName] + "]");
colorSelect.append(option);
let checkbox = $('<input/>');
checkbox.attr({
type: 'checkbox',
id: colorName
});
checkbox.on('click', event => {
if (game){
if (!game.updateChainSettings($(event.target))){
console.info('failed');
event.preventDefault();
}
}
});
let label = $('<label></label>');
label.html(publicName);
label.attr('for', colorName);
possibleColors.append(checkbox, label);
}
}
function downloadTrees(){
let str = JSON.stringify(game.trees, (key, value) => {
if (value instanceof p5.Vector){
return {
x: value.x,
y: value.y
};
}
return value;
});
let data = 'data:application/json;charset=utf-8,['+ encodeURIComponent(str) + "]";
$("#save_link").attr("href", data);
$("#save_link")[0].click();
}
function canvasSetup(){
p.frameRate(60);
let w = $('#canvas_holder').width(),
h = $('#canvas_holder').height();
let canvas = p.createCanvas(w, h);
canvas.parent('canvas_holder');
p.textFont(font);
p.imageMode(p.CENTER);
}
async function loadDynamicScripts(){
const json = await p.httpGet('data/settings/libraries.json' + antiCacheQuery, 'json');
let requests = [];
for (let script in json) {
if (json[script]) {
let url = '/lib/benjocraeft/' + script + '.js';
requests.push($.getScript(url, () => {
console.log('Successfully loaded script: ', url);
}));
}
}
await $.when(...requests);
console.log('All dynamic scripts have been loaded!');
}

@ -0,0 +1,54 @@
class Loader{
pos: p5.Vector
c: any
radius: number
center: p5.Vector
angle: number
progress: number
message: string
constructor(pos: p5.Vector, radius: number){
this.pos = pos;
this.c = p.createGraphics(radius * 2, radius * 2);
this.radius = radius;
this.center = p.createVector(radius, radius);
this.message = 'Loading...';
this.progress = 0;
this.angle = 0;
}
update(){
this.angle += Math.PI / 10;
}
display(){
let c = this.c;
c.clear();
c.noFill();
c.stroke(20, 100, 200);
c.strokeWeight(this.radius * 0.025);
//c.arc(this.center.x, this.center.y, this.radius * 2, this.radius * 2, this.angle, this.angle + Math.PI * 1.5);
c.strokeWeight(2);
c.rect(this.center.x - this.radius + 5, this.center.y - 25, this.radius * 2 - 50, 50);
c.fill(50, 150, 255);
c.rect(this.center.x - this.radius + 5, this.center.y - 25, (this.radius - 50) * this.progress, 50);
c.textAlign(p.CENTER, p.CENTER);
c.textSize(25);
c.fill(220);
c.strokeWeight(4);
c.text(this.message, this.radius, this.radius * 1.5);
c.textSize(40);
c.text(Math.floor(this.progress * 100) + '%', this.radius, this.radius / 2);
p.imageMode(p.CENTER);
p.image(c, this.pos.x, this.pos.y);
}
destroy(){
this.c.remove();
}
}

@ -0,0 +1,10 @@
'use strict';
function socketConnect(project, name = "noone"){
let urlQueries = '?game=' + project.name + '&name=' + name;
let port = 3000
let url = location.hostname + ":" + port + urlQueries;
socket = io.connect(url);
socket.on('connect', () => console.log('Connected to ', url));
}

@ -0,0 +1,89 @@
class Star extends Decoration{
raysCount: number
triangles: p5.Vector[][]
innerRadius: number
constructor(properties: object){
super(properties);
this.createTriangles();
}
get properties(): object{
let obj = super.properties;
obj['raysCount'] = this.raysCount;
obj['innerRadius'] = this.innerRadius;
return obj;
}
createTriangles(){
this.triangles = [];
let increment = p.TWO_PI / this.raysCount / 2,
firstOuter = true;
for (let i = 0; i < p.TWO_PI; i += increment){
let x1 = p.sin(i),
y1 = -p.cos(i),
x2 = p.sin(i + increment),
y2 = -p.cos(i + increment);
if (firstOuter){
x1 *= this.radius,
y1 *= this.radius,
x2 *= this.innerRadius,
y2 *= this.innerRadius;
} else {
x2 *= this.radius,
y2 *= this.radius,
x1 *= this.innerRadius,
y1 *= this.innerRadius;
}
firstOuter = !firstOuter;
this.triangles.push([
p.createVector(x1, y1),
p.createVector(x2, y2),
p.createVector(0, 0)
]);
}
}
brightness(value: number) {
this.triangles.forEach((t, i) => {
p.fill(0, 0, 0, value);
p.triangle(t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y);
});
}
display(pos?: p5.Vector, dim?: p5.Vector){
super.display(pos, dim);
p.noStroke();
this.triangles.forEach((t, i) => {
let color = this.colors[i % this.colors.length];
p.fill(color);
p.triangle(t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y);
});
if (this.mouseIsOver){
this.brightness(70);
}
p.pop();
}
update(){
super.update();
}
toJSON(index){
let list = [
"raysCount",
"innerRadius"
]
let obj = super.toJSON(index);
for (let item of list){
obj[item] = this[item];
}
return obj;
}
}

@ -0,0 +1,256 @@
class Tree{
pos: p5.Vector = p.createVector()
sizeMultiplier: number = 1
stemRadius: number
leafCount: number
leafColors: number[][]
stemColor: number[]
stemHeight: number
leafHeight: number
leafRadius: number
isSmooth: boolean
isPlaceholder: boolean
container: Container
animationProgress: number = 0
animationDirection: number = -1
decorations: Decoration[] = []
constructor(properties: object){
if (properties == null){
this.isPlaceholder = true;
} else {
for (let key in properties){
this[key] = properties[key];
}
}
}
//Always called
mousePressed(){
for (let d of [...this.decorations].reverse()){
if (d.mouseIsOver){
d.mousePressed();
let index = this.decorations.findIndex(deco => deco == d);
this.decorations[index] = this.decorations[this.decorations.length - 1];
this.decorations[this.decorations.length - 1] = d;
break;
}
}
}
//Always called
mouseReleased(){
for (let d of this.decorations){
d.mouseReleased();
}
}
get smoothProgress(): number{
return this.smoothStep(0, 1, this.animationProgress);
}
containsPosition(pos: p5.Vector, excludeStem?: boolean){
let size = game.height,
x = (p.width - size) / 2;
let px = pos.x,
py = pos.y;
let pixelColor = (p.get(px, py) as number[]).slice(0, 3);
let colorIsValid = game.tree.getAllColors(excludeStem).find(c => equals(c, pixelColor)) != null;
return colorIsValid &&
px > x && px < x + size &&
py > 0 && py < size - 20;
}
update(){
this.animationProgress += 0.05 * this.animationDirection;
this.animationProgress = this.animationProgress > 1 ? 1 : this.animationProgress < 0 ? 0 : this.animationProgress;
if (this.isPlaceholder){
this.pos = game.center;
this.sizeMultiplier = game.height * 0.05;
} else {
this.pos = p5.Vector.lerp(this.container.center, game.center, this.smoothProgress);
this.sizeMultiplier = p.lerp(this.container.dim.y * 0.05, game.height * 0.05, this.smoothProgress);
}
this.decorations.forEach((d, i) => {
if (d.isToDelete){
this.decorations.splice(i, 1);
if (d instanceof Ball){
d.graphics.remove();
}
}
});
this.decorations.forEach(d => d.update());
}
display(pos?: p5.Vector, dim?: p5.Vector){
let sizeMultiplier = this.sizeMultiplier;
if (pos || dim){
sizeMultiplier = dim.y * 0.05;
} else {
pos = this.pos;
}
p.push();
p.translate(pos.x, pos.y);
p.scale(sizeMultiplier);
if (this.isPlaceholder){
p.fill(0);
p.stroke(100, 220, 100);
p.strokeWeight(0.05);
p.textSize(1);
p.textAlign(p.CENTER, p.CENTER);
p.text('Select your Christmas Tree!', 0, 0);
} else {
this.drawTree();
this.drawDecorations();
if (debug && this === game.tree){
p.stroke(255, 0, 0, 80);
p.strokeWeight(0.05);
for (let i = -10; i <= 10; i++){
p.line(-10, i, 10, i);
p.line(i, -10, i, 10);
}
}
}
p.pop();
}
drawTree(){
p.push();
p.translate(0, 1);
p.fill(this.stemColor);
p.stroke(0);
p.strokeWeight(0.05);
p.rect(-this.stemRadius, 0, this.stemRadius * 2, this.stemHeight);
let drawLeaf = (r: number, h: number) => {
if (this.isSmooth){
let cps = [
p.createVector(40, 70),
p.createVector(60, 90),
p.createVector(100, 130),
p.createVector(-100, 130),
p.createVector(-60, 90),
p.createVector(-40, 70)
];
cps.forEach(c => {
c.x *= r / 100;
c.y *= h / 100;
});
p.beginShape();
p.vertex(0, 0);
p.bezierVertex(cps[0].x, cps[0].y, cps[1].x, cps[1].y, r, h);
p.bezierVertex(cps[2].x, cps[2].y, cps[3].x, cps[3].y, -r, h);
p.bezierVertex(cps[4].x, cps[4].y, cps[5].x, cps[5].y, 0, 0);
p.endShape();
if (debug){
p.stroke(255, 0, 0);
cps.forEach(c => p.ellipse(c.x, c.y, 0));
}
} else {
p.triangle(0, 0, r, h, -r, h);
}
}
for (let i = 0; i < this.leafCount; i++){
let y = -i * this.leafHeight / 2;
let lr = this.leafRadius * (this.leafCount - i) / this.leafCount;
p.push();
p.translate(0, y);
p.fill(this.leafColors[i % this.leafColors.length]);
drawLeaf(lr, this.leafHeight);
p.pop();
}
p.pop();
}
drawDecorations(){
for (let d of this.decorations){
if (d.needsValidation){
if (!d.hasValidPosition){
d.isToDelete = true;
}
d.needsValidation = false;
}
}
for (let d of this.decorations){
if (released && d.isBoundToMouse){
d.needsValidation = true;
d.isBoundToMouse = false;
if (d instanceof Chain){
d.p1IsBoundToMouse = false;
d.p2IsBoundToMouse = false;
}
}
d.display();
}
}
getAllColors(withoutStem?: boolean): number[][]{
let colors = [];
colors.push(...this.leafColors, [0, 0, 0]);
if (!withoutStem){
colors.push(this.stemColor);
}
return colors;
}
smoothStep(start: number, end: number, t: number): number{
function clamp(x: number, lowLimit: number, upLimit: number): number{
if (x < lowLimit)
x = lowLimit;
if (x > upLimit)
x = upLimit;
return x;
}
t = clamp((t - start) / (end - start), 0, 1);
return t * t * t * (t * (t * 6 - 15) + 10);
}
toJSON(index){
let list = [
"stemRadius",
"leafCount",
"leafColors",
"stemColor",
"stemHeight",
"leafHeight",
"leafRadius",
"isSmooth",
"isPlaceholder",
"decorations"
]
let obj = {};
for (let item of list){
obj[item] = this[item];
}
return obj;
}
restoreFrom(rawTree: object){
for (let key in rawTree){
if (key === 'decorations'){
this[key] = []
rawTree[key].forEach(rawDeco => {
let deco = Decoration.restoreFrom(rawDeco);
this[key].push(deco);
});
} else {
this[key] = rawTree[key];
}
}
}
}

@ -0,0 +1,7 @@
{
"collision": false,
"colorPicker": false,
"cookie": false,
"prototypes": true,
"technical": false
}

@ -0,0 +1,202 @@
{
"project": {
"name": "christmas_tree",
"author": "BenjoCraeft",
"version": "0.0.0",
"playerCounts": [],
"online": {
"iceServers": [
{"urls": "stun:stun.l.google.com:19302"},
{
"urls": "turn:numb.viagenie.ca",
"credential": "muazkh",
"username": "webrtc@live.com"
}
]
}
},
"frameWork": {
"frameRate": 60,
"width": null,
"height": null
},
"game":{
"trees": [
{
"stemRadius": 1,
"stemHeight": 7,
"stemColor": [80, 60, 10],
"leafCount": 6,
"leafHeight": 4,
"leafRadius": 6,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 0.7,
"stemHeight": 8,
"stemColor": [80, 60, 10],
"leafCount": 3,
"leafHeight": 5,
"leafRadius": 4,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1.3,
"stemHeight": 8,
"stemColor": [80, 60, 10],
"leafCount": 7,
"leafHeight": 3.5,
"leafRadius": 7,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1,
"stemHeight": 7,
"stemColor": [80, 60, 10],
"leafCount": 5,
"leafHeight": 4,
"leafRadius": 6,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 0.7,
"stemHeight": 5,
"stemColor": [80, 60, 10],
"leafCount": 9,
"leafHeight": 2,
"leafRadius": 3,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 0.5,
"stemHeight": 4,
"stemColor": [80, 60, 10],
"leafCount": 3,
"leafHeight": 2,
"leafRadius": 2.5,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1,
"stemHeight": 7,
"stemColor": [80, 60, 10],
"leafCount": 5,
"leafHeight": 4.3,
"leafRadius": 5,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1,
"stemHeight": 6,
"stemColor": [80, 60, 10],
"leafCount": 7,
"leafHeight": 3.2,
"leafRadius": 5.5,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1.2,
"stemHeight": 6.5,
"stemColor": [80, 60, 10],
"leafCount": 6,
"leafHeight": 4,
"leafRadius": 8,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1,
"stemHeight": 8,
"stemColor": [80, 60, 10],
"leafCount": 5,
"leafHeight": 4,
"leafRadius": 6,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 0.5,
"stemHeight": 6,
"stemColor": [80, 60, 10],
"leafCount": 2,
"leafHeight": 3.5,
"leafRadius": 4,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
},
{
"stemRadius": 1,
"stemHeight": 6,
"stemColor": [80, 60, 10],
"leafCount": 6,
"leafHeight": 3.8,
"leafRadius": 4,
"leafColors": [
[50, 100, 50],
[75, 150, 75],
[100, 200, 100]
],
"isSmooth": true
}
],
"colors": {
"gold": [255, 215, 0],
"silver": [230, 230, 230],
"red": [255, 0, 0],
"pink": [255, 20, 147],
"blue": [0, 0, 255]
}
}
}

@ -0,0 +1,88 @@
#color_picker{
width: 300px;
height: 25%;
margin: 20px;
margin-top: 50px;
border: 5px solid #000;
background-color: #000;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: relative;
}
#color_picker_numeric{
width: 80%;
padding: 5%;
margin: 5%;
background-color: #888;
border-radius: 10px;
overflow: hidden;
}
.color_picker_rgb{
float: left;
width: 22%;
height: 35px;
font-size: 25px;
color: #000;
}
.color_picker_rgb:nth-child(1){
margin-right: 10%;
margin-left: 3%;
background-color: #F00;
}
.color_picker_rgb:nth-child(2){
background-color: #0F0;
}
.color_picker_rgb:nth-child(3){
margin-left: 10%;
background-color: #00F;
color: #FFF;
}
#color_picker_hex{
width: 50%;
height: 30px;
font-size: 25px;
margin: 10% 25% 0 25%;
}
#saturation{
position: relative;
width: calc(100% - 33px);
height: 100%;
background: linear-gradient(to right, #FFF 0%, #F00 100%);
float: left;
margin-right: 6px;
}
#value {
width: 100%;
height: 100%;
background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%);
}
#sb_picker{
border: 2px solid;
border-color: #FFF;
position: absolute;
width: 14px;
height: 14px;
border-radius: 10px;
bottom: 50px;
left: 50px;
box-sizing: border-box;
z-index: 10;
}
#hue {
width: 27px;
height: 100%;
position: relative;
float: left;
background: linear-gradient(to bottom, #F00 0%, #F0F 17%, #00F 34%, #0FF 50%, #0F0 67%, #FF0 84%, #F00 100%);
}
#hue_picker {
position: absolute;
background: #000;
border-bottom: 1px solid #000;
top: 0;
width: 27px;
height: 2px;
}

@ -0,0 +1,93 @@
Copyright 2018 Boutros International. (https://www.boutrosfonts.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,89 @@
input[type=range] {
-webkit-appearance: none;
margin: 18px 0;
width: 100%;
background: none;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 8.4px;
cursor: pointer;
animate: 0.2s;
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
background: var(--range-input-track-color);
border-radius: 1.3px;
border: 0.2px solid #010101;
}
input[type=range]::-webkit-slider-thumb {
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
border: 1px solid #000000;
height: 36px;
width: 16px;
border-radius: 3px;
background: var(--range-input-thumb-color);
cursor: pointer;
-webkit-appearance: none;
margin-top: -14px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #367ebd;
}
input[type=range]::-moz-range-track {
width: 100%;
height: 8.4px;
cursor: pointer;
animate: 0.2s;
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
background: var(--range-input-track-color);
border-radius: 1.3px;
border: 0.2px solid #010101;
}
input[type=range]::-moz-range-thumb {
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
border: 1px solid #000000;
height: 36px;
width: 16px;
border-radius: 3px;
background: var(--range-input-thumb-color);
cursor: pointer;
}
input[type=range]::-ms-track {
width: 100%;
height: 8.4px;
cursor: pointer;
animate: 0.2s;
background: transparent;
border-color: transparent;
border-width: 16px 0;
color: transparent;
}
input[type=range]::-ms-fill-lower {
background: #2a6495;
border: 0.2px solid #010101;
border-radius: 2.6px;
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
input[type=range]::-ms-fill-upper {
background: var(--range-input-track-color);
border: 0.2px solid #010101;
border-radius: 2.6px;
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
input[type=range]::-ms-thumb {
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
border: 1px solid #000000;
height: 36px;
width: 16px;
border-radius: 3px;
background: var(--range-input-thumb-color);
cursor: pointer;
}
input[type=range]:focus::-ms-fill-lower {
background: var(--range-input-track-color);
}
input[type=range]:focus::-ms-fill-upper {
background: var(--range-input-track-color);
}

@ -0,0 +1,67 @@
<!-- Web project created by Benjo Craeft (alias) -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="/lib/socket.io/socket.io.min.js" type="text/javascript"></script>
<script src="/lib/socket.io/socket.io-p2p.min.js" type="text/javascript"></script>
<script src="/lib/p5/p5.min.js" type="text/javascript"></script>
<script src="/lib/p5/p5.sound.min.js" type="text/javascript"></script>
<script src="/lib/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/lib/jquery/jq-ajax-progress.min.js" type="text/javascript"></script>
<script src="/lib/vue/vue.min.js" type="text/javascript"></script>
<script src="data/scripts/js/init.js" type="text/javascript"></script>
<script src="data/scripts/js/events.js" type="text/javascript"></script>
<script src="data/scripts/js/online.js" type="text/javascript"></script>
<script src="data/scripts/js/loader.js" type="text/javascript"></script>
<script src="data/scripts/js/game.js" type="text/javascript"></script>
<script src="data/scripts/js/tree.js" type="text/javascript"></script>
<script src="data/scripts/js/container.js" type="text/javascript"></script>
<script src="data/scripts/js/decoration.js" type="text/javascript"></script>
<script src="data/scripts/js/chain.js" type="text/javascript"></script>
<script src="data/scripts/js/ball.js" type="text/javascript"></script>
<script src="data/scripts/js/star.js" type="text/javascript"></script>
<script src="data/scripts/js/background.js" type="text/javascript"></script>
<link href="data/styles/color_picker.css" rel="stylesheet">
<link href="data/styles/range_input.css" rel="stylesheet">
<link href="styles.css" rel="stylesheet">
<link href="data/images/favicon.ico" rel="icon" type="image/x-icon">
<title>Christmas Tree</title>
</head>
<body>
<div id="p5_loading"></div>
<div id="content">
<div id="canvas_holder"></div>
<div id="settings_holder">
<fieldset oninput="game.updateDecorationSettings();">
<legend>Decoration Settings</legend>
<label for="radius">Radius</label>
<input id="radius" type="range" min="10" max="100" step="1" value="50">
<label for="color">Color</label><br/>
<select id="color">
</select>
</fieldset>
<fieldset oninput="game.updateStarSettings();">
<legend>Star Settings</legend>
<label for="rays_count">Peaks</label>
<input id="rays_count" type="range" min="3" max="10" step="1" value="5">
<label for="inner_radius">Second Radius</label>
<input id="inner_radius" type="range" min="0.2" max="0.7" step="0.01" value="0.4">
</fieldset>
<fieldset>
<legend>Chain Settings</legend>
<label for="possible_colors">Colors</label>
<div id="possible_colors">
</div>
</fieldset>
<fieldset oninput="game.updateBackgroundSettings();">
<legend>Background Settings</legend>
<label for="snow_intensity">Snow</label>
<input id="snow_intensity" type="range" min="0" max="500" step="1" value="200">
</fieldset>
</div>
<a id="save_link" download="christmas_trees.json"></a>
<input style="display: none;" type="file" id="file_browser" accept=".json">
</div>
</body>
</html>

@ -0,0 +1,86 @@
a:link, a:hover, a:active, a:visited{color: #000;}
html, body{margin: 0; padding: 0; height: 100%; width: 100%;}
canvas{margin: 0; padding: 0; border: none; display: block;}
button:hover{cursor: pointer;}
@font-face{
font-family: "Tajawal";
src: url("data/styles/fonts/Tajawal/Tajawal-Regular.ttf");
}
@font-face{
font-family: "Elronet";
src: url("data/styles/fonts/Elronet/Elronmonospace.ttf");
}
*{
font-family: "Tajawal", serif;
color: #000;
font-size: 17px;
}
:root{
--width: 100vw;
--height: 100vh;
--range-input-track-color: #3071a9;
--range-input-thumb-color: #6eaae2;
}
/**
* Standard styles
*/
#canvas_holder{
position: relative;
width: var(--width);
height: var(--height);
}
#canvas_holder canvas{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: inherit;
}
#p5_loading{
display: none;
}
#settings_holder{
position: fixed;
top: 0;
right: 0;
width: 300px;
background-color: rgba(90, 127, 171, 0.5);
border-width: 0 0 5px 5px;
border-style: solid;
border-color: #000;
border-bottom-left-radius: 10px;
}
#settings_holder > fieldset{
margin: 5px;
border-color: #000;
border-style: solid;
border-width: 2px;
border-radius: 5px;
}
#settings_holder > fieldset > legend{
font-size: 110%;
}
#settings_holder > fieldset > label{
font-size: 100%;
}
select, option{
color: #fff;
}
select{
background: rgba(90, 127, 171, 0.5);
}
input[type=range]{
margin: 10px 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"sourceMap": true,
"alwaysStrict": true,
"outDir": "./data/scripts/js/"
}
}
Loading…
Cancel
Save