This commit is contained in:
2026-06-04 17:59:53 +08:00
parent e6c27ac662
commit 0908495d6f
66 changed files with 4230 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
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()