68 lines
2.7 KiB
Python
68 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
from engine.character import Character
|
|
from engine.character_library import CharacterLibrary
|
|
from engine.defs import DefRegistry
|
|
from engine.entity import EntityPosition
|
|
from engine.world import World
|
|
|
|
|
|
@dataclass
|
|
class StartScreen:
|
|
"""Pure interaction state for the pre-game start screen: pick one of the current account's
|
|
persistent characters (see engine/character_library.py::CharacterLibrary) to join a session
|
|
with - single-player continuation or joining someone else's multiplayer world with a
|
|
character built up over time.
|
|
|
|
No rendering here - same boundary as LayerPickerMenu/AbilityBar/CharacterMenu in
|
|
engine/ui.py: listing characters as clickable rows and drawing the screen itself is a
|
|
follow-up UI-layer task. This is the state/logic side: which characters exist for this
|
|
account, which one is currently selected, and handing off to join_selected_character()
|
|
once confirmed. Creating a brand new character is a separate flow - see
|
|
engine/character_creator.py.
|
|
"""
|
|
|
|
account_id: str
|
|
characters: list[tuple[str, str, str | None]] = field(default_factory=list) # (character_id, name, species_id)
|
|
selected_index: int | None = None
|
|
|
|
@classmethod
|
|
def load_for_account(cls, account_id: str, library: CharacterLibrary) -> "StartScreen":
|
|
return cls(account_id=account_id, characters=library.list_characters(account_id))
|
|
|
|
def refresh(self, library: CharacterLibrary) -> None:
|
|
"""Re-reads the character list (e.g. after creating or deleting one) and clears selection."""
|
|
self.characters = library.list_characters(self.account_id)
|
|
self.selected_index = None
|
|
|
|
def select(self, index: int) -> None:
|
|
if 0 <= index < len(self.characters):
|
|
self.selected_index = index
|
|
|
|
def selected_character_id(self) -> str | None:
|
|
if self.selected_index is None:
|
|
return None
|
|
return self.characters[self.selected_index][0]
|
|
|
|
|
|
def join_world_with_character(
|
|
world: World,
|
|
library: CharacterLibrary,
|
|
account_id: str,
|
|
character_id: str,
|
|
registry: DefRegistry,
|
|
spawn_position: EntityPosition,
|
|
) -> Character | None:
|
|
"""Loads a persistent character from the library and drops it into `world` at
|
|
`spawn_position` - the "confirm join" action once a StartScreen selection is made. Returns
|
|
None (no-op on `world`) if no such character exists for this account.
|
|
"""
|
|
character = library.load_character(account_id, character_id, registry)
|
|
if character is None:
|
|
return None
|
|
character.position = spawn_position
|
|
world.add_entity(character)
|
|
return character
|