# Red Planet — the damage model, end to end How a hit becomes lost armor, lost SCORE, and a burning VTV. Unlike BT, this analysis reads the AUTHENTIC surviving game source (`RP/` in this tree — VTV.cpp, WEAPSYS.cpp, RIVET.cpp, DEMOPACK.cpp, RPPLAYER.cpp) on top of the shared MUNGA engine. The BT companion doc is `TeslaRel410/restoration/source410/BT/DAMAGE-MODEL.md`; the engine layers (Damage record, DamageZone, Entity message routing) are identical and only summarized here. The headline difference: **RP's damage model is a physics + score economy, not a subsystem-failure simulation**. There are no critical tables, no per-zone cascade, no equipment destruction. Damage is dominated by KINETIC events (ramming, walls, crushing doors), armor is calibrated from the vehicle's physics constants, and every point of damage is simultaneously a score transaction between players — Martian football, not a mech duel. ``` laser raycast / rivet impact / demo-pack blast / collision bounce │ ▼ TakeDamageMessage (zone 0 — the hull) │ ▼ VTV::TakeDamageMessageHandler ├─ collision? recompute damage locally via DynamicBounce (+ knockback) ├─ otherwise apply damageForce/mass to velocity (knockback impulse) └─ chain to Entity → damageZones[0]->TakeDamage │ ▼ VTV__DamageZone::TakeDamage ├─ engine armor model: level += amount × scale[type], 1.0 = dead ├─ level ≥ 1.0 → VTV::BurningState (+ VehicleDead to the player) └─ EVERY hit forwarded to the linked RPPlayer │ ▼ RPPlayer::TakeDamageMessageHandler ├─ victim loses damageAmount as SCORE (collision × deathConstant) └─ top attacker in the 2-second revenge window GAINS the same ``` ## 1. Shared engine layers (identical to BT) The `Damage` record (`MUNGA/DAMAGE.HPP`): 5 types (Collision, Ballistic, Explosive, Laser, Energy), `damageAmount`, `damageForce`, `surfaceNormal`, `impactPoint`, `burstCount`. `DamageZone::TakeDamage` integrates `damageLevel += amount × damageScale[type]`, clamps [0,1], 1.0 = Burning + Destroyed graphic. `Entity::TakeDamageMessageHandler` routes `damageZones[zone]->TakeDamage` and DROPS zone −1. RP never needs the −1 path in combat: **every producer passes zone 0**. The VTV streams its zone array from the type-20 resource like any entity (multiple zones can exist for graphics), but combat, collision, suicide and out-of-world damage all land on zone 0 — the hull is one armor pool. There is no RP analog of BT's cylinder hit-location table, no aimed-zone fire, and no criticals: `VTVSubsystem` adds only controls plumbing (compare BT's `MechSubsystem`, which owns a private armored zone per equipment piece). ## 2. Producers — where Damage records are born **LaserGun** (`WEAPSYS.cpp`, front + rear lasers): a RAYCAST along the vehicle ±Z axis (`FindBoxedSolidHitBy`), explosion entity spawned at the beam end, `TakeDamageMessage` (zone 0) at the struck entity. Authored `DamageAmount` is per-second; the ctor pre-multiplies by the firing cycle (`laserDuration + pulseDuration`) so each completed cycle delivers one quantum. Charge drains per cycle; type Laser. **RivetGun** (`RIVET.cpp`): a real projectile entity. On striking a MOVER the authored amount is scaled **kinetically**: `amount ×= (|v_closing|/350)²` — rear-ending a fleeing VTV tickles, a head-on rivet hurts. Static world hits use the authored amount. Explosion spawned at impact; zone 0. **DemoPack** (`DEMOPACK.cpp`): deployed charge. On detonation every entity within `blastRadius` takes the FULL authored amount (no falloff), type Explosive, `damageForce` = normalized blast direction × `concussiveForce` (the knockback shove). Compare the engine's generic `Explosion` splash (EXPLODE.CPP), which decays `burstCount/r^1.2` — the demo pack is a flat binary yes/no blast. **Collisions — the star of the show** (`VTV.cpp` MoveVTV + `MUNGA/MOVER.CPP`): the per-frame integrator collides the hull volume (ray-cast ahead when moving faster than one hull diameter per frame). - **Static world** (walls/terrain): `StaticBounce` computes the energy loss = the damage amount; a keel-angle grace applies — surface normals within 45° of the keel (`collisionNormal.y > 0.7`) take `bottomArmorScale` (authored, default **0.1×**) — belly scrapes are cheap, wall slams are not. - **VTV-on-VTV ram**: the RAMMER computes `DynamicBounce` damage locally, applies it to itself, and dispatches the same record at the victim. The victim's handler does NOT trust the amount: it re-runs `DynamicBounce` from its own frame (relative velocity vs the rammer, its own elasticity), ignores the message if the relative motion doesn't oppose the passed normal, and applies its own bounce physics. Both sides also enter each other's revenge queue. - **Doors**: velocity is adjusted by the door's motion; caught between both doors = CRUSH (the closing-door instant-kill). - **Out of world**: leaving the arena volume = instant full Explosive budget (`1/scale`) + a random tumble. - **Suicide**: the Kavorkian button (authentic name) applies exactly the full Explosive budget to zone 0. **Knockback**: non-collision damage applies `damageForce / moverMass` to the victim's velocity — lasers, rivets and demo packs physically shove the target. While BURNING, collision forces convert to torque (the death tumble, clamped ±20). ## 3. Armor calibration — physics, not points The authored `.dmg` per-type points stream in as in BT, but the VTV zone ctor **overwrites the Collision scale from the vehicle's physics**: scale[Collision] = 2000 / (mass × (maxImpactSpeed/3.6)² × (1 − e²)) i.e. the hull is calibrated so a full-speed impact (authored `MaxImpactSpeed`, km/h → m/s) spends exactly the whole budget — armor is defined in impact-speed terms, matched to what `StaticBounce`/ `DynamicBounce` return (kinetic energy with the elastic fraction restituted). The companion constant deathConstant = 2000 × deathScoreLoss / ((1 − e²) × deathSpeed² × mass) converts collision damage to SCORE at the same exchange rate: dying at `DeathSpeed` costs exactly `DeathScoreLoss` points. ## 4. The victim — `VTV::TakeDamageMessageHandler` + zone override Handler order: (1) look up the inflicting entity's player and push an `OffensivePlayer{player, damageType, timestamp}` onto OUR revenge queue; (2) Collision → local `DynamicBounce` recompute (see above), else → knockback impulse; (3) chain to the Entity base → zone 0. `VTV__DamageZone::TakeDamage` then: 1. Runs the engine armor model. 2. `damageLevel ≥ 1.0` → `VTV::BurningState` — the vehicle is DEAD but keeps tumbling physically (burn + torque) until reset. 3. On the ALIVE→BURNING transition, sends `VehicleDeadMessage` to the linked player. 4. Forwards a COPY of every damage record to the linked `RPPlayer` — the score pipe. Damage and score are the same event in RP. ## 5. The score economy — `RPPlayer::TakeDamageMessageHandler` - Victim: `score_loss = damageAmount` (Collision × `deathConstant`), posted as `DamageScoreLossPointType` — **damage is score loss**. - The revenge queue (`offensivePlayerList`, entries expire after **2.0 s**) is consulted: the TOP attacker — most recent player to damage us — gains the same amount as `DamageBonusPointType` (sign-flipped by the team/`deathBonus` rules; negatives become drop-zone-hit penalties), and the arcade console is notified (`ConsolePlayerVTVDamagedMessage`). - On vehicle death the same queue decides KILL credit (`VehicleDeadMessageHandler`), then the VTV runs `DeathShutdown` (`FootballReset` for Crusher/Blocker roles, `RegularReset` otherwise) and respawns via the DropZone machinery. - Role twists (Martian football): the Runner carries a `scoreMultiplier` that DOUBLES per score-zone pass (reset to 1 on death) — damage taken during the drop-zone penalty window is scored differently; Crusher and Blocker are RPPlayer roles, not weapons — crushing is done with the hull, priced by the collision physics above. ## 6. RP vs BT — the design fork from one engine | | Red Planet | BattleTech 4.10 | |---|---|---| | hull zones in combat | one pool (zone 0) | many, vital/leg flags, LOD shells | | unaimed hit resolve | none needed | cylinder height×angle table | | criticals / equipment | none | per-zone crit tables → subsystem kill | | collision armor | derived from physics constants | authored points (halved scales for legs) | | ram damage | victim recomputes locally | inflictor-authoritative message | | knockback | damageForce/mass on every hit | none (gyro shake only) | | death | zone 0 full → Burning + tumble | vital/leg zone full → mech kill | | meaning of damage | score transaction (2 s revenge window) | armor attrition + equipment failure |