## Gun harness — shared types and Gun concept. ## Coordinate system: 0° = East, CCW positive (Tank Royale standard). ## Bullet speed formula: 20 - 3 * power. import std/math const BotRadius* = 18.0 ## hit detection radius in px type EnemyInfo* = object id*: int x*, y*: float heading*, speed*, energy*: float lastSeenTick*: int ## tracker tick this enemy was last scanned ## (the radar freshness signal; 0 if unknown) WorldState* = object ## All raw data given to every gun every tick. # Enemy (current TARGET) enemyX*, enemyY*: float enemySpeed*, enemyHeading*: float # Self selfX*, selfY*: float selfSpeed*, selfHeading*: float selfRadarHeading*: float selfEnergy*: float enemyEnergy*: float # Arena arenaWidth*, arenaHeight*: float # Meta tick*: int # All alive enemies (for movement modules) enemies*: seq[EnemyInfo] GunPrediction* = object ## Absolute (x, y) where the gun predicts the enemy will be. x*, y*: float FeedbackEvent* = object ## Outcome of a resolved virtual bullet. prediction*: GunPrediction actualX*, actualY*: float ## actual enemy position at resolution time bulletPower*: float fireTick*: int ## tick the virtual bullet was spawned (predict time) powerBin*: int ## index into PowerBins the bullet belongs to missDistance*: float ## px; < BotRadius = hit hit*: bool proc bulletSpeed*(power: float): float {.inline.} = 20.0 - 3.0 * power ## Gun concept — any type T implementing these two procs is a valid gun. template isGun*(T: typedesc): bool = compiles( block: var g: T let ws = WorldState() let p: GunPrediction = g.predict(ws, 0.0) let fe = FeedbackEvent() g.onResult(fe) )