• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Trigger] Unit takes damage leaks

Status
Not open for further replies.
Level 7
Joined
Jun 1, 2009
Messages
104
Hi! From what I've read about "Unit takes damage" event - that it's extremely buggy and you will even need to destroy trigger (sic!) with this event added, cause events will stack up to infinite values.
But how about adding a variable array TempUnit[n]?
Will it fix the problem?
And what another bugs\glitches with this event I should know?

My little test trigger worked ofc. but I wonder, will it lag, leak or glitch in real situations:

  • TEST Damage
    • Events
      • Player - Player 1 (Red) types a chat message containing -td as A substring
    • Conditions
    • Actions
      • Set VariableSet TempUnit[0] = Dummy 0018 <gen>
      • Trigger - Add to TEST Damage Copy <gen> the event (Unit - TempUnit[0] Takes damage)
      • Trigger - Turn off (This trigger)
      • Game - Display to (All players) the text: DONE
  • TEST Damage Copy
    • Events
    • Conditions
    • Actions
      • Game - Display to (All players) the text: HEY! THAT'S HURTS!
Thanks for help!
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,537
That'll work fine with or without a variable, it doesn't make a difference. The variable is only necessary when you want to add to the Event units that aren't pre-placed on the map.

The issue that occurs when using this method is that you can only add a limited number of units to the Event. Meaning eventually it will stop working if you keep adding more and more units.

This problem only exists if you manage to break the limit, otherwise it's perfectly fine to do what you're doing.

Also, you can get around all of this if you use code instead of GUI. Or a Damage Engine of course.
vJASS:
EVENT_UNIT_DAMAGED
EVENT_PLAYER_UNIT_DAMAGED
There are Events for both specific and generic units now.
 
Level 7
Joined
Jun 1, 2009
Messages
104
The issue that occurs when using this method is that you can only add a limited number of units to the Event. .
Yeah, units are not pre-placed. ATM I'm using Unit is Attacked event which checks via For Each Integer A if TempUnit[IntegerA] and then starts timer[IntegerA].

What exactly this limit is? I need max. 64 events for this trigger.
Can split it in 8 triggers with 8 events.
 
Level 8
Joined
Mar 19, 2017
Messages
248
If the unit you registered is removed then you are technically at the presence of a memory leak (memory space and resources used for nothing). I don't know if this leak is sustantial though (ie. effect leaks are like 11 KB (a lot), removing a unit always leave you with 0.04 KB less of memory (you can't do nothing, but the value is marginal)).
This is why that method you are using usually needs a trigger rebuild function, unless, of course the units registered are intended to be permanent in your game. I don't know anything about a limit of units registered (i think this is a myth, see next parragraph).

Bribe old Damage Engine used a Unit Indexer, so whenever a unit was deindexed (basically removed from the game) a counter will increase up to a limit. When the limit was reached, the rebuild actions will trigger: you destroy the trigger, you add the condition/action all over again, and then you register all non-deindexed units again using the same unit indexer system for this matter. You could use a hashtable or even arrays as a pseudo indexer. I don't think that old Bribe's Damage Engine broke at all when registering lots of units, as it used the specific unit registration plus a trigger rebuild but that only fired when lots of units were removed from the game and not because there were simply lots of units.

But if you have a recent version of the game (don't know the exact version, i think it is 1.29) then there is no need to even do that, as recent patches feature Generic Unit Damage Events (just like ie. unit spell effect, unit pick up item, etc). These events are not accessible in GUI so you will need to code it in JASS (just an example):

JASS:
function codeVariable takes nothing returns nothing
 //damage stuff
endfunction

function myFunction takes nothing returns nothing
local trigger triggerVariable = CreateTrigger()

call TriggerRegisterAnyUnitEventBJ(triggerVariable, EVENT_PLAYER_UNIT_DAMAGED)
call TriggerAddCondition(triggerVariable, function codeVariable)

set triggerVariable = null
endfunction
Following this example and in a GUI Map Initialization Event you do a custom script: call myFunction().
You can make a global trigger variable in GUI, but here you will need to add the prefix 'udg_' on the registration (ie. udg_triggerGUIVariable).

You also have another useful related event:
JASS:
 EVENT_PLAYER_UNIT_DAMAGING
This fires before UNIT_DAMAGED.

Or you can always abandon all this and use a Damage Detection System if you want. If you know some JASS and because of the new 1.29+ functions i think this is unecessary unless you want a specific feature on a DDS (ie. Bribe's Damage Engine has reccursion safety, a good event API, and you can detect AOE damage, but you bet that the price is that it uses lots of resources).
 
Level 7
Joined
Jun 1, 2009
Messages
104
Oh, I'm appreciate your advertise, but NO. I'm not buying this stuff. I don't want any systems imported just for one bloody goddamn mofo single trigger. Only max. 8 units per player managed. Oh cm-on.
 
Level 8
Joined
Mar 19, 2017
Messages
248
Then the answer is this:
Yes your method will "leak" if you plan to register units that will be removed from your game (ie. dying). To avoid these leaks you will need a trigger rebuild function. Otherwise no. I don't know if specific unit events break when registering lots of units, i think this is a myth or maybe the answer is the max Jass array size (depends on your version) or whatever.
To asess your other questions i think you will need to tell us what version of the game you are using.
 
Level 7
Joined
Jun 1, 2009
Messages
104
Thanks alot! But im not sure about vJASS code you've mentioned. Will EVENT_PLAYER_UNIT_DAMAGING work for computer players?
If the unit you registered is removed then you are technically at the presence of a memory leak (memory space and resources used for nothing).
TempUnit[n] won't being removed, but it will be killed\exploded in most cases. Not sure if this counts.

My game version is 1.32.8.158 or whatever Blizzards have in their launcher. Non-reforged.
 
Level 8
Joined
Mar 19, 2017
Messages
248
Is a generic event so it will trigger the actions (in this case i registered a condition, which is better) for any unit, no matter the player: TriggerRegisterAnyUnitEventBJ.

Most units that die will be removed after they finished decaying, unless you resurrect them in the meantime, the exception are heroes. So it counts. My point is that you plan to register units on a dynamic way, with lots of things going on (units being created and dying over and over again, etc.). So you either code some vJass or do a Rebuild Trigger. There is a high chance you will end up using vJass for this last matter, even.

Since you have that version and you appear to not need over the top features, a minimalistic damage system is the way to go which is wise. You can make this on GUI but you will need to do what i said in my first post (a Jass event registration).

Version 1.32 features the JassHelper compiler so this (minimalistic system) should work. If you are not interested on a thing like this, just notice how i do the registration (the thing you must do to atempt your own GUI version) on the "private function Init..." part.

JASS:
library DAMAGE initializer Init


 private function Wc3SpellDamage takes attacktype at returns boolean
  return GetHandleId(at) == 0 //Wc3 spells that deal damage have this property (ie. War Stomp, Thunder Clap, etc.)
 endfunction

 private function IgnoredDamage takes real amount returns boolean
  return amount == 0 //this is a condition: if the amount passed variable is 0, then IgnoredDamage is true
 endfunction

 private function DmgMSG takes string s returns nothing
  call DisplayTextToForce(GetPlayersAll(), s) //this just displays a game message for all players
 endfunction

 private function OnDamage takes nothing returns nothing
  local real dmg = GetEventDamage() //All these functions, except the ones i created above are just Blizzard functions
  local real modifiers
  local unit src
  local unit tgt
  local attacktype at
  local damagetype dt

  if IgnoredDamage(dmg) then //See how ignored damage behaves, this function basically tracks 0 damage instances (amount == 0)
    return                                //and ends the event prematurely ("return") to avoid issuing more computations.
  endif


  //Remember, all these are Blizzard functions
  set src = GetEventDamageSource()
  set tgt = GetTriggerUnit()
  set at = BlzGetEventAttackType()
  set dt = BlzGetEventDamageType()
  set modifiers = 0

 
  if Wc3SpellDamage(at) then //This is the easiest way to track vanilla spells ie. storm bolt damage.
   call DmgMSG("Detected vanilla wc3 spell damage")
  elseif BlzGetEventIsAttack() then
   call DmgMSG("Detected unit autoattack damage") //Ídem, but for attacks.
  endif

  call DmgMSG("Event fired: " + GetUnitName(src) + " has damaged " + GetUnitName(tgt) + " for " + R2S(dmg) + " points of damage")

  call BlzSetEventDamage(dmg+modifiers) //this function is redundant as modifiers is 0. Just see the last line that calls the variable "modifiers"

  set src = null
  set tgt = null
 endfunction



 private function Init takes nothing returns nothing
   local trigger trig = CreateTrigger()

   call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGED)
   call TriggerAddCondition(trig, function OnDamage)

   set trig = null
 endfunction


endlibrary

A piece of code like that can take 1 and a half minute to make nowadays (that's the only reason i made it), but a GUI version would be a literal pain. It doesn't have the API of good damage systems, but this is perhaps the base of all those systems in the first place.
If you want to know how it works, try first to read the "function onDamage takes nothing returns nothing ...... endfunction" part first. This is just like reading a book. Then you can ask questions if you like (ie. how do i actually modify damage if the source is an Archmage).
 
Level 7
Joined
Jun 1, 2009
Messages
104
Most units that die will be removed after they finished decaying, unless you resurrect them in the meantime, the exception are heroes. So it counts. My point is that you plan to register units on a dynamic way, with lots of things going on (units being created and dying over and over again, etc.). So you either code some vJass or do a Rebuild Trigger. There is a high chance you will end up using vJass for this last matter, even.
Finally I've understood that I need to Trigger - Add to TEST Damage Copy <gen> the event (Unit - TempUnit[0] Takes damage) each time TempUnit[0] is set.
Well, guess I'll stick with Unit is attacked + timer arrays for now, since it worked fine.
Thx for help, I'll need this if I have to deal with specific events!
 
Level 8
Joined
Mar 19, 2017
Messages
248
Unit attacked + timer recquires you to have a very precise timer otherwise it is really easy to hack. I don't see the point of doing that for damage detection either.
I made a pseudo damage system with some kind of GUI API. You can check it if you want (1.32+).
Be mindful that damage systems on the spell section are kind of Ferrarris in terms of having cool (and costly) added features, which is the point of those, as the basics of a damage systems are very easy to trigger nowadays. This system is like a...Fiat, i would say.
 

Attachments

  • d.w3m
    21.4 KB · Views: 18
Level 7
Joined
Jun 1, 2009
Messages
104
Unit attacked + timer requires you to have a very precise timer otherwise it is really easy to hack. I don't see the point of doing that for damage detection either.
I use damage detection to order units to cast spells or update spells levels. I hope this won't being so vulnerable to online ping etc.
Here is an example:

  • Unit Attacked
  • Events
    • Unit - A unit owned by Player 12 (Brown) Is attacked
  • Conditions
  • Actions
    • -------- Caster --------
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Mana of (Attacked unit)) Greater than or equal to 50.00
    • Then - Actions
      • -------- Caster --------
      • For each (Integer A) from 1 to Max_Player_Number, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of (Attacked unit)) Not equal to (Unit-type of TheChosenTwo[(Integer A)])
              • (Player_Area[(Integer A)] contains (Attacked unit)) Equal to Yes
              • EliteCaster_CD_Timer[(Integer A)] Equal to No
            • Then - Actions
              • Set VariableSet EliteCaster_CD_Timer[(Integer A)] = Yes
              • Set VariableSet EliteCaster_Unit[(Integer A)] = (Attacked unit)
              • Countdown Timer - Start EliteCaster_Ability_Timer[(Integer A)] as a One-shot timer that will expire in 0.00 seconds
            • Else - Actions
              • Do nothing
    • Else - Actions
      • Do nothing
  • Elite Caster Ability
    • Events
    • Conditions
    • Actions
      • -------- Load timer index from hash --------
      • Set VariableSet EliteCaster_Index[1] = (Load 0 of (Key (Expiring timer).) from EliteCaster_Hash[1].)
      • -------- Cast --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • EliteCaster_CD_Bool[EliteCaster_Index[1]] Equal to No
        • Then - Actions
          • -------- Rejuvenation --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • (Unit-type of EliteCaster_Unit[EliteCaster_Index[1]]) Equal to UnitType[0]
                  • (Unit-type of EliteCaster_Unit[EliteCaster_Index[1]]) Equal to UnitType[1]
                  • (Unit-type of EliteCaster_Unit[EliteCaster_Index[1]]) Equal to UnitType[2]
                  • (Unit-type of EliteCaster_Unit[EliteCaster_Index[1]]) Equal to UnitType[3]
                  • (Unit-type of EliteCaster_Unit[EliteCaster_Index[1]]) Equal to UnitType[4]
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Mana of EliteCaster_Unit[EliteCaster_Index[1]]) Greater than or equal to 50.00
                • Then - Actions
                  • Set VariableSet TempPoint[11] = (Position of EliteCaster_Unit[EliteCaster_Index[1]])
                  • Set VariableSet TempGroup[11] = (Units within 800.00 of TempPoint[11] matching (((Owner of (Matching unit)) Equal to Player 12 (Brown)) and ((((Matching unit) is A structure) Equal to No) and ((((Matching unit) is Mechanical) Equal to No) and ((((Matching unit) is alive) Equal to Yes
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Number of units in TempGroup[11]) Greater than 0
                    • Then - Actions
                      • Set VariableSet EliteCaster_CD_Bool[EliteCaster_Index[1]] = Yes
                      • Set VariableSet TempUnit[11] = No unit
                      • Set VariableSet TempInt[11] = 999999
                      • Unit Group - Pick every unit in TempGroup[11] and do (Actions)
                        • Loop - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Integer((Life of (Picked unit)))) Less than TempInt[11]
                            • Then - Actions
                              • Set VariableSet TempUnit[11] = (Picked unit)
                              • Set VariableSet TempInt[11] = (Integer((Life of TempUnit[11])))
                            • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • TempUnit[11] Not equal to No unit
                        • Then - Actions
                          • Unit - Order EliteCaster_Unit[EliteCaster_Index[1]] to Hold Position.
                          • Unit - Order EliteCaster_Unit[EliteCaster_Index[1]] to Night Elf Druid Of The Claw - Rejuvenation TempUnit[11]
                        • Else - Actions
                          • Set VariableSet EliteCaster_CD_Bool[EliteCaster_Index[1]] = No
                      • Countdown Timer - Start EliteCaster_Move_Timer[EliteCaster_Index[1]] as a One-shot timer that will expire in Cooldown_Time[0] seconds
                      • Set VariableSet TempUnit[11] = No unit
                      • Set VariableSet TempInt[11] = 0
                    • Else - Actions
                      • Set VariableSet EliteCaster_CD_Bool[EliteCaster_Index[1]] = No
                  • Custom script: call RemoveLocation(udg_TempPoint[11])
                  • Custom script: call DestroyGroup(udg_TempGroup[11])
                • Else - Actions
                  • Set VariableSet EliteCaster_CD_Bool[EliteCaster_Index[1]] = No
            • Else - Actions
          • -------- long list of spells and unit types next--------
  • Elite Caster Move
    • Events
    • Conditions
    • Actions
      • -------- Load timer index from hash --------
      • Set VariableSet EliteCaster_Index[2] = (Load 0 of (Key (Expiring timer).) from EliteCaster_Hash[2].)
      • -------- Order to move --------
      • Set VariableSet TempPoint[12] = (Center of Regions[(Custom value of EliteCaster_Unit[EliteCaster_Index[2]])])
      • Unit - Order EliteCaster_Unit[EliteCaster_Index[2]] to Move To TempPoint[12]
      • Custom script: call RemoveLocation(udg_TempPoint[12])
      • -------- Reset cooldown --------
      • Set VariableSet EliteCaster_CD_Bool[EliteCaster_Index[2]] = No
      • Set VariableSet EliteCaster_CD_Timer[EliteCaster_Index[2]] = No
 
Last edited:
Status
Not open for further replies.
Top