j148: bound the corridor by speed x ticks (TR_{TFIL,STRAFE}_CORRIDOR_TICKS, default 0 = to the wall); UNTESTED

This commit is contained in:
2026-09-27 08:23:16 +02:00
parent 32ec040e90
commit 5e213df8d9
5 changed files with 62 additions and 0 deletions
+2
View File
@@ -105,6 +105,7 @@ TR_TFIL_RANGE_HI=200.0 # px; upper edge of that band
TR_TFIL_RANGE_TEMP=0.4 # sharpness of the ring mover's weighted random draw
TR_TFIL_RANGE_K=60.0 # px; how fast the weight falls off outside the band
TR_TFIL_CORRIDOR_HEAT=10.0 # lava painted per corridor-overlapping tile
TR_TFIL_CORRIDOR_TICKS=0.0 # corridor length in ticks: 0 = to the wall (shipped); N>0 = min(to wall, bullet speed * N)
TR_TFIL_WALL_HOTNESS=15.0 # peak heat painted on tiles next to a wall
TR_TFIL_WALL_RADIANCE=10.0 # how fast wall heat falls off with distance
TR_TFIL_BULLET_CORE=10.0 # tfil only: lava per bullet-overlapping tile (== PathDangerThreshold, so a bullet is never hot on its own)
@@ -147,6 +148,7 @@ TR_STRAFE_HEAT_GRID=on # draw the whole heat grid; 0 leaves only the chosen til
TR_STRAFE_BULLET_CORE=20.0 # strafe's own retune: lava per bullet-overlapping tile
TR_STRAFE_BULLET_AURA=10.0 # strafe's own retune: lava for the bullet aura ring
TR_STRAFE_CORRIDOR_HEAT=10.0 # strafe's own retune: lava per corridor tile
TR_STRAFE_CORRIDOR_TICKS=0.0 # same length bound for strafe: 0 = to the wall (shipped)
TR_STRAFE_WALL_HOTNESS=15.0 # strafe's own retune: peak wall heat
TR_STRAFE_WALL_RADIANCE=5.0 # strafe's own retune: wall heat falloff
+5
View File
@@ -311,6 +311,8 @@ proc printEffectiveValues(ctx: EnvReportContext) =
emit("TR_TFIL_RANGE_K", $RangeK, sourceOf("TR_TFIL_RANGE_K"))
emit("TR_TFIL_CORRIDOR_HEAT", $the_floor_is_lava_ring.CorridorHeat,
sourceOf("TR_TFIL_CORRIDOR_HEAT"))
emit("TR_TFIL_CORRIDOR_TICKS", $the_floor_is_lava.TfilCorridorTicks,
sourceOf("TR_TFIL_CORRIDOR_TICKS"))
emit("TR_TFIL_WALL_HOTNESS", $the_floor_is_lava_ring.WallHotness,
sourceOf("TR_TFIL_WALL_HOTNESS"))
emit("TR_TFIL_WALL_RADIANCE", $the_floor_is_lava.WallRadiance,
@@ -373,6 +375,7 @@ proc printEffectiveValues(ctx: EnvReportContext) =
emit("TR_STRAFE_BULLET_CORE", $StrafeBulletCore, sourceOf("TR_STRAFE_BULLET_CORE"))
emit("TR_STRAFE_BULLET_AURA", $StrafeBulletAura, sourceOf("TR_STRAFE_BULLET_AURA"))
emit("TR_STRAFE_CORRIDOR_HEAT", $StrafeCorridorHeat, sourceOf("TR_STRAFE_CORRIDOR_HEAT"))
emit("TR_STRAFE_CORRIDOR_TICKS", $StrafeCorridorTicks, sourceOf("TR_STRAFE_CORRIDOR_TICKS"))
emit("TR_STRAFE_WALL_HOTNESS", $StrafeWallHotness, sourceOf("TR_STRAFE_WALL_HOTNESS"))
emit("TR_STRAFE_WALL_RADIANCE", $StrafeWallRadiance, sourceOf("TR_STRAFE_WALL_RADIANCE"))
@@ -646,6 +649,7 @@ proc knownEnvNames*(): seq[string] =
"TR_RAM_PLAN_MARGIN", "TR_RAM_PLAN_HITRATE", "TR_RAM_LOG",
"TR_TFIL_RANGE_LO", "TR_TFIL_RANGE_HI", "TR_TFIL_RANGE_TEMP",
"TR_TFIL_RANGE_K", "TR_TFIL_CORRIDOR_HEAT", "TR_TFIL_WALL_HOTNESS",
"TR_TFIL_CORRIDOR_TICKS",
"TR_TFIL_WALL_RADIANCE", "TR_TFIL_BULLET_CORE", "TR_TFIL_BULLET_AURA",
"TR_TFIL_TILE_REPLAN", "TR_TFIL_COMMIT_TICKS", "TR_TFIL_NO_REV",
"TR_TFIL_COMMIT_LOG", "TR_TFIL_COMMIT_ARRIVAL", "TR_TFIL_COMMIT_MARGIN",
@@ -664,6 +668,7 @@ proc knownEnvNames*(): seq[string] =
"TR_FIRE_LAG",
"TR_STRAFE_HEAT_GRID", "TR_STRAFE_BULLET_CORE", "TR_STRAFE_BULLET_AURA",
"TR_STRAFE_CORRIDOR_HEAT", "TR_STRAFE_WALL_HOTNESS",
"TR_STRAFE_CORRIDOR_TICKS",
"TR_STRAFE_WALL_RADIANCE",
SurfPrefDistEnv, SurfDistBandEnv, SurfWallMarginEnv, SurfRadialFracEnv,
SurfLogEnv,
+17
View File
@@ -226,6 +226,13 @@ const CorridorHeatDefault = 10.0 ## corridor heat (== PathDangerThreshold)
const WallHotnessDefault = 15.0 ## wall radiance peak (retune)
const WallRadianceDefault = 5.0 ## wall radiance falloff (retune)
## j148: the corridor LENGTH bound, `TR_STRAFE_CORRIDOR_TICKS`. The shipped
## corridor runs from the bullet to the ARENA WALL; a bullet only covers
## `speed * t` px in `t` ticks, so a fixed TIME window is the physical length.
## 0 (the default) = to the wall, byte-for-byte shipped.
const DefaultStrafeCorridorTicks = 0.0
var StrafeCorridorTicks* = DefaultStrafeCorridorTicks
## Heat shape is override-able so the shipped field and the retune can be
## compared on one binary. The DEFAULTS are the retune (see the block above);
## the knobs are strafe-specific, so the ring mover's own `TR_TFIL_*` names do
@@ -374,6 +381,15 @@ proc loadStrafeHeatEnv*() =
StrafeCorridorHeat = getEnvFloat("TR_STRAFE_CORRIDOR_HEAT", CorridorHeatDefault)
StrafeWallHotness = getEnvFloat("TR_STRAFE_WALL_HOTNESS", WallHotnessDefault)
StrafeWallRadiance = getEnvFloat("TR_STRAFE_WALL_RADIANCE", WallRadianceDefault)
StrafeCorridorTicks = getEnvFloat("TR_STRAFE_CORRIDOR_TICKS", DefaultStrafeCorridorTicks)
proc strafeCorridorReach*(tWall, speed: float): float =
## The ONE place the strafe corridor length is decided (same rule as tfil's
## `corridorReach`, same physics: a bullet covers `speed * t` px in `t` ticks).
## 0 (default) = to the arena wall, byte-for-byte shipped; N > 0 = N ticks.
## Only the LENGTH changes: the heat inside the surviving corridor is today's
## (`heatDecay(along / speed)` untouched) — this is NOT the j119 time-heat model.
if StrafeCorridorTicks <= 0.0: tWall else: min(tWall, speed * StrafeCorridorTicks)
proc loadStrafeEnv*() =
## Read the strafe knobs. Called once at module init; callable again after
@@ -700,6 +716,7 @@ proc buildHeat(m: var StrafeModule, ws: WorldState) =
elif dx < 0.0: tMin = min(tMin, (0.0 - b.x) / dx)
if dy > 0.0: tMin = min(tMin, (m.arenaHeight - b.y) / dy)
elif dy < 0.0: tMin = min(tMin, (0.0 - b.y) / dy)
tMin = strafeCorridorReach(tMin, speed)
if tMin == 0.0: continue
let (_, auraR) = bulletRadii(b.power)
let bMag = bulletMagScale(b.power)
@@ -269,6 +269,31 @@ proc loadTfilHeatEnv*() =
loadTfilHeatEnv()
# ── Corridor LENGTH bound (TR_TFIL_CORRIDOR_TICKS, default 0 = to the wall) ──
#
# WHY: the shipped corridor is the rotated rectangle from the bullet to the
# ARENA WALL, so one distant bullet blankets a 40px-wide swath across the whole
# map. That is not physical: in `t` ticks a bullet covers `speed * t` px, and
# `speed = 20 - 3*power`, so a fast (low-power) bullet's reach is LONG and a
# slow one's is SHORT.
#
# WHAT: bound the corridor's LENGTH, nothing else — the heat inside the
# surviving corridor is EXACTLY today's (`heatDecay(along/speed)` is unchanged),
# so unlike the j119 time-indexed heat model this does NOT decay heat along the
# corridor; it only removes corridor that no bullet will reach.
var TfilCorridorTicks* = 0.0 ## 0 (default) = to the wall: byte-for-byte shipped
proc loadTfilCorridorEnv*() =
TfilCorridorTicks = getEnvFloat("TR_TFIL_CORRIDOR_TICKS", 0.0)
loadTfilCorridorEnv()
proc corridorReach*(tWall, speed: float): float =
## The ONE place the corridor length is decided, so the heat field and the
## drawn outline can never disagree. `tWall` = distance to the wall along the
## heading, `speed` = the ghost's own px/tick. 0 = to the wall (shipped).
if TfilCorridorTicks <= 0.0: tWall else: min(tWall, speed * TfilCorridorTicks)
proc heatDecay*(dt: float): float =
## Fraction of a bullet's heat still present `dt` ticks before it arrives.
## Exactly 1.0 when the time model is off, so the default field is
@@ -506,6 +531,9 @@ type CorridorGeom = object
bx, by: float ## bullet origin
proc corridorGeom(b: TrackedBullet, arenaWidth, arenaHeight: float): CorridorGeom =
## `tMin` is the corridor LENGTH: the distance to the wall, bounded by
## `corridorReach` (TR_TFIL_CORRIDOR_TICKS). Unset (0) -> exactly the wall
## distance, so the shipped field and outline are byte-for-byte unchanged.
let speed = sqrt(b.velX * b.velX + b.velY * b.velY)
if speed < 0.001: return
let dx = b.velX / speed
@@ -515,6 +543,7 @@ proc corridorGeom(b: TrackedBullet, arenaWidth, arenaHeight: float): CorridorGeo
elif dx < 0.0: tMin = min(tMin, (0.0 - b.x) / dx)
if dy > 0.0: tMin = min(tMin, (arenaHeight - b.y) / dy)
elif dy < 0.0: tMin = min(tMin, (0.0 - b.y) / dy)
tMin = corridorReach(tMin, speed)
CorridorGeom(dx: dx, dy: dy, px: -dy, py: dx, tMin: tMin, bx: b.x, by: b.y)
proc lavaAt(m: TFILModule, col, row: int): float =
+9
View File
@@ -3660,3 +3660,12 @@ isolates the knob):**
the known strafe-over-tfil gap reproducing itself, and it is exactly why the
pre-registration demanded the within-mover reference: read against `tfil_off`
alone, the knob looks like a winner it is not.
**j148 — corridor LENGTH bound (unmeasured).** `TR_TFIL_CORRIDOR_TICKS` and
`TR_STRAFE_CORRIDOR_TICKS` (both default `0`) bound the corridor — the rotated
rectangle from the ghost bullet along its heading — by `min(distance to the wall,
bulletSpeed * TICKS)`, so a fast (low-power) bullet's corridor is long and a
slow one's is short, instead of every bullet blanketing the arena to the wall.
`0` is exactly today's behaviour; only the LENGTH changes, the heat inside the
surviving corridor is untouched. **Untested** — no battle, no measurement, the
parity guard only says the default path is byte-for-byte unchanged.