Mi2dRPGamEng/resources/logic/steering.py

62 lines
2.5 KiB
Python

from __future__ import annotations
from engine.character import Character
from engine.map import MapEmbedding, rotated_size
from engine.world import World
def at_helm(character: Character, world: World) -> MapEmbedding | None:
"""Returns the embedding a character can steer, if they're standing on a helm tile of an
embedded map (e.g. a ship's wheel) - None otherwise.
"""
if character.position is None:
return None
map_ = world.maps[character.position.map_id]
embedding = map_.parent_embedding
if embedding is None:
return None
tile = map_.get_tile(character.position.x, character.position.y, character.position.z)
room_def = world.registry.get_tile_layer_def("room", tile.layer_id("room"))
if room_def is None or not room_def.is_helm:
return None
return embedding
def _footprint_is_clear(world: World, embedding: MapEmbedding, anchor: tuple[int, int, int], rotation: int) -> bool:
parent = embedding.parent_map
width, height = rotated_size(embedding.child_map.width, embedding.child_map.height, rotation)
for ly in range(height):
for lx in range(width):
px, py, pz = anchor[0] + lx, anchor[1] + ly, anchor[2]
if not parent.in_bounds(px, py, pz):
return False
if not parent.get_tile(px, py, pz).is_walkable(world.registry):
return False
return True
def try_steer_ship(character: Character, world: World, dx: int, dy: int, dz: int = 0) -> bool:
"""Moves the embedded map the character is helming by one step, if the destination footprint
is clear in the parent map. Entities aboard need no updates - only the anchor changes.
"""
embedding = at_helm(character, world)
if embedding is None:
return False
new_anchor = (embedding.anchor[0] + dx, embedding.anchor[1] + dy, embedding.anchor[2] + dz)
if not _footprint_is_clear(world, embedding, new_anchor, embedding.rotation):
return False
embedding.move_to(new_anchor)
return True
def try_turn_ship(character: Character, world: World, quarter_turns: int) -> bool:
"""Turns the embedded map the character is helming, if the rotated footprint is clear."""
embedding = at_helm(character, world)
if embedding is None:
return False
new_rotation = (embedding.rotation + quarter_turns) % 4
if not _footprint_is_clear(world, embedding, embedding.anchor, new_rotation):
return False
embedding.rotate_to(new_rotation)
return True