14 KiB
14 KiB
09 — Stamina System (BPC_StaminaSystem)
Purpose
Manages the player's stamina pool — drain during sprinting, jumping, and special actions; regeneration during idle/walking. Prevents action spamming by enforcing minimum stamina thresholds. Drives exhaustion VFX and audio cues.
Dependencies
- Requires:
GI_GameFramework(phase checks),FL_GameUtilities(tag queries) - Required By:
BPC_MovementStateSystem(stamina affects max speed),WBP_HUD(stamina bar),BPC_PlayerMetricsTracker(exhaustion tracking) - Engine/Plugin Requirements: GameplayTags, Timers
Class Info
| Property | Value |
|---|---|
| Parent Class | ActorComponent |
| Class Type | Blueprint Component |
| Asset Path | Content/Framework/Player/BPC_StaminaSystem |
| Implements Interfaces | None |
1. Enums
E_ExhaustionState
| Value | Description |
|---|---|
Normal = 0 |
Full stamina operation |
Low = 1 |
Below LowThreshold, heavy breathing, slightly reduced regen delay |
Exhausted = 2 |
Below ExhaustedThreshold, cannot sprint, slow regen |
E_StaminaActionType
| Value | Description |
|---|---|
Sprint = 0 |
Continuous drain while sprinting |
Jump = 1 |
One-time drain on jump |
Climb = 2 |
Continuous drain while climbing |
SpecialAttack = 3 |
One-time drain for strong melee |
Dodge = 4 |
One-time drain for dodging |
Environment = 5 |
Drain from external sources (cold, poison) |
2. Structs
S_StaminaDrainRate
| Field | Type | Description |
|---|---|---|
ActionType |
E_StaminaActionType |
Which action this rate applies to |
DrainPerSecond |
Float |
Stamina drained per second (continuous) |
DrainFlat |
Float |
Stamina drained once per use (one-shot) |
MinStamina |
Float |
Minimum stamina required to perform this action |
CooldownAfterUse |
Float |
Seconds before this action can be used again |
S_StaminaRegenSettings
| Field | Type | Description |
|---|---|---|
RegenRate |
Float |
Stamina recovered per second |
RegenDelay |
Float |
Seconds after last drain before regen begins |
ExhaustedRegenRate |
Float |
Slower recovery while exhausted |
ExhaustedRegenDelay |
Float |
Longer delay while exhausted |
RegenBlockedWhileMoving |
Boolean |
If true, regen pauses while moving above walk speed |
3. Variables
Configuration (Instance Editable, Expose On Spawn)
| Variable | Type | Default | Category | Description |
|---|---|---|---|---|
MaxStamina |
Float |
100.0 |
Stamina Config |
Maximum stamina pool |
DefaultDrainRates |
Array<S_StaminaDrainRate> |
[] |
Stamina Config |
Base drain rates for each action type |
RegenSettings |
S_StaminaRegenSettings |
(15.0, 2.0, 5.0, 4.0, true) |
Stamina Config |
Regeneration behaviour |
LowThreshold |
Float |
0.30 |
Stamina Config |
Fraction of MaxStamina for Low state |
ExhaustedThreshold |
Float |
0.10 |
Stamina Config |
Fraction of MaxStamina for Exhausted state |
bCanExhaust |
Boolean |
true |
Stamina Config |
Allow exhaustion state |
Internal (Private / Protected, No Expose)
| Variable | Type | Default | Category | Description |
|---|---|---|---|---|
CurrentStamina |
Float |
100.0 |
Stamina State |
Current stamina pool |
ExhaustionState |
E_ExhaustionState |
Normal |
Stamina State |
Current exhaustion tier |
ActiveDrains |
Map<E_StaminaActionType, bool> |
{} |
Stamina State |
Which drains are currently active |
RegenTimerHandle |
FTimerHandle |
- |
Stamina State |
Timer for regen ticks |
DrainTimerHandle |
FTimerHandle |
- |
Stamina State |
Timer for continuous drain ticks |
LastDrainTime |
Float |
0.0 |
Stamina State |
World time of last stamina drain |
bRegenBlocked |
Boolean |
false |
Stamina State |
True while external block is active |
ActionCooldownTimers |
Map<E_StaminaActionType, FTimerHandle> |
{} |
Stamina State |
Per-action cooldown timers |
Replicated (if multiplayer)
| Variable | Type | Condition | Description |
|---|---|---|---|
CurrentStamina |
Float |
RepNotify |
Replicated with OnRep handler for UI updates |
ExhaustionState |
E_ExhaustionState |
RepNotify |
Replicated exhaustion tier |
4. Functions
Public Functions
DrainStamina → Boolean (success)
- Description: Attempts to drain stamina for a specific action. Returns false if insufficient stamina or cooldown active.
- Parameters:
Param Type Description ActionTypeE_StaminaActionTypeWhich action is being performed OverrideAmountFloatIf > 0, use this instead of the DrainRate config value - Blueprint Authority: Server (if MP), Any (single-player)
- Flow:
- If ActionCooldownTimers contains ActionType and timer active: return false
- Look up S_StaminaDrainRate for ActionType
- RequiredStamina = DrainRate.MinStamina
- If CurrentStamina < RequiredStamina: return false
- DrainAmount = OverrideAmount > 0 ? OverrideAmount : DrainRate.DrainFlat
- CurrentStamina = FMath::Max(0, CurrentStamina - DrainAmount)
- Start cooldown timer for DrainRate.CooldownAfterUse
- Update ExhaustionState
- Reset regen delay, stop regen if running
- Fire OnStaminaDrained
- If ActionType is continuous: add to ActiveDrains
- Fire OnStaminaChanged
- Return true
StartContinuousDrain → void
- Description: Begins draining stamina per second for a continuous action.
- Parameters:
Param Type Description ActionTypeE_StaminaActionTypeSprint, Climb, etc. - Flow:
- Add ActionType to ActiveDrains = true
- If DrainTimerHandle not active: start timer with interval 0.1 seconds
- Timer callback: apply (DrainRate.DrainPerSecond * 0.1) to CurrentStamina each tick
- If CurrentStamina <= 0: stop continuous drain, fire OnExhausted
StopContinuousDrain → void
- Parameters:
Param Type Description ActionTypeE_StaminaActionTypeAction to stop draining for - Flow:
- Set ActiveDrains[ActionType] = false
- If no active drains remain: clear DrainTimerHandle
- Start regen delay timer
RestoreStamina → Float (actual restored)
- Description: Adds stamina up to MaxStamina.
- Parameters:
Param Type Description AmountFloatStamina to restore TagsGameplayTagContainerContext tags (e.g. Potion, Rest) - Flow:
- Previous = CurrentStamina
- CurrentStamina = FMath::Min(MaxStamina, CurrentStamina + Amount)
- Update ExhaustionState
- Fire OnStaminaRestored
- Fire OnStaminaChanged
- Return CurrentStamina - Previous
GetStaminaNormalised → Float [0.0 - 1.0]
- Flow: Return CurrentStamina / MaxStamina
CanAffordAction → Boolean
- Parameters:
Param Type Description ActionTypeE_StaminaActionTypeAction to check - Flow:
- Look up MinStamina for this ActionType
- Return CurrentStamina >= MinStamina AND no active cooldown
SetMaxStamina → void
- Parameters:
Param Type Description NewMaxFloatNew maximum value bMaintainRatioBooleanScale current stamina to maintain ratio - Flow:
- If bMaintainRatio: CurrentStamina = (CurrentStamina / MaxStamina) * NewMax
- Else: CurrentStamina = FMath::Min(CurrentStamina, NewMax)
- MaxStamina = NewMax
- Update ExhaustionState
- Fire OnStaminaChanged
BlockRegen → void
- Parameters:
Param Type Description DurationFloatSeconds to block regen - Flow:
- Set bRegenBlocked = true
- Start timer for Duration
- On timer end: bRegenBlocked = false
Protected / Private Functions
UpdateExhaustionState → void
- Flow:
- Normalised = CurrentStamina / MaxStamina
- OldState = ExhaustionState
- If Normalised <= ExhaustedThreshold: NewState = Exhausted
- Else if Normalised <= LowThreshold: NewState = Low
- Else: NewState = Normal
- If NewState != OldState:
- ExhaustionState = NewState
- Fire OnExhaustionStateChanged
- If NewState == Exhausted: fire OnExhausted
RegenTick → void
- Flow:
- If bRegenBlocked: return
- Rate = ExhaustionState == Exhausted ? RegenSettings.ExhaustedRegenRate : RegenSettings.RegenRate
- CurrentStamina = FMath::Min(MaxStamina, CurrentStamina + Rate * 0.1)
- Fire OnStaminaChanged
- If CurrentStamina >= MaxStamina: stop regen timer, fire OnStaminaFullyRestored
OnMovementStateChanged (listener) → void
- Flow:
- Get movement mode from character
- If RegenSettings.RegenBlockedWhileMoving and movement speed > walk:
- Pause regen timer
- Else: resume regen timer if conditions met
5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|---|---|---|---|
OnStaminaChanged |
float OldStamina, float NewStamina, float Delta |
Public |
Fired on any stamina change |
OnStaminaDrained |
E_StaminaActionType ActionType, float Amount |
Public |
Fired when stamina is consumed |
OnStaminaRestored |
float Amount, GameplayTagContainer Tags |
Public |
Fired when stamina is gained |
OnStaminaFullyRestored |
none |
Public |
Fired when stamina reaches MaxStamina |
OnExhaustionStateChanged |
E_ExhaustionState OldState, E_ExhaustionState NewState |
Public |
Fired on any exhaustion tier change |
OnExhausted |
none |
Public |
Fired when entering Exhausted state |
OnActionCooldownStarted |
E_StaminaActionType ActionType, float Cooldown |
Public |
Fired when a per-action cooldown begins |
OnActionCooldownEnded |
E_StaminaActionType ActionType |
Public |
Fired when a per-action cooldown expires |
6. Overridden Events / Custom Events
Event: BeginPlay
- Description: Initialises stamina, starts regen if enabled, binds to movement state changes.
- Flow:
- CurrentStamina = MaxStamina
- Start regen timer with initial delay = 0
- Find BPC_MovementStateSystem on owner, bind to its OnMovementStateChanged dispatcher
- Initialise ExhaustionState = Normal
Event: OnComponentDestroyed
- Description: Clean up all timers.
- Flow:
- Clear RegenTimerHandle, DrainTimerHandle
- Clear all ActionCooldownTimers
7. Blueprint Graph Logic Flow
flowchart TD
A[DrainStamina called] --> B{CanAffordAction?}
B -->|No| C[Return false]
B -->|Yes| D[Apply drain amount]
D --> E[Update CurrentStamina]
E --> F{ExhaustedThreshold crossed?}
F -->|Yes| G[Set Exhausted state]
F -->|No| H{LowThreshold crossed?}
H -->|Yes| I[Set Low state]
H -->|No| J[Keep Normal state]
G --> K[Fire OnExhausted]
K --> L[Stop continuous drains]
I --> M[Fire OnExhaustionStateChanged]
J --> M
L --> M
M --> N[Start regen delay]
N --> O[Fire OnStaminaChanged]
O --> P[Return true]
D --> Q{Continuous action?}
Q -->|Yes| R[Start DrainTimer loop]
Q -->|No| S[Start action cooldown]
R --> O
S --> O
8. Communication Matrix
| Who Talks | How | What Is Sent |
|---|---|---|
BPC_StaminaSystem |
Dispatcher |
OnStaminaChanged -> WBP_HUD (stamina bar update) |
BPC_StaminaSystem |
Dispatcher |
OnExhausted -> BPC_PlayerController (player feedback), BPC_AdaptiveEnvironment |
BPC_StaminaSystem |
Dispatcher |
OnExhaustionStateChanged -> ABP_GASP (breathing animation) |
External (PC_PlayerController) |
Direct |
Calls DrainStamina(Sprint) / StartContinuousDrain on input |
BPC_StaminaSystem |
Dispatcher |
OnStaminaFullyRestored -> WBP_HUD (hide bar) |
BPC_StaminaSystem |
Listener |
Binds to BPC_MovementStateSystem.OnMovementStateChanged for regen blocking |
9. Validation / Testing Checklist
- DrainStamina deducts correct amount and returns false when stamina is insufficient
- Continuous drain stops when stamina reaches 0
- Regen does not start until RegenDelay has passed
- Exhausted state correctly uses ExhaustedRegenRate and ExhaustedRegenDelay
- Exhaustion state transitions are one-way downward until full rest
- Per-action cooldowns prevent spamming dodge/jump
- BlockRegen pauses all regen for its duration
- RegenBlockedWhileMoving pauses regen at high speed, resumes when walking
- Edge case: MaxStamina change with bMaintainRatio=true preserves percentage
- Edge case: Multiple simultaneous continuous drains stack correctly
- Edge case: DrainStamina called during active cooldown returns false without state change
- All timers cleaned up on component destroy
10. Reuse Notes
- Can be placed on AI characters with simplified config (e.g. only Sprint drain, no Exhaustion).
- For enemy stamina, consider removing the exhaustion mechanic and using only drain/restore.
- The drain rate map supports runtime modification for buffs/debuffs (e.g. Adrenaline reduces sprint cost).
- UI widgets should bind to OnStaminaChanged for smooth bar animation, not tick polling.
- For multiplayer: Server authorises all DrainStamina calls; client predicts and corrects on repnotify.
- Exhaustion state can drive conditional audio: heavy breathing, heart-beat, fatigue voice lines.
Blueprint Spec: Stamina System. Conforms to TEMPLATE.md v1.0 — part of the UE5 Modular Game Framework.