xxxxxxxxxx
361
let stoneWall; // Stone texture
let player; // Player object
let maze; // Maze object
let treasure; // Treasure object
let gameStage = 0; // 0: Start Screen, 1: Playing, 2: Win Screen, 3: Trap Screen
let lightRadius = 75; // Initial light radius
let targetLightRadius = 75; // light radius for smooth transitions
let maxLightRadius = 125; // Expanded light radius for when the cat meows
let lightDecay = 1; // Rate at which light shrinks back
let catMeowSound; // Sound for the cat's meow
let meowTimer = 5; // Timer for cat meow effect
let trapImages = []; // Array to hold trap images
let traps = []; // Array to hold trap objects
let startBgd; // Background image for Start page
let winBgd;
let catImg;
let lastMeowTime = 0; // Tracks the last time the player meowed
const meowCooldown = 3000; // Cooldown duration in milliseconds (3 seconds)
function preload() {
stoneWall = loadImage('walls.jpg');
catMeowSound = loadSound('meow.mp3');
startBgd = loadImage('tombbgd.jpg');
winBgd = loadImage('treasure2.jpg');
catImg = loadImage('cat.png');
// Load trap images
trapImages.push(loadImage('mummy2.png'));
trapImages.push(loadImage('beetles 2.png'));
trapImages.push(loadImage('snake.png'));
}
function setup() {
createCanvas(1000, 600);
maze = new Maze();
player = new Player(60, 60, 10);
treasure = new Treasure(984, 548); //puts the win condition at the end of the maze
traps.push({x: 370, y: 425, w: 150, h: 150, img: trapImages[0]}); //mummy trap
traps.push({x: 485, y: 93, w: 150, h: 75, img: trapImages[1]}); // beetle trap
traps.push({x: 900, y: 450, w: 70, h: 70, img: trapImages[2]}); // snake trap
}
function draw() {
background(0);
if (gameStage === 0) {
showStartScreen();
} else if (gameStage === 1) {
lightRadius = lerp(lightRadius, targetLightRadius, 0.1);
drawFlashlight(); // Draw the flashlight effect first
maze.display(); // Draw the maze so that the walls are only visible within the flashlight radius
drawTraps();
treasure.display();
player.move();
player.display();
checkWinCondition();
checkTrapCollision();
// Gradually shrink the light radius after meow
if (meowTimer > 0) {
meowTimer--;
} else {
targetLightRadius = 75;
}
// Display cooldown message if meow is on cooldown at the top
let theCurrentTime = millis();
if (theCurrentTime - lastMeowTime < meowCooldown) {
fill(255);
//strokeWeight(7);
textSize(23);
fill('rgb(127,0,0)')
textAlign(CENTER, CENTER);
text("Meow on cooldown: " + ((meowCooldown - (theCurrentTime - lastMeowTime)) / 1000).toFixed(1) + "s", width / 2, 15);
}
} else if (gameStage === 2) {
showWinScreen();
} else if (gameStage === 3) {
showTrapScreen();
}
}
// Flashlight effect
function drawFlashlight() {
fill(0);
noStroke();
rect(0, 0, width, height);
blendMode(SCREEN);
drawingContext.globalCompositeOperation = 'lighter';
// Draw the flashlight gradient
let gradient = drawingContext.createRadialGradient(
player.x, player.y, lightRadius / 4,
player.x, player.y, lightRadius
);
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
drawingContext.fillStyle = gradient;
drawingContext.beginPath();
drawingContext.arc(player.x, player.y, lightRadius, 0, TWO_PI);
drawingContext.fill();
blendMode(BLEND);
}
// Draw all the traps with their respective images
function drawTraps() {
for (let trap of traps) {
// Create a mask for the trap
drawingContext.save();
drawingContext.beginPath();
drawingContext.arc(player.x, player.y, lightRadius, 0, TWO_PI);
drawingContext.clip();
image(trap.img, trap.x, trap.y, trap.w, trap.h);
drawingContext.restore();
}
}
// What do for all the keys that are pressed
function keyPressed() {
if (keyCode === ENTER && gameStage === 0) {
gameStage = 1; // Enter to start the game
} else if (keyCode === 82 && (gameStage === 2 || gameStage === 3)) { // R key to restart
gameStage = 0;
player.x = 60;
player.y = 60;
lightRadius = 75;
targetLightRadius = 75;
} else if (key === 'C' || key === 'c') {
catMeow(); // calls the cat meow function
}
}
// Cat meow effect and time control
function catMeow() {
let theCurrentTime = millis();
if (theCurrentTime - lastMeowTime >= meowCooldown) {
catMeowSound.play();
targetLightRadius = maxLightRadius;
meowTimer = 200;
lastMeowTime = theCurrentTime;
}
}
// Check if player reaches the 'treasure' or end of the game
function checkWinCondition() {
let d = dist(player.x, player.y, treasure.x, treasure.y);
if (d < player.r + treasure.r) {
gameStage = 2; // Wins the game screen
}
}
// Check if player hits a trap
function checkTrapCollision() {
for (let trap of traps) {
if (player.x > trap.x && player.x < trap.x + trap.w && player.y > trap.y && player.y < trap.y + trap.h) {
gameStage = 3; // Triggers the trap screen
}
}
}
// Start screen
function showStartScreen() {
image(startBgd, 0, 0, width, height);
fill(255);
textSize(26);
textAlign(CENTER, CENTER);
textFont('Papyrus');
let intro = ["Oh no!\n You are an archeologist, and your cat got spooked and ran away \nin the newly discovered tomb. Use the arrow keys to navigate \nthrough the maze. Press the 'C' key to hear your cat meow \n which increases your visibility and follow her through the maze \n with your flashlight. Make sure to avoid the traps! \n \n P.S You can only call out to your cat every 3 seconds so call her wisely. "]
text(intro, width/2, height/2 - 70)
textSize(32)
text("Press Enter to Start", width / 2, height / 2 +120);
}
// Win screen
function showWinScreen() {
image(winBgd, 0, 0, width, height);
image(catImg, 460, 400, 100, 100);
fill(255);
textSize(32);
textAlign(CENTER, 240);
text("You Found the Treasure!", width / 2, height / 2);
textSize(24);
text("Press R to Restart", width / 2, height / 2 + 40);
}
// Trap screen
function showTrapScreen() {
image(startBgd, 0, 0, width, height);
fill(255, 0, 0);
textSize(32);
textAlign(CENTER, CENTER);
text("You Hit a Trap!", width / 2, height / 2);
textSize(24);
text("Press R to Restart", width / 2, height / 2 + 40);
}
//This creates the little player circle and allows for the player's' movement
class Player {
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
this.speed = 3;
}
move() {
let oldX = this.x;
let oldY = this.y;
if (keyIsDown(LEFT_ARROW)) this.x -= this.speed;
if (keyIsDown(RIGHT_ARROW)) this.x += this.speed;
if (keyIsDown(UP_ARROW)) this.y -= this.speed;
if (keyIsDown(DOWN_ARROW)) this.y += this.speed;
// This reverts the position to the previous position if player is colliding with walls
if (maze.checkWallCollision(this.x, this.y, this.r)) {
this.x = oldX;
this.y = oldY;
}
}
display() {
fill(255);
noStroke();
ellipse(this.x, this.y, this.r * 2);
}
}
//this constructs the entire maze using stone wall images, makes sure you can't pass through walls, and creates the mask for the flashlight
class Maze {
constructor() {
this.walls = [
// Outer boundaries (these are always visible)
{x: 75, y: 0, w: 1200, h: 30}, // Top
{x: 0, y: 570, w: 1200, h: 30}, // Bottom
{x: 0, y: 0, w: 30, h: 600}, // Left
{x: 970, y: 0, w: 30, h: 535}, // Right
// Interior walls (these are only visible within the flashlight radius)
{x: 75, y: 25, w: 10, h: 100},
{x: 75, y: 165, w: 10, h: 220},
{x: 75, y: 425, w: 10, h: 90},
{x: 145, y: 25, w: 10, h: 100},
{x: 195, y: 25, w: 10, h: 100},
{x: 195, y: 115, w: 75, h: 10},
{x: 75, y: 115, w: 75, h: 10},
{x: 75, y: 165, w: 200, h: 10},
{x: 265, y:115, w: 10, h: 55},
{x: 75, y: 375, w: 165, h: 10},
{x: 75, y: 425, w: 215, h: 10},
{x: 280, y: 295, w: 10, h: 140},
{x: 235, y: 355, w: 10, h: 30},
{x: 105, y: 345, w: 140, h: 10},
{x: 155, y: 295, w: 135, h: 10},
{x: 155, y: 255, w: 10, h: 50},
{x: 105, y: 205, w: 10, h: 150},
{x: 75, y: 515, w: 210, h: 10},
{x: 280, y: 515, w: 10, h: 60},
{x: 105, y: 490, w: 205, h: 10},
{x: 105, y: 450, w: 205, h: 10},
{x: 105, y: 450, w: 10, h: 50},
{x: 155, y: 255, w: 155, h: 10},
{x: 115, y: 205, w: 195, h: 10},
{x: 300, y:95, w: 10, h: 115},
{x: 235, y: 85, w: 75, h: 10},
{x: 300, y:255, w: 10, h: 205},
{x: 300, y:490, w: 10, h: 85},
{x: 225, y: 25, w: 10, h: 70},
{x: 345, y: 25, w: 10, h: 320},
{x: 345, y: 345, w: 225, h: 10},
{x: 345, y: 395, w: 60, h: 10},
{x: 465, y: 395, w: 60, h: 10},
{x: 345, y: 395, w: 10, h: 180},
{x: 515, y: 395, w: 10, h: 180},
{x: 565, y: 345, w: 10, h: 180},
{x: 565, y: 525, w: 50, h: 10},
{x: 605, y: 300, w: 10, h: 230},
{x: 655, y: 165, w: 10, h: 410},
{x: 405, y: 300, w: 205, h: 10},
{x: 405, y: 255, w: 205, h: 10},
{x: 405, y: 225, w: 205, h: 10},
{x: 605, y: 225, w: 10, h: 40},
{x: 405, y: 255, w: 10, h: 50},
{x: 405, y: 25, w: 10, h: 210},
{x: 465, y: 165, w: 195, h: 10},
{x: 465, y: 85, w: 10, h: 90},
{x: 465, y: 85, w: 225, h: 10},
{x: 685, y: 85, w: 10, h: 25},
{x: 685, y: 150, w: 10, h: 425},
{x: 745, y: 20, w: 10, h: 345},
{x: 745, y: 410, w: 10, h: 120},
{x: 745, y: 525, w: 70, h: 10},
{x: 805, y: 450, w: 10, h: 85},
{x: 865, y: 450, w: 10, h: 85},
{x: 805, y: 445, w: 70, h: 10},
{x: 865, y: 525, w: 140, h: 10},
{x: 745, y: 410, w: 145, h: 10},
{x: 935, y: 410, w: 40, h: 10},
{x: 745, y: 355, w: 170, h: 10},
{x: 905, y: 90, w: 10, h: 275},
{x: 845, y: 90, w: 60, h: 10},
{x: 845, y: 90, w: 10, h: 245},
{x: 785, y: 20, w: 10, h: 315},
{x: 785, y: 325, w: 60, h: 10},
];
}
display() {
for (let i = 0; i < 4; i++) {
let wall = this.walls[i];
image(stoneWall, wall.x, wall.y, wall.w, wall.h);
}
//creating the masking
for (let i = 4; i < this.walls.length; i++) {
let wall = this.walls[i];
// Create a mask for the wall
drawingContext.save();
drawingContext.beginPath();
drawingContext.arc(player.x, player.y, lightRadius, 0, TWO_PI);
drawingContext.clip();
image(stoneWall, wall.x, wall.y, wall.w, wall.h);
drawingContext.restore();
}
}
checkWallCollision(px, py, pr) {
for (let wall of this.walls) {
if (
(px + pr > wall.x) &&
(px - pr < wall.x + wall.w) &&
(py + pr > wall.y) &&
(py - pr < wall.y + wall.h)
) {
return true;
}
}
return false;
}
}
//creates the 'treasure' which is really transparent that when hit causes the win screen to be triggered
class Treasure {
constructor(x, y) {
this.x = x;
this.y = y;
this.r = 20;
}
display() {
fill('rgba(255,215,0,0)');
ellipse(this.x, this.y, this.r * 2);
}
}