> For the complete documentation index, see [llms.txt](https://morningheartgames.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://morningheartgames.gitbook.io/docs/gameplay-ability-toolkit/build-with-it/07-cookbook.md).

# Cookbook

> Online doc: <https://morningheartgames.gitbook.io/docs/gameplay-ability-toolkit/recipes/07-cookbook?fallback=true>

These recipes solve common gameplay problems with the public toolkit surface. Each recipe names what to author, what to write in code, and what to verify.

Use the cookbook after you have run the Basic Tutorial or built the smallest actor/effect setup in [Getting Started](/docs/gameplay-ability-toolkit/try-it/01-getting-started.md). Recipes are intended to be copied into a project one behavior at a time.

## How To Read A Recipe

Each recipe follows the same structure:

| Part   | Meaning                                       |
| ------ | --------------------------------------------- |
| Goal   | The player-facing or system-facing behavior.  |
| Author | Assets and Inspector setup.                   |
| Code   | Minimal runtime glue.                         |
| Verify | Play Mode or test checks that prove it works. |

When adapting a recipe, keep the first version small. Get one actor, one ability/effect, and one verification signal working before adding UI polish, cues, stacking, or save/load support.

## Copy This If You Need

| Need                                | Recipe                              | Best first use                                                      |
| ----------------------------------- | ----------------------------------- | ------------------------------------------------------------------- |
| Show cooldown feedback              | Cooldown HUD Ring                   | You already have an ability and need UI feedback.                   |
| Damage that depends on target state | Damage With Armor Mitigation        | Damage math depends on armor, shields, stance, or other actor data. |
| Hold and release input              | Charge-Up Ability                   | Player charges a skill before firing.                               |
| Stacking status damage              | DoT With Stack Refresh              | Status effects need duration, period, and stack UI.                 |
| World-space targeting               | Ground-Targeted AoE                 | Ability chooses a target point or area.                             |
| Held/channel behavior               | Channeled Ability With Cancellation | Ability runs until input release or cancellation.                   |
| Save owned gameplay state           | Save and Restore Actor State        | You need game-owned persistence around ASC state.                   |

## Editor Preparation

Before using any recipe:

1. Open `Tools > Gameplay Ability Toolkit > Dashboard > Setup`.
2. Confirm the status strip says **READY**.
3. Confirm your tag, cue, and input libraries are assigned.
4. Create or select folders for `Abilities/`, `Effects/`, `Attributes/`, `Inputs/`, and `Cues/`.
5. Use the `GA_`, `GE_`, `ASDef_`, `AS_`, and `GC_` prefixes consistently.

![Unity Editor screenshot of the dashboard Setup page after project assets are configured.](/files/DhEqLyp4g8f3INfnUM9i)

### Shared Editor Click Paths

Use these paths throughout the recipes:

| Goal                    | Click path                                                                                |
| ----------------------- | ----------------------------------------------------------------------------------------- |
| Open setup dashboard    | `Tools > Gameplay Ability Toolkit > Dashboard > Setup`                                    |
| Create ability asset    | `Project window > Right-click folder > Create > Gameplay Ability Toolkit > ...`           |
| Create effect asset     | `Project window > Right-click folder > Create > Gameplay Ability Toolkit > Effect`        |
| Create tag/input assets | `Tools > Gameplay Ability Toolkit > Dashboard`, then use the matching page or create menu |
| Run PlayMode tests      | `Window > General > Test Runner`, then select PlayMode                                    |

When a recipe says "assign this asset," prefer assigning the actual asset reference in the Inspector over looking it up by name in runtime code. That keeps content auditable in GitBook and Unity.

### Minimal Test Scene

For a recipe that does not already live in Tutorial or Arena:

1. Create two GameObjects named `Source` and `Target`.
2. Add an `AbilitySystemComponent` to both.
3. Assign the same starter attribute set to both.
4. Grant only the ability/effect being tested.
5. Add a small UI text or event log component only after the gameplay rule works.

## Cooldown HUD Ring

Goal: render a radial fill while an ability is on cooldown.

Author:

* An ability with a cooldown value.
* A UI `Image` configured as a filled radial image.

Editor setup:

1. Select the ability asset.
2. Set its cooldown duration.
3. Assign cooldown tags if the UI or activation rules need them.
4. Create a UI `Image`.
5. Set the image type to filled/radial.
6. Assign the image and ability to the presenter script.

Code:

```csharp
using GameplayAbilityToolkit.Core;
using UnityEngine;
using UnityEngine.UI;

public sealed class CooldownRing : MonoBehaviour
{
    public AbilitySystemComponent Asc;
    public AbilityDefinition Ability;
    public Image Ring;

    private void LateUpdate()
    {
        Ring.fillAmount = Asc.TryGetAbilityCooldownNormalized(Ability, out float normalized)
            ? normalized
            : 0f;
    }
}
```

Verify:

* `fillAmount` is `1` when cooldown starts.
* `fillAmount` reaches `0` when the ability is ready.
* Activation failure UI listens to `EventAbilityActivationFailed` if you want feedback when cooldown blocks activation.

Common mistakes:

* Reading cooldown from the wrong ability asset.
* Updating UI in `Update` before the ASC has initialized.
* Forgetting that normalized cooldown means `1 = on cooldown` and `0 = ready`.

Editor verification tips:

* Select the ability asset and confirm the cooldown value is on the ability you are activating.
* Select the player actor and confirm the HUD script references the same `AbilitySystemComponent`.
* Trigger activation failure while on cooldown and confirm your UI does not reset early.

## Damage With Armor Mitigation

Goal: write raw damage into a derived `Damage` attribute, then let an actor hook subtract mitigated damage from `Health`.

Author:

* Attributes: `Health`, `Armor`, and derived `Damage`.
* An instant effect that adds raw damage to `Damage`.

Editor setup:

1. Add `Health`, `Armor`, and `Damage` to an attribute set definition.
2. Configure `Damage` as derived/reset behavior according to your attribute design.
3. Create `GE_RawDamage`.
4. Add one modifier writing positive raw damage into `Damage`.
5. Add `ArmorMitigationHook` to the target actor.
6. Assign `DamageId`, `HealthId`, and `ArmorId`.

Code:

```csharp
using GameplayAbilityToolkit.Core;
using UnityEngine;

public sealed class ArmorMitigationHook : MonoBehaviour, IAttributeChangeHook
{
    public AttributeId DamageId;
    public AttributeId HealthId;
    public AttributeId ArmorId;

    public float OnBeforeAttributeChange(AttributeId id, float newValue, float currentValue)
    {
        return newValue;
    }

    public float OnBeforeBaseValueChange(AttributeId id, float newBase, float currentBase)
    {
        return newBase;
    }

    public void OnAfterEffectExecuted(in EffectExecutionContext ctx)
    {
        if (!ctx.TargetAsc.TryGetAttributeCurrentValue(DamageId, out float damage)) return;
        if (damage <= 0f) return;

        ctx.TargetAsc.TryGetAttributeCurrentValue(ArmorId, out float armor);
        float mitigated = damage * Mathf.Max(0f, 1f - armor * 0.01f);

        float currentBase = ctx.TargetAsc.TryGetAttributeBaseValue(HealthId, out float health)
            ? health
            : 0f;

        ctx.TargetAsc.RuntimeAttributes.ModifyAttributeBase(HealthId, currentBase - mitigated);
    }
}
```

Verify:

* Raw `Damage` resets after execution if configured as a derived attribute.
* Higher `Armor` lowers the Health loss.
* The hook is on the target actor or a child discovered by the target ASC.

Use this pattern when mitigation depends on the actor. If mitigation belongs to the damage effect itself, use an `EffectCalculation` or custom magnitude calculator instead.

Editor verification tips:

* Put `ArmorMitigationHook` on the target actor, not the source actor, when the target owns mitigation.
* Assign `DamageId`, `HealthId`, and `ArmorId` from the same attribute definition.
* Test three values: `Armor = 0`, a middle value such as `25`, and a high value that should clamp mitigation.
* Watch the target's `Health` base value after the hook applies, not only the transient `Damage` value.

## Charge-Up Ability

Goal: press to start charging, release to fire with strength based on hold duration.

Author:

* Ability bound to input press.
* Input binding that can report release.
* Runtime policy `InstancedPerActor` or `InstancedPerExecution`.

Editor setup:

1. Create or select the charge ability asset.
2. Bind it to input press.
3. Ensure the input source also reports release.
4. Choose `InstancedPerActor` if the actor cannot overlap charges.
5. Choose `InstancedPerExecution` if overlapping charge activations are allowed.

Code:

```csharp
using GameplayAbilityToolkit.Core;
using UnityEngine;

public sealed class ChargeShotRuntime : AbilityRuntime
{
    protected override async void OnActivate(GameplayEventData eventData)
    {
        float startTime = Time.time;
        await AbilityTask_WaitInputReleased.Wait(this);

        float chargedSeconds = Mathf.Min(Time.time - startTime, 2f);
        FireProjectile(damage: 10f * (1f + chargedSeconds));

        EndAbility();
    }
}
```

Verify:

* Releasing input ends the await.
* Cancelling the ability stops the charge without firing.
* Damage caps at your intended maximum hold time.

Add UI later. First verify that the runtime receives input release and ends the ability correctly.

Editor verification tips:

* In the input source component, confirm the logical input name sends both press and release.
* In the ability asset, confirm instancing is not shared in a way that lets one actor overwrite another actor's charge time.
* In Play Mode, cancel the ability through another rule and verify it does not fire after cancellation.

## DoT With Stack Refresh

Goal: apply a burn that ticks every second, stacks to three, and refreshes duration when reapplied.

Author an `EffectDefinition`:

| Field                  | Value             |
| ---------------------- | ----------------- |
| Type                   | `Duration`        |
| Duration               | `4`               |
| Period                 | `1`               |
| Modifier               | `Health Add -5`   |
| Max stack              | `3`               |
| Refresh on application | Enabled           |
| Tag                    | `Effect.DoT.Burn` |

Editor setup:

1. Open `Tools > Gameplay Ability Toolkit > Dashboard > Effects`.
2. Create or select `GE_BurnDoT`.
3. Set type to `Duration`.
4. Set duration and period.
5. Add the Health damage modifier.
6. Set stack policy and max stack.
7. Add `Effect.DoT.Burn` as a granted or asset tag according to your rule.
8. Apply the effect from an attack ability.

HUD code:

```csharp
asc.EventEffectStackChanged += (spec, oldCount, newCount) =>
{
    if (spec.Effect == burnDefinition) UpdateBurnPips(newCount);
};

asc.EventEffectRemoved += spec =>
{
    if (spec.Effect == burnDefinition) UpdateBurnPips(0);
};
```

Verify:

* First application creates one stack.
* Reapplication increases stack count until three.
* Duration refreshes on successful reapplication.
* UI returns to zero when the effect expires or is removed.

If the first hit damages but later hits do not stack, inspect the stacking key policy before changing modifier math.

Editor verification tips:

* Use the Effects dashboard or Project search to open the exact `GE_BurnDoT` asset referenced by the attack.
* Confirm the effect type is `Duration`; instant effects cannot tick over time.
* Confirm period is greater than zero if you expect repeated damage ticks.
* Confirm max stack and refresh policy before tuning damage numbers.
* Use the event log to separate "effect reapplied" from "stack changed."

## Ground-Targeted AoE

Goal: aim a ground reticle, confirm, then apply an effect to all targets in the area.

Author:

* Ability with `InstancedPerExecution`.
* `GroundAOETargetIndicator` prefab.
* `MultiTargetCollector` or subclass.
* One or more effects to apply to targets.

Editor setup:

1. Create a ground indicator prefab.
2. Create or configure a multi-target collector.
3. Assign layer masks and radius/shape values.
4. Assign the collector and effects to the ability asset.
5. Bind confirm/cancel inputs if the ability waits for user confirmation.

Runtime flow:

```mermaid
flowchart LR
    Activate["Activate ability"]
    Indicator["Spawn indicator"]
    Confirm["Wait for target data"]
    Collect["Collect targets"]
    Apply["Apply effects"]
    End["End ability"]

    Activate --> Indicator --> Confirm --> Collect --> Apply --> End
```

Verify:

* Cancel input removes the indicator and ends cleanly.
* Confirm only applies effects to targets inside the collector shape.
* Cues and cooldowns still come from the ability/effect data, not from the indicator.

Start with instant confirmation during development. Add custom confirm/cancel UX after the collector returns the correct targets.

Editor verification tips:

* Inspect the collector prefab first. Most targeting bugs are layer, radius, shape, or origin settings.
* Put target actors on the layer mask the collector reads.
* Test with one target inside the area and one outside before adding VFX.
* If you use a ground indicator, confirm it is destroyed on both confirm and cancel paths.

## Channeled Ability With Cancellation

Goal: run repeated ticks until duration completes, input releases, or the ability is cancelled.

Prefer `AbilityTask_WaitDelay` for deterministic toolkit timing. UniTask composition is acceptable when the ability genuinely needs awaiter composition.

Editor setup:

1. Bind the ability to input press and release.
2. Use an instancing policy that isolates active channel state.
3. Author a periodic cost effect if resources drain over time.
4. Author a sustained cue if the channel has continuous feedback.
5. Verify cancel cleanup before adding damage.

```csharp
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using GameplayAbilityToolkit.Core;

public sealed class ChannelRuntime : AbilityRuntime
{
    private CancellationTokenSource _cts;

    protected override async void OnActivate(GameplayEventData eventData)
    {
        _cts = new CancellationTokenSource();

        try
        {
            for (int i = 0; i < 5 && !_cts.IsCancellationRequested; i++)
            {
                EmitChannelTick(i);
                await UniTask.Delay(TimeSpan.FromSeconds(0.5f), cancellationToken: _cts.Token);
            }
        }
        catch (OperationCanceledException)
        {
        }
        finally
        {
            _cts?.Dispose();
            _cts = null;
        }

        EndAbility();
    }

    protected override void OnEnd(bool wasCancelled)
    {
        _cts?.Cancel();
    }
}
```

Verify:

* Cancellation does not leave spawned cues, indicators, or token sources alive.
* Resource drains stop when the channel ends.
* Input release path and ability cancellation path both call cleanup.

Watch for double-end bugs. A channel can finish naturally, be cancelled by input release, be cancelled by another ability, or fail because resources run out.

Editor verification tips:

* Bind the channel to press and release, then test holding for less than one tick, one tick, and full duration.
* Confirm periodic costs stop when the channel ends.
* Confirm sustained cues end when the ability cancels.
* Keep the first version single-target or self-targeted before adding cone or AoE collection.

## Save and Restore Actor State

Goal: snapshot enough ASC state for your game-specific save system.

The toolkit does not define a universal save format. Keep ownership of the schema in the upper-layer game.

Design first:

| Decision                        | Recommended default                                                     |
| ------------------------------- | ----------------------------------------------------------------------- |
| Definition lookup               | Stable registry ID or addressable key.                                  |
| Missing definition after update | Drop with a warning.                                                    |
| Cooldowns                       | Do not restore short cooldowns unless the design requires it.           |
| Effect source actor             | Restore source if it still exists; otherwise use the owner as fallback. |
| Attribute values                | Store base values and restore active effects separately.                |

Snapshot model:

```csharp
[Serializable]
public sealed class AscSave
{
    public List<string> Abilities = new();
    public List<EffectSave> Effects = new();
    public List<AttributeSave> Attributes = new();
    public List<string> OwnedTags = new();
}

[Serializable]
public struct EffectSave
{
    public string DefinitionName;
    public int StackCount;
    public float RemainingDuration;
}

[Serializable]
public struct AttributeSave
{
    public string Id;
    public float BaseValue;
}
```

Snapshot:

```csharp
public AscSave Snapshot(AbilitySystemComponent asc)
{
    var save = new AscSave();

    foreach (var spec in asc.RuntimeAbilities.AbilitySpecs)
        save.Abilities.Add(spec.AbilityDefinition.name);

    foreach (var spec in asc.RuntimeEffects.ActiveSpecs)
    {
        save.Effects.Add(new EffectSave
        {
            DefinitionName = spec.Effect.name,
            StackCount = spec.Count,
            RemainingDuration = spec.RemainingDuration
        });
    }

    foreach (var attr in asc.RuntimeAttributes.GetAllRuntimeAttributes())
    {
        save.Attributes.Add(new AttributeSave
        {
            Id = attr.AttributeId.ToString(),
            BaseValue = attr.BaseValue
        });
    }

    foreach (var tag in asc.RuntimeTags.OwnedTags.Tags)
        save.OwnedTags.Add(tag.TagName);

    return save;
}
```

Restore:

```csharp
public void Restore(AbilitySystemComponent asc, AscSave save, IDefinitionLookup lookup)
{
    foreach (var tagName in save.OwnedTags)
        asc.RuntimeTags.AddOwnedTag(GameplayTag.Get(tagName));

    foreach (var rec in save.Attributes)
        asc.RuntimeAttributes.ModifyAttributeBase(AttributeId.FromString(rec.Id), rec.BaseValue);

    foreach (var abilityName in save.Abilities)
        asc.RuntimeAbilities.AddAbility(lookup.Ability(abilityName), level: 1);

    foreach (var rec in save.Effects)
    {
        var effect = lookup.Effect(rec.DefinitionName);
        var ctx = EffectApplicationRequest.Create(target: asc.gameObject, instigator: asc.gameObject);

        for (int i = 0; i < rec.StackCount; i++)
            asc.RuntimeEffects.Apply(effect, ctx, level: 1);
    }
}
```

Handle these edge cases:

* Definition names removed or renamed between game versions.
* Effects whose original source actor no longer exists.
* Cooldowns mid-flight, if your design requires restoring them.
* Versioning of attribute IDs and lookup registries.

Verification:

1. Save an actor with active abilities, effects, attributes, and tags.
2. Restore into a clean actor.
3. Compare base attributes.
4. Compare owned tags.
5. Compare expected active effects and stack counts.
6. Advance time and confirm restored effects tick/expire correctly.

Editor verification tips:

* Build a dedicated save/load test scene with one source actor and one target actor.
* Put definition registries in a single visible asset so renamed definitions are easy to audit.
* Add a version number to your save payload before shipping the first save format.
* Decide whether short cooldowns should be persisted; many games intentionally drop them on load.

## Next Steps

* Use [Extending the Framework](/docs/gameplay-ability-toolkit/build-with-it/05-extending.md) for the extension points behind these recipes.
* Use [Basic Tutorial](/docs/gameplay-ability-toolkit/try-it/basictutorial.md) and [Arena Demo](/docs/gameplay-ability-toolkit/try-it/arena.md) for complete working content.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://morningheartgames.gitbook.io/docs/gameplay-ability-toolkit/build-with-it/07-cookbook.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
