Mi2dRPGamEng/tests/test_ship_test_map.py

185 lines
7.8 KiB
Python

from pathlib import Path
import pytest
from engine.character import Character
from engine.defs import DefRegistry
from engine.entity import Entity, EntityPosition
from engine.inventory import Inventory
from engine.item import ItemInstance
from engine.map_loader import MapLoader
from engine.world import World
from resources.logic.actions import activate_hand
from resources.logic.steering import advance_ship_rotation, at_helm, try_steer_ship, try_turn_ship
from resources.logic.weather import is_exposed
DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs"
@pytest.fixture()
def registry():
return DefRegistry.load(DEFS_DIR)
@pytest.fixture()
def world_and_ship(registry):
loader = MapLoader(DEFS_DIR / "maps")
harbor = loader.load("harbor_world.json")
embedding = harbor.embeddings[0]
ship = embedding.child_map
world = World(registry=registry)
world.add_map(harbor)
world.add_map(ship)
return world, harbor, ship, embedding
def make_human(registry, **kwargs) -> Character:
return Character(species_id="human", default_body_parts=registry.new_default_body_parts("human"), **kwargs)
# --- embedding + rotation ---------------------------------------------------------------------
def test_ship_is_embedded_with_a_quarter_turn_rotation(world_and_ship):
_world, _harbor, _ship, embedding = world_and_ship
assert embedding.rotation == 1
assert embedding.child_map.map_id == "ship_test"
def test_rotated_footprint_places_local_cells_at_the_expected_parent_cells(world_and_ship):
_world, _harbor, _ship, embedding = world_and_ship
# rotation=1 on a square footprint: local (x, y) -> parent (anchor + (height-1-y, x))
assert embedding.local_to_parent(0, 2, 0) == (6, 4, 0) # hold doorway
assert embedding.local_to_parent(3, 3, 0) == (5, 7, 0) # hold stairs_up
assert embedding.local_to_parent(1, 1, 1) == (7, 5, 1) # helm (not reachable from parent, see below)
# --- boarding: parent field -> ship hold via a wall doorway -----------------------------------
def test_walking_through_the_doorway_boards_the_ship(world_and_ship, registry):
world, harbor, _ship, embedding = world_and_ship
door_parent = embedding.local_to_parent(0, 2, 0)
outside = (door_parent[0] - 1, door_parent[1], door_parent[2])
assert harbor.get_tile(*outside).is_walkable(registry)
player = Entity(position=EntityPosition("harbor_world", *outside))
assert world.try_move(player, 1, 0, 0) is True
assert (player.position.map_id, player.position.x, player.position.y, player.position.z) == (
"ship_test", 0, 2, 0,
)
def test_ship_perimeter_wall_blocks_boarding_anywhere_else(world_and_ship, registry):
world, _harbor, _ship, embedding = world_and_ship
wall_parent = embedding.local_to_parent(0, 1, 0) # a walled hold cell, not the doorway
outside = (wall_parent[0] - 1, wall_parent[1], wall_parent[2])
player = Entity(position=EntityPosition("harbor_world", *outside))
assert world.try_move(player, 1, 0, 0) is False
# --- staircase: hold (z=0) <-> main deck (z=1), same (x, y) column ----------------------------
def test_staircase_carries_player_from_main_deck_down_to_the_hold(world_and_ship, registry):
world, _harbor, _ship, _embedding = world_and_ship
player = make_human(registry, position=EntityPosition("ship_test", 3, 2, 1))
world.add_entity(player)
assert world.try_move(player, 0, 1, 0) is True # steps onto (3,3,1) stairs_down
assert (player.position.x, player.position.y, player.position.z) == (3, 3, 0)
def test_staircase_round_trip_back_up_after_stepping_off_and_on(world_and_ship, registry):
world, _harbor, _ship, _embedding = world_and_ship
player = make_human(registry, position=EntityPosition("ship_test", 3, 2, 1))
world.add_entity(player)
world.try_move(player, 0, 1, 0) # down to the hold, standing on stairs_up at (3,3,0)
assert world.try_move(player, -1, 0, 0) is True # step off, to (2,3,0)
assert player.position.z == 0
assert world.try_move(player, 1, 0, 0) is True # step back onto stairs_up at (3,3,0)
assert (player.position.x, player.position.y, player.position.z) == (3, 3, 1)
# --- steering: move and turn the ship from its helm --------------------------------------------
def test_standing_at_the_helm_is_detected(world_and_ship, registry):
world, _harbor, _ship, embedding = world_and_ship
helmsman = make_human(registry, position=EntityPosition("ship_test", 1, 1, 1))
world.add_entity(helmsman)
assert at_helm(helmsman, world) is embedding
def test_steering_moves_the_ships_anchor(world_and_ship, registry):
world, _harbor, _ship, embedding = world_and_ship
helmsman = make_human(registry, position=EntityPosition("ship_test", 1, 1, 1))
world.add_entity(helmsman)
original_anchor = embedding.anchor
assert try_steer_ship(helmsman, world, 1, 0, 0) is True
assert embedding.anchor == (original_anchor[0] + 1, original_anchor[1], original_anchor[2])
def test_turning_the_ship_changes_its_rotation(world_and_ship, registry):
world, _harbor, _ship, embedding = world_and_ship
helmsman = make_human(registry, position=EntityPosition("ship_test", 1, 1, 1))
world.add_entity(helmsman)
assert try_turn_ship(helmsman, world, 1) is True
assert embedding.desired_rotation == 2
# ship doesn't snap - it turns toward the desired heading at its own max_rotation_speed
assert advance_ship_rotation(embedding, world, now=0.0) is True
assert embedding.rotation == 2
def test_not_at_helm_cannot_steer(world_and_ship, registry):
world, _harbor, _ship, embedding = world_and_ship
bystander = make_human(registry, position=EntityPosition("ship_test", 3, 1, 1))
world.add_entity(bystander)
original_anchor = embedding.anchor
assert try_steer_ship(bystander, world, 1, 0, 0) is False
assert embedding.anchor == original_anchor
# --- weather exposure: roofed hold/helm booth vs. unroofed open deck --------------------------
def test_hold_is_sheltered_from_weather(world_and_ship, registry):
_world, _harbor, ship, _embedding = world_and_ship
assert is_exposed(ship, 2, 2, 0, registry) is False
def test_helm_booth_is_sheltered_but_the_open_deck_around_it_is_not(world_and_ship, registry):
_world, _harbor, ship, _embedding = world_and_ship
assert is_exposed(ship, 1, 1, 1, registry) is False # under the helm booth's roof
assert is_exposed(ship, 3, 1, 1, registry) is True # open deck, no roof
# --- deconstructor/constructor tools work on the ship's own tiles -----------------------------
def test_deconstructor_removes_the_helm_booths_wall(world_and_ship, registry):
world, _harbor, ship, _embedding = world_and_ship
character = make_human(registry, position=EntityPosition("ship_test", 1, 1, 1))
character.wield_item("right_hand", "multi_deconstructor", registry)
character.inventory = Inventory(4, 4)
world.add_entity(character)
assert ship.get_tile(2, 1, 1).room is not None
result = activate_hand(character, world, registry, "right_hand", (1, 0, 0))
assert result is True
assert ship.get_tile(2, 1, 1).room is None
def test_constructor_rebuilds_a_wall_from_carried_planks(world_and_ship, registry):
world, _harbor, ship, _embedding = world_and_ship
ship.get_tile(2, 1, 1).room = None # bare slot, as if just deconstructed
character = make_human(registry, position=EntityPosition("ship_test", 1, 1, 1))
character.wield_item("right_hand", "constructor_tool", registry)
character.inventory = Inventory(4, 4)
character.inventory.place(ItemInstance("planks-1", "wood_planks"), registry.get_item_def("wood_planks"), 0, 0)
world.add_entity(character)
result = activate_hand(character, world, registry, "right_hand", (1, 0, 0))
assert result is True
assert ship.get_tile(2, 1, 1).room.def_id == "wood_wall"