Split The code is public; the app answers only to the crew.

A drop from anywhere is free: `fallDamage` is written, tested and called by nothing #228

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Falls now cost health: `fallDamage` is called on the tick a fall ends, instead of being dead code that only its own test used. On `de_forge` and `de_cinder` it never costs anything. The tallest floor anyone can stand on is a 128-unit crate, which lands at about 452 u/s, under the 580 u/s threshold. A drop needs about 210 units before it hurts. This is here so the simulation is whole and taller geo…

shipped merged

opened this

Falls now cost health: fallDamage is called on the tick a fall ends, instead of being dead code that only its own test used. On de_forge and de_cinder it never costs anything. The tallest floor anyone can stand on is a 128-unit crate, which lands at about 452 u/s, under the 580 u/s threshold. A drop needs about 210 units before it hurts. This is here so the simulation is whole and taller geometry gets it for free. The README's What is real and what is not section says so.

Physics (physics.ts): Body has a new landSpeed field. move writes it on every call: the downward speed on the tick a fall ends, 0 on every other tick. That speed is read before the floor zeroes vel[2], so standing still can never count one landing twice. It allocates nothing.

World (world.ts): right after move, applyInput calls a new land. It takes Math.round(fallDamage(landSpeed)), whole points like all other damage here, and armour doesn't reduce it. It uses the bomb's path: hurt, then recordDamage with ENVIRONMENT, then onKill with the victim as their own killer. The feed's weapon field is now a Cause, which is a WeaponId or "fall". I didn't add a fall row to WEAPONS, because other code iterates that list and it would stop being only things you can hold. A fall kill pays nobody.

How it reads:

  • Kill feed: a fall death shows as "name Fall name".
  • Death screen: it says "You fell too far" instead of naming you as your own killer.
  • Flinch and damage wedge: a hurt from straight below has no direction, so the flinch kicks up only and no wedge is drawn. The vignette and pain sound fire as usual, with no new sound or effect kind.
  • Round report: ENVIRONMENT is one ledger row shared by the bomb and falls. The report calls it "the bomb" in a round where the bomb went off, and "a fall" otherwise.

Tests:

  • Physics: a fall reports its landing speed on exactly one tick. A 128-unit drop lands above 400 u/s and costs 0. A plain jump lands at jump speed and costs 0.
  • World: a 128-unit drop costs nothing. A 320-unit drop costs exactly 12, once, even after three seconds standing on the floor, and doesn't touch armour. A fall that kills credits ENVIRONMENT, counts as the kill in damageTaken, shows as a fall in the feed and pays no money. A bot falling takes the same damage but gets no hurt effect.
  • HUD and report: no wedge for a hurt from below, the "You fell too far" line, and the "a fall" label. With the wiring in applyInput removed, the three world tests where damage is expected fail.
  • Changed existing test: the report's bomb-naming test now also sets bomb.state = "detonated". The naming depends on that state now; the test wasn't wrong.

bun run check passes: 810 tests, and the RL arena tests are untouched. Nothing on either map can show this in the preview. The quickest way to try it is bun test src/client/world.test.ts -t fall.

🤖 Generated with Claude Code

Closes #190

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs