Bullet Heaven — Design Bible & Balance Tool (Spec)
Bullet Heaven — Design Bible & Balance Tool (Spec)
Section titled “Bullet Heaven — Design Bible & Balance Tool (Spec)”Date: 2026-06-21 Status: Approved (design) — pending spec review Type: Design reference + the interactive web tool that hosts and edits it
1. Purpose & decomposition
Section titled “1. Purpose & decomposition”A bullet heaven succeeds on the breadth and interplay of its content (weapons, enemies, items, elements, synergies, progression), not on the core loop (done in Milestone 1). This spec defines:
- The content & systems taxonomy — the categories of everything we’ll add, and the schema (attributes) of each.
- The elemental system — a deep, hybrid auras + stacks + reactions model (14 elements, a 91-pair reaction matrix) that is the game’s combinatorial novelty engine.
- The meta-progression structure (the “continual progression” hook).
- The web balance tool — an interactive, master-detail editor of all the above with live computed balance metrics, persistence, and game-ready JSON export. This is the first thing we build, because it is the instrument used to design and balance everything else.
- An exploratory mode-shell seam (genre-bending boss modes) — recorded, architecturally non-blocking, not built.
Build order (each is its own spec→plan→build cycle)
Section titled “Build order (each is its own spec→plan→build cycle)”- The design-bible web tool (next plan) — schema-driven master-detail editor + reaction-matrix view + computed metrics + persistence + JSON export, seeded with the 14-element roster and Milestone 1’s existing content.
- Populate & balance content in the tool (design work, not code).
- Game integration — load the tool’s exported JSON into the Godot game’s
/data; replace M1’s GDScript-literal content with data-driven loading. - Implement systems in-game — the elemental engine first (auras/stacks/reactions/status effects), then the remaining weapon/enemy archetypes, then meta-progression. Separate cycles.
This spec is the source-of-truth design reference; the tool is the source-of-truth data.
2. Content taxonomy & schema
Section titled “2. Content taxonomy & schema”The bible is a JSON document of categories; each category has a schema (typed field definitions) and a list of entries. The tool renders editors from the schema and derives computed metrics per category. Every entry has id (slug) and name.
Field types: number, int, text, enum(options), tags[] (multi-select from the controlled vocabulary), ref(category) (link to another entry), bool, list(subschema).
Categories and key attributes
Section titled “Categories and key attributes”tags (controlled vocabulary) — the shared language everything speaks. Delivery tags (projectile, orbit, beam, area, melee, summon, trap, channeled), element tags (one per element), and trait tags (crit, pierce, chain, homing, dot, cc). Editing this list drives the multi-select options everywhere else.
elements (14) — id, name, color, aura_decay_s (number), status (enum: burn/chill/shock/toxin/gravity/echo/impact/mark/wither/timewarp/resonate/hemorrhage/charm/contagion), status_base (number, per-tick or per-application), stacks_max (int), per_stack_scale (number), tags[]. The roster is fixed at 14: Fire, Cold, Lightning, Poison, Void, Aether, Kinetic, Light, Decay, Time, Sound, Blood, Psychic, Nano. See §3.
reactions — the 14×14 matrix (see §3). Each cell: aura (ref element), applied (ref element), name, effect (enum: burst/spread/shatter/cc/debuff/pull/special), base_magnitude (number), per_stack_scale (number), consumes_aura (bool), notes (text). Off-diagonal unauthored cells fall back to a generic “minor reaction” (small burst). Diagonal = reinforce (extend aura / add stacks).
weapons — id, name, archetype (enum: projectile/orbital/beam/area/melee/summon/trap/channeled), element (ref element, nullable), base_damage (number), cooldown_s (number), projectile_speed (number), projectile_radius (number), lifetime_s (number), projectile_count (int), area (number), pierce (int), level_max (int), evolution (ref evolutions, nullable), tags[]. Computed: base DPS, DPS vs armored, screen-coverage estimate (§4).
mods — id, name, kind (enum: stat/transformative), effect (text/enum: which stat or which behavior change), magnitude (number), applies_to_tags[] (which weapons it affects), tags[]. The synergy makers.
evolutions — id, name, base_weapon (ref weapons), required_mod (ref mods), plus the transformed attribute overrides (same shape as a weapon).
enemies — id, name, archetype (enum: swarmer/tank/ranged/splitter/charger/exploder/shielder/healer/summoner/orbiter), hp (number), speed (number), radius (number), contact_damage (number), xp_value (number), armor (number), resist (list of element→multiplier), tags[]. Computed: effective HP, threat score, TTK vs a reference weapon (§4).
elite_affixes — id, name, mods (list: hp_mult, speed_mult, damage_mult, on_hit, on_death effects). One enemy × many affixes = cheap procedural variety.
bosses — id, name, hp, element, phases (list: hp_threshold, pattern, adds), tags[].
characters — id, name, starting_weapon (ref), stat_mods (list), passive (text), unlock (ref unlocks, nullable).
items (relics) — id, name, rarity (enum: common/rare/epic/legendary), effect (text), tags[], unlock (nullable).
pickups — id, name, type (enum: xp/gold/health/magnet/powerup/chest/shrine), value (number), effect (text).
meta_upgrades — id, name, cost (int), effect (text), max_level (int), prereq (ref meta_upgrades, nullable). The global upgrade tree.
unlocks — id, unlocks (ref any category), condition (text).
ascension_tiers — id, level (int), mods (list: enemy_hp_mult, enemy_speed_mult, spawn_rate_mult, curse).
run_structure — biomes/stages (list), timeline events (list: at_time, event), difficulty-curve params (spawn rate formula coefficients, HP scaling).
3. Elemental system (the deep pillar)
Section titled “3. Elemental system (the deep pillar)”Model: hybrid auras + stacks + reactions.
- Applying an element to an enemy adds an aura of that element and increments its stacks (up to
stacks_max). The aura decays overaura_decay_sif not refreshed. Stacks intensify the element’s status effect (e.g. burn DoT =status_base × stacks). - Applying a different element while an aura is present triggers that cell’s reaction, scaled by the current stacks (
base_magnitude × per_stack_scale^stacks). Ifconsumes_aura, the aura clears; otherwise it’s replaced/added per design. - Same-element application = reinforce (refresh decay, add a stack).
Roster (14): Fire (burn), Cold (chill→freeze), Lightning (shock/chain), Poison (toxin/armor-shred), Void (gravity-crush), Aether (echo/amplify), Kinetic (impact/knockback), Light (blind+mark), Decay (wither: −maxHP, anti-heal), Time (slow/rewind/haste), Sound (resonate: shatter brittle), Blood (hemorrhage+lifesteal), Psychic (charm), Nano (contagion/spread).
Reaction matrix: 14×14. 91 unique off-diagonal pairs + 14 reinforce diagonals. We author the signature reactions first (the ~20 that define the headline combos — Superconduct, Combustion, Plasma, Thermal Shock, Collapse, Absolute Zero, etc.) and rely on the generic fallback for the rest, filling them in over time via the tool. The matrix is symmetric in which pair but not necessarily in effect (Fire-onto-Cold-aura may differ from Cold-onto-Fire-aura) — the tool supports per-direction cells.
Status effects are a referenced sub-vocabulary (burn, chill, freeze, shock, toxin, etc.) with their own kind (dot/cc/debuff), so weapons apply them, mods amplify them, and reactions reference them consistently.
4. Computed balance metrics (what makes it a tool)
Section titled “4. Computed balance metrics (what makes it a tool)”Derived live from attributes as the user edits; never stored, always recomputed:
- Weapon base DPS =
base_damage × projectile_count × (1 / cooldown_s)(withpierce/areafactored into an “effective targets” multiplier for AoE/piercing archetypes). - DPS vs armored = base DPS adjusted by a reference armor value.
- Enemy effective HP =
hp × (1 + armor_factor); threat score = blend of contact_damage, speed, effective HP. - TTK grid = effective HP ÷ a reference weapon’s DPS, shown per enemy (are early enemies 1–3 shots? are tanks a slog?).
- XP pacing = the level-up threshold curve (base × growth^level) plotted, with “minutes to level N” given a reference kill rate.
- Reaction preview =
base_magnitude × per_stack_scale^stacksat sample stack counts. - Category sanity flags = soft warnings (e.g. a weapon whose DPS is 5× the category median; an enemy with 0 xp_value).
Formulas live in one module so they’re inspectable and tunable; the plan specifies each exactly.
5. The web balance tool
Section titled “5. The web balance tool”Form: a small static web app — plain HTML/CSS/JS with ES modules, no framework, no build step. Runs locally via any static server (python3 -m http.server in the tool dir) and is deployable as-is to CF Pages for access anywhere. (ES modules can’t load from file://, so it’s served, not double-clicked; a one-line serve command is the local workflow. If pure double-click is later wanted, we inline to a single file.) Lives in the repo under tools/design-bible/.
Data flow:
- Ships with an embedded seed JSON (the schema + initial content: 14 elements, signature reactions, M1’s 5 weapons / mods / 5 enemies, the tag vocabulary).
- On load, merges
localStorageoverrides over the seed (auto-saves every edit). - Import JSON (load a saved bible) and Export JSON (download the full bible, game-ready) — the export is the format the Godot game will later consume.
- A Reset restores the embedded seed.
UI (master-detail):
- Left: category nav (Elements, Reactions, Weapons, Mods, Evolutions, Enemies, Elite Affixes, Bosses, Characters, Items, Pickups, Meta Upgrades, Unlocks, Ascension, Run Structure, Tags).
- Middle: entry list for the selected category — search/filter, add / duplicate / delete.
- Right: schema-driven detail editor for the selected entry (typed fields), with the computed-metrics panel below it.
- Special views: the Reaction Matrix (14×14 grid; click a cell → edit that reaction in the detail pane); a per-category table view toggle for bulk balance passes.
Quality: neon-dark theme consistent with the game; keyboard-friendly; fast; all state client-side. Schema-driven so adding a new field or category is a data change, not a UI rewrite.
6. Exploratory: mode-shell seam (genre-bending bosses)
Section titled “6. Exploratory: mode-shell seam (genre-bending bosses)”Recorded for the future, not built now. The intent: a boss encounter could temporarily hand control to a swappable mode module (first-person shooter, RTS, strategy), then return to survivor mode. The architectural seam: define the running game as a mode shell that hosts one active “mode” implementing a small common interface (enter(context), tick(input), exit() → result); survivor mode is the first module; a boss can push an alternate module and pop back with a result. This shell is the reusable piece across Chris’s future games. We design nothing concrete here beyond naming the seam so later architecture stays compatible (e.g. don’t hard-couple the survivor sim to “the only mode”).
7. Success criteria (for the tool — the first build)
Section titled “7. Success criteria (for the tool — the first build)”- Every category in §2 is browsable and its entries editable via schema-driven fields.
- The 14-element roster, signature reactions, and M1 content are seeded and editable.
- The reaction-matrix view edits any cell.
- Computed metrics (weapon DPS, enemy effective-HP/TTK, XP pacing, reaction preview) update live as attributes change.
- Edits persist across reloads (localStorage); Import/Export round-trips losslessly.
- Runs from a local static server and deploys unchanged to CF Pages.
8. Out of scope (for the first build)
Section titled “8. Out of scope (for the first build)”- Any Godot-side change (game integration is a later cycle).
- The in-browser mini-simulator (deferred).
- Synergy graph visualization beyond the reaction matrix (later, optional).
- The mode-shell implementation (exploratory only).
- Authoring all 91 reactions (signature set first; rest via fallback + ongoing editing).
9. Risks & notes
Section titled “9. Risks & notes”- Matrix authoring volume (91 pairs): mitigated by generic fallback + authoring signature cells first.
- Single-file vs served: chose served-static for clean multi-module code + CF deploy; note the
file://ES-module caveat. - Schema/data drift with the game later: the export JSON becomes the game’s data contract — version it (a
schemaVersionfield) so the Godot loader can validate.