function draw() {
background(220); // Light gray background
// Apply explosion shake offset
translate(explosionShakeOffset, explosionShakeOffset);
// --- Display Instructions ---
if (showInstructions) {
fill(0);
textSize(min(width, height) * 0.025); // Smaller text for instructions
let lineHeight = textSize() * 1.2;
let startY = height * 0.1;
for (let i = 0; i < instructionsText.length; i++) {
text(instructionsText[i], width / 2, startY + i * lineHeight);
}
// Don't draw food or other game elements while instructions are shown
return; // Exit draw loop early
}
// --- Draw Food ---
// Only draw and allow tapping food if gamePhase is 0, not during UFO event, and not game over
if (gamePhase === 0 && !ufoActive && !gameOver) {
fill(50, 200, 50); // Green color
noStroke();
ellipse(foodX, foodY, foodSize, foodSize); // Food is a circle
foodTappable = true;
} else {
foodTappable = false; // Disable food tapping during UFO event or other phases
}
// --- Display Food Count ---
fill(0); // Black text
textSize(min(width, height) * 0.03);
text(`Food Count: ${foodCount}/${ZOMBIE_TAP_TARGET}`, width / 2, height * 0.05);
// --- Game Logic ---
switch (gamePhase) {
case 0: // Tapping food and UFO event at 100 taps, transitions to microwave at 200, zombie at 300
// Trigger UFO event at 100 taps (only once per game)
if (!ufoActive && foodCount >= UFO_TAP_TARGET && !ufoKilledOnce && !gameOver) {
ufoActive = true;
ufoEventState = 1; // Start UFO appearing
ufoEventStartTime = millis();
// Reset UFO position for the event
ufoX = width / 2;
ufoY = -ufoSize;
zapOsc.amp(0); // Ensure zap sound is off
}
if (ufoActive) {
// Handle UFO event phases (appearing, attacking, leaving)
switch (ufoEventState) {
case 1: // Appearing
let appearDuration = 2000;
let elapsedAppear = millis() - ufoEventStartTime;
if (elapsedAppear < appearDuration) {
let t = elapsedAppear / appearDuration;
ufoY = map(t, 0, 1, -ufoSize, height * 0.2);
} else {
ufoY = height * 0.2;
ufoEventState = 2; // Start attacking
ufoEventStartTime = millis();
}
drawUFO();
break;
case 2: // Attacking
let attackDuration = 3000;
let elapsedAttack = millis() - ufoEventStartTime;
drawUFO();
if (elapsedAttack < 1500) {
ufoEventBeamHeight = map(elapsedAttack, 0, 1500, 0, playerY - ufoY);
fill(255, 0, 0);
noStroke();
rect(ufoX - ufoSize * 0.05, ufoY, ufoSize * 0.1, ufoEventBeamHeight);
} else if (elapsedAttack >= 1500 && elapsedAttack < 1600) {
ufoEventBeamHeight = 0;
ufoEventFlashAlpha = 255;
// Play zap sound
zapOsc.start();
zapOsc.amp(0.5, 0.05);
zapOsc.freq(880, 0.05);
// Set the flag that the UFO kill has occurred
ufoKilledOnce = true;
} else if (elapsedAttack >= 1600) {
ufoEventFlashAlpha = map(elapsedAttack, 1600, attackDuration, 255, 0);
zapOsc.amp(0, 0.5);
if (elapsedAttack >= attackDuration - 500) {
zapOsc.stop(0.5);
}
}
if (elapsedAttack >= attackDuration) {
ufoEventState = 3; // Start leaving
ufoEventStartTime = millis();
}
break;
case 3: // Leaving
let leaveDuration = 2000;
let elapsedLeave = millis() - ufoEventStartTime;
if (elapsedLeave < leaveDuration) {
let t = elapsedLeave / leaveDuration;
ufoY = map(t, 0, 1, height * 0.2, -ufoSize);
} else {
ufoY = -ufoSize;
ufoEventState = 0; // Reset UFO event state
ufoActive = false; // UFO event is over
}
drawUFO(); // Draw UFO while leaving
break;
}
// Draw UFO event flash if active
if (ufoEventFlashAlpha > 0) {
fill(255, ufoEventFlashAlpha);
rect(0, 0, width, height);
}
}
// Transition to microwave phase ONLY if UFO event is NOT active AND foodCount >= MICROWAVE_TAP_TARGET
// AND escapeComplete is false (to avoid microwave after escaping zombie)
// AND microwaveExplodedOnce is false (to only explode once)
if (!ufoActive && foodCount >= MICROWAVE_TAP_TARGET && foodCount < ZOMBIE_TAP_TARGET && !gameOver && !escapeComplete && !microwaveExplodedOnce) {
gamePhase = 1; // Start microwave appearing
phaseStartTime = millis();
}
// Transition to zombie phase ONLY if UFO event is NOT active AND foodCount >= ZOMBIE_TAP_TARGET
if (!ufoActive && foodCount >= ZOMBIE_TAP_TARGET && !gameOver && !escapeComplete) {
gamePhase = 6; // Start zombie event
phaseStartTime = millis();
zombieEventState = 1; // Zombie starts appearing
}
break;
case 1: // Microwave Appears
let appearDuration = 2000; // 2 seconds
let elapsedAppear = millis() - phaseStartTime;
// Microwave moves up
if (elapsedAppear < appearDuration) {
let t = elapsedAppear / appearDuration;
microwaveY = map(t, 0, 1, height + microwaveSize / 2, height * 0.6);
} else {
microwaveY = height * 0.6;
gamePhase = 2; // Pizza enters microwave
phaseStartTime = millis();
}
drawMicrowave();
drawPizza(pizzaX, pizzaY, pizzaSize);
break;
case 2: // Pizza Enters Microwave
let enterDuration = 1500; // 1.5 seconds
let elapsedEnter = millis() - phaseStartTime;
// Pizza moves into microwave opening
if (elapsedEnter < enterDuration) {
let t = elapsedEnter / enterDuration;
pizzaX = map(t, 0, 1, width / 2, microwaveX);
pizzaY = map(t, 0, 1, height * 0.7, microwaveY - microwaveSize * 0.15); // Adjust for microwave opening
} else {
pizzaInMicrowave = true;
microwaveDoorOpen = false; // Close door
gamePhase = 3; // Start cooking
phaseStartTime = millis();
// Start microwave hum
microwaveHumOsc.start();
microwaveHumOsc.amp(0.3, 0.1);
}
drawMicrowave();
if (!pizzaInMicrowave) {
drawPizza(pizzaX, pizzaY, pizzaSize);
}
break;
case 3: // Microwave Cooking
let cookDuration = 3000; // 3 seconds
let elapsedCook = millis() - phaseStartTime;
microwavePlateAngle += 0.05; // Spin plate
microwaveInternalLightAlpha = map(sin(millis() * 0.01), -1, 1, 100, 200); // Flickering light
if (elapsedCook >= cookDuration) {
gamePhase = 4; // Explosion!
phaseStartTime = millis();
// Stop microwave hum
microwaveHumOsc.amp(0, 0.5); // Fade out
microwaveHumOsc.stop(0.5); // Stop after fade
// Play explosion sound
explosionNoise.start();
explosionNoise.amp(1, 0.05); // Quick attack to max volume
explosionNoise.amp(0, 0.5); // Fade out
explosionNoise.stop(0.5); // Stop after fade
// Initiate screen shake
explosionShakeOffset = 10;
// Generate pizza slices
generateSpilledPizzaSlices();
}
drawMicrowave();
// Pizza is inside, not drawn separately
break;
case 4: // Explosion!
let explosionDuration = 1000; // 1 second for flash and shake
let elapsedExplosion = millis() - phaseStartTime;
// Flash
if (elapsedExplosion < 100) { // Instant full white flash
explosionFlashAlpha = 255;
} else if (elapsedExplosion < explosionDuration) { // Flash fades
explosionFlashAlpha = map(elapsedExplosion, 100, explosionDuration, 255, 0);
} else {
explosionFlashAlpha = 0;
}
// Shake effect
if (elapsedExplosion < 500) {
explosionShakeOffset = random(-10, 10);
} else {
explosionShakeOffset = 0;
}
drawMicrowave(); // Still draw microwave, maybe slightly broken visually later
drawSpilledPizzaSlices(); // Draw falling pizza
if (elapsedExplosion >= explosionDuration) {
gamePhase = 0; // Return to tapping phase after explosion
// No gameOver = true here!
microwaveExplodedOnce = true; // Set flag so it doesn't explode again
}
break;
case 5: // Game Over - Pizza Spilled (This phase will now be skipped, but keeping it for reference)
explosionShakeOffset = 0; // Ensure no shake
drawMicrowave(); // Microwave remains
drawSpilledPizzaSlices(); // Pizza remains spilled
fill(255, 0, 0); // Red text
textSize(min(width, height) * 0.08);
text("GAME OVER", width / 2, height / 2);
textSize(min(width, height) * 0.04);
text("Your microwave exploded! The pizza spilled on the floor.", width / 2, height / 2 + min(width, height) * 0.06);
fill(100); // Gray text for announcement
textSize(min(width, height) * 0.025);
text("More coming soon like 400, 500, 600, 700, 800, 900 and more!", width / 2, height / 2 + min(width, height) * 0.12);
break;
case 6: // Zombie Event - Find key and escape
drawKey();
drawDoor();
drawZombie();
// Zombie movement logic
switch (zombieEventState) {
case 1: // Appearing
let zombieAppearDuration = 1000;
let elapsedZombieAppear = millis() - zombieEventStartTime;
if (elapsedZombieAppear < zombieAppearDuration) {
let t = elapsedZombieAppear / zombieAppearDuration;
zombieX = map(t, 0, 1, width + zombieSize / 2, width * 0.8);
} else {
zombieX = width * 0.8;
zombieEventState = 2; // Start moving
}
break;
case 2: // Moving
zombieX -= ZOMBIE_SPEED;
// Check if zombie reached player's conceptual position
if (zombieX <= playerX) {
gamePhase = 8; // Zombie Game Over
gameOver = true;
}
break;
}
// If key is found, move it to the door handle
if (keyFound) {
keyX = doorX + doorWidth / 2;
keyY = doorY;
}
// Check for tapping the door
if (doorOpen) {
gamePhase = 7;
escapeComplete = true; // Mark escape as complete
}
break;
case 7: // Escape Successful
fill(0, 200, 0); // Green text
textSize(min(width, height) * 0.08);
text("YOU ESCAPED!", width / 2, height / 2);
textSize(min(width, height) * 0.04);
text("You found the key and got away from the zombie!", width / 2, height / 2 + min(width, height) * 0.06);
fill(100); // Gray text for announcement
textSize(min(width, height) * 0.025);
text("More coming soon like 400, 500, 600, 700, 800, 900 and more!", width / 2, height / 2 + min(width, height) * 0.12);
break;
case 8: // Zombie Game Over
fill(255, 0, 0); // Red text
textSize(min(width, height) * 0.08);
text("GAME OVER", width / 2, height / 2);
textSize(min(width, height) * 0.04);
text("The zombie got you! You couldn't find the key in time.", width / 2, height / 2 + min(width, height) * 0.06);
fill(100); // Gray text for announcement
textSize(min(width, height) * 0.025);
text("More coming soon like 400, 500, 600, 700, 800, 900 and more!", width / 2, height / 2 + min(width, height) * 0.12);
gameOver = true; // Set game over for this path
break;
}
// Draw explosion flash if active (for microwave explosion)
if (explosionFlashAlpha > 0) {
fill(255, explosionFlashAlpha);
rect(0, 0, width, height);
}
// Reset shake offset for next frame
translate(-explosionShakeOffset, -explosionShakeOffset);
}