/* ============================================================ Wildlife Kingdom — js/main.js Boot · animated hero menu · game loop · autosave · resizing ============================================================ */ (function(){ 'use strict'; const WK = window.WK; const Main = (WK.Main = {}); let canvas, renderer, cam, input; let game=null; let mode='menu'; let heroGame=null; let heroT=0; let lastT=performance.now(); let saveT=0; /* ---------------- boot ---------------- */ function boot(){ canvas=document.getElementById('game'); renderer=new WK.Renderer(canvas); cam=new WK.Camera(); cam.resize(renderer.cssW,renderer.cssH); const small=Math.min(window.innerWidth,window.innerHeight)<720; cam.zoom=small?0.8:1.05; input=new WK.Input(canvas,null,cam,renderer,WK.UI); // QA hooks: ?autostart=1&fast=NN jumps straight into a running zoo const qa=new URLSearchParams(location.search); Main.qaMode=qa.has('autostart'); window.addEventListener('resize',onResize); document.addEventListener('visibilitychange',()=>{ if(document.hidden&&mode==='game'&&game){ game.save(); } }); // menu buttons const cont=document.getElementById('btn-continue'); cont.disabled=!WK.Game.hasSave(); cont.onclick=()=>{ click(); const g=WK.Game.load(); if(g) enterGame(g); else { startNew(); } }; document.getElementById('btn-new').onclick=()=>{ click(); startNew(); }; document.getElementById('btn-how').onclick=()=>{ click(); WK.UI._onHowClose=()=>{}; WK.UI.howtoModal(true); }; function click(){ WK.AudioSys.ensure(); WK.AudioSys.sfx('click'); } startHero(); if(Main.qaMode){ startNew(); const steps=parseInt(qa.get('fast')||'0',10)||0; for(let i=0;i{ WK.UI.howtoModal(false); },350); g.notify('Welcome to Wildlife Kingdom! Follow the missions panel.','gold'); } // camera on the entrance const e=g.exitTile||[g.world.cols/2,g.world.rows/2]; cam.centerOnTile(e[0],e[1]-4); cam.clampToWorld(g.world); WK.AudioSys.setScene('game'); if(WK.AudioSys.enabled&&!WK.AudioSys.musicOn===false){} /* music pref respected internally */ } function exitToMenu(){ /* kept simple: no in-game exit button besides reload */ } /* ---------------- main loop ---------------- */ function loop(now){ requestAnimationFrame(loop); let dt=(now-lastT)/1000; lastT=now; if(dt>0.12) dt=0.12; input.update(dt); if(mode==='menu'){ if(heroGame){ heroGame.timeAbs+=dt; const sdt=dt; // gentle life even on menu heroGame.minutes+=sdt*2; if(heroGame.minutes>=1440)heroGame.minutes-=1440; heroGame.spawnAcc+=sdt*0.02; for(const a of heroGame.animals) a.update(sdt*0.6,heroGame); for(const gu of heroGame.guests) gu.update(sdt*0.75); for(const s of heroGame.staff) s.update(sdt,heroGame); heroGame.particles.update(sdt); heroDrift(dt); renderer.frame(heroGame,cam,{}); } return; } // gameplay game.tick(dt); const grid=!!(WK.UI.tool||WK.UI.bulldoze); let tint=null; if(input.hover&&(WK.UI.tool&&['fences','animals'].includes(WK.UI.tool.cat)||WK.UI.bulldoze)){ game.world.ensureRegions(); const rid=game.world.regionOf(input.hover.x,input.hover.y); if(rid>=0){ const reg=game.world.regions.find(r=>r.id===rid); if(reg&®.openEdges===0&&!reg.hasPath) tint=rid; else if(WK.UI.tool&&WK.UI.tool.cat==='animals') tint=rid; } } renderer.frame(game,cam,{ grid, tintRegionId:tint, overlay:(ctx,c)=>input.overlay(ctx,c), }); WK.UI.updateHUD(); saveT+=dt; if(saveT>30){ saveT=0; if(game.save()){} } } window.addEventListener('DOMContentLoaded',boot); })();