from __future__ import annotations from dataclasses import dataclass, field TILE_LAYERS = ("subfloor", "flooring", "room", "roof") @dataclass(frozen=True) class TileLayerDef: id: str layer: str texture: str | None = None color: tuple[int, int, int] = (255, 0, 255) walkable: bool = True blocks_los: bool = False deconstruct_item_id: str | None = None # item yielded by a careful (right-click) deconstruction is_helm: bool = False # standing here lets a character steer the embedded map it's part of staircase_direction: str | None = None # "up" or "down" - see Tile.staircase_direction speed_multiplier: float = 1.0 # e.g. mud/water slow movement - see World.speed_multiplier_at extra: dict = field(default_factory=dict) @dataclass class TileLayerInstance: """A layer's actual per-cell state (as opposed to its static def).""" def_id: str integrity: float = 100.0 @dataclass class Tile: subfloor: TileLayerInstance | None = None flooring: TileLayerInstance | None = None room: TileLayerInstance | None = None roof: TileLayerInstance | None = None def get_layer(self, layer: str) -> TileLayerInstance | None: return getattr(self, layer) def has_any_layer(self) -> bool: """False for a completely bare/unset tile - e.g. the open air a generated-terrain column leaves above its own surface when nothing (slope/cave) was placed there. See Renderer's depth-shading: it looks straight through tiles like this to whatever's below, the same way World._apply_gravity does for movement. """ return any(self.get_layer(layer) is not None for layer in TILE_LAYERS) def set_layer(self, layer: str, instance: TileLayerInstance | None) -> None: setattr(self, layer, instance) def layer_id(self, layer: str) -> str | None: instance = self.get_layer(layer) return instance.def_id if instance is not None else None def is_walkable(self, registry) -> bool: for layer in ("room", "flooring"): layer_def = registry.get_tile_layer_def(layer, self.layer_id(layer)) if layer_def is not None and not layer_def.walkable: return False return True def staircase_direction(self, registry) -> str | None: """'up'/'down' if either layer here is a staircase/ladder, else None.""" for layer in ("room", "flooring"): layer_def = registry.get_tile_layer_def(layer, self.layer_id(layer)) if layer_def is not None and layer_def.staircase_direction is not None: return layer_def.staircase_direction return None def speed_multiplier(self, registry) -> float: """The most restrictive speed_multiplier among this tile's layers (e.g. mud/water flooring slowing movement) - 1.0 (no slow) if nothing here restricts it. """ multiplier = 1.0 for layer in ("room", "flooring"): layer_def = registry.get_tile_layer_def(layer, self.layer_id(layer)) if layer_def is not None: multiplier = min(multiplier, layer_def.speed_multiplier) return multiplier