Mi2dRPGamEng/engine/aim.py

43 lines
1.4 KiB
Python

from __future__ import annotations
import math
# Clockwise from east, matching the y-down world convention used throughout (see camera.py).
_EIGHT_WAY_DIRECTIONS: tuple[tuple[int, int], ...] = (
(1, 0),
(1, 1),
(0, 1),
(-1, 1),
(-1, 0),
(-1, -1),
(0, -1),
(1, -1),
)
def snap_to_8way(dx: float, dy: float) -> tuple[int, int]:
"""Snaps a free direction to the nearest of the 8 compass directions.
The mouse can point anywhere ("no fixed angles" while aiming), but combat/interaction still
resolves against the tile grid, so this is where continuous aim meets discrete tile logic.
"""
if dx == 0.0 and dy == 0.0:
return 0, 1
angle = math.atan2(dy, dx)
index = round(angle / (math.pi / 4)) % 8
return _EIGHT_WAY_DIRECTIONS[index]
def apply_cone_deviation(dx: int, dy: int, cone_degrees: float, rng) -> tuple[int, int]:
"""Randomly deviates a direction within +/- cone_degrees/2, snapped back to the grid.
This is where "the exact direction of the shot is randomized" within the aim cone happens -
the caller (e.g. perform_shoot) supplies the cone's size (from skill + weapon mods).
"""
if cone_degrees <= 0:
return dx, dy
base_angle = math.atan2(dy, dx)
deviation = math.radians(rng.uniform(-cone_degrees / 2, cone_degrees / 2))
angle = base_angle + deviation
return snap_to_8way(math.cos(angle), math.sin(angle))