/
CodeByKate
/
lastserver-game
Обзор
Документация
Войти
/
CodeByKate
/
lastserver-game
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
engine.js
157 строк
5 KB
CodeByKate
upload files
21 дек 2025, 21:44
21 дек 2025, 21:44
136e74f
Код
Авторство
О чём код?
/* engine.js Core game loop, input handling, timing, pause, restart, and top-level game state. */ export default class Engine { constructor({ canvas, ctx, width, height, targetFPS=60 }){ this.canvas = canvas; this.ctx = ctx; this.width = width; this.height = height; this.targetFPS = targetFPS; this.delta = 0; this.last = performance.now(); this.accumulator = 0; this.entitiesModule = null; this.running = false; this.paused = false; this.frameRequest = null; // Global gameplay state // Use seconds for decayTimer: total stability time (e.g. 300 = 5 minutes) this.decayTimerMax = 300; this.decayTimer = this.decayTimerMax; this.decayRate = 1.0; // seconds per second (counts down in real seconds) this.decayPaused = false; // when Max uses bug this.decisions = 0; this.gameOver = false; this.win = false; this.flashTimeout = null; // Input state this.keys = {}; this.setupInput(); } registerEntities(module){ this.entitiesModule = module; } setupInput(){ window.addEventListener('keydown', e=>{ // handle pause and restart keys at engine level if(e.key === 'p' || e.key === 'P') { this.pause(); e.preventDefault(); return; } if(e.key === 'r' || e.key === 'R') { this.restart(); e.preventDefault(); return; } this.keys[e.key] = true; // prevent default for arrow keys and space/tab to keep canvas focused if(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Tab',' '].includes(e.key)) e.preventDefault(); }); window.addEventListener('keyup', e=>{ this.keys[e.key] = false; }); } start(){ if(this.running) return; this.running = true; this.last = performance.now(); this.loop(); } pause(toggle){ this.paused = (toggle===undefined) ? !this.paused : !!toggle; } restart(){ // Reset core state and re-init entities this.decayTimer = this.decayTimerMax; this.decisions = 0; if(this.entitiesModule && typeof this.entitiesModule.reset === 'function') this.entitiesModule.reset(); this.paused = false; } loop(){ this.frameRequest = requestAnimationFrame((t)=>this._tick(t)); } _tick(now){ if(!this.running) return; const dt = Math.min(1000/30, now - this.last); // clamp large dt this.last = now; if(!this.paused && !this.gameOver && !this.win){ // Update decay timer (real seconds) if(!this.decayPaused){ this.decayTimer -= dt/1000 * this.decayRate; if(this.decayTimer < 0) this.decayTimer = 0; } // Update entities if(this.entitiesModule && typeof this.entitiesModule.update === 'function'){ this.entitiesModule.update(dt/1000, this); } } // Render if(this.entitiesModule && typeof this.entitiesModule.render === 'function'){ // clear this.ctx.fillStyle = '#000009'; this.ctx.fillRect(0,0,this.width,this.height); this.entitiesModule.render(this.ctx, this); } // Check global game over / win conditions and handle win timer if(this.entitiesModule){ const p1 = this.entitiesModule.players.lera; const p2 = this.entitiesModule.players.max; // Standard game over by falling far below: only when BOTH players have fallen const p1Fallen = (p1.y > this.height + 50); const p2Fallen = (p2.y > this.height + 50); if(p1Fallen && p2Fallen && !this.gameOver && !this.win){ this.gameOver = true; this.gameOverReason = 'fall'; // small flash this._triggerFlash(); } // Timer reached zero -> game over and instant platform collapse if(this.decayTimer <= 0 && !this.gameOver){ this.gameOver = true; this.gameOverReason = 'decay'; // make all decaying platforms vanish immediately if(this.entitiesModule && this.entitiesModule.platforms){ this.entitiesModule.platforms.forEach(p=>{ if(p.decaying) p.exists = false; }); } this._triggerFlash('red'); } // If win has just been detected by entities module, mark handled once. // We pause gameplay and show a green flash; level transition is managed externally (levels manager). if(this.win && !this._winHandled){ this._winHandled = true; this.paused = true; this._triggerFlash('green', 2000); // NOTE: do not auto-restart here — LevelManager controls next-level flow. } } // Next frame this.loop(); } _triggerFlash(color='red', duration=300){ const canvas = this.canvas; if(color === 'red'){ canvas.classList.add('flash-red'); clearTimeout(this.flashTimeout); this.flashTimeout = setTimeout(()=>canvas.classList.remove('flash-red'), duration); } else if(color === 'green'){ // apply brightness filter to entire root for pronounced flash const root = canvas.parentElement; root.style.transition = `filter ${duration/1000}s linear`; root.style.filter = 'brightness(1.5)'; clearTimeout(this.flashTimeout); this.flashTimeout = setTimeout(()=>{ root.style.filter = ''; }, duration); } } }