xxxxxxxxxx
465
let butterYellowTransparent = [245, 243, 198, 180];
let butterYellow = [245, 243, 198];
let darkRed = [153, 0, 0];
// transparency variable for the fading start text
let alphaValue = 0;
// variable used for the fade affect of the start text
let fadeDirection = 1;
let numCols = 7;
let leftMargin = 40;
let rightMargin = 100;
// compute the width of each column
// by ignoring the spaces taken up by the 2 margins
let colWidth = (800 - leftMargin - rightMargin) / numCols;
let elapsedTime = 0;
// set the best time to be a very high number
// so that it can be set later to the minimum time
let bestTime = 1000;
let computer;
let game;
let instructions;
let progressBar;
let droppings = [];
let firstGame = true;
let gameStarted = false;
let gameOverFail = false;
let gameOverSuccess = false;
let winSoundPlayed = false;
let droppingWidth = 50;
let droppingHeight = 70;
let computerWidth = 90;
let computerHeight = 110;
// preload assets, fonts and sounds
function preload() {
birdImg = loadImage('/assets/bird.png');
poopImg = loadImage('/assets/poop.png');
computerImg = loadImage('/assets/computer.png');
palmsImg = loadImage('/assets/palms.png');
backgroundImg = loadImage('/assets/transparent_palms.png');
arrowLeftImg = loadImage('/assets/arrow_left.png');
arrowRightImg = loadImage('/assets/arrow_right.png');
spacebarImg = loadImage('/assets/spacebar.png');
titleFont = loadFont('/fonts/LoveYaLikeASister-Regular.ttf');
mainFont = loadFont('fonts/LexendDeca-VariableFont_wght.ttf');
// sound effects are all downloaded from zapsplat.com
beepSound = loadSound('/sounds/beep.mp3');
blopSound = loadSound('/sounds/blop.mp3');
ambienceSound = loadSound('/sounds/ambience.mp3');
winSound = loadSound('/sounds/win.mp3');
loseSound = loadSound('/sounds/lose.mp3');
}
function setup() {
createCanvas(800, 600);
rectMode(CENTER);
// initialize game objects
computer = new Computer();
instructions = new Instructions();
progressBar = new ProgressBar(20, 0.8);
game = new Game();
}
class Instructions {
constructor() {
this.text = "Oh shoot! You are sitting at the Palms working on your assignment due in 5 minutes, but the birds above have declared war. Dodge the dropping disasters, protect your laptop and finish your work ASAP – the quicker the better!";
}
display() {
image(backgroundImg, 0, 0, width, height);
noStroke();
// text box
fill(butterYellowTransparent);
rect(width / 2, 380, 600, 320, 15);
// title text
fill(0);
textAlign(CENTER, CENTER);
textSize(70);
textFont(titleFont);
text('Dodge the Droppings!', width / 2, height / 5);
// description text
textAlign(LEFT, CENTER);
textSize(15);
textFont(mainFont);
text(this.text, width / 2, 270, width / 3 * 2);
// instructions
image(arrowLeftImg, 110, 311, 90, 90);
image(arrowRightImg, 176, 311, 90, 90);
image(spacebarImg, 142, 375, 90, 90);
fill("#806D68");
textAlign(LEFT, TOP);
text('Press the left and right buttons to protect your laptop from droppings', 460, 335, 350);
text('Press the space bar repeatedly to complete your assignment', 460, 402, 350);
// timer
fill(darkRed);
textAlign(LEFT, TOP);
textSize(23);
textFont(mainFont);
// only show time if the user has played before
if (!firstGame) text(`Best Time: ${bestTime.toFixed(2)}s`, width - 195, 25);
// fade effect on the "Press Enter to start" text
// by varying the transparency
alphaValue += fadeDirection * 3
if (alphaValue >= 255 || alphaValue <= 0) {
fadeDirection *= -1;
}
fill(153, 0, 0, alphaValue);
textAlign(CENTER, CENTER);
textSize(20);
textFont(mainFont);
text('Press "Enter" to start', width / 2, 500);
}
}
class Computer {
constructor() {
this.x = 0;
this.y = 480;
this.alive = true;
// initially static
this.direction = 0;
// place the computer at a random column
this.col = int(random(0, numCols));
}
move(dir) {
// if the right arrow is pressed
if (dir === 1) {
// increment the column if it's not out of boundary
if (computer.col < numCols - 1) {
computer.col++;
}
// left arrow is pressed
} else {
// decrement the column if it's not out of boundary
if (computer.col > 0) {
computer.col--;
}
}
}
display() {
// compute the x coordinate of computer
// based on the column number and column width
this.x = colWidth * this.col + leftMargin;
// draw the computer at the specified coordinates
image(computerImg, this.x, this.y, computerWidth, computerHeight);
}
}
class Bird {
constructor(col) {
this.col = col;
this.x = col * colWidth;
// located at the top of the screen
this.y = 5;
}
display() {
image(birdImg, this.x, this.y, 90, 70);
}
}
class Dropping {
constructor() {
// randomly choose a column to place the dropping
this.col = floor(random(0, numCols));
this.x = colWidth * this.col + leftMargin;
// start falling from slightly below the bird nest
this.y = 20;
// randomly choose a speed
this.speed = random(4, 6);
}
fall() {
this.y += this.speed;
}
display() {
image(poopImg, this.x, this.y, droppingWidth, droppingHeight);
}
}
class ProgressBar {
constructor(fillRate, decayRate) {
this.x = width - 80;
this.y = height - 520;
this.width = 40;
this.height = 470;
// how much the progress bar fills up on space press
this.fillRate = fillRate;
// how much the progress bar shrinks
this.decayRate = decayRate;
// initially has 0 progress
this.progress = 0;
}
update() {
if (this.progress > 0) {
// shrink the progress if it's non-zero
this.progress -= this.decayRate;
}
// progress can only be between 0 and the length of the bar
this.progress = constrain(this.progress, 0, this.height);
}
fill() {
this.progress += this.fillRate;
}
isFull() {
// return true if progress bar is filled
return this.progress >= this.height;
}
// draw the progress bar at the right end of the canvas
display() {
push();
rectMode(CORNER);
fill(butterYellow);
rect(this.x, this.y, this.width, this.height);
fill(darkRed);
rect(this.x, this.y, this.width, this.progress);
pop();
fill(0);
textFont(mainFont);
textSize(12);
textAlign(CENTER);
text("Assignment Progress", 737, 555, 100);
}
}
class Game {
constructor() {
this.started = false;
this.progress = 0;
this.time = 0;
}
start() {
this.started = true;
this.progress = 0;
// start tracking time
this.time = millis();
}
run() {
// stop the game if the game is over
if (gameOverFail || gameOverSuccess) return;
computer.display();
// create a new dropping every 1/3 second
if (frameCount % 20 === 0) {
droppings.push(new Dropping());
}
for (let i = droppings.length - 1; i >= 0; i--) {
let dropping = droppings[i];
// check if any dropping has collided with the computer
if (this.checkCollision(dropping, computer)) {
// play blop sound effect
blopSound.play();
// remove the dropping from the array
droppings.splice(i, 1);
// set gameOverFail flag to true
// so that the correct screen can be shown
gameOverFail = true;
break;
} else {
dropping.fall();
dropping.display();
}
// remove droppings that have went beyond canvas
if (dropping.y > height) {
droppings.splice(i, 1);
}
}
// draw a bird in each column at the top
for (let i = 0; i < numCols; i++) {
let bird = new Bird(i);
bird.display();
}
progressBar.update();
progressBar.display();
// game finishes once the progress bar is full
if (progressBar.isFull()) {
gameOverSuccess = true;
}
}
// function to check for collision between dropping and computer
checkCollision(dropping, computer) {
// extract the coordinates
// add/subtract 10px to ensure that collision
// is detected only when the 2 appear to
// touch each other to the visible eye
// instead of collision being triggered as soon
// as they barely collide
let droppingLeft = dropping.x + 10;
let droppingRight = dropping.x + droppingWidth - 10;
let droppingTop = dropping.y + 10;
let droppingBottom = dropping.y + droppingHeight - 10;
let compLeft = computer.x + 10;
let compRight = computer.x + computerWidth - 10;
let compTop = computer.y + 10;
let compBottom = computer.y + computerHeight - 10;
// collision detected if the 2 overlap
if (droppingRight > compLeft && droppingLeft < compRight &&
droppingBottom > compTop && droppingTop < compBottom) {
return true;
}
return false;
}
reset() {
// reset game variables upon restart
gameOverFail = false;
gameOverSuccess = false;
droppings = [];
computer = new Computer();
progressBar = new ProgressBar(20, 0.8);
winSoundPlayed = false;
// stop the sounds in case they continue playing
beepSound.stop();
blopSound.stop();
}
}
function draw() {
background(backgroundImg);
// start playing the ambience sound once game starts
// sound starts over again once it finishes
if (gameStarted && !ambienceSound.isPlaying()) {
ambienceSound.play();
}
// compute elapsed time if game is over
if (!gameOverFail && !gameOverSuccess) {
elapsedTime = (millis() - game.time) / 1000;
}
// time text
fill(153, 0, 0);
textAlign(LEFT, TOP);
textSize(23);
textFont(mainFont);
// show time in seconds in 2 d.p.
text(`Time: ${elapsedTime.toFixed(2)}s`, width - 140, 25);
// switch screens
if (!gameStarted) {
instructions.display();
} else {
game.run();
}
if (gameOverSuccess) {
ambienceSound.stop();
// play the win sound once
if (!winSoundPlayed) {
winSound.play();
winSoundPlayed = true;
}
// set firstGame to false as the game has been won
// this flag is used to display the best time
firstGame = false;
// best time is the minimum time taken
bestTime = min(elapsedTime, bestTime);
// ending title text
fill(darkRed);
textSize(70);
textAlign(CENTER, CENTER);
textFont(titleFont);
text("WELL DONE!", width / 2, height / 2 - 50);
// time text
fill(0);
textSize(30);
textFont(mainFont);
text(`Current Time: ${elapsedTime.toFixed(2)}s`, width / 2, height / 2 + 20);
text(`Best Time: ${bestTime.toFixed(2)}s`, width / 2, height / 2 + 60);
fill(darkRed);
textSize(20);
text('Press "Enter" to return to homepage', width / 2, height / 2 + 120);
// if the computer is hit by a dropping, it's considered the fail condition
} else if (gameOverFail) {
ambienceSound.stop();
// ending title text
fill(darkRed);
textSize(60);
textAlign(CENTER, CENTER);
textFont(titleFont);
text('Oopsies, a bird pooped on your laptop', width / 2, height / 2 - 50, width);
fill(0);
textFont(mainFont);
textSize(15);
text('Perhaps this is a sign of good luck...not really...anyways, hurry up and email your professor to ask for late submission because you just missed your assignment deadline :)', width / 2, height / 2 + 80, 2 * width / 3);
fill(darkRed);
textSize(20);
text('Press "Enter" to return to homepage', width / 2, height / 2 + 150);
}
}
function keyPressed() {
if (keyCode === ENTER) {
// reset game variables if game is over
if (gameOverFail || gameOverSuccess) {
game.reset();
gameStarted = false;
} else {
// start the game if on homepage
gameStarted = true;
game.start();
}
// move computer to the left
} else if (keyCode === LEFT_ARROW) {
computer.move(-1);
// move computer to the right
} else if (keyCode === RIGHT_ARROW) {
computer.move(1);
} else if (key === ' ') {
// fill up the progress bar if game has started
// otherwise do nothing
if (gameStarted && !gameOverSuccess && !gameOverFail) {
progressBar.fill();
beepSound.play();
}
}
}
function keyReleased() {
// computer should be static if arrow keys are released
if (keyCode == LEFT_ARROW || keyCode === RIGHT_ARROW) {
computer.direction = 0;
}
}