Files
matrix-show/backend/app/services/game_state_manager.py
T
2026-06-04 17:59:53 +08:00

143 lines
4.9 KiB
Python

from datetime import datetime
from typing import Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.orm import GameState
from app.models.schemas import GamePhase, GameStateResponse
class GameStateManager:
PHASE_ORDER = GamePhase.ordered_phases()
PHASE_COUNT = len(PHASE_ORDER)
async def get_state(self, session: AsyncSession) -> Optional[GameState]:
result = await session.execute(
select(GameState).order_by(GameState.created_at.desc()).limit(1)
)
return result.scalars().first()
async def get_or_create_state(self, session: AsyncSession) -> GameState:
state = await self.get_state(session)
if state is None:
state = GameState(
current_phase=GamePhase.INTRO.value,
is_running=False,
is_paused=False,
)
session.add(state)
await session.commit()
await session.refresh(state)
return state
async def next_phase(self, session: AsyncSession) -> GameState:
state = await self.get_or_create_state(session)
current = state.current_phase
try:
idx = self.PHASE_ORDER.index(current)
if idx < self.PHASE_COUNT - 1:
state.current_phase = self.PHASE_ORDER[idx + 1]
except ValueError:
pass
state.phase_started_at = datetime.utcnow()
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
async def prev_phase(self, session: AsyncSession) -> GameState:
state = await self.get_or_create_state(session)
current = state.current_phase
try:
idx = self.PHASE_ORDER.index(current)
if idx > 0:
state.current_phase = self.PHASE_ORDER[idx - 1]
except ValueError:
pass
state.phase_started_at = datetime.utcnow()
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
def get_current_phase(self, state: GameState) -> str:
return state.current_phase
def get_progress_percent(self, state: GameState) -> float:
try:
idx = self.PHASE_ORDER.index(state.current_phase)
return (idx / max(self.PHASE_COUNT - 1, 1)) * 100
except ValueError:
return 0.0
async def start_game(self, session: AsyncSession, script_id: str) -> GameState:
state = await self.get_or_create_state(session)
state.active_script_id = script_id
state.current_phase = GamePhase.INTRO.value
state.is_running = True
state.is_paused = False
state.phase_started_at = datetime.utcnow()
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
async def pause_game(self, session: AsyncSession) -> GameState:
state = await self.get_or_create_state(session)
state.is_paused = True
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
async def resume_game(self, session: AsyncSession) -> GameState:
state = await self.get_or_create_state(session)
state.is_paused = False
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
async def reset(self, session: AsyncSession) -> GameState:
state = await self.get_or_create_state(session)
state.current_phase = GamePhase.INTRO.value
state.current_speaker_id = None
state.speaker_order = []
state.active_script_id = None
state.is_running = False
state.is_paused = False
state.phase_started_at = None
state.config = {}
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
async def set_speaker(self, session: AsyncSession, character_id: str) -> GameState:
state = await self.get_or_create_state(session)
state.current_speaker_id = character_id
state.updated_at = datetime.utcnow()
await session.commit()
await session.refresh(state)
return state
def to_response(self, state: GameState) -> GameStateResponse:
return GameStateResponse(
id=state.id,
current_phase=GamePhase(state.current_phase),
current_speaker_id=state.current_speaker_id,
speaker_order=state.speaker_order or [],
active_script_id=state.active_script_id,
is_running=state.is_running,
is_paused=state.is_paused,
phase_started_at=state.phase_started_at,
config=state.config or {},
progress_percent=self.get_progress_percent(state),
)
game_state_manager = GameStateManager()