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