var translateration = 0; var fps = 0; var isDebug = false; var draw_grid = false; var plummetingSoundNotPlayedBefore = false; var text_hor_unit; var text_vert_unit; var sfx = {}; var mus = {}; var skydecay = 0; var fps_recent_values = []; var random_values = []; // from 0 to 1 for (i = 0; i < 500; i++) { random_values[i] = Math.random(); } var memorized_platform; var amounts = { tree: 200, cloud: 200, mountain: 200, canyon: 80, collectable: 23, enemies: 30, platforms: 50, }; var env = { skyColor: 0, //assigned at setup() floorPos_y: ((512 + 256) * 3) / 4, groundColor: "#7F562A", end_x: 3000, state: "INTRO", isSoundEnabled: false, // never set it true. it causes premature playing of the game sfx and music before loading. isMusicEnabled: false, }; class Entity { constructor(x, y) { (this.x = x), (this.y = y), (this.size = 1.5), (this.speed = 8), (this.score = 0), (this.lives = 3), (this.health = 100), this.prejump_y, (this.gravity = 1.5); this.vert_speed = -1; // Is negative. (this.state = { isLeft: false, isRight: false, isFalling: false, isJumping: false, isPlummeting: false, isDescending: false, isAscending: false, }), (this.projectiles = []); } resetValues() { this.size = 1.5; this.speed = 7; } } var gameChar; var flagpole = { x: env.end_x, isReached: false, size_vert: 4, size_hor: 7, cell_size: 20, cell_size_v: 20, cell_size_h: 20, }; class Cloud { constructor(x, y, size) { this.x = x; this.y = y; this.size = size; } } class Canyon { constructor(x, width) { this.x = x; this.width = width; } } class Collectable { constructor(x, y, size, worth) { this.x = x; this.y = y; this.size = size; this.worth = worth; } } class Enemy extends Entity { constructor(x, y, damage, sin_x_speed, sin_y_speed) { super(x, y); this.damage = damage; this.sin_x_speed = sin_x_speed; this.sin_y_speed = sin_y_speed; } collidesWith(whoCollidesWith) { let temp_x = this.x; let temp_y = this.y; this.x = this.x + 25 * Math.sin(frameCount / this.sin_x_speed) - 15; this.y = this.y + 50 * Math.sin(frameCount / this.sin_y_speed) - 20; if ( Math.abs(whoCollidesWith.x - this.x) < 24 * this.size && Math.abs(whoCollidesWith.y - this.y) < 24 * this.size ) { return true; } this.y = temp_y; this.x = temp_x; return false; } drawEnemy() { // lazy so its just a game character model, but with a black hat and no body. let temp_y = this.y; let temp_x = this.x; this.x = this.x + 25 * Math.sin(frameCount / this.sin_x_speed); this.y = this.y + 50 * Math.sin(frameCount / this.sin_y_speed); drawGameChar(this.x, this.y, 1, this.size, false, color(this.damage, 100)); this.y = temp_y; this.x = temp_x; } } class Platform { constructor(left_x, right_x, y) { this.left_x = left_x; this.right_x = right_x; this.y = y; } isInXBorders(whoCollidesWith) { return whoCollidesWith.x > this.left_x && whoCollidesWith.x < this.right_x; } isUnder(whoCollidesWith) { return this.isInXBorders(whoCollidesWith) && whoCollidesWith.y < this.y; } isOver(whoCollidesWith) { return this.isInXBorders(whoCollidesWith) && whoCollidesWith.y > this.y; } drawPlatform() { push(); rectMode(CORNERS); fill(env.groundColor); rect(this.left_x, this.y, this.right_x, this.y + 10); pop(); } } var mountains_x = []; var trees_x = []; var enemies = []; var clouds = []; var canyons = []; var collectables = []; var platforms = []; var cnv; var win_img; var gameover_img; function preload() { soundFormats("mp3", "ogg"); sfx.enemy = loadSound("../assets/sfx/enemy.ogg"); sfx.falling = loadSound("../assets/sfx/falling.ogg"); sfx.gameover = loadSound("../assets/sfx/gamover.ogg"); sfx.jumping = loadSound("../assets/sfx/jumping.ogg"); sfx.levelcomplete = loadSound("../assets/sfx/levelcomplete.ogg"); sfx.plummeting = loadSound("../assets/sfx/plummeting.ogg"); sfx.coin = loadSound("../assets/sfx/coin.mp3"); /* "Midnight Tale", "Take a Chance" and "SCP-x5x" Kevin MacLeod (incompetech.com) Licensed under Creative Commons: Attribution 4.0 License http://creativecommons.org/licenses/by/4.0/ */ mus.level = loadSound("../assets/Midnight Tale.mp3"); mus.win = loadSound("../assets/Take A Chance.mp3"); mus.gameover = loadSound("../assets/SCP-x5x.mp3"); win_img = loadImage( "https://static.wikia.nocookie.net/someordinarygamers/images/8/88/Bp_1401520973_youre_winner.png" ); gameover_img = loadImage("../assets/gameover.png"); } function drawFlagpole() { push(); strokeWeight(0); fill(0); translate(translateration, 0); if (flagpole.isReached && flagpole.cell_size_h > -flagpole.cell_size) flagpole.cell_size_h--; // the flagpole pole rect(flagpole.x, env.floorPos_y / 2, 2, env.floorPos_y / 2); // the flagpole flag for (i = 0; i < flagpole.size_hor; i++) { for (j = 0; j < flagpole.size_vert; j++) { fill(((i + j) % 2) * 255); rect( flagpole.x + flagpole.cell_size_h * i, env.floorPos_y / 2 + flagpole.cell_size_v * j, flagpole.cell_size_h, flagpole.cell_size_v ); } } pop(); } function drawGameChar(x, y, sprite, scale, has_body, hat_color, hand, width_mod, height_mod, jump_shift) { // Note: // The drawing starts at bottom-middlecenter! not at top-left! // The aspect ratio is 5:8. if (has_body == undefined) has_body = true; if (hat_color == undefined) hat_color = "red"; if (scale == undefined) { scale = 1; } if (width_mod == undefined) { width_mod = 1; } if (height_mod == undefined) { height_mod = 1; } if (jump_shift == undefined) { jump_shift = 12; } var ax_step = 5 * scale * width_mod; var ay_step = 8 * scale * height_mod; jump_shift = jump_shift * scale; function _drawDefaultHat() { strokeWeight(1); stroke(0); fill(hat_color); ellipse(x, y - ay_step * 6, ax_step * 10, ay_step * 3); ellipse(x, y - ay_step * 6, ax_step * 4, ay_step * 1); // x +0.5 * scale is a minor fix of misalignment. May become a bug. strokeWeight(1); beginShape(); vertex(x - ax_step * 2, y - ay_step * 6); vertex(x, y - ay_step * 10); vertex(x + ax_step * 2, y - ay_step * 6); endShape(); } function _drawDefaultBody(jumping) { if (jumping) { strokeWeight(1); stroke(1); fill("brown"); triangle(x - ax_step * 2, y - ay_step * 1.5, x, y - ay_step * 4, x + ax_step * 2, y - ay_step * 1.5); } else { strokeWeight(1); stroke(1); fill("brown"); triangle(x - ax_step * 2, y, x, y - ay_step * 4, x + ax_step * 2, y); } } function _drawDefaultHand(direction) { switch (direction) { // to left case 0: { line(x, y - ay_step * 2.5, x + ax_step * 0.8, y - ay_step * 1); break; } // relaxed case 1: { line(x, y - ax_step * 4, x, y - ay_step); break; } // to right case 2: { line(x, y - ay_step * 2.5, x - ax_step * 0.8, y - ay_step * 1); break; } // relaxed; doubled for easy animation cycling case 3: { line(x, y - ax_step * 4, x, y - ay_step); break; } // relaxed default: { line(x, y - ax_step * 2.5, x, y - ay_step); break; } } } switch (sprite) { // Standing, facing frontwards case 1: { // Body if (has_body) _drawDefaultBody(); // Head fill(0); ellipse(x, y - ay_step * 5, ax_step * 6, ay_step * 4); // Eyes fill(255); ellipse(x - ax_step * 1.2, y - ay_step * 4.2, ax_step / 1.5, ay_step / 2); ellipse(x + ax_step * 1.2, y - ay_step * 4.2, ax_step / 1.5, ay_step / 2); // Hat _drawDefaultHat(); break; } // Jumping, facing forwards case 2: { y -= jump_shift; // Body if (has_body) _drawDefaultBody(true); // Hands. (Hands!) line(x - ax_step * 1, y - ay_step * 2.5, x - ax_step * 3.2, y - ay_step * 3.2); line(x + ax_step * 1, y - ay_step * 2.5, x + ax_step * 3.2, y - ay_step * 3.2); // Head fill(0); ellipse(x, y - ay_step * 5, ax_step * 6, ay_step * 4); // Eyes fill(255); ellipse(x - ax_step * 1, y - ay_step * 3.8, ax_step / 1.5, ay_step / 2); ellipse(x + ax_step * 1, y - ay_step * 3.8, ax_step / 1.5, ay_step / 2); // Hat _drawDefaultHat(); break; } // Walking left case 3: { // Body if (has_body) _drawDefaultBody(); // Hand. (Hand!) if (has_body) _drawDefaultHand(hand); // Head fill(0); ellipse(x, y - ay_step * 5, ax_step * 6, ay_step * 4); // Eyes fill(255); ellipse(x + ax_step * 1.2, y - ay_step * 4.2, ax_step / 1.5, ay_step / 2); // Hat _drawDefaultHat(); break; } // Walking right case 4: { // Body if (has_body) _drawDefaultBody(); // Hand. (Hand!) if (has_body) _drawDefaultHand(hand); // Head fill(0); ellipse(x, y - ay_step * 5, ax_step * 6, ay_step * 4); // Eyes fill(255); ellipse(x - ax_step * 1.2, y - ay_step * 4.2, ax_step / 1.5, ay_step / 2); // Hat _drawDefaultHat(); break; } // Jumping left case 5: { y -= jump_shift; // Body if (has_body) _drawDefaultBody(true); // Hands. (Hands!) line(x - ax_step * 1, y - ay_step * 2.5, x - ax_step * 3.2, y - ay_step * 3.2); line(x + ax_step * 1, y - ay_step * 2.5, x + ax_step * 3.2, y - ay_step * 3.2); // Head fill(0); ellipse(x, y - ay_step * 5, ax_step * 6, ay_step * 4); // Eyes fill(255); ellipse(x - ax_step * 1, y - ay_step * 3.8, ax_step / 1.5, ay_step / 2); // Hat _drawDefaultHat(); break; } // Jumping right case 6: { y -= jump_shift; // Body if (has_body) _drawDefaultBody(true); // Hands. (Hands!) line(x - ax_step * 1, y - ay_step * 2.5, x - ax_step * 3.2, y - ay_step * 3.2); line(x + ax_step * 1, y - ay_step * 2.5, x + ax_step * 3.2, y - ay_step * 3.2); // Head fill(0); ellipse(x, y - ay_step * 5, ax_step * 6, ay_step * 4); // Eyes fill(255); ellipse(x + ax_step * 1, y - ay_step * 3.8, ax_step / 1.5, ay_step / 2); // Hat _drawDefaultHat(); break; } // Error default: fill("red"); text("Character error - No or wrong sprite number provided", x, y); break; } } function drawTree(x, y, scale, type, hasLeaves) { // x, y are the middle bottom of the tree. noStroke(); fill(255); if (hasLeaves == undefined) { hasLeaves = true; } if (scale == undefined) { scale = 1; } if (type == 0) { fill(110, 87, 20); triangle(x - 15 * scale, y, x, y - 150 * scale, x + 15 * scale, y); fill(63, 72, 52); if (hasLeaves) triangle(x - 45 * scale, y - 45 * scale, x, y - 120 * scale, x + 45 * scale, y - 45 * scale); if (hasLeaves) triangle(x - 45 * scale, y - 85 * scale, x, y - 180 * scale, x + 45 * scale, y - 85 * scale); } else if (type == 1) { fill(147, 105, 7); triangle(x - 10 * scale, y, x, y - 120 * scale, x + 10 * scale, y); fill(78, 61, 31); if (hasLeaves) { ellipse(x, y - 50 * scale, 80 * scale, 30 * scale); ellipse(x, y - 70 * scale, 100 * scale, 30 * scale); ellipse(x, y - 90 * scale, 80 * scale, 30 * scale); ellipse(x, y - 110 * scale, 40 * scale, 30 * scale); } } } function drawCloud(cloud) { if (cloud.size == undefined) { scale = 1; } noStroke(); fill(200); ellipse(cloud.x - 20, cloud.y - 10, 50 * cloud.size, 30 * cloud.size); fill(220); ellipse(cloud.x + 20, cloud.y - 20, 70 * cloud.size, 50 * cloud.size); fill(255); ellipse(cloud.x, cloud.y, 90 * cloud.size, 40 * cloud.size); ellipse(cloud.x + 45, cloud.y - 10, 50 * cloud.size, 35 * cloud.size); } function drawMountain(x, m_width, m_height, skew) { noStroke(); fill(60); triangle(x - m_width, env.floorPos_y, x + skew, m_height, x + m_width, env.floorPos_y); fill(90); triangle(x - m_width, env.floorPos_y, x + skew, m_height, x - m_width / 1.5, env.floorPos_y); fill(170); triangle( (4 * x - m_width + 3 * skew) / 4, (env.floorPos_y + 3 * m_height) / 4, x + skew, m_height, (4 * x + m_width + 3 * skew) / 4, (env.floorPos_y + 3 * m_height) / 4 ); } function drawCanyon(canyon) { noStroke(); let x = canyon.x; let c_width = canyon.width; fill(90, 30, 0); quad( x - c_width / 2, env.floorPos_y, x + c_width / 2, env.floorPos_y, x + c_width / 2, height, x - c_width / 2, height ); fill("blue"); quad( x - c_width / 20, env.floorPos_y, x + c_width / 20, env.floorPos_y, x + c_width / 20, height, x - c_width / 20, height ); fill(30); quad( x - c_width / 2, env.floorPos_y, x + c_width / 2, env.floorPos_y, x + c_width / 10, env.floorPos_y + env.floorPos_y / 40, x - c_width / 10, env.floorPos_y + env.floorPos_y / 40 ); } function drawCollectable(collectable) { let x = collectable.x; let y = collectable.y; let size = collectable.size; let worth = collectable.worth; strokeWeight(0.5); fill(253, 195, 52); ellipse(x, y, 3.5 * size, 3.5 * size); fill(171); ellipse(x, y, 3 * size, 3 * size); fill(253, 195, 52); ellipse(x, y, 2.5 * size, 2.5 * size); fill(200, 150, 30); textSize(size * 1.5); textAlign(CENTER, CENTER); fill(0); text(worth, x, y); textSize(12); textAlign(LEFT, TOP); } function drawSky() { background(env.skyColor); } function drawAudioSymbol() { push(); fill(0); strokeWeight(4); triangle( width - text_vert_unit * 5, text_hor_unit * 7, width - text_vert_unit * 4, text_hor_unit * 5, width - text_vert_unit * 4, text_hor_unit * 9 ); if (env.isSoundEnabled) { arc(width - text_vert_unit * 3, text_hor_unit * 7, text_hor_unit, text_vert_unit * 1.5, (3 * PI) / 2, PI / 2); arc(width - text_vert_unit * 3.5, text_hor_unit * 7, text_hor_unit, text_vert_unit, (3 * PI) / 2, PI / 2); } textSize(text_vert_unit / 2); strokeWeight(0); pop(); } function drawMusicSymbol() { push(); fill(0); strokeWeight(4); rectMode(CORNERS); if (env.isMusicEnabled) { line(width - text_vert_unit * 4.5, text_hor_unit * 15, width - text_vert_unit * 4.5, text_hor_unit * 11); line(width - text_vert_unit * 2.5, text_hor_unit * 14, width - text_vert_unit * 2.5, text_hor_unit * 10); line(width - text_vert_unit * 4.5, text_hor_unit * 11, width - text_vert_unit * 2.5, text_hor_unit * 10); } ellipse(width - text_vert_unit * 4.5, text_hor_unit * 15, text_vert_unit / 2); ellipse(width - text_vert_unit * 2.5, text_hor_unit * 14, text_vert_unit / 2); pop(); } function playSound(sound, volume, isMusicEnabled) { if (volume == undefined) volume = 1; if (env.isSoundEnabled) { sound.play(0, 1, volume); } else { sound.stop(); } } function playMusic(sound, volume) { if (volume == undefined) volume = 1; if (env.isMusicEnabled) { sound.play(0, 1, volume); } else { sound.stop(); } } function drawMountains() { push(); translate(translateration * 0.3, 0); for (i = 0; i < mountains_x.length; i++) { drawMountain( mountains_x[i], 100 + 200 * random_values[i + 121], 400 - 50 * random_values[i + 211], 50 * random_values[i + 8] ); } pop(); } function drawClouds(areMoving) { push(); translate(translateration * 0.6, 0); for (i = 0; i < clouds.length; i++) { push(); if (areMoving) translate(frameCount * random_values[i], 0); drawCloud(clouds[i]); pop(); } pop(); } function drawCanyons() { push(); translate(translateration, 0); for (i = 0; i < canyons.length; i++) { drawCanyon(canyons[i]); fill(200, 150, 0); } pop(); } function drawTrees() { push(); translate(translateration, 0); for (i = 0; i < trees_x.length; i++) { drawTree(trees_x[i], env.floorPos_y, 0.5 + random_values[i], Math.round(trees_x[i]) % 2); } pop(); } function drawCollectables() { push(); translate(translateration, 0); for (i = 0; i < collectables.length; i++) { drawCollectable(collectables[i]); } pop(); } function drawPlatforms() { push(); translate(translateration, 0); for (i = 0; i < platforms.length; i++) { platforms[i].drawPlatform(); } pop(); } function manageEnemies() { push(); translate(0, 0); for (i = 0; i < enemies.length; i++) { enemies[i].drawEnemy(); for (j = 0; j < gameChar.projectiles.length; j++) { if (enemies[i].collidesWith(gameChar.projectiles[j])) { enemies.splice(i, 1); gameChar.projectiles.splice(j, 1); i = 0; j = 0; playSound(sfx.enemy); } } if (enemies[i].collidesWith(gameChar) && env.state == "LEVEL") { background(255 * random(), 255 * random(), 255 * random()); playSound(sfx.enemy, enemies[i].damage / 10); gameChar.state.isPlummeting = true; } } pop(); } function drawInterface() { push(); translate(-translateration, 0); fill("black"); rect(0, 0, width, text_vert_unit * 2); stroke("red"); line(0, text_vert_unit * 2, width, text_vert_unit * 2); fill("red"); textFont("Courier New"); textSize(text_vert_unit); text("Score: " + gameChar.score, width - text_vert_unit * 7, text_hor_unit * 2); text("Lives: " + gameChar.lives, width - text_vert_unit * 14, text_hor_unit * 2); // Debug things drawAudioSymbol(); drawMusicSymbol(); if (isDebug) debug(); pop(); } function shootProjectile() { playSound(sfx.coin, 0, 1, 0.5); let projectile = { color: "red", x: gameChar.x, y: gameChar.y - 45, direction: 0 }; if (gameChar.state.isLeft) { projectile.direction = -15; gameChar.projectiles.push(projectile); } else { projectile.direction = 15; gameChar.projectiles.push(projectile); } } function updateProjectiles() { for (i = 0; i < gameChar.projectiles.length; i++) { push(); stroke(gameChar.projectiles[i].color); strokeWeight(4); line( gameChar.projectiles[i].x, gameChar.projectiles[i].y, gameChar.projectiles[i].x - 30, gameChar.projectiles[i].y ); pop(); gameChar.projectiles[i].x += gameChar.projectiles[i].direction; if (gameChar.projectiles[i].x > width - translateration || gameChar.projectiles[i].x < 0 - translateration) { gameChar.projectiles.splice(i, 1); } } } function manageMovement() { if (gameChar.state.isPlummeting) { gameChar.y += gameChar.speed / 2; gameChar.size *= 0.98; gameChar.speed *= 0.96; if (gameChar.size < 0.2) restart(1); } else if (!(gameChar.state.isJumping || gameChar.state.isFalling)) { // Checking if the character is beyond the platform, then making them fall. if (memorized_platform != undefined && !memorized_platform.isInXBorders(gameChar)) { gameChar.state.isFalling = true; memorized_platform = undefined; } else { // Falling into canyons if (isOverCanyon(gameChar) && memorized_platform == undefined) { gameChar.state.isPlummeting = true; if (!plummetingSoundNotPlayedBefore) { playSound(sfx.plummeting); plummetingSoundNotPlayedBefore = true; } } if (gameChar.state.isAscending && gameChar.y > floorPos_y) { gameChar_y -= gameChar.speed; } if (gameChar.state.isDescending && gameChar_y < height) { gameChar_y += gameChar.speed; } if (gameChar.state.isLeft) { gameChar.x -= gameChar.speed; } if (gameChar.state.isRight) { gameChar.x += gameChar.speed; } } } else if (gameChar.state.isJumping) { // Forget the platform as it is irrelevant when we gain height. memorized_platform = undefined; gameChar.y += gameChar.vert_speed; gameChar.vert_speed += gameChar.gravity; if (gameChar.vert_speed > 0) { gameChar.state.isJumping = false; gameChar.state.isFalling = true; playSound(sfx.falling); } if (gameChar.state.isLeft) { gameChar.x -= gameChar.speed; } if (gameChar.state.isRight) { gameChar.x += gameChar.speed; } } else if (gameChar.state.isFalling) { // Checking is a platform under the player, and therefore, if it is, save it and check the next frame did the character // go under the platform - if so, end them on the platform. for (i = 0; i < platforms.length; i++) { if (platforms[i].isUnder(gameChar)) memorized_platform = platforms[i]; } if (memorized_platform != undefined && memorized_platform.isOver(gameChar)) { gameChar.y = memorized_platform.y; gameChar.state.isJumping = false; gameChar.state.isFalling = false; } else { gameChar.y += gameChar.vert_speed; gameChar.vert_speed += gameChar.gravity; if (gameChar.y >= env.floorPos_y + 50) { gameChar.y = env.floorPos_y + 50; gameChar.state.isJumping = false; gameChar.state.isFalling = false; } if (gameChar.state.isLeft) { gameChar.x -= gameChar.speed; } if (gameChar.state.isRight) { gameChar.x += gameChar.speed; } } } // Borders of the world check if (gameChar.x <= 50) { gameChar.x = 50; } } // Fun fact - the following function is made using ChatGPT! function drawIsogonalBrickWall(x, y, size, hSkew, vSkew) { push(); translate(translateration, 0); // Set the fill color of the bricks to a redish fill(150, 100, 110); // Calculate the width and height of each brick let brickWidth = size / 5; let brickHeight = size / 10; // Draw the bricks in a 5x10 grid, with a small gap between each brick for (let i = 0; i < 5; i++) { for (let j = 0; j < 10; j++) { let xPos = x + i * (brickWidth + 1) + j * hSkew; let yPos = y + j * (brickHeight + 1) + i * vSkew; rect(xPos, yPos, brickWidth, brickHeight); } } pop(); } function checkCollectable(whoCollidesWith) { for (i = 0; i < collectables.length; i++) { if ( Math.abs(whoCollidesWith.x - collectables[i].x) < collectables[i].size * 2 && Math.abs(whoCollidesWith.y - collectables[i].y) < collectables[i].size * 2 ) { whoCollidesWith.score += collectables[i].worth; collectables.splice(i, 1); playSound(sfx.coin); } } } function isOverCanyon(who) { for (i = 0; i < canyons.length; i++) { if (Math.abs(who.x - canyons[i].x) < 43 + 25 * random_values[i + 124]) { return true; } } return false; } function scrolling(centerOnWho) { if (centerOnWho.x > width - width / 2.5 - translateration) { translateration -= centerOnWho.speed; } else if (centerOnWho.x < width / 2.5 - translateration) { translateration += centerOnWho.speed; } } function checkFlagpole(withWho) { if (withWho.x > env.end_x) { flagpole.isReached = true; env.state = "WIN"; withWho.isWinner = true; playSound(sfx.levelcomplete); } } function restart(decreaseLives) { if (env.state != "LEVEL") window.location.reload(); // Reloads the web page. gameChar.state.isPlummeting = false; gameChar.lives -= decreaseLives; if (gameChar.lives < 1) { env.state = "GAMEOVER"; playSound(sfx.gameover); return; } gameChar.x = width / 2; gameChar.y = env.floorPos_y + 50; gameChar.resetValues(); translateration = -gameChar.x; plummetingSoundNotPlayedBefore = false; } function getSpriteNumber() { if (gameChar.state.isJumping && gameChar.state.isLeft) return 5; else if (gameChar.state.isJumping && gameChar.state.isRight) return 6; else if (gameChar.state.isJumping) return 2; else if (gameChar.state.isFalling && gameChar.state.isLeft) return 5; else if (gameChar.state.isFalling && gameChar.state.isRight) return 6; else if (gameChar.state.isFalling) return 2; else if (gameChar.state.isPlummeting) return 2; else if (gameChar.state.isLeft) return 4; else if (gameChar.state.isRight) return 3; else if (gameChar.state.isDescending) return 1; else return 1; } function debug() { function _drawGridLines() { push(); console.log("works"); stroke(255); strokeWeight(1); for (i = text_vert_unit; i < width; i += text_vert_unit) { line(0, i, width, i); } for (j = text_hor_unit; j < height * 2; j += text_hor_unit) { line(j, 0, j, height); } pop(); } this.drawGrid = false; if (gameChar.state.isJumping) text("JUMPING", text_vert_unit * 6, text_hor_unit * 2); if (gameChar.state.isFalling) text("FALLING", text_vert_unit * 6, text_hor_unit * 2); if (gameChar.state.isPlummeting) text("PLUMMETING", text_vert_unit * 11, text_hor_unit * 2); if (frameCount % 2 == 0) { fps = Math.round(frameRate() * 10) / 10; if (fps_recent_values.length < 200) fps_recent_values.push(fps); else fps_recent_values.splice(0, 1); fps_recent_values.push(fps); } fill("red"); text(fps, text_vert_unit, text_hor_unit * 2); stroke("black"); beginShape(LINES); for (i = 1; i < fps_recent_values.length; i++) { vertex((text_vert_unit + i) * 1.5, (text_hor_unit * 6 - fps_recent_values[i]) * 2); } endShape(); text(gameChar.x, text_vert_unit * 18, text_hor_unit * 2); if (draw_grid) drawGridLines(); } function createEnemies() { enemies[0] = new Enemy(1100, 600, random(10, 50), random(20, 50), random(10, 30)); for (let i = 1; i < amounts.enemies; i++) { let elevation; if (random_values[i] <= 1 / 3) elevation = 350; else if (random_values[i] > 1 / 3 && random_values <= 2 / 3) elevation = 300; else elevation = 400; enemies[i] = new Enemy( enemies[i - 1].x + 200 + 250 * random(), elevation, random(10, 50), random(20, 50), random(10, 30) ); } } function createTrees() {} function setup() { cnv = createCanvas(1024 + 256, 512 + 256); env.skyColor = color(142, 152, 135); gameChar = new Entity(); gameChar.x = width / 2; gameChar.y = env.floorPos_y + 50; platforms[0] = new Platform(500, 600, 350); for (i = 1; i < amounts.platforms; i++) { let left_x = platforms[i - 1].right_x + 100 + 300 * random(); let right_x = left_x + 50 + 100 * random(); if (left_x > env.end_x && right_x > env.end_x) break; platforms[i] = new Platform(left_x, right_x, 300 + 100 * random()); } createEnemies(); trees_x = [50]; for (i = 1; i <= amounts.tree; i++) { trees_x[i] = trees_x[i - 1] + 20 + 50 * random_values[i]; } clouds[0] = new Cloud(-1000, 60, 1); for (i = 1; i <= amounts.cloud; i++) { clouds[i] = new Cloud( clouds[i - 1].x + 20 + 50 * random_values[i], (env.floorPos_y / 1.9) * random_values[i + 60], 0.5 + random_values[i + 100] ); } mountains_x = [50]; for (i = 1; i <= amounts.mountain; i++) { mountains_x[i] = mountains_x[i - 1] + 20 + 400 * random_values[i + 100]; } canyons[0] = new Canyon(gameChar.x + 200, 100 + 50 * random_values[124]); for (i = 1; i <= amounts.canyon; i++) { let x = canyons[i - 1].x + 200 + 600 * random_values[i + 100]; if (x > flagpole.x - 250) break; canyons[i] = new Canyon(x, 100 + 50 * random_values[i + 124]); } collectables[0] = new Collectable(gameChar.x + 350, env.floorPos_y + 50, 20, 10); for (i = 1; i <= amounts.collectable; i++) { let worth; if (random_values[i] <= 1 / 3) worth = 15; else if (random_values[i] > 1 / 3 && random_values <= 2 / 3) worth = 20; else worth = 25; let x = collectables[i - 1].x + 70 + 400 * random_values[i + 213]; if (x > flagpole.x) break; collectables[i] = new Collectable(x, env.floorPos_y + 50, worth, worth); } // Pruning over-canyon trees. Literally. for (i = 0; i < trees_x.length; i++) { for (j = 0; j < canyons.length; j++) { if ( trees_x[i] > canyons[j].x - (40 + 50 * random_values[j + 124]) && trees_x[i] < canyons[j].x + (40 + 50 * random_values[j + 124]) ) { trees_x[i] = undefined; } } } // Doing the same with collectables, for honesty. for (i = 0; i < collectables.length; i++) { for (j = 0; j < canyons.length; j++) { if ( collectables[i].x > canyons[j].x - (40 + 50 * random_values[j + 124]) && collectables[i].x < canyons[j].x + (40 + 50 * random_values[j + 124]) ) { collectables[i].x = NaN; } } } } function draw() { text_hor_unit = height / 40; text_vert_unit = width / 40; drawSky(); //fill the sky fill(env.groundColor); rect(0, env.floorPos_y, width, height); drawMountains(); // Also updates clouds movement using translate(). May be disabled. drawClouds(true); drawTrees(); drawCanyons(); drawFlagpole(); drawIsogonalBrickWall(-89, env.floorPos_y - 89, 200, 0, -30); drawCollectables(); drawPlatforms(); switch (env.state) { case "INTRO": { push(); textAlign(CENTER); textSize(text_vert_unit * 3); strokeWeight(10); textFont("Courier"); textStyle(BOLD); fill(0); text("The Dead Magic Run", width / 2, height / 2); textSize(text_vert_unit); text("Press any button to attempt a run", width / 2, height / 2 + text_vert_unit * 2); pop(); break; } case "INSTRUCTIONS": { push(); textAlign(LEFT); textSize(text_vert_unit); strokeWeight(3); textFont("Courier"); rectMode(CORNERS); fill(0, 100); rect(text_hor_unit, text_vert_unit * 3, text_hor_unit * 55, text_vert_unit * 11); fill(255); text("The goal is to survive and reach the right end.", text_hor_unit, text_vert_unit * 4); text("Press WASD to move, Space to jump", text_hor_unit, text_vert_unit * 5); text("E or mouse click to shoot.", text_hor_unit, text_vert_unit * 6); text("Press P to enable and disable sounds,", text_hor_unit, text_vert_unit * 7); text("and O letter to enable and disable music.", text_hor_unit, text_vert_unit * 8); text("Press Space to continue and begin the run. Good luck.", text_hor_unit, text_vert_unit * 10); pop(); break; } case "LEVEL": { // Update scrolling based on the character movement scrolling(gameChar); translate(translateration, 0); // Translating everything else to prevent next elements from scrolling too. // Collectables collision checkCollectable(gameChar); checkFlagpole(gameChar); manageMovement(); // the sprite state machine var sprite = getSpriteNumber(); drawGameChar( gameChar.x, gameChar.y, sprite, gameChar.size, true, "red", (hand = Math.floor(frameCount / 12) % 4) ); manageEnemies(); updateProjectiles(); playMusic(mus.level); break; } case "WIN": { manageEnemies(); push(); image(win_img, width / 2 - win_img.width / 2 + 10, height / 2 - win_img.height / 2); fill("yellow"); textSize(text_vert_unit * 1.5); textAlign(CENTER); strokeWeight(2); textFont("Courier"); text("Level complete. Press R to replay.", width / 2, height / 2 - win_img.height / 2); translate(translateration, frameCount); pop(); scrolling(flagpole); // fix the viewpoint on the flagpole translate(translateration, 0); // Translating everything else to prevent next elements from scrolling too. // Forcing to run right gameChar.state.isRight = true; gameChar.state.isLeft = false; // the sprite state machine var sprite = getSpriteNumber(); manageMovement(); drawGameChar(gameChar.x, gameChar.y, sprite, gameChar.size, true, "red", Math.floor(frameCount / 12) % 4); playMusic(mus.win); break; } case "GAMEOVER": { manageEnemies(); push(); image(gameover_img, width / 2 - 256 / 2, height / 2 - 256 / 2); fill("black"); textSize(text_vert_unit * 1.5); textAlign(CENTER); strokeWeight(2); textFont("Courier"); text("Gamover. Press R to replay.", width / 2, height / 2 - 256 / 2); pop(); translate(translateration, 0); playMusic(mus.gameover); break; } default: { push(); textSize(50); fill("red"); text("Game state error!", width / 2, height / 2); pop(); break; } } drawInterface(); } function playMusic(requestedMelody) { // spaghetti code. if (requestedMelody != mus.level) mus.level.stop(); if (requestedMelody != mus.win) mus.win.stop(); if (requestedMelody != mus.gameover) mus.gameover.stop(); if (!requestedMelody.isPlaying() && env.isMusicEnabled) { requestedMelody.play(0, 1, 0.3); } else if (!env.isMusicEnabled) requestedMelody.stop(); } function mouseClicked() { shootProjectile(); } function keyPressed() { if (env.state == "INTRO") { env.state = "INSTRUCTIONS"; } else if (key == " " && env.state == "INSTRUCTIONS") { env.state = "LEVEL"; } if (keyCode == 32 && !(gameChar.state.isJumping || gameChar.state.isFalling)) { gameChar.state.isJumping = true; gameChar.prejump_y = gameChar.y; gameChar.vert_speed = -30; playSound(sfx.jumping); } if (key == "E" && env.state == "LEVEL") { shootProjectile(); } if (key == "A") { gameChar.state.isLeft = true; } if (key == "D") { gameChar.state.isRight = true; } if (key == "R" && (env.state == "WIN" || env.state == "GAMEOVER")) { restart(); } if (key == "P" && !env.isSoundEnabled) { env.isSoundEnabled = true; } else if (key == "P" && env.isSoundEnabled) { env.isSoundEnabled = false; } if (key == "O" && !env.isMusicEnabled) { env.isMusicEnabled = true; } else if (key == "O" && env.isMusicEnabled) { env.isMusicEnabled = false; } if (key == "G" && isDebug) draw_grid = true; console.log("keyPressed: " + key); console.log("keyPressed: " + keyCode); return false; } function keyReleased() { if (key == "M") { if (isDebug) isDebug = false; else isDebug = true; } if (key == "A") { gameChar.state.isLeft = false; } if (key == "D") { gameChar.state.isRight = false; } if (key == "G" && isDebug) draw_grid = false; console.log("keyReleased: " + key); console.log("keyReleased: " + keyCode); }