Pivot parity audit — web (truth) vs Unity port¶
STATUS 2026-08-10: Wave 1 landed in
d24dbbc(+ playtest feedback: defmob HP bars/melee, building bars+pips, timber currency UI, dash-proof blockers, DropIconSet.timber). Wave 2 landed in72cad4e(all listed items; G50 verified a false positive — GearValue already matches web; G22 skipped, tap input parked; G49 folded into the existing Result body strings). Wave 3 landed (presentation/harness + the D17-D19 popup/contour bugs). Still open after wave 3: D46 (balance preview tooling), S15/S17 (rival telegraph, echo badges — rivals parked / needs echo-view text), S22+A41+A13+A39 (tutorial-pipeline patterns), A4 (tap fall-through — tap parked), G39 (fog-gated murk — needs fog-layer work), G42/G43 (map corruption bands — needs a MapView band element), G51–G54 (smoke harness needs a headless populate path), D34-partial (spikes are web-tinted cubes, not iso triangles — art pass), plus the parked-dependent items (shield cells, safe pocket, rival neutrality, stationary sentinels A11-A14).2026-08-10. Post-implementation audit of everything landed on
bza-unity-pivot-parity(P1–P10,68042ce..abd506a) against webgame.js/config.jsatebd6c39. Five independent audit passes; ~200 findings, only differences listed — verified-matching behaviour was checked and is recorded at the end of each section where it closes a question.ID prefixes: D defense · S stealth/targeting/ring · N nest/tells · A AI/combat/input · G generator/loot/corruption/run-flow. Tags: BUG (gameplay wrong), BEHAVIOR (sim diverges from web), VISUAL / MISSING (presentation or feature gap), LATENT (wrong but dormant under current config).
0. Root causes of the reported defects¶
| Report | Root cause | Finding |
|---|---|---|
| "defense mobs not destroying buildings, walking to exit portal" | DefenseMob.Update goal ladder is player → unit → entry → PadWorld. Web's is player → unit → nearest live building at any range → player fallback. The building branch was never ported; meleeTarget stays null on the pad path so mobs idle on the pad. |
D1 |
| "archer towers aren't shooting" | Tower attack is instant Health.TakeDamage with no projectile. Web spawns a real travelling projectile (spd 640, life 0.9, #ffd27a, 16px muzzle offset) that can miss. Nothing visible ⇒ reads as "not shooting". |
D2 |
| "engage ring should be dashed, show dashed line to target, hide when unused" | Ring is a solid always-on LineRenderer. Web: dashed [7,9], hidden unless a valid visible target is within engageRange×1.6, hidden entirely mid-fight (peace-only), plus a separate dashed→solid gold aim line to the target which Unity lacks entirely. |
S1–S11 |
| "nest spawn reticles should look like web" | Unity tell is a small violet pulsing quad. Web: 3-layer red/orange reticle, radius 44px = 1.10u, inner disc grows with the countdown (T-driven, not pulsing), vision-gated. | N1–N4 |
1. DEFENSE (holdout, waves, buildings, spikes)¶
Bugs¶
- D1 [BUG] Defense mobs never target buildings. Web
defPickTarget(game.js:6803-6813): player <130px → unit (d−r <70px) → nearest live building at ANY range (box-clamp for gates,d − rothers, Base included) → player fallback. UnityDefenseMob.cs:58-74: player → unit → entry →PadWorld; no building branch, noDefenseController.NearestBuilding,meleeTargetnull on pad path. Fix: addNearestBuilding(pos, out aim)(gate = box-clamp vsDoorTilesspan), insert between unit and fallback; fallback = player, never the pad. - D2 [BUG] Towers deal instant invisible damage. Web game.js:6897-6911 spawns a real projectile (origin +16px along aim, spd
defense.tower.projSpd640, dmgdefStat, faction player,#ffd27a, r4, life 0.9, can miss); fires regardless offightOn. UnityDefenseController.cs:592-609callsTakeDamagedirectly. Fix: spawn sharedProjectileprefab (Faction.Player, 640/U, life 0.9, gold tint). - D3 [BUG] T-doorway spawn box inset on the wrong edge. Web trims the apron half nearest the chamber (
sy1 -= TILE); after the z-flip that'sz0 += ts, butDefenseController.cs:375-377doesz1 -= ts— trims the OUTER half, biasing T spawns onto the gate line. L/R correct. - D8 [BUG] "Past the doorway" is a 24px proximity latch to
EntryWorld, not web's room test (defInRoom= strictly inside chamberBox, game.js:6957). A mob deflected around the entry point by a gate/crowd never clears its goal and grinds on the doorway forever. Fix:_pastEntry |= Chamber.WorldBox.Contains(me.xz). - D9 [BUG] Mobs not frozen during the SPIKES sweep. Web skips the whole per-mob block when
phase === "spikes"(game.js:6950); Unity mobs keep walking and keep killing the player during the ~1.4s sweep. Fix: early-out inDefenseMob.UpdateonDefPhase.Spikes. - D17 [BUG] Gates practically unselectable in the build popup. Web
defNearZonemeasures a gate by box-clamp to its 6-tile span (buildable anywhere along the run), others byd − r(r 40/20/26), accept <46px. Unity uses point distance to slot centre minus flat 1f/0.5f — a gate needs standing within 66px of the centre of a 240px wall that is itself solid. Fix: storegw/gh, box-clamp; real per-type radii. - D18 [BUG] Destroyed buildings show no restore contour. Web draws contours for unbuilt and dead slots with
⟳ cost; Unity gates ons.Lvl == 0 && !s.Deadand destroyss.Viz— dead building = empty floor, no affordance. - D19 [BUG] Contours never torn down.
UpdateContoursonly runs fromUpdatePopup(Idle phase); once the pad arms, the quads persist through the whole holdout. Fix: call unconditionally fromUpdate(). - D25 [BUG] Player-eject from sealed doorways is a slow per-frame drift toward the pad with no wall test (can shove through side rock) and no path cancel (PathAgent walks back in → jitter loop). Web
defResolvePlayer(game.js:6599-6617) snaps to the best of 4 edge candidates (mind(cand,pad) + d(cand,player)*0.25) and clears the path. Fix: port the edge snap +PathAgent.Stop(). - D30 [BUG, latent] Hand-clear calls
OpenPortalwhich only unseals; it works today becauseincoming/window = -1⇒AlwaysOpen, but any positiveextraction.incomingre-arms a second arrival wait, andPortal.TickIdlerefuses whilePlayerHunting(force-true while DefMobs exist + fightLinger). Fix: forcePortal.Phase = Opendirectly.
Behavior¶
- D4 Threat-cost trash reference is iteration-order-dependent (
trashRef= last Trash entry; web hardcodes swarm). Deterministic pick needed. - D5
elunclamped in the scheduler (duration − timercan exceed duration on the last frame; web clamps). - D7 Defense mobs spawn with 0 initial melee cd; web seeds
rand(0,0.5)to avoid synchronized alpha strikes. - D10 Gate chew fires off a forward point-probe only when an axis is blocked; web resolves the body circle vs overlapping WALL/FENCE tiles (
defResolveTiles) in both walk branches — diagonal sliders never chew in Unity. - D11 Melee reach hardcodes 13px target radius; web uses the actual target's r (wrong for Base r40/barracks r26 once D1 lands).
- D12 No steering wobble (
sin(m.wob)*0.12, wob += dt*2) — web packs weave, Unity mobs stack in a line. - D13
engageUnitignores unit radius (~11px tighter than web). - D21 Pad activation combat-locks the player: Unity stamps
PlayerChannelFrame(suppresses fire/melee/reload) during the 2.5s arm; web setssummoningZoneonly — you can shoot while it fills. Fix: display-only flag. - D24 SPIKES button never hides on death (web re-evaluates
p.aliveevery frame). - D26 Blocker test is a point test; web tests
x ± p.r— player sinks half a body into sealed doorways. - D27 Entry-only exemption is per-rect-set (
CurrentlyInany of L/R/T), web is per-rect. Harmless today. - D31 Board radius mismatch on the defense pad: activation 60px,
Portal.radius1.3u = 52px (web 60 for both). - D32 Spike sweep-end hardcodes
descended:trueandsession++unconditionally; web routes throughendRunwithG._defWon && !G.sandboxguard andextractKind. - D35
UpdateChamberFogwritesPlayerSightRadius/VisionField.sightRadiusevery frame even at fogK 0 (clobbers any future sight modifier) + per-frameFindFirstObjectByTypeuntil resolved. Web keeps widening as a pure read (fogRadiusNow()at consumers). - D36 Defense targeting bypasses the grade ladder and
switchMarginlock —SelectFireTargetreturns nearest defmob immediately (flip-flops between equidistant mobs; a rival attacking you can't win the target; at peace between packets the bow drops because the fall-through peace gate doesn't knowdefFight). Web folds defmobs in as grade-1 candidates. (= S32.) - D37 Passive arena ring only shows while mobs are alive; web shows it from
phase incoming|finaleonward. (= S8.) - D6 Spawn-credit drain equivalent-but-not-by-construction (window-close flush is inside the gate; web drains outside). Note only.
Visual / missing¶
- D14 [MISSING] No hit/death feedback on defmobs: web hurt-flash 0.18s,
-Nfloater#ffe9a8, colored death burst,G.kills++only when source is the player (Unity credits kills only on the spike path). - D15 [VISUAL] Infantry muster on the wrong side of the barracks (sign not z-flipped: web +y toward entrance ⇒ Unity −z, code uses +0.75z).
- D16 [MISSING] Infantry have no HP bar / hurt flash / spawn+death bursts (web
#b58cffbursts, white hit sparks, 20×3 bar). - D20 [MISSING] Build popup has no stat chips / desc / next-level preview / letter badge. Web shows
level N/max · HP, flavour desc, and per-type chips atlvl+1(Base→unlock ladder; Tower→Dmg/Rate/Range/HP; Gate→HP/Armor; Barracks→Spawn/Cap/UnitHP/UnitDmg), rebuilt on signature change. - D22 [MISSING] No
pulseon build/upgrade/damage/unlock (web pops the building 1.1×, and a Base upgrade pulses every newly-unlocked slot). - D23 [MISSING] No build/restore/destroy burst FX (web colored bursts; red
#ff6b6bon destruction). - D28 [MISSING] Gate slots don't store
gw/gh(needed by D1/D17 fixes, contour rects, plank orientation). - D29 [MISSING] Holdout is silent: no
assignPortalGuardson call/open, noextractCallnoise, noportalIncomingpulse + green burst cadence. - D33 [VISUAL] Spike grid alignment: web half-step offset + exclusive end, spark
#8b95a8per spike pop; Unity flush-aligned inclusive, no spark. (Spacing/pad/delay/kill-radius/envelope all match.) - D34 [VISUAL] Spikes are white cubes; web draws shadow + dark iso triangle
#2c3444+ highlight sliver, height 42×envelope. - D38 [MISSING] Off-screen red edge-darts for live defmobs (web up to 40,
#ff6b6b, α0.55, clamped to screen edges) — "a doorway you can't see never quietly chews your fort". - D39 [MISSING] The pulsing red dashed seal line across the S door during incoming/finale/spikes. The invisible blocker has zero visual feedback.
- D40 [MISSING] Gate planks don't degrade with HP (web: 3 posts/tile,
standing = max(1, round(3*(0.35+0.65*hp)))). - D41 [MISSING] Single gate HP sliver on the designated middle tile (
_hpTile). - D42 [MISSING] Building letters (B/AT/G/IB), level pips, appear-when-bitten HP bar.
- D43 [MISSING] Animated dashed contours with letter + cost label (gold affordable / orange not,
⟳restoring, near-slot highlight). - D44 [MISSING] Defmob token art: web per-kind palette (swarm
#f2a3a3, grunt#e0685f, runner#e8b04a, brute#a3455f), eyes, hurt flash, HP bar; names Gnawer/Marauder/Skitterer/Crusher. Unity: untextured capsules, different palette, names Swarmling/Grunt/Runner/Brute. - D45 [MISSING] Melee/chew spark FX (
#ff8a6bmob melee,#c9a37agate chew, white infantry hits). - D46 [MISSING]
defBalanceRowsheadless scheduler preview (tuning surface; needs anISpawnSinkseam inEmitGroup).
Verified at parity: threat/counts/midShare math, shape sampling mean, group size, sides rotation, apron insets, stream weights, all four defmob stat tables, gate tile geometry/spans after z-flip, restore economics, save-write discipline, CanBuild rule, Base-death ladder collapse, KeyE shortcut, uSpd/uSeek/uCd, infantry gate-passing.
2. STEALTH / TARGETING / ENGAGE RING¶
Engage ring¶
- S1 [VISUAL] Ring solid; web dashed
[7,9], lw 1.5 → 2.5 at full draw (game.js:8800). - S2 [BUG] Ring unconditionally on; web hides unless a valid target (alive, visible, not woken nest, not unmarked-neutral rival) is within
engageRange×1.6(=256px) (game.js:8774-8790). - S3 [BUG] Peace-only rule missing: while hunted web draws NO ring (except a sleeping nest inside R×1.6); Unity shows a red ring mid-fight.
- S4 [BUG] Precedence inverted: web tests
aimingbefore war (nest zone-war while drawing = gold, not red); Unity checksPlayerAtWarfirst. - S5 [BUG] Gold math wrong: web = fixed
255,214,100with alpha0.2+0.5·aimK; Unity uses that ramp as a Color.Lerp t from blue (20% gold at 0, never pure). - S6 [VISUAL] Color triples off: war
rgba(255,96,72,.30), peacergba(158,232,255,.28)— Unity's are more saturated/opaque. - S7 [BUG] Woken-nest rule inverted: web skips the nest as a ring candidate (ring survives on other targets); Unity blanks the whole ring when FireTarget is a woken nest (flickers as arrows retarget — also N26).
- S8 [BEHAVIOR] Defense arena ring should show from
incoming|finalephase, not onlyDefMobs.Count > 0. (= D37.) - S9 [MISSING] No
ui.engageRingkill-switch (web gates the whole draw). - S10 [BUG] Aiming state doesn't require a live
_aimTargetand starts one frame late (keys offAimDrawFraction > 0).
Aim telegraph line — the single largest presentation gap¶
- S11 [MISSING] The player's aim line does not exist: web game.js:8803-8813 — player→target, gold
rgba(255,214,100, .25+.6·aimK), lw 2→3, round caps, dashed[4,6]while aimK<1, snapping SOLID at full draw; only while aiming (peace); movement wipes_plantT⇒ line vanishes ("move to cancel"). - S12 [MISSING]
_aimTargetis private — exposepublic ICombatant AimTarget. - S13 [MISSING] Camera lean into the draw: web leads toward the mark at
leadFight*(0.4+0.6·aimK)when not fighting.
Enemy telegraphs & badges¶
- S14 [BUG] Ranged charge line never snaps solid at
chargeLockLead— the hard dodge cue is gone. Web: locked ⇒rgba(255,60,35,.92)lw4 solid; tracking ⇒ dashed[4,5]lw2 α.2+.55k. UnityDrawChargeis always dashed; onlyDrawLungeimplements the snap. Fix:ChargeLockedaccessor + branch like DrawLunge. (= A22.) - S15 [MISSING] Rival "drawing a bead" telegraph (thin dashed
[3,6]line 200px along facing). - S16 [MISSING] No ❗ / ? / ☠ detection glyphs anywhere (web: ❗ red at alert≥1, ? amber under binary detection, ☠ for calm boss). Alert is conveyed only by cone tint/echo color/arrows. Fix: badge slot on
CharacterLabelfed from det state. - S17 [MISSING] Same badges on echo-sensed blips.
- S18 [MISSING] Alerted gaze-cone edge stroke (
rgba(255,80,60,.45)lw1.5 outline when aware). - S19 [VISUAL] Cone tiers wrong: web has TWO states (aware
#ff2e2e@0.26, else#ffb45a@0.09); Unity has three, Suspect gets orange not red, all 2–4× more opaque. - S20 [VISUAL] Unity cones have a radial alpha falloff; web is deliberately flat.
- S21 [BEHAVIOR]
hideLockedConesadds_targetIsPlayer— a mob that switched target keeps a cone web would hide. - S22 [MISSING] Tutorial scanning-sentry cone telegraph (
tutScanbright outline + sweep-edge dot) — pairs with A41.
Backstab tell¶
- S23 [VISUAL] 🗡 pulsing emoji on the player's side of the enemy replaced with a static gold ground ring.
- S24 [BUG] Only ONE tell shown (nearest-wins); web draws it on every qualifying enemy.
- S25 [BUG] Tell gated on
!HasEyesOnPlayer— that'sbackstabReady's rule, notbackstabTell's. WithflankCriton, web's tell survives as a "you're in the crit arc" indicator even when seen. Fix: only skip when!flankCrit && e.HuntsPlayer. - S26 [BUG] Tell radius includes
meleeReach(+25px); web isp.r + en.r + backstabTellRangeonly. - S27 [BUG] No wind-up/lunge/recover exclusion — 🗡 lights on a committed lunger.
Stance arbiter / grade ladder¶
- S28 [BUG] AutoMelee is a second, independent attack system. Web has ONE arbiter: one
nearestEngaged()target,meleeStancepicks melee OR ranged; only the blind-spot backstab is stance-free. Unity's AutoMelee scansHostilesand strikes any seen foe regardless of grade/lock/stance; duringstanceCommitthe player melees AND shoots. Fix: route normal swings through the arbiter's target. - S29 [BUG] AutoMelee has no visibility/LOS gate (distance only) — can punch through thin walls/unlit foes.
- S30 [BUG] Lock stickiness re-adopts
_engLockwhen the fresh scan yields nothing; web drops the lock and stops firing. - S31 [BUG] Lock re-grading skips LOS (raycast only in the candidate loop) — a lock that broke LOS is retained via S30.
- S32 [BUG] Defense branch short-circuits instead of merging as grade-1 candidates. (= D36.)
- S33 [BEHAVIOR] Weapon-reach candidate cap (
projSpd*life) not in web — a grade-1 attacker is valid at ANY range (drives facing/camera too). - S34 [MISSING] Rival Tier-0 neutrality absent from the whole grammar (grading, auto-stab, tell, ring near-scan, vision circle). No
neutral/_markedconcept; every rival is always hostile. Design decision needed: rivals are disabled anyway (botsFromBiome 99) — defer or port with the raider re-enable. - S35 [BUG]
PlayerStabReady(melee-outranks-looting) laxer than webbackstabReady: missing wind-up exclusion +isVisibleToPlayer— chest cracking cancelled by a mid-lunge or unseen enemy web would ignore.
Misc presentation¶
- S36 [MISSING] 🌿 concealed glyph (alpha drop exists, glyph doesn't).
- S37 [MISSING] Bouncing gold target caret above the hunt/lock target (both stances).
3. NESTS & SPAWN TELLS¶
Tell visual (web spec: game.js:8333-8350, radius 44px = 1.10u, k = t/dur)¶
Layers: (1) constant base disc rgba(255,60,50,.10+.28k); (2) growing inner disc r·k rgba(255,120,80,.22+.34k) — the fill IS the countdown; (3) constant rim stroke rgba(255,90,70,.5+.45k) lw 2+2k. Monotonic, no pulse.
- N1 [VISUAL] Unity is a Time.time ping-pong pulse (phase-shared across all tells), not T-driven growth; store Dur and render 3 layers.
- N2 [VISUAL] Violet → must be the red/orange spawn-danger palette above (violet is the nest-event color, not the birth zone).
- N3 [VISUAL] ~2.4× too small (0.45u vs 1.10u radius).
- N4 [VISUAL] Not fog/vision-gated; web if (!inVision(s.x,s.y)) continue — leaks nest activity through walls. Gate at the tell's position.
- N5 [MISSING] No hatch VFX (web violet burst #b06aff at birth).
- N6 [QUALITY] Tell viz allocates GO+Quad+Material per birth and Destroys at hatch — pool via Views.Rent + MaterialPropertyBlock.
- N7 [MISSING] No hatch-time open-floor re-test: web relocates via nearestOpenSpot (ring search r≤4) if the tile became solid; Unity spawns blind at tell.Pos.
- N8 [VISUAL, minor] Unity kills orphaned tells on nest death immediately (web lets them lapse) — arguably better, keep deliberate.
- N9 [BEHAVIOR] Brood leash not "hard" — no post-move fence clamp (web leashHard clamps position when state ≠ chase). (Superset: A36.)
- N10 [BEHAVIOR, trivial] Mob scale snapshot at hatch vs web's at tell creation.
Wake / rally¶
- N11 [BUG] Nest doesn't wake from sourceless damage:
OnDamagedreturns onLastAttacker == nullbefore theIsNestbranch — corruption/DoT hits never wake it. Web sets_woken = trueunconditionally, then gates only the rally on source. Fix: hoist the wake above the null guard. - N12 [BUG] Rally doesn't commit the brood to the attacker: web sets
alert=1, lastSeen, state=chase, _searchT=0, target=sourceper rallied mob; Unity'sRouseonly force-chases a point — they walk there instead of hunting you. - N13 [BUG] Already-alerted brood skipped by the rally (
Rouseearly-returns at alert>0.5; web's nest branch has no guard — mid-search brood snap back to the attacker). - N14 [BUG, latent]
BornOfrally wrongly gated behindPackId != null(web's_nestOfclause is independent). Populator always sets pid today. - N15 [VISUAL] Wake beat wrong: web "The X stirs!"
#b06affsize 14 +ripple2double ring 0.5s; Unity uses the gatekeeper style (11px#9FB2FF), no ripple. Fix: add anestEventTextStyle.
Payout / death¶
- N17 [BEHAVIOR] Web's
G.blueprints += randi(1,3)is DIRECT currency; Unity spills 1–3 extra Common blueprint items — bigger shower, wrong route. (Guaranteed gear + 5–8 valuables/bp rolls match.) - N18 [VISUAL] "NEST DESTROYED!" should be violet size 18 at body origin (Unity: gatekeeper style 11 at +2.0).
- N19 [VISUAL, minor] Death burst should use the theme mob's color (knock-on of N23).
Presentation / HUD¶
- N21 [BUG/VISUAL] Sleeping nest shows its HP bar — web hides it until woken (the bar appearing IS the wake tell; before that the nest reads as scenery).
- N22 [VISUAL] Nest HP bar not boss-width (web 50×5 vs 34×4 for
boss || nestDef). - N23 [VISUAL] Nest body/label not tinted to theme color (web
n.color = MOB_DEF[theme].color). - N24 [VISUAL] Name derived from the key, not the theme's display-name first word ("Emberball Nest" vs web "Emberling Nest"). (= G12.)
- N25 [VISUAL] Woken-but-unseen nest echo blip turns red ("has spotted you" — a lie; nests have no senses). Web blips are always calm blue: pass alert 0.
- N27 [MISSING] No nest POI/map label (web
◈ Nestin-world at <540px + tactical map). (⊂ G15.) - N28 [BEHAVIOR, minor]
Revealeddrops the+ en.rslack (nest pops ~0.45u later than web).
Map population¶
- N29 [MISSING] No 3×3 bush clearing around nests (web game.js:392-402 stamps BUSH→FLOOR; Unity can seat a nest inside concealment, blocking clean shots at the win condition). Re-run
BuildBushClustersafter clearing.
Verified at parity: corner-seat scoring, brood gbox, mirrored pickup + lootSafeR, liveCap counting, first-interval halving, birth sampler, warZone LOS, all IsNest combat exemptions, death-noise suppression, NoDrop brood.
4. ENEMY AI / COMBAT / INPUT¶
Action input / dash¶
- A1 [MISSING] Dash button wind-up pulse (web: off-cooldown AND any enemy charging/winding within 380px ⇒ pulse; the dodge-lesson window).
- A2 [BEHAVIOR] Dash fires on release (
Button.onClick), web on press (pointerdown) — implementIPointerDownHandler. - A3 [BEHAVIOR] Stick magnitude rescaled past the deadzone; web is unrescaled
min(1, r/R)(jumps to ~0.15 at the edge — the shipped feel). - A4 [MISSING] Tap fall-through on release (<12px, <260ms ⇒ map tap / minimap open / sheet close). Parked with tap-to-move but the classification hook is absent entirely.
- A5 [BEHAVIOR]
stickRadius 160ref-px vs web 56 CSS-px — close relative to reference widths but never verified; confirm against ui reference resolution. - A6 [BEHAVIOR] Dash collision radius hardcoded 0.32u (=12.8px) vs web
p.r*0.8= 10.4px; also inPathAgent.DirectSteer. Derive from config. - A7 [MISSING] Dash VFX: launch burst +
dashEndsettle puff. - A8 [BEHAVIOR, deliberate] Unity sub-steps the roll and breaks when wedged (web burns dodgeT unstepped) — keep, it's better; recorded as a deliberate divergence.
- A9 [BEHAVIOR] Dash calls
_agent.Stop(); web keeps the path. Moot while tap-to-move is parked. - A10 [BEHAVIOR] PLANT timer: Unity also zeroes on
IsMoving || _dodgeT > 0; web only on steer input — a dash should NOT break the plant.
Stationary sentinels¶
- A11 [BUG] No stationary/turret branch: web early-outs for
en.stationary(never moves, tracks while charging, fires only at alert≥1); Unity routes them through Chase/CombatMove/Search — a sentry would walk. Currently unreachable (see A14) but must land before sentinels are enabled. - A12 [MISSING] Idle gaze wander (rand-gaze creep) — Unity uses the generic sinusoidal ScanSpin.
- A13 [BEHAVIOR] Suspect-state tracking (turn toward half-noticed target at 3 rad/s below alert 1) — dormant under
aggroOnly, live in the tutorial pipeline. - A14 [CONTENT]
sentinel(Stone Sentry, tier 3, legendary pool, mobIntro 8) exists as[UNUSED] Enemy_sentinel.assetwithrankAndFile: 0— never rolled. Flip only after A11.
Vision / melee / defs¶
- A16 [BEHAVIOR] Vision "stir" boost doesn't reach combat:
_visRMulapplies to detection only;ChaserecomputesvisRunboosted (web mutatesen.visRitself, growing the fire gate + standoff). - A17 [BUG] Rocketeer + Bombardier have
melee: 1in their assets; web authors bothmelee: false. Set 0. - A18 [BEHAVIOR] Per-def
meleeCdoverrides (brute 1.5 / grunt 1.0 / runner 0.7 / swarm 0.8) diverge from web's flatmobMeleeCd 0.7for normal mobs — the field was added for defense-mob parity but the values now differ in the raid context too. Decide source of truth; zeroing the overrides restores web parity (defense stats read their own table). - A19 [BEHAVIOR] Melee swat range: web
en.r + tgt.r + 8(~r+21px, target-aware); Unitydef.radius + meleeReach(25) + 0.2u(~r+33px, no target radius) — also shifts the CombatMove hold band ~12px out. - A20 [BEHAVIOR] Lunger eligibility: web = pure-melee non-boss tier ≥
lungeMinTier 2(⇒ NO current mob lunges); Unity = per-def flag,Enemy_skeletonhas it ⇒ skeletons lunge only in Unity. Add the tier gate or clear the flag. - A21 [BEHAVIOR] Lunger in-reach simple attack omits
applyWeb+ slash effect.
Charge / fire details¶
- A22 [BUG] = S14 (charge line lock snap missing).
- A23 [BEHAVIOR] Anti-standoff push-in: web passes
pushIn = ranged && !canFireso a blocked shooter closes to flush you out; Unity passesranged || lobsalways and holds standoff doing nothing. Fix: pass!seen. - A24 [BEHAVIOR] Fire gate: web (mutualSightFire off) = pure distance
≤ fog.radius + r; Unity usesRevealed(adds LOS + bush cutoff, per-frame, no litHold) — stricter and flickerier. - A25 [BEHAVIOR] Muzzle origin at body centre; web offsets
(r+4)along the aim for player AND mobs. - A26 [BEHAVIOR] Mob projectile lifetime computed (
max(1.2, visR*1.2/speed)); web uses flat default 1.6. - A27 [BEHAVIOR]
weapons.inheritVelasset = 0.33, web = 1.0 — sync the asset. - A28 [BEHAVIOR] Player gunshot-noise throttle is a 0.35s timer; web counts shots (ping every
gunCd 2shots) for every shooter. - A29 [BEHAVIOR, deliberate?] Predictive aim lead (
aimLead 0.35, two-pass intercept) is Unity-only; web aims at the live position. Flag for a decision — it changes player hit rates vs web tuning.
CombatMove / pathing¶
- A31 [BEHAVIOR] Wedged A* fallback repaths every frame; web throttles at 0.35s. Add a cooldown.
Patrol / drift / guards¶
- A32 [BUG] Chest drift gated on
_hasLeash— an unleashed roamer never drifts (web rolls 35% for every non-bot mob, leash box only constrains if present). - A33 [BEHAVIOR] No world-bounds clamp on the wander goal.
- A34 [BEHAVIOR] Return-home bypasses the guaranteed dwell (web substitutes home only inside the leg-pick).
- A35 [BEHAVIOR] Guard assignment missing web's tutorial early-return and
!e.siegeexclusion.
Hard leash¶
- A36 [MISSING] No hard-leash post-move clamp anywhere: web clamps
leashHardmobs (garrisons, room residents, nest brood) into the box after AI movement (state ≠ chase, only when within ±1 tile of the box so returners walk through the mouth). Unity's_leashnever constrains investigate/search movement. Port the clamp + aleashHardflag from the populator. (⊃ N9.)
Search / investigate¶
- A37 [BEHAVIOR] Search scan: web spins one direction at 0.9 rad/s (per-mob sign); Unity sweeps sinusoidally.
- A38 [BEHAVIOR] Investigate: web arrival 44px + 9s hard timeout; Unity 24px, no cap, stall-detector instead. Align radius, add the cap.
- A39 [BEHAVIOR] Suspect creep (stop at visR·0.45, turn 4 rad/s, never fire) unported — dormant under aggroOnly, live in tutorial.
- A40 [BEHAVIOR] POI stamped from a live seen target — combined with A38 a mob decaying out of Chase walks to a stale point.
- A41 [BEHAVIOR]
tutScansweeping sentry pattern unported (pairs with S22).
Minor¶
- A42 [BEHAVIOR] Footstep-hunt velocity: web smoothed EMA, Unity raw per-frame delta — spikier at the 20px/s threshold.
- A43 [BEHAVIOR] Eyes-memory ticks before Seen/Lost instead of after — one-frame skew.
Verified at parity: speed derivation, all weave/turn/charge numerics, chargeEntryArc, dwell, guard-ring geometry, lunge sequence, bodyBlocked rule, sealed-vault blocking, hitstop, binary detection.
5. GENERATOR / LOOT / CORRUPTION / RUN FLOW¶
Generator¶
- G1 [BUG] Grove bush density hardcoded 0.03 in
StructureFor—rooms.bushDensitytuning silently ignored for groves. - G2 [BEHAVIOR] Room centre wrong for EVEN widths: web
x0 + ((x1-x0)>>1)(+0.5); Unityx0 + (W>>1)— half/one-tile shift for w∈{8,10,12} on cache chest, camp anchors, nest mirror pivot, protected pillar. (Height/z derivation is correct.) - G3 [BEHAVIOR, accept?] Pillar/outcrop "don't bury a mob" rejection dropped (structures run pre-populate). Web's check only ever saw earlier rooms' mobs — likely accept.
- G4 [BEHAVIOR]
MapCaphardcoded 120×120; read from config.
Populator¶
- G5 [BUG] Trash pool one tier too narrow: web
heavyTier−1= tier ≤2; UnityAvailableKeys(step, 1)— plain rooms measurably softer. - G6 [BEHAVIOR] Plain-room guard loop breaks on first placement failure (web: no break — a failed spot loses one guard, not the garrison).
- G7 [BEHAVIOR] Nest brood loop breaks on first failure —
continueinstead. - G8 [BUG] Guards/brood/timber can land on BUSH tiles (web requires strict FLOOR;
RoomFloortests!IsWall) — mobs spawn inside concealment, piles hide in bushes. - G9 [BEHAVIOR]
spawnSafegets an extraW*0.3cap not in web (~35% smaller quiet bubble on narrow floors; timber gap shrinks with it). - G10 [LATENT] px→world conversions omit
*tsinportalClear/spawnSafe(harmless at tileSize 1; same class in G34). - G11 [BEHAVIOR, keep] Second cache chest is clamped
InRoomFloor(web is raw and can sit in a wall) — Unity is better; record as deliberate. - G12 [VISUAL] Nest display name from key not display-name first word. (= N24.)
- G13 [VISUAL] Nest theme tint unapplied. (= N23.)
- G14 [BEHAVIOR] Nest theme fallback order differs when the themed set is empty (web random roster pick; Unity prefers goblin).
- G15 [MISSING] No POI list at all — web pushes Nest/Cache/Extraction-Chamber POIs drawn on minimap + tactical map. Add
MapData.Pois+ emit. - G16 [BEHAVIOR] Crackable loot pre-rolled at gen (web stores a closure evaluated on open). Accept or wrap in a Func.
Timber budget¶
- G17 [BEHAVIOR] Pile placement gets ~12× attempts (RoomFloor's 12 tries inside the 300 guard) — web's remainder-at-spawn rail almost never fires in Unity, changing floor distribution. Sample once per iteration.
- G20 [MISSING] Timber HUD chip —
RaidState.Timberis never surfaced in any UI. Web shows#timberChipfrom the first plank.
Crack channel / interactions¶
- G21 [BEHAVIOR] Crack reach 56px vs web 43 (
p.r + c.r + 14). - G22 [MISSING] Explicit-tap override (a tapped container beats fight/stab suppression) — no tap path exists.
- G23 [BEHAVIOR] Fresh-channel pick is first-Update-wins, web is nearest-eligible — matters in camps with adjacent crackables.
- G24 [BEHAVIOR] "Empty" nudge requires stationary (web doesn't) and ignores fight/stab state.
- G25 [VISUAL] Crack-tick spark
#ffd964missing. - G26 [VISUAL] Chest-open rarity burst missing (TODO in code).
- G27 [BUG] Gate doesn't defer to an in-progress chest crack — both channels fill simultaneously and stomp
PlayerChannelProgressthe same frame. Web: chest channel ⇒ gate decays. - G28 [BEHAVIOR] Gate ignores
stabReadysuppression.
Loot pipeline¶
- G29 [BUG] Collected shield CELLS never granted —
CellsThisRaid++only, noAddCell()on the player (contrast salves). The item is a no-op in-raid. - G30 [BEHAVIOR, deliberate] Magnet arming: settle grace alone arms under a stationary player (web never self-collects). Documented intentional — keep or align.
- G31 [BEHAVIOR] Pre-spawned floor loot carries a magnet grace web doesn't have.
- G32 [VISUAL] Floaters: timber
+N 🪵#d8a35e(Unity "+N timber" gold); web also floats+nameat the item's position;ClearOfWallsnudge is a Unity-only fix (keep).
Corruption¶
- G33 [BUG] First damage bite a full tick late (counts up from 0; web counts down from 0 ⇒ immediate bite on entry and on re-entry). Seed
_tickT = tickon entry. - G34 [LATENT]
warnDist/killMobs grace omit tileSize conversion. - G35 [BEHAVIOR] Death cause lost (
TakeDamage(dmg, null)) — web's card reads "the Corruption". - G36 [VISUAL] Murk color brighter/pinker than web
rgba(96,44,150,.34). - G37 [VISUAL] 46px rising glow gradient off the front missing.
- G38 [VISUAL] Front line should be an animated sine (
sin(x*0.045+t*2.2)*5,rgba(196,124,255,.85)lw3); Unity is a static quad. - G39 [VISUAL] Murk not fog-gated — renders over unexplored area (web draws under the fog).
- G40 [MISSING] In-wave pulsing screen wash.
- G41 [MISSING] Purple particles on the player inside + burst per swallowed creature.
- G42/G43 [MISSING] Minimap + tactical-map corruption band/front line (already in known-debt).
- G44 [BEHAVIOR] Spawn conditions: web gates on
corruption.enabled && sys.timer !== falseand nulls it in the defense sandbox; Unity spawns whenever a chamber exists — no suppression hooks.
Run flow / banking¶
- G45 [MISSING] The Safe Pocket does not exist — web death banks
safeItems()(top-safeSlots()non-stash bag items) + manual safe; Unity's death branch banks nothing. The single biggest endRun divergence: Unity death forfeits strictly more than web. (Timber/gold/bp forfeits verified correct.) - G46 [MISSING] Run-loadout persistence on win (found equipped pieces get
origin:"stash"and write intosave.loadout; death deliberately doesn't write). - G47 [BEHAVIOR]
raidsWonskips FTUE extracts (web counts them). - G48 [BEHAVIOR] Re-clearing a finished biome yanks the player to the next biome's floor 1 (web moves the cursor only if the next biome was locked; else stays with "replay any floor to grind"). Guard on
!wasUnlocked. - G49 [BEHAVIOR]
progNoteresult-card strings absent (floor/biome unlocked, loot banked, progress safe). - G50 [BEHAVIOR]
autoEquipEmptyranks by level-scaled synthetic stats valued at level 1 — different ordering for levelled pieces than web'slootValue.
Smoke harness¶
- G51 [MISSING] Populator never exercised (no chests/timber/nests/guards observed) — every §B/§C risk untested.
- G52 [MISSING] Timber-budget invariant (
== timberBudgetper floor) unasserted — the load-bearing contract of the build economy. - G53 [MISSING] Chest-in-wall check covers only room centres/nest seats, not actual chest positions.
- G54 [MISSING] Distribution stats (sideSteps, crackable counts, aspect histograms).
- G55 [BEHAVIOR] Unseeded, unlogged sweep — failures unreplayable. Seed per floor from a printed base seed.
6. Suggested fix order¶
Wave 1 — the reported defects + hard gameplay bugs 1. D1 building targeting (+ D28 gw/gh, D11 target radius) · D2 tower projectiles · D3 T-spawn inset · D8 room-test latch · D9 spikes freeze · D10 gate chew 2. S1–S11 engage ring rework + aim telegraph line (one view rewrite) · S14/A22 charge lock snap 3. N1–N4 tell reticle (+ N5 hatch burst, N7 open-floor re-test) 4. N11–N14 nest wake/rally · G33 corruption first bite · G29 cells no-op · G27 gate-vs-chest channel · G5/G6/G8 populator (trash tier, garrison break, bush tiles) · A17 rocketeer/bombardier melee 5. S28–S32 stance arbiter collapse + defense-target merge (D36) · G45 Safe Pocket
Wave 2 — behavior parity (D-behavior block, A16/A19/A23–A28, A32/A36/A38, G2/G9/G17/G21–G24, G44/G47–G50, N17, D21, D30–D32)
Wave 3 — presentation + harness (D14–D16, D20, D22–D24, D33–D34, D38–D45, S15–S27, S36–S37, N15/N18/N21–N25/N27/N29, G12–G15, G20, G25–G26, G32, G36–G43, G51–G55, A1–A7)
Deliberate divergences to keep (record, don't fix): A8 dash sub-stepping, G11 clamped second cache chest, G32c ClearOfWalls nudge, N8 orphan-tell cleanup, G30 magnet settle-arm (user call). Decisions needed: A18 per-def meleeCd in raids, A29 predictive aim lead, S34 rival neutrality (parked with rivals), G16 pre-rolled crackables.