182 lines
6.7 KiB
Python
182 lines
6.7 KiB
Python
from engine.entity import Entity, EntityPosition
|
|
from engine.map import Map
|
|
from engine.tile import Tile, TileLayerDef, TileLayerInstance
|
|
from engine.world import World
|
|
|
|
|
|
def make_world():
|
|
parent = Map("world", 10, 10, 1)
|
|
child = Map("ship", 4, 3, 1)
|
|
for x in range(4):
|
|
for y in range(3):
|
|
child.set_tile(x, y, 0, Tile(flooring=TileLayerInstance("wood_deck")))
|
|
# leave (2, 0, 0) open as the doorway, wall the rest of the front/back/sides
|
|
for x, y in [(0, 0), (1, 0), (3, 0), (0, 2), (3, 2)]:
|
|
tile = child.get_tile(x, y, 0)
|
|
tile.room = TileLayerInstance("wood_wall")
|
|
|
|
for x in range(10):
|
|
for y in range(10):
|
|
parent.set_tile(x, y, 0, Tile(flooring=TileLayerInstance("grass")))
|
|
|
|
parent.embed(child, (6, 6, 0))
|
|
return parent, child
|
|
|
|
|
|
def test_local_to_parent_and_back_are_inverses():
|
|
_parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
for lx, ly, lz in [(0, 0, 0), (2, 0, 0), (3, 2, 0)]:
|
|
px, py, pz = embedding.local_to_parent(lx, ly, lz)
|
|
assert embedding.parent_to_local(px, py, pz) == (lx, ly, lz)
|
|
|
|
|
|
def test_embedding_at_detects_child_footprint():
|
|
parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
assert parent.embedding_at(8, 6, 0) is embedding # inside ship footprint (6..9, 6..8)
|
|
assert parent.embedding_at(0, 0, 0) is None # outside ship footprint
|
|
|
|
|
|
def test_embed_defaults_max_rotation_speed_and_desired_rotation():
|
|
_parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
assert embedding.max_rotation_speed == 90.0
|
|
assert embedding.desired_rotation == embedding.rotation == 0
|
|
assert embedding.rotation_ready_at == 0.0
|
|
|
|
|
|
def test_embed_accepts_a_custom_max_rotation_speed():
|
|
parent = Map("world", 10, 10, 1)
|
|
child = Map("ship", 2, 2, 1)
|
|
embedding = parent.embed(child, (0, 0, 0), rotation=1, max_rotation_speed=45.0)
|
|
assert embedding.max_rotation_speed == 45.0
|
|
assert embedding.rotation == embedding.desired_rotation == 1
|
|
|
|
|
|
class FakeRegistry:
|
|
def get_tile_layer_def(self, layer, def_id):
|
|
if layer == "room" and def_id == "wood_wall":
|
|
return TileLayerDef(id="wood_wall", layer="room", walkable=False, blocks_los=True)
|
|
return None
|
|
|
|
|
|
def test_try_move_boards_ship_from_parent_map():
|
|
parent, child = make_world()
|
|
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.registry = FakeRegistry()
|
|
|
|
player = Entity(position=EntityPosition("world", 7, 5, 0)) # just below the doorway at (8,6)
|
|
assert world.try_move(player, 1, 1, 0) is True
|
|
assert player.position.map_id == "ship"
|
|
assert (player.position.x, player.position.y, player.position.z) == (2, 0, 0)
|
|
|
|
|
|
def test_try_move_disembarks_ship_back_to_parent_map():
|
|
parent, child = make_world()
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.registry = FakeRegistry()
|
|
|
|
# standing in the doorway (ship-local 2,0,0), step north off the ship's edge
|
|
player = Entity(position=EntityPosition("ship", 2, 0, 0))
|
|
assert world.try_move(player, 0, -1, 0) is True
|
|
assert player.position.map_id == "world"
|
|
assert (player.position.x, player.position.y, player.position.z) == (8, 5, 0)
|
|
|
|
|
|
def test_try_move_blocked_by_wall():
|
|
parent, child = make_world()
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.registry = FakeRegistry()
|
|
|
|
player = Entity(position=EntityPosition("ship", 1, 1, 0))
|
|
assert world.try_move(player, -1, -1, 0) is False # (0,0) is a wood_wall
|
|
assert player.position.map_id == "ship"
|
|
assert (player.position.x, player.position.y, player.position.z) == (1, 1, 0)
|
|
|
|
|
|
def test_rotated_embedding_local_to_parent_and_back_are_inverses():
|
|
_parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
embedding.rotate_to(1) # 90 degrees clockwise: 4x3 footprint becomes 3x4
|
|
assert embedding.footprint_size() == (3, 4)
|
|
for lx, ly, lz in [(0, 0, 0), (2, 0, 0), (3, 2, 0), (1, 1, 0)]:
|
|
px, py, pz = embedding.local_to_parent(lx, ly, lz)
|
|
assert embedding.parent_to_local(px, py, pz) == (lx, ly, lz)
|
|
|
|
|
|
def test_boarding_still_works_after_ship_rotates():
|
|
parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
embedding.rotate_to(1)
|
|
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.registry = FakeRegistry()
|
|
|
|
# the doorway (ship-local 2,0) now lands at a different parent cell post-rotation
|
|
door_parent = embedding.local_to_parent(2, 0, 0)
|
|
approach = (door_parent[0] - 1, door_parent[1] - 1, 0)
|
|
player = Entity(position=EntityPosition("world", *approach))
|
|
assert world.try_move(player, 1, 1, 0) is True
|
|
assert player.position.map_id == "ship"
|
|
assert (player.position.x, player.position.y, player.position.z) == (2, 0, 0)
|
|
|
|
|
|
def test_moving_and_rotating_ship_does_not_touch_aboard_entities_local_position():
|
|
_parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
|
|
sailor = Entity(position=EntityPosition("ship", 2, 1, 0))
|
|
original_local = (sailor.position.x, sailor.position.y, sailor.position.z)
|
|
|
|
embedding.move_to((0, 0, 0))
|
|
embedding.rotate_to(2)
|
|
|
|
assert (sailor.position.x, sailor.position.y, sailor.position.z) == original_local
|
|
|
|
|
|
def test_visible_embeddings_hides_the_ship_when_the_player_is_off_it():
|
|
parent, child = make_world()
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.player = Entity(position=EntityPosition("world", 0, 0, 0))
|
|
assert world.visible_embeddings(parent) == []
|
|
|
|
|
|
def test_visible_embeddings_shows_the_ship_when_the_player_is_aboard():
|
|
parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.player = Entity(position=EntityPosition("ship", 1, 1, 0))
|
|
assert world.visible_embeddings(parent) == [embedding]
|
|
|
|
|
|
def test_visible_embeddings_empty_before_a_player_exists():
|
|
parent, _child = make_world()
|
|
world = World()
|
|
world.maps = {"world": parent}
|
|
assert world.visible_embeddings(parent) == []
|
|
|
|
|
|
def test_disembarking_resolves_against_current_anchor_after_ship_moves():
|
|
parent, child = make_world()
|
|
embedding = child.parent_embedding
|
|
|
|
world = World()
|
|
world.maps = {"world": parent, "ship": child}
|
|
world.registry = FakeRegistry()
|
|
|
|
embedding.move_to((5, 5, 0)) # ship has sailed elsewhere since boarding
|
|
|
|
player = Entity(position=EntityPosition("ship", 2, 0, 0))
|
|
assert world.try_move(player, 0, -1, 0) is True
|
|
assert player.position.map_id == "world"
|
|
# disembarks relative to the ship's *current* anchor, not the original (6,6,0)
|
|
assert (player.position.x, player.position.y, player.position.z) == (7, 4, 0)
|