Class AggressionController
Manages an aggression (threat) table for a single NPC. Tracks threat from damage, healing, resource expenditure, and arbitrary point adjustments. Threat decays over time. Target selection uses both raw threat points and a vulnerability multiplier based on the target's current health and mana percentages.
Plain C# class — one instance per NPC, owned by AggressionState.
public class AggressionController
- Inheritance
-
AggressionController
- Inherited Members
Fields
DamageWeight
Points per 1 damage dealt to the NPC.
public float DamageWeight
Field Value
DecayRate
Points per second each entry decays while no new events arrive.
public float DecayRate
Field Value
HealingWeight
Points per 1 healing witnessed on an enemy of the NPC.
public float HealingWeight
Field Value
HitBonusPoints
Flat points added per hit regardless of damage amount.
public float HitBonusPoints
Field Value
LowHealthThreatMultiplier
Multiplier applied to threat points for targets below 30% health. Makes the AI prefer to finish off wounded enemies.
public float LowHealthThreatMultiplier
Field Value
LowHealthThreshold
Health threshold (0-1) below which LowHealthThreatMultiplier activates.
public float LowHealthThreshold
Field Value
LowResourceThreatMultiplier
Multiplier applied to threat points for targets below 20% mana. Makes the AI pressure casters who are running out of resources.
public float LowResourceThreatMultiplier
Field Value
LowResourceThreshold
Resource threshold (0-1) below which LowResourceThreatMultiplier activates.
public float LowResourceThreshold
Field Value
ResourceWeight
Points per 1 resource point (mana/stamina) spent casting near the NPC.
public float ResourceWeight
Field Value
StaleEntryTimeout
Seconds after last event before a zero-point entry is removed.
public float StaleEntryTimeout
Field Value
TargetVarietyChance
Chance (0-1) to pick a secondary target for variety.
public float TargetVarietyChance
Field Value
Properties
Clock
Seconds of AI time this table has observed, advanced only by Tick(float).
public float Clock { get; }
Property Value
Remarks
The staleness clock used to be UnityEngine.Time.time — Unity's wall clock. That tied threat expiry to real elapsed time while the decay it is paired with advanced on the AI tick, so the two disagreed whenever the two rates did: an NPC throttled down to the Far LOD tier decayed its threat slowly but expired entries at full speed, and a server hitch expired threat for NPCs that had not run a single update during it.
Deriving both from the same tick-advanced clock makes expiry mean "this many seconds of AI time without an event", which is what the tuning value has always claimed to mean, and makes the whole table reproducible from a tick count.
Count
Number of entries currently tracked in the aggression table.
public int Count { get; }
Property Value
HasAggression
Returns true if any characters are tracked in the table.
public bool HasAggression { get; }
Property Value
MaximumVulnerabilityMultiplier
The largest VulnerabilityMultiplier(ICharacter) any character could currently receive.
public float MaximumVulnerabilityMultiplier { get; }
Property Value
Remarks
The bound a taunt needs. The table stores raw points and holds no character references,
so it cannot compute another entry's SCORE — but score is bounded above by
raw * this for every entry, so beating highestRaw * this beats every actual
score whichever entry happens to carry it. Both multipliers can apply at once (a caster
that is both wounded and out of mana), so they compound here exactly as they do there.
Floored at the neutral 1 per factor, so a multiplier a designer tuned BELOW one cannot turn this bound into an under-estimate.
Table
The raw aggression table keyed by character ID. Read-only.
public IReadOnlyDictionary<long, AggressionEntry> Table { get; }
Property Value
Methods
AddPoints(long, float)
Adds arbitrary points (positive for taunt, negative for de-aggro).
public void AddPoints(long characterId, float points)
Parameters
Clear()
public void Clear()
GetEntry(long)
Returns the aggression entry for a character, or null if not tracked.
public AggressionEntry GetEntry(long characterId)
Parameters
characterIdlong
Returns
GetHighestPoints(long)
Returns the highest raw threat currently held by any tracked character, optionally ignoring one of them.
public float GetHighestPoints(long excludeCharacterId = 0)
Parameters
excludeCharacterIdlongCharacter to leave out of the comparison, or 0 for none.
Returns
- float
The highest threat points, or 0 when nothing is tracked.
Remarks
Used by ApplyTauntAction to place a taunter decisively on top rather than adding a flat bonus that a long fight has already outgrown.
GetPoints(long)
Returns raw threat points for a character, or 0 if not tracked.
public float GetPoints(long characterId)
Parameters
characterIdlong
Returns
GetThreatScore(long, ICharacter)
Computes a threat score for a character, factoring in vulnerability. Low health and low mana targets get a multiplier so the AI finishes weak enemies and pressures casters running out of resources.
public float GetThreatScore(long characterId, ICharacter character)
Parameters
characterIdlongcharacterICharacter
Returns
Remarks
This, not GetPoints(long), is what decides who an NPC attacks — see
PickTarget(List<ICharacter>, DeterministicRNG). Anything that means to move a character up or down the target
order has to reason in this space; ApplyTauntAction compared raw points and its
"guarantee" was therefore not one.
PickTarget(List<ICharacter>, DeterministicRNG)
Selects the best target from candidates using threat scoring with vulnerability.
public ICharacter PickTarget(List<ICharacter> candidates, DeterministicRNG rng = null)
Parameters
candidatesList<ICharacter>rngDeterministicRNG
Returns
RecordDamage(long, int)
Records damage dealt to this NPC.
public void RecordDamage(long attackerId, int amount)
Parameters
RecordHealing(long, int)
Records healing witnessed by this NPC on one of its enemies.
public void RecordHealing(long healerId, int amount)
Parameters
RecordResourceSpent(long, int)
Records resource expenditure (mana/stamina) from a character casting near this NPC. Casters who spend heavily to damage or heal draw additional threat.
public void RecordResourceSpent(long characterId, int amount)
Parameters
RemoveEntry(long)
Removes a single character's entry, returning it to the pool. No-op when the character is not tracked — importantly, it does not create an entry in order to remove it.
public bool RemoveEntry(long characterId)
Parameters
characterIdlongThe character whose threat should be forgotten.
Returns
- bool
True if an entry was removed.
Reset()
Clears all entries and returns them to the pool.
public void Reset()
ShouldSwitchTarget(long, long, float)
Returns true if candidate should replace current target based on threat delta.
public bool ShouldSwitchTarget(long currentId, long candidateId, float threshold = 50)
Parameters
Returns
Tick(float)
Decays all entries and removes stale ones.
public void Tick(float deltaTime)
Parameters
deltaTimefloat
VulnerabilityMultiplier(ICharacter)
The vulnerability scaling GetThreatScore(long, ICharacter) applies to a character's raw points.
public float VulnerabilityMultiplier(ICharacter character)
Parameters
characterICharacterThe character to weigh. Null scores no scaling.
Returns
- float
The multiplier, never less than the neutral 1.
Remarks
Split out of GetThreatScore(long, ICharacter) so a caller that needs to work backwards from a desired SCORE to the raw points that produce it uses the same rule the forward direction does, rather than a second copy of it that can drift.