Skip to content

GAS Overview

Summary: Project Eternal uses the Gameplay Ability System (GAS) as the foundation for all character actions, abilities, status effects, and attributes. This document explains the architectural decisions, component relationships, and data flow patterns.


Why GAS?

GAS provides a production-ready framework for: - Predictive Networking: Client-side prediction with server authority - Attribute Replication: Automatic sync of stats across clients - Effect Stacking: Complex buff/debuff interactions out of the box - Tag-Based Filtering: Abilities blocked/allowed by gameplay tags

Project Eternal extends GAS with custom input binding, Blueprint delegates for UI, and a consolidated attribute set.


Architecture

ASC Ownership Model

+---------------------------+
|   AEternalPlayerState     |  <-- Owner Actor (persists across respawn)
|   (Owner)                 |
+---------------------------+
           |
           | owns
           v
+---------------------------+
| UEternalAbilitySystemComponent |
|   - Manages abilities     |
|   - Tag-based input       |
|   - UI delegates          |
+---------------------------+
           |
           | contains
           v
+---------------------------+
|   UEternalAttributeSet    |
|   - All character stats   |
|   - Replicated values     |
+---------------------------+

Why PlayerState? - Survives pawn death/respawn - Proper multiplayer replication - Consistent ability state across character swaps

Input Flow

Enhanced Input Action
        |
        v
UEternalInputSubsystem
        |
        | Converts to GameplayTag
        v
AbilityInputTagHeld(Tag)
        |
        | Finds abilities with matching DynamicAbilityTags
        v
TryActivateAbility(Handle)

Component API

UEternalAbilitySystemComponent

Method Purpose
AddCharacterAbilities(TArray<TSubclassOf<UGameplayAbility>>) Grants startup abilities with input tags
AbilityInputTagHeld(FGameplayTag) Activates abilities matching the input tag
AbilityInputTagReleased(FGameplayTag) Notifies abilities of input release
GetSpecByHandle(FGameplayAbilitySpecHandle) Retrieves ability spec for UI display

Delegates

Delegate Fires When Use Case
OnAbilityGranted Any ability is given Skillbar updates
OnAbilityRemoved Any ability is removed Skillbar cleanup
EffectAssetTags GE with asset tags applied UI notifications

Attribute Categories

Organization

+------------------+     +------------------+     +------------------+
|  Hard Attributes |     |  Vital Attributes|     | Recovery Rates   |
|  (Primary Stats) |     |  (Resources)     |     |                  |
+------------------+     +------------------+     +------------------+
| Ferocity         |     | Health/MaxHealth |     | HealthRecoveryRate    |
| Grace            |     | Stamina/MaxStamina|    | StaminaRecoveryRate   |
| Insight          |     | Resonance/MaxRes |     | ResonanceRecoveryRate |
| Clarity          |     | MaxAdrenaline    |     |                       |
| Feral            |     +------------------+     +------------------+
| Dread            |
+------------------+

+------------------+     +------------------+     +------------------+
| Soft Attributes  |     | Combat Attributes|     | Armor/Resistance |
| (Secondary)      |     |                  |     |                  |
+------------------+     +------------------+     +------------------+
| Resolve          |     | MovementSpeed    |     | Armor            |
| Presence         |     | Poise/MaxPoise   |     | ArmorPenetration |
+------------------+     | PoiseRecoveryRate|     | ResistanceFire   |
                         | BaseDamage       |     | ResistanceCorruption |
                         +------------------+     | ResistanceElectric |
                                                  +------------------+

+------------------+     +------------------+     +------------------+
| Critical Strike  |     | Area Modifiers   |     | Cost Modifiers   |
|                  |     |                  |     |                  |
+------------------+     +------------------+     +------------------+
| CritStrikeChance |     | DamageDealtMult  |     | StaminaCostMult  |
| CritStrikeDamage |     | DamageTakenMult  |     | ResonanceCostMult|
+------------------+     +------------------+     +------------------+

+----------------------------+     +-----------------------------+
| Increased Damage Buckets   |     | Conditional Damage vs Status|
| (additive %, base 0)       |     | (additive %, base 0)        |
+----------------------------+     +-----------------------------+
| IncreasedPhysicalDamage    |     | ConditionalDamageVsBleeding |
| IncreasedFireDamage        |     | ConditionalDamageVsIgnited  |
| IncreasedCorruptionDamage  |     | ConditionalDamageVsPoisoned |
| IncreasedElectricDamage    |     | ConditionalDamageVsShocked  |
| IncreasedDamageOverTime    |     +-----------------------------+
| IncreasedStatusDuration    |
+----------------------------+

Attribute Purposes

Category Attributes Role in Gameplay
Hard Ferocity, Grace, Insight, Clarity, Feral, Dread Core stats from character build
Vital Health, Stamina, Resonance Consumable resources
Recovery *RecoveryRate variants Passive regen speeds
Combat MovementSpeed, Poise, BaseDamage Combat pacing, NPC damage scaling (no AttackSpeed attribute — attack speed is weapon-local: weapon base rate + combo ramp)
Critical Strike CriticalStrikeChance, CriticalStrikeDamage Crit chance (%) and bonus damage multiplier
Defense Armor, Resistances (Fire, Corruption, Electric) Damage reduction
Area Modifiers DamageDealtMultiplier, DamageTakenMultiplier Dungeon area modifiers (base 1.0)
Cost Modifiers StaminaCostMultiplier, ResonanceCostMultiplier Ability cost scaling (base 1.0)
Increased Damage IncreasedPhysical/Fire/Corruption/Electric, IncreasedDamageOverTime, IncreasedStatusDuration Additive-summed % buckets folded into the per-type damage line — see Damage Execution
Conditional vs Status ConditionalDamageVsBleeding/Ignited/Poisoned/Shocked Flat % that joins the matching per-type Increased line only when the target carries the matching Status.* tag — see Conditional Modifiers
Meta IncomingDamage, IncomingCritical Damage pipeline (not replicated)

Note — IncreasedStatusDuration is not a damage bucket. Although it lives in the same Stats.Offensive.Increased.* family, the damage ExecCalc never reads it. It is consumed source-side by UOnHitAbility (via DurationMultiplierAttribute) to extend the duration of status effects the wearer inflicts — distinct from the per-type Increased buckets the ExecCalc folds into the damage line. See Damage Execution.

Replication

All gameplay-relevant attributes use REPNOTIFY_Always to ensure UI updates even when the value is unchanged (e.g., clamped at max).


Tag-to-Attribute Mapping

The AttributeSet maintains a runtime map for dynamic lookups:

FGameplayTag                              -> FGameplayAttribute Getter
--------------------------------------------|--------------------------
Stats.Attributes.Hard.Ferocity             -> GetFerocityAttribute
Stats.Attributes.Hard.Grace                -> GetGraceAttribute
Stats.Resources.Health                     -> GetHealthAttribute
Stats.Resources.Stamina                    -> GetStaminaAttribute
Stats.Offensive.Increased.Fire             -> GetIncreasedFireDamageAttribute
Stats.Offensive.ConditionalDamage.VsBleeding -> GetConditionalDamageVsBleedingAttribute
...

Why? Allows GameplayEffects to target attributes by tag rather than hardcoded references, enabling data-driven stat modifications. The Increased and Conditional buckets are resolved the same dynamic way — a rolled Stats.Offensive.* modifier never names a C++ attribute; the TagsToAttributes map turns its tag into the matching getter at apply time.


Character Class Configuration

Class Defaults

Class Description Starting Focus
Acolyte Religious scholar Insight, Clarity
Artist Creative combatant Grace, Presence
Commoner Everyman survivor Balanced stats
Weaver Magical artisan Insight, Feral

Each class defines: - Primary attribute GE (base stats) - Secondary attribute GE (derived stats) - Starter Ability Kit (StartupKit) - Starting item manifests

Ability granting is kit-based. The legacy flat "startup abilities array" was replaced by an Ability Kit (UEternalAbilitySet) referenced from the class row's StartupKit, granted server-side at possession. Abilities reach an ASC from three layers in order — class starter kit, preset kits, then item abilities — with input-slot collisions resolving last-granted-wins. See Ability Kits for the data model, the server-authority grant path, and the possession-race latch that grants each kit exactly once per session.


Dynamic GameplayEffect Pattern

Equipment, dungeon modifiers, and glyphs all apply stat modifications via runtime-created UGameplayEffect objects rather than Blueprint GE classes. This allows data-driven modifier definitions to target any attribute by tag without requiring a separate Blueprint asset per modifier.

How It Works

Modifier Source (Equipment, Dungeon, Glyph)
    ├─ Resolve FGameplayTag → FGameplayAttribute via TagsToAttributes map
    ├─ Create UGameplayEffect at runtime (NewObject on ASC)
    ├─ Set DurationPolicy = Infinite
    ├─ Add FGameplayModifierInfo with:
    │   ├─ EModifierOperation::Flat → EGameplayModOp::Additive
    │   └─ EModifierOperation::Increased → EGameplayModOp::Multiplicative (bias 1.0)
    └─ ApplyGameplayEffectToSelf → ActiveEffectHandle for cleanup

Why Dynamic GEs?

Concern Blueprint GEs Dynamic GEs
New modifier type New Blueprint asset New data row only
Attribute targeting Hardcoded per asset Resolved by tag at runtime
Magnitude Fixed or SetByCaller Set directly from rolled value
Asset count One per modifier family Zero — all created at runtime

Systems Using This Pattern

System Component Applies When
Equipment FEquipmentFragment Item equipped
Dungeon UDungeonModifierComponent Player enters dungeon
Glyph UPlayerGlyphComponent Glyph socketed

Helper: UEternalAbilitySystemLibrary::ApplyDynamicModifierEffect()


Data Flow Examples

Ability Activation

1. Player presses attack button
2. InputSubsystem -> AbilityInputTagHeld("InputTag.Attack.Primary")
3. ASC iterates ActivatableAbilities
4. Finds ability with matching DynamicAbilityTags
5. Calls AbilitySpecInputPressed + TryActivateAbility
6. Ability activates, plays montage, waits for events

Attribute Modification

1. GameplayEffect applies to target
2. Modifier evaluated (base value + modifiers)
3. PreAttributeChange() - validation/clamping
4. Value updated on AttributeSet
5. PostGameplayEffectExecute() - side effects
6. OnRep_* fires on clients for UI update

Source Reference

Component Location
EternalAbilitySystemComponent Source/ProjectEternal/Public/AbilitySystem/EternalAbilitySystemComponent.h
EternalAttributeSet Source/ProjectEternal/Public/AbilitySystem/EternalAttributeSet.h
CharacterClassInfo Source/ProjectEternal/Public/AbilitySystem/Data/CharacterClassInfo.h
Tag-to-Attribute Map EternalAttributeSet.cpp:Constructor
Dynamic GE Helper Source/ProjectEternal/Public/Utils/EternalAbilitySystemLibrary.h
Modifier Operations Source/ProjectEternal/Public/Inventory/Modifiers/ModifierDefinitions.hEModifierOperation


Refactoring Considerations

Issue Current State Recommendation
Large AttributeSet 30+ attributes in one class Split into focused sets (Vital, Combat, Poise, Resistance)
EffectAssetTags delegate FEffectAssetTags (non-dynamic multicast), limited Blueprint use Convert to a BlueprintAssignable dynamic multicast delegate
Input tag iteration Scans all abilities per input Cache TMap<FGameplayTag, TArray<FGameplayAbilitySpecHandle>>

Recent Changes

Date Change Impact
2026-06-17 Theorycraft attribute wave Added Increased Damage buckets (Physical/Fire/Corruption/Electric, DamageOverTime, StatusDuration) and Conditional Damage vs Status (Bleeding/Ignited/Poisoned/Shocked); all resolved via TagsToAttributes from Stats.Offensive.Increased.* / Stats.Offensive.ConditionalDamage.Vs*
2026-02 Dynamic GE pattern Runtime-created GEs replace Blueprint GEs for equipment, dungeon, glyph modifiers
2026-02 EModifierOperation enum Flat (GAS Additive) and Increased (GAS Multiplicative with bias 1.0)
2026-02 Critical strike attributes CriticalStrikeChance, CriticalStrikeDamage, IncomingCritical meta attribute
2026-02 Area modifier attributes DamageDealtMultiplier, DamageTakenMultiplier (base 1.0, used by dungeon modifiers)
2026-02 Cost modifier attributes StaminaCostMultiplier, ResonanceCostMultiplier (base 1.0)
2026-02 Added BaseDamage attribute NPC damage scaling via Balance System
2025-Q1 Trimmed to 3 resistances Fire, Corruption, Electric
2024-Q4 Added Poise attribute system New MaxPoise, PoiseRecoveryRate attributes
2024-Q4 Blueprint delegates for UI OnAbilityGranted/Removed now BlueprintAssignable
2024-Q3 Tag-based input binding Replaced enum-based InputID system