Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Thronglets Simulation (v3)</title> | |
<style> | |
:root { | |
--grass-dark: #2a6141; --grass-light: #3a815b; --water: #4171a7; | |
--rock-dark: #6b727c; --tree-leaves: #2b602c; --ui-bg: #e0cda9; | |
--ui-border: #8b4513; --ui-accent: #6d8c4f; --ui-text: #3a2e20; | |
} | |
body { | |
margin: 0; overflow: hidden; background: var(--grass-dark); | |
color: #eee; font-family: 'Verdana', sans-serif; display: flex; | |
flex-direction: column; align-items: center; justify-content: center; | |
min-height: 100vh; position: relative; | |
} | |
.game-container { | |
position: relative; width: 95vmin; height: 70vmin; | |
max-width: 1000px; max-height: 700px; | |
background-color: var(--grass-light); /* Simple background color */ | |
border: 1px solid var(--tree-trunk); box-sizing: border-box; | |
overflow: hidden; display: flex; justify-content: center; | |
align-items: center; font-size: 1.5em; position: relative; | |
image-rendering: pixelated; box-shadow: inset 0 0 20px rgba(0,0,0,0.4); | |
} | |
.game-elements { | |
position: absolute; top: 0; left: 0; width: 100%; height: 100%; | |
pointer-events: none; | |
} | |
.game-elements .emoji { | |
position: absolute; pointer-events: auto; transform: translate(-50%, -50%); | |
user-select: none; z-index: 1; transition: opacity 0.5s ease-out; | |
image-rendering: pixelated; | |
} | |
/* Static elements */ | |
.rock-cluster { position: absolute; top: 30%; left: 25%; z-index: 0; font-size: 1.2em; color: var(--rock-dark); } | |
.river { position: absolute; top: 0; left: 35%; width: 8%; height: 100%; background: var(--water); z-index: 0; } | |
.tree { position: absolute; z-index: 0; font-size: 1.8em; color: var(--tree-leaves);} | |
.tree.t1 { top: 10%; left: 10%; } .tree.t2 { top: 15%; left: 55%; } | |
.tree.t3 { top: 60%; left: 85%; } .tree.t4 { top: 70%; left: 15%; } | |
.tree.t5 { top: 5%; left: 80%; } | |
/* Dynamic elements */ | |
.egg { top: 50%; left: 50%; cursor: pointer; transition: transform 0.5s ease, opacity 0.5s ease-out; z-index: 2;} | |
.egg.hatching { animation: hatch-pulse 0.5s infinite alternate; } | |
.thronglet { | |
cursor: default; | |
transition: top 1s linear, left 1s linear, transform 0.2s ease, opacity 0.5s ease-out, filter 0.3s ease-out; | |
z-index: 3; | |
font-family: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', Times, Symbola, Aegyptus, Demo; | |
} | |
.feedback { | |
position: absolute; transform: translate(-50%, -150%); font-size: 0.8em; | |
opacity: 0; transition: opacity 0.5s ease-out, transform 0.5s ease-out; | |
pointer-events: none; z-index: 5; text-shadow: 1px 1px 1px rgba(0,0,0,0.5); | |
} | |
.feedback.active { opacity: 1; transform: translate(-50%, -200%); } | |
.dead { filter: grayscale(100%); opacity: 0.4; pointer-events: none; z-index: 1; } | |
.blood { | |
position: absolute; font-size: 1.2em; transform: translate(-50%, -50%); pointer-events: none; | |
opacity: 0; transition: opacity 0.5s ease-out; z-index: 0; color: #a03030; | |
} | |
.blood.splatter { opacity: 0.7; } | |
/* --- UI Panels --- */ | |
.ui-panel-container { | |
position: absolute; top: 15px; left: 15px; right: 15px; | |
display: flex; justify-content: space-between; | |
pointer-events: none; z-index: 10; | |
} | |
.ui-panel { | |
background: var(--ui-bg); border: 3px solid var(--ui-border); | |
border-radius: 8px; box-shadow: 3px 3px 5px rgba(0,0,0,0.3); | |
padding: 5px; pointer-events: auto; image-rendering: pixelated; | |
} | |
/* Top Left UI Panel */ | |
#topLeftUI { display: flex; flex-direction: column; align-items: center; width: 70px; } | |
.logo { /* ... Same style ... */ | |
background: var(--ui-accent); border: 2px solid var(--ui-border); color: var(--ui-bg); | |
font-size: 1.8em; font-weight: bold; width: 40px; height: 40px; display: flex; | |
align-items: center; justify-content: center; border-radius: 50%; margin-bottom: 5px; | |
} | |
.action-grid { /* ... Same style ... */ | |
display: grid; grid-template-columns: 1fr 1fr; gap: 4px; width: 100%; | |
} | |
.action-grid button { /* ... Same style ... */ | |
background: var(--ui-bg); border: 2px solid var(--ui-border); color: var(--ui-text); | |
font-size: 1.2em; width: 30px; height: 30px; display: flex; align-items: center; | |
justify-content: center; border-radius: 4px; cursor: pointer; | |
transition: background-color 0.2s, transform 0.1s, border-color 0.1s; padding: 0; line-height: 1; | |
} | |
.action-grid button.selected { border-color: #fff; box-shadow: inset 0 0 3px rgba(0,0,0,0.4); } | |
.action-grid button:hover { background-color: #f5e5c5; } | |
.action-grid button:active { transform: scale(0.95); } | |
/* Button IDs */ | |
#pointerBtn { grid-column: 1; grid-row: 1; } #targetBtn { grid-column: 2; grid-row: 1; } | |
#feedButton { grid-column: 1; grid-row: 2; } #cleanButton { grid-column: 2; grid-row: 2; } | |
#brainBtn { grid-column: 1; grid-row: 3; } #plantBtn { grid-column: 2; grid-row: 3; } | |
#houseBtn { grid-column: 1; grid-row: 4; } #gearBtn { grid-column: 2; grid-row: 4; } | |
/* Top Right UI Panel */ | |
#topRightUI { display: flex; flex-direction: column; align-items: center; width: 80px; } | |
.thronglet-icon-display { | |
background: var(--ui-accent); border: 2px solid var(--ui-border); width: 50px; height: 50px; | |
border-radius: 50%; display: flex; align-items: center; justify-content: center; | |
font-size: 2em; /* Make single emoji larger */ | |
margin-bottom: 4px; color: var(--ui-bg); | |
font-family: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', Times, Symbola, Aegyptus, Demo; /* Ensure emoji font */ | |
} | |
/* Removed inner spans for multiple icons */ | |
#throngletCountDisplayTopRight { color: var(--ui-text); font-weight: bold; font-size: 1.1em; } | |
/* Hidden elements */ | |
.scanline-overlay, .glitch-overlay, .ominous-elements, .info-panel, | |
.final-screen, .full-black, .netflix-games-logo { | |
opacity: 0; pointer-events: none; display: none; | |
} | |
.visible { opacity: 1 ; pointer-events: auto ; display: flex ; } | |
.ominous-elements.visible, .info-panel.visible { display: block ; } | |
@keyframes hatch-pulse { /* Unchanged */ | |
from { transform: translate(-50%, -50%) scale(1); } | |
to { transform: translate(-50%, -50%) scale(1.05); } | |
} | |
</style> | |
</head> | |
<body> | |
<div class="game-container" id="gameContainer"> | |
<div class="game-elements" id="gameElements"> | |
<!-- Static Elements --> | |
<div class="river"></div> <div class="rock-cluster">πͺ¨<br>πͺ¨πͺ¨</div> | |
<span class="emoji tree t1">π³</span> <span class="emoji tree t2">π³</span> | |
<span class="emoji tree t3">π³</span> <span class="emoji tree t4">π³</span> | |
<span class="emoji tree t5">π³</span> | |
<!-- Initial dynamic elements --> | |
<span class="emoji egg" id="egg">π₯</span> | |
<!-- Hidden Elements --> | |
<div class="ominous-elements" id="ominousElements">π<br>π¦΄<br>ππ¦</div> | |
<div class="info-panel" id="infoPanel"></div> | |
<span class="emoji blood" id="bloodSplatter">π©Έ</span> | |
</div> | |
<!-- UI Panels --> | |
<div class="ui-panel-container"> | |
<div class="ui-panel" id="topLeftUI"> | |
<div class="logo">T</div> | |
<div class="action-grid" id="actionGrid"> | |
<button id="pointerBtn" title="Pointer">β</button> | |
<button id="targetBtn" title="Target">π―</button> | |
<button id="feedButton" title="Feed">π</button> | |
<button id="cleanButton" title="Clean">π§Ό</button> | |
<button id="brainBtn" title="Mood?">π§ </button> | |
<button id="plantBtn" title="Plant?">π±</button> | |
<button id="houseBtn" title="Build?">π </button> | |
<button id="gearBtn" title="Settings?">βοΈ</button> | |
</div> | |
</div> | |
<div class="ui-panel" id="topRightUI"> | |
<div class="thronglet-icon-display">πΉ</div> <!-- Simplified Icon --> | |
<span id="throngletCountDisplayTopRight">0</span> | |
</div> | |
</div> | |
</div> | |
<!-- Hidden Overlays/Final Screens --> | |
<div class="scanline-overlay" id="scanlineOverlay"></div> | |
<div class="glitch-overlay" id="glitchOverlay"></div> | |
<div class="final-screen" id="finalScreen">END</div> | |
<div class="full-black" id="fullBlack"></div> | |
<div class="netflix-games-logo" id="netflixGamesLogo"><span class="emoji">N</span></div> | |
<script> | |
// --- DOM Elements --- | |
const egg = document.getElementById('egg'); | |
const gameContainer = document.getElementById('gameContainer'); | |
const gameElements = document.getElementById('gameElements'); | |
const actionGrid = document.getElementById('actionGrid'); | |
const throngletCountDisplay = document.getElementById('throngletCountDisplayTopRight'); | |
const infoPanel = document.getElementById('infoPanel'); | |
const bloodSplatter = document.getElementById('bloodSplatter'); | |
// --- Game State --- | |
let thronglets = []; | |
let nextThrongletId = 0; | |
let deathCount = 0; | |
const MAX_THRONGLETS = 100; | |
let gameInterval; | |
let gameActive = true; | |
let selectedTool = 'pointer'; | |
// --- Web Audio API Setup --- | |
let audioContext; | |
let isAudioContextResumed = false; | |
// initAudio and playSound functions (same as previous version) | |
function initAudio() { | |
if (isAudioContextResumed) return; // Already resumed | |
if (!audioContext) { | |
try { | |
window.AudioContext = window.AudioContext || window.webkitAudioContext; | |
audioContext = new AudioContext(); | |
} catch (e) { console.error("Web Audio API not supported", e); return; } | |
} | |
// Resume on first user interaction if needed | |
if (audioContext.state === 'suspended') { | |
const resumeAudio = () => { | |
if (audioContext.state === 'suspended') { | |
audioContext.resume().then(() => { | |
isAudioContextResumed = true; console.log("AudioContext Resumed"); | |
document.body.removeEventListener('click', resumeAudio); | |
document.body.removeEventListener('touchend', resumeAudio); | |
}).catch(e => console.error("Audio resume failed", e)); | |
} else { // Already running or closed? | |
isAudioContextResumed = (audioContext.state === 'running'); | |
document.body.removeEventListener('click', resumeAudio); | |
document.body.removeEventListener('touchend', resumeAudio); | |
} | |
}; | |
// Use capture phase to potentially catch earlier interactions | |
document.body.addEventListener('click', resumeAudio, { once: true, capture: true }); | |
document.body.addEventListener('touchend', resumeAudio, { once: true, capture: true }); | |
} else { | |
isAudioContextResumed = true; // Already running | |
} | |
} | |
function playSound(type) { | |
if (!audioContext || !isAudioContextResumed) { | |
console.log("Audio not ready, skipping sound:", type); | |
// Attempt to resume if called before interaction | |
if (audioContext && audioContext.state === 'suspended') initAudio(); | |
return; | |
} | |
const osc = audioContext.createOscillator(); const gain = audioContext.createGain(); osc.connect(gain); gain.connect(audioContext.destination); | |
const now = audioContext.currentTime; let freq = 440, duration = 0.1, vol = 0.2; osc.type = 'sine'; | |
switch (type) { /* ... cases same as before ... */ | |
case 'hatch': freq = 660; duration = 0.3; vol = 0.3; osc.type = 'triangle'; gain.gain.setValueAtTime(0, now); gain.gain.linearRampToValueAtTime(vol, now + 0.05); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
case 'feed': freq = 880; duration = 0.08; vol = 0.15; gain.gain.setValueAtTime(vol, now); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
case 'clean': freq = 1100; duration = 0.1; vol = 0.1; osc.type = 'square'; gain.gain.setValueAtTime(vol, now); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
case 'death': freq = 110; duration = 0.8; vol = 0.4; osc.type = 'sawtooth'; gain.gain.setValueAtTime(vol, now); gain.gain.exponentialRampToValueAtTime(0.01, now + duration); osc.frequency.setValueAtTime(freq, now); osc.frequency.exponentialRampToValueAtTime(55, now + duration); break; | |
case 'duplicate': freq = 523; duration = 0.4; vol = 0.25; osc.type = 'triangle'; gain.gain.setValueAtTime(0, now); gain.gain.linearRampToValueAtTime(vol, now + 0.05); osc.frequency.setValueAtTime(freq, now); osc.frequency.linearRampToValueAtTime(freq * 1.5, now + duration * 0.8); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
case 'feedback': freq = 1320; duration = 0.05; vol = 0.08; gain.gain.setValueAtTime(vol, now); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
case 'error': freq = 220; duration = 0.2; vol = 0.2; osc.type = 'square'; gain.gain.setValueAtTime(vol, now); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
case 'ui_click': freq = 1000; duration = 0.04; vol = 0.05; osc.type = 'square'; gain.gain.setValueAtTime(vol, now); gain.gain.linearRampToValueAtTime(0, now + duration); break; | |
default: freq = 900; duration = 0.05; vol = 0.1; gain.gain.setValueAtTime(vol, now); gain.gain.linearRampToValueAtTime(0, now + duration); | |
} | |
osc.frequency.setValueAtTime(freq, now); osc.start(now); osc.stop(now + duration); | |
} | |
// --- Game Logic --- | |
function updateUI() { | |
const livingCount = thronglets.filter(t => !t.isDead).length; | |
throngletCountDisplay.textContent = livingCount; | |
} | |
function showInfoPanel(text, duration = 2000) { | |
if (!gameActive || !infoPanel) return; | |
infoPanel.textContent = text; | |
infoPanel.style.display = 'block'; | |
requestAnimationFrame(() => { // Ensure display:block is applied before opacity transition starts | |
infoPanel.style.opacity = 1; | |
}); | |
clearTimeout(infoPanel.timeout); | |
infoPanel.timeout = setTimeout(() => { | |
infoPanel.style.opacity = 0; | |
// Use transitionend event listener for reliable hiding | |
const hidePanel = () => { infoPanel.style.display = 'none'; }; | |
infoPanel.addEventListener('transitionend', hidePanel, { once: true }); | |
}, duration); | |
} | |
function createThronglet(xPercent, yPercent) { | |
const livingCount = thronglets.filter(t => !t.isDead).length; | |
if (!gameActive || livingCount >= MAX_THRONGLETS) { | |
if (livingCount >= MAX_THRONGLETS) { | |
playSound('error'); showInfoPanel("Population Limit!", 1500); | |
} return; | |
} | |
const throngletElement = document.createElement('span'); | |
throngletElement.classList.add('emoji', 'thronglet'); | |
throngletElement.textContent = 'πΉ'; // Hamster emoji | |
const currentId = nextThrongletId++; | |
throngletElement.dataset.id = currentId; | |
const spawnX = Math.max(5, Math.min(95, xPercent)); | |
const spawnY = Math.max(5, Math.min(95, yPercent)); | |
throngletElement.style.top = `${spawnY}%`; throngletElement.style.left = `${spawnX}%`; | |
throngletElement.dataset.bornTime = Date.now(); | |
gameElements.appendChild(throngletElement); | |
const newThronglet = { id: currentId, element: throngletElement, hunger: 25, cleanliness: 20, happiness: 70, lastInteraction: Date.now(), lastDuplication: 0, isDead: false, memory: [], feedbackElement: null }; | |
thronglets.push(newThronglet); | |
const feedbackElement = document.createElement('span'); | |
feedbackElement.classList.add('emoji', 'feedback'); | |
feedbackElement.style.top = throngletElement.style.top; feedbackElement.style.left = throngletElement.style.left; | |
gameElements.appendChild(feedbackElement); newThronglet.feedbackElement = feedbackElement; | |
updateUI(); | |
setTimeout(() => { if (!newThronglet.isDead) wander(newThronglet); }, 100); | |
} | |
function feedThronglet(thronglet) { | |
if (!gameActive || thronglet.isDead) return; | |
thronglet.hunger = Math.max(0, thronglet.hunger - 50); thronglet.happiness = Math.min(100, thronglet.happiness + 8); | |
thronglet.lastInteraction = Date.now(); showFeedback(thronglet, 'π'); playSound('feed'); thronglet.memory.push('Fed'); | |
} | |
function cleanThronglet(thronglet) { | |
if (!gameActive || thronglet.isDead) return; | |
thronglet.cleanliness = Math.max(0, thronglet.cleanliness - 60); thronglet.happiness = Math.min(100, thronglet.happiness + 4); | |
thronglet.lastInteraction = Date.now(); showFeedback(thronglet, 'β¨'); playSound('clean'); thronglet.memory.push('Cleaned'); | |
} | |
function showFeedback(thronglet, emoji) { | |
if (!gameActive || !thronglet.feedbackElement || thronglet.isDead) return; | |
thronglet.feedbackElement.style.top = thronglet.element.style.top; thronglet.feedbackElement.style.left = thronglet.element.style.left; | |
thronglet.feedbackElement.textContent = emoji; thronglet.feedbackElement.classList.add('active'); playSound('feedback'); | |
clearTimeout(thronglet.feedbackElement.timeout); | |
thronglet.feedbackElement.timeout = setTimeout(() => { if (thronglet.feedbackElement) { thronglet.feedbackElement.classList.remove('active'); } }, 600); | |
} | |
function killThronglet(thronglet, reason = "expiration") { | |
if (!gameActive || thronglet.isDead) return; | |
thronglet.isDead = true; thronglet.element.classList.add('dead'); thronglet.element.textContent = 'π'; | |
if (thronglet.feedbackElement) { thronglet.feedbackElement.remove(); thronglet.feedbackElement = null; } | |
bloodSplatter.style.top = thronglet.element.style.top; bloodSplatter.style.left = thronglet.element.style.left; | |
bloodSplatter.classList.add('splatter'); setTimeout(() => { bloodSplatter.classList.remove('splatter'); }, 800); | |
deathCount++; updateUI(); playSound('death'); thronglet.memory.push(`Died (${reason})`); | |
setTimeout(() => { if (thronglet.element) { thronglet.element.style.opacity = 0; setTimeout(() => { if(thronglet.element) thronglet.element.remove(); }, 500); } }, 3000); | |
} | |
function wander(thronglet) { | |
if (!gameActive || thronglet.isDead) return; | |
const moveDist = 0.8; // Further reduced movement distance | |
const currentX = parseFloat(thronglet.element.style.left); const currentY = parseFloat(thronglet.element.style.top); | |
const newX = Math.max(5, Math.min(95, currentX + (Math.random() - 0.5) * moveDist * 2)); | |
const newY = Math.max(5, Math.min(95, currentY + (Math.random() - 0.5) * moveDist * 2)); | |
thronglet.element.style.left = `${newX}%`; thronglet.element.style.top = `${newY}%`; | |
if (thronglet.feedbackElement) { thronglet.feedbackElement.style.left = `${newX}%`; thronglet.feedbackElement.style.top = `${newY}%`; } | |
} | |
function updateThronglets() { | |
if (!gameActive) return; | |
const now = Date.now(); | |
const livingThronglets = thronglets.filter(t => !t.isDead); | |
livingThronglets.forEach(thronglet => { | |
const timeSinceLast = now - thronglet.lastInteraction; | |
const baseNeedRate = 0.15; const neglectMultiplier = 1 + Math.min(5, timeSinceLast / 10000); | |
thronglet.hunger = Math.min(100, thronglet.hunger + baseNeedRate * neglectMultiplier * 1.0); | |
thronglet.cleanliness = Math.min(100, thronglet.cleanliness + baseNeedRate * neglectMultiplier * 0.8); | |
thronglet.happiness = Math.max(0, thronglet.happiness - baseNeedRate * neglectMultiplier * 1.1); | |
let deathReason = null; | |
if (thronglet.hunger >= 100) deathReason = "starvation"; else if (thronglet.cleanliness >= 100) deathReason = "filth"; else if (thronglet.happiness <= 0) deathReason = "misery"; | |
if (deathReason) { killThronglet(thronglet, deathReason); return; } | |
// Duplication Logic | |
const canDuplicate = thronglet.happiness > 85 && thronglet.hunger < 15 && thronglet.cleanliness < 15 && timeSinceLast < 15000 && (now - thronglet.lastDuplication > 20000); | |
if (canDuplicate && Math.random() < 0.015) { | |
showFeedback(thronglet, 'π'); playSound('duplicate'); thronglet.lastDuplication = now; thronglet.lastInteraction = now; | |
setTimeout(() => { | |
const parentX = parseFloat(thronglet.element.style.left); const parentY = parseFloat(thronglet.element.style.top); | |
createThronglet(parentX + (Math.random() - 0.5) * 5, parentY + (Math.random() - 0.5) * 5); | |
}, 500); | |
} | |
// Wander even less often | |
if (Math.random() < 0.15) { wander(thronglet); } | |
}); | |
if (Math.random() < 0.2) updateUI(); // Update UI count periodically | |
} | |
// --- Tool Selection Function --- | |
function selectTool(toolButtonElement) { | |
// Get the ID to store the tool name | |
selectedTool = toolButtonElement.id.replace(/Btn|Button/g, ''); // Get base name like 'pointer', 'feed' | |
playSound('ui_click'); | |
// Remove 'selected' class from all buttons in the grid | |
actionGrid.querySelectorAll('button').forEach(btn => btn.classList.remove('selected')); | |
// Add 'selected' class to the clicked button | |
toolButtonElement.classList.add('selected'); | |
// console.log("Selected tool:", selectedTool); | |
} | |
// --- Event Handlers --- | |
egg.addEventListener('click', () => { | |
if (!gameActive || egg.classList.contains('hatching') || !document.contains(egg)) return; | |
initAudio(); // IMPORTANT: Call initAudio on first interaction! | |
egg.classList.add('hatching'); playSound('hatch'); | |
setTimeout(() => { | |
if(document.contains(egg)) egg.remove(); | |
createThronglet(45, 50); createThronglet(55, 50); createThronglet(50, 45); | |
}, 1000); | |
}); | |
// Centralized Action Grid Listener | |
actionGrid.addEventListener('click', (event) => { | |
if (event.target.tagName === 'BUTTON') { | |
const buttonElement = event.target; | |
const buttonId = buttonElement.id; | |
initAudio(); // Ensure audio context is active | |
// Select the tool visually first | |
selectTool(buttonElement); | |
// Perform immediate action for feed/clean | |
if (buttonId === 'feedButton') { | |
const living = thronglets.filter(t => !t.isDead); | |
if (living.length > 0) { | |
// Use reduce with initial value null to handle empty array | |
const target = living.reduce((p, c) => (p === null || c.hunger > p.hunger) ? c : p, null); | |
if (target) { // Check if a target was found | |
feedThronglet(target); | |
} else { | |
// This case shouldn't happen if living.length > 0, but good practice | |
playSound('error'); | |
} | |
} else { | |
playSound('error'); // No living thronglets | |
} | |
} else if (buttonId === 'cleanButton') { | |
const living = thronglets.filter(t => !t.isDead); | |
if (living.length > 0) { | |
// Use reduce with initial value null | |
const target = living.reduce((p, c) => (p === null || c.cleanliness > p.cleanliness) ? c : p, null); | |
if (target) { // Check if target found | |
cleanThronglet(target); | |
} else { | |
playSound('error'); | |
} | |
} else { | |
playSound('error'); // No living thronglets | |
} | |
} else if (buttonId !== 'pointerBtn') { | |
// Placeholder message/sound for other tools for now | |
console.log(`${selectedTool} tool selected (no action implemented)`); | |
// Optionally play a generic 'select' sound different from 'ui_click' | |
} | |
} | |
}); | |
// --- Initial Setup --- | |
function initGame() { | |
gameActive = true; updateUI(); | |
gameInterval = setInterval(updateThronglets, 400); | |
// Attempt to initialize audio context, but it requires user interaction first | |
// It will be properly initialized/resumed on the first click (e.g., egg click) | |
initAudio(); | |
selectTool(document.getElementById('pointerBtn')); // Set pointer as default selected tool visually | |
console.log("Game Initialized. Click the egg."); | |
} | |
initGame(); // Start the game | |
</script> | |
</body> | |
</html> |