diff --git a/sketch.js b/sketch.js index 757ed55..67f4167 100644 --- a/sketch.js +++ b/sketch.js @@ -6,7 +6,7 @@ var fps_recent_values = []; var death_timer; var showDebugData = false; var debug_charTrace = []; -var shadows_enabled = false; +var shadows_enabled = true; var gameChar; @@ -43,6 +43,7 @@ function GameCharacter() { this.isOnPlatform = false; this.curGroundIndex = 0; this.memorizedPlatform; + this.invincibility_time = 0; this._updateSprite = function () { if (this.isPlummeting) { @@ -114,7 +115,6 @@ function GameCharacter() { stroke(1); this.x_step *= this.scale; this.y_step *= this.scale; - // Standing, facing frontwards if (this.sprite == 1) { // Body @@ -226,6 +226,12 @@ function GameCharacter() { console.error('Bad this sprite number: ' + this.sprite); noLoop(); } + // Drawing an aura of invincibility + if (this.invincibility_time > 0 && floor(frameCount/10)%2==0) { + fill('rgba(255,255,255,0.2)'); + noStroke(); + ellipse(this.x, this.y - this.y_step*3, this.x_step*9, this.y_step*9); + } this.x_step /= this.scale; this.y_step /= this.scale; pop(); @@ -249,6 +255,10 @@ function GameCharacter() { this.y_step /= this.scale; pop(); }; + this.takeLife = function () { + gameChar.curLives--; + gameChar.invincibility_time+=180; + } this._checkPlayerDie = function () { if (frameCount - death_timer > 60) { if (this.curLives > 1) { @@ -263,6 +273,7 @@ function GameCharacter() { } }; this.act = function () { + this.invincibility_time = max(0, this.invincibility_time-1); if (!this.isPlummeting) { if (this.isLeft && !this.isRight) { this.x -= this.speed; @@ -323,7 +334,6 @@ function GameCharacter() { this.y += 3; } }; - this.getCurGroundY = function () { return groundPositions[this.curGroundIndex]; }; @@ -346,8 +356,9 @@ function GameCharacter() { this.scale += 0.1; } }; + } -// NB: All the platforms mechanics were done +// NB: All the platforms and enemies mechanics were done // before the task was seen on Week 20. // Therefore, the implementation may // differ from one in the lecture. @@ -380,7 +391,7 @@ function Platform(curGroundIndex, x, width, y) { push(); // drawing shadow on the ground noStroke(); - fill('rgba(0,0,0, 0.15)'); + fill('rgba(0,0,0, 0.08)'); rect( this.x - this.width, groundPositions[curGroundIndex], @@ -412,6 +423,41 @@ function Platform(curGroundIndex, x, width, y) { return false; }; } +function Enemy(curGroundIndex, x, y, size) { + this.x = x; + this.y = y; + this.size = size; + this.curGroundIndex = curGroundIndex; + this.draw = function () { + push(); + fill(lerpColor(palette.enemy_head_color, palette.ground_colors[this.curGroundIndex], 0.7)); + ellipse(this.x, this.y, this.size); + fill(255,0,0); + ellipse(this.x - this.size/2, this.y, this.size/8, this.size/4); + ellipse(this.x + this.size/2, this.y, this.size/8, this.size/4); + textSize(this.size/1.5); + textAlign(CENTER); + fill(palette.enemy_body_color); + text(this.curGroundIndex, this.x, this.y+this.size/4); + pop(); + } + this.drawShadow = function () { + push(); + fill('rgba(0,0,0,0.2)'); + noStroke(); + ellipse(this.x, groundPositions[this.curGroundIndex], this.size/2, this.size/4); + pop(); + } + this.collidesWith = function (who) { + if (dist(who.x, who.y, this.x, this.y) < this.size/2 && + who.curGroundIndex == this.curGroundIndex) return true; + return false; + } + this.updatePosition = function () { + this.x = this.x + sin(frameCount/random(20,50)*random(0.5, 10)); + this.y = this.y + cos(frameCount/random(10,20)*random(0.5, 10)); + } +} var text_size; // Variables to set colors. Set in setup() @@ -440,8 +486,8 @@ function setup() { coin_middle: color('#ababab'), coin_inner: color('#FDC334'), heart_color: color('darkred'), - enemy_head_color: color('red'), - enemy_body_color: color('red'), + enemy_head_color: color('rgba(0,0,0,0.5)'), + enemy_body_color: color('rgba(255,255,255,0.3)') }; startGame((level_start = true)); } @@ -457,7 +503,7 @@ function startGame(level_start) { camera_speed = 0.9; gravity = 1; game_score = 0; - finish_position_x = 2000; + finish_position_x = 6000; textSize(width / 20); flagpole = { x: finish_position_x, @@ -471,147 +517,7 @@ function startGame(level_start) { if (level_start) { gameChar = new GameCharacter(); cameraPosX = gameChar.x - width / 2; - // Creating trees, clouds, mountains, rivers, collectables. - { - // Creating trees, clouds, mountains, rivers, collectables, platforms, enemies. - for (i = 0; i < 150; i++) { - trees_x[i] = random(-100, 10000); - } - for (i = 0; i < 100; i++) { - clouds[i] = { - x: random(-100, 10000), - y: random(0, floorPos_y / 2), - size: random(0.4, 2), - }; - } - // Latter mountains. - for (i = 0; i < finish_position_x / 90; i++) { - mountains[i] = { - x: random(-200, finish_position_x + 200), - width: random(60, 150), - height: random(floorPos_y - 50, floorPos_y - 150), - skew: random(-20, 20), - }; - // To prevent spiky mountains which are quite ugly - mountains[i].height = max( - mountains[i].width, - mountains[i].height, - ); - } - // Start mountains. - for (i = 0; i < 6; i++) { - mountains[i] = { - x: width / (i + 1.5), - width: width / (i + random(2, 4)), - height: height / (2 + 0.8 * i), - skew: (random(-10, 20) * i) / 2, - }; - } - for (i = 0; i < 100; i++) { - if (i == 0) { - rivers[0] = { - x: 700, - width: 50, - points: [], - }; - } else { - if (rivers[i - 1].x + 600 > finish_position_x) break; - rivers[i] = { - x: rivers[i - 1].x + 300 + 200 * random(0.5, 1), - width: 50 + 30 * random(), - points: [], - }; - } - } - for (i = 0; i < 100; i++) { - if (i == 0) { - collectables[0] = { - x: 600, - curGroundIndex: 0, - y: - groundPositions[0] - - 10 /*taking 10 for better visuals*/, - size: 75, - isFound: false, - }; - } else { - // Skipping generating coins after the finish line - if (collectables[i - 1].x + 200 > finish_position_x) { - break; - } - r = floor(random(0, groundPositions.length)); - collectables[i] = { - x: collectables[i - 1].x + 50 + 100 * random(0.5, 1), - curGroundIndex: r, - y: - groundPositions[r] - - 10 /*taking 10 for better visuals*/, - size: 75, - isFound: false, - }; - // Checking whether the coin is over a river; - // marking it as isFound (making it disabled) in case if. - for (k = 0; k < rivers.length; k++) { - if ( - rivers[k].x - rivers[k].width / 2 < - collectables[i].x && - rivers[k].x + rivers[k].width / 2 > - collectables[i].x - ) { - collectables[i].isFound = true; - } - } - } - } - for (i = 0; i < groundPositions.length; i++) { - platforms[i] = []; - } - for (i = 0; i < 100; i++) { - if (i == 0) { - platforms[0][0] = new Platform( - 0, - 700, - 50, - groundPositions[0] - Platform.max_y_deviation, - ); - platforms[1][0] = new Platform( - 1, - 800, - 50, - groundPositions[1] - Platform.max_y_deviation, - ); - platforms[2][0] = new Platform( - 2, - 900, - 50, - groundPositions[2] - Platform.max_y_deviation, - ); - } else { - let groundPosIndex = floor( - random(0, groundPositions.length), - ); - // array.slice(-1)[0] gets the last element of an array without removing it, - // contrary to array.pop(). - prev = platforms[groundPosIndex].slice(-1)[0]; - // stopping generation after the flagpole - if (prev.x + prev.width + 155 > finish_position_x) break; - platforms[groundPosIndex].push( - new Platform( - groundPosIndex, - prev.x + random(prev.width + 70, prev.width + 155), - random(15, 60), - max( - random( - prev.y - prev.max_y_deviation, - floorPos_y - prev.max_y_deviation, - ), - height / 8, - ), - ), - ); - } - } - } + generateObjects(); } else { gameChar.x = width / 2; gameChar.y = floorPos_y + (height - floorPos_y) / 6; @@ -625,6 +531,166 @@ function startGame(level_start) { gameChar.isOnPlatform = false; } } +function generateObjects() { + // Creating trees, clouds, mountains, rivers, collectables, platforms, enemies. + // Trees coords + for (i = 0; i < 150; i++) { + trees_x[i] = random(-100, 10000); + } + // Clouds + for (i = 0; i < 100; i++) { + clouds[i] = { + x: random(-100, 10000), + y: random(0, floorPos_y / 2), + size: random(0.4, 2), + }; + } + // Latter mountains. + for (i = 0; i < finish_position_x / 90; i++) { + mountains[i] = { + x: random(-200, finish_position_x + 200), + width: random(60, 150), + height: random(floorPos_y - 50, floorPos_y - 150), + skew: random(-20, 20), + }; + // To prevent spiky mountains which are quite ugly + mountains[i].height = max( + mountains[i].width, + mountains[i].height, + ); + } + // Start mountains. + for (i = 0; i < 6; i++) { + mountains[i] = { + x: width / (i + 1.5), + width: width / (i + random(2, 4)), + height: height / (2 + 0.8 * i), + skew: (random(-10, 20) * i) / 2, + }; + } + // Rivers + for (i = 0; i < 100; i++) { + if (i == 0) { + rivers[0] = { + x: 700, + width: 50, + points: [], + }; + } else { + if (rivers[i - 1].x + 600 > finish_position_x) break; + rivers[i] = { + x: rivers[i - 1].x + 100 + 200 * random(0.5, 1), + width: 50 + 30 * random(), + points: [], + }; + } + } + // Collectables + for (i = 0; i < 100; i++) { + if (i == 0) { + collectables[0] = { + x: 600, + curGroundIndex: 0, + y: + groundPositions[0] - + 10 /*taking 10 for better visuals*/, + size: 75, + isFound: false, + }; + } else { + // Skipping generating coins after the finish line + if (collectables[i - 1].x + 200 > finish_position_x) { + break; + } + r = floor(random(0, groundPositions.length)); + collectables[i] = { + x: collectables[i - 1].x + 50 + 100 * random(0.5, 1), + curGroundIndex: r, + y: + groundPositions[r] - + 10 /*taking 10 for better visuals*/, + size: 75, + isFound: false, + }; + // Checking whether the coin is over a river; + // marking it as isFound (making it disabled) in case if. + for (k = 0; k < rivers.length; k++) { + if ( + rivers[k].x - rivers[k].width / 2 < + collectables[i].x && + rivers[k].x + rivers[k].width / 2 > + collectables[i].x + ) { + collectables[i].isFound = true; + } + } + } + } + // Platform rows + for (i = 0; i < groundPositions.length; i++) { + platforms[i] = []; + } + // Platforms + for (i = 0; i < 100; i++) { + if (i == 0) { + platforms[0][0] = new Platform( + 0, + 700, + 50, + groundPositions[0] - Platform.max_y_deviation, + ); + platforms[1][0] = new Platform( + 1, + 800, + 50, + groundPositions[1] - Platform.max_y_deviation, + ); + platforms[2][0] = new Platform( + 2, + 900, + 50, + groundPositions[2] - Platform.max_y_deviation, + ); + } else { + let groundPosIndex = floor( + random(0, groundPositions.length), + ); + // array.slice(-1)[0] gets the last element of an array without removing it, + // contrary to array.pop(). + prev = platforms[groundPosIndex].slice(-1)[0]; + // stopping generation after the flagpole + if (prev.x + prev.width + 155 > finish_position_x) break; + // adding platforms + platforms[groundPosIndex].push( + new Platform( + groundPosIndex, + prev.x + random(prev.width + 70, prev.width + 155), + random(15, 60), + max( + random( + prev.y - prev.max_y_deviation, + floorPos_y - prev.max_y_deviation, + ), + height / 8, + ), + ), + ); + } + } + // Enemies + enemies = []; + for (i = 0; i < floor(finish_position_x/130); i++) { + index = floor(random(0, groundPositions.length)) + enemies.push( + new Enemy( + index, + random(500, finish_position_x), + groundPositions[index] - random(50, floorPos_y), + random(40, 60) + ) + ) + } +} function draw() { // -------- SKY ---------------- background(palette.sky_color); @@ -672,8 +738,17 @@ function draw() { // -------- FLAGPOLE ------------ renderFlagpole(); if (!flagpole.isReached) checkFlagpole(); - // GAMECHAR, PLATFORMS & ENEMIES RENDER + // GAMECHAR & PLATFORMS RENDER -- complexDraw(); + // -------- ENEMIES ------------- + for (i=0; i