feat: Initial commit Plants vs Zombies Web Edition v2.4
- 5 Biomes: Day, Night, Pool, Fog, Roof (25 Campaign levels) - Minigames: Wall-nut Bowling, I Zombie, Roguelike, Boss Rush & Dr. Zomboss - Systems: Zen Garden, Workshop Crafting, Almanac, Tree of Wisdom, Achievements - Backend: REST API + SQLite Accounts & Save Synchronization - Infrastructure: Nginx HTTPS & Docker deployment
This commit is contained in:
commit
6457a8904e
50 files changed
+14486
No files matched your search
@@ -0,0 +1,538 @@
|
||||
// Zen Garden System: Idle Potted Plant Farming, AFK Coins, Care & Stinky the Snail
|
||||
window.ZenGardenSystem = class ZenGardenSystem {
|
||||
constructor(economy) {
|
||||
this.economy = economy;
|
||||
|
||||
// Default Zen Garden state
|
||||
this.pots = [
|
||||
{ id: 0, plantType: 'sunflower', stage: 2, thirstTimer: 0, needsFertilizer: false, happyTimer: 60, coinTimer: 5 },
|
||||
{ id: 1, plantType: 'peashooter', stage: 1, thirstTimer: 15, needsFertilizer: false, happyTimer: 45, coinTimer: 8 },
|
||||
{ id: 2, plantType: 'wallnut', stage: 0, thirstTimer: 0, needsFertilizer: true, happyTimer: 0, coinTimer: 0 }, // Sprout
|
||||
{ id: 3, plantType: null, stage: 0, thirstTimer: 0, needsFertilizer: false, happyTimer: 0, coinTimer: 0 },
|
||||
{ id: 4, plantType: null, stage: 0, thirstTimer: 0, needsFertilizer: false, happyTimer: 0, coinTimer: 0 },
|
||||
{ id: 5, plantType: null, stage: 0, thirstTimer: 0, needsFertilizer: false, happyTimer: 0, coinTimer: 0 },
|
||||
{ id: 6, plantType: null, stage: 0, thirstTimer: 0, needsFertilizer: false, happyTimer: 0, coinTimer: 0 },
|
||||
{ id: 7, plantType: null, stage: 0, thirstTimer: 0, needsFertilizer: false, happyTimer: 0, coinTimer: 0 }
|
||||
];
|
||||
|
||||
this.fertilizerCount = 5;
|
||||
this.chocolateCount = 3;
|
||||
|
||||
// Snail state
|
||||
this.snail = {
|
||||
x: 100,
|
||||
targetX: 300,
|
||||
dir: 1,
|
||||
isAwake: true,
|
||||
sleepTimer: 120, // seconds until sleep
|
||||
boostTimer: 0 // chocolate boost
|
||||
};
|
||||
|
||||
// Dropped coins in Zen Garden
|
||||
this.gardenCoins = [];
|
||||
|
||||
// Offline progression tracking
|
||||
this.lastVisit = Date.now();
|
||||
this.offlineEarned = 0;
|
||||
|
||||
this.loadFromAuth();
|
||||
this.calculateOfflineEarnings();
|
||||
}
|
||||
|
||||
loadFromAuth() {
|
||||
const defaultPots = [
|
||||
{ id: 1, plantType: 'sunflower', stage: 2, watered: true, fertilized: true, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 2, plantType: 'peashooter', stage: 1, watered: true, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 3, plantType: null, stage: 0, watered: false, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 4, plantType: null, stage: 0, watered: false, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 5, plantType: null, stage: 0, watered: false, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 6, plantType: null, stage: 0, watered: false, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 7, plantType: null, stage: 0, watered: false, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 },
|
||||
{ id: 8, plantType: null, stage: 0, watered: false, fertilized: false, needWaterTime: 0, matureTime: 0, dropsCollected: 0 }
|
||||
];
|
||||
this.pots = JSON.parse(JSON.stringify(defaultPots));
|
||||
this.fertilizerCount = 5;
|
||||
this.chocolateCount = 2;
|
||||
this.snail = { name: 'Стинки', active: false, speed: 60, boostTimer: 0, x: 50, direction: 1 };
|
||||
this.lastVisit = Date.now();
|
||||
|
||||
if (window.PvZAuth) {
|
||||
const uData = window.PvZAuth.getUserData();
|
||||
if (uData.zenGarden) {
|
||||
if (uData.zenGarden.pots && Array.isArray(uData.zenGarden.pots) && uData.zenGarden.pots.length > 0) {
|
||||
this.pots = JSON.parse(JSON.stringify(uData.zenGarden.pots));
|
||||
}
|
||||
if (uData.zenGarden.fertilizerCount !== undefined) this.fertilizerCount = uData.zenGarden.fertilizerCount;
|
||||
if (uData.zenGarden.chocolateCount !== undefined) this.chocolateCount = uData.zenGarden.chocolateCount;
|
||||
if (uData.zenGarden.snail) this.snail = { ...this.snail, ...uData.zenGarden.snail };
|
||||
if (uData.zenGarden.lastVisit) this.lastVisit = uData.zenGarden.lastVisit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveToAuth() {
|
||||
if (window.PvZAuth && !window.PvZAuth.isGuest()) {
|
||||
window.PvZAuth.saveUserData({
|
||||
zenGarden: {
|
||||
pots: this.pots,
|
||||
fertilizerCount: this.fertilizerCount,
|
||||
chocolateCount: this.chocolateCount,
|
||||
snail: this.snail,
|
||||
lastVisit: Date.now()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
calculateOfflineEarnings() {
|
||||
const now = Date.now();
|
||||
const elapsedSec = Math.max(0, (now - this.lastVisit) / 1000);
|
||||
this.lastVisit = now;
|
||||
|
||||
// Count mature/blooming plants
|
||||
let earningRatePerMinute = 0;
|
||||
for (const pot of this.pots) {
|
||||
if (pot.plantType && pot.stage >= 2) {
|
||||
earningRatePerMinute += 15; // 15 coins / minute per mature plant
|
||||
} else if (pot.plantType && pot.stage === 1) {
|
||||
earningRatePerMinute += 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (earningRatePerMinute > 0 && elapsedSec > 15) {
|
||||
// Cap offline earnings to 8 hours max
|
||||
const cappedMinutes = Math.min(elapsedSec / 60, 480);
|
||||
const earned = Math.floor(cappedMinutes * earningRatePerMinute);
|
||||
if (earned > 0) {
|
||||
this.offlineEarned = earned;
|
||||
if (this.economy) {
|
||||
this.economy.addCoins(earned);
|
||||
}
|
||||
this.saveToAuth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plant a new sprout in first empty pot
|
||||
plantSprout(plantType = 'sunflower') {
|
||||
const emptyPot = this.pots.find(p => !p.plantType);
|
||||
if (emptyPot) {
|
||||
emptyPot.plantType = plantType;
|
||||
emptyPot.stage = 0; // Sprout
|
||||
emptyPot.thirstTimer = 10;
|
||||
emptyPot.needsFertilizer = false;
|
||||
emptyPot.happyTimer = 30;
|
||||
emptyPot.coinTimer = 0;
|
||||
this.saveToAuth();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
waterPlant(potId) {
|
||||
const pot = this.pots.find(p => p.id === potId);
|
||||
if (!pot || !pot.plantType) return false;
|
||||
|
||||
if (pot.thirstTimer > 0) {
|
||||
pot.thirstTimer = 0;
|
||||
if (pot.stage < 2 && Math.random() < 0.6) {
|
||||
pot.needsFertilizer = true;
|
||||
}
|
||||
pot.happyTimer += 60;
|
||||
if (window.PvZAudio) window.PvZAudio.playPlant();
|
||||
this.spawnGardenCoin(potId, 'silver');
|
||||
this.saveToAuth();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fertilizePlant(potId) {
|
||||
const pot = this.pots.find(p => p.id === potId);
|
||||
if (!pot || !pot.plantType) return false;
|
||||
|
||||
if (pot.needsFertilizer && this.fertilizerCount > 0) {
|
||||
this.fertilizerCount--;
|
||||
pot.needsFertilizer = false;
|
||||
if (pot.stage < 2) {
|
||||
pot.stage++;
|
||||
if (pot.stage === 2 && this.economy && this.economy.engine && this.economy.engine.achievements) {
|
||||
this.economy.engine.achievements.trigger('zen_blooming');
|
||||
}
|
||||
}
|
||||
pot.happyTimer += 120;
|
||||
if (window.PvZAudio) window.PvZAudio.playWin();
|
||||
this.spawnGardenCoin(potId, 'gold');
|
||||
this.saveToAuth();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
playMusicToPlant(potId) {
|
||||
const pot = this.pots.find(p => p.id === potId);
|
||||
if (!pot || !pot.plantType) return false;
|
||||
|
||||
pot.happyTimer = Math.max(pot.happyTimer, 180);
|
||||
this.spawnGardenCoin(potId, 'silver');
|
||||
if (window.PvZAudio) window.PvZAudio.playCoin();
|
||||
this.saveToAuth();
|
||||
return true;
|
||||
}
|
||||
|
||||
feedSnail() {
|
||||
if (this.chocolateCount > 0) {
|
||||
this.chocolateCount--;
|
||||
this.snail.isAwake = true;
|
||||
this.snail.sleepTimer = 300; // 5 min
|
||||
this.snail.boostTimer = 120; // 2 min super fast speed
|
||||
if (this.economy && this.economy.engine && this.economy.engine.achievements) {
|
||||
this.economy.engine.achievements.trigger('snail_feed');
|
||||
}
|
||||
if (window.PvZAudio) window.PvZAudio.playWin();
|
||||
this.saveToAuth();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spawnGardenCoin(potId, coinType = 'silver') {
|
||||
const pot = this.pots.find(p => p.id === potId);
|
||||
if (!pot) return;
|
||||
|
||||
const potCol = potId % 4;
|
||||
const potRow = Math.floor(potId / 4);
|
||||
const baseX = 120 + potCol * 150;
|
||||
const baseY = 95 + potRow * 125;
|
||||
|
||||
this.gardenCoins.push({
|
||||
x: baseX + (Math.random() * 30 - 15),
|
||||
y: baseY + 30,
|
||||
vy: -140,
|
||||
type: coinType,
|
||||
value: coinType === 'diamond' ? 200 : (coinType === 'gold' ? 50 : 10),
|
||||
collected: false,
|
||||
alpha: 1.0,
|
||||
anim: 0
|
||||
});
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
// 1. Plant timers
|
||||
for (const pot of this.pots) {
|
||||
if (!pot.plantType) continue;
|
||||
|
||||
if (pot.happyTimer > 0) pot.happyTimer -= dt;
|
||||
|
||||
// Mature plants drop coins always (slower interval when unhappy)
|
||||
if (pot.stage >= 2) {
|
||||
pot.coinTimer -= dt;
|
||||
if (pot.coinTimer <= 0) {
|
||||
const happy = pot.happyTimer > 0;
|
||||
pot.coinTimer = happy ? (15 + Math.random() * 10) : (40 + Math.random() * 20);
|
||||
const isGold = happy && Math.random() < 0.4;
|
||||
this.spawnGardenCoin(pot.id, isGold ? 'gold' : 'silver');
|
||||
}
|
||||
}
|
||||
|
||||
// Thirst accumulation
|
||||
if (pot.thirstTimer <= 0 && Math.random() < 0.005) {
|
||||
pot.thirstTimer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Snail update
|
||||
if (this.snail.isAwake) {
|
||||
this.snail.sleepTimer -= dt;
|
||||
if (this.snail.boostTimer > 0) this.snail.boostTimer -= dt;
|
||||
|
||||
if (this.snail.sleepTimer <= 0) {
|
||||
this.snail.isAwake = false;
|
||||
} else {
|
||||
const speed = this.snail.boostTimer > 0 ? 110 : 45;
|
||||
|
||||
// Move towards nearest uncollected coin
|
||||
let targetCoin = null;
|
||||
let minDist = Infinity;
|
||||
for (const c of this.gardenCoins) {
|
||||
if (!c.collected) {
|
||||
const d = Math.abs(c.x - this.snail.x);
|
||||
if (d < minDist) {
|
||||
minDist = d;
|
||||
targetCoin = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetCoin) {
|
||||
this.snail.targetX = targetCoin.x;
|
||||
} else if (Math.abs(this.snail.x - this.snail.targetX) < 15) {
|
||||
// Pick a new random target at least 120px away to avoid spinning in place
|
||||
let newTarget;
|
||||
let tries = 0;
|
||||
do {
|
||||
newTarget = 80 + Math.random() * 560;
|
||||
tries++;
|
||||
} while (Math.abs(newTarget - this.snail.x) < 120 && tries < 10);
|
||||
this.snail.targetX = newTarget;
|
||||
}
|
||||
|
||||
const distToTarget = this.snail.targetX - this.snail.x;
|
||||
const moveStep = speed * dt;
|
||||
|
||||
if (Math.abs(distToTarget) <= moveStep) {
|
||||
this.snail.x = this.snail.targetX;
|
||||
} else if (distToTarget > 0) {
|
||||
this.snail.dir = 1;
|
||||
this.snail.x += moveStep;
|
||||
} else {
|
||||
this.snail.dir = -1;
|
||||
this.snail.x -= moveStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Garden coins update & Snail collision
|
||||
const FLOOR_Y = 280; // garden floor where coins land and snail walks
|
||||
for (let i = this.gardenCoins.length - 1; i >= 0; i--) {
|
||||
const c = this.gardenCoins[i];
|
||||
c.anim += dt;
|
||||
|
||||
if (!c.collected) {
|
||||
// Coin physics: arc up then fall to garden floor
|
||||
if (c.y < FLOOR_Y) {
|
||||
c.y += c.vy * dt;
|
||||
c.vy += 350 * dt;
|
||||
if (c.y >= FLOOR_Y) {
|
||||
c.y = FLOOR_Y;
|
||||
c.vy = 0;
|
||||
}
|
||||
} else {
|
||||
c.vy = 0; // already on floor
|
||||
}
|
||||
|
||||
// Snail collects coins on the floor
|
||||
if (this.snail.isAwake && Math.abs(this.snail.x - c.x) < 45 && c.y >= FLOOR_Y - 40) {
|
||||
this.collectCoin(c);
|
||||
}
|
||||
} else {
|
||||
c.y -= 90 * dt;
|
||||
c.alpha -= 2.2 * dt;
|
||||
if (c.alpha <= 0) {
|
||||
this.gardenCoins.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectCoin(c) {
|
||||
if (c.collected) return;
|
||||
c.collected = true;
|
||||
if (this.economy) {
|
||||
this.economy.addCoins(c.value);
|
||||
}
|
||||
if (window.PvZAudio) window.PvZAudio.playCoin();
|
||||
this.saveToAuth();
|
||||
}
|
||||
|
||||
// Draw Zen Garden Canvas (720x330)
|
||||
draw(ctx) {
|
||||
// 1. Greenhouse Background
|
||||
const bgGrad = ctx.createLinearGradient(0, 0, 0, 330);
|
||||
bgGrad.addColorStop(0, '#1b3b22');
|
||||
bgGrad.addColorStop(0.6, '#2e5b38');
|
||||
bgGrad.addColorStop(1, '#3e2723');
|
||||
ctx.fillStyle = bgGrad;
|
||||
ctx.fillRect(0, 0, 720, 330);
|
||||
|
||||
// Glass arches
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.12)';
|
||||
ctx.lineWidth = 2;
|
||||
for (let x = 60; x < 720; x += 120) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, 0);
|
||||
ctx.lineTo(x, 260);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Wooden Shelves
|
||||
ctx.fillStyle = '#4e342e';
|
||||
ctx.fillRect(40, 150, 640, 16);
|
||||
ctx.fillRect(40, 275, 640, 16);
|
||||
|
||||
ctx.fillStyle = '#6d4c41';
|
||||
ctx.fillRect(40, 150, 640, 4);
|
||||
ctx.fillRect(40, 275, 640, 4);
|
||||
|
||||
// 2. Pots & Plants
|
||||
for (const pot of this.pots) {
|
||||
const col = pot.id % 4;
|
||||
const row = Math.floor(pot.id / 4);
|
||||
const px = 120 + col * 150;
|
||||
const py = (row === 0 ? 150 : 275);
|
||||
|
||||
this.drawPot(ctx, px, py, pot);
|
||||
}
|
||||
|
||||
// 3. Stinky the Snail
|
||||
this.drawSnail(ctx, this.snail.x, 290, this.snail);
|
||||
|
||||
// 4. Floating Coins
|
||||
for (const c of this.gardenCoins) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = Math.max(0, c.alpha);
|
||||
ctx.translate(c.x, c.y);
|
||||
|
||||
ctx.fillStyle = c.type === 'diamond' ? '#00e5ff' : (c.type === 'gold' ? '#ffd700' : '#cfd8dc');
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, 10, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = '#3e2723';
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.stroke();
|
||||
|
||||
ctx.fillStyle = '#212121';
|
||||
ctx.font = 'bold 9px sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(c.type === 'diamond' ? '💎' : '$', 0, 1);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
drawPot(ctx, x, y, pot) {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
|
||||
// Ceramic clay pot
|
||||
ctx.fillStyle = '#bf360c';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-24, 0);
|
||||
ctx.lineTo(24, 0);
|
||||
ctx.lineTo(18, 22);
|
||||
ctx.lineTo(-18, 22);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Pot rim
|
||||
ctx.fillStyle = '#d84315';
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(-28, -6, 56, 7, 3);
|
||||
ctx.fill();
|
||||
|
||||
// If pot has plant
|
||||
if (pot.plantType) {
|
||||
if (pot.stage === 0) {
|
||||
// Sprout
|
||||
ctx.fillStyle = '#8bc34a';
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, -12, 6, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = '#558b2f';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, -6);
|
||||
ctx.lineTo(0, -12);
|
||||
ctx.stroke();
|
||||
} else {
|
||||
// Plant icon representation
|
||||
ctx.font = pot.stage === 1 ? '22px sans-serif' : '30px sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'bottom';
|
||||
const icons = {
|
||||
sunflower: '🌻',
|
||||
peashooter: '🌱',
|
||||
wallnut: '🌰',
|
||||
snowpea: '❄️',
|
||||
repeater: '🌿',
|
||||
chomper: '🌺',
|
||||
potatomine: '🥔'
|
||||
};
|
||||
ctx.fillText(icons[pot.plantType] || '🌱', 0, -4);
|
||||
}
|
||||
|
||||
// Needs bubble indicator
|
||||
if (pot.thirstTimer > 0) {
|
||||
ctx.fillStyle = 'rgba(33, 150, 243, 0.9)';
|
||||
ctx.beginPath();
|
||||
ctx.arc(20, -32, 10, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.font = '11px sans-serif';
|
||||
ctx.fillText('💧', 20, -28);
|
||||
} else if (pot.needsFertilizer) {
|
||||
ctx.fillStyle = 'rgba(76, 175, 80, 0.9)';
|
||||
ctx.beginPath();
|
||||
ctx.arc(20, -32, 10, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.font = '11px sans-serif';
|
||||
ctx.fillText('🧪', 20, -28);
|
||||
} else if (pot.happyTimer > 0 && pot.stage >= 2) {
|
||||
ctx.fillStyle = '#ffd54f';
|
||||
ctx.font = '10px sans-serif';
|
||||
ctx.fillText('✨', 18, -34);
|
||||
}
|
||||
} else {
|
||||
// Empty pot text
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.35)';
|
||||
ctx.font = '10px sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText('+ Росток', 0, -8);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawSnail(ctx, x, y, snail) {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.scale(snail.dir, 1);
|
||||
|
||||
// Shell
|
||||
ctx.fillStyle = snail.boostTimer > 0 ? '#ffb300' : '#8d6e63';
|
||||
ctx.beginPath();
|
||||
ctx.arc(-6, -10, 14, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = '#4e342e';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
// Body
|
||||
ctx.fillStyle = '#ffecb3';
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(4, -4, 18, 7, 0, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Eye stalks
|
||||
ctx.strokeStyle = '#ffecb3';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(14, -6);
|
||||
ctx.lineTo(18, -14);
|
||||
ctx.moveTo(10, -6);
|
||||
ctx.lineTo(13, -15);
|
||||
ctx.stroke();
|
||||
|
||||
// Eyes
|
||||
ctx.fillStyle = '#212121';
|
||||
ctx.beginPath();
|
||||
ctx.arc(18, -14, 1.5, 0, Math.PI * 2);
|
||||
ctx.arc(13, -15, 1.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
// Zzz if asleep
|
||||
if (!snail.isAwake) {
|
||||
ctx.fillStyle = '#90caf9';
|
||||
ctx.font = 'bold 12px sans-serif';
|
||||
ctx.fillText('Zzz...', -10, -26);
|
||||
} else if (snail.boostTimer > 0) {
|
||||
ctx.fillStyle = '#ff3d00';
|
||||
ctx.font = 'bold 10px sans-serif';
|
||||
ctx.fillText('⚡ 2x', -8, -26);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user