{
 "status": "PARTIALLY VERIFIED",
 "date": "2026-09-13",
 "verifier": "never ran; the DATA numbers were re-read against the vendored CSVs instead",
 "specs": [
  {
   "family": "projectile-spells",
   "cards": [
    {
     "card": "Fireball",
     "data_chain": "cards.json cards[name=Fireball].projectile  (= spells_other.Fireball.Projectile -> projectiles.FireballSpell) -> .speed 600, .damage 325 (damage_source projectiles.FireballSpell.Damage), .radius_milli 2500, .pushback_milli 1800, .pushback_all false, .homing false, .aoe_to_air true, .aoe_to_ground true, .only_enemies true, .crown_tower_damage_percent 40 (.crown_tower_damage_percent_raw -60); cards[Fireball].elixir 4 (spells_other.Fireball.ManaCost); cards[Fireball].level_scaling.multiplier_percent_by_level (rarities.csv Rare); victim immunity cards.json units[<victim unit>].ignore_pushback (characters.csv IgnorePushback); launch point arena.json king_blocks[team].center -> arena.rs::king_tower_pos (Blue (9,3), Red (9,29) tiles); speed conversion calibration.json time.SPEED_TO_SUBTILES_PER_TICK (proposed split: projectile.SPEED_TO_SUBTILES_PER_TICK). Live-vintage cross-refs: smlbiobot/cr-csv 2023 projectiles.FireballSpell (Pushback 1000, CTDP -70); Supercell posts 2022-08-02 / 2026-06-01 / 2026-09-08.",
     "numbers": [
      {
       "name": "elixir cost",
       "value": "4",
       "units": "elixir",
       "source": "spells_other.Fireball.ManaCost (2017 data; same in 2023 cr-csv)",
       "confidence": "DATA"
      },
      {
       "name": "projectile Speed (raw)",
       "value": "600",
       "units": "raw Speed column, unit disputed",
       "source": "projectiles.FireballSpell.Speed (2017 and 2023 data sets); wiki Fireball stats table 600",
       "confidence": "DATA"
      },
      {
       "name": "projectile speed in engine units",
       "value": "9000 subtiles/tick if Speed is tiles/min at 20 TPS (x15); 10800 if Speed/50 tiles/s (x18)",
       "units": "subtiles per tick",
       "source": "derived from Speed 600 and calibration time.SPEED_TO_SUBTILES_PER_TICK candidates; community: AesDragon blog asserts tiles/min (unmeasured), Darknighture frame test on Musketeer projectile gives Speed/50",
       "confidence": "INFERENCE"
      },
      {
       "name": "homing",
       "value": "false (flies to the tap point captured at cast)",
       "units": "boolean",
       "source": "projectiles.FireballSpell.Homing blank (2017), homing false (2023 cr-api-data)",
       "confidence": "DATA"
      },
      {
       "name": "damage, level 1 (2017 rarity-relative Rare)",
       "value": "325",
       "units": "hp",
       "source": "projectiles.FireballSpell.Damage (2017 and 2023)",
       "confidence": "DATA"
      },
      {
       "name": "damage, level 11 live",
       "value": "688 (689 before 2025-03-31 -0.14%)",
       "units": "hp",
       "source": "wiki Fireball dmg_11 var and history; cr-api-data damage_per_level L11 689",
       "confidence": "COMMUNITY"
      },
      {
       "name": "impact radius",
       "value": "2500 = 45000 subtiles (2.5 tiles), unchanged through live 2026",
       "units": "millitiles",
       "source": "projectiles.FireballSpell.Radius (2017, 2023); wiki stats table 2.5",
       "confidence": "DATA"
      },
      {
       "name": "CrownTowerDamagePercent raw (2017)",
       "value": "-60 -> towers take 40%",
       "units": "percent delta from 100",
       "source": "projectiles.FireballSpell.CrownTowerDamagePercent; reading cross-checked: wiki 40% held 19/2/2016..12/12/2017 and this data predates 12/12/2017 (Knight HitSpeed 1100, HogRider 1500, Prince 1500 are pre-Electrifying-Update values); 2023 data -70 <-> wiki 30% (2020-08-04)",
       "confidence": "DATA"
      },
      {
       "name": "crown tower percent, live history",
       "value": "35% (2017-12-12), 30% (2020-08-04), 25% (2026-06-01, Supercell 'Fireball: 207 -> 172'), ~23% (2026-09-08, Supercell 'Crown Tower Damage: 172 -> 159'; 23% is the only integer percent reproducing 159 from 688, under the ceil law)",
       "units": "percent of damage",
       "source": "wiki Fireball history; supercell.com june-balance-changes-2026, september-balance-changes-2026",
       "confidence": "COMMUNITY"
      },
      {
       "name": "crown tower damage, level 11 live",
       "value": "159",
       "units": "hp",
       "source": "supercell.com september-balance-changes-2026",
       "confidence": "COMMUNITY"
      },
      {
       "name": "Pushback (2017 vintage)",
       "value": "1800 = 32400 subtiles (1.8 tiles)",
       "units": "millitiles of displacement",
       "source": "projectiles.FireballSpell.Pushback; unit cross-checked: Supercell 2022-08-02 'Pushback: (1.8 tile -> 1 tile)', Log 1000 <-> wiki '1 tile' 23/1/2017",
       "confidence": "DATA"
      },
      {
       "name": "Pushback (live since 2022-08-02)",
       "value": "1000 = 18000 subtiles (1 tile)",
       "units": "millitiles",
       "source": "cr-csv 2023 projectiles.FireballSpell.Pushback 1000; Supercell balance-changes-august-2022-2",
       "confidence": "DATA"
      },
      {
       "name": "PushbackAll",
       "value": "false (respects victim IgnorePushback)",
       "units": "boolean",
       "source": "projectiles.FireballSpell.PushbackAll blank (2017, 2023)",
       "confidence": "DATA"
      },
      {
       "name": "AoeToAir / AoeToGround / OnlyEnemies",
       "value": "true / true / true",
       "units": "boolean",
       "source": "projectiles.FireballSpell; spells_other.Fireball.OnlyEnemies",
       "confidence": "DATA"
      },
      {
       "name": "Gravity",
       "value": "50",
       "units": "unknown (arc visual)",
       "source": "projectiles.FireballSpell.Gravity",
       "confidence": "DATA"
      },
      {
       "name": "cast delay before launch",
       "value": "0",
       "units": "ms",
       "source": "spells_other.Fireball.CustomDeployTime blank (2017 and 2023)",
       "confidence": "DATA"
      },
      {
       "name": "example flight time, king centre (9,3) to enemy princess centre (3.5,25.5), 23.16 tiles",
       "value": "2.316 s / 47 ticks under /60; 1.930 s / 39 ticks under /50",
       "units": "seconds / ticks at TICK_MS 50",
       "source": "arithmetic on the row above",
       "confidence": "INFERENCE"
      },
      {
       "name": "knockback duration",
       "value": "unknown; crforge uses 500 ms (unsourced, 'reference JS'); no key in 2017 or 2023 globals.csv",
       "units": "ms",
       "source": "crforge KnockbackHelper.java; grep of both globals.csv",
       "confidence": "INFERENCE"
      },
      {
       "name": "thin-slice units immune to Fireball knockback (live)",
       "value": "Giant, Prince (2023 IgnorePushback TRUE). BabyDragon is TRUE in the 2017 data but lost immunity 2018-08-06",
       "units": "set",
       "source": "characters.csv IgnorePushback 2017 and 2023; wiki Version History 2018 (Balance Changes 6/8/2018)",
       "confidence": "DATA"
      },
      {
       "name": "DeflectRadius (Monk only, not thin slice)",
       "value": "700",
       "units": "millitiles",
       "source": "cr-csv 2023 projectiles.FireballSpell.DeflectRadius; wiki 'reflect radius 0.7' 27/3/2023",
       "confidence": "DATA"
      }
     ],
     "behaviour": [
      "1. VALIDATE: cost 4 elixir [DATA spells_other.Fireball.ManaCost]. Territory Anywhere: enemy side, river, on top of buildings/towers all legal [COMMUNITY wiki Fireball 25/4/2018 river; DATA cr-csv 2023 CanPlaceOnWater/CanDeployOnEnemySide/CanPlaceOnBuildings TRUE]. The 2017-derived spell.can_deploy_on_enemy_side=false and can_place_on_buildings=false are BLANK cells, not facts: do not consume them [INFERENCE]. arena.rs deploy_zone already returns Ok for Territory::Anywhere before the water check.",
      "2. COMMIT: elixir spent and card cycled at the command, as for troops [INFERENCE: engine convention].",
      "3. LAUNCH: in the first Spawn phase after the command (same latency as a troop, so both teams' same-tick casts materialise together) create one spell projectile: team = caster, source entity = caster King Tower [COMMUNITY wiki King's Tower: spell damage 'originates from the King Tower and technically counts as an attack by it'], pos = launch point (calibration spells.LAUNCH_POINT, default caster king tower centre arena.rs::king_tower_pos) [COMMUNITY wiki Fireball: 'travel time ... depending on how far it is sent from the King's Tower'], aim = tap point, fixed [DATA Homing blank], no pre-launch delay [DATA CustomDeployTime blank].",
      "4. FLIGHT: each Projectile phase advance by 600 * projectile.SPEED_TO_SUBTILES_PER_TICK along the straight ground segment with fractional carry (path.rs::advance). Gravity 50 is treated as visual (key projectile.FLIGHT_PATH) [INFERENCE]. Flight is never blocked or deflected in the thin slice [INFERENCE; DeflectBehaviour is Monk-only].",
      "5. IMPACT HIT TEST, evaluated on the arrival tick with positions as they are then, not at cast [COMMUNITY wiki Fireball: troops at the edge at launch 'may not be hit by the time the spell actually lands']: every live entity that is an enemy of the caster [DATA OnlyEnemies], air or ground [DATA AoeToAir, AoeToGround], with centre distance <= 45000 subtiles + victim collision radius (edge-inclusive, as combat.rs::splash does today; key spells.AOE_HIT_TEST) [INFERENCE]. A crown tower is reached by a square instead: the distance from the spell's point to a square of half-side 1000 native round the tower's centre, strictly below the spell's radius (spells.CROWN_TOWER_SPELL_REACH = client_square_1000_strict, measured on client 15.535.29). Skip hidden entities (underground Tesla) once hiding exists [COMMUNITY wiki Fireball: Tesla 'immune to it while underground'; DATA projectiles.csv has no AffectsHidden, AEO AffectsHidden TRUE only on Freeze].",
      "6. DAMAGE into the damage buffer: level-scaled damage (325 at level 1 of the 2017 Rare table) to troops and non-crown buildings (Cannon, Tesla) in full [COMMUNITY wiki 'quite effective versus buildings']. Crown towers (princess and king) take dmg + trunc_toward_zero(dmg * CTDP / 100) = ceil(dmg * pct / 100), with CTDP -60 in the 2017 vintage [DATA; law: COMMUNITY, Supercell published L11 values fit 7/7 non-circular points vs 3 for floor]. A king tower hit triggers activation (already in engine) [COMMUNITY wiki King's Tower].",
      "7. KNOCKBACK SELECTION (buffered like damage, never applied inline): among the step-5 victims, every TROOP (never a building or crown tower: no Speed column) [DATA; INFERENCE] whose ignore_pushback is false, because PushbackAll is false for Fireball [DATA; decoded: only Log/Monk/MegaCannon ship PushbackAll TRUE and the wiki names exactly Log, Monk and Little Prince as pushing 'all ground troops']. Air units are included [COMMUNITY wiki Fireball: Balloon 'knocked away', Inferno Dragon ramp reset]. Thin slice live: pushed = Knight, Archer, Musketeer, HogRider, Minion, BabyDragon, Valkyrie, Skeleton, Wizard; not pushed = Giant, Prince.",
      "8. KNOCKBACK VECTOR: unit(victim.pos - impact point) * Pushback (32400 subtiles for the 2017 vintage, 18000 live) [DATA Pushback = distance; COMMUNITY radial: wiki 'place ... on the side of the troop opposite to where the player would like to redirect it']. Integer normalisation: i64 isqrt, remainder handling ours. Zero vector (victim exactly on the impact point): key knockback.ZERO_VECTOR_DIRECTION, and it must be mirror-symmetric (for example the caster's forward y) [INFERENCE; crforge's fixed (0,+1) for both teams breaks the 180-degree seat symmetry]. Two knockbacks landing on one unit in the same tick are combined by an order-independent rule (key knockback.STACKING, default vector sum) [INFERENCE].",
      "9. KNOCKBACK MOTION: the displacement is spread over knockback.DURATION_MS (0 = instant) [INFERENCE; no data key in 2017 or 2023]. During it the unit neither walks nor attacks, and an attack windup in progress is reset to Idle (not paused like a stun) [COMMUNITY wiki Fireball: 'a true stun that resets the attack animations'; wiki The Log: 'will reset their attack animation']. The target is not force-cleared; ordinary retargeting applies afterwards [COMMUNITY wiki Fireball: Rascal Boy 'possibly causing him to retarget'; VH2018: Sparky no longer resets after being knocked out of range].",
      "10. KNOCKBACK TERRAIN: the river does not block the displacement [COMMUNITY wiki The Log: push 'the unit over the river back to the opponent's side']. A ground unit left on a water cell is resolved by knockback.WATER_RESOLUTION [COMMUNITY VH2020: hover units 'When knocked into the river, they will no longer bounce off of it', which implies ground units do bounce]. Clamp to arena bounds [INFERENCE].",
      "11. RESOLVE/REAP as usual: deaths from the damage buffer; the projectile is removed on its arrival tick. A unit killed this tick is not displaced (displacement is irrelevant once it is dead) [INFERENCE]."
     ],
     "targets_affected": "Air and ground enemy troops (damage plus knockback, except IgnorePushback units, which take damage only); enemy non-crown buildings (Cannon, Tesla) take full damage and are never displaced; enemy crown towers (princess and king) take reduced damage (2017 data 40%, live 23% as of 2026-09-08) and are never displaced; hitting the king tower activates it. No friendly fire (OnlyEnemies) on own troops, buildings or towers. Knockback is troops only; PushbackAll=false so immune units stay put (live thin slice: Giant, Prince). Air units are pushed.",
     "unsettled": [
      "LAUNCH POINT. Candidates: (L1) caster king tower centre (9,3)/(9,29); (L2) king centre offset toward the target by KingTower ProjectileStartRadius 750 mt; (L3) an off-screen point behind the king. Decide by casting Fireball at 3 or more distances in at least 2 directions, timestamping the frame the fireball first appears and the explosion frame, then fitting t = |aim - L| / v (the intercept separates L from command latency).",
      "PROJECTILE SPEED UNITS. Candidates: Speed/60 tiles/s (AesDragon assertion, crforge, same as the troop /60 camp) vs Speed/50 (Darknighture frame test: Musketeer projectile Speed 1000 moved 20 tiles/s; also cites Giant 54 tiles/min at Speed 45). No source found for the claim 'troops /50, projectiles /60'. Decide by the same flight-time regression: over 23 tiles the two predict 2.32 s vs 1.93 s. Measure projectile and troop divisors independently; do not assume they share units.",
      "FLIGHT PATH LENGTH. Candidates: straight 2D ground distance vs ballistic arc length from Gravity 50. Decide: if flight time vs ground distance is linear through the intercept, the path is 2D.",
      "AOE HIT TEST. Candidates: edge-inclusive (dist <= 2.5 tiles + victim collision radius; engine convention) vs centre-in-radius. Decide: stationary Knight (r 0.5) with its centre at 2.8 tiles from the impact is hit under edge, missed under centre.",
      "CROWN-TOWER ROUNDING. Candidates: ceil(dmg*pct/100) == dmg + trunc0(dmg*CTDP/100) (fits every Supercell-published value: 207@30, 172@25, 31@25, 25@20, 371@25, 58@30, 48@25); floor (engine today, fits 3/8); round half-up (5/8). The evidence is patch-note stat sheets, not in-battle numbers. Decide by reading the princess tower HP number before and after a Fireball impact in a recording.",
      "KNOCKBACK DISPLACEMENT LAW. Candidates: (K1) fixed Pushback regardless of distance from centre; (K2) linear falloff with distance; (K3) crforge's speed=P/1s for 0.5 s, which moves only P/2. Supercell words it as 'Pushback: 1.8 tile -> 1 tile', which favours K1. Decide: frame-track Knights at 0.5 and 2.3 tiles from the impact and compare final displacements.",
      "KNOCKBACK DURATION and incapacitation window. Candidates: instant; constant velocity over D ms (crforge D=500, unsourced). Decide: frame-count the displacement at 60 fps.",
      "ZERO-VECTOR DIRECTION. Candidates: caster forward y; projectile travel direction (impact - launch); no push. Decide: Fireball centred exactly on a stationary unit.",
      "WATER RESOLUTION after a push into the river. Candidates: eject to nearest land cell; continue to the far bank along the push; clamp at the bank. Decide: Fireball a Knight standing 0.3 tiles from the bank, off-bridge.",
      "KNOCKBACK OF DEPLOYING UNITS (deploy_ms > 0). Community evidence shows deploying units take spell damage (wiki Arrows vs Goblin Barrel; standard Fireball-on-placement play), but not whether they are pushed. Decide: Fireball a Musketeer on its placement frame.",
      "IgnorePushback VINTAGE. The 2017 flags are stale for BabyDragon (and Balloon, InfernoDragon, BattleRam); 2023 data agrees with the wiki changes. Which vintage the engine loads is a project decision, not a measurement (the target is live 2026)."
     ],
     "engine_test_ideas": [
      "fireball_flight_ticks_equal_distance_over_speed: Blue casts at a point D subtiles from Blue king centre; assert the damage tick = spawn tick + ceil(D / (600*mult)). PLANT spell_instant (damage applied at the tap tick) and PLANT launch_from_tap_point must fail it.",
      "fireball_hit_test_is_at_impact_not_cast: an enemy Hog Rider walking out of the radius during flight takes 0 damage, while a unit that walks in takes full damage. PLANT hit_test_at_cast.",
      "fireball_radius_edge_inclusive: Knight centres at exactly 45000+9000 subtiles (hit) and +1 subtile (miss). PLANT aoe_centre_to_centre.",
      "fireball_crown_tower_uses_ceil_law: princess tower hit by a 688-damage Fireball with CTDP -70 loses exactly 207; with -75 loses 172; with -77 loses 159. PLANT ct_floor (combat.rs::damage_against as today) reports 206 and 158.",
      "fireball_knockback_radial_fixed_distance: Knight 1 tile due east of the impact ends exactly +Pushback in x and 0 in y once knockback completes, with no other motion (for example DURATION 0, or the Knight has no target). PLANT no_knockback; PLANT knockback_half_distance (crforge law).",
      "fireball_respects_ignore_pushback_and_pushback_all: Giant and Prince in radius keep their positions bit-identically and take damage; the same test with a projectile whose pushback_all=true moves them. PLANT ignore_flag_not_read (card.rs never loads ignore_pushback today, so the test is a live specification).",
      "fireball_never_moves_buildings: Cannon, Tesla and princess tower inside the radius keep positions bit-identically. PLANT push_buildings.",
      "fireball_only_enemies: own Knight and own princess tower inside the radius take 0 damage and 0 displacement. PLANT friendly_fire.",
      "fireball_pushes_air: Minion in radius is damaged and displaced; BabyDragon too when loaded from live-vintage flags. PLANT air_immune_to_knockback.",
      "fireball_knockback_resets_windup: Musketeer mid-windup when hit fires strictly later than an unhit control Musketeer; attack_phase goes to Idle, not paused. PLANT knockback_pauses_like_stun.",
      "fireball_mirror_symmetry_including_zero_vector: Blue Fireball at (x,y) onto a Red Knight exactly at the impact vs Red Fireball at (x,32-y) onto a Blue Knight; final states are mirror images. PLANT zero_vector_plus_y (fixed +y for both teams, as crforge does).",
      "two_fireballs_same_tick_order_independent: Blue and Red spells (or two Blue casts) landing on one unit in the same tick give the same state_hash whichever is enqueued first. PLANT sequential_knockback (apply inline in slot order).",
      "knockback_into_river_never_leaves_ground_unit_in_water: after the knockback completes, no ground unit centre is on a water cell. PLANT no_water_resolution.",
      "save_load_mid_flight_and_mid_knockback: snapshot while the projectile is in flight and while a unit is mid-knockback; the loaded run reproduces state_hash for 200 ticks. PLANT save_drops_spell_projectiles, PLANT save_drops_knockback_timer."
     ]
    },
    {
     "card": "Arrows",
     "data_chain": "2017 vintage (the vendored data): cards.json cards[name=Arrows].spell.first_projectile (= spells_other.Arrows.CustomFirstProjectile -> projectiles.ArrowsSpell) -> .damage 115, .radius_milli 4000, .speed 800, .homing true, .crown_tower_damage_percent 40 (raw -60), .aoe_to_air/.aoe_to_ground true, .only_enemies true, .pushback_milli null; cards[Arrows].spell.multiple_projectiles 15 (spells_other.Arrows.MultipleProjectiles, visual count); cards[Arrows].spell.radius_milli 4000 (spells_other.Arrows.Radius); cards[Arrows].projectile = ArrowsSpellDeco (spells_other.Arrows.Projectile), damage null, VISUAL ONLY and never the damage source; cards[Arrows].elixir 3. Live vintage (not vendored): smlbiobot/cr-csv 2023 spells_other.Arrows.{Projectile=ArrowsSpell, ProjectileWaves 3, ProjectileWaveInterval 200, MultipleProjectiles 10, Radius 4000} -> projectiles.ArrowsSpell.{Damage 48, Radius 1400, Speed 1100, CTDP -70, HideWhenDelayed TRUE}; Supercell 2025-03-04 radius 3500; Supercell 2026-06-01 CT 20%.",
     "numbers": [
      {
       "name": "elixir cost",
       "value": "3",
       "units": "elixir",
       "source": "spells_other.Arrows.ManaCost (2017 and 2023)",
       "confidence": "DATA"
      },
      {
       "name": "damage per hit, level 1, 2017 vintage (single hit)",
       "value": "115",
       "units": "hp",
       "source": "projectiles.ArrowsSpell.Damage via spells_other.Arrows.CustomFirstProjectile",
       "confidence": "DATA"
      },
      {
       "name": "waves, 2017 vintage",
       "value": "1",
       "units": "damage events",
       "source": "wiki Arrows 26/11/2019 'three volleys of Arrows instead of one'; 2017 spells_other.csv has no ProjectileWaves column",
       "confidence": "COMMUNITY"
      },
      {
       "name": "MultipleProjectiles (2017)",
       "value": "15 (1 damaging CustomFirstProjectile + deco arrows; visual only)",
       "units": "projectile count",
       "source": "spells_other.Arrows.MultipleProjectiles; cross-card: characters.Princess MultipleProjectiles 5 + CustomFirstProjectile PrincessProjectile + Projectile PrincessProjectileDeco (damage-less)",
       "confidence": "DATA"
      },
      {
       "name": "radius, 2017 vintage",
       "value": "4000 = 72000 subtiles",
       "units": "millitiles",
       "source": "projectiles.ArrowsSpell.Radius and spells_other.Arrows.Radius",
       "confidence": "DATA"
      },
      {
       "name": "projectile Speed raw",
       "value": "800 (2017; raised from 600 on 13/3/2017); 1100 live (since 2019-06-03)",
       "units": "raw Speed",
       "source": "projectiles.ArrowsSpell.Speed 2017; cr-csv 2023 1100; wiki Arrows history",
       "confidence": "DATA"
      },
      {
       "name": "projectile speed in engine units",
       "value": "800: 12000 (x15) or 14400 (x18); 1100: 16500 (x15) or 19800 (x18)",
       "units": "subtiles per tick",
       "source": "derived; same unit dispute as Fireball",
       "confidence": "INFERENCE"
      },
      {
       "name": "Homing",
       "value": "true, but inert for a position-targeted spell",
       "units": "boolean",
       "source": "projectiles.ArrowsSpell.Homing TRUE (2017, 2023); inertness inferred",
       "confidence": "INFERENCE"
      },
      {
       "name": "CrownTowerDamagePercent raw (2017)",
       "value": "-60 -> 40%",
       "units": "percent delta from 100",
       "source": "projectiles.ArrowsSpell.CrownTowerDamagePercent; wiki 40% until 12/12/2017",
       "confidence": "DATA"
      },
      {
       "name": "crown tower percent, live history",
       "value": "35% (2017-12-12), 30% (2020-08-04), 25% (2024-08-06), 20% (2026-06-01, Supercell 'Arrows: 93 -> 75', i.e. 3 x 31 -> 3 x 25 at L11)",
       "units": "percent",
       "source": "wiki Arrows history; supercell.com june-balance-changes-2026",
       "confidence": "COMMUNITY"
      },
      {
       "name": "waves, live",
       "value": "3",
       "units": "damage events per cast",
       "source": "cr-csv 2023 spells_other.Arrows.ProjectileWaves; wiki 26/11/2019",
       "confidence": "DATA"
      },
      {
       "name": "wave interval, live",
       "value": "200 = 4 ticks at TICK_MS 50",
       "units": "ms",
       "source": "cr-csv 2023 spells_other.Arrows.ProjectileWaveInterval",
       "confidence": "DATA"
      },
      {
       "name": "damage per wave, level 1, live",
       "value": "48 (L11: 122)",
       "units": "hp",
       "source": "cr-csv 2023 projectiles.ArrowsSpell.Damage; cr-api-data damage_per_level L11 122; wiki dmg_11 122",
       "confidence": "DATA"
      },
      {
       "name": "sub-area radius per arrow, live",
       "value": "1400 = 25200 subtiles",
       "units": "millitiles",
       "source": "cr-csv 2023 projectiles.ArrowsSpell.Radius; wiki 26/10/2022 'multiple 1.4-tiles areas instead of one 4-tiles area ... effective Attack Radius remained unchanged'",
       "confidence": "DATA"
      },
      {
       "name": "arrows per wave, live",
       "value": "10 (whether each carries damage with per-unit dedupe is unsettled)",
       "units": "projectiles",
       "source": "cr-csv 2023 spells_other.Arrows.MultipleProjectiles",
       "confidence": "DATA"
      },
      {
       "name": "effective spell radius, live",
       "value": "3500 = 63000 subtiles (4000 until 2025-03-04)",
       "units": "millitiles",
       "source": "supercell.com march-balance-changes (4 Mar 2025) 'Radius: 4 tiles -> 3.5 tiles'",
       "confidence": "COMMUNITY"
      },
      {
       "name": "crown tower damage per wave, level 11 live",
       "value": "25",
       "units": "hp",
       "source": "Supercell June 2026 '75' total / 3 waves",
       "confidence": "COMMUNITY"
      },
      {
       "name": "pushback",
       "value": "none",
       "units": "-",
       "source": "projectiles.ArrowsSpell.Pushback blank (2017, 2023)",
       "confidence": "DATA"
      },
      {
       "name": "AoeToAir / AoeToGround / OnlyEnemies",
       "value": "true / true / true",
       "units": "boolean",
       "source": "projectiles.ArrowsSpell; spells_other.Arrows.OnlyEnemies",
       "confidence": "DATA"
      },
      {
       "name": "example flight time king centre to enemy princess centre (23.16 tiles), Speed 1100",
       "value": "1.263 s / 26 ticks (/60) vs 1.053 s / 22 ticks (/50); first wave, later waves +200 ms each",
       "units": "s / ticks",
       "source": "arithmetic on the row above",
       "confidence": "INFERENCE"
      }
     ],
     "behaviour": [
      "1. VALIDATE and COMMIT exactly as Fireball: 3 elixir, Territory Anywhere [DATA spells_other.Arrows.ManaCost; COMMUNITY wiki 25/4/2018 river; DATA cr-csv 2023 CanPlaceOnWater/CanDeployOnEnemySide TRUE].",
      "2. RESOLVE THE DAMAGING PROJECTILE FROM DATA, NOT FROM cards[Arrows].projectile: when spell.first_projectile (CustomFirstProjectile) exists it is the only damage carrier; spell.projectile (ArrowsSpellDeco) and the remaining MultipleProjectiles are visual and deal nothing [DATA cross-card Princess pattern; COMMUNITY 2017 Arrows was one hit]. When the row has no CustomFirstProjectile (2023+), Projectile itself carries the damage [DATA cr-csv 2023].",
      "3. WAVES: N = spells_other.ProjectileWaves, defaulting to 1 when the column is absent (2017), 3 live; interval = ProjectileWaveInterval ms, defaulting to 0 (200 live) [DATA 2023]. In the first Spawn phase after the command create wave 0; wave i is released i*interval ms later (4 ticks at 50 ms), hidden until then [DATA HideWhenDelayed TRUE]. All waves fly from the same launch point (spells.LAUNCH_POINT) to the same tap point, so their impacts are exactly i*interval apart [INFERENCE].",
      "4. FLIGHT: as Fireball, Speed 800 (2017) or 1100 (live) times the projectile multiplier; Homing TRUE is inert because the aim is a fixed position with no target entity [INFERENCE].",
      "5. PER-WAVE IMPACT, each evaluated on its own arrival tick with current positions: enemies only [DATA OnlyEnemies], air and ground [DATA], the same edge-inclusive hit test as Fireball [INFERENCE]. Area model per key arrows.WAVE_AREA_MODEL. Default W1: one disc of the spell radius (4000 in 2017, 3500 live) centred on the tap point, each enemy hit AT MOST ONCE per wave [COMMUNITY wiki Arrows: '2 volleys eliminate ... Goblins' and 'a single volley is enough to take out Skeletons'; Supercell 93 = 3 x 31 for towers]. W2, explicit 10 sub-areas of 1400 with per-wave dedupe, is available for when the pattern is measured.",
      "6. DAMAGE per wave: level-scaled damage (115 at level 1 in 2017; 48 live) to troops and non-crown buildings; crown towers take the same ceil law as Fireball with CTDP -60 (2017) [DATA; law COMMUNITY]. Every wave that touches the king tower counts as king damage (activation) [COMMUNITY wiki King's Tower].",
      "7. NO KNOCKBACK, NO STATUS: positions and attack phases of hit units are untouched [DATA no Pushback, no TargetBuff].",
      "8. Waves are independent: a unit killed by wave 0 is not a target for wave 1, and a unit that enters the area before wave 2 lands takes wave 2 only [INFERENCE, follows from step 5 evaluating at arrival]. Remove each wave on its arrival tick; the cast is complete after wave N-1."
     ],
     "targets_affected": "Air and ground enemy troops; enemy non-crown buildings (full damage); enemy crown towers (reduced: 40% in 2017 data, 20% live since 2026-06-01), once per wave. No friendly fire. No knockback and no stun on anything. King tower hits activate the king.",
     "unsettled": [
      "WAVE AREA MODEL (live). Candidates: (W1) one disc of spell Radius per wave with one hit per unit; (W2) 10 explicit 1.4-tile sub-areas per wave with per-unit dedupe; (W3) W2 without dedupe (a unit under overlapping sub-areas takes several hits per wave). The wiki's '2 volleys eliminate Goblins' (Goblin L11 hp 204, wave 122) argues against W3. Geometric note [INFERENCE, covering constant from recollection, not verified]: 10 discs of r=1.4 cannot cover a disc of r=4.0 (ratio 0.35 < ~0.43), so W2 at the 2023 radius must leave rim gaps, while at live 3.5 with a 0.5 victim radius (1.9/4.0 = 0.475) it can cover. Decide by casting Arrows repeatedly on Skeletons (one-hit kill) spaced at radii 2.5-3.9 tiles and at several angles; a fixed gap pattern means W2.",
      "WAVE RELEASE. Candidates: all waves launch together and land together while their damage is delayed, vs launch i*200 ms apart. These differ only in whether the delay is applied before or after flight; the impact spacing is the same. Decide by frame-timing the three hit flashes against the cast frame at two distances.",
      "WHICH VINTAGE LOADS. The 2017 data describes a mechanically different card (one hit of 115 at r=4.0, speed 800) from the live one (3 x 48 at r=3.5, speed 1100, 200 ms apart). That is not tuning. The engine should read waves and interval from data with defaults 1 and 0 so either vintage runs; choosing the vintage is a project decision, not a measurement.",
      "The LAUNCH POINT, SPEED UNITS, AOE HIT TEST and CROWN-TOWER ROUNDING questions are shared with Fireball and have the same deciding observations.",
      "Does the extractor's multiple_projectiles comment ('Whether 15 is visual arrows or damage waves is NOT established') still stand? Settled as visual: the 2017 card was a single hit per the wiki, and 2023 carries waves in a separate ProjectileWaves column. The comment in tools/extract_cards.py is stale and can be updated."
     ],
     "engine_test_ideas": [
      "arrows_2017_vintage_single_hit_ignores_deco: a Skeleton-army-sized stack under 2017 Arrows takes exactly 115 each, once. PLANT multiple_projectiles_as_damage (15 hits) and PLANT deco_projectile_as_damage_source (damage 0 or crown 100 read from cards[Arrows].projectile).",
      "arrows_live_three_waves_four_ticks_apart: with ProjectileWaves 3 and interval 200 at TICK_MS 50, a stationary enemy Knight's hp drops on ticks T, T+4, T+8 by exactly the wave damage each. PLANT waves_simultaneous (all on T) and PLANT waves_missing_defaults_to_3 (a 2017 row without the column must stay single-hit).",
      "arrows_one_hit_per_unit_per_wave: under W2 with overlapping sub-areas, a unit at the centre loses exactly one wave's damage per wave. PLANT per_subarea_hits.",
      "arrows_waves_hit_test_at_each_arrival: a Hog Rider that leaves the disc between wave 0 and wave 1 takes only wave 0. PLANT hit_test_once_at_first_wave.",
      "arrows_no_knockback: every unit in radius has the same position and attack_phase as a control run without Arrows (except for damage deaths). PLANT pushback_from_card_level (reads a Fireball-like 1800).",
      "arrows_crown_tower_per_wave_ceil: princess tower loses 3 * ceil(122*20/100) = 75 over the cast. PLANT ct_floor (72).",
      "arrows_mirror_symmetry: Blue Arrows at (x,y) vs Red Arrows at (x,32-y) on mirrored swarms give mirrored hp vectors and deaths on the same ticks. PLANT wave_release_uses_raw_entity_id_order.",
      "arrows_save_load_between_waves: snapshot after wave 0 lands; the loaded run lands waves 1 and 2 on identical ticks with identical state_hash. PLANT save_drops_pending_waves."
     ]
    }
   ],
   "shared_mechanics": [
    "CAST PIPELINE (all projectile spells: Fireball, Arrows, Rocket, Goblin Barrel's barrel). A deploy command validates with Territory::Anywhere, including river and buildings (live since 2018-04-25). In the next Spawn phase it enqueues SpellProjectile{team, source=caster KingTower id, pos=spells.LAUNCH_POINT, aim=tap point (fixed), speed=raw*projectile.SPEED_TO_SUBTILES_PER_TICK, damage carrier, release_ms}. It advances in the Projectile phase with fractional carry; on arrival it writes damage into the DamageBuffer and knockback into a parallel KnockbackBuffer, and resolves in one pass. Same-tick casts by both teams must be indistinguishable from either order. Sources: wiki Fireball/King's Tower (launch from the King's Tower); spells_other CustomDeployTime blank (no cast delay); engine TICK_PHASES convention.",
    "DAMAGE CARRIER RESOLUTION. If spells_other.CustomFirstProjectile is set, it carries the damage and Projectile is visual (the 2017 Arrows and Princess pattern). Otherwise Projectile carries the damage. MultipleProjectiles is a visual or sub-area count, never a damage multiplier; waves are ProjectileWaves x ProjectileWaveInterval (2023+ columns, defaulting to 1 and 0 when absent). Loader: card.rs RawProjectileObj reads speed, damage and radius, the target buff, homing, the range projectile's range and radius, aoe_to_air, aoe_to_ground, only_enemies, pushback and pushback_all, the pingpong time, check_collisions, the start extra radius, the random delay, a spawned projectile and a spawned area effect. The engine acts on each through its own ledger key. Spells load as card.rs SpellShape.",
    "CROWN TOWER REDUCTION ENCODING AND ROUNDING. CrownTowerDamagePercent raw is a delta from 100 (blank means 100). Cross-vintage evidence: 2017 -60 <-> 40% before 12/12/2017; 2023 -70 <-> 30% (2020-08-04); 2023 Rocket -75 <-> Supercell 371 = 25% of 1484; 2023 Log -80 <-> Supercell 58 = 20% of 290. Rounding: towers take dmg + trunc_toward_zero(dmg*CTDP/100) (== ceil(dmg*(100+CTDP)/100)). It reproduces all 8 Supercell-published level-11 values checked (7/7 non-circular); combat.rs::damage_against's floor reproduces 3/8 and round-half-up 5/8 (checked against the published level-11 values). 'Crown tower' means princess + king only (EntityKind::is_crown_tower); Cannon and Tesla take full damage.",
    "AOE HIT TEST FOR SPELLS. Evaluated on the arrival tick; OnlyEnemies; AoeToAir/AoeToGround choose the flying/ground sets with height ignored; edge-inclusive (centre distance <= radius + victim collision radius) is the engine convention and is UNVERIFIED for spells. A crown tower is reached by the square of spells.CROWN_TOWER_SPELL_REACH instead (half-side 1000 round its centre, strictly below the radius; measured). Hidden entities (underground Tesla) are immune to projectile spells (no AffectsHidden column on projectiles.csv).",
    "KNOCKBACK LAW (Fireball, Rocket, The Log, Bowler, Mega Knight spawn, Golem/Golemite death). Data semantics settled by cross-card and history checks. Pushback is a displacement DISTANCE in millitiles (Fireball 1800 <-> Supercell '1.8 tile'; Log 1000 <-> wiki '1 tile' 23/1/2017; Golemite 1800 -> wiki '0.9 (from 1.8)'). PushbackAll=TRUE overrides the victim's IgnorePushback: only Log, Monk and MegaCannon ship it (2023), and the wiki names exactly Log, Monk and Little Prince as pushing 'all ground troops'; the Log reset Prince/Dark Prince charge from 19/9/2016. PushbackAll=false does NOT mean primary-target-only (Fireball, Bowler and Mega Knight spawn all push many). There is no mass term in any column (Mass is collision-only; Tornado's push_mass_factor is a buff column). Direction is radial from the impact for area sources and along travel for rolling or piercing ones (COMMUNITY). Buildings and crown towers are never moved. Air units are pushed. A windup in progress is reset, not paused (COMMUNITY). The river does not block the push and ground units in water get resolved (COMMUNITY). Duration, zero-vector direction, stacking, water resolution, displacement falloff and deploy-state behaviour are NOT in any data (no globals key in 2017 or 2023) and must be calibration keys. Buffer the displacements and combine them commutatively; the zero-vector fallback must be mirror-symmetric (crforge's fixed (0,+1) is not).",
    "IgnorePushback IS VINTAGE-DEPENDENT. Wiki Version History and 2023 characters.csv agree: pushback immunity was REMOVED from Battle Ram (2018-06-04) and from Inferno Dragon, Baby Dragon and Balloon (2018-08-06); ADDED to Cannon Cart (2018-08-06), Skeleton King (2021), Mighty Miner (2022) and Monk (2025-12-12). For the thin slice, the 2017 cards.json BabyDragon ignore_pushback=true is stale for the live target. The same 2023 file ships Prince CollisionRadius 600 (2017: 650), which is the vintage difference recorded in mechanics.md under Open questions.",
    "DATA VINTAGE FINDING. The vendored retroroyale data predates the Electrifying Update of 12/12/2017: Knight HitSpeed 1100, HogRider 1500 and Prince 1500 are that update's pre-change values, spell CT is 40%, and Bats/MegaKnight/SkeletonBalloon/DartBarrell/MovingCannon carry ReleaseDate 'Soon'. It postdates 12/6/2017 (Log ProjectileRange 11100) and 13/3/2017 (Arrows Speed 800). This contradicts cards.json provenance.roster_dating ('roughly the first half of 2018').",
    "LIVE-2026 OVERLAY VALUES FOR THE FAMILY, each with a Supercell or wiki date. Fireball: Pushback 1000 (2022-08-02); CT 30% -> 25% (2026-06-01) -> ~23% (2026-09-08, 159 at L11); radius 2.5; speed 600; L11 damage 688. Arrows: 3 waves x 48 (L1) / 122 (L11), 200 ms apart; sub-area 1.4; spell radius 3.5 (2025-03-04); speed 1100 (2019-06-03); CT 20% (2026-06-01). Whether to load 2017 data, 2023 data, or a hand-sourced live overlay is a project decision (a vintage question, not a tuning choice).",
    "DERIVED-DATA TRAPS. cards[Arrows].projectile is ArrowsSpellDeco (damage null, crown 100, radius null); the damaging object is cards[Arrows].spell.first_projectile. cards[Fireball|Arrows].spell.can_deploy_on_enemy_side=false and can_place_on_buildings=false come from BLANK 2017 cells (2023 rows are TRUE) and must not gate spell placement. The engine's Territory::Anywhere already ignores them.",
    "PENDING DAMAGE (unsettled; affects towers retargeting while a lethal spell is in flight). 2017 and 2023 globals ship LOGIC_PENDING_DAMAGE_IGNORE_IF_DURATION_LESS=600 and (2023) CURRENT_TARGET_IGNORES_PENDING_DAMAGE=FALSE. Their semantics are not established, and it is not established whether in-flight spell damage counts as pending. Do not implement without an observation (for example, whether a princess tower keeps shooting a Skeleton that an in-flight Fireball will kill)."
   ],
   "calibration_keys_proposed": [
    "spells.LAUNCH_POINT: value 'caster_king_tower_centre', status community (wiki: launched from the King's Tower; exact origin unstated), confidence LOW; candidates caster_king_tower_centre | king_centre_plus_ProjectileStartRadius_toward_target | offscreen_behind_king. Promotion: flight-time regression over >=3 cast distances in >=2 directions from 60 fps video, fitting launch point and speed jointly.",
    "projectile.SPEED_TO_SUBTILES_PER_TICK: value 15 (mirrors time.SPEED_TO_SUBTILES_PER_TICK), status hypothesis, confidence LOW; candidates 15 (Speed = tiles/min at 20 TPS; AesDragon assertion, crforge) | 18 (Speed/50 tiles/s; Darknighture frame test, Musketeer projectile 1000 -> 20 tiles/s). Kept separate from the troop key so a measurement can split them. Promotion: Fireball flight time over 20+ tiles (2.32 s vs 1.93 s over 23.16 tiles).",
    "projectile.FLIGHT_PATH: value 'straight_ground_2d', status guess; candidates straight_ground_2d | ballistic_arc_from_Gravity. Promotion: flight time is linear in ground distance.",
    "spells.CAST_DELAY_MS: value 0, status datamined (spells_other.CustomDeployTime blank for Fireball and Arrows in 2017 and 2023), confidence medium. Promotion: cast-frame to projectile-appearance frame in a recording.",
    "spells.AOE_HIT_TEST: value 'edge_inclusive', status guess (engine convention from ADD_CHARACTER_RANGE_TO_RADIUS, not shown for spells); candidates edge_inclusive | centre_in_radius. Promotion: Knight centre at radius + 0.3 tile from a Fireball impact, hit or miss.",
    "combat.CROWN_TOWER_DAMAGE_ROUNDING: value 'ceil_kept_share' (dmg + trunc_toward_zero(dmg*CTDP/100)), status community (Supercell patch-note level-11 values 8/8; floor 3/8; round 5/8), confidence medium; candidates ceil_kept_share | floor | round_half_up. Supersedes the unsourced floor in combat.rs::damage_against, so combat.DAMAGE_ARITHMETIC's note should strike 'TRUNCATED (floor)'. Promotion: princess tower HP readout before and after a spell impact in a recording.",
    "knockback.DISPLACEMENT_LAW: value 'fixed_distance_radial', status community (Supercell wording 'Pushback: 1.8 tile -> 1 tile'), confidence LOW; candidates fixed_distance_radial | linear_falloff_with_distance | half_distance_constant_speed (crforge). Promotion: frame-tracked displacement of Knights at 0.5 and 2.3 tiles from the impact.",
    "knockback.DURATION_MS: value 0 (instant) as placeholder, status guess, confidence LOW; candidates 0 | 500 (crforge, unsourced). Absent from 2017 and 2023 globals.csv. Promotion: 60 fps frame count of a Fireball knockback.",
    "knockback.ZERO_VECTOR_DIRECTION: value 'caster_forward_y', status guess; candidates caster_forward_y | projectile_travel_direction | none. All must be mirror-symmetric. Promotion: Fireball centred exactly on a stationary unit.",
    "knockback.STACKING: value 'vector_sum', status guess; candidates vector_sum | max_magnitude_with_mirror_invariant_tiebreak | latest_replaces. Promotion: two knockback sources landing on the same tick (Fireball + Log) on one Knight.",
    "knockback.WATER_RESOLUTION: value 'eject_to_nearest_land', status guess (community confirms pushes cross the river and ground units 'bounce'); candidates eject_to_nearest_land | continue_along_push_to_far_bank | clamp_at_bank. Promotion: Fireball a Knight 0.3 tiles from the bank off-bridge.",
    "knockback.RESETS_WINDUP: value true, status community (wiki Fireball 'true stun that resets the attack animations'; wiki The Log 'reset their attack animation'), confidence medium. Promotion: Musketeer shot timing with and without a mid-windup Fireball.",
    "knockback.AFFECTS_DEPLOYING_UNITS: value true, status guess (damage during deploy is community-established; displacement is not). Promotion: Fireball on a Musketeer's placement frame.",
    "arrows.WAVE_AREA_MODEL: value 'single_disc_spell_radius_one_hit_per_wave', status hypothesis (2023 data ships 10 x 1400-radius sub-areas, the wiki says the effective radius is unchanged, community kill counts imply one hit per wave), confidence LOW; candidates single_disc_one_hit_per_wave | ten_subareas_dedupe | ten_subareas_no_dedupe. Promotion: one-hit-kill Skeletons placed at radii 2.5-3.9 tiles and several angles across repeated casts.",
    "data.PUSHBACK_IMMUNITY_VINTAGE: value 'vendored_2017', status datamined 2017, known stale for BabyDragon/Balloon/InfernoDragon/BattleRam (wiki 2018 changes, confirmed by 2023 characters.csv). A project decision, not a measurement: whether live-target runs override from the 2023 data.",
    "data.SPELL_BALANCE_VINTAGE: value 'vendored_2017' (Fireball Pushback 1800, CT 40%; Arrows 1 x 115, r 4000, speed 800), live values (Fireball Pushback 1000, CT ~23%; Arrows 3 x 48 at 200 ms, r 3500, speed 1100, CT 20%) sourced from the Supercell posts dated above. A project decision on which to load; the engine must support both shapes (waves from data, default 1)."
   ]
  },
  {
   "family": "area-effect-and-stun",
   "cards": [
    {
     "card": "Zap",
     "data_chain": "cards.json[name=Zap].spell.area_effect_object (damage_source area_effect_objects.Zap.Damage) -> spells_other.Zap.AreaEffectObject=Zap (ManaCost=2, Radius=2500 = indicator, no Projectile, CustomDeployTime blank) -> area_effect_objects.Zap.{Radius=2500, LifeDuration=1, HitSpeed=<blank>, Damage=75, CrownTowerDamagePercent=-60, Buff=ZapFreeze, BuffTime=500, OnlyEnemies=TRUE, HitsGround=TRUE, HitsAir=TRUE, AffectsHidden=<blank>, IgnoreBuildings=<blank>, NoEffectToCrownTowers=<blank>, MaximumTargets=<blank>, Pushback=<blank>, BuffTimeIncreasePerLevel=<blank>} -> character_buffs.ZapFreeze.{SpeedMultiplier=-100, HitSpeedMultiplier=-100, SpawnSpeedMultiplier=-100, ImmuneToAntiMagic=TRUE}; damage level scaling -> rarities.csv Common (cards.json Zap.level_scaling.multiplier_percent_by_level); reset switches -> globals.csv LOGIC_LOAD_FIRST_HIT_RESET_TIMER_WHEN_ZAPPED=TRUE, CLONE_RESET_CHARGE=FALSE, CLONE_RESET_TARGET=FALSE. All ~2018 vintage. EXTRACTOR GAP: tools/extract_cards.py norm_aeo does not emit AffectsHidden/BuffNumber/CapBuffTimeToAreaEffectTime/BuffTimeIncreasePerLevel/LifeDurationIncreasePerLevel/HitBiggestTargets/ControlsBuff and norm_buff does not emit EnableStacking/IgnorePushBack, so card.rs cannot read them from cards.json today (Zap's blank AffectsHidden is right only by the default).",
     "numbers": [
      {
       "name": "elixir_cost",
       "value": "2",
       "units": "elixir",
       "source": "spells_other.Zap.ManaCost (2018); wiki Zap infobox Cost=2 (live)",
       "confidence": "DATA"
      },
      {
       "name": "aeo_radius",
       "value": "2500 (= 45000 subtiles; live 2.5 tiles)",
       "units": "millitiles",
       "source": "area_effect_objects.Zap.Radius (2018); spells_other.Zap.Radius=2500; clashroyale.fandom.com/wiki/Zap stats table 2.5 (live, no radius change in its history)",
       "confidence": "DATA"
      },
      {
       "name": "aeo_life_duration",
       "value": "1",
       "units": "ms",
       "source": "area_effect_objects.Zap.LifeDuration (2018); crforge cards.json lifeDuration 0.001 s",
       "confidence": "DATA"
      },
      {
       "name": "aeo_hit_speed",
       "value": "blank => one-shot (single application)",
       "units": "ms",
       "source": "area_effect_objects.Zap.HitSpeed (2018)",
       "confidence": "DATA"
      },
      {
       "name": "damage_base_level1",
       "value": "75",
       "units": "hp at rarity-relative level 1",
       "source": "area_effect_objects.Zap.Damage (2018) = cards.json Zap.damage",
       "confidence": "DATA"
      },
      {
       "name": "damage_L9 (engine default level)",
       "value": "159",
       "units": "hp",
       "source": "75*212/100, rarities.csv Common (2018)",
       "confidence": "DATA"
      },
      {
       "name": "damage_L11",
       "value": "192",
       "units": "hp",
       "source": "75*256/100 exact (2018); wiki Zap dmg_11=192 (live 2026; Zap damage unchanged since 2017-01-23)",
       "confidence": "DATA"
      },
      {
       "name": "damage_L13",
       "value": "231 (231.75 before rounding)",
       "units": "hp",
       "source": "75*309/100; rounding per calibration combat.DAMAGE_ARITHMETIC (guess)",
       "confidence": "INFERENCE"
      },
      {
       "name": "crown_tower_percent_2018",
       "value": "40 (raw -60, negative = delta from 100)",
       "units": "percent of damage",
       "source": "area_effect_objects.Zap.CrownTowerDamagePercent (2018); wiki Zap history: 40% from 2016-02-19 until 2017-12-12 (so this data's Zap row predates the 2017-12-12 change)",
       "confidence": "DATA"
      },
      {
       "name": "crown_tower_percent_live_2026",
       "value": "25",
       "units": "percent of damage",
       "source": "supercell.com/en/games/clashroyale/blog/release-notes/june-balance-changes-2026/ 'Zap: 58 -> 48 (-17%)' (2026-06-01); wiki history 50->40 (2016-02-19) ->35 (2017-12-12) ->30 (2020-08-04) ->25 (2026-06-01)",
       "confidence": "COMMUNITY"
      },
      {
       "name": "crown_tower_damage_L11_live",
       "value": "48 (58 before 2026-06-01)",
       "units": "hp",
       "source": "supercell.com 2026-06-01 (verified at source). 192*30/100 = 57.6 published as 58: truncation (engine combat.rs damage_against) would give 57",
       "confidence": "COMMUNITY"
      },
      {
       "name": "crown_tower_damage_L9_as_engine_would_compute",
       "value": "63 (159*40/100 = 63.6 truncated)",
       "units": "hp",
       "source": "combat.rs damage_against with 2018 percent",
       "confidence": "INFERENCE"
      },
      {
       "name": "stun_buff_time",
       "value": "500 (no per-level increase)",
       "units": "ms",
       "source": "area_effect_objects.Zap.BuffTime, BuffTimeIncreasePerLevel blank (2018); wiki Zap 0.5 s since 2016-08-24 (live)",
       "confidence": "DATA"
      },
      {
       "name": "stun_ticks_suppressed",
       "value": "10 at TICK_MS=50 (ceil(500/50)); ticks N+1..N+10 after the applying tick N",
       "units": "ticks",
       "source": "tick alignment chosen here (ours); unobservable at video resolution",
       "confidence": "INFERENCE"
      },
      {
       "name": "speed_multiplier",
       "value": "-100 => movement at 0%",
       "units": "raw percent (negative = delta)",
       "source": "character_buffs.ZapFreeze.SpeedMultiplier",
       "confidence": "DATA"
      },
      {
       "name": "hit_speed_multiplier",
       "value": "-100 => attack timers advance at 0% = PAUSE",
       "units": "raw percent (negative = delta)",
       "source": "character_buffs.ZapFreeze.HitSpeedMultiplier; supercell.com 2017-03-13 'All stun/freeze effects will now pause the target's attack'",
       "confidence": "DATA"
      },
      {
       "name": "spawn_speed_multiplier",
       "value": "-100 => spawner timers paused",
       "units": "raw percent",
       "source": "character_buffs.ZapFreeze.SpawnSpeedMultiplier; wiki Zap history 2016-07-04 (stops Elixir Collector generation)",
       "confidence": "DATA"
      },
      {
       "name": "cast_delay",
       "value": "0",
       "units": "ms",
       "source": "spells_other.Zap.CustomDeployTime blank (2018); wiki Zap 'deploys instantly'; wiki Rage 2022-12-12 'added a 0.5 second deploy timer to Rage' implies AEO spells otherwise have none",
       "confidence": "DATA"
      },
      {
       "name": "maximum_targets",
       "value": "unlimited",
       "units": "count",
       "source": "area_effect_objects.Zap.MaximumTargets blank",
       "confidence": "DATA"
      },
      {
       "name": "pushback",
       "value": "0 (no knockback)",
       "units": "millitiles",
       "source": "area_effect_objects.Zap.Pushback blank",
       "confidence": "DATA"
      },
      {
       "name": "affects_hidden",
       "value": "false (hidden Tesla untouched: no damage, no stun)",
       "units": "bool",
       "source": "area_effect_objects.Zap.AffectsHidden blank vs area_effect_objects.Freeze.AffectsHidden=TRUE; wiki Tesla 'immune to all damage except for the Earthquake and the Freeze'; supercell.com 2026-06-01 Tesla: 'untouchable by spells while underground'",
       "confidence": "DATA"
      },
      {
       "name": "kill_thresholds",
       "value": "Zap kills Minions two levels lower and Goblins one level lower, never equal level (e.g. L9 Zap 159 kills L7 Minion 158 and L8 Goblin 154; L8 Minion 173 and L9 Goblin 169 survive); holds at every level 3..13 under the rarities.csv table with truncation",
       "units": "hp",
       "source": "wiki Zap strategy section (live) x characters.csv Minion Hitpoints=90, Goblin Hitpoints=80 x Common table",
       "confidence": "COMMUNITY"
      }
     ],
     "behaviour": [
      "1. DEPLOY: legal anywhere in the arena including the enemy side and the river, costs 2 elixir, no deploy timer. deploy() appends an AreaEffect record {team, pos, level, def=area_effect_objects.Zap, life_ms=1, pulse_acc_ms=0, applied=false}; no entity is spawned. Grid snapping and elixir/hand checks belong to the deploy code, not this family. [DATA spells_other.Zap CustomDeployTime blank; COMMUNITY wiki Zap 'deploys instantly', 2018-04-25 'allowed the Zap to be placed on the river']",
      "2. PHASE: the record is processed in the first tick N after the cast, in a new Phase::AreaEffect placed after Projectile and before Resolve. Reason: every writer of the damage buffer must come before Resolve (lib.rs), and pulses should see post-move positions, like projectile arrivals. TICK_PHASES and its phase-order test change in the same commit. [INFERENCE: placement is ours; a one-tick shift is unobservable]",
      "3. ONE-SHOT APPLICATION: HitSpeed is blank, so the AEO applies exactly once, on its first update, whatever LifeDuration says (LifeDuration=1 ms is shorter than a tick). Then life_ms -= TICK_MS. At <=0 the record is removed at the end of the phase. It never touches a unit that enters the radius later. [DATA LifeDuration=1, HitSpeed blank; COMMUNITY crforge OneShotHandler does the same]",
      "4. VICTIM SET: every alive entity with hp>0 whose team != record.team (OnlyEnemies; no friendly fire) that passes (flying ? HitsAir : HitsGround) (both TRUE) and is not hidden (AffectsHidden blank). Troops, buildings (IgnoreBuildings blank) and both crown towers (NoEffectToCrownTowers blank) all qualify. Deploying troops (deploy_ms>0) qualify. Geometry: fixed::in_range_edge(record.pos, victim.pos, milli(2500), victim.radius), i.e. the edge-to-edge reading the engine's splash already uses (UNSETTLED, see unsettled[0]); a crown tower by the square of spells.CROWN_TOWER_SPELL_REACH instead (measured). Candidates come from SpatialHash::neighbours_within(pos, milli(2500)+max_radius), which is index-sorted. There is no target cap, so no tie-break and no mirror asymmetry. [DATA OnlyEnemies/HitsAir/HitsGround/AffectsHidden; COMMUNITY wiki Zap kills a Skeleton Army or Bats as they deploy; hidden Tesla per supercell.com 2026-06-01]",
      "5. DAMAGE: amount = CardDb::scale(75, Common multiplier[level]), which is 159 at the engine default level 9. Each victim gets Hit{target, damage_against(kind, amount, crown_pct)} pushed into the DamageBuffer. crown_pct is 40 in the 2018 data (live: 25). The damage is summed and applied in Resolve with everything else, so a unit whose windup completes in tick N's Attack phase still lands that hit, and a unit Zap kills in tick N still swings in tick N. [DATA; simultaneous outcomes may not depend on entity order]",
      "6. BUFF: every victim also gets BuffApply{target, name=ZapFreeze, class=STUN (all three raw multipliers == -100), duration_ms=500, source=Aeo} pushed into a new BuffBuffer. [DATA area_effect_objects.Zap.Buff/BuffTime, character_buffs.ZapFreeze]",
      "7. RESOLVE (tick N), in this order. (a) Apply the damage buffer as today: sums, shields, deaths, king_hit. Zap damage on an inactive King Tower starts its activation. (b) Decrement existing stun timers by TICK_MS. This moves from phase_status to here so a D-ms buff suppresses exactly ceil(D/TICK_MS) ticks. (c) Apply the BuffBuffer to targets still alive after (a): stun_ms = max(stun_ms, 500). Merging is max plus OR, so buffer order is irrelevant. [DATA; COMMUNITY wiki Freeze 'may damage and activate it' (King); alignment is ours]",
      "8. ON STUN APPLICATION (each application, including a refresh). Set target_locked=false and retarget_on_resume=true (new per-entity bool, hashed and serialized). Reset charge state (Prince; implemented in state.rs::charge_pass, gated by tests/charge.rs) unless the source is the Clone buff and CLONE_RESET_CHARGE=FALSE. Reset the variable-damage ramp to stage 1 (Inferno; not in the thin slice). If load_first_hit && LOGIC_LOAD_FIRST_HIT_RESET_TIMER_WHEN_ZAPPED, set attack_phase=Idle and attack_ms=0 (Sparky; not in the thin slice). Leave attack_phase and attack_ms UNCHANGED for every other attacker. [COMMUNITY supercell.com 2017-03-13, verified at source: 'All stun/freeze effects will now pause the target's attack, causing them to retarget when they resume. Exceptions are Sparky, Inferno Tower and Inferno Dragon, which will still be reset'; same post 'Clone: No longer resets charging troops (Princes, Sparky, etc.)'; wiki Zap 'reset charging attacks from ... Prince'; DATA globals.csv LOGIC_LOAD_FIRST_HIT_RESET_TIMER_WHEN_ZAPPED=TRUE, CLONE_RESET_CHARGE=FALSE, CLONE_RESET_TARGET=FALSE]",
      "9. WHILE STUNNED (ticks N+1..N+10). Target phase: the stun's landing clears the target and releases the lock (status.STUN_CLEARS_TARGET = client_cleared, measured on client 15.535.29); no scan while held. Path phase: no movement proposal (already). Move phase: the unit can still be displaced by collision separation and knockback. Attack phase: attack_step(can_act=false) freezes phase and ms (already). REQUIRED FIX: phase_attack currently sets target_locked = preserve && phase==Windup every tick, which re-locks a stunned unit in Windup; gate it on stun_ms==0. Also frozen: spawner timers and the Tesla hide/emerge timers (a stunned Tesla cannot retreat). Still running: deploy_ms, building lifetime_ms, king_wake_ms, the buff timers themselves. A projectile the unit has already launched keeps flying. [DATA HitSpeed/Speed/SpawnSpeed -100; COMMUNITY wiki Freeze 'Troops frozen by the Freeze will remain frozen even if they are pushed out of the Freeze's radius'; wiki Freeze 'will not affect the King Tower's activation animation'; wiki Rage 'building lifetime still drops with its normal rate, even with the Rage'; wiki Tesla 'The retreat can also be stopped with a Freeze spell'; INFERENCE for deploy_ms and in-flight projectiles]",
      "10. RESUME (tick N+11, the first tick whose Target phase sees stun_ms==0 and retarget_on_resume). The Target phase bypasses target lock and keep-target hysteresis (LOGIC_RANGE_EXTENSION_TO_KEEP_TARGET) and runs target::scan() as for a fresh unit, then clears the flag. If scan returns the previous target, windup or cooldown resumes exactly where it paused, and phase_attack re-locks it. If it returns a different target, the paused windup carries onto it (status.RESUME_RETARGET_WINDUP = carry, measured on client 15.535.29; cancel, to Idle, is the old arm). Buildings and crown towers retarget too. [COMMUNITY supercell.com 2017-03-13 'causing them to retarget when they resume'; wiki Zap 'Zapping units or the opposing Crown Tower can possibly change their targets' (X-Bow example); wiki Rage 'unlike the Zap, it does not force the Crown Tower to retarget']",
      "11. OVERLAP: a second stun (another Zap, an E-Wiz hit, Freeze, Lightning) sets stun_ms = max(remaining, new) and never shortens a longer one. Step 8's resets fire again at each application. Retarget happens once, when the last stun ends. [INFERENCE: ZapFreeze has no EnableStacking and different buff names coexist in Supercell's buff model; COMMUNITY supercell.com 2017-03-13 'Electro Wizard: No longer permanently stuns' is consistent with pause+refresh; crforge AppliedEffect.refresh uses max]",
      "12. TESLA: a revealed Tesla is damaged and stunned and cannot retreat while stunned. A hidden Tesla is excluded from the victim set. Hide is implemented: entity.rs::HideState and state.rs::hide_pass read hides_when_not_attacking / hide_time_ms / up_time_ms, target.rs and combat.rs::resolve honour it, and tests/hide.rs gates it. [DATA buildings.csv Tesla HidesWhenNotAttacking=true, HideTimeMs=800, UpTimeMs=800; COMMUNITY wiki Tesla; supercell.com 2026-06-01]",
      "13. PRINCE: stun resets his charge; after the stun he must build the charge up again from zero. Charge is ABSENT, so step 8 is a call-out the charge implementation must make. Knockback (Log) resets charge through the same call-out (other family). [COMMUNITY wiki Zap, wiki Freeze, supercell.com 2017-03-13 Clone note; wiki Sparky 'The knockback from The Log will also reset the charge of the Prince']",
      "14. DETERMINISM: the AreaEffect list and retarget_on_resume go into state_hash and the save/load snapshot, alongside projectiles. Zap has no randomness and no capped target selection. Its mirror is a Red Zap at (x, 32-y). , under the 180-degree seat symmetry."
     ],
     "targets_affected": "Enemies only (OnlyEnemies): own troops, buildings and towers are never damaged or stunned (no friendly fire). Ground and air troops (HitsGround, HitsAir). Enemy buildings (IgnoreBuildings blank): Cannon always; Tesla only while not hidden (AffectsHidden blank; hidden Tesla is immune to both damage and stun). Enemy crown towers, Princess and King (NoEffectToCrownTowers blank): damaged at CrownTowerDamagePercent (2018 raw -60 = 40%; live 25% since 2026-06-01, L11 48) and stunned (their attack pauses and they retarget on resume). Damage on an inactive King activates it; the stun does not delay that activation. Deploying troops are hit. No knockback, no target cap, no level-scaled stun duration.",
     "unsettled": [
      "AEO radius geometry. (E) victim hit if dist(centre, victim) <= 2500 milli + victim CollisionRadius, the engine's splash convention and ADD_CHARACTER_RANGE_TO_RADIUS by analogy. (C) centre-to-centre, dist <= 2500 milli. Nothing public states which, and the Zap / AesDragon hidden-stats wiki pages are silent. Deciding observation: cast Zap with its centre 2.6-3.0 tiles from a stationary enemy Cannon (radius 0.6); under E the Cannon loses HP and flashes stunned, under C it is untouched. A tile-snapped building keeps the geometry exact, and the 0.5-0.6 tile margin is well above video error.",
      "Windup on retarget-to-a-different-target at resume. SETTLED: P2, carry (status.RESUME_RETARGET_WINDUP, measured on client 15.535.29). (P1) the paused windup is cancelled to Idle and a full LoadTime is paid on the new target; this is the engine's existing 'changed && Windup -> Idle' rule. (P2) the paused progress carries over. Deciding observation: a Musketeer locked mid-windup on a far Giant, a Skeleton dropped nearer, then Zap. Measure new-target hit time minus stun end: ~remaining windup under P2, a full LoadTime under P1.",
      "Pause vs reset in the LIVE 2026 game. The 2017-03-13 official note (pause) predates the 2018 data, and the live wiki Freeze and E-Wiz pages still say pause; no later reversal was found. crforge implements reset, the pre-2017 model. Deciding observation (O1): a Knight or Giant attacking a building, Zapped at a known offset t after a hit lands. Pause gives a hit-to-hit interval of HitSpeed+500 ms for every t. Reset gives t+500+LoadTime: shorter than pause when zapped early in cooldown, longer when zapped late in windup. That is categorical at 60 fps.",
      "Tick alignment of buff expiry. (a) ceil(D/TICK_MS) ticks from the tick after application: 10 for Zap, the recommended default. (b) one tick fewer: 9, what the shipped phase_status decrement would produce. There is no deciding observation at video resolution (50 ms vs 16.7 ms, under client interpolation), so this is ours and pinned by a test. NOTE: the calibration.json TICK_MS promotion rule suggests timing 'Zap stun start to end' to settle the tick rate. A 500 ms buff lasts 500 ms under every tick hypothesis, so that measurement is far weaker than it reads.",
      "Crown-tower damage rounding for Zap (feeds combat.DAMAGE_ARITHMETIC). Truncation is contradicted: the official L11 value was 58 at 30% and 192*30/100 = 57.6. Round-half-up and ceil both fit (Fireball's official 207 at 30% of wiki 688 = 206.4 fits ceil only, but 688 itself may be a rounded display). Assumes the published number equals battle damage. Deciding observation (O4): Zap a crown tower at card level 8 (144.75*25% = 36.19: trunc/round 36, ceil 37) and at level 12 (210.75*25% = 52.69: trunc 52, round/ceil 53), reading the tower's displayed HP number before and after.",
      "Which crown-tower percent the engine should ship: 40 (2018 data) or 25 (live since 2026-06-01). The target is the live game, but the stat is card data, not a calibration constant. That is a data-vintage decision, not a mechanic. The same applies to live levels 14-16, which rarities.csv lacks (Common ships an unused_tail 339).",
      "Does a stun applied during deploy pause deploy_ms? (D1) no, both timers run and only stun left after deploy blocks action. This is the recommended default, by analogy with the Freeze wiki's King activation note. (D2) yes. Deciding observation (O6): Zap a Knight about 0.8 s into its 1 s deploy. D1: starts walking about 1.3 s after placement. D2: about 1.5 s.",
      "Is a Tesla hittable during its retreat and emerge animations, or only when fully up? The wiki says 'the retreat is not instant, enabling quick troops ... to get a hit off before it goes underground', which suggests the transition counts as not hidden. It belongs with the hide mechanic itself, but is_hidden() must be defined before Zap can be gated on it.",
      "Semantics of BuffNumber, blank on Zap and 1 on Rage/Freeze/Poison/Heal. Candidates: max stacks per target from one AEO; applications per target; unused. Irrelevant to a one-shot Zap. It matters for pulsing AEOs, where a Poison with EnableStacking re-applied every 250 ms would otherwise hold 4 stacks and deal 4x its DPS.",
      "One-shot AEOs with LifeDuration longer than a tick (Freeze 4000 ms, not in thin slice). (O1) apply once at the first update. (O2) apply to each new entity entering during life. Weak support for O2: wiki Freeze says death-spawned units 'remain frozen until the duration of the Freeze ends', though buff inheritance by death spawns explains that equally. Zap is insensitive: LifeDuration=1 makes O1 and O2 identical. Deciding observation (O7): walk or deploy an enemy troop into an active Freeze 1 s after cast.",
      "ImmuneToAntiMagic=TRUE on ZapFreeze/Freeze/Poison/Stun. No 2018 row sets NegatesBuffs or applies an anti-magic buff, so there is no consumer and no observable. Record it, do not implement it.",
      "EXTRA_BUFF_RADIUS=2000 (globals.csv). No 2018 character uses AreaBuff, so the meaning is unknown: extra radius for AreaBuff auras, or a buff-linger margin around AEOs. No consumer for Zap.",
      "Whether stunned units still push and get pushed in collision separation. The recommended default is yes: the Freeze wiki's 'pushed out of the Freeze's radius' implies displacement while frozen. Who does the pushing is not stated. Deciding observation: a stunned Knight in the path of a friendly Giant walking into it."
     ],
     "engine_test_ideas": [
      "zap_damages_and_stuns_for_exactly_ceil_buff_ticks. An enemy Knight (L9) stands still with no target. A Blue Zap (L9) is cast before tick N. Assert hp drops by exactly 159 in tick N's Resolve, the Knight proposes zero movement and does not advance attack_ms in ticks N+1..N+10, and moves again at N+11. PLANT stun_decrement_at_status_start (keep the shipped Status-phase decrement) must turn it red (only 9 ticks).",
      "stun_pauses_windup_and_cooldown_not_resets. A Red Knight attacks a Blue building and is zapped (a) 300 ms into its windup and (b) 100 ms into its cooldown. Assert the next hit tick equals the unzapped baseline hit tick + 10 ticks in both cases. PLANT stun_resets_attack (Idle/0 on application, crforge's model) must go red: later than baseline+10 in (a), earlier in (b). Needing both cases stops a plant that only moves the time one way from passing.",
      "stunned_unit_is_not_relocked_each_tick. A unit zapped mid-windup must show target_locked==false on every stunned tick. PLANT relock_while_stunned (restore the ungated target_locked = preserve && Windup in phase_attack) must go red.",
      "zap_forces_retarget_on_resume. A Red Musketeer is locked on a far Blue Giant, and a Blue Skeleton is placed nearer, inside keep-target hysteresis so it is NOT chosen without Zap. Control run with no Zap: the target stays the Giant for 40 ticks, so the test is not vacuous. Zap run: the target becomes the Skeleton on tick N+11, not before. PLANT no_retarget_after_stun (resume keeps the old target) must go red. PLANT retarget_at_application (scan during the stun) must also go red.",
      "zap_hits_enemies_only. A Blue Zap over an overlapping Blue Knight and Red Knight: the Blue Knight's hp and stun_ms stay unchanged, the Red Knight takes 159 and a 500 ms stun. PLANT aeo_ignores_team must go red.",
      "zap_hits_air_ground_buildings_and_crown_towers. Red Minions (air), a Red Knight, a Red Cannon and a Red Princess tower all inside the edge-radius: each takes damage; the tower takes damage_against(PrincessTower, 159, 40) = 63 and gets stun_ms=500. PLANT aeo_skips_air, PLANT aeo_crown_pct_ignored (full 159 on the tower) and PLANT aeo_skips_buildings must each go red.",
      "zap_radius_is_edge_to_edge_under_default_reading. A Red Knight (radius 500 milli) with its centre exactly milli(2500)+milli(500) subtiles from the Zap centre is hit; one subtile farther is not; with centre-to-centre reading selected, the first position is not hit. PLANT aeo_centre_radius must turn the default-reading assertion red. Distances squared must be i64: (45000+9000)^2 is about 2.9e9 > i32::MAX.",
      "zap_is_one_shot. A Red unit placed so it enters the radius one tick after the cast tick takes no damage and no stun. PLANT aeo_applies_every_tick (treat blank HitSpeed as 'pulse each tick while alive', with life_ms extended) must go red. PLANT aeo_expires_before_applying (remove the record before processing when LifeDuration < TICK_MS) must go red, because the unit inside the radius at cast is then never hit.",
      "zap_kill_thresholds_match_wiki_across_levels. For every Zap level L in 3..13 (Common table), Minions at L-2 die and at L-1 survive; Goblins at L-1 die and at L survive. Assert these from hp after one Zap. PLANT pow11_level (existing) must go red, since 1.1^(L-1) drifts at the thresholds. So must a plant that rounds the level product up instead of truncating (e.g. Zap L3 91 vs Minion L2 99 still survives, but other pairs flip; assert the table explicitly).",
      "refresh_never_shortens_a_longer_stun. Use a test-only buff from inline card JSON (like card.rs tests): a 2000 ms STUN, then a Zap 500 ms later. Assert stun_ms after the Zap's Resolve equals 1500 minus one tick, not 500. PLANT stun_replace (stun_ms = new) must go red. Also assert that swapping the two buff entries in the same tick's BuffBuffer gives an identical state_hash (order independence).",
      "same_tick_swing_lands_before_stun. A Red Knight whose windup completes in tick N is zapped in tick N. Assert its hit is in tick N's DamageBuffer and resolved, and the Zap still applies. PLANT aeo_before_attack_inline (process AEOs before Attack and set stun immediately) must go red.",
      "stun_does_not_pause_lifetime_king_activation_or_deploy. Zap a Red Cannon: lifetime_ms still falls by TICK_MS each stunned tick. Zap the Red King Tower: king_wake_ms counts up through the stun and activation happens at KING_ACTIVATE_TIME_MS after the hit. Zap a deploying Red Knight: deploy_ms keeps decreasing. PLANT stun_pauses_all_timers must go red.",
      "zap_state_is_hashed_and_snapshotted. Cast Zap, snapshot before the tick, load, and step both: equal state_hash. Then step a unit into resume. PLANT hash_skips_aeo (drop the AEO list from state_hash) must make the determinism gate red on a pair of runs whose AEO lists differ. PLANT save_drops_aeo must make save_load red. PLANT hash_skips_retarget_flag must go red.",
      "zap_mirror_symmetry. A Blue army plus Blue Zap at (x, y) against the mirrored Red army plus Red Zap at (x, 32-y) cast the same tick. Assert mirrored hp, stun_ms and targets on both sides every tick. PLANT buff_merge_in_slot_order (apply buffs inline in slot order with a first-writer-wins guard) must go red.",
      "SPEC GATE, lands with Tesla hide: zap_does_not_touch_hidden_tesla. An underground Red Tesla inside the radius keeps full hp and stun_ms==0, and a revealed Tesla is damaged and stunned and does not retreat during the stun. PLANT aeo_affects_hidden must go red. Re-anchor as a failing test with a sentence, landed in the same commit as hide.",
      "SPEC GATE, lands with charge: zap_resets_prince_charge. A Red Prince with charge accumulated is zapped: charge progress is 0 after the stun and the next hit deals Damage, not DamageSpecial, unless he re-accumulates. PLANT stun_keeps_charge must go red. A Clone-sourced -100 buff with CLONE_RESET_CHARGE=FALSE must NOT reset it (PLANT clone_resets_charge must go red)."
     ]
    }
   ],
   "shared_mechanics": [
    "BUFF MULTIPLIER ENCODING [DATA cross-row + COMMUNITY]. character_buffs Speed/HitSpeed/SpawnSpeedMultiplier: pct(raw) = 100 if blank/0, raw if raw>0 (absolute percent), 100+raw if raw<0 (delta), clamped at 0. Evidence: Rage +135 = +35% (wiki Rage 2016-11-01..2025-10-06); IceWizardSlowDown -35 = -35% (2018); ChargeSpeedMultiplier 200 = 2x; newer crforge data keeps the pattern (Rage 130, IceWizard -30, ArcherQueenRapid 280). No shipped positive value below 100 exists, so the reading of e.g. 50 is untested. Implement as integer percent. Movement uses speed*pct/100 with the existing move_frac carry. Attack and spawn timers advance by TICK_MS*pct/100 per tick with a remainder carry kept in state (not needed while only -100 buffs exist in the thin slice).",
    "HIT-SPEED MULTIPLIER IS A TIMER RATE [INFERENCE, strong]. Attack windup and cooldown timers advance at pct% of real time; the reading 'the period is scaled' is rejected. The -100 limit of the rate reading is exactly the official 2017 behaviour ('pause the target's attack'). The period reading has no sensible -100 limit. Rage +35% therefore makes a 1.5 s attack take 1.111 s, not 0.975 s. The wiki Rage line 'an X-Bow can shoot 4 projectiles every second' weakly favours the rate reading.",
    "STUN CLASS [DATA + COMMUNITY]. A buff is STUN-class iff its speed, hit-speed and spawn-speed raw multipliers are all -100: ZapFreeze, Freeze, Stun and Clone in 2018 data. Freeze and ZapFreeze behave identically (official 2017 note says 'stun/freeze effects'). Sources: AEO Buff (Zap, Freeze, E-Wiz spawn Zap), projectile TargetBuff (LighningSpell ZapFreeze 500, IceSpiritsProjectile Freeze 1500 applied to every entity its AoE damages), character BuffOnDamage (ElectroWizard ZapFreeze 500 per damaging hit). The existing single stun_ms timer is sufficient for the whole class: stun_ms = max over every active STUN-class buff.",
    "STUN EFFECTS [COMMUNITY supercell.com 2017-03-13, verified at source]. On application: the target lock is cleared; retarget_on_resume is set; charge is reset (skipped for Clone while CLONE_RESET_CHARGE=FALSE); the variable-damage ramp is reset; LoadFirstHit attack timers are reset when LOGIC_LOAD_FIRST_HIT_RESET_TIMER_WHEN_ZAPPED=TRUE. Ordinary windup and cooldown are PAUSED, not reset. While stunned: no movement proposal, no target change, frozen attack/spawn/hide timers. Still running: deploy, building lifetime, king activation and buff timers. At resume the unit does a fresh scan() that ignores lock and hysteresis. Clone also skips the target reset while CLONE_RESET_TARGET=FALSE.",
    "ONE INTERRUPT ROUTINE, SEVERAL CAUSES. Implement interrupt(i, cause) once. cause=StunApplied does the step-8 resets above. cause=Knockback (other family) resets the attack windup outright: COMMUNITY wiki Freeze 'It is not like The Log's effect, where the attack animation of affected units will be completely restarted'. It also resets charge (wiki Sparky: Log knockback resets Prince charge). Clone-sourced stuns take the CLONE_RESET_* opt-outs. Charge (Prince) is implemented (state.rs::charge_pass, tests/charge.rs) and the routine must reset it; the Inferno ramp (combat.VARIABLE_DAMAGE) restarts on a stun too, in the same routine.",
    "BUFF BUFFER AND MERGE [INFERENCE; simultaneous outcomes may not depend on entity order]. No writer sets a timer inline. AEO pulses, projectile arrivals and on-damage handlers push BuffApply{target, name, class, duration_ms, source_kind, source_id}. Resolve applies them after damage, skipping targets that died. Merge for a non-stacking name: duration = max(existing, all new). Reset flags are OR-ed. Commutative, so slot and writer order cannot matter. EnableStacking=TRUE (Poison, Heal, Tornado) means one instance per source object (per AEO id) coexisting with others, refreshed by that same source. Re-application every pulse cannot add stacks, or Poison's 250 ms pulses with a 1000 ms buff would triple its damage.",
    "BUFF TICK ALIGNMENT (ours). A buff of D ms applied during tick N is active for ticks N+1..N+ceil(D/TICK_MS). Implement by decrementing buff timers in Resolve immediately BEFORE applying that tick's new buffs, not at Status start (which yields one tick fewer), and update lib.rs Phase::Status docs. Tick N itself can never be suppressed because Attack runs before Resolve.",
    "AREA EFFECT OBJECT LIFECYCLE [DATA columns; INFERENCE timing]. A record {team, pos, level, def, life_ms, pulse_acc_ms, applied, source_id} lives in state beside projectiles, is hashed and snapshotted, and runs in a new Phase::AreaEffect after Projectile and before Resolve. One-shot (HitSpeed blank/0): apply on the first update regardless of LifeDuration (Zap 1 ms). Pulsing: pulse_acc += TICK_MS; while pulse_acc >= HitSpeed {pulse; pulse_acc -= HitSpeed}. The first pulse is therefore at t = HitSpeed, not t = 0: Lightning 1500/460 gives the known 3 bolts; t=0 would give 4. life_ms -= TICK_MS after processing, remove at <=0. Level-scaled life: LifeDuration + LifeDurationIncreasePerLevel*(level steps) (Rage, Freeze; semantics of the step base unverified).",
    "AEO VICTIM FILTER [DATA]. alive && hp>0. Team: OnlyEnemies => enemy of record.team; OnlyOwnTroops => same team. flying ? HitsAir : HitsGround. IgnoreBuildings => skip non-troops (whether this covers crown towers is unverified). NoEffectToCrownTowers => skip crown towers. AffectsHidden false => skip hidden entities (Tesla underground), for both damage and buff. MaximumTargets caps the set, and HitBiggestTargets picks by hp; any cap needs the engine's mirror-invariant tie-break key, never EntityId. Geometry uses in_range_edge with the victim's radius by default (UNSETTLED); a crown tower is reached by the square of spells.CROWN_TOWER_SPELL_REACH instead (measured). Candidates come from the index-sorted spatial-hash query.",
    "AEO DAMAGE AND BUFF AMOUNTS [DATA]. damage = CardDb::scale(Damage, rarity multiplier at the card level; for character-spawned AEOs, SpawnAreaObjectLevelIndex). Per victim use damage_against(kind, damage, crown_pct) with crown_pct = 100 + raw when raw < 0 (cards.json convention; Zap -60 = 40% matches the wiki's pre-2017-12-12 Zap value). buff_time = BuffTime (+ BuffTimeIncreasePerLevel). With CapBuffTimeToAreaEffectTime, buff_time = min(buff_time, the area's life left after the application's tick plus one HitSpeed of the area) (status.AREA_BUFF_SOURCE_BINDING; measured on the Earthquake, client 15.535.29, and the Rage, client 16.402). So the Rage's last application, with 250 ms left after its tick, lasts 550 ms and the buff outlives the area by 250 ms, where wiki Rage says it 'will always expire once the spell's duration ends'. ControlsBuff (Tornado) removes its buff from every entity when the record dies. Damage-over-time buffs deal DamagePerSecond*HitFrequency/1000 per HitFrequency with their own crown percent (not in thin slice; first-tick timing unverified).",
    "MULTI-BUFF COMPOSITION (not needed for the thin slice) [COMMUNITY, conflicting]. Candidates for combining non-stun speed buffs: (M1) multiplicative product (crforge's choice, no source); (M2) additive sum of deltas (fits wiki 'Rage can be used to neutralize the Ice Wizard's slow effect'); (M3) strongest slow wins among slows, combined with the strongest boost (fits wiki Ice Wizard 'does not stack with ... Giant Snowball and Ice Golem' and 'overwritten by the Earthquake and Ram Rider ... more severe'). STUN-class buffs yield zero action under all three, so Zap is insensitive. This is implemented: status.rs carries a per-entity buff list with each buff's raws, and movement.BUFF_SPEED_COMPOSITION = strongest_up_and_down is the arm it runs (M3).",
    "ENGINE CONSUMER GAPS STILL OPEN. (1) phase_attack re-sets target_locked for stunned units in Windup every tick. (2) tools/extract_cards.py still drops BuffNumber, Life/BuffTimeIncreasePerLevel, HitBiggestTargets, ControlsBuff (AEO) and IgnorePushBack (buff), so card.rs cannot honour them from cards.json. AffectsHidden, CapBuffTimeToAreaEffectTime and EnableStacking are emitted now; the buff list of status.rs replaced the dead slow timer, and Tesla hide is implemented (state.rs::hide_pass, tests/hide.rs).",
    "PROJECTILE SPEED UNITS (overlaps the projectile family) [COMMUNITY, weak]. The wiki user blog 'AesDragon/Hidden card stats: Projectile travel speeds' states projectile Speed uses 'the same kind of measurement as troops do' (tiles per minute) and tabulates values equal to projectiles.csv Speed (Fireball 600, Arrows 1100, Log 200, Lightning 500). No source was found for a separate /60-vs-/50 divisor for projectiles; this is an assertion, not a measurement. The engine already uses one SPEED_TO_SUBTILES_PER_TICK for both, flagged UNVERIFIED in card.rs.",
    "VINTAGE CAUTION ON crforge [COMMUNITY]. crforge resets attack state on stun/freeze (resetAttackState: windup, cooldown, accumulatedLoadTime, targetLocked). That is the pre-2017-03-13 model Supercell replaced with pause+retarget. Its buffs.json and cards.json are newer than the 2018 data (Zap crown -70 = 30%, Rage 130, IceWizard -30, Poison speed -15) but predate 2026-06-01, and their provenance is undocumented."
   ],
   "calibration_keys_proposed": [
    "status.STUN_ATTACK_TIMER_MODEL = \"pause\" | status: sourced-community (Supercell release note 2017-03-13, verified at source; live wiki Freeze/E-Wiz pages agree) | confidence: MEDIUM-HIGH for 2026 (no later reversal found, nine years old) | alternatives: \"reset\" (pre-2017, crforge) | promotion: deciding observation O1, hit-to-hit interval after a Zap at two phase offsets is HitSpeed+500 (pause) vs offset+500+LoadTime (reset).",
    "status.STUN_RETARGET_ON_RESUME = true | status: sourced-community (Supercell 2017-03-13 'causing them to retarget when they resume'; wiki Zap X-Bow example) | confidence: MEDIUM-HIGH | promotion: deciding observation O2, a locked Musketeer or Hog with a nearer valid target placed after the lock switches target 0.5 s after a Zap.",
    "status.RESUME_RETARGET_WINDUP = \"carry\" | status: measured (client 15.535.29: the paused windup carries onto the resume rescan's new target; shipped since the 2026-09-28 round 9 arms flip) | confidence: HIGH | alternatives: \"cancel\" | promotion: deciding observation O2 variant with the switch mid-windup; new-target hit time minus stun end is a full LoadTime (cancel) or the remaining windup (carry).",
    "status.BUFF_EXPIRY_TICK_ALIGNMENT = \"ceil_from_next_tick\" | status: ours (not a property of the game we can observe) | confidence: n/a | alternatives: \"one_tick_short\" (the shipped Status-phase decrement) | promotion: none by video (below interpolated-frame resolution); pinned by engine test zap_damages_and_stuns_for_exactly_ceil_buff_ticks.",
    "status.BUFF_MULTIPLIER_ENCODING = \"positive_absolute_negative_delta\" | status: datamined-pattern (Rage 135 = +35%, IceWizard -35 = -35%, 2018 wiki percentages) | confidence: HIGH for single buffs | promotion: already consistent with 2018 wiki; re-confirm with one Rage traversal time if a rage source ever enters the slice.",
    "status.HIT_SPEED_MULTIPLIER_MODEL = \"timer_rate\" | status: inference (the -100 limit reproduces the official pause) | confidence: MEDIUM | alternatives: \"period_scale\" | promotion: time a Raged or Ice-Wizard-slowed Knight's hit interval; 1100/1.35=815 ms vs 1100*0.65=715 ms under +35%.",
    "status.MULTI_BUFF_COMPOSITION = \"unset\" | status: guess, not needed for the thin slice | confidence: LOW | alternatives: \"multiplicative\" (crforge), \"additive_deltas\" (wiki 'Rage neutralizes Ice Wizard slow'), \"strongest_slow_plus_strongest_boost\" (wiki Ice Wizard 'overwritten by ... more severe') | promotion: Ice Wizard slowing a Knight inside Rage; walk speed 1.0x (additive), 0.8775x (multiplicative).",
    "status.SAME_BUFF_REAPPLY = \"refresh_max\" | status: inference (no EnableStacking on ZapFreeze; crforge refresh) | confidence: MEDIUM | alternatives: \"replace\" | promotion: Zap a unit 1 s into an Ice Spirit freeze (1.1 s live); under replace it moves at 1.5 s after the freeze began, under max at 1.1 s.",
    "status.STUN_PAUSES_DEPLOY_TIMER = false | status: inference (analogy to wiki Freeze 'will not affect the King Tower's activation animation') | confidence: LOW | promotion: deciding observation O6, Zap a Knight 0.8 s into deploy; walk starts about 1.3 s (false) or about 1.5 s (true) after placement.",
    "status.STUN_PAUSES_BUILDING_LIFETIME = false | status: sourced-community by analogy (wiki Rage 'building lifetime still drops with its normal rate, even with the Rage') | confidence: MEDIUM | promotion: Freeze a Cannon (4 s) and time its total lifetime; ~30 s vs ~34 s at 2018 LifeTime.",
    "status.STUN_PAUSES_KING_ACTIVATION = false | status: sourced-community (wiki Freeze) | confidence: MEDIUM | promotion: Freeze an enemy King at the moment it is first damaged; it attacks immediately when the freeze ends (false) or after a further activation delay (true).",
    "aoe.RADIUS_GEOMETRY = \"edge_to_edge\" | status: guess (engine splash convention, by analogy with ADD_CHARACTER_RANGE_TO_RADIUS) | confidence: LOW | alternatives: \"centre\" | promotion: deciding observation O3, Zap centre 2.6-3.0 tiles from a stationary Cannon; hit => edge_to_edge.",
    "aoe.FIRST_PULSE_AT = \"hit_speed\" | status: inference (Lightning 1500/460 yields the known 3 strikes only if the first pulse is at 460 ms) | confidence: MEDIUM | alternatives: \"zero\" | promotion: time Lightning's first strike after cast on video (~0.46 s vs immediate).",
    "aoe.ONE_SHOT_APPLICATION = \"first_update_only\" | status: inference (Zap LifeDuration=1; crforge OneShotHandler) | confidence: HIGH for Zap, LOW for Freeze | alternatives: \"each_new_entity_during_life\" | promotion: deciding observation O7, walk a unit into an active Freeze 1 s after cast.",
    "aoe.PHASE_POSITION = \"after_projectile_before_resolve\" | status: ours | confidence: n/a | alternatives: \"spawn_phase_pre_move\" | promotion: none (one tick of movement, below video resolution); record the choice in architecture.md and pin it with the TICK_PHASES test.",
    "targeting.CLONE_RESET_TARGET = false, targeting.CLONE_RESET_CHARGE = false, targeting.LOGIC_LOAD_FIRST_HIT_RESET_TIMER_WHEN_ZAPPED = true, targeting.LOGIC_LOAD_FIRST_HIT_RESET_TIMER_AFTER_ATTACK = true | status: datamined (globals.csv ~2018) | confidence: HIGH for 2018; CLONE_RESET_CHARGE=false corroborated by the Supercell 2017-03-13 Clone note | promotion: vendor newer globals data; not needed for the thin slice except LOAD_FIRST_HIT (no thin-slice consumer).",
    "combat.DAMAGE_ARITHMETIC evidence to attach (existing key, do not re-create) | status stays guess | new evidence: Supercell 2026-06-01 published Zap L11 crown damage 58 at 30% of an exact 192 (=75*256/100); truncation gives 57, so the current truncating damage_against is contradicted if the published value equals battle damage | promotion: deciding observation O4, Zap at card level 8 and 12 on a crown tower; tower HP number deltas 36/37 and 52/53 separate trunc, round and ceil."
   ]
  },
  {
   "family": "rolling-and-spawning (The Log, Goblin Barrel)",
   "cards": [
    {
     "card": "Log",
     "data_chain": "RAW: spells_other.Log.{ManaCost=2, Projectile=LogProjectile, SpellAsDeploy=true, CanPlaceOnBuildings=TRUE, CanDeployOnEnemySide=(blank), OnlyEnemies=true} -> projectiles.LogProjectile.{Speed=360, MinDistance=3000, Gravity=30, Radius=1950, RadiusY=600, AoeToGround, OnlyEnemies, no Damage, SpawnProjectile=LogProjectileRolling} -> projectiles.LogProjectileRolling.{Damage=240, Speed=200, ProjectileRadius=1950, ProjectileRadiusY=600, ProjectileRange=11100, Pushback=1000, PushbackAll=true, CrownTowerDamagePercent=-60, MinDistance=2500, AoeToGround=true, AoeToAir=(blank), OnlyEnemies=true}; victim flags characters.<unit>.IgnorePushback (cards.json units[].ignore_pushback / cards[].ignore_pushback) and FlyingHeight (flying); scaling rarities.Legendary (RelativeLevel 8, multipliers 100,110,121,133,146). DERIVED: cards.json cards[name=Log].projectile(=LogProjectile).spawn_projectile(=LogProjectileRolling).{damage, speed, projectile_radius_milli, projectile_radius_y_milli, projectile_range_milli, pushback_milli, pushback_all, crown_tower_damage_percent=40, crown_tower_damage_percent_raw=-60}; cards[name=Log].spell.{spell_as_deploy=true, can_place_on_buildings=true, can_deploy_on_enemy_side=false}; cards[name=Log].level_scaling. TRAPS: cards[name=Log].area_damage_radius_milli=1950 is lifted from the AIRBORNE LogProjectile.Radius (which has no damage) -- do not splash the tap with it; cards[name=Log].spell.pushback_milli is null because the pushback lives on the rolling projectile.",
     "numbers": [
      {
       "name": "elixir cost",
       "value": "2",
       "units": "elixir",
       "source": "spells_other.Log.ManaCost (2018 data); fandom The_Log live 2",
       "confidence": "DATA"
      },
      {
       "name": "rolling damage at Legendary local level 1 (= engine unified level 9)",
       "value": "240",
       "units": "hitpoints",
       "source": "projectiles.LogProjectileRolling.Damage (data 2017-10..2017-12); live fandom L9 220, L11 266 after 2025 nerfs",
       "confidence": "DATA"
      },
      {
       "name": "rolling damage at unified L11 / L13 (engine truncating scale)",
       "value": "290 / 350",
       "units": "hitpoints",
       "source": "240 x rarities.Legendary 121% / 146%",
       "confidence": "DATA"
      },
      {
       "name": "crown tower damage percent",
       "value": "40 (raw -60, read as 100+raw)",
       "units": "percent of damage, King and Princess towers",
       "source": "LogProjectileRolling.CrownTowerDamagePercent; fandom history says 40% until 2017-12-12, which corroborates the delta-from-100 reading; live 13% since 2026-06-01 (raw would be -87)",
       "confidence": "DATA"
      },
      {
       "name": "crown tower damage at unified L9",
       "value": "96",
       "units": "hitpoints",
       "source": "240*40/100 truncating (combat.rs damage_against)",
       "confidence": "DATA"
      },
      {
       "name": "rolling speed (raw)",
       "value": "200",
       "units": "raw Speed column; subtiles/tick = 200 x projectile speed multiplier = 3000 at k=15 (/60 @20TPS) or 3600 at k=18 (/50 @20TPS)",
       "source": "LogProjectileRolling.Speed == fandom history 'projectile speed to 200' (2016-10-20, unchanged since)",
       "confidence": "DATA"
      },
      {
       "name": "airborne (cast) speed (raw)",
       "value": "360",
       "units": "raw Speed; 5400 subtiles/tick at k=15, 6480 at k=18",
       "source": "LogProjectile.Speed == fandom 'casting speed to 360' (2016-10-20); AesDragon blog: airborne phase has no hitbox",
       "confidence": "DATA"
      },
      {
       "name": "roll distance",
       "value": "11100 millitiles = 199800 subtiles (2018); live 10100 = 181800",
       "units": "distance along team-forward axis from the tap point to the roll end",
       "source": "LogProjectileRolling.ProjectileRange == fandom 'rolling distance 11.1' (2017-06-12..2020-08-04); live 10.1 (fandom stats)",
       "confidence": "DATA"
      },
      {
       "name": "half-width (x half-extent)",
       "value": "1950 millitiles = 35100 subtiles",
       "units": "distance perpendicular to roll",
       "source": "LogProjectileRolling.ProjectileRadius; fandom Width 3.9 = 2x1.95 (live, unchanged)",
       "confidence": "DATA"
      },
      {
       "name": "half-depth (along roll)",
       "value": "600 millitiles = 10800 subtiles",
       "units": "distance along roll",
       "source": "LogProjectileRolling.ProjectileRadiusY; semantics (rectangle half-depth) not stated by any source",
       "confidence": "INFERENCE"
      },
      {
       "name": "knockback distance",
       "value": "1000 millitiles = 18000 subtiles (2018); live 700 = 12600",
       "units": "total displacement of a struck ground troop",
       "source": "LogProjectileRolling.Pushback == fandom 'pushback range to 1 tile' (2017-01-23..2023-02-07); live 0.7 tiles since 2023-02-07",
       "confidence": "DATA"
      },
      {
       "name": "PushbackAll",
       "value": "true",
       "units": "flag: knockback ignores the victim's IgnorePushback",
       "source": "LogProjectileRolling.PushbackAll (only projectile in projectiles.csv with it); fandom 2016-09-19 'allowed The Log to push back all ground troops'; fandom strategy 'unlike most other cards with a pushback effect ... can affect all ground troops'",
       "confidence": "COMMUNITY"
      },
      {
       "name": "airborne MinDistance",
       "value": "3000 millitiles = 54000 subtiles",
       "units": "distance; meaning unsettled",
       "source": "LogProjectile.MinDistance",
       "confidence": "DATA"
      },
      {
       "name": "rolling MinDistance",
       "value": "2500 millitiles = 45000 subtiles",
       "units": "distance; meaning unsettled",
       "source": "LogProjectileRolling.MinDistance",
       "confidence": "DATA"
      },
      {
       "name": "airborne Gravity",
       "value": "30",
       "units": "unknown (arc parameter)",
       "source": "LogProjectile.Gravity",
       "confidence": "DATA"
      },
      {
       "name": "cast delay if the airborne phase covers MinDistance",
       "value": "10 ticks = 0.500 s (k=15) or 8.33 ticks = 0.417 s (k=18)",
       "units": "ticks / seconds from deploy to rolling hitbox at the tap",
       "source": "54000 / (360*k)",
       "confidence": "INFERENCE"
      },
      {
       "name": "roll duration",
       "value": "2018 range: 66.6 ticks = 3.33 s (k=15) or 55.5 ticks = 2.775 s (k=18); live range: 3.03 s or 2.525 s",
       "units": "ticks at TICK_MS=50 / seconds",
       "source": "ProjectileRange / (200*k)",
       "confidence": "INFERENCE"
      }
     ],
     "behaviour": [
      "1. PLACEMENT [DATA spells_other.Log.SpellAsDeploy=true with CanDeployOnEnemySide blank, versus Tornado SpellAsDeploy=true AND CanDeployOnEnemySide=true and Miner (troop) CanDeployOnEnemySide=true -- cross-card: SpellAsDeploy switches to troop placement, CanDeployOnEnemySide lifts it; COMMUNITY fandom The_Log 'It can only be deployed on the player's own side', trivia '1 of 3 spells which can only be placed in the player's own territory']: territory = the TROOP rule (Territory::OwnHalfAndPocket, same pocket depth key as troops). Not Territory::Anywhere.",
      "2. PLACEMENT, river exception [COMMUNITY fandom 2018-04-25 'allowed The Log to be placed on the river if a Crown Tower has been already destroyed']: when the enemy princess of a lane is destroyed, the river (water) half-cells of that lane are legal for the Log; otherwise water -> DeployError::Water as for troops. Which river cells exactly is unsettled (see unsettled).",
      "3. PLACEMENT, buildings [DATA CanPlaceOnBuildings=TRUE; COMMUNITY 2017-03-13 bug 'not able to be deployed on top of buildings', fixed 2017-06-12]: no Occupied/footprint check. Whether the tilemap NO_DEPLOY bits (king block, river-corner tiles) block the Log is unsettled.",
      "4. DEPLOY: elixir 2 deducted and the card cycles exactly as for any card (existing state.rs deploy_slot). No deploy time (spells_other.Log.CustomDeployTime blank) [DATA].",
      "5. AIRBORNE PHASE [DATA LogProjectile has no Damage; COMMUNITY AesDragon 'still in the air without a hitbox ... 360']: a non-entity projectile that deals no damage, pushes nothing, cannot be hit. Default launch model (calibration key spells.SPELL_AS_DEPLOY_LAUNCH_MODEL=airborne_from_behind_lands_on_tap): starts MinDistance (54000 subtiles) behind the tap along the team-forward axis, moves at 360*k subtiles/tick with the last step clamped, and on arrival spawns the rolling projectile AT the tap [INFERENCE; the Barbarian Barrel river lore and the Goblin Barrel counter lore both need the roll to cover tap .. tap+ProjectileRange].",
      "6. ROLLING DIRECTION [INFERENCE; the Log cannot be aimed]: straight along the team-forward axis, +y for Blue and -y for Red (engine mirror (x, 32-y)). Never homing (Homing blank). Not clamped to the arena bounds (Monk trivia: a deflected Log leaves the arena) [INFERENCE].",
      "7. ROLLING MOTION [DATA ProjectileRange, Speed]: each tick in Phase::Projectile advance by 200*k subtiles, clamping the final step so total travel from the roll start is exactly ProjectileRange (199800 in 2018 data, 181800 live); then despawn (DeathEffect log_die is visual only).",
      "8. HIT TEST each tick [INFERENCE for the shape, see unsettled]: a swept axis-aligned rectangle x in [x0-35100, x0+35100], forward extent [prev-10800, cur+10800] (team frame), against each victim's collision circle edge-to-edge (the same edge convention as combat::splash). Swept so the result does not depend on TICK_MS. This geometry must live in fixed.rs (the only place distance arithmetic is defined), integer, i64 squares.",
      "9. VICTIM FILTER [DATA OnlyEnemies=true, AoeToGround=true, AoeToAir blank; COMMUNITY]: enemy team only (no friendly fire); ground entities only (flying never hit); alive; not already hit by THIS log (a per-projectile hit set, so each entity is hit at most once -- fandom Barbarian_Barrel 'The Log which will only hit the tank once'); deploying units ARE valid victims (fandom 'Any swarms placed on top of The Log will be immediately hit', and the Goblin Barrel counter lore); hidden Tesla is NOT a victim (fandom Tesla: immune to everything but Earthquake/Freeze while underground, live); units mid-jump are NOT victims (fandom: Hog Rider, Mega Knight, Ram Rider, Royal Hogs 'cannot be struck by it while they use their jump abilities'; the engine has no jump yet).",
      "10. DAMAGE [DATA]: level-scaled Damage (card level through rarities.Legendary, truncating) pushed into the DamageBuffer. Crown towers (King and Princess) take damage*crown_pct/100 through combat::damage_against. Other buildings (Cannon, Tesla) take full damage. Applied in Phase::Resolve with every other hit, so simultaneous logs from both sides stay order-independent. A King Tower hit with amount > 0 wakes the king through the existing resolve() king_hit path.",
      "11. KNOCKBACK [DATA Pushback, PushbackAll; COMMUNITY]: every TROOP victim hit this tick (not buildings, not towers), INCLUDING IgnorePushback=true units (Giant, Prince, Baby Dragon is air so never hit), gets a knockback of Pushback subtiles. Record it in a push buffer during Phase::Projectile and apply it after Resolve to victims that survived, summing buffered displacements so the order cannot matter. Default direction (calibration key knockback.DIRECTION_ROLLING=radial_from_projectile_centre): the unit vector from the log's current centre to the victim, in integer arithmetic. This is INFERENCE, chosen because three fandom strategy lines need a sideways component: lane switching by 'deploying The Log on the side of the troop opposite', pushing troops 'closer to the inside of the Arena', and pushing a corner Miner 'towards the King's Tower'. A pure forward push cannot produce any of the three.",
      "12. KNOCKBACK SIDE EFFECTS [COMMUNITY fandom The_Log]: 'If a troop is locked into a target, the pushback caused by The Log will reset their attack animation' -- set attack_phase out of Windup and restart the load timer. Keep or drop the target lock: unsettled. Once Charge exists, knockback also cancels the Prince's charge [COMMUNITY 2016-09-19]. A unit on the river edge can be pushed 'over the river back to the opponent's side' [COMMUNITY], so knockback is NOT blocked by water the way walking is; how it resolves is unsettled.",
      "13. END: the rolling projectile despawns after ProjectileRange. The rule 'at most once per victim' holds for its whole life, and nothing re-hits."
     ],
     "targets_affected": "Enemy ground troops, including units still deploying, take full damage and knockback. Knockback also moves IgnorePushback units, because PushbackAll=true. Enemy non-tower buildings take full damage and no knockback. Enemy crown towers (King and Princess) take damage x CrownTowerDamagePercent (40% in 2018 data, 13% live) and no knockback; a hit on the King Tower activates it. Air units are never hit. Friendly units are never hit (OnlyEnemies=true, no friendly fire). A hidden Tesla (live) and units mid-jump are not hit. Each entity is hit at most once per Log.",
     "unsettled": [
      "LAUNCH MODEL / CAST DELAY (LogProjectile.MinDistance=3000, Gravity=30). Candidates: (A) the airborne log starts MinDistance behind the tap and lands on the tap, then rolls tap..tap+ProjectileRange, giving a 0.5 s (k=15) or 0.417 s (k=18) delay before any hit; (B) airborne from the tap forward MinDistance, with the roll ending at tap+ProjectileRange, so nothing between the tap and tap+3 tiles is ever hit; (C) the roll starts at the tap instantly. Evidence: Barbarian Barrel 'placed at most 2 tiles from the river, the Barbarian will spawn at the opposing side' (live range 4.5) rules out 'airborne forward, then a full ProjectileRange roll' but does not separate A from B; 'swarms placed on top of The Log will be immediately hit' and 'Log placed on the centre tile behind the Tower always hits all 3 Goblins' favour A or C over B. crforge implements B with a units bug its own comment admits. DECIDING OBSERVATION: 60 fps recording of a Log dropped with a Skeleton exactly on the tap tile and a second Skeleton 1.5 tiles ahead. Note (i) whether the tap-tile skeleton is hit, and (ii) the frames from release to the first damage number.",
      "ROLLING MinDistance=2500. Candidates: (a) ignored whenever ProjectileRange is set; (b) a no-hit zone for the first 2.5 tiles of the roll (crforge). This contradicts the swarm-on-top lore unless the roll starts well ahead of the tap, and would make the live Barbarian Barrel (range 4.5) miss everything in its first 2.5 tiles; (c) a minimum travel distance for non-rolling use. DECIDING OBSERVATION: the same recording as above. Under (b) a Skeleton 1 tile ahead of the tap survives.",
      "HIT SHAPE. Candidates: rectangle vs victim circle edge-to-edge (a Knight of radius 0.5 is hit out to 2.45 tiles off-axis), or the victim centre inside the rectangle (hit only to 1.95 tiles). 2023-03-27 'improved The Log's collision detection' changed something unspecified, so the live shape may differ from 2018. DECIDING OBSERVATION: Skeletons at 2.2 tiles lateral offset from the log centre line on both sides; hit under edge-to-edge, untouched under centre-in-rectangle.",
      "KNOCKBACK DIRECTION. Candidates: radial from the log's centre point (default; matches the lateral-push lore), travel direction only (crforge; contradicts the lore), or perpendicular from the nearest point of the log's centre segment (pure forward for |dx|<=1.95, so no lane switching). DECIDING OBSERVATION: track a Knight struck 1.5 tiles off-axis; its displacement vector is diagonal under radial and pure-y under travel-direction.",
      "KNOCKBACK PROFILE AND MASS. No column or global gives a duration or a mass law (globals.csv has no PUSHBACK key; Mass is not a formula). Candidates: instantaneous teleport vs linear slide over N ms; distance independent of Mass vs mass-scaled. DECIDING OBSERVATION: Log a Skeleton (mass 1) and a Giant (mass 18) side by side; compare total displacement and the number of frames the slide takes.",
      "KNOCKBACK INTO WATER, BUILDINGS, UNITS. The river-crossing lore says water does not stop the push. Candidates: allow the overlap and then resolve to the nearest bank (possibly the far bank), or clamp at the bank; building footprints could block the push or resolve it afterwards. DECIDING OBSERVATION: Log a Knight standing on the bridge exit and one at a river bank next to water.",
      "ATTACK RESET DETAILS. 'Reset their attack animation' is certain (COMMUNITY). Unknown: whether the target lock survives, and whether LOGIC_LOAD_FIRST_HIT_RESET_TIMER_AFTER_ATTACK-style load-first-hit timers reset too. DECIDING OBSERVATION: a Knight hitting a tower is logged; measure the frames from the push to his next hit, compared with load_time and hit_speed.",
      "SPEED DIVISOR FOR PROJECTILES. The engine and card.rs:57 apply the troop multiplier (UNVERIFIED). The claimed split 'troops /50, projectiles /60' traced to no source: crforge and legacy/ use /60 for both; the only frame measurement found (fandom Darknighture 2023) gives x1.2 (/50) for both troops (Giant) and projectiles (Musketeer). DECIDING OBSERVATION: live Log roll time, first contact to despawn, is 3.03 s at /60 and 2.525 s at /50.",
      "NO_DEPLOY CELLS AND RIVER CELLS FOR THE LOG. Can it be placed on own king-tower no-deploy block and the river-corner tiles (0,14)/(17,14)? After a lane's princess falls, which river half-cells are allowed (lane half only, or the whole river)? DECIDING OBSERVATION: drag the Log over each in a friendly battle and note the placement indicator colour.",
      "VINTAGE (a data-vintage decision, not a mechanic). 2018 data gives range 11.1, pushback 1.0, crown 40%, dmg 240@L9. Live values are range 10.1, pushback 0.7, crown 13%, dmg 220@L9. The data window from this family's evidence is 2017-10-06..2017-12-12: the roster includes Flying Machine (released 2017-10-06), while the Goblin damage and the Log crown % predate the 2017-12-12 changes. That is earlier than cards.json provenance's 'first half of 2018'."
     ],
     "engine_test_ideas": [
      "log_territory_is_troop_territory: Blue Log at tile (9,10) -> Ok; (9,20) -> Err(OutOfTerritory); on own princess footprint (3.5,6.5) -> Ok (no Occupied); GoblinBarrel at (9,20) -> Ok. The Python mask (action.py cell_grid) and mock_engine._check must give the same verdicts. PLANT log_territory_anywhere (map Log to Territory::Anywhere) -> (9,20) accepted -> red.",
      "log_river_allowed_only_in_fallen_lane: Red left princess destroyed -> Blue Log on a left-lane river half-cell (3.0,16.0) Ok, right-lane river (14.0,16.0) Err(Water); with no tower down both are Err. PLANT log_river_never (keep the troop water check) -> the fallen-lane river cell is rejected -> red.",
      "log_ground_only: Red Minion and Red Knight both at (9,12), Blue Log at (9,10), level 9: Knight loses exactly 240, Minion HP unchanged and not displaced. PLANT rolling_hits_air -> Minion damaged -> red.",
      "log_pierces_and_hits_once: three Red Skeletons at (9,11),(9,13),(9,15) all die; a Red Giant at (9,12) (overlaps the rectangle for many ticks) loses exactly 240 in total. PLANT rolling_rehit_every_tick (clear the hit set each tick) -> Giant loses more than 240 -> red.",
      "log_width_edge: under ROLLING_HIT_SHAPE=rect_vs_circle_edge a Red Knight centred at x = 9 tiles + 35100 + 9000 - 1 subtiles is hit, one at +1 subtile beyond is not; parameterised on the calibration key. PLANT rolling_centre_in_rect -> the inner Knight is not hit -> red.",
      "log_range_end: a Red Knight whose circle overlaps the final swept rectangle by 1 subtile (roll start + ProjectileRange + 10800 + 9000 - 1) is hit, 2 subtiles further is not; ProjectileRange read from cards.json, never typed. PLANT roll_range_from_airborne_start (end = tap + range - MinDistance) -> the near-end Knight is missed -> red.",
      "log_crown_tower_reduction: Blue Log placed at (3.5,14.5) with the pocket-free territory reaches the Red left princess tower. Tower HP drops by exactly 240*crown_pct/100 = 96 once at level 9. PLANT rolling_ignores_crown_pct -> drop 240 -> red; PLANT rolling_rehit_every_tick also reds it.",
      "log_pushback_all_moves_heavy_units: Red Giant (IgnorePushback=true) and Red Knight on the log axis both end displaced by Pushback subtiles (after the knockback profile completes, no other units nearby), while a Fireball-style pushback without PushbackAll leaves the Giant in place. PLANT pushback_respects_ignore -> Giant unmoved -> red.",
      "log_knockback_resets_windup: Red Knight in Windup against a Blue Knight is logged; the same tick after application attack_phase != Windup and his next damage lands no earlier than load_time_ms after the push. PLANT knockback_keeps_windup -> the hit lands on the original schedule -> red.",
      "log_knockback_direction_lateral: Red Knight at 1.5 tiles lateral offset gets a displacement with |dx| > 0 away from the log axis under DIRECTION_ROLLING=radial_from_projectile_centre. PLANT knockback_travel_direction_only -> dx == 0 -> red.",
      "log_mirror_symmetry (tests/mirror.rs): Blue Log at (x,y) with Red targets at mirrored positions vs Red Log at (x,32-y) with Blue targets; HP and positions mirror every tick, including simultaneous crossing logs. PLANT rolling_forward_plus_y_for_both -> the Red log rolls into its own king side -> red.",
      "log_timing_reads_calibration: first damage tick on a victim at the tap equals the airborne ticks plus the roll ticks computed from calibration time.SPEED_TO_SUBTILES_PER_TICK (or the proposed projectile key); run at k=15 and k=18 and expect the tick index to move. PLANT inline_projectile_speed_15 -> the k=18 run keeps the k=15 tick -> red.",
      "log_hits_deploying_units: Red Skeleton Army deployed on the log path the tick the roll passes; skeletons with deploy_ms > 0 die. PLANT rolling_skips_deploying -> they survive -> red.",
      "log_no_friendly_fire: Blue Knight in the Blue Log's path keeps full HP and position. PLANT rolling_hits_friends -> red.",
      "log_data_trap_guard: assert the Log never produces a tap-centred circular splash of radius cards.json area_damage_radius_milli; a Red Skeleton 1.9 tiles BEHIND the tap is untouched. PLANT splash_log_with_area_damage_radius -> it dies -> red."
     ]
    },
    {
     "card": "GoblinBarrel",
     "data_chain": "RAW: spells_other.GoblinBarrel.{ManaCost=3, Rarity=Epic, Projectile=GoblinBarrelSpell, OnlyEnemies=true, no SpellAsDeploy, no CanDeployOnEnemySide} -> projectiles.GoblinBarrelSpell.{Speed=400, Gravity=40, Radius=1500, AoeToGround=true, OnlyEnemies=true, Homing=(blank), no Damage, SpawnCharacter=Goblin, SpawnCharacterCount=3, SpawnCharacterDeployTime=1200, SpawnCharacterLevelIndex=5, SpawnConstPriority=TRUE} -> characters.Goblin.{Rarity=Common, Hitpoints=80, Damage=50, HitSpeed=1100, LoadTime=900, Speed=120, Range=500, SightRange=5500, CollisionRadius=500, Mass=2, DeployTime=1000 (overridden), DeployDelay=400, AttacksGround=true}; rarities.Epic.RelativeLevel=5 (== SpawnCharacterLevelIndex), rarities.Common multipliers; arena.json king_blocks[team].center = (9,3) Blue / (9,29) Red (launch origin); arena.json water bits (placement). DERIVED: cards.json cards[name=GoblinBarrel].projectile(=GoblinBarrelSpell).{speed=400, radius_milli=1500, spawn_character, spawn_character_count, spawn_character_deploy_time_ms=1200, spawn_character_level_index=5} == cards[name=GoblinBarrel].spell.spawn.{character, count, deploy_time_ms, level_index, source}; cards.json units[name=Goblin].{hitpoints, damage, hit_speed_ms, load_time_ms, speed, range_milli, sight_range_milli, collision_radius_milli, mass, deploy_time_ms, raw.DeployDelay}. GAP: card.rs loads only cards+towers, so units[name=Goblin] has no engine consumer yet.",
     "numbers": [
      {
       "name": "elixir cost",
       "value": "3",
       "units": "elixir",
       "source": "spells_other.GoblinBarrel.ManaCost (2018; live 3, fandom)",
       "confidence": "DATA"
      },
      {
       "name": "barrel flight speed (raw)",
       "value": "400",
       "units": "raw Speed; 6000 subtiles/tick at k=15 (/60 @20TPS) or 7200 at k=18 (/50)",
       "source": "projectiles.GoblinBarrelSpell.Speed; AesDragon 2020 table 400",
       "confidence": "DATA"
      },
      {
       "name": "flight time, Blue king centre (9,3) to Red princess centre (3.5,25.5)",
       "value": "23.162 tiles: 69.49 ticks = 3.474 s (k=15) or 57.91 ticks = 2.895 s (k=18); to Red king (9,29): 3.900 s or 3.250 s",
       "units": "ticks at TICK_MS=50 / seconds",
       "source": "straight-line distance / (400*k); origin per fandom King's_Tower 'Launched spells ... seem to come from the King's Tower'",
       "confidence": "INFERENCE"
      },
      {
       "name": "Gravity",
       "value": "40",
       "units": "unknown arc parameter",
       "source": "GoblinBarrelSpell.Gravity",
       "confidence": "DATA"
      },
      {
       "name": "Radius",
       "value": "1500 millitiles = 27000 subtiles",
       "units": "distance; no damage attached (impact damage removed 2016-07-04 per fandom); fandom lists 'Radius 1.5'",
       "source": "GoblinBarrelSpell.Radius",
       "confidence": "DATA"
      },
      {
       "name": "spawned unit count",
       "value": "3",
       "units": "Goblins",
       "source": "GoblinBarrelSpell.SpawnCharacterCount; fandom x3",
       "confidence": "DATA"
      },
      {
       "name": "spawned Goblin deploy time",
       "value": "1200 (2018); live 1100",
       "units": "ms, overriding characters.Goblin.DeployTime=1000",
       "source": "GoblinBarrelSpell.SpawnCharacterDeployTime == fandom 'Goblin spawn time 1.2 seconds' (2016-07-04..2019-05-06); 1.1 s since 2019-05-06",
       "confidence": "DATA"
      },
      {
       "name": "spawn level index",
       "value": "5 (== rarities.Epic.RelativeLevel)",
       "units": "level offset: Goblin unified level = barrel unified level",
       "source": "GoblinBarrelSpell.SpawnCharacterLevelIndex; cross-card every spawner's index equals its rarity RelativeLevel (Witch 5, BattleRam 2, DarkWitch 8, huts/Tombstone 2); fandom 2016-01-11 fix 'Level 8 Goblin Barrel would be able to spawn Level 14 Goblins' (off-by-one of this offset)",
       "confidence": "INFERENCE"
      },
      {
       "name": "Goblin HP / damage at Common local level 1",
       "value": "80 / 50",
       "units": "hitpoints",
       "source": "characters.Goblin (pre-2017-12-12: live -6% damage, -1% HP on 2018-07-02)",
       "confidence": "DATA"
      },
      {
       "name": "Goblin HP / damage from a barrel at engine unified L9 (Epic local 4)",
       "value": "169 / 106",
       "units": "hitpoints",
       "source": "80x212/100, 50x212/100 truncating (Common local 9)",
       "confidence": "DATA"
      },
      {
       "name": "Goblin HP / damage from a barrel at Epic local 1 (unified 6)",
       "value": "128 / 80",
       "units": "hitpoints",
       "source": "x160%; live fandom L6 125/75 after the 2017-12 and 2018-07 nerfs",
       "confidence": "DATA"
      },
      {
       "name": "Goblin first hit after acquiring a target",
       "value": "200 (2018: HitSpeed 1100 - LoadTime 900); live 400",
       "units": "ms",
       "source": "characters.Goblin; fandom 'First Hit Speed 0.4 sec', 0.2 before 2022-10-26",
       "confidence": "DATA"
      },
      {
       "name": "Goblin speed / range / sight / radius / mass",
       "value": "120 / 500 / 5500 / 500 / 2",
       "units": "raw Speed / millitiles / millitiles / millitiles / mass",
       "source": "characters.Goblin",
       "confidence": "DATA"
      },
      {
       "name": "Goblin DeployDelay",
       "value": "400",
       "units": "ms; semantics unsettled",
       "source": "characters.Goblin.DeployDelay",
       "confidence": "DATA"
      }
     ],
     "behaviour": [
      "1. PLACEMENT [DATA plain spells_other row, no SpellAsDeploy; COMMUNITY fandom Goblin_Barrel trivia 'unable to be placed directly on the river', 'the only spell that cannot be placed directly on the river']: anywhere strictly inside the arena EXCEPT water half-cells (the troop closed-cell water rule is the default, INFERENCE). Bridges are legal (not water). Enemy buildings, crown towers and the king no-deploy block are legal: the canonical play is tower-centre and 'barrel the King' [COMMUNITY]. No Occupied check, no territory check. This is a NEW territory rule (Anywhere minus water) in arena.rs deploy_zone, and the Python mask's SPELL = all-ones grid must gain the same water exclusion for this card.",
      "2. DEPLOY: elixir 3 deducted, card cycles (existing). No deploy time for the barrel itself.",
      "3. LAUNCH [COMMUNITY fandom King's_Tower 'Launched spells ... seem to come from the King's Tower'; Goblin_Barrel strategy 'as soon as the Goblin Barrel is launched from the opposing King's Tower']: in the next Spawn phase create a non-entity projectile at the caster's King Tower centre (arena.json king_blocks[team].center; exact origin point unsettled) aimed at the tap point. It is not homing [DATA Homing blank]. It moves in a straight line at 400*k subtiles/tick, and the last step is clamped to land exactly on the tap [INFERENCE: Gravity treated as visual].",
      "4. IN FLIGHT [INFERENCE; COMMUNITY: every listed counter acts on the Goblins after landing; the 2017-10-11 Touchdown bug 'did not clear flying barrels' shows barrels are not battle entities]: the barrel is not an entity. It cannot be targeted, damaged, stunned, pushed or blocked. It ignores water, buildings and units. Its landing point is fixed at launch.",
      "5. ARRIVAL IMPACT [DATA no Damage; COMMUNITY 2016-07-04 'removed its impact damage']: no hits. If the spell pipeline builds a circular AoE from Radius, it must push zero hits; combat::resolve already ignores amount <= 0, so a barrel landing on the King Tower does not wake it.",
      "6. SPAWN [DATA SpawnCharacter/Count/DeployTime/LevelIndex]: on the arrival tick, release 3 Goblins (team = caster). They exist on that same frame, the one the barrel vanishes on, and are inert on it: created at the end of the tick, after the deploy countdown and the move pass, so their countdown starts on the next tick and they deploy for SpawnCharacterDeployTime / TICK_MS frames, one more than an ordinary deploy (measured on client 15.535.29: goblins first on 147, deploying 147..168, first step 170; calibration spawner.RELEASE_TIMING). The earlier arm, next_spawn_phase, queued them for the next Spawn phase. Level = the barrel's unified level (the Common table then gives HP/damage). deploy_ms = SpawnCharacterDeployTime (1200 in 2018, live 1100), NOT Goblin.DeployTime. Positions come from a formation around the landing point, which is unsettled. The engine default is state.rs formation for count 3: offsets (-0.5,+0.5), (+0.5,+0.5), (0,-0.5) tiles, y-mirrored for Red. Formation points on water fall back to passable ground (existing fallback).",
      "7. SPAWN ON A BUILDING [COMMUNITY fandom: placed at the centre of a Crown Tower the Goblins 'split apart on all sides of the Tower'; 'Crown Tower will first target the Goblin horizontally closest to the King's Tower']: spawn positions inside a building footprint are pushed out by the normal building-collision resolution (collide.rs), ending up around the tower.",
      "8. DEPLOYING GOBLINS [COMMUNITY: the Log 'will be able to defeat them instantly'; Mega Knight / Electro Wizard spawn damage and Giant Snowball timing all hit the Goblins during their deploy]: while deploy_ms > 0 they do not target, move or attack (target::decide already returns None), but they ARE valid victims of damage and knockback, and they occupy collision space.",
      "9. AFTER DEPLOY: ordinary Goblin behaviour: ground melee, AttacksGround only, sight 5500, range 500, speed 120, normal targeting (nearest target incl. crown towers per target.rs), full damage to towers (CrownTowerDamagePercent default 100)."
     ],
     "targets_affected": "The barrel itself: nothing. No damage, no pushback, no status, no king activation, no friendly fire; its AoeToGround/OnlyEnemies flags have nothing to act on. It cannot be targeted or intercepted in flight. The 3 spawned Goblins are ground melee units that attack enemy ground troops, buildings and crown towers at full damage (no crown reduction), and never attack air. They are ground victims: hittable (also while deploying) by ground- and air-targeting attacks and spells, and knockback-able (IgnorePushback false).",
     "unsettled": [
      "SPAWN FORMATION / ROLE OF Radius=1500. Candidates: (a) the engine's generic 3-unit grid (tight, about 1 tile span); (b) Goblins on a ring of radius Radius (1.5 tiles) around the landing point; (c) a count-dependent ring as in crforge (which dropped the 1.5-tile spread for a <1-tile triangle without evidence). The Barbarian Barrel lore (a barrel 1 tile to the side of the tower lets a 1.3-half-width roll miss one Goblin, but never the 1.95-half-width Log) is consistent with (a) and (b) once tower push-out is included, so it does not decide. DECIDING OBSERVATION: 60 fps recording of a barrel on open ground (e.g. the attacker's own side, far from buildings). Read the three Goblin positions on the first frame they are visible, then repeat 3 times at the same tile.",
      "SpawnConstPriority=TRUE (only on GoblinBarrelSpell and buildings.SkeletonContainer, both containers that release units). Candidates: a deterministic (non-random) spawn layout; a constant entity update/push priority for the released units; or something about spawn ordering. DECIDING OBSERVATION: the repeated-placement recording above. An identical layout every time favours the deterministic-layout reading; nothing else in reach decides the priority reading.",
      "DeployDelay=400 on Goblin. Does it stagger the 3 barrel Goblins (e.g. appearing 0/400/800 ms apart), as it may for multi-unit troop cards? The Log wiki says multi-unit cards other than Skeletons are 'slightly staggered', yet Skeleton ships DeployDelay 400 too, so the column is not simply the stagger. DECIDING OBSERVATION: frame of first visibility and of first movement for each Goblin after impact.",
      "LAUNCH ORIGIN AND PATH LENGTH. King Tower centre vs another point of the king block, and straight-line horizontal distance vs arc length with Gravity=40. DECIDING OBSERVATION: flight times, release frame to landing frame, for two targets at different distances, e.g. the enemy princess centre (23.16 tiles from the king centre) and the bridge (14.12 tiles). Fit t = (d + offset)/v; a non-zero offset or a v that disagrees with the troop divisor decides.",
      "SPEED DIVISOR (shared with the Log). King to enemy princess is 3.474 s at /60 and 2.895 s at /50, a difference of 0.58 s, trivially visible.",
      "WATER RULE GRANULARITY. Does a tap whose half-cells merely touch the river bank count as 'on the river' (troop closed-cell rule) or is the test on the tap point only? DECIDING OBSERVATION: the placement indicator when dragging the barrel along the bank.",
      "LEVEL INDEX SEMANTICS. 'Offset added to the barrel's rarity-local level' and 'same unified level as the barrel' give identical results for every shipped row, because each index equals its rarity's RelativeLevel. They only diverge for a hypothetical row whose index differs from its RelativeLevel. No observation needed for the thin slice; flag it if a future data set breaks the equality.",
      "VINTAGE (a data-vintage decision): 2018 deploy 1.2 s and first hit 0.2 s, versus live deploy 1.1 s and first hit 0.4 s, plus Goblin damage -6% (2017-12-12) and HP -1% (2018-07-02)."
     ],
     "engine_test_ideas": [
      "barrel_placement_anywhere_but_water: Blue barrel at (9,25) Ok, at the Red king centre (9,29) Ok, on the Red princess footprint (3.5,25.5) Ok, on water (9,16) Err(Water), on the left bridge (3.5,16) Ok. The Python mask and mock_engine._check must agree. PLANT barrel_anywhere_incl_water (keep Territory::Anywhere) -> the water tap is accepted -> red.",
      "barrel_flight_ticks_from_king: deploy a Blue barrel at (3.5,25.5) at tick 0. No Blue Goblin exists before the arrival tick = ceil(dist(king centre, tap)/(400*k)), i.e. 70 at k=15 and 58 at k=18 with k read from calibration. Exactly 3 exist on the arrival tick itself. PLANT release_deferred -> Goblins a tick late -> red; PLANT barrel_origin_at_tap -> red.",
      "barrel_goblin_deploy_time_override: every spawned Goblin has deploy_ms == cards.json GoblinBarrel.spell.spawn.deploy_time_ms (1200) on its first tick, and does not move or target until it reaches 0. PLANT spawn_uses_character_deploy_time (1000) -> red.",
      "barrel_goblin_level_matches_card: barrel level 9 -> Goblin max HP 169, damage 106; barrel level 6 -> 128 / 80. PLANT spawn_level_local_one (Goblin at Common local 1) -> HP 80 -> red.",
      "barrel_not_interceptable: a Red Musketeer placed under the flight path with no other Blue units acquires no target during the flight, and a Red Log rolled across the barrel's in-flight position does not cancel it (3 Goblins still spawn). PLANT barrel_is_entity (spawn the barrel as a targetable air entity) -> Musketeer fires -> red.",
      "barrel_impact_is_harmless: barrel landing on the Red king (9,29): King HP unchanged on the arrival tick and the king_wake timer not started until a Goblin's hit resolves. PLANT barrel_impact_uses_goblin_damage (splash 50 at Radius 1500) -> king woken on arrival -> red.",
      "barrel_on_tower_goblins_pushed_out: barrel on the Red princess centre. After the first collision passes no Goblin centre lies inside the tower footprint, and Goblins exist on both sides of the tower's x centre. PLANT no_building_block (existing plant name) -> Goblins inside the footprint -> red.",
      "barrel_goblins_hittable_while_deploying: Red Log (level 9) timed to roll over the landing point while the Goblins have deploy_ms > 0 -> all 3 die (240 >= 169). PLANT rolling_skips_deploying (shared with the Log) -> they survive -> red.",
      "barrel_near_water_never_spawns_on_water: barrel tapped on the last legal half-cell next to the river -> every Goblin position is passable ground on every tick of its deploy. PLANT formation_ignores_water -> a Goblin centre in water -> red.",
      "barrel_mirror_symmetry (tests/mirror.rs): Blue barrel at (x,y) and Red barrel at (x,32-y) on the same tick give mirrored Goblin positions, HP and targets every tick. PLANT formation_not_y_mirrored_for_red -> red."
     ]
    }
   ],
   "shared_mechanics": [
    "SPEED UNITS FOR PROJECTILES. Engine combat.rs:198 and state.rs apply time.SPEED_TO_SUBTILES_PER_TICK to projectile Speed; card.rs:57 already marks that UNVERIFIED. Evidence found 2026-09-13: fandom AesDragon (2020) asserts, without measuring, that projectile Speed is tiles/minute, the SAME unit as troop Speed. fandom Darknighture (2023) frame test: Musketeer projectile (1000) measured 1200 tiles/min and Giant (45) measured 54 tiles/min, so x1.2 (/50) for BOTH. crforge and legacy/ use /60 for both. The 'troops /50, projectiles /60' split traced to NO source. Implement ONE multiplier for both, and give projectiles their own proposed key that defaults to the troop value, so a Log roll-time or barrel flight-time recording can confirm or split it without a code change.",
    "SPELL LAUNCH (non-SpellAsDeploy projectile spells: Goblin Barrel here, Fireball/Arrows in other families). Created in the Spawn phase after deploy at the caster's King Tower centre [COMMUNITY King's_Tower page], not homing, straight line to the tap, final step clamped, not an entity (untargetable, unblockable). On arrival it resolves its payload in Phase::Projectile into the damage buffer and push buffer and onto the deferred spawn queue. Arrival tick = ceil(distance/(Speed*k)).",
    "TERRITORY BY SPELL FLAGS (a new function of card flags, replacing 'kind == Spell -> Anywhere' in state.rs::check_position). SpellAsDeploy=true and CanDeployOnEnemySide=false gives the troop rule (OwnHalfAndPocket) [DATA cross-card Log vs Tornado vs Miner; COMMUNITY 'only on own side']. CanDeployOnEnemySide=true lifts the side restriction (Tornado; not in slice). CanPlaceOnBuildings=true skips Occupied. A projectile spell whose projectile has SpawnCharacter gets Anywhere minus water [COMMUNITY Goblin Barrel river trivia]. SpellAsDeploy cards may also use river cells of a lane whose enemy princess is down [COMMUNITY 2018-04-25, Log and Barbarian Barrel]. CONSUMERS that must change in the same commit: crates/royalesim/src/arena.rs (Territory enum + deploy_zone), state.rs::check_position, py.rs DeployError codes if a new reason appears, RoyaleGym/royalegym/protocol.py Placement (ROLLING doc, and a SPELL-not-on-water variant), RoyaleGym/royalegym/action.py cell_grid (SPELL is all-ones today; ROLLING excludes all water), RoyaleGym/royalegym/mock_engine.py _check and the loader at line 365 (maps every SpellAsDeploy card to ROLLING, which is wrong for Tornado because CanDeployOnEnemySide=true). tests/test_rust_engine.py already asserts mask/engine equality of the pocket rule; extend it to the new rules.",
    "ROLLING (PIERCING) PROJECTILE LAW. Straight along team-forward, speed Speed*k, total travel ProjectileRange from roll start, swept axis-aligned rectangle (half-width ProjectileRadius, half-depth ProjectileRadiusY) tested against victim circles edge-to-edge (shape key). Enemy-only per OnlyEnemies, air/ground per AoeToAir/AoeToGround, a per-projectile hit set so each entity is hit at most once, crown pct through damage_against, level scaling of the caster card. The rectangle-circle test is new integer geometry and belongs in fixed.rs. The same law serves the Bowler and Barbarian Barrel later (BowlerProjectile ProjectileRadius 1000, ProjectileRange 6000).",
    "SpellAsDeploy AIRBORNE PHASE. A first projectile (LogProjectile: Speed 360, MinDistance 3000, Gravity 30, no Damage) with no hitbox whose arrival spawns SpawnProjectile at its landing point, inheriting team, card level and forward direction. Launch geometry is the open key spells.SPELL_AS_DEPLOY_LAUNCH_MODEL.",
    "KNOCKBACK LAW (shared with Fireball and any Pushback source). Distance = Pushback millitiles x18 subtiles [COMMUNITY history: Log 1 tile <-> 1000]. Applied only to troops (not buildings or towers). Skipped for IgnorePushback units UNLESS the source has PushbackAll=true [DATA + COMMUNITY]. Recorded in a buffer during the phase that produced the hit and applied after Resolve to survivors as summed displacements, so the order cannot matter and tie-breaks stay mirror-invariant. Resets the victim's attack windup [COMMUNITY]. Not blocked by water the way walking is [COMMUNITY river lore]. Units mid-jump are immune. Direction (radial from the source centre is the default for both circular and rolling sources), time profile, mass dependence and water/building resolution are all calibration keys with no primary source. The engine does not load ignore_pushback today (card.rs has no field); cards.json already carries it.",
    "SPAWN-ON-ARRIVAL. A projectile with SpawnCharacter enqueues SpawnCharacterCount units of that character on the deferred spawn queue at its arrival point. Level = caster's unified level (SpawnCharacterLevelIndex equals the caster rarity RelativeLevel in every shipped row). deploy_ms = SpawnCharacterDeployTime when present, else the character's DeployTime. Formation is a key. Water fallback is the existing formation rule. card.rs must load cards.json units[] (Goblin) because no card in the thin slice summons a plain Goblin; the same loader serves death spawns later.",
    "VALID SPELL VICTIMS. Deploying units are hittable and pushable (combat::splash already does not skip deploy_ms > 0; keep that for rolling hits). Untargetable states exclude victims: hidden Tesla (live: immune to all damage except Earthquake/Freeze), units mid-jump, and the Skeleton Barrel's 0.5 s release animation (not in slice). Friendly units never, for OnlyEnemies=true sources.",
    "CROWN TOWER DAMAGE PERCENT = 100 + raw column, applied to King and Princess towers with truncating integer division (combat::damage_against). The Log's -60 <-> the wiki's 40% before 2017-12-12 is independent corroboration of the delta-from-100 hypothesis in cards.json conventions.",
    "DATA VINTAGE FINDING (not a mechanic). This family's numbers (Log crown 40%, roll 11.1, Goblin damage 50, barrel deploy 1.2 s) plus the roster (Flying Machine released 2017-10-06, no Royal Ghost) date csv_logic to about 2017-10-06..2017-12-12, earlier than cards.json provenance 'first half of 2018'. Live-2026 deltas for this family (fandom): Log range 10.1, pushback 0.7, crown 13%, damage 220@L9; barrel Goblin deploy 1.1 s, first hit 0.4 s. Whether to overlay them is a project decision: the target is the live 2026 game, and card stats are evidence, not spec."   ],
   "calibration_keys_proposed": [
    "time.PROJECTILE_SPEED_TO_SUBTILES_PER_TICK -- status: hypothesis, tied to time.SPEED_TO_SUBTILES_PER_TICK (default equal). Provenance: card.rs:57 UNVERIFIED; AesDragon asserts same unit; Darknighture frame test shows x1.2 for both; no source for a split. Promotion: live Log roll duration, first contact to despawn (3.03 s at /60, 2.525 s at /50), and Goblin Barrel flight time king centre to enemy princess centre (3.474 s vs 2.895 s). Split from the troop key only if a projectile measurement disagrees with the troop measurement beyond frame error.",
    "spells.SPELL_LAUNCH_ORIGIN -- candidates king_tower_centre (default) | king_block_front_edge. Status guess, COMMUNITY qualitative ('seem to come from the King's Tower'). Promotion: fit the offset in t = (d + offset)/v from barrel flight times to two targets at different distances.",
    "spells.PROJECTILE_PATH_LENGTH -- candidates straight_line_horizontal (default, Gravity visual) | arc_length_with_gravity. Status guess. Promotion: the same two-distance flight-time fit; arc length makes the time non-linear in distance.",
    "spells.SPELL_AS_DEPLOY_LAUNCH_MODEL -- candidates airborne_from_behind_lands_on_tap (default) | airborne_from_tap_forward_range_from_tap | instant_roll_at_tap. Status guess. Promotion: Log recording with a Skeleton on the tap tile and one 1.5 tiles ahead; which is hit, and frames from release to first damage (0.5 s / 0.417 s / ~0).",
    "spells.ROLLING_MIN_DISTANCE_SEMANTICS -- candidates ignored_when_range_set (default) | no_hit_zone_from_roll_start | minimum_travel. Status guess. Promotion: the same recording; a Skeleton 1 tile ahead of the tap survives only under no_hit_zone.",
    "spells.ROLLING_HIT_SHAPE -- candidates rect_vs_circle_edge (default, matches combat::splash edge convention) | rect_contains_centre. Status guess; 2023-03-27 'improved collision detection' changed it unspecifiedly. Promotion: Skeletons at 2.2 tiles lateral offset from the log axis.",
    "knockback.DIRECTION_ROLLING -- candidates radial_from_projectile_centre (default; three fandom lateral-push strategy lines) | travel_direction (crforge) | from_centre_segment. Status guess. Promotion: displacement vector of a Knight struck 1.5 tiles off-axis.",
    "knockback.PROFILE -- candidates instant | linear_over_ms (with knockback.DURATION_MS). Status guess, no column or global exists. Promotion: count frames of the slide on a logged Skeleton.",
    "knockback.MASS_SCALING -- candidates none (default: Pushback is a distance) | mass_weighted. Status guess; 'a field existing is not a formula'. Promotion: Skeleton (mass 1) and Giant (mass 18) logged side by side.",
    "knockback.WATER_AND_BUILDING_RESOLUTION -- candidates allow_overlap_then_nearest_passable (default; river-crossing lore) | clamp_at_obstacle. Status guess. Promotion: Log a Knight on the bridge exit and one on a bank.",
    "knockback.ATTACK_RESET -- candidates reset_windup_keep_target (default) | reset_windup_and_clear_target. The reset itself is COMMUNITY-settled; only target retention is open. Promotion: frames from push to next hit of a Knight attacking a tower.",
    "spawn.PROJECTILE_SPAWN_FORMATION -- candidates engine_grid (default, state.rs formation) | ring_at_projectile_radius (GoblinBarrelSpell.Radius 1500) | count_ring_tight. Status guess. Promotion: first-visible-frame Goblin positions from a barrel on open ground, repeated 3x.",
    "spawn.SPAWNED_UNITS_STAGGER_BY_DEPLOY_DELAY -- candidates false (default) | true (characters.DeployDelay, Goblin 400 ms). Status guess. Promotion: per-Goblin first-visible and first-move frames after impact.",
    "arena.SPELL_AS_DEPLOY_RIVER_WHEN_LANE_TOWER_DOWN -- candidates lane_half_of_river (default) | whole_river | none. Status: COMMUNITY that some river placement is allowed (2018-04-25); cell set guess. Promotion: drag the Log over river cells in both lanes after one princess falls. Must be the same key for the Rust deploy_zone and the Python mask (as POCKET_DEPTH_HALF_ROWS is).",
    "arena.SPELL_AS_DEPLOY_ON_NO_DEPLOY_CELLS -- candidates blocked (default, troop rule) | allowed (CanPlaceOnBuildings extends to the king block). Status guess. Promotion: placement indicator over own King Tower and the river-corner tiles (0,14)/(17,14).",
    "arena.SPAWNING_SPELL_WATER_RULE -- candidates closed_cell_touch (default, troop rule) | point_only. Status guess; the rule that the barrel cannot land on water is COMMUNITY-settled. Promotion: placement indicator dragging the barrel along the river bank."
   ]
  }
 ],
 "lead_reread_2026_09_13": {
  "DATA_numbers": 68,
  "MATCH": 29,
  "BLANK": 8,
  "UNCHECKABLE": 26,
  "FLAGGED": 5,
  "contradictions_of_vendored_data": 0,
  "note": "4 flagged are live-vintage values citing 2023-data cells; 1 is blank=unlimited. The spec mixes 2018 and live values under DATA."
 }
}