187 lines
7.4 KiB
Python
187 lines
7.4 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from engine.character import Character
|
|
from engine.defs import DefRegistry
|
|
from engine.entity import EntityPosition
|
|
from engine.map_loader import MapLoader
|
|
from engine.tile import TileLayerInstance
|
|
from engine.world import World
|
|
from resources.logic.steering import advance_all_ship_rotations, advance_ship_rotation, at_helm, try_steer_ship, try_turn_ship
|
|
|
|
DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs"
|
|
|
|
|
|
@pytest.fixture()
|
|
def registry():
|
|
return DefRegistry.load(DEFS_DIR)
|
|
|
|
|
|
def make_human(registry, **kwargs) -> Character:
|
|
return Character(species_id="human", default_body_parts=registry.new_default_body_parts("human"), **kwargs)
|
|
|
|
|
|
def build_demo_world(registry) -> World:
|
|
loader = MapLoader(DEFS_DIR / "maps")
|
|
demo_world = loader.load("demo_world.json")
|
|
ship = demo_world.embeddings[0].child_map
|
|
world = World(registry=registry)
|
|
world.add_map(demo_world)
|
|
world.add_map(ship)
|
|
return world
|
|
|
|
|
|
def test_at_helm_true_when_standing_on_the_helm_tile(registry):
|
|
world = build_demo_world(registry)
|
|
sailor = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
embedding = at_helm(sailor, world)
|
|
assert embedding is not None
|
|
assert embedding.child_map.map_id == "ship_small"
|
|
|
|
|
|
def test_at_helm_none_elsewhere_on_the_ship(registry):
|
|
world = build_demo_world(registry)
|
|
sailor = make_human(registry, position=EntityPosition("ship_small", 1, 1, 0))
|
|
assert at_helm(sailor, world) is None
|
|
|
|
|
|
def test_at_helm_none_when_not_aboard_an_embedded_map(registry):
|
|
world = build_demo_world(registry)
|
|
person = make_human(registry, position=EntityPosition("demo_world", 1, 1, 0))
|
|
assert at_helm(person, world) is None
|
|
|
|
|
|
def test_steering_moves_the_ship_and_leaves_aboard_entities_local_position_untouched(registry):
|
|
world = build_demo_world(registry)
|
|
ship = world.maps["ship_small"]
|
|
embedding = ship.parent_embedding
|
|
original_anchor = embedding.anchor
|
|
|
|
helmsman = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
passenger = make_human(registry, position=EntityPosition("ship_small", 1, 1, 0))
|
|
|
|
assert try_steer_ship(helmsman, world, 0, 1, 0) is True
|
|
assert embedding.anchor == (original_anchor[0], original_anchor[1] + 1, original_anchor[2])
|
|
# nobody aboard needed to move - their local coordinates are exactly as before
|
|
assert (helmsman.position.x, helmsman.position.y) == (2, 1)
|
|
assert (passenger.position.x, passenger.position.y) == (1, 1)
|
|
|
|
|
|
def test_steering_fails_if_destination_runs_into_something_unwalkable(registry):
|
|
world = build_demo_world(registry)
|
|
parent = world.maps["demo_world"]
|
|
# ship's footprint is x:6-9, y:6-8; moving west by 1 would occupy x:5-8, y:6-8 -
|
|
# put a wall right in that path
|
|
parent.get_tile(5, 7, 0).room = TileLayerInstance("wood_wall")
|
|
|
|
helmsman = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
before = embedding.anchor
|
|
assert try_steer_ship(helmsman, world, -1, 0, 0) is False
|
|
assert embedding.anchor == before
|
|
|
|
|
|
def test_steering_fails_off_the_parent_map_edge(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
embedding.move_to((0, 0, 0)) # right at the parent's edge
|
|
|
|
helmsman = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
assert try_steer_ship(helmsman, world, -1, 0, 0) is False
|
|
|
|
|
|
def test_turning_the_ship_sets_desired_rotation_but_not_the_actual_one_yet(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
helmsman = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
|
|
assert try_turn_ship(helmsman, world, 1) is True
|
|
assert embedding.desired_rotation == 1
|
|
assert embedding.rotation == 0 # doesn't snap - advance_ship_rotation steps it there
|
|
|
|
|
|
def test_repeated_turn_commands_stack_the_desired_rotation(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
helmsman = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
|
|
try_turn_ship(helmsman, world, 1)
|
|
try_turn_ship(helmsman, world, 1)
|
|
assert embedding.desired_rotation == 2
|
|
|
|
|
|
def test_advance_ship_rotation_steps_one_quarter_turn_at_a_time(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
helmsman = make_human(registry, position=EntityPosition("ship_small", 2, 1, 0))
|
|
embedding.max_rotation_speed = 90.0 # 1 quarter-turn/second
|
|
|
|
try_turn_ship(helmsman, world, 2) # desired_rotation = 2, two steps needed
|
|
assert advance_ship_rotation(embedding, world, now=0.0) is True
|
|
assert embedding.rotation == 1
|
|
# not enough time has passed yet for the second step
|
|
assert advance_ship_rotation(embedding, world, now=0.5) is False
|
|
assert embedding.rotation == 1
|
|
assert advance_ship_rotation(embedding, world, now=1.0) is True
|
|
assert embedding.rotation == 2
|
|
|
|
|
|
def test_advance_ship_rotation_takes_the_shorter_direction(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
embedding.desired_rotation = 3
|
|
advance_ship_rotation(embedding, world, now=0.0)
|
|
assert embedding.rotation == 3 # one counter-clockwise step, not three clockwise ones
|
|
|
|
|
|
def test_advance_ship_rotation_no_op_when_already_at_desired(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
assert advance_ship_rotation(embedding, world, now=0.0) is False
|
|
assert embedding.rotation == 0
|
|
|
|
|
|
def test_slower_max_rotation_speed_takes_longer_per_step(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
embedding.max_rotation_speed = 30.0 # 3 seconds per quarter-turn
|
|
embedding.desired_rotation = 1
|
|
|
|
assert advance_ship_rotation(embedding, world, now=0.0) is True
|
|
assert embedding.rotation == 1
|
|
assert embedding.rotation_ready_at == pytest.approx(3.0)
|
|
|
|
|
|
def test_advance_ship_rotation_pauses_if_blocked_but_keeps_desired_heading(registry):
|
|
world = build_demo_world(registry)
|
|
parent = world.maps["demo_world"]
|
|
ship = world.maps["ship_small"]
|
|
embedding = ship.parent_embedding
|
|
# block the intermediate rotated footprint so the step can't complete
|
|
for x in range(parent.width):
|
|
for y in range(parent.height):
|
|
parent.get_tile(x, y, 0).room = TileLayerInstance("wood_wall")
|
|
embedding.desired_rotation = 1
|
|
|
|
assert advance_ship_rotation(embedding, world, now=0.0) is False
|
|
assert embedding.rotation == 0
|
|
assert embedding.desired_rotation == 1 # still wants to get there, just blocked for now
|
|
|
|
|
|
def test_advance_all_ship_rotations_advances_every_embedding(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
embedding.desired_rotation = 1
|
|
advance_all_ship_rotations(world, now=0.0)
|
|
assert embedding.rotation == 1
|
|
|
|
|
|
def test_non_helmsman_cannot_steer(registry):
|
|
world = build_demo_world(registry)
|
|
embedding = world.maps["ship_small"].parent_embedding
|
|
before = embedding.anchor
|
|
bystander = make_human(registry, position=EntityPosition("ship_small", 1, 1, 0))
|
|
assert try_steer_ship(bystander, world, 1, 0, 0) is False
|
|
assert embedding.anchor == before
|