feat: initialize TRXTD browser tower defense
This commit is contained in:
commit
c4347f8420
34 changed files with 8302 additions and 0 deletions
72
scripts/test-mp.mts
Normal file
72
scripts/test-mp.mts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Lockstep determinism check: two engines receiving the same actions at the
|
||||
* same ticks must produce identical game state (the foundation of multiplayer
|
||||
* without state sync).
|
||||
*/
|
||||
import { GameEngine } from '../src/game/engine.ts'
|
||||
|
||||
const e1 = new GameEngine()
|
||||
const e2 = new GameEngine()
|
||||
for (const e of [e1, e2]) {
|
||||
e.startGame('normal')
|
||||
e.mpGameActive = true
|
||||
e.mpMode = 'coop'
|
||||
e.coopHpMul = 1.7
|
||||
}
|
||||
|
||||
// scripted action stream (tick, action) – like the relayed server order
|
||||
const script: [number, Parameters<GameEngine['applyAction']>[0]][] = [
|
||||
[5, { type: 'build', kind: 'arrow', tx: 8, ty: 5 }],
|
||||
[5, { type: 'build', kind: 'frost', tx: 3, ty: 0 }],
|
||||
[10, { type: 'wave' }],
|
||||
[200, { type: 'build', kind: 'cannon', tx: 7, ty: 0 }],
|
||||
[400, { type: 'upgrade', towerId: 1 }],
|
||||
[400, { type: 'build', kind: 'tesla', tx: 12, ty: 6 }],
|
||||
[700, { type: 'wave' }],
|
||||
[900, { type: 'obstacle', tx: 19, ty: 0 }],
|
||||
[1200, { type: 'build', kind: 'laser', tx: 18, ty: 4 }],
|
||||
[1500, { type: 'speed', s: 2 }],
|
||||
[1800, { type: 'wave' }],
|
||||
]
|
||||
|
||||
let idx = 0
|
||||
const TOTAL_TICKS = 3600 // ~2 Minuten Spielzeit @30 Ticks/s
|
||||
let p0: ReturnType<GameEngine['applyAction']> = true
|
||||
let p1 = p0
|
||||
|
||||
for (let tick = 1; tick <= TOTAL_TICKS; tick++) {
|
||||
while (idx < script.length && script[idx][0] <= tick) {
|
||||
const a = script[idx][1]
|
||||
p0 = e1.applyAction(a, idx % 2) // alternate acting players
|
||||
p1 = e2.applyAction(a, idx % 2)
|
||||
idx++
|
||||
}
|
||||
e1.update(1 / 30)
|
||||
e2.update(1 / 30)
|
||||
}
|
||||
|
||||
function state(e: GameEngine): string {
|
||||
return JSON.stringify({
|
||||
money: Math.floor(e.money * 1000),
|
||||
lives: e.lives,
|
||||
score: e.score,
|
||||
kills: e.kills,
|
||||
wave: e.waveNo,
|
||||
phase: e.phase,
|
||||
enemies: e.enemies.map((x) => [x.id, Math.round(x.hp * 1000), x.wp]),
|
||||
towers: e.towers.map((t) => [t.id, t.kind, t.level, t.kills, Math.round(t.damageDealt)]),
|
||||
queue: e.spawnQueue.map((s) => [Math.round(s.t * 1000), s.kind]),
|
||||
blocked: e.blockedCells.size,
|
||||
})
|
||||
}
|
||||
|
||||
const s1 = state(e1)
|
||||
const s2 = state(e2)
|
||||
console.log('Aktionen ausgeführt:', idx, '| identisch:', p0 === p1)
|
||||
console.log('Zustand identisch:', s1 === s2)
|
||||
if (s1 !== s2) {
|
||||
console.log('e1:', s1.slice(0, 400))
|
||||
console.log('e2:', s2.slice(0, 400))
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`OK: beide Engines identisch nach ${TOTAL_TICKS} Ticks | Welle ${e1.waveNo} | ❤ ${e1.lives} | 🪙 ${Math.floor(e1.money)} | Türme ${e1.towers.length}`)
|
||||
Loading…
Add table
Add a link
Reference in a new issue