Mi2dRPGamEng/tests/test_continuous_movement.py

118 lines
4.7 KiB
Python

import pytest
from engine.entity import Entity, EntityPosition
from engine.map import Map
from engine.tile import Tile, TileLayerDef, TileLayerInstance
from engine.world import World
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 make_world():
parent = Map("world", 10, 10, 1)
for x in range(10):
for y in range(10):
parent.set_tile(x, y, 0, Tile(flooring=TileLayerInstance("grass")))
ship = Map("ship", 4, 3, 1)
for x in range(4):
for y in range(3):
ship.set_tile(x, y, 0, Tile(flooring=TileLayerInstance("wood_deck")))
for x, y in [(0, 0), (1, 0), (3, 0), (0, 1), (3, 1), (0, 2), (1, 2), (2, 2), (3, 2)]:
ship.get_tile(x, y, 0).room = TileLayerInstance("wood_wall") # (2,0) is the open doorway
parent.embed(ship, (6, 6, 0))
world = World()
world.maps = {"world": parent, "ship": ship}
world.registry = FakeRegistry()
return world, parent, ship
def test_continuous_move_lands_at_a_fractional_position():
world, _parent, _ship = make_world()
player = Entity(position=EntityPosition("world", 2.0, 2.0, 0))
assert world.try_move_continuous(player, 1, 0, 0, distance=0.35) is True
assert player.position.x == pytest.approx(2.35)
assert player.position.y == pytest.approx(2.0)
def test_continuous_move_normalizes_diagonal_direction():
world, _parent, _ship = make_world()
player = Entity(position=EntityPosition("world", 2.0, 2.0, 0))
assert world.try_move_continuous(player, 1, 1, 0, distance=1.0) is True
# moved exactly 1 unit total, split evenly between x and y (not 1 in each axis)
import math
dx = player.position.x - 2.0
dy = player.position.y - 2.0
assert math.hypot(dx, dy) == pytest.approx(1.0)
assert dx == pytest.approx(dy)
def test_continuous_move_blocked_by_wall_leaves_position_unchanged():
world, _parent, ship = make_world()
# ship-local (0,0) is a wood_wall; approach it from inside at (1.2,1.2) moving toward it
player = Entity(position=EntityPosition("ship", 1.2, 1.2, 0))
assert world.try_move_continuous(player, -1, -1, 0, distance=1.5) is False
assert player.position.x == pytest.approx(1.2)
assert player.position.y == pytest.approx(1.2)
def test_continuous_move_crosses_into_embedded_ship_at_fractional_local_position():
world, _parent, ship = make_world()
# doorway column is parent x=8 (ship-local x=2); approach from just north of it
player = Entity(position=EntityPosition("world", 8.3, 5.6, 0))
assert world.try_move_continuous(player, 0, 1, 0, distance=0.6) is True
assert player.position.map_id == "ship"
assert player.position.x == pytest.approx(2.3)
assert player.position.y == pytest.approx(0.2)
def test_continuous_move_disembarks_at_fractional_parent_position():
world, _parent, ship = make_world()
player = Entity(position=EntityPosition("ship", 2.3, 0.2, 0))
assert world.try_move_continuous(player, 0, -1, 0, distance=0.6) is True
assert player.position.map_id == "world"
assert player.position.x == pytest.approx(8.3)
assert player.position.y == pytest.approx(5.6)
def test_entity_at_matches_by_tile_membership_for_fractional_positions():
world, _parent, _ship = make_world()
entity = Entity(position=EntityPosition("world", 3.7, 4.2, 0))
world.add_entity(entity)
assert world.entity_at("world", 3, 4, 0) is entity
assert world.entity_at("world", 4, 4, 0) is None
def test_rotate_position_round_trips_through_a_rotated_embedding():
world, _parent, ship = make_world()
embedding = ship.parent_embedding
embedding.rotate_to(1)
for lx, ly in [(0.5, 0.5), (2.7, 1.1), (3.99, 0.01)]:
px, py, pz = embedding.local_to_parent_pos(lx, ly, 0)
rx, ry, rz = embedding.parent_to_local_pos(px, py, pz)
assert rx == pytest.approx(lx)
assert ry == pytest.approx(ly)
def test_continuous_move_crosses_into_rotated_embedded_ship():
world, _parent, ship = make_world()
embedding = ship.parent_embedding
embedding.rotate_to(1) # 90 deg CW: 4x3 footprint becomes 3x4 in parent space
# find where the doorway (ship-local 2,0) lands post-rotation, approach from just outside it
door_parent = embedding.local_to_parent(2, 0, 0)
start_x = door_parent[0] + 0.5 - 1 # one tile back along x, offset into the tile
start_y = door_parent[1] + 0.5
player = Entity(position=EntityPosition("world", start_x, start_y, 0))
assert world.try_move_continuous(player, 1, 0, 0, distance=1.0) is True
assert player.position.map_id == "ship"