182 lines
7.5 KiB
Python
182 lines
7.5 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 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.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"
|