This commit is contained in:
Mottributo 2023-03-23 00:06:30 +03:00
commit 13d9f831ae
6 changed files with 2026 additions and 0 deletions

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

1195
game.js Normal file

File diff suppressed because it is too large Load Diff

12
index.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="p5.min.js"></script>
<script src="p5.sound.min.js"></script>
<script src="sketch.js"></script>
<style> body {padding: 0; margin: 0;} </style>
</head>
<body>
</body>
</html>

9
p5.min.js vendored Normal file

File diff suppressed because one or more lines are too long

28
p5.sound.min.js vendored Normal file

File diff suppressed because one or more lines are too long

782
sketch.js Normal file
View File

@ -0,0 +1,782 @@
var floorPos_y;
var cameraPosX;
var processStop_timer;
var gameChar;
var gravity;
var game_score;
var finish_position_x;
var trees_x = [];
var clouds = [];
var mountains = [];
var collectables = [];
var canyons = [];
var flagpole;
var text_size;
// Variables to set colors. Set in setup()
var palette;
function setup() {
createCanvas(1024, 576);
palette = {
body_color: color("white"),
head_color: color("darkgreen"),
sky_color: color("#8E9887"),
ground_color0: color("#684622"),
ground_color1: color("#734E26"),
ground_color2: color("#7F562A"),
canyon_river_color: color("#56C525"),
canyon_river_wave_color: color("#4BAD21"),
canyon_color: color("#7B672A"),
pine_leaves_color: color("#3F4834"),
maple_leaves_color: color("#4E3D1F"),
pine_stem: color("#644D0D"),
maple_stem: color("#936907"),
mountain: color("#5a5a5a"),
mountain_shadow: color("#3c3c3c"),
cloud0: color(200),
cloud1: color(220),
cloud2: color(255),
coin_outer: color("#FDC334"),
coin_middle: color("#ababab"),
coin_inner: color("#FDC334"),
heart_color: color("darkred"),
enemy_head_color: color("red"),
enemy_body_color: color("red"),
};
startGame((full_start = true), (update_objects = true));
}
function startGame(full_start, update_objects) {
floorPos_y = 432;
cameraPosX = 0;
if (full_start) {
gameChar = {
x: width / 2,
y: floorPos_y + (height - floorPos_y) / 6,
x_step: 5,
y_step: 8,
scale: 1,
sprite: 1,
speed: 5,
baseLives: 3,
curLives: 3,
isRight: false,
isLeft: false,
isFalling: false,
isJumping: false,
isPlummeting: false,
possibleGroundPosY: [
floorPos_y + (height - floorPos_y) / 6,
floorPos_y + (3 * (height - floorPos_y)) / 6,
floorPos_y + (5 * (height - floorPos_y)) / 6,
],
curGroundPosYIndex: 0,
getCurGroundPosY() {
return this.possibleGroundPosY[this.curGroundPosYIndex];
},
goUp() {
memorizedIndex = this.curGroundPosYIndex;
this.curGroundPosYIndex = max(0, this.curGroundPosYIndex - 1);
if (memorizedIndex != this.curGroundPosYIndex) {
gameChar.y -= (height - floorPos_y) / 3;
gameChar.scale -= 0.1;
}
},
goDown() {
memorizedIndex = this.curGroundPosYIndex;
this.curGroundPosYIndex = min(
this.possibleGroundPosY.length - 1,
this.curGroundPosYIndex + 1
);
if (memorizedIndex != this.curGroundPosYIndex) {
gameChar.y += (height - floorPos_y) / 3;
gameChar.scale += 0.1;
}
},
};
} else {
gameChar.x = width / 2;
gameChar.y = floorPos_y + (height - floorPos_y) / 6;
gameChar.scale = 1;
gameChar.curGroundPosYIndex = 0;
gameChar.isPlummeting = false;
gameChar.isFalling = false;
gameChar.isJumping = false;
gameChar.isLeft = false;
gameChar.isRight = false;
}
gravity = 1;
game_score = 0;
finish_position_x = 2000;
if (update_objects || full_start) {
// Assigning values of trees, clouds, mountains, canyons, collectables.
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) {
canyons[0] = {
x: 700,
width: 50,
points: [],
};
} else
canyons[i] = {
x: canyons[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,
y_pos: floorPos_y,
size: 50,
isFound: false,
};
} else
collectables[i] = {
x: collectables[i - 1].x + 50 + 150 * random(0.5, 1),
y_pos: gameChar.possibleGroundPosY[
random(0, gameChar.possibleGroundPosY.length)
],
size: 50,
isFound: false,
};
}
}
flagpole = {
x: finish_position_x,
isReached: false,
size_vert: 4,
size_hor: 7,
cell_size: 20,
cell_size_v: 20,
cell_size_h: 20,
};
text_size = width / 20;
textSize(text_size);
}
function draw() {
// -------- SKY ----------------
background(palette.sky_color);
// -------- GROUND -------------
drawGround();
push(); // Scrolling
fill(0);
translate(-cameraPosX, 0); // Scrolling
cameraPosX = gameChar.x - width / 2; // Scrolling
// -------- CLOUDS --------------
drawClouds();
// -------- MOUNTAINS -----------
drawMountains();
// -------- CANYONS -------------
for (i = 0; i < canyons.length; i++) {
canyon = canyons[i];
drawCanyon(canyon);
if (!gameChar.isFalling && !gameChar.isJumping) checkCanyon(canyon);
}
// -------- TREES ---------------
drawTrees();
// -------- COLLECTABLES --------
for (i = 0; i < collectables.length; i++) {
collectable = collectables[i];
if (!collectable.isFound) drawCollectable(collectable);
checkCollectable(collectable);
}
// -------- FLAGPOLE ------------
renderFlagpole();
if (!flagpole.isReached) checkFlagpole();
// -------- GAME CHARACTER ------
{
// Behavior
if (!gameChar.isPlummeting) {
if (gameChar.isLeft && !gameChar.isRight) {
gameChar.x -= gameChar.speed;
} else if (gameChar.isRight && !gameChar.isLeft) {
gameChar.x += gameChar.speed;
}
if (gameChar.isJumping) {
gameChar.y -= gameChar.jumpingStrength;
gameChar.jumpingStrength -= gravity;
if (gameChar.jumpingStrength <= 0) {
gameChar.isFalling = true;
gameChar.isJumping = false;
}
} else if (gameChar.isFalling) {
gameChar.y -= gameChar.jumpingStrength;
gameChar.jumpingStrength -= gravity;
if (gameChar.y >= gameChar.getCurGroundPosY()) {
gameChar.y = gameChar.getCurGroundPosY();
gameChar.isFalling = false;
}
} else {
}
}
checkPlayerDie();
// Setting sprite
{
if (gameChar.isPlummeting) {
gameChar.sprite = 2;
} else if (gameChar.isFalling) {
gameChar.sprite = 2;
if (gameChar.isLeft && !gameChar.isRight) gameChar.sprite = 5;
else if (gameChar.isRight && !gameChar.isLeft)
gameChar.sprite = 6;
} else {
if (gameChar.isLeft && !gameChar.isRight) gameChar.sprite = 4;
else if (gameChar.isRight && !gameChar.isLeft)
gameChar.sprite = 3;
else gameChar.sprite = 1;
}
}
// Depicting plummeting
if (gameChar.isPlummeting) {
gameChar.y += 3;
}
// Drawing a sprite
drawGameCharacter();
}
pop(); // Scrolling
// -------- INTERFACE -----------
push();
fill(0);
text("Score: " + game_score, 12, text_size);
drawLives();
if (gameChar.curLives < 1)
text("Game over. Press space to continue...", 0, height / 2);
else if (flagpole.isReached)
text("Level complete. Press space to continue...", 0, height / 2);
text(gameChar.curGroundPosYIndex, 99, 99);
text(gameChar.getCurGroundPosY(), 188, 99);
text(gameChar.sprite, 277, 99);
pop();
}
function drawGameCharacter() {
strokeWeight(1);
stroke(1);
gameChar.x_step *= gameChar.scale;
gameChar.y_step *= gameChar.scale;
function _drawBody(jumping = false) {
fill(palette.body_color);
if (jumping) {
triangle(
gameChar.x - gameChar.x_step * 2,
gameChar.y - gameChar.y_step * 1.5,
gameChar.x,
gameChar.y - gameChar.y_step * 4,
gameChar.x + gameChar.x_step * 2,
gameChar.y - gameChar.y_step * 1.5
);
} else {
triangle(
gameChar.x - gameChar.x_step * 2,
gameChar.y,
gameChar.x,
gameChar.y - gameChar.y_step * 4,
gameChar.x + gameChar.x_step * 2,
gameChar.y
);
}
}
function _drawHead() {
fill(palette.head_color);
ellipse(
gameChar.x,
gameChar.y - gameChar.y_step * 5,
gameChar.x_step * 6,
gameChar.y_step * 4
);
}
function _drawEyes(draw_left, draw_right, eyes_height) {
fill(0);
if (draw_left) {
ellipse(
gameChar.x - gameChar.x_step * 1.2,
gameChar.y - gameChar.y_step * eyes_height,
gameChar.x_step / 1.5,
gameChar.y_step / 2
);
}
if (draw_right) {
ellipse(
gameChar.x + gameChar.x_step * 1.2,
gameChar.y - gameChar.y_step * eyes_height,
gameChar.x_step / 1.5,
gameChar.y_step / 2
);
}
}
// Standing, facing frontwards
if (gameChar.sprite == 1) {
// Body
_drawBody();
// Head
_drawHead();
// Eyes
_drawEyes(true, true, 4.2);
}
// Jumping, facing forwards
else if (gameChar.sprite == 2) {
// Body
_drawBody(true);
// Hands. (Hands!)
line(
gameChar.x - gameChar.x_step * 1,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x - gameChar.x_step * 3.2,
gameChar.y - gameChar.y_step * 3.2
);
line(
gameChar.x + gameChar.x_step * 1,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x + gameChar.x_step * 3.2,
gameChar.y - gameChar.y_step * 3.2
);
// Head
_drawHead();
// Eyes
_drawEyes(true, true, 3.8);
}
// Walking right
else if (gameChar.sprite == 3) {
// Body
_drawBody();
// Hand. (Hand!)
line(
gameChar.x,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x + gameChar.x_step * 0.8,
gameChar.y - gameChar.y_step * 1
);
// Head
_drawHead();
// Eyes
_drawEyes(false, true, 4.2);
}
// Walking left
else if (gameChar.sprite == 4) {
// Body
_drawBody();
// Hand. (Hand!)
line(
gameChar.x,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x - gameChar.x_step * 0.8,
gameChar.y - gameChar.y_step * 1
);
// Head
_drawHead();
// Eyes
_drawEyes(true, false, 4.2);
}
// Jumping left
else if (gameChar.sprite == 5) {
// Body
_drawBody(true);
// Hands. (Hands!)
line(
gameChar.x - gameChar.x_step * 1,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x - gameChar.x_step * 3.2,
gameChar.y - gameChar.y_step * 3.2
);
line(
gameChar.x + gameChar.x_step * 1,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x + gameChar.x_step * 3.2,
gameChar.y - gameChar.y_step * 3.2
);
// Head
_drawHead();
// Eyes
_drawEyes(true, false, 3.8);
}
// Jumping right
else if (gameChar.sprite == 6) {
// Body
_drawBody(true);
// Hands. (Hands!)
line(
gameChar.x - gameChar.x_step * 1,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x - gameChar.x_step * 3.2,
gameChar.y - gameChar.y_step * 3.2
);
line(
gameChar.x + gameChar.x_step * 1,
gameChar.y - gameChar.y_step * 2.5,
gameChar.x + gameChar.x_step * 3.2,
gameChar.y - gameChar.y_step * 3.2
);
// Head
_drawHead();
// Eyes
_drawEyes(false, true, 3.8);
} else {
text("Bad sprite number!", 10, 10);
console.error("Bad gameChar sprite number: " + gameChar.sprite);
}
gameChar.x_step /= gameChar.scale;
gameChar.y_step /= gameChar.scale;
}
function checkCanyon(t_canyon) {
if (
gameChar.x > canyon.x - canyon.width / 2 &&
gameChar.x < canyon.x + canyon.width / 2
) {
gameChar.isPlummeting = true;
}
}
function checkCollectable(t_collectable) {
if (
!t_collectable.isFound &&
collectable.y_pos == gameChar.getCurGroundPosY() &&
dist(
t_collectable.x,
t_collectable.y_pos,
gameChar.x,
gameChar.y - gameChar.x_step * 4
) <
t_collectable.size / 2
) {
t_collectable.isFound = true;
game_score++;
}
}
function drawGround() {
push();
noStroke();
fill(palette.ground_color0);
rect(0, floorPos_y, width, height - floorPos_y);
fill(palette.ground_color1);
rect(
0,
floorPos_y + (2 * (height - floorPos_y)) / 6,
width,
floorPos_y + (2 * (height - floorPos_y)) / 6
);
fill(palette.ground_color2);
rect(
0,
floorPos_y + (4 * (height - floorPos_y)) / 6,
width,
floorPos_y + (4 * (height - floorPos_y)) / 6
);
stroke(1);
translate(-cameraPosX, 0);
for (i = 0; i < finish_position_x; i += 20) {
line(
i,
floorPos_y + (2 * (height - floorPos_y)) / 6,
i + 1,
floorPos_y + (2 * (height - floorPos_y)) / 6
);
line(
i,
floorPos_y + (4 * (height - floorPos_y)) / 6,
i + 1,
floorPos_y + (4 * (height - floorPos_y)) / 6
);
}
pop();
}
function drawCanyon(t_canyon) {
push();
fill(palette.canyon_river_color);
rect(t_canyon.x - t_canyon.width / 2, floorPos_y, t_canyon.width, height);
if (frameCount % 4 == 0) {
PointX = random(
t_canyon.x - t_canyon.width / 2,
t_canyon.x + t_canyon.width / 2
);
PointY = random(floorPos_y, height);
if (t_canyon.points.length > 3) {
t_canyon.points.shift();
}
t_canyon.points.add((PointX, PointY));
}
stroke(palette.canyon_river_wave_color);
strokeWeight(5);
line(PointX, PointY, PointX, PointY + random(20, 100));
pop();
}
function drawCollectable(t_collectable) {
stroke(0.2);
fill(palette.coin_outer);
ellipse(t_collectable.x, t_collectable.y_pos, 0.7 * t_collectable.size);
fill(palette.coin_middle);
ellipse(t_collectable.x, t_collectable.y_pos, 0.6 * t_collectable.size);
fill(palette.coin_inner);
ellipse(t_collectable.x, t_collectable.y_pos, 0.5 * t_collectable.size);
}
function drawClouds() {
for (i = 0; i < clouds.length; i++) {
push();
translate((frameCount / clouds[i].y) * 20, 0); // imitating clouds movement, upper ones should go faster
noStroke();
fill(palette.cloud0);
ellipse(
clouds[i].x - 20,
clouds[i].y - 10,
20 * clouds[i].size,
30 * clouds[i].size
);
fill(palette.cloud1);
ellipse(
clouds[i].x + 20,
clouds[i].y - 20,
70 * clouds[i].size,
50 * clouds[i].size
);
fill(palette.cloud2);
ellipse(
clouds[i].x,
clouds[i].y,
90 * clouds[i].size,
40 * clouds[i].size
);
ellipse(
clouds[i].x + 45,
clouds[i].y - 10,
50 * clouds[i].size,
35 * clouds[i].size
);
pop();
}
}
function drawMountains() {
for (i = 0; i < mountains.length; i++) {
noStroke();
fill(palette.mountain_shadow);
triangle(
mountains[i].x - mountains[i].width,
floorPos_y,
mountains[i].x + mountains[i].skew,
mountains[i].height,
mountains[i].x + mountains[i].width,
floorPos_y
);
fill(palette.mountain);
triangle(
mountains[i].x - mountains[i].width,
floorPos_y,
mountains[i].x + mountains[i].skew,
mountains[i].height,
mountains[i].x - mountains[i].width / 1.5,
floorPos_y
);
}
}
function drawHeart(isEmpty, x, y, size) {
push();
if (isEmpty) {
noFill();
stroke(0);
} else {
fill(palette.heart_color);
stroke(palette.heart_color);
}
triangle(x, y, x + size, y - size, x + size * 2, y);
triangle(x - size * 2, y, x - size, y - size, x, y);
triangle(x, y, x + size, y + size, x - size, y + size);
triangle(x - size * 2, y, x, y + size * 2, x + size * 2, y);
pop();
}
function drawLives() {
push();
stroke(255, 0, 0);
noFill();
var heart_size = 20;
for (i = gameChar.baseLives; i > 0; i--) {
drawHeart(
true,
width - i * heart_size * 4.5,
heart_size * 1.5,
heart_size
);
}
for (i = gameChar.curLives; i > 0; i--) {
drawHeart(
false,
width - i * heart_size * 4.5,
heart_size * 1.5,
heart_size
);
}
pop();
}
function drawTrees() {
for (i = 0; i < trees_x.length; i++) {
// Draw pine in case the integer part of tree
// coordinate is even.
if (Math.trunc(trees_x[i]) % 2 == 0) {
fill(palette.pine_stem);
triangle(
trees_x[i] - 15,
floorPos_y,
trees_x[i],
floorPos_y - 150,
trees_x[i] + 15,
floorPos_y
);
fill(palette.pine_leaves_color);
triangle(
trees_x[i] - 45,
floorPos_y - 45,
trees_x[i],
floorPos_y - 120,
trees_x[i] + 45,
floorPos_y - 45
);
triangle(
trees_x[i] - 45,
floorPos_y - 85,
trees_x[i],
floorPos_y - 180,
trees_x[i] + 45,
floorPos_y - 85
);
}
// Draw maple
else {
fill(palette.maple_stem);
triangle(
trees_x[i] - 10,
floorPos_y,
trees_x[i],
floorPos_y - 120,
trees_x[i] + 10,
floorPos_y
);
fill(palette.maple_leaves_color);
ellipse(trees_x[i], floorPos_y - 50, 80, 30);
ellipse(trees_x[i], floorPos_y - 70, 100, 30);
ellipse(trees_x[i], floorPos_y - 90, 80, 30);
ellipse(trees_x[i], floorPos_y - 110, 40, 30);
}
}
}
function renderFlagpole() {
// NB - This function is implemented a bit differently than how it was requested in Part 6.
// It has no states to switch between - instead, the flag gradually goes into the opposite direction
// as the player crosses it, what is done by manipulating flagpole's parameters.
// This is done for animation purposes.
push();
strokeWeight(0);
fill(0);
// Movement animation, as mentioned in NB.
if (flagpole.isReached && flagpole.cell_size_h > -flagpole.cell_size)
flagpole.cell_size_h -= 0.8;
// the flagpole pole
rect(flagpole.x, floorPos_y / 2, 2, 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,
floorPos_y / 2 + flagpole.cell_size_v * j,
flagpole.cell_size_h,
flagpole.cell_size_v
);
}
}
pop();
}
function checkFlagpole() {
// Considering the linear nature of the game, the flagpole is triggered whenever player is beyond it by x.
// If to imagine a hypothetical jump where the player somehow jumps beyond the pole in one frame,
// this function will trigger anyway.
if (gameChar.x >= finish_position_x) {
flagpole.isReached = true;
}
}
function checkPlayerDie() {
if (gameChar.y >= height) {
if (gameChar.curLives > 1) {
gameChar.curLives--;
startGame();
} else if (gameChar.curLives == 1) {
gameChar.curLives--;
} else {
gameChar.curLives = 0;
}
}
}
function keyPressed() {
console.log(frameCount + " pressed " + key + " " + keyCode);
if (gameChar.curLives < 1 || flagpole.isReached) {
if (keyCode == 32 /*Space*/) {
/*Hard resets the game*/
startGame((full_start = true), (update_objects = true));
}
} else if (!gameChar.isPlummeting) {
if (keyCode == 65 /*A*/) gameChar.isLeft = true;
if (keyCode == 68 /*D*/) gameChar.isRight = true;
if (keyCode == 83 /*S*/) gameChar.goDown();
if (keyCode == 87 /*W*/) gameChar.goUp();
// Rewrote jumping routine to make it more natural and be able to support platforms and different player dimensions
if (
keyCode == 32 /*Space*/ &&
!gameChar.isFalling &&
!gameChar.isJumping
) {
gameChar.isJumping = true;
gameChar.jumpingStrength = 15;
}
}
}
function keyReleased() {
console.log(frameCount + " released " + key + " " + keyCode);
if (keyCode == 65 /*A*/) gameChar.isLeft = false;
if (keyCode == 68 /*D*/) gameChar.isRight = false;
}