Threat / Aggro
Server-only accumulated threat tables ("who do I hate most"): first-seen bonus, exact exponential decay, forget threshold, TargetChanged hooks. Feeding (damage/taunts) and consuming (Targeting selector, Agents brains) are composition bridges — Crossplay:Threat:DriveTargeting flips Targeting to highest-threat-first. Zero wire; inert until fed.
Services you call 1
Crossplay implements these. Resolve them from DI and call them from your own systems.
IThreatService
Server-side threat API — accumulated aggro tables, one per holder entity. The piece stores and ranks NUMBERS; what generates threat (damage, heals, taunts, proximity) and what consumes it (a Targeting selector, an Agents behavior, a boss script) are composition bridges or game glue. Works for MMO aggro, zombie hordes, tower-defense taunts — any "who do I hate most" question.
-
void Add(long holderId, long sourceId, float amount)Adds threat from sourceId onto holderId's table (the first entry of a source also gains the configured first-seen bonus). Negative amounts reduce; an entry falling to/below the forget threshold is dropped.
-
void Clear(long holderId)Forgets a holder's whole table (a leash reset, a wipe, an evade).
-
void Drop(long holderId, long sourceId)Forgets one source on one holder's table.
-
float GetThreat(long holderId, long sourceId)The threat sourceId currently holds on holderId's table (0 when absent).
-
event Action<long, long> TargetChangedRaised when a holder's highest-threat source CHANGES: (holder id, new highest source id — 0 when the table emptied). The hook brains/selectors use to switch targets.
-
bool TryGetHighest(long holderId, out long sourceId)The holder's current highest-threat source, if it holds any.
Configuration 1
Every tunable lives in an options object — there are no magic numbers to hunt for.
ThreatOptions
Configurable threat parameters (defaults here; never inlined in logic).
-
float DecayIntervalSeconds { get; set; }Seconds between decay sweeps (decay is exact over elapsed time regardless of the sweep cadence; this only bounds sweep frequency).
-
float DecayPerSecond { get; set; }Fraction of every entry that decays away per second (0.1 = 10%/s). 0 (the default) = threat never decays.
-
bool DriveTargeting { get; set; }When true (via the Hosting composition flag Crossplay:Threat:DriveTargeting), the Targeting piece's selector seam is bridged to highest-threat-first. The piece itself never reads this — it documents the composition switch.
-
float FirstSeenBonus { get; set; }Extra threat granted the FIRST time a source enters a holder's table — the "first tag" stickiness that stops a fresh attacker from instantly stealing aggro. 0 (the default) = no bonus.
-
float ForgetBelow { get; set; }Entries that decay below this are forgotten entirely.
-
int MaxEntriesPerHolder { get; set; }Upper bound on one holder's table. When full, a new source replaces the lowest entry only if it enters with MORE threat — the table never grows past this.