> 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/05-extending.md).

# Extending the Framework

> Online doc: <https://morningheartgames.gitbook.io/docs/gameplay-ability-toolkit/foundations/05-extending?fallback=true>

Gameplay Ability Toolkit is built around explicit extension points. Use the narrowest extension that matches your rule; that keeps runtime behavior predictable and easier to test.

For a small project, extend in this order:

1. Configure existing assets and Dashboard settings.
2. Use built-in hooks, tasks, collectors, cues, and animation driver options.
3. Add a focused subclass or interface implementation.
4. Add a broader manager only when the documented extension point requires one.

This keeps custom code tied to one gameplay reason instead of creating another framework around the toolkit.

## Choose the Right Extension Point

| You need to...                                                                           | Use                                                  |
| ---------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| Change how one modifier magnitude is calculated                                          | Custom `IModifierMagnitudeCalculator`                |
| React to attribute changes based on actor components, equipment, stance, or shield state | `IAttributeChangeHook`                               |
| Change clamp or write behavior that belongs to an attribute definition                   | `RuntimeAttribute` subclass                          |
| Wait for custom gameplay timing or events inside an ability                              | `AbilityTask` subclass                               |
| Collect targets in a custom way                                                          | `TargetCollector` or `MultiTargetCollector` subclass |
| Play montages through another animation backend                                          | `IAnimationDriver` registration                      |
| Route logs somewhere other than `UnityEngine.Debug`                                      | `ILogBackend`                                        |
| Customize cue instance behavior                                                          | `GameplayNotifyCueBehavior` or `IGameplayCueNotify`  |

## Editor Pages For Extension Work

Most extension work starts in code, but several editor pages help you wire or validate the result.

| Work                                            | Editor path                                                               |
| ----------------------------------------------- | ------------------------------------------------------------------------- |
| Enable tests or optional packages               | `Tools > Gameplay Ability Toolkit > Dashboard > Dependencies`             |
| Create animation driver configuration           | `Tools > Gameplay Ability Toolkit > Dashboard > Extensions`               |
| Configure cue library mappings                  | `Tools > Gameplay Ability Toolkit > Dashboard > Cues`                     |
| Validate tag references                         | `Tools > Gameplay Ability Toolkit > Dashboard > Tags`                     |
| Tune logging output                             | `Tools > Gameplay Ability Toolkit > Dashboard > Logging`                  |
| Create ability/effect assets for new subclasses | `Tools > Gameplay Ability Toolkit > Dashboard > Abilities` or **Effects** |

Treat the Dashboard as the integration surface. Treat C# subclasses as the behavior surface.

## Extension Ability

Use this sequence for most custom extension points:

1. Identify the narrow extension point.
2. Write the smallest concrete subclass or interface implementation.
3. Register it at startup if the extension point needs registration.
4. Create or update the relevant ScriptableObject asset.
5. Wire the asset through Dashboard, Inspector, or configuration.
6. Add one focused EditMode or PlayMode test.
7. Verify in a sample scene or minimal test actor.

Avoid adding broad manager classes unless the extension point explicitly needs one.

If you cannot name the single asset, component, or subsystem that should own the rule, return to [Architecture](/docs/gameplay-ability-toolkit/build-with-it/02-architecture.md) before adding a new abstraction.

## Custom Modifier Magnitudes

Implement `IModifierMagnitudeCalculator` when one modifier needs reusable custom math:

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

public sealed class MyMagnitudeCalculator : IModifierMagnitudeCalculator
{
    public float CalculateMagnitude(in ModifierMagnitudeCalContext ctx)
    {
        return 10f;
    }
}

public static class MyMagnitudeBootstrap
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Register()
    {
        ModifierMagnitudeCalculatorFactory.RegisterCalculatorCreator(
            ModifierMagnitudeType.Custom1,
            () => new MyMagnitudeCalculator());
    }
}
```

Then set the modifier's magnitude type in the Inspector.

Use a custom magnitude calculator for "one modifier, custom number." Use an `EffectCalculation` when one application needs to emit several related modifiers.

### Editor Checklist

1. Add the calculator class.
2. Register it before scene load.
3. Select the effect asset.
4. Edit the modifier.
5. Set the magnitude type to your custom enum value.
6. Enter Play Mode and apply the effect.
7. Verify the resolved number through attribute output, event log, or a test assertion.

If the enum value appears but the calculator does not run, check the registration timing first.

## Attribute-Side Hooks

There are two supported ways to customize attribute changes.

### Actor-Scoped: `IAttributeChangeHook`

Add a `MonoBehaviour` implementing `IAttributeChangeHook` to the same GameObject as the ASC or to a child. The ASC discovers hooks during initialization.

```csharp
public interface IAttributeChangeHook
{
    float OnBeforeAttributeChange(AttributeId id, float newValue, float currentValue);
    float OnBeforeBaseValueChange(AttributeId id, float newBase, float currentBase);
    void OnAfterEffectExecuted(in EffectExecutionContext context);
}
```

Use this for actor-level rules:

* Armor mitigation based on equipped gear.
* Shield components that redirect damage before Health drops.
* Stance or status components that clamp or redirect values.
* Reactions after an effect executes.

If you add or remove hook components dynamically, call `asc.RefreshAttributeChangeHooks()`.

### Definition-Scoped: `RuntimeAttribute`

Subclass `RuntimeAttribute` when the attribute itself owns the rule:

```csharp
protected virtual float OnPreClampValue(float proposed, float maxAttributeValue);
protected virtual void OnPostValueChanged(bool isBase, float oldValue, float newValue);
```

Then subclass `AttributeSetDefinition` and override the factory that creates runtime attributes.

Use this for definition-level behavior:

* Custom clamp curves.
* Shield overflow rules that belong to a specific attribute schema.
* Attribute-specific write notifications that should travel with the definition.

### Decision Rule

```mermaid
flowchart TD
    Rule["Where does the rule belong?"]
    Actor["Actor, equipment, stance, components"]
    Definition["Attribute schema itself"]
    Hook["Use IAttributeChangeHook"]
    RuntimeAttr["Use RuntimeAttribute subclass"]

    Rule --> Actor --> Hook
    Rule --> Definition --> RuntimeAttr
```

The two approaches compose: the definition can clamp first, then actor hooks can make final post-clamp adjustments.

## Custom Ability Tasks

Subclass `AbilityTask` when an ability needs reusable frame-spanning behavior. Representative built-ins are `AbilityTask_WaitDelay`, `AbilityTask_WaitInputPressed`, and `AbilityTask_WaitForTargetData`.

Do not add coroutine, `Task.Delay`, or `Task.Yield` timing paths to core gameplay rules unless you have a specific reason to leave the toolkit tick model.

### Task Implementation Checklist

1. Store only state owned by the current ability activation.
2. Subscribe to runtime events in the task start path.
3. Unsubscribe in completion and cancellation paths.
4. Signal completion exactly once.
5. Make cancellation safe if the owning ability ends early.
6. Add a test that ticks the owning ASC rather than relying on real wall-clock time.

## Custom Target Collectors

Subclass `TargetCollector` for single-target abilities or `MultiTargetCollector` for list-of-targets abilities.

Override the lifecycle methods that match your targeting mode:

| Method           | Purpose                                           |
| ---------------- | ------------------------------------------------- |
| `BeginTargeting` | Initialize indicator state or cached references.  |
| `Tick`           | Update preview, reticle, or candidate target set. |
| `Confirm`        | Produce target data.                              |
| `Cancel`         | End targeting without producing target data.      |

Use `TargetDataFilter` and `TargetDataFilterGroup` for reusable filtering such as team, distance, and line of sight.

### Collector Authoring Checklist

1. Create a collector prefab or asset according to your collector type.
2. Assign shape, range, layer mask, and filter data.
3. Assign the collector to the ability definition.
4. Use `AbilityTask_WaitForTargetData` in the runtime.
5. Test instant confirm first.
6. Add user-confirmed or custom-confirmed behavior after target selection works.

For ground targeting, also verify the indicator prefab is visible, follows the intended plane, and cleans up on cancel.

## Custom Montage Animation Drivers

`MontagePlayer` talks to animation systems through `IAnimationDriver`. Core ships with a Unity Playables driver, so montage playback does not require Animancer or GameCreator.

Driver selection falls back in this order:

1. `MontagePlayer.driverConfig`
2. `GASConfiguration.defaultAnimationDriverConfig`
3. Built-in `UnityAnimationDriver`

| Capability                           | Unity Playables                              | Animancer              | GameCreator              |
| ------------------------------------ | -------------------------------------------- | ---------------------- | ------------------------ |
| Single clip playback                 | Yes                                          | Yes                    | Yes                      |
| Blend in/out                         | Immediate / limited                          | Yes                    | Yes                      |
| Additive blending                    | No                                           | Delegated to Animancer | Yes                      |
| Root motion                          | Sets `Animator.applyRootMotion`              | Delegated to Animancer | Delegated to GameCreator |
| Coexists with Animator state machine | No, it replaces Animator output while active | Yes                    | Yes                      |

Register a backend before scene load:

```csharp
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Register()
{
    AnimationDriverConfig.RegisterBackend(
        AnimationDriverConfig.DriverBackend.MyBackend,
        (owner, config) => new MyAnimationDriver(owner));
}
```

Add a new `DriverBackend` enum value only when you also add the matching driver and authoring support.

### Editor Checklist

1. Open `Tools > Gameplay Ability Toolkit > Dashboard > Extensions`.
2. Create or select an `AnimationDriverConfig`.
3. Choose the backend.
4. Assign the config to `GASConfiguration.defaultAnimationDriverConfig` or to a specific `MontagePlayer`.
5. Enter Play Mode and play a montage.
6. Verify start, completion, cancellation, and root motion behavior.

## Custom Log Backend

Implement `ILogBackend` and register it with `Logger.SetBackend(myBackend)`.

The default backend writes to `UnityEngine.Debug`, gated by category and level. Runtime logging compiles out unless `GAS_LOGGING_ENABLED` is defined.

## Test Configuration

Use `GASConfigurationProvider.SetOverride(config)` to replace the resolved configuration during a test. Pair it with teardown cleanup:

```csharp
[TearDown]
public void TearDown()
{
    GASConfigurationProvider.ClearOverride();
}
```

## Gameplay Cues

Bind cue tags to `GameplayNotifyCueSO` assets through the `GameplayCueLibrary`. The active library is loaded from `GASConfiguration.defaultCueLibrary`.

Use `Tools > Gameplay Ability Toolkit > Dashboard > Cues` to wire cue mappings.

Customize cue playback through:

| Extension                   | Use it when                                            |
| --------------------------- | ------------------------------------------------------ |
| `GameplayNotifyCueBehavior` | You need per-instance behavior on spawned cue objects. |
| `IGameplayCueNotify`        | You need per-asset cue handling.                       |

Cue instances are pooled by `GameplayNotifyCuePool`.

### Cue Authoring Checklist

1. Create or select a cue notify asset/prefab.
2. Open `Tools > Gameplay Ability Toolkit > Dashboard > Cues`.
3. Select the default `GameplayCueLibrary`.
4. Add a mapping from `Cue.*` tag to notify.
5. Assign the cue tag on an effect or ability.
6. Enter Play Mode and apply the rule that triggers the cue.
7. Confirm the cue starts, stops, and pools correctly.

## Save and Load

The toolkit exposes enumeration APIs, but it does not own a serialization contract. Save schema belongs to your game.

Recommended snapshot categories:

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

foreach (var spec in asc.RuntimeEffects.ActiveSpecs)
    save.Effects.Add(/* definition, stack count, remaining time */);

foreach (var attr in asc.RuntimeAttributes.GetAllRuntimeAttributes())
    save.Attributes[attr.AttributeId.ToString()] = (attr.BaseValue, attr.CurrentValue);

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

Restore by looking up definitions from your own registry, then re-granting abilities, attributes, tags, and effects in a controlled order.

See [Cookbook: Save and restore actor state](/docs/gameplay-ability-toolkit/build-with-it/07-cookbook.md#save-and-restore-actor-state) for a fuller recipe.

### Save/Load Design Questions

Decide these before writing the serializer:

| Question                                                                  | Why it matters                                          |
| ------------------------------------------------------------------------- | ------------------------------------------------------- |
| Are definitions addressed by name, GUID, addressable key, or registry ID? | Names are easy but fragile across renames.              |
| Do active effects restore remaining duration?                             | Important for cooldowns, buffs, and DoTs.               |
| Do source actors matter after loading?                                    | Some effects scale or stack by source.                  |
| Are cooldowns worth restoring?                                            | Many games intentionally reset short cooldowns on load. |
| How are removed definitions handled after game updates?                   | Saves need version-tolerant fallback behavior.          |

## Deliberately Out of Scope

Gameplay Ability Toolkit does not provide:

* Network replication, prediction, or server-authoritative ability flow.
* Runtime tag source registration.
* Dynamic mutation of `TargetCollector.Filters` after collector construction.
* A separate custom `IGameplayCueHandler` interface beyond the cue library and cue notify extension points.

## Next Steps

* Use [Cookbook](/docs/gameplay-ability-toolkit/build-with-it/07-cookbook.md) for practical recipes that combine several extension points.
* Use [API Reference](/docs/gameplay-ability-toolkit/build-with-it/06-api-reference.md) to confirm which types are supported public API.


---

# 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/05-extending.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.
