import math import pytest from engine.aim import apply_cone_deviation, snap_to_8way from engine.camera import OrthoCamera class FixedUniformRng: def __init__(self, value: float): self._value = value def uniform(self, _a, _b): return self._value @pytest.mark.parametrize( "dx,dy,expected", [ (5, 0, (1, 0)), (0, 5, (0, 1)), (-5, 0, (-1, 0)), (0, -5, (0, -1)), (5, 5, (1, 1)), (-5, 5, (-1, 1)), (-5, -5, (-1, -1)), (5, -5, (1, -1)), (0, 0, (0, 1)), (5, 0.1, (1, 0)), # nearly east, snaps to east not northeast ], ) def test_snap_to_8way(dx, dy, expected): assert snap_to_8way(dx, dy) == expected def test_snap_to_8way_covers_full_circle(): for degrees in range(0, 360, 5): radians = math.radians(degrees) dx, dy = math.cos(radians), math.sin(radians) dirx, diry = snap_to_8way(dx, dy) assert (dirx, diry) != (0, 0) assert abs(dirx) <= 1 and abs(diry) <= 1 def test_apply_cone_deviation_with_zero_cone_is_a_no_op(): assert apply_cone_deviation(1, 0, 0.0, FixedUniformRng(50.0)) == (1, 0) def test_apply_cone_deviation_with_zero_deviation_keeps_direction(): assert apply_cone_deviation(1, 0, 30.0, FixedUniformRng(0.0)) == (1, 0) def test_apply_cone_deviation_can_shift_to_an_adjacent_direction(): # east (1,0) deviated by +40 degrees should snap to northeast (1,1) -> y-down world, # so a positive angle rotates toward (0,1)/south side result = apply_cone_deviation(1, 0, 90.0, FixedUniformRng(40.0)) assert result != (1, 0) assert abs(result[0]) <= 1 and abs(result[1]) <= 1 def test_apply_cone_deviation_stays_within_grid_directions(): rng = FixedUniformRng(179.0) for base_dx, base_dy in [(1, 0), (0, 1), (-1, 0), (0, -1)]: dirx, diry = apply_cone_deviation(base_dx, base_dy, 20.0, rng) assert (dirx, diry) != (0, 0) def test_screen_to_world_center_of_viewport_is_camera_target(): camera = OrthoCamera(viewport_w=800, viewport_h=600, tiles_visible_y=12.0) camera.set_target(5.0, 5.0) wx, wy = camera.screen_to_world(400, 300) assert wx == pytest.approx(5.0, abs=0.05) assert wy == pytest.approx(5.0, abs=0.05) def test_screen_to_world_top_left_is_up_and_left_of_target(): camera = OrthoCamera(viewport_w=800, viewport_h=600, tiles_visible_y=12.0) camera.set_target(5.0, 5.0) wx, wy = camera.screen_to_world(0, 0) assert wx < 5.0 assert wy < 5.0 def test_screen_to_world_round_trips_with_scale_offset(): camera = OrthoCamera(viewport_w=800, viewport_h=600, tiles_visible_y=12.0) camera.set_target(3.0, 7.0) (scale_x, scale_y), (offset_x, offset_y) = camera.scale_offset() wx, wy = camera.screen_to_world(200, 450) ndc_x = wx * scale_x + offset_x ndc_y = wy * scale_y + offset_y # (200,450) in an 800x600 viewport -> ndc (-0.5, -0.5) in the y-up NDC space assert ndc_x == pytest.approx(-0.5, abs=1e-6) assert ndc_y == pytest.approx(-0.5, abs=1e-6)