feat(sim): game state, state machine and audio cues

This commit is contained in:
dualfroz committed 2026-08-26 09:57:08 +02:00
1 parent d79de4674d
commit 251d79a348
3 files changed
+164

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
class SoundEvent:
__slots__ = ("name", "x", "frame")
def __init__(self, name, x, frame):
self.name = name
self.x = x
self.frame = frame
class AudioBus:
def __init__(self, sink=None, historyCap=64):
self.sink = sink
self.events = []
self.historyCap = historyCap
self.frame = 0
self.totalEmitted = 0
def setSink(self, sink):
self.sink = sink
def tick(self):
self.frame += 1
def play(self, name, x=None):
event = SoundEvent(name, x, self.frame)
self.events.append(event)
if len(self.events) > self.historyCap:
self.events.pop(0)
self.totalEmitted += 1
if self.sink is not None:
self.sink(event)
def drain(self):
drained = self.events
self.events = []
return drained
+78
View File
@@ -0,0 +1,78 @@
from .world import World
MENU = "menu"
PLAYING = "playing"
PAUSED = "paused"
GAMEOVER = "gameover"
COMPLETE = "complete"
VICTORY = "victory"
class Game:
def __init__(self, levels, rng=None):
self.levels = list(levels)
self.levelIndex = 0
self.mode = MENU
self.world = None
self.rng = rng
self.frame = 0
self.transitions = []
def startLevel(self, index):
self.levelIndex = index
self.world = World(self.levels[index], rng=self.rng)
self.setMode(PLAYING)
def start(self):
self.startLevel(0)
def setMode(self, mode):
if mode != self.mode:
self.transitions.append((self.mode, mode, self.frame))
self.mode = mode
def update(self, command):
self.frame += 1
handler = {
MENU: self.updateMenu,
PLAYING: self.updatePlaying,
PAUSED: self.updatePaused,
GAMEOVER: self.updateGameover,
COMPLETE: self.updateComplete,
VICTORY: self.updateVictory,
}[self.mode]
handler(command)
def updateMenu(self, command):
if command.confirm or command.jump:
self.start()
def updatePlaying(self, command):
if command.pause:
self.setMode(PAUSED)
return
self.world.step(command)
if self.world.gameOver:
self.setMode(GAMEOVER)
elif self.world.completed:
if self.levelIndex + 1 < len(self.levels):
self.setMode(COMPLETE)
else:
self.setMode(VICTORY)
def updatePaused(self, command):
if command.pause:
self.setMode(PLAYING)
def updateGameover(self, command):
if command.confirm:
self.setMode(MENU)
def updateComplete(self, command):
if command.confirm:
self.startLevel(self.levelIndex + 1)
def updateVictory(self, command):
if command.confirm:
self.levelIndex = 0
self.setMode(MENU)
+50
View File
@@ -0,0 +1,50 @@
class State:
name = "state"
def onEnter(self, owner, world):
pass
def onExit(self, owner, world):
pass
def update(self, owner, world):
return None
class StateMachine:
def __init__(self, owner):
self.owner = owner
self.states = {}
self.current = None
self.previous = None
self.timeInState = 0
def addState(self, state):
self.states[state.name] = state
return self
def setInitial(self, name, world=None):
self.current = self.states[name]
self.timeInState = 0
if world is not None:
self.current.onEnter(self.owner, world)
return self
def transition(self, name, world):
if name == self.current.name:
return
self.previous = self.current
self.current.onExit(self.owner, world)
self.current = self.states[name]
self.timeInState = 0
self.current.onEnter(self.owner, world)
def update(self, world):
self.timeInState += 1
nextState = self.current.update(self.owner, world)
if nextState is not None and nextState != self.current.name:
self.transition(nextState, world)
@property
def stateName(self):
return self.current.name if self.current else None