09 — Stamina System (BPC_StaminaSystem)
⚡ C++ Status: Stub — Source/PG_Framework/Public/Player/BPC_StaminaSystem.h provides the UCLASS shell, MaxStamina/CurrentStamina variables, and OnExhaustionStateChanged dispatcher. The C++ stub has NO gameplay logic. Create a BP child and build ALL logic: sprint drain, action costs, exhaustion state machine, regen delay + rate, CanAffordAction(Cost) query. See docs/developer/cpp-integration-guide.md.
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 |
ActionType |
E_StaminaActionType |
Which action is being performed |
OverrideAmount |
Float |
If > 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 |
ActionType |
E_StaminaActionType |
Sprint, 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 |
ActionType |
E_StaminaActionType |
Action 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 |
Amount |
Float |
Stamina to restore |
Tags |
GameplayTagContainer |
Context 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 |
ActionType |
E_StaminaActionType |
Action to check |
- Flow:
- Look up MinStamina for this ActionType
- Return CurrentStamina >= MinStamina AND no active cooldown
SetMaxStamina → void
- Parameters:
| Param |
Type |
Description |
NewMax |
Float |
New maximum value |
bMaintainRatio |
Boolean |
Scale 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 |
Duration |
Float |
Seconds 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:
- 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
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
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.
11. Multiplayer Networking (Expanded)
Server RPCs
| RPC |
Direction |
Description |
Server_DrainStamina |
Client→Server |
Client requests stamina drain for action. Server validates MinStamina, applies. |
Server_StartContinuousDrain |
Client→Server |
Client starts sprinting. Server begins drain timer. |
Server_StopContinuousDrain |
Client→Server |
Client stops sprinting. Server stops drain, begins regen. |
Server_RestoreStamina |
Client→Server |
Client uses stamina item. Server validates item, applies restore. |
Authority Gates
Client Prediction
- Stamina bar: Client predicts decrease on sprint/jump. Server corrects via
OnRep_CurrentStamina.
- Exhaustion: ExhaustionState replicated. Client plays breathing audio locally.
- Cooldowns: Server-authoritative. Client shows cooldown timer after server confirms action.
- 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.