86 lines
3.6 KiB
Python
86 lines
3.6 KiB
Python
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
|
|
hack_difficulty: int | None = None # None = not hackable; otherwise the tech check DC (see
|
|
# resources/logic/hacking.py::try_hack_tile_layer) - e.g. a locked door
|
|
hackable_unlocked_def_id: str | None = None # this layer's own def to swap to on a
|
|
# successful hack (e.g. a locked door's def swaps to its open counterpart) - same
|
|
# "replace the layer's def_id in place" idiom construction.py's build/deconstruct use
|
|
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
|