Dynamically adding global

Level 8
Joined
Apr 16, 2025
Messages
160
The issue is that I can’t afford to rely on a global event every time it happens. For example, when a unit takes damage — the map becomes too heavy.

My question: is it possible to maintain a group of units that have spawned on the map in, let’s say, a global group, and then, when they attack, have the system check only those units instead of iterating through every unit on the map?

I’d appreciate any tips or guidance!
 
If you don't want the global damage event, then you could always use the specific unit event--i.e.:
  • GameStart
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Unit Group - Pick every unit in MyDamageGroup and do (Actions)
        • Loop - Actions
          • Trigger - Add to OnDamage <gen> the event (Unit - (Picked unit) Takes damage)
  • OnDamage
    • Events
    • Conditions
    • Actions
      • -------- Do your logic when a unit takes damage --------
But that only works for tracking when a specific unit takes damage. If you want to detect when any of those units cause damage, you will have no choice but to use the global event. If you're running into performance issues though, I'd just recommend checking if the damaging unit is in your unit group as early as possible in the trigger--and then skip the remaining actions if they aren't.
 
I would try to use a lightweight "Damage Engine". Bribe's system is fantastic but the fact that it covers EVERYTHING comes at the cost of performance. I know whenever I use it I only ever need 3-4 of the variables, 90% of the features go unused. The recursion protection is really the best feature in there but even that can be unneeded if you design your triggers around it. So if possible, ditch the jack of all trades approach and have a tightly focused system that only runs logic when it's necessary, tailored to your map's needs.

I attached a map with a very barebones example of this. Note that I can't promise that this will improve performance, it would really depend on how many Damage Events + complex Conditions you have running all of the time in your current setup. My design works like this:

At any time you can register a unit-type + trigger(s) to run whenever that unit-type deals damage.

Then you have one central trigger for when "damage is taken" which handles running those registered triggers:
  • SDE Damage Event
    • Events
      • Unit - A unit Takes damage
    • Conditions
    • Actions
      • Set VariableSet SDE_Source = (Damage source)
      • Custom script: set udg_SDE_Source_ID = GetUnitTypeId(udg_SDE_Source)
      • Set VariableSet SDE_Trigger_Count = (Load SDE_KEY_COUNT of SDE_Source_ID from SDE_Hash.)
      • -------- --------
      • -------- Skip if unregistered: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SDE_Trigger_Count Equal to 0
        • Then - Actions
          • Skip remaining actions
        • Else - Actions
      • -------- --------
So you set three variables, do a hashtable lookup, and a basic If Then Else that exits the trigger early if it fails to find a trigger to run. Pretty lightweight.

If the unit is registered with the system then the rest of the trigger will continue and handle the more complex execution logic:
  • -------- Continue if registered: --------
  • Set VariableSet SDE_Target = (Damage Target)
  • Set VariableSet SDE_Damage_Amount = (Damage taken)
  • -------- --------
  • -------- Run registered damage triggers (turn this off to avoid infinite loops): --------
  • Trigger - Turn off SDE Damage Event <gen>
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • SDE_Trigger_Count Equal to 1
    • Then - Actions
      • Trigger - Run (Load SDE_Trigger_Count of SDE_Source_ID in SDE_Hash.) (checking conditions)
    • Else - Actions
      • For each (Integer SDE_Loop) from 1 to SDE_Trigger_Count, do (Actions)
        • Loop - Actions
          • Trigger - Run (Load SDE_Loop of SDE_Source_ID in SDE_Hash.) (checking conditions)
  • Trigger - Turn on SDE Damage Event <gen>
So this design reduces the complexity when you have say 10+ triggers using the "A unit Takes damage" Event with a sum of 100+ Conditions all varying in complexity. Instead, you use one central trigger that will filter out any unwanted units early, avoiding the need of asking a bunch of impossible questions.

Then moving onto the trigger(s) that run in response to dealing damage, you would use SDE_Source, SDE_Target, and SDE_Damage_Amount which are automatically Set for you. Note that SDE_Damage_Amount is just a reference to a value, unlike in Bribe's system where it's used by the system to update the final damage dealt.
  • Paladin Deals Damage
    • Events
    • Conditions
      • (Unit-type of SDE_Target) Equal to Footman
      • (Owner of SDE_Target) Equal to Player 1 (Red)
    • Actions
      • -------- Note: This trigger was registered to run whenever any Paladin deals damage. --------
      • -------- You could modify the system to use specific unit handles instead of unit-type id! --------
      • -------- --------
      • Game - Display to (All players) for 5.00 seconds the text: (Damage dealer: + (Name of SDE_Source))
      • Game - Display to (All players) for 5.00 seconds the text: (Damage taker: + (Name of SDE_Target))
      • Game - Display to (All players) for 5.00 seconds the text: (Damage: + (String(SDE_Damage_Amount)))
      • -------- --------
      • Special Effect - Create a special effect attached to the weapon of SDE_Source using Abilities\Weapons\DragonHawkMissile\DragonHawkMissile.mdl
      • Special Effect - Destroy (Last created special effect)
      • -------- --------
      • Special Effect - Create a special effect attached to the origin of SDE_Target using Abilities\Spells\Other\Stampede\StampedeMissileDeath.mdl
      • Special Effect - Destroy (Last created special effect)
Note how we don't have to check if the Paladin was the (Damage source), that's already been confirmed, otherwise this trigger wouldn't be running in the first place.
 

Attachments

Last edited:
If you don't want the global damage event, then you could always use the specific unit event--i.e.:
  • GameStart
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Unit Group - Pick every unit in MyDamageGroup and do (Actions)
        • Loop - Actions
          • Trigger - Add to OnDamage <gen> the event (Unit - (Picked unit) Takes damage)
  • OnDamage
    • Events
    • Conditions
    • Actions
      • -------- Do your logic when a unit takes damage --------
But that only works for tracking when a specific unit takes damage. If you want to detect when any of those units cause damage, you will have no choice but to use the global event. If you're running into performance issues though, I'd just recommend checking if the damaging unit is in your unit group as early as possible in the trigger--and then skip the remaining actions if they aren't.

Well, as I thought, there's no native "unit attacks" feature, so I have to check every attack. Anyway, thanks!

WOW! So the damage is triggered once, and then it filters through to all other options? That blows my mind! Your head is full of brilliant ideas...
I would try to use a lightweight "Damage Engine". Bribe's system is fantastic but the fact that it covers EVERYTHING comes at the cost of performance. I know whenever I use it I only ever need 3-4 of the variables, 90% of the features go unused. The recursion protection is really the best feature in there but even that can be unneeded if you design your triggers around it. So if possible, ditch the jack of all trades approach and have a tightly focused system that only runs logic when it's necessary, tailored to your map's needs.

I attached a map with a very barebones example of this. Note that I can't promise that this will improve performance, it would really depend on how many Damage Events + complex Conditions you have running all of the time in your current setup. My design works like this:

At any time you can register a unit-type + trigger(s) to run whenever that unit-type deals damage.

Then you have one central trigger for when "damage is taken" which handles running those registered triggers:
  • SDE Damage Event
    • Events
      • Unit - A unit Takes damage
    • Conditions
    • Actions
      • Set VariableSet SDE_Source = (Damage source)
      • Custom script: set udg_SDE_Source_ID = GetUnitTypeId(udg_SDE_Source)
      • Set VariableSet SDE_Trigger_Count = (Load SDE_KEY_COUNT of SDE_Source_ID from SDE_Hash.)
      • -------- --------
      • -------- Skip if unregistered: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SDE_Trigger_Count Equal to 0
        • Then - Actions
          • Skip remaining actions
        • Else - Actions
      • -------- --------
So you set three variables, do a hashtable lookup, and a basic If Then Else that exits the trigger early if it fails to find a trigger to run. Pretty lightweight.

If the unit is registered with the system then the rest of the trigger will continue and handle the more complex execution logic:
  • -------- Continue if registered: --------
  • Set VariableSet SDE_Target = (Damage Target)
  • Set VariableSet SDE_Damage_Amount = (Damage taken)
  • -------- --------
  • -------- Run registered damage triggers (turn this off to avoid infinite loops): --------
  • Trigger - Turn off SDE Damage Event <gen>
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • SDE_Trigger_Count Equal to 1
    • Then - Actions
      • Trigger - Run (Load SDE_Trigger_Count of SDE_Source_ID in SDE_Hash.) (checking conditions)
    • Else - Actions
      • For each (Integer SDE_Loop) from 1 to SDE_Trigger_Count, do (Actions)
        • Loop - Actions
          • Trigger - Run (Load SDE_Loop of SDE_Source_ID in SDE_Hash.) (checking conditions)
  • Trigger - Turn on SDE Damage Event <gen>
So this design reduces the complexity when you have say 10+ triggers using the "A unit Takes damage" Event with a sum of 100+ Conditions all varying in complexity. Instead, you use one central trigger that will filter out any unwanted units early, avoiding the need of asking a bunch of impossible questions.

Then moving onto the trigger(s) that run in response to dealing damage, you would use SDE_Source, SDE_Target, and SDE_Damage_Amount which are automatically Set for you. Note that SDE_Damage_Amount is just a reference to a value, unlike in Bribe's system where it's used by the system to update the final damage dealt.
  • Paladin Deals Damage
    • Events
    • Conditions
      • (Unit-type of SDE_Target) Equal to Footman
      • (Owner of SDE_Target) Equal to Player 1 (Red)
    • Actions
      • -------- Note: This trigger was registered to run whenever any Paladin deals damage. --------
      • -------- You could modify the system to use specific unit handles instead of unit-type id! --------
      • -------- --------
      • Game - Display to (All players) for 5.00 seconds the text: (Damage dealer: + (Name of SDE_Source))
      • Game - Display to (All players) for 5.00 seconds the text: (Damage taker: + (Name of SDE_Target))
      • Game - Display to (All players) for 5.00 seconds the text: (Damage: + (String(SDE_Damage_Amount)))
      • -------- --------
      • Special Effect - Create a special effect attached to the weapon of SDE_Source using Abilities\Weapons\DragonHawkMissile\DragonHawkMissile.mdl
      • Special Effect - Destroy (Last created special effect)
      • -------- --------
      • Special Effect - Create a special effect attached to the origin of SDE_Target using Abilities\Spells\Other\Stampede\StampedeMissileDeath.mdl
      • Special Effect - Destroy (Last created special effect)
Note how we don't have to check if the Paladin was the (Damage source), that's already been confirmed or this trigger wouldn't be running in the first place.

I'll check it out and maybe report back on the performance in a few days.
 
That's nice to hear, but here's some questions to ask yourself along with some answers that might drive you away from using this design. I say this in hopes that I don't end up wasting your time:

Q: Do I want to be able to deal additional damage in response to a damage event?

A: This design will ignore new damage events while running your registered triggers. That means no crashes due to infinite loops, but it also means that you could skip over an important damage event. To be clear, this only skips damage events that are triggered from within your registered triggers.

Q: Looking at all of my damage event triggers, are there a lot of them and most importantly are the Conditions complex enough to justify using this?

A: Using the same event many times could have a noticeable performance cost but I can't say for certain.
A complex Condition would be something like "Is unit in a Unit Group" since it needs to enumerate over the group and potentially check each unit inside. If you organize your Conditions well, like PurgeandFire suggested, you could avoid this complexity by a lot. This is because Conditions will stop being checked the moment one fails. That means the order can be very important, for example, this is bad:
  • Events
    • Unit - A unit Takes damage
  • Conditions
    • ((Damage target) is in All_Enemy_Units_Group) Equal to True
    • (Damage target) Equal to Paladin 0001
This is good:
  • Events
    • Unit - A unit Takes damage
  • Conditions
    • (Damage target) Equal to Paladin 0001
    • ((Damage target) is in All_Enemy_Units_Group) Equal to True
A simple re-ordering can make all the difference. You could also go one step further and "mark" these units, that way you don't have to rely on the Unit Group function at all. It can be much faster to check say an Array that uses the unit's Custom Value (unit indexing), a Hashtable value, or an Ability of some sort (pseudo classification). Taking all of this into consideration and applying it to your map could help solve a lot of your map's performance problems.

Q: What about the "About to Take damage" Event and registering a trigger for when a unit TAKES damage rather than DEALS it?

A: The system could be expanded to support these features, but it would of course come at the cost of performance. It might still be worth it but the whole point is to use as barebones of a design as possible (max performance) and only expand upon it when you actually NEED a feature.
 
Last edited:
Back
Top