commit d0f71bc79d76004f004d4b6b9c8f62e029262382 Author: Benjamin Kraft Date: Fri Mar 24 15:36:55 2023 +0100 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6ef218 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea + diff --git a/project.json b/project.json new file mode 100644 index 0000000..7104f46 --- /dev/null +++ b/project.json @@ -0,0 +1,6 @@ +{ + "display_name": "2D Openworld", + "info_text": "My first 2D Openworld project. You can explore by moving your cursor to the frame borders.". + "visible": false, + "tags": ["Game", "Simulation"] +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..e7b27c7 --- /dev/null +++ b/public/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + 2D Openworld + + + + + + + + + + +
+ + + + +
+ +
+
+

+ //Hier stehen irgendwann mal Infos +

+
+
+
+ + \ No newline at end of file diff --git a/public/scripts/Bird.js b/public/scripts/Bird.js new file mode 100644 index 0000000..f7ffe7a --- /dev/null +++ b/public/scripts/Bird.js @@ -0,0 +1,41 @@ +class Bird{ + + constructor(x){ + this.x = x; + this.y = random(height * 0.05, height * 0.3); + this.v = random([true, false]) ? random(3, 5) : random(-3, -5); + this.length = random(50, 100); + } + + move(){ + this.x += this.v; + if (this.x - this.length / 2 > worldWidth) this.x = -this.length / 2; + if (this.x + this.length / 2 < 0) this.x = worldWidth + this.length / 2; + } + + show(){ + let wing = sin(0.02 * PI * this.x); + if (this.x + this.length / 2 > viewPort.x && this.x - this.length / 2 < viewPort.x + width){ + fill(0); + beginShape(); + vertex(this.x - this.length / 2, this.y); + vertex(this.x - this.length * 0.4, this.y - this.length * 0.1); + vertex(this.x + this.length * 0.15, this.y - this.length * 0.1); + vertex(this.x + this.length * 0.15, this.y - this.length * 0.1 - this.length * 0.4 * wing); + vertex(this.x + this.length * 0.1, this.y - this.length * 0.1 - this.length * 0.5 * wing); + vertex(this.x + this.length * 0.05, this.y - this.length * 0.1 - this.length * 0.4 * wing); + vertex(this.x, this.y - this.length * 0.1 - this.length * 0.5 * wing); + vertex(this.x - this.length * 0.05, this.y - this.length * 0.1 - this.length * 0.4 * wing); + vertex(this.x - this.length * 0.1, this.y - this.length * 0.1 - this.length * 0.5 * wing); + vertex(this.x - this.length * 0.15, this.y - this.length * 0.1 - this.length * 0.4 * wing); + vertex(this.x - this.length * 0.15, this.y - this.length * 0.1); + vertex(this.x + this.length * 0.4, this.y - this.length * 0.1); + vertex(this.x + this.length / 2, this.y); + vertex(this.x + this.length * 0.4, this.y + this.length * 0.1); + vertex(this.x - this.length * 0.4, this.y + this.length * 0.1); + endShape(); + fill(255); + ellipse(this.x + ((this.v > 0) ? 1 : -1) * this.length * 0.4, this.y - this.length * 0.025, this.length * 0.05); + } + } +} \ No newline at end of file diff --git a/public/scripts/Cloud.js b/public/scripts/Cloud.js new file mode 100644 index 0000000..fdae4cb --- /dev/null +++ b/public/scripts/Cloud.js @@ -0,0 +1,30 @@ +class Cloud{ + + constructor(x, y, w, h){ + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.v = 0.25; + this.brightness = random(0.75, 1); + } + + move(){ + this.x += this.v; + if (this.x - this.width / 2 > worldWidth){ + this.x = -this.width; + } + } + + show(){ + fill(255 * this.brightness); + if (this.x + this.width * 9 / 8 > viewPort.x && this.x - this.width * 9 / 8 < viewPort.x + width){ + ellipse(this.x + this.width / 4, this.y + this.height * 2 / 3, this.width / 2, this.height / 2); + ellipse(this.x + this.width / 2, this.y + this.height * 2 / 3, this.width / 2, this.height / 2); + ellipse(this.x + this.width * 3 / 4, this.y + this.height * 2 / 3, this.width / 2, this.height / 2); + ellipse(this.x + this.width * 3 / 8, this.y + this.height * 1 / 3, this.width / 2, this.height / 2); + ellipse(this.x + this.width * 5 / 8, this.y + this.height * 1 / 3, this.width / 2, this.height / 2); + } + } + +} \ No newline at end of file diff --git a/public/scripts/Human.js b/public/scripts/Human.js new file mode 100644 index 0000000..ff5815a --- /dev/null +++ b/public/scripts/Human.js @@ -0,0 +1,20 @@ +function Human(x, h, v){ + this.x = x; + this.height = h; + this.male = random([true, false]); + this.legDist = 0; + this.v = v; + + this.move = function(d){ + this.x += this.v * d; + } + + this.show = function(){ + var y = ground[this.x]; + stroke(0); + fill(0); + strokeWeight(1); + line(this.x, y - this.height * 0.4, this.x, y - this.height * 0.85); + ellipse(this.x, y - this.height * 0.925, this.height * 0.1, this.height * 0.15); + } +} \ No newline at end of file diff --git a/public/scripts/Tree.js b/public/scripts/Tree.js new file mode 100644 index 0000000..bca3ac9 --- /dev/null +++ b/public/scripts/Tree.js @@ -0,0 +1,30 @@ +class Tree{ + + constructor(x, w, h){ + this.x = x; + this.width = w; + this.height = h; + let c = round(random(60, 100)); + this.color = {r: c, g: round(random(170, 230)), b: c}; + } + + show(){ + if (this.x + this.height * 0.375 > viewPort.x && this.x - this.height * 0.375 < viewPort.x + width){ + let yArr = []; + for (let x = this.x - this.width / 2; x < this.x + this.width / 2; x++){ + try{yArr.push(ground[round(x)].y);} + catch(e){} + } + let crownY = max(yArr) - this.height; + fill(80, 50, 40); + rect(this.x - this.width / 2, crownY, this.width, this.height); + fill(this.color.r, this.color.g, this.color.b); + ellipse(this.x, crownY, this.height / 2, this.height / 2); + for (let i = 0; i < TWO_PI; i += 1 / 4 * PI){ + let x = this.x + this.height / 4 * sin(i); + let y = crownY + this.height / 4 * cos(i); + ellipse(x, y, this.height / 2, this.height / 2); + } + } + } +} \ No newline at end of file diff --git a/public/scripts/lib/BenjoLibrary.js b/public/scripts/lib/BenjoLibrary.js new file mode 100644 index 0000000..ef3fa60 --- /dev/null +++ b/public/scripts/lib/BenjoLibrary.js @@ -0,0 +1,380 @@ +var TOP = 1; +var RIGHT = 2 +var BOTTOM = 3; +var LEFT = 4; +var TOP_RIGHT = 5; +var BOTTOM_RIGHT = 6; +var BOTTOM_LEFT = 7; +var TOP_LEFT = 8; + +var wWidth = window.innerWidth; +var wHeight = window.innerHeight; +var oldWHeight; +var oldWWidth; + +function updateVars(){ + oldWWidth = wWidth; + oldWHeight = wHeight; + wWidth = window.innerWidth; + wHeight = window.innerHeight; +} + +function collisionDetection(obj0, obj1){ + + var sp = strokePadding; + if (sp == null) sp = 0; + + if (obj0.isEllipse && obj1.isRectangle){ + + //Ball + var b = obj0; + + //Rectangle + var r = obj1; + + for (var i = 0; i < TWO_PI; i += PI / 32){ + + /* Check every borderpoint of the ball beginning + at the top in clock direction up to top again */ + + // Ball Center X + var bcx = b.x; + + // Ball Center Y + var bcy = b.y; + + // Ball Border X + var bbx = b.x + sin(i) * b.r; + + // Ball Border Y inverted because Y = 0 is the TOP of the screen + var bby = b.y - cos(i) * b.r; + + // Rectangle Width + var rW = r.width + 2 * sp; + + // Rectangle Height + var rH = r.height + 2 * sp; + + // Rectangle Border X + var rX = r.x - sp; + + // Rectangle Border Y + var rY = r.y - sp; + + // Objects touch + if (bbx > rX && bbx < rX + rW + && bby > rY && bby < rY + rH){ + + // STRAIGHT FACES // + + //Top/Bottom touch + if (bcx > rX && bcx < rX + rW){ + + //Top touch + if (b.v.y > 0) return {isTouching: true, location: TOP}; + + //Bottom touch + if (b.v.y < 0) return {isTouching: true, location: BOTTOM}; + } + + //Left/Right touch + if (bcy > rY && bcy < rY + rH){ + + //Left touch + if (b.v.x > 0) return {isTouching: true, location: LEFT}; + + //Right touch + if (b.v.x < 0) return {isTouching: true, location: RIGHT}; + + } + + // CORNERS // + + // BOTTOM Left/Right + if (i > 0 && i <= PI / 2) return {isTouching: true, location: BOTTOM_LEFT}; + + //LEFT Bottom/Top + if (i > PI / 2 && i <= PI) return {isTouching: true, location: TOP_LEFT}; + + //TOP Left/Right + if (i > PI && i <= PI + PI / 2) return {isTouching: true, location: TOP_RIGHT}; + + //RIGHT Bottom/Top + if (i > PI + PI / 2 && i <= TWO_PI) return {isTouching: true, location: BOTTOM_RIGHT}; + } + } + + } + + if (obj0.isEllipse && obj1.isEllipse){ + + //Ball 1 + var b1 = obj0; + + //Ball 2 + var b2 = obj1; + + //Balls are close to each other + if (b1.x + b1.r > b2.x - b2.r + && b1.x - b1.r < b2.x + b2.r + && b1.y + b1.r > b2.y - b2.r + && b1.y - b1.r < b2.y + b2.r){ + + var distance = sqrt(pow(b1.x - b2.x, 2) + pow(b1.y - b2.y, 2)); + + if (distance < b1.r + b2.r) return {isTouching: true}; + } + } + return {isTouching: false, location: 0}; +} + +function performCollision(obj0, obj1, collision){ + if (obj0.isEllipse){ + + var ball = obj0; + + //Ball collides with frameborder + if (obj1.isFrameborder){ + switch (collision.location){ + case BOTTOM: + ball.v.y *= -1; + break; + case LEFT: + case RIGHT: + ball.v.x *= -1; + break; + } + if (testMode && collision.location == TOP) ball.v.y *= -1; + ball.move(); + } + + //Ball collides with any brick + if (obj1.isBrick){ + switch (collision.location){ + case TOP: + case BOTTOM: + ball.v.y *= -1; + ball.move(); + return; + case LEFT: + case RIGHT: + ball.v.x *= -1; + ball.move(); + return; + case TOP_LEFT: + var cornerX = obj1.x; + var cornerY = obj1.y; + break; + case TOP_RIGHT: + var cornerX = obj1.x + obj1.width; + var cornerY = obj1.y; + break; + case BOTTOM_LEFT: + var cornerX = obj1.x; + var cornerY = obj1.y + obj1.height; + break; + case BOTTOM_RIGHT: + var cornerX = obj1.x + obj1.width; + var cornerY = obj1.y + obj1.height; + break; + } + + var nx = ball.x - cornerX; + var ny = ball.y - cornerY; + var length = sqrt(nx * nx + ny * ny); + nx /= length; + ny /= length; + + var projection = ball.v.x * nx + ball.v.y * ny; + ball.v.x = ball.v.x - 2 * projection * nx; + ball.v.y = ball.v.y - 2 * projection * ny; + + ball.move(); + + } + + //Ball collides with paddleboard + if (obj1.isPaddle){ + switch (collision.location){ + case TOP: + case TOP_LEFT: + case TOP_RIGHT: + ball.v.x = ball.calcVelocityX(obj1, ball.v.x); + ball.v.y = -ball.calcVelocityY(); + ball.move(); + return; + case LEFT: + case RIGHT: + ball.v.x *= -1; + ball.move(); + return; + case BOTTOM_LEFT: + var cornerX = obj1.x; + var cornerY = obj1.y + obj1.height; + break; + case BOTTOM_RIGHT: + var cornerX = obj1.x + obj1.width; + var cornerY = obj1.y + obj1.height; + break; + } + var nx = ball.x - cornerX; + var ny = ball.y - cornerY; + var length = sqrt(nx * nx + ny * ny); + nx /= length; + ny /= length; + + var projection = ball.v.x * nx + ball.v.y * ny; + ball.v.x = ball.v.x - 2 * projection * nx; + ball.v.y = ball.v.y - 2 * projection * ny; + + ball.move(); + } + + //Ball collides with other ball + if (obj1.isEllipse){ + + //Ball 1 + var b1 = obj0; + + //Ball 2 + var b2 = obj1; + + //Set mass equal to radius of each ball + b1.mass = b1.r; + b2.mass = b2.r; + + //Colliding angle of ball 1 to ball 2 using arc tan of both x and y differences + var collisionAngle = atan2((b2.y - b1.y), (b2.x - b1.x)); + + //Converting directions of velocity vector of balls into angles + var d1 = atan2(b1.v.y, b1.v.x); + var d2 = atan2(b2.v.y, b2.v.x); + + //Ignoring mass effects new velocites are simply magnitude multiplied with value of angle differences + var newXspeed1 = b1.v.mag * cos(d1 - collisionAngle); + var newYspeed1 = b1.v.mag * sin(d1 - collisionAngle); + var newXspeed2 = b2.v.mag * cos(d2 - collisionAngle); + var newYspeed2 = b2.v.mag * sin(d2 - collisionAngle); + + //According to the principle of linear momentum, kinetic energy stays the same after collision, so velocities are now related to masses + var finalXspeed1 = ((b1.mass - b2.mass) * newXspeed1 + b2.mass * 2 * newXspeed2) / (b1.mass + b2.mass); + var finalYspeed1 = newYspeed1; + var finalXspeed2 = (b1.mass * 2 * newXspeed1 + (b2.mass - b1.mass) * newXspeed2) / (b1.mass + b2.mass); + var finalYspeed2 = newYspeed2; + + //Values of collisionAngle + var cosAngle = cos(collisionAngle); + var sinAngle = sin(collisionAngle); + + //To also keep velocites relative to pure collisionAngle, subtract sin*x from cos*x and add sin*y to cos*y because coordSystem has y = 0 on the top + var u1x = cosAngle * finalXspeed1 - sinAngle * finalYspeed1; + var u1y = sinAngle * finalXspeed1 + cosAngle * finalYspeed1; + var u2x = cosAngle * finalXspeed2 - sinAngle * finalYspeed2; + var u2y = sinAngle * finalXspeed2 + cosAngle * finalYspeed2; + + //Set new velocities to both balls + b1.v.x = u1x; + b1.v.y = u1y; + b2.v.x = u2x; + b2.v.y = u2y; + + //Update magnitude + b1.v.mag = sqrt(pow(b1.v.x, 2) + pow(b1.v.y, 2)); + b2.v.mag = sqrt(pow(b2.v.x, 2) + pow(b2.v.y, 2)); + + + //Move balls one vx/vy forward to avoid double inverting collision detection + b1.x += b1.v.x; + b1.y += b1.v.y; + b2.x += b2.v.x; + b2.y += b2.v.y; + } + } +} + + + +function toTimeString(time, hoursWanted){ + + var time = floor(time / 10); + + var hs = String(floor(time % 100)); + var fs = String(floor((time / 100) % 60)); + + if (hoursWanted){ + var min = String(floor(((time / 100) / 60) % 60)); + var hr = String(floor(((time / 100) / 60) / 60)); + + if (hs.length < 2) hs = "0" + hs; + if (fs.length < 2) fs = "0" + fs; + if (min.length < 2) min = "0" + min; + if (hr.length < 2) hr = "0" + hr; + + var timeString = hr + ":" + min + ":" + fs + ":" + hs; + } else { + var min = String(floor(((time / 100) / 60) % 60)); + + if (hs.length < 2) hs = "0" + hs; + if (fs.length < 2) fs = "0" + fs; + if (min.length < 2) min = "0" + min; + + var timeString = min + ":" + fs + ":" + hs; + } + + + + return timeString; +} + + +function setCookie(name, value, years){ + var expires = ""; + if (years){ + var date = new Date(); + date.setTime(date.getTime() + (years * 365 * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + document.cookie = name + "=" + value + expires + "; path=/"; +} + +function getCookie(name){ + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++){ + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; +} + +function deleteCookies(){ + for (var i = 0; i < arguments.length; i++) setCookie(arguments[i], "", -1); +} + +function deleteAllCookies(){ + var cookies = document.cookie.split(";"); + for (var i = 0; i < cookies.length; i++) deleteCookies(cookies[i].split("=")[0]); +} + +Array.prototype.shuffle = function(){ + let currentIndex = this.length, temporaryValue, randomIndex; + while (0 != currentIndex) { + randomIndex = floor(random() * currentIndex); + currentIndex -= 1; + temporaryValue = this[currentIndex]; + this[currentIndex] = this[randomIndex]; + this[randomIndex] = temporaryValue; + } +} + +Array.prototype.clone = function() { + return this.slice(0); +}; + +Array.prototype.partitiate = function(dimensions){ + if (dimensions == 0) return this; + let parts = []; + while(this.length) parts.push(this.splice(0, round(pow(this.length, 1 / (1 + 1 / dimensions)))).partitiate(dimensions - 1)); + return parts; +} \ No newline at end of file diff --git a/public/scripts/sketch.js b/public/scripts/sketch.js new file mode 100644 index 0000000..3603188 --- /dev/null +++ b/public/scripts/sketch.js @@ -0,0 +1,434 @@ +let trees = [], + birds = [], + clouds = [], + humans = [], + ground = [], + time = 12, // in hours + cycleTime = 10, // in minutes + worldWidth = 40000, + viewPort = {x: Math.round(worldWidth / 2 - window.innerWidth / 2), y: 0, v: 20}, + waveDispo = 0, + cliffDispo = 25, + STRAIGHT_GROUND = 0, + SMALL_HILLS = 1, + LARGE_HILL = 2, + LAKE = 3, + gameIsRunning = false; + +function setup(){ + setFrameRate(120); + createCanvas(window.innerWidth, window.innerHeight); + background(160, 160, 255); + noStroke(); + $("#saveLink").hide(); + $("#fileBrowser").hide(); + $("#toolBarOpener").hide(); + $("#infoWrapper").hide(); + $("button").on("click", function(){this.blur();}); +} + +function createWorld(){ + $("#restoreWorld, #createWorld").hide(); + fill(80); + rect(width * 0.1, height * 0.5 - 25, width * 0.8, 50); + fill(0, 255, 0); + let x = 0, + createProgress = 0, + y = random(height * 0.7, height * 0.8), + treeDist = 0, + a = 0, c = 1, b = 0, + biome, + biomeX = 0, + biomeDistance = 1, + usedBiomes = [], + biomes = [STRAIGHT_GROUND, SMALL_HILLS, LARGE_HILL, LAKE], + isWooden; + + function create(){ + //Updating progressbar + createProgress++; + rect(width * 0.1, height * 0.5 - 25, width * 0.8 * (createProgress / (worldWidth * 2)), 50); + + //Main creating + if (ground.length < worldWidth){ + //Ground + let biomeRemainder = (x - biomeX) % biomeDistance; + if (biomeRemainder == 0){ + biome = random(biomes); + if (usedBiomes.includes(biome)) biome = random(biomes); + usedBiomes.push(biome); + biomeX = x; + isWooden = biome == LAKE ? false : random([true, false]); + biomeDistance = round(random(1000, 1500)); + a = 0, c = 1, b = 0; + } + switch (biome){ + case STRAIGHT_GROUND: + a = 0; + if (y < height * 0.3) a = -1; + if (y > height * 0.95) a = 1; + //y = noise(x / 100, y / 100) * height; + y += random(-0.25 - a, 0.25 - a); + break; + case SMALL_HILLS: + if (biomeRemainder == round(biomeDistance / 12) + || biomeRemainder == round(biomeDistance * 5 / 12) + || biomeRemainder == round(biomeDistance * 0.75)) + c = -1; + if (biomeRemainder == round(biomeDistance * 0.25) + || biomeRemainder == round(biomeDistance * 7 / 12) + || biomeRemainder == round(biomeDistance * 11 / 12)) + c = 1; + if (y < height * 0.3){ + c = -1; + b = -0.5; + } + else if (y > height * 0.9){ + c = 1; + b = 0.5; + } + else b = 0; + a -= random(0.005, 0.015) * c; + y += random(2 * a - b); + break; + case LARGE_HILL: + if (biomeRemainder == round(biomeDistance * 0.25)) c = -1; + if (biomeRemainder == round(biomeDistance * 0.75)) c = 1; + if (y < height * 0.3){ + c = -1; + b = -1; + } + else if (y > height * 0.9){ + c = 1; + b = 1; + } + else b = 0; + a -= random(0.0025, 0.0075) * c; + y += random(2 * a - b); + break; + } + ground[x] = {y: y, biome: biome, isWooden: isWooden}; + + //Trees + let treeH, treeW, valid; + treeDist++; + if (isWooden) valid = random(100) < 1 ? true : false; + if (!isWooden) valid = random(1000) < 0.5 ? true : false; + treeH = random(height * 0.3, height * 0.5), + treeW = random(40, 60); + if (treeDist < treeH * 0.25) valid = false; + try{if (ground[round(x - treeW / 2)].biome == LAKE || biomeRemainder > biomeDistance - treeW || y - treeH < height * 0.15) valid = false;} + catch(e){} + if (valid){ + trees.push(new Tree(x, treeW, treeH)); + treeDist = 0; + } + + //Clouds + let cloudY, cloudW, cloudH; + valid = random(1000) < 1 ? true : false; + if (valid){ + cloudY = random(0, 200), + cloudW = random(100, 500), + cloudH = random(50, 80); + clouds.push(new Cloud(x, cloudY, cloudW, cloudH)); + } + + //Birds + valid = random(1000) < 1 ? true : false; + if (valid){ + birds.push(new Bird(x)); + } + + x++; + } + + //Reset some variables + if (x == worldWidth){ + x = 0; + a = 0; + } + + //Ground details + if (ground.length == worldWidth){ + try{ + if (ground[x].biome != LAKE && ground[x + cliffDispo * 2].biome == LAKE){ + a++; + ground[x].y += a * a * 0.01; + } + else if (ground[x].biome != LAKE && ground[x - cliffDispo * 2].biome == LAKE){ + a--; + ground[x].y += a * a * 0.01; + } + else if (ground[x].biome == LAKE) a = cliffDispo * 2; + else a = 0; + }catch(e){} + x++; + } + + //End progress if bar is full + if (createProgress == worldWidth * 2){ + clearInterval(interval); + trees.shuffle(); + clouds.shuffle(); + birds.shuffle(); + $("#toolBarOpener").show(); + gameIsRunning = true; + } + if (createProgress < worldWidth * 2){ + setTimeout(create, 0); + } + } + let interval = setInterval(create, 0); +} + +function downloadWorld(){ + let newGround = [], + newTrees = [], + newBirds = [], + newClouds= [], + worldValues = [{time: time, viewPort: {x: viewPort.x, y: viewPort.y}}]; + for (let g of ground) newGround.push({y: round(g.y), b: g.biome, w: g.isWooden ? 1 : 0}); + for (let t of trees) newTrees.push({x: round(t.x), w: round(t.width), h: round(t.height), c: t.color}); + for (let b of birds) newBirds.push({x: round(b.x), y: round(b.y), v: round(b.v * 100) / 100, l: round(b.length)}); + for (let c of clouds) newClouds.push({x: round(c.x), y: round(c.y), w: round(c.width), h: round(c.height), b: round(c.brightness * 100) / 100}); + let str = + JSON.stringify(newGround.partitiate(1)) + ", " + + JSON.stringify(newTrees.partitiate(1)) + ", " + + JSON.stringify(newBirds.partitiate(1)) + ", " + + JSON.stringify(newClouds.partitiate(1)) + ", " + + JSON.stringify(worldValues); + let data = 'data:application/json;charset=utf-8,['+ encodeURIComponent(str) + "]"; + $("#saveLink").attr("href", data); + $("#saveLink")[0].click(); +} + +function uploadWorld(){ + gameIsRunning = false; + let files = document.getElementById("fileBrowser").files; + let fr = new FileReader(), recovery = {ground: ground, trees: trees, birds: birds, clouds: clouds, time: time, viewPort: viewPort}; + fr.onload = function(e){ + try{ + let data = JSON.parse(e.target.result); + let newGround = data[0].departitiate(); + let newTrees = data[1].departitiate(); + let newBirds = data[2].departitiate(); + let newClouds = data[3].departitiate(); + + ground = [], trees = [], birds = [], clouds = []; + + for (let g of newGround) ground.push({y: g.y, biome: g.b, isWooden: g.w == 1 ? true : false}); + for (let t of newTrees){ + let i = newTrees.indexOf(t); + trees.push(new Tree(t.x, t.w, t.h)); + trees[i].color = t.c; + } + for (let b of newBirds){ + let i = newBirds.indexOf(b); + birds.push(new Bird(b.x)); + birds[i].y = b.y; + birds[i].v = b.v; + birds[i].length = b.l; + } + for (let c of newClouds){ + let i = newClouds.indexOf(c); + clouds.push(new Cloud(c.x, c.y, c.w, c.h)); + clouds[i].brightness = c.b; + } + + time = data[4][0].time; + viewPort = {x: data[4][0].viewPort.x, y: data[4][0].viewPort.y, v: recovery.viewPort.v}; + $("#restoreWorld, #createWorld").hide(); + $("#toolBarOpener").show(); + + } catch(e){ + ground = recovery.ground; + trees = recovery.trees; + birds = recovery.birds; + clouds = recovery.clouds; + time = recovery.time; + viewPort = recovery.viewPort; + waveDispo = recovery.waveDispo; + window.alert("Data is damaged!"); + } + + if (ground.length > 0) gameIsRunning = true; + else {$("#restoreWorld, #createWorld").show();} + } + fr.readAsText(files.item(0)); +} + +function draw(){ + if (gameIsRunning){ + clear(); + translate(-viewPort.x, -viewPort.y); + updateUnits(); + updateGround(); + updateTime(); + checkViewPort(); + } +} + +function updateUnits(){ + //Sky + background(160, 160, 255); + + //Clouds + for (let c of clouds){ + c.move(); + c.show(); + } + + //Birds + for (let b of birds){ + b.move(); + b.show(); + } + + //Trees + for (let t of trees){ + t.show(); + } + + //Humans + for (let h of humans){ + h.move(); + h.show(); + } +} + +function updateGround(){ + let x, y, lake = false, grassH = 20, waveH = 2; + waveDispo += 1; + waveDispo %= 8 * PI; + function wave(x){ + return sin((0.25 * (x - waveDispo))) * 3; + } + + // grass + fill(100, 200, 100); + beginShape(); + for (x = viewPort.x; x <= viewPort.x + width; x++){ + //console.log(x); + let g = ground[x]; + if (g.biome == LAKE) + vertex(x, height); + else + vertex(x, g.y); + } + for (x = viewPort.x + width; x >= viewPort.x; x--){ + let g = ground[x]; + if (g.biome == LAKE) + vertex(x, height); + else + vertex(x, g.y + grassH); + } + endShape(); + + // water + fill(100, 100, 255); + beginShape(); + for (x = viewPort.x; x <= viewPort.x + width; x++){ + let g = ground[x]; + if (g.biome == LAKE) + vertex(x, g.y + grassH / 2 + waveH + cliffDispo + wave(x)); + else + vertex(x, height); + } + for (x = viewPort.x + width - 1; x >= viewPort.x; x--){ + vertex(x, height); + } + endShape(); + + // water --> waves + fill(50, 50, 255); + beginShape(); + for (x = viewPort.x; x <= viewPort.x + width; x++){ + let g = ground[x]; + if (g.biome == LAKE) + vertex(x, g.y + grassH / 2 + cliffDispo + wave(x)); + else + vertex(x, height); + } + for (x = viewPort.x + width - 1; x >= viewPort.x; x--){ + let g = ground[x]; + if (g.biome == LAKE) + vertex(x, g.y + grassH / 2 + cliffDispo + wave(x) + waveH); + else + vertex(x, height); + } + endShape(); + + // dirt + fill(80, 60, 40); + beginShape(); + for (x = viewPort.x; x <= viewPort.x + width; x++){ + let g = ground[x]; + if (g.biome == LAKE) + vertex(x, height); + else + vertex(x, g.y + grassH); + } + for (x = viewPort.x + width; x >= viewPort.x; x--){ + vertex(x, height); + } + endShape(); +} + +function updateTime(){ + function getDayBrightness(){ + let value = sin(PI / 12 * (time - 8)) + 0.25; + value = value > 1 ? 1 : value; + value = value < 0.1 ? 0.1 : value; + return value; + } + let timeTick = 24 / (60 * cycleTime * frameRate()); + time += (timeTick == Infinity ? 0 : timeTick); + fill(0, 0, 0, 255 - 255 * getDayBrightness()); + rect(viewPort.x, viewPort.y, width, height); +} + +function checkViewPort(){ + let scrollBegin = 100; + if (winMouseX <= scrollBegin && viewPort.x > 0) + viewPort.x -= round(viewPort.v * (scrollBegin - winMouseX) / scrollBegin); + if (winMouseX >= width - scrollBegin && viewPort.x < worldWidth - width - viewPort.v) + viewPort.x += round(viewPort.v * (winMouseX - (width - scrollBegin)) / scrollBegin); + if (viewPort.x < 0) viewPort.x = 0; + if (viewPort.x >= worldWidth + width) viewPort.x = worldWidth - width - 1; +} + +function togglePause(obj){ + gameIsRunning = !gameIsRunning; + if (gameIsRunning) $(obj).html("||"); + else $(obj).html("►"); +} + +function toggleToolBar(obj){ + if ($(obj).css("top") == "0px"){ + $(obj).animate({top: 103}, {top: "easeOut"}); + $("#toolBar").animate({top: 0}, {top: "easeOut"}); + } + if ($(obj).css("top") == "103px"){ + $(obj).animate({top: 0}, {top: "easeOut"}); + $("#toolBar").animate({top: -103}, {top: "easeOut"}); + } +} + +function toggleInfo(){ + $("#pauseButton")[0].click(); + $("#infoWrapper").toggle(); +} + +Array.prototype.departitiate = function(){ + let newArr = []; + for (let i = 0; i < this.length; i++){ + let a = this[i]; + for (let j = 0; j < a.length; j++){ + let object = a[j]; + newArr.push(object); + //for (let k = 0; k < b.length; k++){} + } + } + return newArr; +} \ No newline at end of file diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..2fad39a --- /dev/null +++ b/public/styles.css @@ -0,0 +1,103 @@ +a:link, a:hover, a:active, a:visited{color: #000;} + +body{margin: 0; padding: 0; overflow-y:hidden; overflow-x: hidden;} + +canvas{margin: 0; padding: 0; border: none; display: block;} + +button{cursor: pointer;} + +#toolBarOpener{ + position: absolute; + display: block; + background-color: #FFF; + left: calc((100% - 50px) / 2); + top: 0; + width: 50px; + height: 20px; + border: none; + border-radius: 0 0 5px 5px; +} + +#toolBar{ + position: absolute; + display: block; + background-color: #F90; + left: calc((100% - 371px) / 2); + top: -103px; + width: 335px; + height: 70px; + padding: 15px; + border-radius: 0 0 20px 20px; + border: 3px solid #000; + border-top: none; +} + +.toolBarEntry{ + width: 70px; + height: 70px; + margin: 0 5px 0 5px; + border: 3px solid #000; + border-radius: 10px; + background-color: #FFF; + padding: 0; + font-size: 50px; +} + +#downloadButton{color: #0C0;} +#uploadButton{color: #C00;} +#infoButton{color: #00F;} + +#createWorld{ + position: absolute; + top: calc(100% / 2 - 50px); + left: calc(100% / 2 - 220px); + width: 200px; + height: 100px; + border: 5px solid #000; + border-radius: 10px; + font-size: 25px; +} + +#restoreWorld{ + position: absolute; + top: calc(100% / 2 - 50px); + left: calc(100% / 2 + 20px); + width: 200px; + height: 100px; + border: 5px solid #000; + border-radius: 10px; + font-size: 25px; +} + +#infoWrapper{ + position: absolute; + width: 100%; + height: 100%; + z-index: 5; + background-color: rgba(0, 0, 0, 0.3); +} + +#info{ + position: absolute; + width: 600px; + height: 400px; + margin: auto; + top: 0; + bottom: 0; + left: 0; + right: 0; + border: 7px solid #333; + border-radius: 30px; + font-size: 25px; + background-color: #999; +} + +#info button{ + width: 100px; + height: 50px; + margin: 30px; + background-color: #0C0; + border: 3px solid #080; + border-radius: 5px; + font-size: 30px; +} diff --git a/public/thumbnail.png b/public/thumbnail.png new file mode 100644 index 0000000..c6ee343 Binary files /dev/null and b/public/thumbnail.png differ