Skip to content

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.

Ability System Analysis

Purpose

The current ability system determines which abilities a player has, when they activate, cooldown/stamina behavior, and the actual effects: dashes, shields, weapon changes, projectiles, buffs, companion spawns, and damage modifiers.

This is the most important system to redesign. The current AbilityHolder should not be copied directly.

Current Unity Implementation

Main scripts:

Path Script Role
Assets/Script/Game/Entities/Player/Core/AbilityHolder.cs AbilityHolder Per-player ability list plus most ability behavior.
Assets/Script/Game/Utilities/AbilityFunction.cs AbilityFunction Singleton helper for adding/checking abilities, cooldown coroutines, effect/potion queues.
Assets/Script/Shared/ScriptableObjects/Ability.cs Ability ScriptableObject with AbilityType, cooldown, stamina, and runtime cooldown fields.
Assets/Script/Shared/ScriptableObjects/Upgrade.cs Upgrade Upgrade data can reference an Ability; EQUIPABILITY adds it to player list.
Assets/Script/Game/GameSystem/Upgrades/UpgradeFunction.cs UpgradeFunction Hardcoded upgrade effects and ability registration.
Assets/Script/Game/Entities/Player/Weapons/PlayerWeapon.cs PlayerWeapon Weapon firing and many ability hooks/attack modifiers.
Assets/Script/Game/Entities/Player/Combat/PlayerAttack.cs PlayerAttack Projectile/hitbox damage behavior and ability flags.
Assets/Script/Game/Entities/Player/Combat/PlayerAttackCarrier.cs PlayerAttackCarrier Carrier projectiles for bombs, rockets, airstrike, explosions.
Assets/Script/Game/Resources/AbilityResources.cs AbilityResources Shared visual effect prefab references.

Key Data

Ability ScriptableObject fields:

Field Meaning
type AbilityType enum id.
cooldownTime Base cooldown. Mutated by some runtime code.
staminaCost Stamina cost. Mutated by dash runtime code.
isOnCooldown Runtime cooldown flag stored on asset.
currentCooldownTime Runtime cooldown timer stored on asset.

AbilityHolder key fields:

  • abilities: unlocked ability list. Starts with character ability after player initialization.
  • Dash fields: dashSpeed, dashForce, isDashing, dashAudio.
  • Character ability state groups:
  • Knight: knightProtoBarrier, KnightCurrentDmgThreshold, KnightLongSlashChance, etc.
  • Soldier: soldierFrenzyEffect, SoldierFrenzyCDModif, SoldierDashReloadPercentage, etc.
  • Archer: archerArtemisArrow, ArcherAttackToLusterBurstCount, etc.
  • Miner: minerGem, MinerGroundBreaker*, MinerAssuredCrit*.
  • Star Wanderer: zenovaStarBurstChargeIndicatorEffect, zenovaMissileCarrier, Zenova*.
  • Celestial: sword prefabs, rotator, sword counts, lightning behavior.
  • Priestess: orb prefab, tidal veil object, orb/veil counters.
  • General ability/effect state: shield objects, roll effects, rage/frenzy/focus/tactical flags, meditate state, war cry, airstrike, judgement, etc.

AbilityType enum contains over 90 entries. It covers dash, character abilities, special upgrades, boss upgrades, evolved upgrades, and in-progress abilities.

Runtime Flow

Registration

  1. Player prefab has an AbilityHolder with a serialized abilities list.
  2. AbilityHolder.Start() initializes many character/general ability fields to defaults.
  3. PlayerSettingsAbilityFirstTime() waits for GameInitialization.Instance.characterInit.
  4. It adds the selected character's Character.ability to the player's abilities.
  5. It resets cooldown flags for all abilities in the list.
  6. It runs initial setup for abilities such as ProtoBarrier, SkyYingSword, and AbyssOrb.
  7. Later, upgrades add abilities through UpgradeFunction.SelectUpgrade when an upgrade entry is EQUIPABILITY.
  8. AbilityFunction.AddAbility(ability, abilities) appends the Ability ScriptableObject and resets its cooldown flags.

Activation

AbilityHolder.Update() polls many activators every frame:

  • Dash input.
  • Soldier Frenzy.
  • Archer Artemis Burst.
  • Knight ProtoBarrier.
  • Star Wanderer Rocket Barrage.
  • Celestial Yang Sword.
  • Light Blessing.
  • Ultra Magnet.
  • Survival Instinct.
  • Stellar Shield.
  • Transcend/Eclipse.
  • War Cry.
  • Meditate.

Other activators are called from player/weapon/damage events:

  • PlayerWeapon.FireWeapon() calls ProtoSword, NeoSword, LusterBurst, GroundBreaker, CCombo, Assassination, Berserker Blood, SureFire, Unsteady Hand, BloodThirst, Rebuke, and weapon spawn modifiers.
  • Player.TakeDamage() calls shield/block/reflect/revive/final form/first aid/flee boots/panic frenzy/ET armor/jet propulsion/enrage/emergency barrier logic.
  • PlayerCollectItem calls item and potion ability effects.

Cooldown

AbilityFunction.UseAbility:

  1. Finds the ability by AbilityType in the player's abilities.
  2. Returns if missing.
  3. Returns if on cooldown unless bypassCooldown is true.
  4. Starts CooldownTimer unless bypassing.
  5. Starts the ability coroutine.

CooldownTimer:

  1. Sets ability.isOnCooldown = true.
  2. Starts a CurrentCooldownTimer coroutine that decrements currentCooldownTime every 0.1 seconds.
  3. Waits 0.5 seconds, then waits (ability.cooldownTime - 0.5) * CooldownModifier.
  4. Sets isOnCooldown = false.

Important issue: cooldown state is stored on Ability ScriptableObjects. If the same asset reference is shared between players, cooldown state can leak across players unless each player has unique runtime instances. Unknown / needs manual confirmation.

Ability Levels

There is no generic ability-level system in Ability.

Ability progression is handled through several indirect mechanisms:

  • Upgrades can be taken multiple times using Upgrade.maxUpgrade and upgrade.upgradeTaken.
  • Basic stat upgrades stack through PlayerModifier.
  • Weapon level is tracked in PlayerModifier.finalWeaponLevel and changed by UpgradeList.WEAPONLEVELUP.
  • Character weapon evolution is driven by WeaponEvoData.
  • Some ability-specific counters live in AbilityHolder, such as ArcherAttackToLusterBurstAmount, MinerGroundBreakerAmount, CelestialYinSwordAmount, PriestessOrbAmount, stellarShieldBlock.
  • Evolution upgrades are handled by UpgradeEvoFunction with counter/time conditions.

Upgrades Affecting Abilities

Upgrade effects are hardcoded in UpgradeFunction.SelectUpgrade.

Examples:

  • EQUIPABILITY adds an ability to the player's AbilityHolder.abilities.
  • RS is reduced if certain ability types are present, such as ProtoBarrier, GroundBreaker, RocketBarrage, SkyYingSword.
  • WEAPONLEVELUP runs WeaponEvoData functions for the selected character.
  • DRONE spawns a companion drone and sets its master.
  • JETPROP starts Jet Propulsion logic.
  • REVIVE reactivates dead players, resets cooldown flags, and restores drone ownership.
  • EVOUPGRADE starts UpgradeEvoFunction.

Projectiles / Hitboxes / Damage

Main flow:

  1. PlayerWeapon.CheckReadyFireWeapon() checks dashing, pause, target presence, attack range, character exceptions, ammo/reload, and attack timer.
  2. PlayerWeapon.FireWeapon() sets weapon animation/audio and runs ability-specific activation checks.
  3. SpawnAttack() pulls the selected PlayerAttack prefab from PoolManager.
  4. AttackModify() copies player reference, ability flags, damage, crit state, spell amp, hit count bonuses, CC flags, vampiric flags, and character-specific changes onto PlayerAttack.
  5. PlayerAttack.OnTriggerEnter2D() applies damage/CC/effects, handles max hits, hit effects, explosions, ricochet, hydra splits, poison, vacuum, eclipse, vampiric healing, and sky sword marking.

Projectile behavior is therefore split between:

  • Weapon firing logic in PlayerWeapon.
  • Ability state in AbilityHolder.
  • Hit/effect logic in PlayerAttack.
  • Carrier behavior in PlayerAttackCarrier.
  • Enemy response in Enemy.TakeDamage.

Dependencies

  • AbilityHolder depends on Player, GameManager, GameInitialization, InputManager, AbilityFunction, PoolManager, AudioResources, Game.Audio, UIResourceProvider, PlayerWeapon, PlayerAttack, PlayerAttackCarrier, GameEvents.
  • AbilityFunction depends on Ability, Player, PoolManager, Items.ItemType.
  • PlayerWeapon depends on AbilityHolder, PlayerModifier, AbilityFunction, PoolManager, GameManager, AudioResources.
  • PlayerAttack depends on Enemy, PoolManager, AbilityFunction, GameManager.

Problems / Risks

  • AbilityHolder combines registry, runtime state, input, cooldown activators, behavior execution, character-specific logic, and VFX spawning.
  • Ability assets contain runtime cooldown state. This risks shared-state bugs.
  • Many abilities are activated by polling in Update, even passive or event-driven abilities.
  • Ability logic is split unpredictably across AbilityHolder, PlayerWeapon, PlayerAttack, and Player.TakeDamage.
  • AbilityFunction.CheckAbility scans a list each time. Many checks happen in hot paths.
  • CoroutineWorker keys are based on CurrentCoroutine.ToString() + ability.ToString(), which is fragile.
  • Some cooldowns are manually started inside ability coroutines that were also started through UseAbility, creating possible double-cooldown or timing confusion.
  • AbilityHolder methods include in-progress or placeholder behavior, such as empty StarBurstShield.
  • There is no clear ability schema for level/scaling/stat formulas.

Migration Notes for Godot

Preserve:

  • Ability ids and unlock conditions.
  • Cooldowns, stamina costs, counters, and activation triggers.
  • Character-specific play patterns:
  • Knight shield/long slash/Neo Sword.
  • Soldier Frenzy and dash reload.
  • Archer Luster/Artemis burst.
  • Miner Ground Breaker and Lucky Find.
  • Star Wanderer charge/rocket barrage.
  • Celestial sword/orb style companion logic.
  • Priestess Abyss Orb/Tidal Veil.
  • Projectile and hitbox effects: crit, poison, freeze, ricochet, explosion, vampiric, hydra split, vacuum, eclipse, shields.

Redesign target:

AbilityRegistry / AbilityRunner
- owns unlocked ability ids
- owns per-player runtime cooldown/state
- handles activation requests
- emits signals for UI
- does not contain all ability behavior

AbilityData / AbilityResource
- id/type
- name/icon/description key
- base cooldown/stamina
- tags
- base stats/scaling references

AbilityRuntimeState
- current cooldown
- stacks/counters
- active flag
- owner reference

AbilityBehavior
- one script per ability or reusable behavior family
- activation trigger: input, on_dash, on_fire, on_hit, on_damage_taken, on_level_up, timer
- executes effect using owner/context

UpgradeEffect
- modifies stats, unlocks abilities, changes ability parameters, or starts evolution conditions
- no giant switch in one file

Suggested Godot mapping:

Unity Concept Godot Direction
Ability ScriptableObject AbilityResource with static data only.
AbilityHolder.abilities AbilityRegistry with ids/resources and runtime states.
AbilityFunction.UseAbility AbilityRunner.request_activation(ability_id, context).
AbilityHolder methods Individual AbilityBehavior scripts or scenes.
Ability flags on PlayerAttack Attack modifier/effect list applied to attack context.
UpgradeFunction.EQUIPABILITY UnlockAbilityEffect.
AbilityType enum Stable string ids or generated enum-like constants.

Do not copy:

  • One massive holder script.
  • Runtime state on shared data resources.
  • Polling all passive abilities every frame.
  • Projectile effects as a large set of booleans on one attack class.

Open Questions

  • Whether all Ability assets are unique per player at runtime or shared from ScriptableObject references.
  • Which AbilityType entries are unused, deprecated, in development, or boss-only.
  • Exact intended cooldown behavior for abilities that start CooldownTimer manually inside their coroutine.
  • Whether Multishoot is spelled intentionally in code while assets/text use Multishot.
  • Unknown / needs manual confirmation.