Archived on 2026-10-24. The Godot rewrite is cancelled. Migration decisions and work checklists below are withdrawn. Original claims, code examples, file paths, and completion statements have not all been revalidated. Use the current documentation and issue register for maintenance decisions.
Upgrade System Analysis¶
Purpose¶
The upgrade system controls level-up choices, chest/boss rewards, permanent shop modifiers, special upgrades, ability unlocks, weapon level evolution, and timed/counter-based upgrade evolutions.
Current Unity Implementation¶
Main scripts:
| Path | Script | Role |
|---|---|---|
Assets/Script/Shared/ScriptableObjects/Upgrade.cs |
Upgrade |
Data asset for basic/special/boss/evo upgrades. |
Assets/Script/Game/GameSystem/Rewards/RewardRoller.cs |
RewardRoller |
Builds reward pool, rolls card choices, handles reward phases and reroll. |
Assets/Script/Game/GameSystem/Upgrades/UpgradeSelection.cs |
UpgradeSelection |
Handles one/two-player card navigation and confirmation. |
Assets/Script/Game/UI/GameHUD/Upgrades/UpgradeButton.cs |
UpgradeButton |
Stores one card's upgrade data and calls UpgradeFunction. |
Assets/Script/Game/GameSystem/Upgrades/UpgradeFunction.cs |
UpgradeFunction |
Applies upgrade effects through a large switch on UpgradeList. |
Assets/Script/Game/GameSystem/Upgrades/UpgradeEvoFunction.cs |
UpgradeEvoFunction |
Starts evolution conditions and swaps old upgrade/ability for evolved upgrade. |
Assets/Script/Game/GameSystem/Upgrades/WeaponEvoData.cs |
WeaponEvoData |
Character weapon-level evolution functions. |
Assets/Script/App/Managers/ShopManager.cs |
ShopManager |
Permanent shop upgrades that become GameSession modifiers. |
Assets/Script/Shared/ScriptableObjects/ShopUpgrade.cs |
ShopUpgrade |
Shop upgrade data and costs. |
Key Data¶
Upgrade fields:
| Field | Meaning |
|---|---|
idname |
Stable upgrade id used by save/leaderboard/localization. |
upgradeName |
Display name. |
rarity |
Basic, Uncommon, Rare, Epic, Legendary, Unique, Mythic, Evo. |
upgradeList |
Array of UpgradeList enum effects. |
value |
Parallel array of effect values. |
ability |
Ability asset to equip or use as requirement/evolution id. |
appearsInStages |
Stage filter. |
weight, modifiedWeight |
Roll weight and runtime modified weight. |
isBasicUpgrade, appearInSpecialChest |
Reward table classification. |
maxUpgrade, upgradeTaken |
Per-run limit and runtime count. |
requiredUpgrade, requiredAchievement, isUnlocked |
Gating. |
evolve |
Evolved upgrade asset. |
popularity |
Used by leaderboard/popularity UI. |
UpgradeList effect categories:
- Stat add/sub:
DMG,ASP,CDMG,CCH,RS,MS,AMMO,HP,STM,SAMP. - Stat modifiers:
DMGMODIF,ASPMODIF,CDMGMODIF,CCHMODIF,RSMODIF,MSMODIF,AMMOMODIF,HPMODIF,STMMODIF,SAMPMODIF. - Ability:
EQUIPABILITY. - Utility/progression:
MAGNET,REROLL,STAMINAREGEN,REVIVE,POTIONBAG,WEAPONLEVELUP. - Special:
DRONE,ARCHMAGE,JETPROP,KARMAYANA,EVOUPGRADE, and others.
Runtime Flow¶
Reward Table Import¶
GameInitialization.ImportRewardTable()clears reward tables.- It iterates all special upgrades from
GameDataLoader.GetAllUpgrades(). - If an upgrade appears in the current stage:
appearInSpecialChestupgrades are also added to the boss/ultimate table.- Mythic upgrades are skipped from normal special reward import.
- Other eligible upgrades are added to the special table.
- It iterates all basic upgrades from
GetAllBasicUpgrades()and adds stage-eligible basic upgrades. PrepareRewardTable()resetsupgradeTakento 0 on all table entries.
Reward Rolling¶
GameManager.EnqueueReward()sends reward types intoRewardRoller.RewardRoller.ProcessRewardQueue()starts rolling if no reward phase is active.RollTheUpgradeTable()sets reward state/phase and chooses a source table:LevelUpReward: basic upgrades.SpecialReward: special upgrades.BossReward: ultimate/boss upgrades.- Candidate upgrades are filtered:
- Revive is blocked if all relevant players are alive.
- Weapon level is blocked if maxed.
- Some vampire/basic edge cases are filtered.
- Special rewards require unlock and required abilities.
- Roll weights are modified by luck for Epic, Legendary, and Mythic rarities.
- Cards are rolled without duplicates by removing chosen upgrades from
currentUpgrade. - UI cards are instantiated and configured.
DisplayAnimation()moves reward phase toSelectingand activates selection markers.
Selection¶
UpgradeSelection.Update()only processes card navigation/selection when game is paused and reward phase isSelecting.- Each active player moves a marker left/right and locks a choice with Dash input.
- Two active players cannot choose the same card.
- Once all active players lock a selection,
ConfirmSelect()waits briefly. - Each selected
UpgradeButtoncallsUpgradeFunction.SelectUpgrade(playerIndex, ...). - Reward UI is destroyed,
RewardRoller.CompleteReward()clears reward state, and the game resumes.
Applying Upgrades¶
UpgradeFunction.SelectUpgrade():
- Gets
GameManager.instance.player[playerIndex]. - Increments
upgrade.upgradeTakenif limited. - Iterates
upgradeListandvaluearrays. - Applies each effect through a switch.
- Stat effects call
PlayerModifieradd/modify methods. - Ability effects call
AbilityFunction.AddAbility. - Special effects directly mutate player fields, ability holder fields, game manager reroll, companion spawns, revival state, or weapon evolution.
Evolution¶
UpgradeEvoFunction.StartEvo():
- Builds an
UpgradeEvoDatafromupgrade.evolve. - Switches on the old upgrade ability type.
- Starts either:
- counter-based evolution, such as collect 300 SuperMagnet coins or 500 reflected damage,
- time-based evolution, such as 120 seconds.
- Once condition is met, plays evolve sound, applies evolved upgrade via
UpgradeFunction.SelectUpgrade, and removes the old ability.
Dependencies¶
- Reward system depends on
GameManageras the owner of all serialized UI/reward references. - Upgrade logic depends on
Player,PlayerModifier,AbilityHolder,AbilityFunction,GameSession,PoolManager,WeaponEvoData,UpgradeEvoFunction,GameDataCount. - UI depends on DOTween,
InputManager,AudioResources, andGame.Audio. - Unlock/progression depends on
AchievementManager,Save, andGameDataLoader.
Problems / Risks¶
UpgradeFunctionis a large hardcoded switch.Upgrade.upgradeListandvalueare parallel arrays; mismatched lengths would break or misapply effects.- Runtime state (
upgradeTaken,modifiedWeight,isUnlocked) is stored on shared ScriptableObject assets. - Reward rolling and UI animation are coupled in
RewardRoller. UpgradeSelectionrequiresGameManager.instance.isPaused, while reward state is tracked separately asRewardPhaseandGameState.IsRewarding.- Evolution conditions are hardcoded by ability type.
- Some conditions assume two player slots exist.
- Upgrade ids use both
idnameandupgradeName; saves use someupgradeNamekeys for special unlocks.
Migration Notes for Godot¶
Preserve:
- Reward types: level-up, special chest, boss chest.
- Rarity/weight/luck behavior.
- Required achievement/required ability gating.
- Max upgrade count behavior.
- Two-player card selection restrictions.
- Evolution conditions and final swap behavior.
- Old shop upgrade values and costs only as balancing/save-conversion reference.
Redesign:
UpgradeResource
- id
- display/localization keys
- rarity
- roll weight
- stage filters
- max count
- requirements
- effects: Array[UpgradeEffectResource]
UpgradeRuntimeState
- times_taken
- modified_weight
- unlocked for current run if needed
UpgradeEffect
- apply(context)
- examples: StatAddEffect, StatMultiplyEffect, UnlockAbilityEffect, SpawnCompanionEffect, ReviveEffect, StartEvolutionEffect
RewardDirector
- owns queue and roll logic
- no UI animation details
RewardUI
- displays cards and emits selected(upgrade_id, player_id)
Do not copy:
- One giant
UpgradeFunctionswitch. - Runtime mutation of upgrade resources.
- Parallel
upgradeList/valuearrays. - Card UI instantiation logic inside reward roll logic.
Open Questions¶
- Which upgrades in
Testing,InDevelopment, andNeed Revampshould be excluded from Godot data conversion? - Most saved content is expected to use manually typed ids, but audit whether any upgrade saves still use
upgradeNameor localized names. - Exact reward-state pause behavior should be separated from pause-menu behavior so selection cannot soft-lock.