• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

[Trigger] Trample system

Level 6
Joined
Feb 2, 2025
Messages
36
Hi, i'm currently working on a trample system for Cavalry and Monster units for my map. To get the idea of how it works, the cavalier or monster has a permanent invisible fire aura that deals neglectable amounts of damage on it's own (0.001). Whenever that aura hits a unit, the trigger checks if the cavalier isn't slowed down in any way and if it's moving, if both conditions are true the cavalier deals double his max damage to his enemies, stuns them and knocks them back (not implemented yet) when the target is appropriate (Spear units while stunned and knocked back, only receive the normal weapon damage and deal revenge damage onto the cavalier whenever they are hit by it, and monsters are simply too big to be affected). I intended for it to also put the cavalier under a deceleration effect. But while the first effects I made work fine (the double damage and the stun), the Cavalier isn't affected by my deceleration effect whatsoever.

I'm using the "Damage Engine" and the "Is unit Moving ?" Systems by Bribe.
Here is how my trigger looks is at the moment.
  • Charge Behaviour
    • Events
      • Game - PreDamageEvent becomes Equal to 0.00
    • Conditions
    • Actions
      • Set VariableSet Attacker = DamageEventSource
      • Set VariableSet Target = DamageEventTarget
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Attacker has buff Trample ) Equal to True
          • IsDamageSpell Equal to True
          • UnitMoving[(Custom value of Attacker)] Equal to True
          • (Attacker has buff Slow) Equal to False
          • (Attacker has buff Weakness) Equal to False
          • (Attacker has buff Purge) Equal to False
          • (Attacker has buff Slow poison (non-stackable)) Equal to False
          • (Attacker has buff Slow poison (Stackable)) Equal to False
          • (Attacker has buff Deceleration ) Equal to False
        • Then - Actions
          • Unit - Cause Attacker to damage Target, dealing (((Real((Base Damage of (Triggering unit) for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) x 2.00) damage of attack type Spells and damage type Normal
          • Set VariableSet Attacker_Location = (Position of Attacker)
          • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
          • Set VariableSet Charge_Dummy = (Last created unit)
          • Unit - Add Charge stun to Charge_Dummy
          • Unit - Add Charge Deceleration to Charge_Dummy
          • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
          • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
          • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
          • Custom script: call RemoveLocation(udg_Attacker_Location)
        • Else - Actions
          • Do nothing
Any idea what I did wrong ? Is there a function in the Damage Engine that would allow the deceleration effect to be applied to the cavalier ? Do i need another trigger dedicated specifically for it ? I really could use your help.
 
One Dummy, one Cast Rule,

Aka a unit can only process one order at a time. If the dummy's cast point isn't perfectly 0.000, or if it has to turn to face the new target, the second order will immediately overwrite and cancel the first one. It's highly likely one of your spells will randomly fail depending on the angle of the units. Try to use one dummy per cast.

But before that, I noticed that, if i recall this correctly, in Bribe’s Damage Engine, Triggering unit is the target (the unit taking damage). You should use your Attacker variable instead. Otherwise, the cavalier deals double the target’s base damage, not its own.
  • Unit - Cause Attacker to damage Target, dealing (((Real((Base Damage of (Triggering unit) for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) x 2.00) damage of attack type Spells and damage type Normal
You’re inside a PreDamageEvent and you deal spell damage. Your condition checks IsDamageSpell Equal to True ; this can cause recursive damage events. You should guard against it - disable the trigger temporarily or use a flag to avoid unintended loops.
! you mentioned the Trample is triggered by an invisible fire aura dealing 0.001 damage.

PreDamageEvent catches all damage, leaving main conditions out means the Cavalier's standard right-click physical attacks will also trigger the massive bonus damage, stun, and slow.

Also, make sure the deceleration ability allows the correct targets (self/friend), otherwise it won’t apply. Mana cost and cooldown should also be zero for dummy casting.

One thing you can do is test abilities manually first.

 
Last edited:
@the.Axolotl
An oversight on my part, it's fixed now. I also changed the flags for the deceleration ability as you said and everything works fine now. Only thing that remains is to apply the knockback for the main effect, and define the exceptions. As for the recursive damage, as you can see in the trigger i made sure the effects of the charge only triggers under very specific conditions (The Cavalier MUST be on the move for the effect to apply, if it simply stand idles and attack this won't trigger, thanks to the "Is Unit Moving ?" System that i use in conjuction with the "damage engine".) The only question left to answer will be if the system considers a unit being knocked back as a unit being on the move (even then i think checking if the unit is stunned or not should prevent the charge from trigerring if that's the case.)

Whatever the case thanks for your help, I'm still inexperienced in that domain, (I've only begun using GUI and exploring what the map editor allows last year and I've only picked it up again after months of break.)
 
Last edited:
@the.Axolotl
An oversight on my part, it's fixed now. I also changed the flags for the deceleration ability as you said and everything works fine now. Only thing that remains is to apply the knockback for the main effect, and define the exceptions. As for the recursive damage, as you can see in the trigger i made sure the effects of the charge only triggers under very specific conditions (The Cavalier MUST be on the move for the effect to apply, if it simply stand idles and attack this won't trigger, thanks to the "Is Unit Moving ?" System that i use in conjuction with the "damage engine".) The only question left to answer will be if the system considers a unit being knocked back as a unit being on the move (even then i think checking if the unit is stunned or not should prevent the charge from trigerring if that's the case.)

Whatever the case thanks for your help, I'm still inexperienced in that domain, (I've only begun using GUI and exploring what the map editor allows last year and I've only picked it up again after months of break.)
Welcome back!

The IsUnitMoving library scans all units on the map every 0.03125 seconds and compares their current xy coordinates to the ones it stored from the previous scan, so it will detect the movement, since most knockback systems use rapid change of unit's xy;

To be safe, you could add a simple cooldown or a last trample time check to your trigger to prevent it from firing too rapidly during a knockback.


About the recursion, the only way I see it happening would be:

  • The 0.001 Aura damage hits.
  • PreDamageEvent fires.
  • The system checks the boolean-true.
  • trigger orders the Cavalier to deal double damage
  • Double damage hits instantly.
  • IsUnitMoving system operates on a 0.03-second delay, while the above happened instantly
  • PreDamageEvent fires again
---} Recursion.


If it does happen at some point, the simplest way would be to add a condition that ignores the recursive damage chunk, such as checking if DamageEvent dmg less than 1.00 (so it only fires on the 0.001 aura tick), or add a flag check.

Overall though, really nice system idea 🍻
 
Ah, found another problem. For the spearmen exception, when I define it without checking if the cavalier has the deceleration debuff, it hurts the cavalier whenever it moves, and when i put it on it, the exception isn't taken into account at all. Here is how the triggers presents itself.

  • Charge Behaviour
    • Events
      • Game - PreDamageEvent becomes Equal to 0.00
    • Conditions
    • Actions
      • Set VariableSet Attacker = DamageEventSource
      • Set VariableSet Target = DamageEventTarget
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Attacker has buff Trample ) Equal to True
          • IsDamageSpell Equal to True
          • UnitMoving[(Custom value of Attacker)] Equal to True
          • (Attacker has buff Lenteur) Equal to False
          • (Attacker has buff Faiblesse) Equal to False
          • (Attacker has buff Purge) Equal to False
          • (Attacker has buff Poison lent (non cumulable)) Equal to False
          • (Attacker has buff Poison lent (Cumulable)) Equal to False
          • (Attacker has buff Deceleration ) Equal to False
        • Then - Actions
          • Unit - Cause Attacker to damage Target, dealing (((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) x 2.00) damage of attack type Siege and damage type Normal
          • Set VariableSet Attacker_Location = (Position of Attacker)
          • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
          • Set VariableSet Charge_Dummy = (Last created unit)
          • Unit - Add Charge stun to Charge_Dummy
          • Unit - Add Charge Deceleration to Charge_Dummy
          • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
          • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
          • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
          • Custom script: call RemoveLocation(udg_Attacker_Location)
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Ability: (Unit: Target's Ability with Ability Code: Charge Revenge )'s Boolean Field: Check Dependencies ('achd')) Equal to True
              • UnitMoving[(Custom value of Attacker)] Equal to True
              • (Attacker has buff Deceleration ) Equal to False
            • Then - Actions
              • Unit - Cause Target to damage Attacker, dealing (((Real((Base Damage of Target for weapon index 0))) + ((Real((Dice Number of Target for weapon index 0))) x (Real((Dice Sides of Target for weapon index 0))))) x 3.00) damage of attack type Pierce and damage type Normal
              • Unit - Cause Attacker to damage Target, dealing ((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) damage of attack type Siege and damage type Normal
              • Set VariableSet Attacker_Location = (Position of Attacker)
              • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
              • Set VariableSet Charge_Dummy = (Last created unit)
              • Unit - Add Charge stun to Charge_Dummy
              • Unit - Add Charge Deceleration to Charge_Dummy
              • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
              • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
              • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
              • Custom script: call RemoveLocation(udg_Attacker_Location)
            • Else - Actions
I hope I'm not a bother.
 
Hey, no worries

Right now both of your branches are running inside PreDamageEvent as we mentioned, and both use “Cause unit to damage…”.

So what ends up happening is yuour trample damage triggers; that damage fires the event again
  • Then your “revenge” branch also becomes valid and you get unintended extra damage or self-damage
The deceleration check doesn’t fix it because the buff is applied after the damage, while the recursion happens immediately before the buff exists.



Try with boolean flag, or try to disable the trigger temporarily.

Also, one thing to be careful with: doing both attacker damages target; target damages attacker inside the same damage event can easily create a feedback loop, so it’s good to guard that as well.

Try like this:


  • Actions
    • // CACHE
    • Set Attacker = DamageEventSource
    • Set Target = DamageEventTarget
    • // FILTER: only aura tick
    • If (DamageEventAmount < 1.00) then do (Actions)
      • If (All Conditions are True) then do (Actions)
        • Conditions:
          • (Attacker has buff Trample) Equal to True
          • IsDamageSpell Equal to True
          • UnitMoving[(Custom value of Attacker)] Equal to True
          • (Attacker has buff Deceleration) Equal to False
      • Then:
        • If (Target has Charge Revenge) Equal to True
          • // Spearman
          • Unit - Cause Target to damage Attacker
          • Unit - Cause Attacker to damage Target
        • Else
          • // Normal trample
          • Unit - Cause Attacker to damage Target
        • // Dummy effects here
 
Last edited:
Rearranged as you said and it now works as intended. I had forgotten that you had to put the intended effect in the "else" section and put the exceptions before.
The last exception I intend to do is for the charge to do Nothing to Monsters, Buildings and Mechanical units. (while it would be fun to have a cavalier stun and knockback a barrack. It would strike most people as immersion breaking.)
After that, all that'll remain to be done is to set up the knockback effect.
I'll also add the check you've put into your example, for good measure.

Thanks a lot man.
 
One Dummy, one Cast Rule,
Just to clarify, which you basically explained already but...

One Dummy can cast any number of abilities IF it's setup to do so. That means it has the settings you mentioned about Cast Point = 0.00 and Movement Type = None.

So I'd scrap that as a rule (sorry for being a stickler). To this day all of the tutorials on Dummys are basically wrong so unfortunately most people are misinformed.
 
Just to clarify, which you basically explained already but...

One Dummy can cast any number of abilities IF it's setup to do so. That means it has the settings you mentioned about Cast Point = 0.00 and Movement Type = None.
Yup, I remember that, you taught me that in my first ever post - Withering Fire,... for this case I went with multiple dummies mostly for convenience/readability while testing.

Buuut, as u wish :
Code:
call RemoveRule(udg_OneDummyOneCastRule)
 
Last edited:
New issue found, I implemented the knockback effect (via knockback 2.5D), but instead of being repulsed into a straight line the charge's victim gets shaken in all directions and, as a result, barely moves from it's initial position. Here is how it looks at the moment.
  • Charge Behaviour
    • Events
      • Game - PreDamageEvent becomes Equal to 0.00
    • Conditions
    • Actions
      • Set VariableSet Attacker = DamageEventSource
      • Set VariableSet Target = DamageEventTarget
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Less than 1.00
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Attacker has buff Trample ) Equal to True
              • IsDamageSpell Equal to True
              • UnitMoving[(Custom value of Attacker)] Equal to True
              • (Attacker has buff Slow) Equal to False
              • (Attacker has buff Cripple) Equal to False
              • (Attacker has buff Purge) Equal to False
              • (Attacker has buff Slow Poison (non stackable)) Equal to False
              • (Attacker has buff Slow Posion (stackable)) Equal to False
              • (Attacker has buff Deceleration ) Equal to False
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Ability: (Unit: Target's Ability with Ability Code: Charge Revenge )'s Boolean Field: Check Dependencies ('achd')) Equal to Vrai
                  • UnitMoving[(Custom value of Attacker)] Equal to Vrai
                • Then - Actions
                  • Set VariableSet CenterPoint = (Position of Attacker)
                  • Set VariableSet TargetPoint = (Position of Target)
                  • Set VariableSet Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
                  • Custom script: call RemoveLocation(udg_CenterPoint)
                  • Custom script: call RemoveLocation(udg_TargetPoint)
                  • Set VariableSet Knockback2DTime = 0.90
                  • Set VariableSet Knockback2DDistance = 760.00
                  • Set VariableSet Knockback2DUnit = Target
                  • Unit - Cause Target to damage Attacker, dealing (((Real((Base Damage of Target for weapon index 0))) + ((Real((Dice Number of Target for weapon index 0))) x (Real((Dice Sides of Target for weapon index 0))))) x 3.00) damage of attack type Pierce and damage type Universal
                  • Unit - Cause Attacker to damage Target, dealing ((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) damage of attack type Siege and damage type Universal
                  • Set VariableSet Attacker_Location = (Position of Attacker)
                  • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
                  • Set VariableSet Charge_Dummy = (Last created unit)
                  • Unit - Add Charge stun to Charge_Dummy
                  • Unit - Add Charge Deceleration to Charge_Dummy
                  • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
                  • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
                  • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
                  • Custom script: call RemoveLocation(udg_Attacker_Location)
                  • Trigger - Run Knockback 2D <gen> (checking conditions)
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Or - Any (Conditions) are true
                        • Conditions
                          • (Target is Mechanical) Equal to Vrai
                          • (Target is An Ancient) Equal to Vrai
                          • (Target is A town-hall-type unit) Equal to Vrai
                          • (Target is A structure) Equal to Vrai
                    • Then - Actions
                      • Do nothing
                    • Else - Actions
                      • Set VariableSet CenterPoint = (Position of Attacker)
                      • Set VariableSet TargetPoint = (Position of Target)
                      • Set VariableSet Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
                      • Custom script: call RemoveLocation(udg_CenterPoint)
                      • Custom script: call RemoveLocation(udg_TargetPoint)
                      • Set VariableSet Knockback2DTime = 0.90
                      • Set VariableSet Knockback2DDistance = 760.00
                      • Set VariableSet Knockback2DUnit = Target
                      • Unit - Cause Attacker to damage Target, dealing (((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) x 2.00) damage of attack type Siege and damage type Universal
                      • Set VariableSet Attacker_Location = (Position of Attacker)
                      • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
                      • Set VariableSet Charge_Dummy = (Last created unit)
                      • Unit - Add Charge stun to Charge_Dummy
                      • Unit - Add Charge Deceleration to Charge_Dummy
                      • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
                      • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
                      • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
                      • Custom script: call RemoveLocation(udg_Attacker_Location)
                      • Trigger - Run Knockback 2D <gen> (checking conditions)
            • Else - Actions
              • Do nothing
        • Else - Actions
          • Do nothing
Any idea why ? Can it be fixed by using the other settings of Knockback 2.5D ?

Edit : NEVERMIND !!! The extra parameters did the work. And with that my Trample System is officially complete. Thank you everyone.
 
Last edited:
One problem resolved, another emerges. The creation of that ability has broken "Detachment 1.2.0.0", the squad system i use for my map. Worst still that was only squad system within the hive workshop with a built-in replenishment system😔. Maybe a compatibility issue between Bribe's systems and the ones that system uses ?

Edit : Incompatibility CONFIRMED (disabled my ability and removed bribe's systems. And "Detachment" worked again flawlessly). And that's quite the bother, i don't know enough about Jass and Lua enough to identify what causes this incompatibility and fix it.
 
Last edited:
I found what was the incompatibility, it was a parameter within the AutoIndex library used by the Detachment system that conflicted with Bribe's Indexer (the "UseUnitUserData") turned it to false and and both systems work fine now. (That's a HUGE relief, though before I found that out...thinking i had to give up on either my ability or the squad system, i have deleted my squad registry as well as the abilities and some of the leading units of the squads I had already defined. The price of rushing to conclusions i guess. Will take some time to redo it all...)
 
I would like to finetune my charge system a bit, I want to add a tracker of the number of units the cavalier/monster has hit with the charge, and have the deceleration trigger only when they reach a certain threshold that is proper to them (For example : If the cavalier is, let's say, a night elf huntress with a deceleration threshold of 1, it is hit immediately by the deceleration effect, whereas a knight with a deceleration threshold of 2 can charge 2 units before being decelerated). I figured i would need a Hashtable to stock the thresholds for each cavalry and monster unit i intend to make and call upon these data in my charge behaviour script. But i wonder how i can do the rest.
 
Solid idea, let’s make it work:
U’ll need:
a hit counter (integer array, using Custom Value as indices). threshold table (hashtable, storing per‑unit‑type max hits).

The rest is just hooking them together and resetting the counter properly.

=== Hit counter array===

Create an integer array, TrampleHits.

Inside your charge trigger (inside the condition that already checks for the aura tick and UnitMoving):

  • Set TrampleHits[(Custom value of Attacker)] = TrampleHits[(Custom value of Attacker)] + 1
Hashtable thresholds:

On map init, store your thresholds. Use the unit‑type id as key.

  • Hashtable - Create a hashtable,
  • set TrampleHash = last created hash
//—Example: Huntress can hit 1 target before decelerating, Knight can hit 2
  • Custom script: call SaveInteger(udg_TrampleHash, 'eHun', 0, 1)
  • Custom script: call SaveInteger(udg_TrampleHash, 'hKni', 0, 2)
//(Replace 'eHun' and 'hKni' with your actual unit rawcodes.)

—Conditional deceleration—

Inside your "successful trample" block, wrap the dummy creation/deceleration cast inside a check:

  • Custom script: set udg_TempInt = LoadInteger(udg_TrampleHash, GetUnitTypeId(udg_Attacker), 0)
  • Custom script: if (TrampleHits[(Custom value of Attacker)] >= TempInt) then
  • // create dummy, add stun & deceleration, issue orders
  • Custom script: endif
If the threshold is 1, the deceleration will be applied on the very first hit. If it’s 2, the first hit only does damage & stun, the second hit also adds the deceleration.

!!Resetting the counter—-

otherwise a knight would trigger the deceleration on the very first unit of the next charge as well. You need to clear the counter when the unit stops moving.

Since you’re already using IsUnitMoving, the easiest is a periodic check (or even just inside the same aura tick); but a cleaner way is a separate trigger:

  • Charge Reset
    • Events
      • Time - Every 0.25 seconds of game time
    • Conditions
    • Actions
      • // Loop through your cavalry units (or just all units with Trample buff)
      • Custom script: if UnitMoving[(Custom value of Picked Unit)] == false then
      • Custom script: set TrampleHits[(Custom value of Picked Unit)] = 0
      • Custom scripts: endif
Alternatively, you can store the last move time in the hashtable and clear the counter a few seconds after the last hit.

! Edge cases that come to mind:

—- When a unit dies, you don’t strictly need to clear the counter (the index gets recycled), but it’s good practice.
— Morph, unlikely for now; ignore

—-Multiplayer safety: If you ever take this multiplayer, the hash operations in custom script are fine as long as you don't use GetLocalPlayer() to alter the hash.

A small efficiency tip:

You'll find that writing hashtable operations directly in custom script is faster and much more readable, especially as your system grows. For example, instead of:
  • Hashtable - Save 1 as 0 of (Key (Triggering unit)) in MyHash
Do this:
  • Custom script: call SaveInteger(udg_MyHash, GetHandleId (GetTriggerUnit()), 0, 1)

It's the same speed, but you don't have to fight the GUI dropdowns, and it's far easier to copy/paste and debug. I'd recommend switching to this style early on.
 
Last edited:
Thanks a lot man, getting on it right away.
Edit : Hum probably did a mistake somewhere. Only a single unit gets stunned by the charge now.
Here is how my trigger looks right now.
  • Charge Behaviour
    • Events
      • Game - PreDamageEvent becomes Equal to 0.00
    • Conditions
    • Actions
      • Set VariableSet Attacker = DamageEventSource
      • Set VariableSet Target = DamageEventTarget
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Less than 1.00
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Attacker has buff Trample ) Equal to True
              • IsDamageSpell Equal to True
              • UnitMoving[(Custom value of Attacker)] Equal to True
              • (Attacker has buff Slow) Equal to False
              • (Attacker has buff Cripple) Equal to False
              • (Attacker has buff Purge) Equal to False
              • (Attacker has buff Slow Poison (non stackable)) Equal to False
              • (Attacker has buff Slow Poison (Stackable)) Equal to False
              • (Attacker has buff Deceleration ) Equal to False
              • (Attacker has buff Stun (Pause)) Equal to False
            • Then - Actions
              • Set VariableSet Charge_Hits[(Custom value of Attacker)] = (Charge_Hits[(Custom value of Attacker)] + 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Or - Any (Conditions) are true
                    • Conditions
                      • (Target is Mechanical) Equal to True
                      • (Target is An Ancient) Equal to True
                      • (Target is A town-hall-type unit) Equal to True
                      • (Target is A structure) Equal to True
                      • (Target has buff Étourdi (Pause)) Equal to True
                • Then - Actions
                  • Do nothing
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Ability: (Unit: Target's Ability with Ability Code: Charge Revenge )'s Boolean Field: Check Dependencies ('achd')) Equal to True
                      • UnitMoving[(Custom value of Attacker)] Equal to True
                    • Then - Actions
                      • Set VariableSet CenterPoint = (Position of Attacker)
                      • Set VariableSet TargetPoint = (Position of Target)
                      • Set VariableSet Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
                      • Custom script: call RemoveLocation(udg_CenterPoint)
                      • Custom script: call RemoveLocation(udg_TargetPoint)
                      • Set VariableSet Knockback2DTime = 0.50
                      • Set VariableSet Knockback2DDistance = ((Default movement speed of Attacker) / 2.00)
                      • Set VariableSet Knockback2DUnit = Target
                      • Set VariableSet Knockback2DAmphibious = False
                      • Set VariableSet Knockback2DBounces = False
                      • Set VariableSet Knockback2DCollision = 16.00
                      • Set VariableSet Knockback2DDestRadius = 128.00
                      • Set VariableSet Knockback2DKillTrees = False
                      • Set VariableSet Knockback2DPause = True
                      • Set VariableSet Knockback2DSimple = True
                      • Set VariableSet Knockback2DOverride = True
                      • Unit - Cause Target to damage Attacker, dealing (((Real((Base Damage of Target for weapon index 0))) + ((Real((Dice Number of Target for weapon index 0))) x (Real((Dice Sides of Target for weapon index 0))))) x 3.00) damage of attack type Pierce and damage type Universal
                      • Unit - Cause Attacker to damage Target, dealing ((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) damage of attack type Siege and damage type Universal
                      • Custom script: set udg_Charge_Deceleration_Threshold = LoadInteger(udg_Charge_Hashtable, GetUnitTypeId(udg_Attacker), 0)
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Charge_Hits[(Custom value of Attacker)] Equal to Charge_Deceleration_Threshold
                        • Then - Actions
                          • Set VariableSet Attacker_Location = (Position of Attacker)
                          • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
                          • Set VariableSet Charge_Dummy = (Last created unit)
                          • Unit - Add Charge stun to Charge_Dummy
                          • Unit - Add Charge Deceleration to Charge_Dummy
                          • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
                          • Custom script: call RemoveLocation(udg_Attacker_Location)
                        • Else - Actions
                      • Trigger - Run Knockback 2D <gen> (checking conditions)
                    • Else - Actions
                      • Set VariableSet CenterPoint = (Position of Attacker)
                      • Set VariableSet TargetPoint = (Position of Target)
                      • Set VariableSet Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
                      • Custom script: call RemoveLocation(udg_CenterPoint)
                      • Custom script: call RemoveLocation(udg_TargetPoint)
                      • Set VariableSet Knockback2DTime = 0.50
                      • Set VariableSet Knockback2DDistance = ((Default movement speed of Attacker) / 2.00)
                      • Set VariableSet Knockback2DUnit = Target
                      • Set VariableSet Knockback2DAmphibious = False
                      • Set VariableSet Knockback2DBounces = False
                      • Set VariableSet Knockback2DCollision = 16.00
                      • Set VariableSet Knockback2DDestRadius = 128.00
                      • Set VariableSet Knockback2DKillTrees = False
                      • Set VariableSet Knockback2DPause = True
                      • Set VariableSet Knockback2DSimple = True
                      • Set VariableSet Knockback2DOverride = True
                      • Custom script: set udg_Charge_Deceleration_Threshold = LoadInteger(udg_Charge_Hashtable, GetUnitTypeId(udg_Attacker), 0)
                      • Unit - Cause Attacker to damage Target, dealing (((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) x 3.00) damage of attack type Siege and damage type Universal
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Charge_Hits[(Custom value of Attacker)] Equal to Charge_Deceleration_Threshold
                        • Then - Actions
                          • Set VariableSet Attacker_Location = (Position of Attacker)
                          • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
                          • Set VariableSet Charge_Dummy = (Last created unit)
                          • Unit - Add Charge stun to Charge_Dummy
                          • Unit - Add Charge Deceleration to Charge_Dummy
                          • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
                          • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
                          • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
                          • Custom script: call RemoveLocation(udg_Attacker_Location)
                        • Else - Actions
                      • Trigger - Run Knockback 2D <gen> (checking conditions)
            • Else - Actions
              • Do nothing
        • Else - Actions
          • Do nothing
 
Last edited:
Thanks a lot man, getting on it right away.
Edit : Hum probably did a mistake somewhere. Only a single unit gets stunned by the charge now.
Here is how my trigger looks right now.
  • Charge Behaviour
    • Events
      • Game - PreDamageEvent becomes Equal to 0.00
    • Conditions
    • Actions
      • Set VariableSet Attacker = DamageEventSource
      • Set VariableSet Target = DamageEventTarget
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Less than 1.00
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Attacker has buff Trample ) Equal to True
              • IsDamageSpell Equal to True
              • UnitMoving[(Custom value of Attacker)] Equal to True
              • (Attacker has buff Slow) Equal to False
              • (Attacker has buff Cripple) Equal to False
              • (Attacker has buff Purge) Equal to False
              • (Attacker has buff Slow Poison (non stackable)) Equal to False
              • (Attacker has buff Slow Poison (Stackable)) Equal to False
              • (Attacker has buff Deceleration ) Equal to False
              • (Attacker has buff Stun (Pause)) Equal to False
            • Then - Actions
              • Set VariableSet Charge_Hits[(Custom value of Attacker)] = (Charge_Hits[(Custom value of Attacker)] + 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Or - Any (Conditions) are true
                    • Conditions
                      • (Target is Mechanical) Equal to True
                      • (Target is An Ancient) Equal to True
                      • (Target is A town-hall-type unit) Equal to True
                      • (Target is A structure) Equal to True
                      • (Target has buff Étourdi (Pause)) Equal to True
                • Then - Actions
                  • Do nothing
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Ability: (Unit: Target's Ability with Ability Code: Charge Revenge )'s Boolean Field: Check Dependencies ('achd')) Equal to True
                      • UnitMoving[(Custom value of Attacker)] Equal to True
                    • Then - Actions
                      • Set VariableSet CenterPoint = (Position of Attacker)
                      • Set VariableSet TargetPoint = (Position of Target)
                      • Set VariableSet Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
                      • Custom script: call RemoveLocation(udg_CenterPoint)
                      • Custom script: call RemoveLocation(udg_TargetPoint)
                      • Set VariableSet Knockback2DTime = 0.50
                      • Set VariableSet Knockback2DDistance = ((Default movement speed of Attacker) / 2.00)
                      • Set VariableSet Knockback2DUnit = Target
                      • Set VariableSet Knockback2DAmphibious = False
                      • Set VariableSet Knockback2DBounces = False
                      • Set VariableSet Knockback2DCollision = 16.00
                      • Set VariableSet Knockback2DDestRadius = 128.00
                      • Set VariableSet Knockback2DKillTrees = False
                      • Set VariableSet Knockback2DPause = True
                      • Set VariableSet Knockback2DSimple = True
                      • Set VariableSet Knockback2DOverride = True
                      • Unit - Cause Target to damage Attacker, dealing (((Real((Base Damage of Target for weapon index 0))) + ((Real((Dice Number of Target for weapon index 0))) x (Real((Dice Sides of Target for weapon index 0))))) x 3.00) damage of attack type Pierce and damage type Universal
                      • Unit - Cause Attacker to damage Target, dealing ((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) damage of attack type Siege and damage type Universal
                      • Custom script: set udg_Charge_Deceleration_Threshold = LoadInteger(udg_Charge_Hashtable, GetUnitTypeId(udg_Attacker), 0)
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Charge_Hits[(Custom value of Attacker)] Equal to Charge_Deceleration_Threshold
                        • Then - Actions
                          • Set VariableSet Attacker_Location = (Position of Attacker)
                          • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
                          • Set VariableSet Charge_Dummy = (Last created unit)
                          • Unit - Add Charge stun to Charge_Dummy
                          • Unit - Add Charge Deceleration to Charge_Dummy
                          • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
                          • Custom script: call RemoveLocation(udg_Attacker_Location)
                        • Else - Actions
                      • Trigger - Run Knockback 2D <gen> (checking conditions)
                    • Else - Actions
                      • Set VariableSet CenterPoint = (Position of Attacker)
                      • Set VariableSet TargetPoint = (Position of Target)
                      • Set VariableSet Knockback2DAngle = (Angle from CenterPoint to TargetPoint)
                      • Custom script: call RemoveLocation(udg_CenterPoint)
                      • Custom script: call RemoveLocation(udg_TargetPoint)
                      • Set VariableSet Knockback2DTime = 0.50
                      • Set VariableSet Knockback2DDistance = ((Default movement speed of Attacker) / 2.00)
                      • Set VariableSet Knockback2DUnit = Target
                      • Set VariableSet Knockback2DAmphibious = False
                      • Set VariableSet Knockback2DBounces = False
                      • Set VariableSet Knockback2DCollision = 16.00
                      • Set VariableSet Knockback2DDestRadius = 128.00
                      • Set VariableSet Knockback2DKillTrees = False
                      • Set VariableSet Knockback2DPause = True
                      • Set VariableSet Knockback2DSimple = True
                      • Set VariableSet Knockback2DOverride = True
                      • Custom script: set udg_Charge_Deceleration_Threshold = LoadInteger(udg_Charge_Hashtable, GetUnitTypeId(udg_Attacker), 0)
                      • Unit - Cause Attacker to damage Target, dealing (((Real((Base Damage of Attacker for weapon index 0))) + ((Real((Dice Number of Attacker for weapon index 0))) x (Real((Dice Sides of Attacker for weapon index 0))))) x 3.00) damage of attack type Siege and damage type Universal
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Charge_Hits[(Custom value of Attacker)] Equal to Charge_Deceleration_Threshold
                        • Then - Actions
                          • Set VariableSet Attacker_Location = (Position of Attacker)
                          • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
                          • Set VariableSet Charge_Dummy = (Last created unit)
                          • Unit - Add Charge stun to Charge_Dummy
                          • Unit - Add Charge Deceleration to Charge_Dummy
                          • Unit - Add a 3.00 second Generic expiration timer to Charge_Dummy
                          • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
                          • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
                          • Custom script: call RemoveLocation(udg_Attacker_Location)
                        • Else - Actions
                      • Trigger - Run Knockback 2D <gen> (checking conditions)
            • Else - Actions
              • Do nothing
        • Else - Actions
          • Do nothing
stun and knockback are "trapped" inside the threshold check. This means those effects only fire when the counter exactly hits the limit, rather than on every unit you trample—in other words:

By putting the Stun inside the If Hits == Threshold block, the Knight now only stuns the exact unit that makes him slow down. He "forgot" how to stun the first 1–2 units because the trigger now skips the dummy creation entirely until the counter hits the limit.

Here is what we gonna do, we need to separate the Every Hit effects (Stun/Knockback) from the Threshold effect (Deceleration).

Then we need to get the exact action sequences in both spearmen and normal branches

1st we increment and load
  • Set Charge_Hits[(Custom value of Attacker)] = (Charge_Hits[(Custom value of Attacker)] + 1)
    • Custom script: set udg_Charge_Deceleration_Threshold = LoadInteger(udg_Charge_Hashtable, GetUnitTypeId(udg_Attacker), 0)

2nd every hit effects:
  • Set Attacker_Location = (Position of Attacker)
    • Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location facing Default building facing degrees
  • Set Charge_Dummy = (Last created unit)
  • Unit - Add a 2.00 second Generic expiration timer to Charge_Dummy
  • Unit - Add Charge stun to Charge_Dummy
  • Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
  • Trigger - Run Knockback 2D <gen> (checking conditions)
  • Custom script: call RemoveLocation(udg_Attacker_Location)

!! Threshold check!!
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions:
      • Charge_Hits[(Custom value of Attacker)] Greater than or equal to Charge_Deceleration_Threshold
    • Then - Actions:
      • Unit - Add Charge Deceleration to Charge_Dummy
      • Unit - Order Charge_Dummy to Human Sorceress - Slow Attacker
      • Set Charge_Hits[(Custom value of Attacker)] = 0 // Optional: Resets the count for the next charge
  • • Else - Actions: leave empty do not use do nothing !

!Note:
The Greater Than Safety: Using >= ensures that if the counter somehow skips a number (which happens in high-speed triggers), the deceleration still fires.

Resetting the counter to 0 inside the Then block ensures that once the cavalier finishes their slow-down and starts moving again, the counter starts fresh.

Make sure your Charge_Deceleration_Threshold in the Hashtable is set to at least 1 for all your units at Map Initialization. If it's 0, the cavalier will slow down on the very first hit

If the dummy does not fire both spells for some strange reason, but if it’s set correctly, as Uncle mentioned, you can separate the cast :

Code:
// Per-hit dummy (stun only)
Set Attacker_Location = (Position of Attacker)
Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location …
Set Charge_Dummy = (Last created unit)
Unit - Add a 2.00 second Generic expiration timer to Charge_Dummy
Unit - Add Charge stun to Charge_Dummy
Unit - Order Charge_Dummy to Human Mountain King - Storm Bolt Target
Custom script:   call RemoveLocation(udg_Attacker_Location)

// On threshold, create a second dummy for deceleration
If (Charge_Hits[(Custom value of Attacker)] >= Charge_Deceleration_Threshold) then
    Set Attacker_Location = (Position of Attacker)
    Unit - Create 1 dummy for (Owner of Attacker) at Attacker_Location …
    Set Charge_Decel_Dummy = (Last created unit)
    Unit - Add a 2.00 second Generic expiration timer to Charge_Decel_Dummy
    Unit - Add Charge Deceleration to Charge_Decel_Dummy
    Unit - Order Charge_Decel_Dummy to Human Sorceress - Slow Attacker
    Custom script:   call RemoveLocation(udg_Attacker_Location)
    Set Charge_Hits[(Custom value of Attacker)] = 0
Endif

If anything behaves weird after this, post again and we’ll refine it.

Edit: One thing that is scratching my brain :

If the Knight hits an enemy that has a "Thorns" or "Feedback" stun, or if the first unit he hits stuns him back, trample system shuts off midcharge…For a singleplayer map, this is actually a cool "Counter-play" mechanic. You can ignore that check, but this comes to design philosophy.
 
Last edited:
That optional part of yours sounds more like "ABSOLUTELY vital" to me. :xxd:

Jokes Aside, the finetuned system works fine now, thanks a lot man.

Though it's part of a "Battle for Middle Earth II" gameplay adaptation for Warcraft 3 at the moment (still very early in it's development), I'm seriously considering transfering the end result of this passive charge on another map, to have it published on the site.

Edit : OUCH, another problem emerged...I've got repeated hits on the same unit now. Doesn't seem to trigger everytime though. When a unit is alone it seems to work as intended, but when there are multiple units nearby the charge triggers twice on the same target if they are spread around a bit.
 
Last edited:
This is probably due to knockback + aura ….If the knockback keeps the target inside the aura, or the unit “jitters” during movement, the engine can register it as a new hit. Since your system only stops when the Attacker gets deceleration, the Target is still valid and gets processed again following that logic.

Let’s try simple fix first that relies on existing stun as anti-spam lock.

Add this to your main Conditions:
(Target has buff Charge Stun) Equal to false


Note that this relies on stun always being applied and stun lasting long enough, I believe around 1.00 is fine.

If you later add targets that shouldn’t be stunned, you’ll need a separate anti-spam method (boolean/timer)

If you see units jitter during knockback, make sure:
  • Knockback2DOverride = True

**
Ahh that actually makes perfect sense for a BFME-style system 😄 the charge flowing through multiple units is exactly the feel you want.
Also yeah…at this point you’re not fixing the system anymore, you’re just polishing edge cases.
 
Thanks again. All is good now.
No problem, I'm glad we made it work. Anyways, since you mentioned scaling and publishing it, I'd recommend to look into the Time Stamp method - this could be particularly useful as you move forward with your project.

Buff check is more of a hack solution than a proper one, but I didn't want to plague you with everything all of a sudden. But note that the buff hack can fail if stun isn't applied (immune units, bosses, future changes).

Time stamp requires a game timer and hashtable ( you can use the existig hashtable just save memory on different key slots), slightly more setup. The game timer can be created once at the map init:

  • Custom script: set udg_GameTimer = CreateTimer()
  • Custom script: call TimerStart(udg_GameTimer, 99999.00, false, null)

Hashtables store data in a two‑dimensional layout: (parentKey, childKey) → value.
We are currently using:

Threshold data:
Parent key = GetUnitTypeId(unit) (e.g., 'hKni')
Child key = 0

For timestamps, you'd use a completely different parent key scope

Timestamp data:

Parent key = GetHandleId(targetUnit) (a unique integer per unit)
Child key = 1 (or another unused integer)
These two sets of entries never collide because the parent keys are from different domains (unit‑type IDs vs. handle IDs).

Here is the example of saving a timestamp after a sucessful hit:
  • Custom script: call SaveReal(udg_Charge_Hashtable, GetHandleId(udg_Target), 1, udg_CurrentTime)
Then later, we check the timestamp before applying effects:
  • Custom script: set udg_TargetHandle = GetHandleId(udg_Target)
  • Custom script: set udg_LastHitTime = LoadReal(udg_Charge_Hashtable, udg_TargetHandle, 1)
  • Custom script: if (udg_CurrentTime - udg_LastHitTime) >= 1.00 then
    • // apply effects
    • Custom script: call SaveReal(udg_Charge_Hashtable, udg_TargetHandle, 1, udg_CurrentTime)
  • Custom script: endif

One Small Note

If you later store something else under GetHandleId(unit) with the same child key (1), you'd overwrite the timestamp. But that's easy to avoid: just pick a consistent child key for each data type (e.g., 0 = thresholds, 1 = timestamps, 2 = hit counters etc). You're the one in control, basically.
Regards!
 
Last edited:
Congrats on the release! Tnx for the mention : )
I went through the posted system briefly:

There are a few things worth patching: the frontal cone issue, a memory leak, and a missing timer.

---The Frontal Cone Fix:
Auras are 360` circles;
add this single condition to your main If - Conditions block:


Set TempPoint1 = (Position of Attacker)
Set TempPoint2 = (Position of Target)

Set TempReal = ((Facing of Attacker) - (Angle from TempPoint1 to TempPoint2))
// (Angle is converted to radians for Cos)
If (Cos(TempReal * 0.01745) >= 0.50) then
// do stuff
Custom script: call RemoveLocation(udg_TempPoint1)
Custom script: call RemoveLocation(udg_TempPoint2)

The Cosine of 60 degrees is 0.5. By using this math, the trigger will only fire if the target is within a 120-degree cone directly in front of the charger.

---Your reset trigger fires every 0.25 seconds and uses this action:


Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)

The fix:

Set Variable Temp_Group = (Units in (Playable map area) matching (((Matching unit) has buff Deceleration) Equal to True))
Unit Group - Pick every unit in Temp_Group and do (Actions)
Loop - Actions
If (UnitMoving[(Custom value of (Picked unit))] Equal to False)
then
Set Charge_Hits[(Custom value of (Picked unit))] = 0
else // leave empty; avoid "Do nothing" as it still runs a function unnecessarily
Custom script: call DestroyGroup(udg_Temp_Group)

--} This is also much better for performance because it only picks the units that actually have the buff, rather than every single unit on the map

---"Charge Revenge" branch.
You forgot to add expiration timer for the dummy unit.

I noticed your Charge_Hits increments before you check if the target is a building/mechanical. This means if a Knight charges into a wall, he loses momentum (counts as a hit) even though the wall isn't knocked back. This is actually really realistic.

Potential edge case as I mentioned above - provides a more robust approach; This can be kept as-is for realism, but it’s worth noting as a potential edge case depending on design intent.

Don’t sweat the fixes too much; you’ve got plenty of time to roll out an update once feedback starts from reviewers. I'll keep an eye out as I play around with the system, and if any other optimizations come to mind I'll definitely let you know.
 
Another thing i noticed : unlike in the GUI version, the new system doesn't consider a stunned unit as an invalid target for a cavalry trample (a cavalier can trample the same unit twice in a row, and that's something i wanted to avoid originally. You've already done much with the system, making it much more configurable than i ever could.) I'll try and see if i can't add a vjass function to exclude stunned units of the available targets. (Not entirely unfamiliar reading the thing, and I can't have you do all the work, now can I ? :xxd:)
 
Another thing i noticed : unlike in the GUI version, the new system doesn't consider a stunned unit as an invalid target for a cavalry trample (a cavalier can trample the same unit twice in a row, and that's something i wanted to avoid originally. You've already done much with the system, making much more configurable than i ever could.) I'll try and see if i can't add a vjass function to exclude stunned units of the available targets. (Not entirely unfamiliar reading the thing, and I can't have you do all the work, now can I ? :xxd:)
Hahaha yeah, that makes sense xd
I was honestly playing around with it a bit too much and got carried away with how nice the sliding/knockback felt, so I didn’t even notice the repeated hits on stunned units.

Feel free to tweak that part however you like — I’m totally fine with you shaping it closer to your original behavior.

Easiest/safest way would probably be to just filter stunned units directly via the buff, something like:

GetUnitAbilityLevel(u, <stun_buff_rawcode>) == 0

Or alternatively, you could tie it to the stun ability itself if you prefer. :grin:
 
And it works !!! I Made a copy of the Charge Behaviour trigger, converted it to VJass, and used and adapted the "Target has buff Stunned (pause) = False" condition of my GUI code before adding it to the aura check of the new system. Here is how it looks.
JASS:
if not UnitHasBuffBJ(tgt, 'BPSE') == false then
        return false
    endif

Perhaps not what you had in mind though (was a bit of a noob here and couldn't figure out how to set up the function you gave me.:hohum:) If you have other examples of ways to get the same results I'm up for it. Could use any tip really before I update the system.
 
Last edited:
And it works !!! I Made a copy of the Charge Behaviour trigger, converted it to VJass, and used and adapted the "Target has buff Stunned (pause) = False" condition of my GUI code before adding it to the aura check of the new system. Here is how it looks.
JASS:
if not UnitHasBuffBJ(tgt, 'BPSE') == false then
        return false
    endif

Perhaps not what you had in mind though (was a bit of a noob here and couldn't figure out how to set up the function you gave me.:hohum:) If you have other examples of ways to get the same results I'm up for it. Could use any tip really before I update the system.
nice, you’re picking it up quickly 😄

That condition is kinda double-negating itself though lol — you can simplify it to:

if GetUnitAbilityLevel(tgt, 'BPSE') > 0 then
return false
endif
 
Modified the condition as you indicated. I also wonder if this function would have been a good way to optimise my "isn't slowed down any way" condition for the charge to trigger instead of checking for all possible speed debuffs in existence.

  • Untitled Trigger 001
    • Events
    • Conditions
      • (Current movement speed of Attacker) Less than (Default movement speed of Attacker)
    • Actions
JASS:
if GetUnitMoveSpeed(udg_Attacker) < GetUnitDefaultMoveSpeed(udg_Attacker) then
        return false
    endif

If that's the case i'll update the system with this condition as well.
 
Last edited:
Modified the condition as you indicated. I also wonder if this function would have been a good way to optimise my "isn't slowed down any way" condition for the charge to trigger instead of checking for all possible speed debuffs in existence.

  • Untitled Trigger 001
    • Events
    • Conditions
      • (Current movement speed of Attacker) Less than (Default movement speed of Attacker)
    • Actions
JASS:
if GetUnitMoveSpeed(udg_Attacker) < GetUnitDefaultMoveSpeed(udg_Attacker) then
        return false
    endif
The only thing is that checking movement speed like that can get a bit unreliable once you have auras/items/upgrades involved — a unit might still be “slowed” but end up above its default speed.

If you want something a bit more deterministic, it’s usually better to gate it through the actual state/buff instead of the result. For example:

if GetUnitAbilityLevel(tgt, 'BPSE') > 0 then
return false
endif

Or even cleaner long-term, you can track it yourself when applying/removing the stun:

// on stun apply
call SaveBoolean(hash, GetUnitUserData(tgt), 10, true)

// on stun end
call SaveBoolean(hash, GetUnitUserData(tgt), 10, false)

And then you just:

if LoadBoolean(hash, GetUnitUserData(tgt), 10) then
return false
endif

That way you’re checking the actual state instead of trying to infer it, ah but don't overthink it , even your solution is totally fine : )
 
@the.Axolotl Hi ! How have you been doing ? I wanted to add a filter to our system so the charge don't trigger with units who have the Charge Ability. I tried with this condition...
JASS:
    if UnitHasBuffBJ(tgt, 'trample code') == true then
        return false
    endif

But instead of excluding the units with the Charge ability, it excludes...well...everyone.

Any idea how i can achieve that effect ?

Sorry in advance if I don't respond right away. It's very late at night where I live.

Edit : Nevermind, found it on my own, it was basicly the same as the Stun check with the Charge ability instead of the Stun. Sorry for the bother
 
Last edited:
@the.Axolotl Hi ! How have you been doing ? I wanted to add a filter to our system so the charge don't trigger with units who have the Charge Ability. I tried with this condition...
JASS:
    if UnitHasBuffBJ(tgt, 'trample code') == true then
        return false
    endif

But instead of excluding the units with the Charge ability, it excludes...well...everyone.

Any idea how i can achieve that effect ?

Sorry in advance if I don't respond right away. It's very late at night where I live.

Edit : Nevermind, found it on my own, it was basicly the same as the Stun check with the Charge ability instead of the Stun. Sorry for the bother
No worries, I was just a bit busy, glad you figured it out 🤓
 
Back
Top