Skip to content

Weapon Damage + Upgrade Boost Implementation Plan

Weapon Damage + Upgrade Boost Implementation Plan

Section titled “Weapon Damage + Upgrade Boost Implementation Plan”

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Raise weapon damage output across the board per Chris’s direction — base_damage +30% on every weapon that uses it, and the per-weapon “power” upgrade mod from ×1.25 (×1.20 for scar_tissue) to ×1.5 for every weapon that has one, including the manual aim weapon.

Architecture: Two independent, purely numeric changes — a content-data edit (data/bible.json) and a small constant-table edit (sim/upgrade_system.gd). No new code paths, no logic changes. A determinism re-pin is expected and normal (see Global Constraints).

Tech Stack: Godot 4.6.3, typed GDScript, GUT 9.6.0.

  • Chronowell’s base_damage (0) stays untouched — it’s an intentional zero-damage utility/CC weapon (sim/weapon_chronowell.gd:8: “Deals ZERO direct damage — it never touches sim.elemental_system.damage_enemy”), not an omission.
  • Scythe’s and Scar Tissue’s bespoke damage formulas (%-max-HP execute, stack-based reactive burst) are untouched — only their table-driven power mod magnitude changes; their own internal damage constants are a separate, unrequested change.
  • Determinism: this change will almost certainly shift the pinned baseline hash/checksum in tests/test_determinism_survival.gd (enemies die faster on a fixed seed, shifting downstream spawn/kill timing). Re-pinning it is the normal, expected last step here — not a sign of a bug.
  • sim.death_cause/soak death-cause tracking (merged earlier today) is unrelated and untouched.

Task 1: data/bible.json — +30% base_damage on the 12 weapons that use it

Section titled “Task 1: data/bible.json — +30% base_damage on the 12 weapons that use it”

Files:

  • Modify: data/bible.json (12 base_damage fields)
  • Modify: tests/test_weapon_orbit.gd:59 (pinned live-value test)
  • Modify: tests/test_sweep_and_nova_stats.gd:40-41 (pinned live-value test)
  • Modify: tests/test_coop_integration.gd:14 (stale comment only, value unchanged)

Interfaces: none (pure content-data change, no new function signatures).

  • Step 1: Update the 12 base_damage values in data/bible.json

Read the file first to confirm each weapon’s exact current line (line numbers below are from the most recent read of the file; verify before editing, since content data can drift). Apply exactly this ×1.30 table — every value below rounds to 2 decimal places where the multiply isn’t exact:

Weapon Old New (×1.30)
blade 3.0 3.9
pulse 3 3.9
nova 4 5.2
orbit 10.0 13.0
beam 0.8 1.04
turret 1.0 1.3
scatter 1.4 1.82
blaster 4 5.2
chalice 4.0 5.2
thunder_wake 20.0 26.0
railgun 6.0 7.8
wildfire 12.0 15.6

Do NOT touch scythe, scar_tissue, or chronowell’s base_damage (all 0) — leave them exactly as-is, per this plan’s Global Constraints.

  • Step 2: Update tests/test_weapon_orbit.gd’s pinned live-value test

Find const BIBLE_BASE_DAMAGE := 10.0 (currently line 59) inside test_orbit_full_contact_second_removes_exactly_base_damage_hp. Change to:

const BIBLE_BASE_DAMAGE := 13.0 # data/bible.json weapons/orbit base_damage (live value, boosted 2026-07-17: 10.0 * 1.30)

The rest of the test (the assertion comparing final HP to 100.0 - BIBLE_BASE_DAMAGE) needs no other change — it already derives its expected value FROM this constant, not a second hardcoded number.

  • Step 3: Update tests/test_sweep_and_nova_stats.gd’s pinned live-value assertion

Find (currently lines 40-41):

# nova base_damage 4 * nova.damage_mult 2.0 = 8 ; no shock aura on a fresh enemy so unamplified
assert_almost_eq(sim.enemies.data[e], 100.0 - 8.0, 0.001, "nova damage = base_damage * nova.damage_mult")

Replace with:

# nova base_damage 5.2 (boosted 2026-07-17: bible.json 4 * 1.30) * nova.damage_mult 2.0 = 10.4 ; no shock aura on a fresh enemy so unamplified
assert_almost_eq(sim.enemies.data[e], 100.0 - 10.4, 0.001, "nova damage = base_damage * nova.damage_mult")
  • Step 4: Update the stale comment in tests/test_coop_integration.gd

Find (currently line 14):

const SPAWN_HP := 2.0 # one-shot by the blade's 3.0 base_damage

The numeric value 2.0 is CORRECT and unchanged — blade’s new 3.9 base_damage still one-shots it (in fact more safely). Only the comment is stale. Replace with:

const SPAWN_HP := 2.0 # one-shot by the blade's 3.9 base_damage (boosted 2026-07-17, was 3.0)
  • Step 5: Run the affected tests to verify they pass

Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_weapon_orbit -gexit Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_sweep_and_nova_stats -gexit Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_coop_integration -gexit Expected: all PASS.

  • Step 6: Commit
Terminal window
git add data/bible.json tests/test_weapon_orbit.gd tests/test_sweep_and_nova_stats.gd tests/test_coop_integration.gd
git commit -m "balance(weapons): +30% base_damage across the 12 weapons that use it"

Task 2: sim/upgrade_system.gd — power mod magnitude 1.25/1.20 → 1.5

Section titled “Task 2: sim/upgrade_system.gd — power mod magnitude 1.25/1.20 → 1.5”

Files:

  • Modify: sim/upgrade_system.gd:32-54 (WEAPON_MODS, AIM_MODS)

Interfaces: none (constant-table value change only, no signature changes).

  • Step 1: Update every "power" entry in WEAPON_MODS

Read the current file first to confirm the table hasn’t drifted since this plan was written. Change every ["power", 1.25] (14 occurrences: blade, pulse, nova, orbit, beam, turret, scatter, blaster, scythe, chalice, thunder_wake, railgun, wildfire, momentum_core) to ["power", 1.5], AND change scar_tissue’s ["power", 1.20] to ["power", 1.5] too (per Chris’s explicit confirmation to bring it in line with the rest, not preserve the lower outlier value). Do NOT add a "power" entry to chronowell — it has none today and this plan doesn’t add one (adding damage to an intentionally-zero-damage weapon is a different, unrequested change).

The resulting table (verify your edit matches this exactly):

const WEAPON_MODS := {
"blade": [["power", 1.5], ["arc", 1.30], ["reach", 1.25]],
"pulse": [["power", 1.5], ["chain", 1.0], ["range", 1.30]],
"nova": [["power", 1.5], ["radius", 1.30], ["rate", 0.82]],
"orbit": [["power", 1.5], ["shard", 1.0], ["spin", 1.40], ["reach", 1.25]],
"beam": [["power", 1.5], ["width", 1.40], ["reach", 1.25]],
"turret": [["power", 1.5], ["count", 1.0], ["rate", 0.80]],
"scatter": [["power", 1.5], ["pellets", 1.0], ["spread", 0.80]],
"blaster": [["power", 1.5], ["rate", 0.80], ["multishot", 1.0]],
"scythe": [["power", 1.5], ["arc", 1.30], ["reach", 1.25]],
"chalice": [["power", 1.5], ["radius", 1.30], ["duration", 1.25]],
"thunder_wake": [["power", 1.5], ["radius", 1.30], ["duration", 1.25], ["root", 1.0]],
"railgun": [["power", 1.5], ["lances", 1.0], ["mark", 1.3]],
"wildfire": [["power", 1.5], ["radius", 1.30], ["decal", 1.0]],
"scar_tissue": [["power", 1.5], ["radius", 1.20], ["threshold", 0.85], ["lifesteal", 0.08]],
"momentum_core": [["power", 1.5], ["rate", 0.85], ["charge_ease", 0.85], ["multishot", 1.0]],
"chronowell": [["duration", 1.25], ["rate", 0.85], ["heal", 3.0]],
}

(Only the "power" entries change — every other mod kind/magnitude in this table is unchanged; chronowell’s row is unchanged entirely, shown here only for context.)

  • Step 2: Update AIM_MODS’ power entry

Change (currently line 53):

const AIM_MODS := [["power", 1.25], ["pierce", 1.0], ["burst", 1.20],
["knockback", 1.30], ["firerate", 0.85], ["velocity", 1.20]]

to:

const AIM_MODS := [["power", 1.5], ["pierce", 1.0], ["burst", 1.20],
["knockback", 1.30], ["firerate", 0.85], ["velocity", 1.20]]
  • Step 3: Run the upgrade-system test file to verify nothing broke

Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_upgrade_system -gexit Expected: PASS. (Per the pre-implementation audit, no test in this project asserts a damage number derived from reading WEAPON_MODS/AIM_MODS’ magnitude through the live upgrade pipeline — tests that call apply_mod("power", 1.25) pass their own literal magnitude directly and are unaffected. If this run surfaces an assertion failure tied to the magnitude change anyway, treat that as new information the pre-implementation audit missed — investigate and fix the test’s expected value, don’t weaken the assertion.)

  • Step 4: Commit
Terminal window
git add sim/upgrade_system.gd
git commit -m "balance(weapons): power upgrade mod 1.25/1.20 -> 1.5 across all weapons"

Task 3: Verification, determinism re-pin, and push

Section titled “Task 3: Verification, determinism re-pin, and push”

Files:

  • Modify: tests/test_determinism_survival.gd (re-pinned hash/checksum, expected)

  • Step 1: Full suite

Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gdir=res://tests -ginclude_subdirs -gexit Expected: 0 unexpected failures outside test_determinism_survival (that one is EXPECTED to fail here — its pinned hash/checksum literals are now stale). Same 4 pre-existing risky/pending.

  • Step 2: Re-pin the determinism baseline

Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_survival -gexit Read the failure output — it prints the ACTUAL current snapshot_string().hash() and state_checksum() values for the fixed seed. Update the two pinned literals in tests/test_determinism_survival.gd to those actual values (find the existing assert_eq(...).hash(), 4217109746-style literal and the state_checksum(), 3312602286-style literal — read the file to get their exact current form before editing). Re-run the same command; expected: PASS with the new pinned values.

  • Step 3: Count guard

Run: bash scripts/check-test-count.sh Expected: test-count guard OK (241 scripts — this plan adds no new test files, only edits existing ones).

  • Step 4: Boot smoke

Run: godot --headless --path . --quit-after 90 2>&1 | grep "SCRIPT ERROR" Expected: no output (clean).

  • Step 5: Soak sanity check — confirm the bigger numbers don’t break anything

Run: bash scripts/soak.sh --runs=10 --ticks=36000 --player=skilled --seed0=11000 Expected: 0 invariant failures, 0 anomalies (specifically: no NaN/negative-HP flags from a much larger hit landing in one tick, no early-end-without-game_over anomalies). Report the printed death_tick_distribution/level_distribution — a real check on whether survival got meaningfully easier with this change (informational for Chris, not a pass/fail gate, since a requested damage buff naturally raising survival numbers is the expected and intended outcome, not a regression).

  • Step 6: Full re-pin commit
Terminal window
git add tests/test_determinism_survival.gd
git commit -m "test(determinism): re-pin baseline after weapon damage/upgrade boost"
  • Step 7: Push
Terminal window
git push origin main