feat(sim): game state, state machine and audio cues
This commit is contained in:
1 parent
d79de4674d
commit
251d79a348
3 files changed
+164
No files matched your search
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Reference in new issue
Block a user