Mi2dRPGamEng/engine/world_creation.py

71 lines
2.7 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, field
from engine.defs import DefRegistry
from engine.map_loader import MapLoader
from engine.world import World
@dataclass(frozen=True)
class WorldTemplate:
"""One selectable starting-world option on the 'create world' screen.
For now every template is just a fixed map file this engine ships with. Planned follow-up
game modes (campaign/survival/creative) will add templates that build a World from
procedural generation instead of a map_file - map_file is left optional so those can slot
in as templates with map_file=None and their own generator, without changing this shape.
"""
id: str
name: str
description: str = ""
map_file: str | None = None # resources/defs/maps/*.json, loaded via MapLoader
AVAILABLE_WORLD_TEMPLATES: list[WorldTemplate] = [
WorldTemplate(
id="test_map",
name="Test Map",
description="A small sample field with a docked, rotatable two-deck ship - exercises "
"the engine's core mechanics (embedding, steering, staircases, weather).",
map_file="harbor_world.json",
),
]
@dataclass
class WorldCreationMenu:
"""Pure interaction state for the 'create world' screen: pick which starting world template
to build a fresh World from. No rendering here - same boundary as StartScreen/CharacterMenu
(see their docstrings in this module and engine/ui.py) - this is the state/logic side: which
templates exist, which is selected, and handing back the chosen template once confirmed.
"""
templates: list[WorldTemplate] = field(default_factory=lambda: list(AVAILABLE_WORLD_TEMPLATES))
selected_index: int | None = None
def select(self, index: int) -> None:
if 0 <= index < len(self.templates):
self.selected_index = index
def selected_template(self) -> WorldTemplate | None:
if self.selected_index is None:
return None
return self.templates[self.selected_index]
def build_world_from_template(template: WorldTemplate, registry: DefRegistry, map_loader: MapLoader) -> World:
"""Builds an empty (player-less) World from a template's map - the "confirm" action once a
WorldCreationMenu selection is made. Spawning/joining a character into it is a separate
step - see engine/start_screen.py::join_world_with_character.
"""
if template.map_file is None:
raise NotImplementedError(f"world template {template.id!r} has no map_file and no generator yet")
world = World(registry=registry)
root_map = map_loader.load(template.map_file)
world.add_map(root_map)
for embedding in root_map.embeddings:
world.add_map(embedding.child_map)
return world