Table of Contents

Class TargetSelector

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

Abstract base class for selecting targets in an ability or event context. Implementations consume the current EventData (its Target or Initiator serve as the spatial / contextual reference) and yield one or more UnityEngine.GameObjects for triggers, conditions or actions to operate on.

Asset safety: selectors are serialized inline on Trigger ScriptableObjects via [SerializeReference]. Unity cannot serialize references to scene GameObjects from asset files, so selectors intentionally hold no direct scene references. To "pick a specific scene object" from an asset-based Trigger, use NamedSceneObjectTargetSelector or TaggedSceneObjectTargetSelector — they resolve scene objects at runtime by name or tag. For inline (MonoBehaviour-hosted) triggers in a scene, prefer setting Target at the invocation site so the trigger receives the picked GameObject through standard event flow.

[Serializable]
public abstract class TargetSelector
Inheritance
TargetSelector
Derived
Inherited Members

Fields

Conditions

Conditions that must be met for a target to be valid. Evaluated per-candidate inside AreConditionsMet(GameObject, EventData). Honors the ambient ConditionFilter via AreConditionsMet(List<BaseCondition>, EventData, Func<BaseCondition, bool>).

[Tooltip("Conditions that must be met for a target to be valid.")]
[SerializeReference]
public List<BaseCondition> Conditions

Field Value

List<BaseCondition>

Methods

AreConditionsMet(GameObject, EventData)

Evaluates this selector's per-target Conditions against the candidate target. Builds a forked EventData scoped to the candidate so conditions see the right Target / TargetCharacter, then delegates to AreConditionsMet(List<BaseCondition>, EventData, Func<BaseCondition, bool>) so nested conditions' own TargetSelector and Combine settings are honored uniformly with top-level Trigger conditions.

protected bool AreConditionsMet(GameObject target, EventData eventData)

Parameters

target GameObject

The candidate target GameObject.

eventData EventData

The parent event data, or null.

Returns

bool

True when no conditions exist, or all conditions pass.

GatherRewound(EventData, GameObject, List<GameObject>, GatherTargets)

Collects candidates inside a single rewind scope and returns them once it has closed.

protected static void GatherRewound(EventData eventData, GameObject context, List<GameObject> results, TargetSelector.GatherTargets gather)

Parameters

eventData EventData

The event driving the selection.

context GameObject

The spatial origin object; its scene is the one rewound.

results List<GameObject>

Receives the selected objects, in the gather's order.

gather TargetSelector.GatherTargets

The query and ranking to run under the scope.

Remarks

This is ChainTargetSelector's pattern, generalised, and it exists for two reasons the chain learned first.

Ranking must read the same world the query did. LagCompensatedQuery closes its rewind before returning, so a selector that queried through it and then measured distances was selecting from the rewound world and ranking by live positions — the nearest candidate by one measure and a different one by the other, differing by the peer's speed times its latency. Conditions are evaluated inside the scope for the same reason: a range condition that disagrees with the query that produced the candidate is not a filter, it is a coin toss.

Nothing is yielded while the world is displaced. Selectors are iterators and the consumer between two yields is the damage and ECA pipeline. Materialising the whole result first means the scope has closed before any of that runs.

GetContext(EventData)

protected static GameObject GetContext(EventData eventData)

Parameters

eventData EventData

Returns

GameObject

GetTooltipContribution()

Returns a short, designer-facing tooltip line describing this selector's targeting (e.g. "Nearest enemy within 10m"), or null when the selector has nothing to contribute. Override on selectors that have player-visible targeting semantics.

public virtual string GetTooltipContribution()

Returns

string

IsAuthoritativePeer(EventData)

Returns the spatial origin for this selector — preferring Target, falling back to Initiator's GameObject.

protected static bool IsAuthoritativePeer(EventData eventData)

Parameters

eventData EventData

The current event data.

Returns

bool

A GameObject to use as a spatial reference, or null.

Remarks

Server only, and not "not a replicate tick". Every physics selector used to open with if (tickData.IsReplicateTick) yield break;, on the theory that a replicate tick means a client replay. It does not. AbilityObject's self-target dispatch and its OnPreSpawn/OnSpawn dispatches, and AbilityController's activation triggers, all attach a TickEventData built from the replicate PredictionTick — on the server as well. So the guard fired on the server too, and a point-blank area ability selected nothing and dealt no damage on any peer. AbilityApplyAreaAction was corrected for exactly this and gates on the peer instead; this is the same rule, applied at the selector.

The tick payload is left on the event untouched. It still carries real information for the tick-domain consumers downstream (buff stamping, cooldowns) — it just never meant what the old guard read it as.

QueryBufferSize(int)

Query buffer size for a selector whose authored cap is maxHits.

protected static int QueryBufferSize(int maxHits)

Parameters

maxHits int

Returns

int

Remarks

Deliberately larger than the cap. Sizing the buffer at exactly MaxHits makes the physics broadphase perform the truncation, in its own order, before the selector ever sees the candidates — so a cap of 5 in a crowd of 20 picked five arbitrary characters and the deterministic sort that follows had nothing to work with. Querying wide and capping after the sort is what makes the cap mean "the first five in a defined order".

A starting size, not a limit. It only moves the truncation point from MaxHits up to MaxHits * 4; every caller must still grow the buffer through TryGrowQueryBuffer<T>(ref T[], int) when a query comes back full.

The rule itself lives on TargetOrdering now, because the ability actions resolve hits without a selector and need the identical arithmetic — it was duplicated inline in AbilityApplyAreaAction for exactly as long as it lived somewhere only selectors could reach. This forwarder stays so the selectors read naturally.

SelectTargets(EventData)

Selects targets based on the supplied event context.

public abstract IEnumerable<GameObject> SelectTargets(EventData eventData)

Parameters

eventData EventData

The event data driving the selection. Target (when set) or Initiator typically serves as the spatial origin.

Returns

IEnumerable<GameObject>

The selected GameObjects.