Vitals: Shield, Barrier & Reservation
The defensive vocabulary ARPGs actually ship: a shield pool that absorbs before health and refills after a quiet period, a temporary barrier that can exceed maximum health and expires on a timer, and aura-style reservation that makes part of the health pool permanently unusable.
UCrimsonAbilitySystemComponent and a UCrimsonHealthComponent - both are created for you by ACrimsonCharacter. See Character Pipeline. The bar itself is UCrimsonPoolBarWidget from CrimsonUI; see CrimsonUI -> How-To: Layered Pool Bars for the rendering side.What lives where
| Piece | Ships in | Purpose |
|---|---|---|
UCrimsonHealthSet | CrimsonCore | Health, MaxHealth, and RESERVATION. |
UCrimsonShieldSet | CrimsonCore | Both absorb pools: Shield and Overhealth. |
UCrimsonHealthComponent | CrimsonCore | The single bind point for all of it. |
UCrimsonVitalsViewModel | CrimsonCore | Field-notify mirror of the component. |
UCrimsonCoreVitalsWidget | CrimsonCore | The player's HUD bar. |
UCrimsonPoolBarWidget | CrimsonUI | The generic layered renderer it derives from. |
Attributes
| Set | Attribute | Meaning |
|---|---|---|
UCrimsonHealthSet | HealthReservedFlat, HealthReservedPercent | What auras and similar permanent costs reserve. Percent is 0..1. |
UCrimsonHealthSet | HealthReserved | Derived final, replicated. Capped one point below MaxHealth. |
UCrimsonShieldSet | Shield, MaxShield | A standing pool. MaxShield is tiered like MaxHealth. |
UCrimsonShieldSet | MaxShieldBase / Flat / Additive / Multiplier | The tiers gear and buffs modify. |
UCrimsonShieldSet | ShieldRechargeRate, ShieldRechargeDelay | Points per second, and seconds of quiet before recharge starts. |
UCrimsonShieldSet | ShieldDecayPerSecond | Optional bleed-off for a ward build. 0 disables. |
UCrimsonShieldSet | Overhealth, MaxOverhealth | The barrier. May exceed MaxHealth. |
GetEffectiveMaxHealth() is MaxHealth - HealthReserved, and every clamp on Health uses it - not MaxHealth. That is the entire behavioral contract of reservation. MaxHealth stays the untaxed total because the bar needs both numbers to draw the dead zone.1. Grant the shield set
UCrimsonShieldSet is optional by design. Without it, UCrimsonHealthComponent reports zero for every absorb value, UCrimsonHealthSet skips the absorption chain entirely, and damage lands on health exactly as before. Most NPCs never need it.
Add it to the Granted Attributes array of the ability set your pawn uses, and fill in Default Values - order matters, because Shield is clamped to MaxShield and MaxShield is only derived once MaxShieldBase is written.
| Order | Attribute | Why |
|---|---|---|
| 1 | MaxShieldBase | Derives MaxShield through the tiered calculation. |
| 2 | Shield | Start full, or leave at 0 to watch it charge on spawn. |
| 3 | ShieldRechargeRate | Points per second. |
| 4 | ShieldRechargeDelay | Seconds of no damage before recharge begins. |
AbilitySet_CrimsonCore (/CrimsonCore/AbilitySystem/) grants UCrimsonHealthSet, UCrimsonMovementSet, UCrimsonCombatSet, UCrimsonHasteSet and UCrimsonShieldSet together. Study its Granted Attributes rows - each one pairs a set with its starting values.showdebug abilitysystem. MaxShield reads your number, not 0. Reservation needs no grant - it lives on UCrimsonHealthSet, which is already granted everywhere.2. Absorption order
Incoming damage is spent Overhealth, then Shield, then Health, in UCrimsonHealthSet::PostGameplayEffectExecute. The expiring resource goes first: a barrier that times out unused is worse for the player than a shield that has to wait to recharge.
before hit Health 328.12 Shield 150.0 Overhealth 140.0250 damageafter hit Health 328.12 Shield 40.0 Overhealth 0.0barrier absorbed 140, shield absorbed 110, health took 0
A hit that was absorbed stamps Damage.Result.Absorbed onto the effect context, and the hit that empties the shield also stamps Damage.Result.ShieldBroken, so combat text reports both without extra plumbing. The pop still shows the full incoming damage - the player wants to know what was swung at them, and the absorb tag is what says the pools ate it.
PostGameplayEffectExecute is authority-side. Clients see the resulting attribute values replicate; they never run the absorption chain themselves.3. Barriers have no timer
MaxOverhealth is the sum of the currently active barrier effects. UCrimsonShieldSet::PostAttributeChange mirrors a rise onto Overhealth - barriers arrive full - and clamps it back down on a fall - barriers leave empty. A duration gameplay effect adding capacity is therefore the entire lifetime mechanism.
Overhealth steps down twice, once per expiry.4. The recharge delay is a tag, not a timer
UCrimsonHealthComponent applies the effect in its Shield Recharge Block Effect property to the victim on every damaging hit. That effect grants Status.Shield.RechargeBlocked, and the recharge effect refuses to run while the target owns it. That pairing is the whole delay - no C++ anywhere has to know what time it is.
| Effect | Shape | Key detail |
|---|---|---|
| Recharge block | Has Duration, no modifiers | Duration is Attribute Based on ShieldRechargeDelay, Source Target, Snapshot OFF. Grants the blocked tag. |
| Recharge | Infinite, Period 0.25 | Shield Add (Base), Attribute Based on ShieldRechargeRate, Coefficient 0.25. |
| Decay (optional) | Infinite, Period 0.25 | Same, but Coefficient -0.25 against ShieldDecayPerSecond. |
Ongoing Tag Requirements -> Must Not Have Tags. Put it in Must Have Tags and recharge runs ONLY during the delay window, so the shield refills by exactly ShieldRechargeRate * ShieldRechargeDelay after each hit and then stops forever. It looks like a cap and is not one. This is the single easiest field in the system to invert.Shield in showdebug abilitysystem. It sits flat for exactly ShieldRechargeDelay, then climbs at ShieldRechargeRate per second all the way to MaxShield. If it stops short, check the tag requirement direction.5. Reservation
An aura applies an infinite effect that adds to HealthReservedPercent via SetByCaller SetByCaller.Health.ReservePercent, as a fraction 0..1. Use Add (Base), never Override - several auras have to reserve at once, and HealthReserved is derived from the running total. Removing the effect frees the capacity immediately.
6. Put the bar on the HUD
UCrimsonCoreVitalsWidget does the binding, the retry-until-the-ability-system-replicates loop, repossession handling, the trail band and the eased fill. A Blueprint child needs only the visuals.
W_CrimsonCoreVitals (/CrimsonCore/UserInterface/HUD/). Its tree is SizeBox_Root -> Box_Root -> (SizeBox_Bar -> Image_Bar) + (Box_Readout -> Text_Value + Text_Barrier), and it has zero graph nodes - Text_Value and Text_Barrier are BindWidgetOptional and are filled in by C++.| Widget name | Required | Purpose |
|---|---|---|
Image_Bar | For the bar to draw | Brush set to MI_UI_CrimsonPoolBar_Player, with an explicit brush Image Size. |
Text_Value | Optional | Health over EFFECTIVE max. Format on Value Format. |
Text_Barrier | Optional | Barrier badge. Collapses whenever Overhealth is 0. |
Register it at the HUD.Slot.Health extension point through a UCrimsonGameFeatureAction_AddWidget action, and make sure the HUD layout actually contains a CrimsonUIExtensionPoint carrying that tag - without one the widget registers successfully and appears nowhere, with no error.
7. Nameplates get the same bar
UCrimsonNameplateWidget looks for an optional child named Bar_Vitals of type CrimsonPoolBarWidget and pushes the layered snapshot straight into it - no graph wiring, and enemy plates pick up shield and barrier for free. Team color still applies: the widget calls SetHealthColor with the target's team tint.
W_CrimsonNameplateWidget (/CrimsonCore/UserInterface/Nameplates/) is SizeBox_0 -> Bar_Vitals, using MI_UI_CrimsonPoolBar_Nameplate. Use that instance, not the player one - it compiles four decorative features out of the shader, and it is the instance drawn dozens of times a frame.OnVitalsVisual still fires alongside Bar_Vitals if you want a different treatment.
Binding is retried, not assumed
Attribute sets replicate as ability-system subobjects and routinely land several frames after the pawn. UCrimsonVitalsViewModel::TryBind is idempotent and returns false until the data is really there; the widget retries on a short timer and gives up after Rebind Timeout Seconds with a warning rather than spinning forever.
MaxHealth as 'not replicated yet'.Testing
Console cheats on UCrimsonCheatManager. They are server-authoritative, so run them from the host window - a client has no cheat manager of its own (see below).
| Command | Effect |
|---|---|
VitalsDamage <n> | Applies damage through the full pipeline, so absorption and combat text run. |
VitalsHeal <n> | Applies healing through the Healing meta attribute. |
VitalsSetShield <n> | Sets MaxShieldBase and fills Shield to it. |
VitalsBarrier <amount> <duration> | Grants a barrier for that many seconds. |
VitalsReserve <percent> | Reserves that percent of MaxHealth. |
VitalsSetMaxHealth <n> | Sets MaxHealthBase. |
VitalsReport | Prints the values AND the fractions the bar should be drawing. |
CheatManager, so a null one makes every cheat name unrecognized. Two things have to be true:1. The player controller's
Cheat Class points at UCrimsonCheatManager. ACrimsonPlayerController sets this in C++ and B_PlayerController sets it in class defaults, so this is already done.2. The game mode allows cheats. Epic's
AGameModeBase::AllowCheats returns true only for NM_Standalone or the editor, which silently removes cheats from exactly the setup you need them in - a listen server or client launched outside the editor. ACrimsonGameMode overrides it to allow cheats in every net mode outside shipping builds.Clients still get no manager:
APlayerController::AddCheats only consults the game mode through GetAuthGameMode(), which is null on a client. Run cheats from the host, where the server holds authority over every player's state anyway.UCrimsonPoolBarWidget::ComputeFractions - the same function the widget uses - so if the printed fractions and the pixels disagree, the bug is in the material, not the maths.Multiplayer
- Absorption, reservation recalculation and the recharge-block application are server only - they run from
PostGameplayEffectExecute, which is authority-side. - Every attribute here is replicated; clients receive final values and never re-derive them.
RecalculateHealthReservedreturns early off authority for exactly that reason. UCrimsonVitalsViewModeland the bar are local and read-only. They are safe on a simulated proxy and never mutate gameplay.- The trail band, the eased fill and the flash are cosmetic and local. They are driven by replicated value changes, so each client animates its own.
See also
- CrimsonUI -> How-To: Layered Pool Bars - the renderer, its style and its motion.
- Nameplates - floating plates and boss bars.
- Character Pipeline - where the health component comes from.
- Testing Dummy - a target to hit while checking absorption.