Class DamageNegationBuffTemplate
Buff template that takes damage off incoming hits — the Block half of block and deflect.
[CreateAssetMenu(fileName = "New Damage Negation Buff Template", menuName = "FishMMO/Character/Buff/Damage Negation Buff", order = 3)]
public class DamageNegationBuffTemplate : BaseBuffTemplate, ICachedObject, ITooltip
- Inheritance
-
ObjectScriptableObjectDamageNegationBuffTemplate
- 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
- Extension Methods
Remarks
Passive, and queried rather than applied. Unlike
AttributeBuffTemplate this writes nothing to the attribute ledger: negation is
not a stat, it is a decision taken while one specific hit is being resolved, and it needs the
attacker's position to answer the facing question. DamageMitigation.Negate is what
reads it, from inside CharacterDamageController.Damage.
The three modes are the three block abilities. A channelled shield holds Reduce or Immune for as long as the button is down, because a channel re-applies its buff every tick and a duration-only buff refreshes cleanly. A consumable barrier — "absorbs 500 damage, then vanishes" — is Absorb. See the block-and-deflect section of the buff README for how to wire either through ECA.
Facing is what makes a shield a shield. With RequiresFacing set, only
damage arriving from within FacingAngleDegrees of where the defender is looking
is negated — so a player who blocks the sword in front still takes the arrow in the back.
The test runs server-side against real positions; see DamageMitigation for why it
cannot be lag compensated and why that is the right answer here.
Fields
Amount
[Tooltip("Absorb: damage points the pool holds. Reduce: percent taken off each hit (0-100). Immune: unused.")]
[Min(0)]
public int Amount
Field Value
Remarks
One field for both because a template is only ever in one mode, and two fields would let a designer fill in the one the mode does not read and wonder why nothing happened.
FacingAngleDegrees
Total spread of the protected arc, in degrees — 120 means 60 either side of forward.
[Tooltip("Total spread of the protected arc in degrees. 120 protects 60 degrees either side of forward.")]
[Range(0, 360)]
public float FacingAngleDegrees
Field Value
Remarks
Measured the same way IsWithinCone(Vector3, Vector3, Vector3, float) measures every other cone in the project, so "in front" means one thing across targeting and mitigation. An attacker standing exactly on the defender is outside every cone, which is the same rule and the same reason: a direction test cannot be satisfied by a point with no direction.
Mode
How this buff takes damage off a hit.
[Tooltip("Absorb spends a pool of damage points and ends at zero. Reduce takes a percentage off every hit. Immune negates every hit.")]
public DamageNegationMode Mode
Field Value
RequiresFacing
True to negate only damage arriving from in front of the defender.
[Tooltip("Negate only damage arriving from within FacingAngleDegrees of where the defender is looking.")]
public bool RequiresFacing
Field Value
Shield
The physical shield this buff raises: a real volume with real dimensions, standing where the character holds it.
[Tooltip("Physical shield volume. Ability objects that strike inside it are stopped outright, whatever the mode above.")]
public ShieldVolume Shield
Field Value
Remarks
A different question from the arc above, answered for a different kind of damage. RequiresFacing gates the MITIGATION — how much is taken off damage that reaches the character, whether that is a sword, a burning ground or an explosion, none of which has a meaningful impact point on a shield. This volume gates PHYSICAL INTERCEPTION: an ability object whose impact lands inside it never touched the character at all, so it is stopped outright rather than reduced, and the mode above never comes into it.
Leaving Shape at None gives a buff
that only mitigates — a ward or a damage shield rather than a held shield. Setting it
gives an object in the world that arrows stop against, and it is the same shape
ShieldInterceptAction sweeps for things in flight.
VolumeBlockCost
Charges spent each time Shield stops something. Zero — the default — means blocking does not wear the buff down.
[Tooltip("Charges spent per object the shield volume stops. 0 means blocking never wears it down.")]
[Min(0)]
public int VolumeBlockCost
Field Value
Remarks
The knob that separates a shield bounded by TIME from one bounded by PUNISHMENT. Zero suits a channelled block, which ends when the player releases it. A positive value against Absorb makes each stopped projectile eat that much of the pool, so a barrier that has already turned three arrows has visibly less left in it.
Properties
InitialCharges
public override int InitialCharges { get; }
Property Value
Remarks
This is what makes the buff "disappear when the remaining amount hits 0": a non-zero value is what IsSpent tests, and the reduce and immune modes must NOT report one or they would be removed the first time anything asked.
Methods
OnApply(Buff, ICharacter)
Nothing to install. The pool is seeded by RefreshCharges() from InitialCharges, and the negation itself is read at damage time.
public override void OnApply(Buff buff, ICharacter target)
Parameters
buffBufftargetICharacter
Remarks
Deliberately empty rather than absent: OnApply(Buff, ICharacter) is abstract, and a passive buff having nothing to do on apply is a real answer rather than an omission.
OnApplyStack(Buff, ICharacter)
Refills the pool when a stack lands, so holding or re-casting a shield restores it.
public override void OnApplyStack(Buff buff, ICharacter target)
Parameters
buffBufftargetICharacter
Remarks
Buff.AddStack calls this BEFORE incrementing Stacks, so the
count after the operation is Stacks + 1 — the same off-by-one
OnApplyStack(Buff, ICharacter) documents. Refreshing to the
post-operation count is what makes two stacks hold twice the damage rather than the same
pool twice.
OnRemove(Buff, ICharacter)
Nothing to reverse — see OnApply(Buff, ICharacter).
public override void OnRemove(Buff buff, ICharacter target)
Parameters
buffBufftargetICharacter
OnRemoveStack(Buff, ICharacter)
Trims the pool back to what the remaining stacks are worth.
public override void OnRemoveStack(Buff buff, ICharacter target)
Parameters
buffBufftargetICharacter
Remarks
Buff.RemoveStack decrements after this returns, so the post-operation count is
Stacks - 1. Never RAISES the pool: a shield that has already spent most of its
charge must not be topped up by losing a stack.
ResolveNegation(int, int)
Damage this buff takes off incoming, without spending anything.
public int ResolveNegation(int incoming, int remainingCharges)
Parameters
incomingintDamage remaining after resistances and any earlier negation.
remainingChargesintThe buff instance's pool, for Absorb.
Returns
- int
How much of
incomingthis buff would take off.
Remarks
Pure, so the whole mitigation rule can be exercised without a character, a buff container
or a physics scene — the same reason LagCompensationTick.ResolveAnchor and
CharacterPredictionController.IsTransformRedundant are shaped this way. The caller
is responsible for the facing test and for spending the pool; this only answers "how
much".
SecondaryTooltip(TooltipContent)
Appends a secondary tooltip describing what this buff blocks.
public override void SecondaryTooltip(TooltipContent content)
Parameters
contentTooltipContent