Class HealerAttackingState
Healer archetype. Keeps caster spacing from the enemy, but interrupts its damage rotation to top up the most wounded nearby ally.
[CreateAssetMenu(fileName = "New AI Healer Attacking State", menuName = "FishMMO/Character/NPC/AI/Healer Attacking State", order = 4)]
public class HealerAttackingState : BaseAttackingState, ICachedObject
- Inheritance
-
ObjectScriptableObjectHealerAttackingState
- Implements
- Inherited Members
-
ScriptableObject.SetDirty()ScriptableObject.CreateInstance<T>()Object.GetEntityId()Object.GetInstanceID()Object.GetHashCode()Object.InstantiateAsync<T>(T)Object.InstantiateAsync<T>(T, Transform)Object.InstantiateAsync<T>(T, Vector3, Quaternion)Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion, Transform)Object.Instantiate(Object)Object.Instantiate(Object, Scene)Object.Instantiate<T>(T, InstantiateParameters)Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)Object.Instantiate(Object, Transform)Object.Instantiate<T>(T)Object.Instantiate<T>(T, Vector3, Quaternion)Object.Instantiate<T>(T, Vector3, Quaternion, Transform)Object.Instantiate<T>(T, Transform)Object.Destroy(Object)Object.DestroyImmediate(Object)Object.DontDestroyOnLoad(Object)Object.DestroyObject(Object)Object.FindObjectsOfType<T>()Object.FindObjectsByType<T>(FindObjectsSortMode)Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)Object.FindObjectOfType<T>()Object.FindFirstObjectByType<T>()Object.FindAnyObjectByType<T>()Object.FindFirstObjectByType<T>(FindObjectsInactive)Object.FindAnyObjectByType<T>(FindObjectsInactive)Object.FindObjectsByType<T>()Object.FindObjectsByType<T>(FindObjectsInactive)Object.ToString()Object.nameObject.hideFlags
Remarks
This is one of the three archetypes that genuinely needs code rather than tuning: it has to scan for a second, friendly target that the shared combat decision knows nothing about. Everything after "no ally needs healing" falls through to BaseAttackingState's shared logic.
Heals identify themselves. An ability counts as a heal when AIAbilityClassifier finds a healing action in its ECA graph — no list of template IDs on the archetype, no naming convention. That matters because the list version was a second copy of a fact the ability asset already stated, and the two drifted silently: a designer adding a third heal got a healer that went on casting only the two it had been told about, with nothing logged and nothing to notice. It also stopped one healer asset being shared by creatures with different spellbooks, which is most of them.
HealAbilityTemplateIDs survives as an override for abilities the classifier reads wrongly. Leave it empty in the normal case.
Fields
AllyLayers
The physics layers to check when scanning for allies. Should include the layers that allied characters (other NPCs, pets, players) are on.
[Header("Healer Behavior")]
[Tooltip("Physics layers to scan for allies.")]
public LayerMask AllyLayers
Field Value
- LayerMask
AllyScanInterval
Seconds between ally scans.
[Tooltip("Seconds between ally scans. Higher is cheaper; health changes slowly enough that this can be coarse.")]
public float AllyScanInterval
Field Value
Remarks
The ally scan is an OverlapSphere plus an interface component lookup per hit, and
it used to run on every single combat tick — so a healer cost roughly twice what any
other archetype cost, purely to re-answer a question whose answer barely changes between
ticks. Health does not move fast enough for a fifth of a second of staleness to matter,
and the result is cached in between.
AllyScanRadius
Radius within which to scan for injured allies.
[Tooltip("Radius within which to scan for allies.")]
public float AllyScanRadius
Field Value
CanHealSelf
Whether the healer counts itself as a heal candidate when it is the most wounded character in range.
[Tooltip("Allow the healer to heal itself when it is the most wounded character in range.")]
public bool CanHealSelf
Field Value
HealAbilityTemplateIDs
Optional escape hatch: template IDs to treat as heals in addition to whatever AIAbilityClassifier identifies.
[Tooltip("Optional. Extra AbilityTemplate IDs to force-treat as heals. Normally empty — heals are detected from the ability's ECA actions.")]
public List<int> HealAbilityTemplateIDs
Field Value
Remarks
Normally empty. Heals are recognised from the ability's own ECA actions; this is only for an ability that heals by some route the classifier cannot see, such as a project specific action type.
HealThreshold
Health percentage (0-1) below which an ally is considered injured and eligible for healing. E.g., 0.8 means heal allies below 80% health.
[Range(0, 1)]
[Tooltip("Heal allies whose health is below this fraction of max.")]
public float HealThreshold
Field Value
Methods
Enter(AIController)
Called when entering the healer attacking state.
public override void Enter(AIController controller)
Parameters
controllerAIControllerThe AI controller.
PickAbility(AIController)
Excludes heal abilities from the damage picker so the healer never "attacks" with a heal.
protected override Ability PickAbility(AIController controller)
Parameters
controllerAIControllerThe AI controller.
Returns
- Ability
The best damage ability, or null.
TryAttack(AIController, ICharacter)
Heals first, fights second. Falls through to the shared combat logic when nobody needs healing or no heal is available.
protected override void TryAttack(AIController controller, ICharacter targetCharacter)
Parameters
controllerAIControllerThe AI controller.
targetCharacterICharacterThe current enemy target.