96 lines
3.0 KiB
Python
96 lines
3.0 KiB
Python
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)
|