Class EventData
Container for event-related data in the ECA trigger system.
Carries three first-class fields so triggers, conditions and actions never have to hunt through typed sub-payloads to find the basic identities involved in an event:
- Initiator — the single character that started the trigger (immutable).
- Target — the GameObject the trigger is acting on (mutable; set by selectors / collision dispatch).
- TargetCharacter — the ICharacter on Target, when one exists.
- RNG — optional deterministic RNG carried alongside hit events.
Phase-specific data (collision, tick, spawn, damage amount, …) is still stored as typed sub-payloads accessible via TryGet<T>(out T).
public class EventData
- Inheritance
-
EventData
- Derived
- Inherited Members
Constructors
EventData(ICharacter, params EventData[])
Constructs a new EventData with no target, optionally seeded with extra payloads.
public EventData(ICharacter initiator, params EventData[] initialData)
Parameters
initiatorICharacterThe character that initiated the event.
initialDataEventData[]Optional initial sub-payloads to add.
EventData(ICharacter, ICharacter, DeterministicRNG)
Constructs a new EventData targeting an ICharacter. The character's GameObject is also assigned to Target for consumers that want a GameObject reference.
public EventData(ICharacter initiator, ICharacter targetCharacter, DeterministicRNG rng = null)
Parameters
initiatorICharacterThe character that initiated the event.
targetCharacterICharacterThe character being targeted.
rngDeterministicRNGOptional deterministic RNG.
EventData(ICharacter, GameObject, DeterministicRNG)
Constructs a new EventData targeting a UnityEngine.GameObject. When the GameObject has an ICharacter component it is auto-assigned to TargetCharacter.
public EventData(ICharacter initiator, GameObject target, DeterministicRNG rng = null)
Parameters
initiatorICharacterThe character that initiated the event.
targetGameObjectThe GameObject being targeted.
rngDeterministicRNGOptional deterministic RNG.
Properties
ConditionFilter
Optional ambient filter applied to every condition evaluation occurring within
this trigger fire. When non-null, returning false from the filter causes
the condition to be skipped (treated as if not present) at every level —
top-level Conditions, nested
CompositeCondition children, and selector-scoped
AreConditionsMet(GameObject, EventData) calls. Carried across
Fork(GameObject, ICharacter) so target fan-outs preserve the
filter context.
Set by Execute(EventData) from ShouldEvaluateCondition(BaseCondition). Designers and authors of new triggers/selectors generally do not need to touch this.
public Func<BaseCondition, bool> ConditionFilter { get; set; }
Property Value
HasExplicitRNG
True when a generator was explicitly threaded onto this event rather than derived from it.
public bool HasExplicitRNG { get; }
Property Value
Remarks
For diagnostics and for the few call sites that genuinely want to know whether an upstream system owns the stream. Reading it does not derive one.
Initiator
The character that initiated the event. Immutable for the lifetime of the EventData — a single trigger has exactly one initiator. To rebind initiator (e.g. reflected damage, summon procs) construct a new EventData.
public ICharacter Initiator { get; }
Property Value
RNG
The deterministic RNG for this event. An explicitly threaded generator (the ability object's, for a hit) is returned as-is; otherwise one is derived on first use from the event's own identity and cached.
public DeterministicRNG RNG { get; set; }
Property Value
Remarks
Never null, and never the shared instance. It used to be null for every event type
that was not an ability collision — AbilityEventData, RegionEventData,
BuffEventData — and every consumer coped by falling back to
Shared, a process-wide stream seeded from
Environment.TickCount. That is not a fallback, it is a different answer on every
peer and on every run: a random target selector picking out of the same five candidates
picked a different one on the client than on the server, and a different one again the
next time the same cast happened.
The derived stream is a function of the initiator's network identity and the event's tick, both of which every peer agrees on, so the same event yields the same rolls everywhere. Two events in the same tick from the same initiator share a stream deliberately: they are the same event as far as reproduction is concerned. Where a caller needs an independent reproducible stream — a selector that must not consume the rolls a later action expects — it takes one from IndependentRNG(int) with its own salt.
Target
The GameObject the trigger is currently targeting. Mutable so target selectors can reuse a single EventData while iterating selected candidates. May be null for world-level events.
public GameObject Target { get; set; }
Property Value
- GameObject
TargetCharacter
The character on Target, when the target GameObject implements ICharacter. May be null for non-character targets or world events.
public ICharacter TargetCharacter { get; set; }
Property Value
Methods
Add(EventData)
Adds an event data sub-payload, keyed by its concrete type.
Refuses to overwrite the self-registration that wires this into the dictionary.
To copy sub-payloads from another EventData, use Merge(EventData).
public void Add(EventData data)
Parameters
dataEventDataThe event data sub-payload to add.
AddRange(EventData[])
Adds multiple sub-payloads, ignoring nulls.
public void AddRange(EventData[] payloads)
Parameters
payloadsEventData[]Sub-payloads to add.
Contains<T>()
Checks if a sub-payload of type T exists. Honors the same
inheritance fallback as TryGet<T>(out T).
public bool Contains<T>() where T : EventData
Returns
- bool
True if found; otherwise, false.
Type Parameters
TThe type of event data to check for.
DeriveRNG(int)
Builds a reproducible generator for this event, independent of any other stream.
public DeterministicRNG DeriveRNG(int salt)
Parameters
saltintDistinguishes one consumer from another within the same event. Use a constant per call site; anything derived from local state would defeat the point.
Returns
- DeterministicRNG
A generator whose sequence is identical on every peer for the same event.
Remarks
A pure factory: every call returns a NEW generator positioned at the start of the sequence for these inputs. That is what makes it testable, and what makes it the wrong thing for a consumer that draws more than once — see IndependentRNG(int), which memoises one per (event chain, salt) so the sequence actually advances.
DeriveSeed(int, uint, int)
The seed mix behind DeriveRNG(int), as a pure function.
public static int DeriveSeed(int identity, uint tick, int salt)
Parameters
identityintThe initiator's network object id, or its character id.
tickuintThe event's tick, or 0 when it carries none.
saltintPer-call-site constant.
Returns
Remarks
FNV-1a over the three inputs. Separated out so the properties that matter — same inputs give the same seed, a different tick gives a different one — are testable without an event, a character or a network.
Fork(GameObject, ICharacter)
Creates a new EventData sharing this instance's Initiator and RNG, with all sub-payloads merged in, but with a new Target (and inferred TargetCharacter). Used by selectors to scope an event to one of several selected targets without losing the original phase-specific payloads.
public EventData Fork(GameObject target, ICharacter targetCharacter = null)
Parameters
targetGameObjectThe new target GameObject. May be null.
targetCharacterICharacterOptional pre-resolved character on
target.
Returns
- EventData
A new event data scoped to the supplied target.
IndependentRNG(int)
The one independent stream this event chain holds for salt, created on
first use and shared by the root event and every Fork(GameObject, ICharacter) of it.
public DeterministicRNG IndependentRNG(int salt)
Parameters
saltintDistinguishes one consumer's stream from another's within one event. Salt 0 is reserved: the lazily-derived shared RNG is
DeriveRNG(0), so an independent stream taken with salt 0 would start at the identical sequence and walk in lockstep with it — exactly the correlation this method exists to avoid. Use a distinct non-zero constant per call site (existing: "RAND" 0x52414E44, "DSPL" 0x4453504C).
Returns
- DeterministicRNG
The shared-per-chain generator for
salt.
Remarks
This is what a server-only consumer draws from, not RNG. The shared
generator is threaded onto an ability object's OnSpawn/OnTick/OnHit/OnDestroy payloads
and is advanced by side effect, so a draw taken behind a peer gate advances it only on
the peers that pass — and an ungated action later in the same chain
(AbilityForkHitAction) then reads a different number. See AbilityObject.RNG
for the rule and what it cost. An action can satisfy it by evaluating its providers
above the gate; a server-only SELECTOR cannot, because the gate is the selector, so it
takes a stream of its own instead.
Memoised, which is the whole difference from DeriveRNG(int). That method is a pure factory and hands back a FRESH generator every call, so two consumers sharing a salt — or one consumer called twice — would draw identical numbers rather than independent ones, and the sequence would not advance at all. Holding one generator per (event chain, salt) gives a stream that advances across draws exactly as RNG does, while touching nothing RNG's consumers rely on.
Reproducible, not seedable. The sequence is a function of the initiator's network
identity, the event's tick and salt — values every peer agrees on —
so it is identical on every peer and on every run of the same cast. It deliberately does
NOT depend on any generator a caller assigned to RNG; that independence is
the point. Use a distinct constant salt per call site.
Merge(EventData)
Copies every sub-payload from source into this event data.
Use when wrapping or re-targeting an existing event so downstream conditions/actions
continue to see the original phase-specific payloads (e.g. RegionEventData,
QuestEventData) carried on the source.
public void Merge(EventData source)
Parameters
sourceEventDataThe event data whose sub-payloads should be merged into this one.
SetTarget(GameObject)
Sets Target and infers TargetCharacter from the GameObject's ICharacter component (if any).
public void SetTarget(GameObject target)
Parameters
targetGameObjectThe GameObject being targeted, or null to clear.
SetTarget(GameObject, ICharacter)
Sets both Target and TargetCharacter at once when both
are already resolved by the caller (avoids a redundant GetComponent).
public void SetTarget(GameObject target, ICharacter targetCharacter)
Parameters
targetGameObjectThe GameObject being targeted.
targetCharacterICharacterThe character on
target.
ToString()
Returns the type name of this event data instance.
public override string ToString()
Returns
TryGet<T>(out T)
Attempts to retrieve a sub-payload of type T. Performs an
exact-type lookup first (the fast path); if no payload is registered under that
exact key, falls back to a linear scan returning the first stored payload
assignable to T. This lets callers request a base type
(e.g. TryGet<AbilityCollisionEventData>()) and still receive a
designer-authored subclass payload.
public bool TryGet<T>(out T data) where T : EventData
Parameters
dataTThe retrieved sub-payload, or default if not found.
Returns
- bool
True if found; otherwise, false.
Type Parameters
TThe type of event data to retrieve.