"""Base classes for states.""" from __future__ import annotations from typing import Protocol, TypeAlias import attrs import tcod.console import tcod.event import g class State(Protocol): """An abstract game state.""" __slots__ = () def on_event(self, event: tcod.event.Event) -> StateResult: """Called on events.""" def on_draw(self, console: tcod.console.Console) -> None: """Called when the state is being drawn.""" @attrs.define() class Push: """Push a new state on top of the stack.""" state: State @attrs.define() class Pop: """Remove the current state from the stack.""" @attrs.define() class Reset: """Replace the entire stack with a new state.""" state: State StateResult: TypeAlias = "Push | Pop | Reset | None" """Union of state results.""" def main_draw() -> None: """Render and present the active state.""" if not g.states: return g.console.clear() g.states[-1].on_draw(g.console) g.context.present(g.console) def apply_state_result(result: StateResult) -> None: """Apply a StateResult to `g.states`.""" match result: case Push(state=state): g.states.append(state) case Pop(): g.states.pop() case Reset(state=state): while g.states: apply_state_result(Pop()) apply_state_result(Push(state)) case None: pass case _: raise TypeError(result) def main_loop() -> None: """Run the active state forever.""" while g.states: main_draw() for event in tcod.event.wait(): tile_event = g.context.convert_event(event) if g.states: apply_state_result(g.states[-1].on_event(tile_event)) def get_previous_state(state: State) -> State | None: """Return the state before `state` in the stack if it exists.""" current_index = next(index for index, value in enumerate(g.states) if value is state) return g.states[current_index - 1] if current_index > 0 else None def draw_previous_states(state: State, console: tcod.console.Console, dim: bool = True) -> None: """Draw previous states, optionally dimming all but the active state.""" prev_state = get_previous_state(state) if prev_state is None: return prev_state.on_draw(console) if dim and state is g.states[-1]: console.rgb["fg"] //= 4 console.rgb["bg"] //= 4