From 38763233be21ada946d5081950932cda2ae7abe4 Mon Sep 17 00:00:00 2001 From: McDope Date: Thu, 27 Aug 2026 02:19:30 +0200 Subject: [PATCH] notes: spread a kill's drops instead of stacking them on one tile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backlog item only, no code. Verified before writing it down rather than recording the observation as-is: Every drop from a kill is pushed at exactly `enemy.x, enemy.y` — the guaranteed health pack (engine.ts:6125), the ammo/swap roll or its Toolchain consolation (:6152/:6159), the bonus-weapon roll (:6164), and both Elite pushes (lootApply.ts:156/:158/:167). A regular kill can therefore leave three drops on identical coordinates and an Elite two. The renderer applies no offset — sprites.ts:778 projects raw drop.x/drop.y — so they draw as perfectly overlapping billboards. Recorded two things the observation alone does not carry. It is cosmetic: collectLoot walks every drop within AMMO_PICKUP_RADIUS, so the whole stack is collected and nothing is lost, which is why nobody noticed. And the fix has a trap — jittering the position off the shared PRNG stream would shift every subsequent draw, re-rolling every level and invalidating stored replays. The drops already carry a per-enemy `dropSeq`, so an index-derived offset spreads them deterministically at no cost. Co-Authored-By: Claude Opus 5 (1M context) --- notes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/notes b/notes index abb7d32..a5fc43b 100644 --- a/notes +++ b/notes @@ -181,6 +181,10 @@ One dependency chain runs through this section: the decoration mechanism (a tile - **Ships with two doc edits and a changelog line.** `doc/user/hud-and-ui.md:10` spells the label out in its panel list; `LABEL_STABIL` (`hudLayout.ts:178`) is one string by design ("named so a rename moves one string"), and renaming the constant alongside it is optional. Leave `CHANGELOG.md:60` and `history.md` alone — those are records of what shipped, not current claims. This one *is* player-facing, so it earns a real changelog entry. - **Verification is visual, because nothing pins the string.** No test asserts `"STABIL"` — `hud.test.ts:892` only names it in a comment — so the check is a screenshot of the bar at **both** presets rather than the one that happens to be loaded, which is exactly how a marker shipped wrapping once before. +- [ ] **Spread a kill's drops across the tile — right now they stack and read as one item.** Every drop from a kill is pushed at exactly `enemy.x, enemy.y`: the guaranteed health pack (`engine.ts:6125`), the ammo/swap roll or its Toolchain consolation (`:6152`/`:6159`), the bonus-weapon roll (`:6164`), and both Elite pushes (`lootApply.ts:156`/`:158`/`:167`). So a regular kill can leave **three** drops on identical coordinates and an Elite **two**. The renderer applies no offset — `sprites.ts:778` projects raw `drop.x`/`drop.y` — so they draw as perfectly overlapping billboards and the player sees one pickup. + - **Cosmetic only, which is why it has gone unnoticed.** `collectLoot` walks every drop within `AMMO_PICKUP_RADIUS`, so the whole stack is picked up and nothing is lost; the bug is that you cannot tell there was anything to think about. It also makes the loot-drop screenshots misleading in the same way. + - **The trap for whoever implements it: do not spend an rng draw.** Jittering the position off the shared PRNG stream shifts every subsequent draw and re-rolls the layout of every level ever generated, invalidating stored replays — the same constraint that keeps the styleset on its own salted stream and `planAcidOverflows` at zero draws. Spread deterministically instead: the drops are pushed in a fixed order and already carry a per-enemy sequence number (`pushLootDrop`'s `dropSeq`), so an index-derived offset costs nothing and changes no existing map. + ### Multiplayer & backend The multiplayer *features* are still one-liners in the last section; this is the operational half.