How-To: Set Up a Cooldown

Goal: put an ability on cooldown for a duration that augments can modify, and understand why authoring a duration on the Gameplay Effect alone does nothing.

Prerequisites
How-To: Declare an Ability Contract - the cooldown duration lives on the contract, not on the effect. Read that first if you have not authored one.
The Gameplay Effect does not hold the duration
UCrimsonGameplayAbility::ApplyCooldown calls SpecHandle.Data->SetDuration(CooldownDuration, true), which overwrites and locks whatever Duration Magnitude you authored on the asset. The number comes from the ability's Ability.Property.Cooldown contract property.

Worse, the effect is only applied at all when that number resolves above zero. An ability whose contract does not declare the property gets a duration of 0, the apply block is skipped entirely, and you get no cooldown, no warning and no log line. This is the single most common cooldown mistake in this plugin.

The two halves

PieceSuppliesDoes NOT supply
The cooldown Gameplay EffectThe granted tag that blocks re-activation, and a Duration Policy of Has Duration.The duration. It is overwritten at runtime.
The ability's contractThe duration in seconds, via a property tagged Ability.Property.Cooldown.Any blocking. Without the effect's tag nothing stops activation.

Both are required. Either one alone produces an ability that looks configured and does not go on cooldown.

Step 1 - create the cooldown effect

Create a GameplayEffect Blueprint, e.g. GE_Cooldown.

SettingValueWhy
Duration PolicyHas DurationSetDuration sets the spec's duration, but the policy decides whether a duration applies at all. Instant or Infinite and the number is ignored.
Duration Magnitudeanything - leave the defaultOverwritten by SetDuration(..., bLockDuration = true). Authoring it here is wasted effort and misleads the next reader.
Granted Tagsat least one tag, e.g. Cooldown.Ability.FireballThis is the cooldown's identity. GAS CheckCooldown blocks activation by testing whether the ASC carries it. No granted tag means the effect applies and blocks nothing.
ModifiersnoneA cooldown effect is a tag carrier, not a stat change.
Verify
Nothing observable yet. The effect is inert until an ability names it and a contract supplies a duration.

Step 2 - declare the duration on the contract

The property tag is hardcoded - it must be exactly Ability.Property.Cooldown
ApplyCooldown and GetEffectiveCooldownDuration both read CrimsonGameplayTags::Ability_Property_Cooldown, which is the native tag Ability.Property.Cooldown. This is not configurable and not matched by parent tag.

A property declared as Ability.Property.CooldownTime, Ability.Cooldown or anything else is simply never read. The contract will look correct in the editor, the ability will validate, and no cooldown will ever apply.

Open the ability's contract asset, add an entry to Properties, and set:

FieldValue
TagAbility.Property.Cooldown (exactly)
Default Valuea float property definition, set to your cooldown in seconds
Display Name"Cooldown" - what augment tooltips show
bAllowAdditive / bAllowMultiplicativeleave on so augments can shorten it
bAllowOverrideoff unless you want an augment to hard-set the value
Why the indirection exists
Routing the duration through a contract property is what makes cooldowns augment-modifiable. The value resolves through EvaluatePropertyCache, so an augment that shortens a cooldown is previewed automatically in tooltips as a before-and-after number. A duration baked into the effect asset could do neither.

Step 3 - point the ability at the effect

On the ability Blueprint's Class Defaults, set Cooldowns -> Cooldown Gameplay Effect Class to your GE_Cooldown, and confirm its Ability Contract is the one you edited in step 2.

Verify
Play and activate the ability. It should refuse to re-activate for the authored duration. Confirm with showdebug abilitysystem, which lists active effects and their remaining time on the target.

One shared effect for the whole game

You do not need a cooldown effect per ability. Author one GE_Cooldown and point every ability at it - the duration already comes from each ability's own contract, and CooldownTags gives each ability its own cooldown identity.

PieceSupplies
The shared effectDuration Policy Has Duration. Leave its Granted Tags empty.
The ability's CooldownTagsThis ability's cooldown identity, e.g. Cooldown.Ability.Fireball.
The ability's contractThe duration in seconds.

Set CooldownTags on the ability Blueprint under Class Defaults -> Crimson | Ability | Cooldown. The tags are appended to the applied spec's DynamicGrantedTags and to GetCooldownTags(), which is what CheckCooldown tests - so what is granted and what is checked can never drift apart.

Existing abilities keep working
GetCooldownTags() appends rather than replaces. An ability that got its cooldown purely from a dedicated effect's granted tags, with CooldownTags left empty, behaves exactly as before. Adopt this per ability at your own pace.
A shared effect with a granted tag is a shared cooldown
If you leave a granted tag on the shared effect, every ability using it both grants and checks that tag - so activating any one of them locks out all the rest, for that ability's duration. Casting a 10-second ability would lock the whole bar for 10 seconds.

That is almost never what you want. Leave the shared effect's Granted Tags empty and let CooldownTags carry identity. For a real global cooldown, see below - it is a different mechanism.
Required project setting (the editor offers to set it for you)
With the shared effect granting nothing, Epic's data validation raises a hard error on every ability using it - "CooldownGameplayEffectClass 'GE_Cooldown' grants no tags". It reads the effect asset directly rather than calling GetCooldownTags(), so it cannot see the CooldownTags this plugin appends. Epic's own comment on the switch names this case as the reason it exists.

The first time you open a project with CrimsonAbilitySystem enabled, it detects this and offers to write the setting for you along with the other required entries - see Quick Start. The manual equivalent, in Config/DefaultEngine.ini:

```ini
[SystemSettings]
AbilitySystem.WarnCooldownEffectWithoutTags=0
```

You are not losing the safety net. UCrimsonGameplayAbility::IsDataValid replaces it with a check that understands the design: an error only when the effect grants nothing and CooldownTags is empty - the combination that really does leave an ability with no cooldown.

Global cooldowns, silences and other lockouts

A cooldown answers "this specific ability is recharging". Anything that locks out a group of abilities - a global cooldown, a silence, a stun, being disarmed - is a different question and should not go through the cooldown system at all. Three separate mechanisms, and picking the wrong one shows up in the UI.

You wantMechanismHow
This ability recharges after useCooldownCooldownTags + contract duration, as above.
Pressing any combat ability briefly locks the others (a GCD)A separate effect + ActivationBlockedTagsAuthor GE_GlobalCooldown: Has Duration, a real duration on the asset (nothing overwrites it - it is not applied by ApplyCooldown), granting Cooldown.Global. Apply it from your ability base on activation. Every ability that respects the GCD lists Cooldown.Global in its Activation Blocked Tags.
Silenced, stunned, disarmed - a state blocks a whole class of abilitiesTag relationship mappingThe state effect grants Ability.State.Silenced; the ASC's tag relationship mapping blocks abilities carrying the matching category tag. The plugin already ships Ability.ActivateFail.Silenced so the failure widget prints the right reason. See How-To: Block and Cancel by Tag.
Do not model a silence as a cooldown
It looks like it works and then misbehaves in two visible ways. An ability-state widget reads GetCooldownTimeRemainingAndDuration, so a silence modeled as a cooldown draws a cooldown sweep on every affected slot instead of a silence treatment. And UCrimsonAbilitySystemComponent::ReduceCooldownsByTag and UCrimsonAugmentAction_ReduceCooldown operate on cooldown tags - a cooldown-reduction augment would happily cut the silence short.

Blocking tags carry no duration and are invisible to both, which is exactly right.
They compose
Nothing stops an ability from having all three: its own CooldownTags recharge, Cooldown.Global in its Activation Blocked Tags for the GCD, and a silence category tag the relationship mapping blocks. They are independent axes, and FCrimsonAbilityStateSnapshot on the ability-state widget reports them separately - bOnCooldown for the first, FailureTags for the other two.

When it does not work

OnCooldownStarted.Broadcast(CooldownDuration) fires outside the apply block, so it broadcasts even when nothing was applied. That splits the failure modes cleanly - bind it temporarily and print the value.

SymptomCauseFix
Broadcast fires with 0.0The contract does not declare Ability.Property.Cooldown, or declares it under a different tag.Step 2. Check the tag character for character.
Broadcast never firesGetCooldownGameplayEffect() returned null - no effect class is set.Step 3.
Broadcast fires with the right number, but the ability re-activates freelyThe effect grants no tag and CooldownTags is empty, so CheckCooldown has nothing to test.Set CooldownTags. UCrimsonGameplayAbility::IsDataValid flags this pairing at author time.
Cooldown applies but expires instantlyDuration Policy is Instant.Step 1, Duration Policy.
One ability's cooldown blocks an unrelated abilityBoth share a cooldown effect that grants a tag, so both grant and check that same tag.Clear the shared effect's Granted Tags and give each ability its own CooldownTags - see above. A separate effect per ability is not the fix.
Validation error: "grants no tags"Epic's check reads the effect asset and cannot see CooldownTags.Set AbilitySystem.WarnCooldownEffectWithoutTags=0 - see the callout above.

Cooldown recovery rate

The resolved duration is divided by GetCooldownRecoveryRate() before it is applied, so 2.0 halves every cooldown on that ability. It is a BlueprintNativeEvent returning 1.0 by default - override it to read a haste attribute. GetEffectiveCooldownDuration() returns the same post-rate number for UI.

Reducing a cooldown already running
The recovery rate only affects a cooldown at the moment it is applied. To shorten one already ticking, use UCrimsonAbilitySystemComponent::ReduceCooldownsByTag or the UCrimsonAugmentAction_ReduceCooldown augment action, both of which work on the granted tag - another reason the effect needs one.

See also

  • How-To: Declare an Ability Contract - where the duration property lives.
  • How-To: Add Ability Costs - the same contract-property pattern for costs.
  • How-To: Use Toggle, Charges and Channeling - the Charges shape reads the cooldown effect's first granted tag to drive recharging.
  • Concept: Modifiers and Dynamic Tags - how an augment reaches the cooldown value.
  • API Reference -> Ability lifecycle events - OnApplyCooldownTags, broadcast after the effect lands.