Two hard guarantees
The strongest weapon in any pool is always Legendary and the weakest is always Common. Every map — vanilla, DLC, or a community Mapmaker creation — gets a real jackpot and a real floor, automatically.
Reference Read in any order
Rarity tiers it works out per map, a luck curve, and pity that cannot fail you.
The stock box gives every weapon in the pool identical odds, forever — the best gun on the map, the worst pistol, and the dud all equally likely, at wave 1 and at wave 50. This mod replaces the decision of which weapon you get, and nothing else about the purchase.
The vanilla purchase pipeline is left completely untouched
Score deduction, the “YOU PURCHASED” message, WeaponManager.AddWeapon,
challenge progress, the buy sound — all of it runs exactly as shipped. The plugin
changes only which weapon that pipeline hands you, and it makes that decision at the
one moment that matters: the instant your purchase commits.
Recovered from the binary
The vanilla logic was reverse-engineered from the game’s IL2CPP metadata and native x64 method bodies before a line of this plugin was written. It is brutally simple, and knowing it makes everything below make sense.
This is why the mod is cheaper than vanilla to run
One of its three patches suppresses that 50 Hz re-roll, which was a managed-to-native interop call on every box on every physics tick. A modded client does less background work per frame than an unmodded one. More on that below.
No configuration
The first time a box is used, the mod reads the real combat statistics of every weapon in that box’s pool from the game’s own weapon data and computes a composite power score.
| Weight | Statistic |
|---|---|
| 0.46 | Damage per trigger pull (damage × pellet count for shotguns) |
| 0.20 | Magazine burst potential (per-pull damage × magazine size) |
| 0.12 | Reserve ammo depth |
| 0.08 | Bullet penetration count |
| 0.08 | Headshot multiplier ceiling |
| 0.06 | Effective range |
Scores are normalised within that box’s own pool, then cut into five tiers by percentile rank — roughly the top 13% Legendary, then Epic, Rare, Uncommon and Common.
The strongest weapon in any pool is always Legendary and the weakest is always Common. Every map — vanilla, DLC, or a community Mapmaker creation — gets a real jackpot and a real floor, automatically.
The isNothing entry is pulled out of the ladder entirely and
handled by its own model, rather than sitting in Common
distorting the odds.
Tie-breaks are stable by weapon id, so an identical pool tiers identically on every client and every launch. Nothing depends on load order or timing.
A fingerprint over the pool’s weapon ids is re-verified every 16 rolls. If a map script or Mapmaker edit changes the pool, the box re-censuses automatically.
A census from the author’s validation run on a 14-slot pool came out like this:
Legendary Sniper, AT-Rifle
Epic Shotgun, Trench, LMG
Rare Revolver, AR-A, AR-B
Uncommon Carbine, Flame
Common Pistol, SMG-A, SMG-B
Dud Nothing
Tiers are relative to each box, on purpose
A “Legendary” in a starter pool is the best of that pool, not the best in the game. That is what makes the system work on maps the author has never seen — it never needs a hand-written weapon list.
Wave-scaled luck
Tier weights shift with the current wave, so a wave-30 roll is meaningfully better than a wave-1 roll. These are the odds conditional on a non-dud result:
| Tier | Wave 1 | Wave 10 | Wave 20 | Wave 30+ |
|---|---|---|---|---|
| Legendary | 5.2% | 7.1% | 9.3% | 11.5% |
| Epic | 11.3% | 14.1% | 17.3% | 20.7% |
| Rare | 18.2% | 20.1% | 22.2% | 24.4% |
| Uncommon | 26.8% | 25.1% | 23.1% | 21.1% |
| Common | 38.5% | 33.6% | 28.1% | 22.4% |
Legendary and Epic together move from about 16.5% at wave 1 to about 32% at wave 30 and beyond — roughly doubling — while Common falls from 38.5% to 22.4%. The author’s 400,000-roll validation harness reproduced the wave-1 and wave-30 columns to within ±0.06%.
Pity
A per-match counter tracks consecutive rolls below Epic — and a dud counts double, because a dud is maximally unlucky.
Epic odds are multiplied by 1 + 0.35 × steps and Legendary odds by
1 + 0.75 × steps, climbing with every further miss. The longer the
drought, the harder the box pushes back.
The next roll is guaranteed Epic or Legendary, with Legendary taking a 35% share of that guarantee. There is no way past this — it is not a probability boost, it is a floor.
Including one you got by ordinary luck rather than through pity.
Validated across 400,000 live rolls
The pity counter never once exceeded the hard-pity band. Endless brutal droughts are not unlikely under this system — they are mathematically impossible.
An aborted purchase does not cost you pity
The commit step runs as a Harmony postfix, which Harmony skips if the sale throws. If a purchase fails partway through, your pity counter does not advance for it.
Nothing
Vanilla gives the “Nothing” entry flat pool odds forever, which is why it can hit you three times in a row on wave 40. Here it has rules.
If a box’s pool contains no dud entry at all, the dud stage simply does not exist for that box.
Fairness bags
Picking a tier is only half the decision. Within each tier the mod draws from a shuffled bag — the same principle Tetris uses for its pieces — so every weapon of that tier is offered once before any repeats, and a fresh shuffle can never open with the card that just left.
On top of that, any weapon you already hold, or received within your last three grants, is deferred behind every fresh option remaining in the current bag cycle.
Duplicates stay possible, and that is deliberate
The game’s own AddWeapon on a weapon you already hold is its
ammo refill path, so removing duplicates entirely would remove a real use of the
box. A bag’s final card can still be a held one.
What the measurements showed across 400,000 live rolls: zero back-to-back identical grants, and a held weapon never opened a bag cycle while a fresh same-tier alternative existed — 0 occurrences in 5,000 forced cycles.
Casino-grade
Every roll comes from a session-seeded xoshiro256** generator with SplitMix64 expansion and Lemire unbiased bounded sampling — pure managed maths, statistically independent of frame timing.
It is completely separate from the engine’s own random number generator, so nothing else in the game — weapon spread, zombie spawns, effects — is ever perturbed by this plugin. It also means your prize no longer depends on the millisecond you happened to press the key, which is the single biggest change in feel.
By construction
This is worth explaining rather than just asserting, because it is a genuinely unusual property for a mod that changes what items you receive.
In this game the box purchase is driven entirely by the purchasing client. The rolled index is never synchronised over the network in vanilla — every client’s copy of a box already holds a different value at all times, and it has never mattered, because only the buyer’s own value is ever read.
The mod keeps that contract exactly. It writes the chosen index immediately before the untouched vanilla purchase body runs, on the buyer’s machine only.
Compare this with the zombie AI
The two mods take opposite approaches for good reasons. Zombies are host-authoritative, so the AI mod drives them from the host only. Box purchases are already client-owned, so this mod needs no such rule. Each follows the contract the game itself established.
Performance
The mod has no update loop or coroutine of its own — it is purely event-driven. And because it suppresses vanilla’s 50 Hz re-roll on every box, a modded client does less background work per frame than an unmodded one.
One pass of plain managed arithmetic over the pool, at human click rate. The steady-state roll path allocates nothing — bags, tier tables and score data are all preallocated at census.
One pass of stat reads per box, once, the first time that box is used. Weapon names are copied to managed strings there, so even debug logging never touches interop again.
Per-box data is pure managed state in a cache capped at 64 boxes. No engine references are held, so an unloaded scene can never leak through this plugin. Worst case is a few kilobytes.
Self-healing
The mod is built to fail into vanilla rather than into a broken state, which is the right trade for something that decides what weapon you get.
[SKIP] and its path runs pure vanilla while the rest keeps working.If the kill switch trips you will see it in the console, in plain language:
UngodlyMysteryBox is standing down for the rest of this game session. The box now
runs 100% vanilla logic (including the vanilla FixedUpdate re-roll). Nothing is
left in a modified state.
Stated, not hidden
| Limitation | Why |
|---|---|
| Fire rate is excluded from the power model | The metadata alone cannot prove whether the game's fire-rate field means shots per second or delay between shots, and guessing wrong would invert the ranking. The magazine-burst term already separates weapon archetypes well. |
| Luck is per-client | Exactly like vanilla. The box index was never network-synced by the game, so this mod does not sync it either. |
| Tiers are relative to each pool | By design — it is what lets the system work on maps that did not exist when the mod was written. |
| Unresolvable pool entries score mid-pool | If a weapon controller cannot be resolved, that entry is scored exactly mid-pool. If an entire pool is unresolvable, that box falls back to high-quality uniform rolls — still frame-decorrelated, still cheaper than vanilla. |
Optional
At the default Info level you get the load banner, which patch groups engaged, one census
line per box, and session resets. Set the log level to Debug in
BepInEx\config\BepInEx.cfg and you additionally get the full per-tier weapon
list at census and a one-line verdict for every roll: tier, weapon, and pity state.
Turning the BepInEx console on is what makes any of this visible. It is worth doing for a round just to watch the census run.