• 🏆 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] Map Randomly Crashing whilst being played Online

Status
Not open for further replies.
Level 11
Joined
May 7, 2008
Messages
300
EDIT: Solution found, read the end of the thread.

Hello and sorry for the long thread name.

Ever since few days ago (can't remember and pinpoint the exact date) my map started to freeze and eventually crash during gameplay. The weirdest thing is that the map does not crash when being played solo (aka if you run it through World Editor ''Test Map'') or if you play it alone online.

The map crashes only if people play with me online, and usually around 20th minute or 12th minute. Now, I'm not sure what's causing this, but I did my best and basically fixed every leak I could think of to prevent this from happening.

Whilst the map crashes, I noticed that Warcraft 3 Reforged uses around 4gb of ram, I'm not sure if this is normal but this is the latest thing I've seen going on when it crashes. I would gladly test my map by turning off one trigger per game, but unfortunately as I've said the problem does not represent itself unless someone is playing with me :(


What I did that could potentially cause the crash
  • Hosting the game with an icon before the title, something like '' 🛡️ Defend the King''. - Tried hosting without the ''/'' or the icon, still crashes.
  • Replaced original Music ''ArthasTheme'' with my own theme. - Tried to restore the original file by importing it from ''New Map'' and using custom Music, didn't help.
  • Disabled my antivirus fully during gameplay - Did not work
  • Fixed most of the leaks - apparently that didn't work either


I'm attaching my map in hopes for someone to take a look at it. Now, I know it will take a lot of time for you to inspect this, and the best thing I can offer you is donation via Paypal for your help!


Edit: I also am getting this message everytime I open up World Editor ''Failed to load Enviroment Map for tileset K'' and it's specifically when I'm opening my map (attachment below)

Edit2: I did some tweaking, disabling one by one category in triggers every time I host map online to play with others. So far I covered a lot, there are few categories left that I haven't checked and the map still crashes unfortunately.


Thank you in advance!

Sincerely,
Crusher.
 

Attachments

  • Screenshot_1.png
    Screenshot_1.png
    2.8 MB · Views: 26
  • Defend the King 1.0a.w3x
    22.1 MB · Views: 14
  • Screenshot_2.png
    Screenshot_2.png
    24.2 KB · Views: 18
Last edited by a moderator:
Level 11
Joined
May 7, 2008
Messages
300
Are you able to host old versions of the map to identify when the issue started to occur?

I can, however as I've stated above the issue does not reveal itself unless being played online with other users, so it would be kind of pointless for me to play it by my self cause this current version also works in single player.

The only thing I can do is to compare the code vs current version and see what has drastically changed from one week ago.
 
Last edited by a moderator:
Level 11
Joined
May 7, 2008
Messages
300
Tried getting all players to not use Reforged graphics? As Warcraft III was originally a 32 bit game it has been known to have stability issues when passing 4 GB. Most of these should have been fixed, but maybe the odd one has not been.
Would be possible if I have someone to play with to tell them that, but unfortunately I don't. People are often in a rush whenever I host a wc3 game, and if you don't start within 20 seconds they insta jump out of your game. Combine that with a question ''can we try to test my map with a classic version of the game, I think there's a potential bug that can be pin pointed that way'' and I'll be in a lobby for a pretty long time.

I updated the original post now, I did disable few triggers every time I joined an online game, but there are still few that I haven't checked. All checked triggers in my map have been categorized with a + sign, so that should significantly help if anyone is willing to take a swing at my map.


Or better yet, can anyone host this map with a friend (2 players total) and check if the game freezes? You can type -disable right and -disable left at the start so you only get middle creeps popping. The reason I'm asking this is cause I'm not sure if it's just me.
 
Last edited by a moderator:

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,516
I recommend finding one person who can be a play-tester and run through multiple games with them

The obvious approach to minimize time lost is to disable half triggers, then the other half, then bisect until you find the broken trigger

If you can categorically say "this w3x was not broken, this w3x is broken", then i can help you get a Jass diff which may help debugging process
 
Level 11
Joined
May 7, 2008
Messages
300
I recommend finding one person who can be a play-tester and run through multiple games with them

The obvious approach to minimize time lost is to disable half triggers, then the other half, then bisect until you find the broken trigger

If you can categorically say "this w3x was not broken, this w3x is broken", then i can help you get a Jass diff which may help debugging process

Thank you for your help, I'll keep digging into the triggers these days and once I find exactly what causes the freeze / crash I will send you a private message so that I don't bump this thread twice.

PS - I do remember you back from the old days, I originally registered here in 2008 as Crusher8, but lost my account credentials and forgot the password to my original email so I made this new account :)


EDIT: Good news, I think I pin-pointed the problem, but I will update this post tomorrow!
 
Last edited by a moderator:
Level 11
Joined
May 7, 2008
Messages
300
It will be ok to double post from time to time if you have material new information about the investigation
Good news, I found out what caused the crashing.

Unusual as it sounds, the spell which I imported in my map called ''Fatal Bonds'' is what's causing my map to crash. I do not know why, but I ran few times whilst disabling the mentioned spell and my map is working now.

Under my map you can find the trigger under name Spells - Sealed Fate (entire category).
 
Level 11
Joined
May 7, 2008
Messages
300
Please post the code
Sorry, thought it would be easier just to take a look directly since the code is quite long :p


  • Fatal Bonds Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet FatalBonds_Ability = Sealed Fate
      • Set VariableSet FatalBonds_MaxLevel = 5
      • -------- NOTE: Make sure to set MaxLevel correctly or the damage calculation will not work --------
      • Set VariableSet FatalBonds_AbilityDummy = Fatal Bonds (Buff Indicator)
      • Set VariableSet FatalBonds_Buff = |cff008080Sealed Fate|r
      • -------- NOTE: Make sure the Buff Indicator ability is giving the appropriate buff --------
      • Set VariableSet FatalBonds_DummyUnitType = Dummy Unit
      • -------- --------
      • Set VariableSet FatalBonds_MaxTargets[1] = 3
      • Set VariableSet FatalBonds_MaxTargets[2] = 4
      • Set VariableSet FatalBonds_MaxTargets[3] = 6
      • Set VariableSet FatalBonds_MaxTargets[4] = 8
      • Set VariableSet FatalBonds_MaxTargets[5] = 10
      • -------- --------
      • Set VariableSet FatalBonds_DamagePercent[1] = 12.00
      • Set VariableSet FatalBonds_DamagePercent[2] = 15.00
      • Set VariableSet FatalBonds_DamagePercent[3] = 20.00
      • Set VariableSet FatalBonds_DamagePercent[4] = 25.00
      • Set VariableSet FatalBonds_DamagePercent[5] = 30.00
      • Set VariableSet FatalBonds_AttackType = Spells
      • Set VariableSet FatalBonds_DamageType = Magic
      • -------- --------
      • Set VariableSet FatalBonds_Duration[1] = 6.00
      • Set VariableSet FatalBonds_Duration[2] = 7.00
      • Set VariableSet FatalBonds_Duration[3] = 8.00
      • Set VariableSet FatalBonds_Duration[4] = 9.00
      • Set VariableSet FatalBonds_Duration[5] = 10.00
      • -------- --------
      • Set VariableSet FatalBonds_Radius[1] = 500.00
      • Set VariableSet FatalBonds_Radius[2] = 525.00
      • Set VariableSet FatalBonds_Radius[3] = 550.00
      • Set VariableSet FatalBonds_Radius[4] = 575.00
      • Set VariableSet FatalBonds_Radius[5] = 600.00
      • -------- --------
      • Set VariableSet FatalBonds_SFXCaster = Abilities\Spells\Orc\SpiritLink\SpiritLinkTarget.mdl
      • Set VariableSet FatalBonds_SFXCaster_AP = overhead
      • Set VariableSet FatalBonds_SFXTarget = Abilities\Spells\Orc\SpiritLink\SpiritLinkTarget.mdl
      • Set VariableSet FatalBonds_SFXTarget_AP = overhead
      • Set VariableSet FatalBonds_SFXBuff = Abilities\Spells\Orc\SpiritLink\SpiritLinkTarget.mdl
      • Set VariableSet FatalBonds_SFXBuff_AP = chest
      • -------- --------
      • Set VariableSet FatalBonds_LightningType = Spirit Link
      • Set VariableSet FatalBonds_LightningDuration = 0.75
      • Set VariableSet FatalBonds_LightningOffset = 25.00
      • -------- NOTE: LightningOffset refers to how high from the origin of the unit it will be --------
      • Set VariableSet FatalBonds_LRed = 1.00
      • Set VariableSet FatalBonds_LGreen = 1.00
      • Set VariableSet FatalBonds_LBlue = 1.00
      • Set VariableSet FatalBonds_LAlpha = 1.00
      • -------- NOTE: Vertex options take values between 0.00 and 1.00 --------
      • -------- --------
      • -------- --------
      • Set VariableSet FatalBonds_PeriodicTimer = 0.03
      • Trigger - Add to Fatal Bonds Loop <gen> the event (Time - Every FatalBonds_PeriodicTimer seconds of game time)
      • Trigger - Add to Fatal Bonds Lightning <gen> the event (Time - Every FatalBonds_PeriodicTimer seconds of game time)
      • Set VariableSet FatalBonds_LoopInt = 0
      • For each (Integer FatalBonds_LoopInt) from 1 to FatalBonds_MaxLevel, do (Actions)
        • Loop - Actions
          • Set VariableSet FatalBonds_DamagePercent[FatalBonds_LoopInt] = (FatalBonds_DamagePercent[FatalBonds_LoopInt] / 100.00)
      • Set VariableSet FatalBonds_Loc = (Center of (Playable map area))
      • Unit - Create 1 FatalBonds_DummyUnitType for Neutral Extra at FatalBonds_Loc facing Default building facing degrees
      • Set VariableSet FatalBonds_DummyCaster = (Last created unit)
      • Unit - Add FatalBonds_AbilityDummy to FatalBonds_DummyCaster

  • Fatal Bonds Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to FatalBonds_Ability
    • Actions
      • Set VariableSet FatalBonds_Caster = (Triggering unit)
      • Set VariableSet FatalBonds_TempUnit = (Target unit of ability being cast)
      • Set VariableSet FatalBonds_Owner = (Triggering player)
      • Set VariableSet FatalBonds_AbilityLvl = (Level of FatalBonds_Ability for FatalBonds_Caster)
      • -------- --------
      • Custom script: call MoveLocation(udg_FatalBonds_Loc, GetUnitX(udg_FatalBonds_TempUnit), GetUnitY(udg_FatalBonds_TempUnit))
      • Unit - Move FatalBonds_DummyCaster instantly to FatalBonds_Loc
      • Unit - Set level of FatalBonds_AbilityDummy for FatalBonds_DummyCaster to FatalBonds_AbilityLvl
      • Special Effect - Create a special effect attached to the FatalBonds_SFXCaster_AP of FatalBonds_Caster using FatalBonds_SFXCaster
      • Special Effect - Destroy (Last created special effect)
      • -------- storing unit that will be the origin of the next lightning effect --------
      • Set VariableSet FatalBonds_PrevUnit = FatalBonds_TempUnit
      • -------- --------
      • -------- checking if target already has an active instance; if so, end it and create an ew one --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (FatalBonds_TempUnit has buff FatalBonds_Buff) Equal to True
        • Then - Actions
          • Set VariableSet FatalBonds_Spell_ID = FatalBonds_SpellNode[(Custom value of FatalBonds_TempUnit)]
          • -------- --------
          • Set VariableSet FatalBonds_Instruction = 3
          • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
        • Else - Actions
          • -------- target does not have a spell instance --------
      • -------- --------
      • -------- creating the unit group to hold affected units --------
      • Set VariableSet FatalBonds_Instruction = 1
      • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
      • -------- caching data for target --------
      • Set VariableSet FatalBonds_Instruction = 0
      • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
      • -------- --------
      • -------- finding units to link with --------
      • Custom script: call GroupEnumUnitsInRangeOfLoc(udg_FatalBonds_UnitGroup, udg_FatalBonds_Loc, udg_FatalBonds_Radius[udg_FatalBonds_AbilityLvl], null)
      • Custom script: loop
      • Custom script: set udg_FatalBonds_TempUnit = FirstOfGroup(udg_FatalBonds_UnitGroup)
      • Custom script: exitwhen(udg_FatalBonds_TempUnit == null) or (udg_FatalBonds_GroupCount[udg_FatalBonds_Group_ID] >= udg_FatalBonds_MaxTargets[udg_FatalBonds_AbilityLvl])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (FatalBonds_TempUnit is A structure) Equal to False
          • (FatalBonds_TempUnit is A Hero) Equal to False
          • (FatalBonds_TempUnit is Mechanical) Equal to False
          • (FatalBonds_TempUnit is Magic Immune) Equal to False
          • (FatalBonds_TempUnit is alive) Equal to True
          • (FatalBonds_TempUnit belongs to an enemy of FatalBonds_Owner.) Equal to True
          • (FatalBonds_TempUnit has buff FatalBonds_Buff) Equal to False
        • Then - Actions
          • Set VariableSet FatalBonds_Instruction = 0
          • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
          • -------- creating a lightning effect between the last unit and this unit --------
          • Set VariableSet FatalBonds_Instruction = 2
          • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
        • Else - Actions
          • -------- unit did not pass filters; do not link --------
      • Unit Group - Remove FatalBonds_TempUnit from FatalBonds_UnitGroup.
      • Custom script: endloop

  • Fatal Bonds Loop
    • Events
    • Conditions
    • Actions
      • Set VariableSet FatalBonds_Spell_ID = 0
      • For each (Integer FatalBonds_LoopInt) from 1 to FatalBonds_SpellCount, do (Actions)
        • Loop - Actions
          • Set VariableSet FatalBonds_Spell_ID = FatalBonds_NodeNext[FatalBonds_Spell_ID]
          • -------- --------
          • Set VariableSet FatalBonds_Counter[FatalBonds_Spell_ID] = (FatalBonds_Counter[FatalBonds_Spell_ID] - FatalBonds_PeriodicTimer)
          • -------- checking if is time to deindex --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • (FatalBonds_Target[FatalBonds_Spell_ID] is dead) Equal to True
                  • (FatalBonds_Target[FatalBonds_Spell_ID] has buff FatalBonds_Buff) Equal to False
                  • FatalBonds_Counter[FatalBonds_Spell_ID] Less than or equal to 0.00
            • Then - Actions
              • -------- deindexing unit --------
              • Set VariableSet FatalBonds_Instruction = 3
              • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
            • Else - Actions
              • -------- it is not time to deindex; continue spell instance --------

  • Fatal Bonds Data
    • Events
    • Conditions
    • Actions
      • Custom script: local real x1
      • Custom script: local real y1
      • Custom script: local real z1
      • Custom script: local real x2
      • Custom script: local real y2
      • Custom script: local real z2
      • -------- --------
      • -------- 0 - index affected unit --------
      • -------- 1 - index unit groups --------
      • -------- 2 - index lightning effect --------
      • -------- 3 - deindex unit --------
      • -------- 4 - deindex unit group --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FatalBonds_Instruction Equal to 0
        • Then - Actions
          • -------- --------
          • -------- indexing affected unit --------
          • Set VariableSet FatalBonds_SpellCount = (FatalBonds_SpellCount + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FatalBonds_SpellCount Equal to 1
            • Then - Actions
              • Trigger - Turn on Fatal Bonds Loop <gen>
              • Trigger - Turn on Fatal Bonds Split Damage <gen>
              • Trigger - Turn on Fatal Bonds Null Fatal <gen>
            • Else - Actions
              • -------- not the first spell instance; triggers are already on --------
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FatalBonds_RecycledSize Equal to 0
            • Then - Actions
              • Set VariableSet FatalBonds_MaxIndex = (FatalBonds_MaxIndex + 1)
              • Set VariableSet FatalBonds_Spell_ID = FatalBonds_MaxIndex
            • Else - Actions
              • Set VariableSet FatalBonds_RecycledSize = (FatalBonds_RecycledSize - 1)
              • Set VariableSet FatalBonds_Spell_ID = FatalBonds_RecycledStack[FatalBonds_RecycledSize]
          • Set VariableSet FatalBonds_NodeNext[FatalBonds_Spell_ID] = 0
          • Set VariableSet FatalBonds_NodePrev[FatalBonds_Spell_ID] = FatalBonds_NodePrev[0]
          • Set VariableSet FatalBonds_NodeNext[FatalBonds_NodePrev[0]] = FatalBonds_Spell_ID
          • Set VariableSet FatalBonds_NodePrev[0] = FatalBonds_Spell_ID
          • -------- --------
          • Set VariableSet FatalBonds_Target[FatalBonds_Spell_ID] = FatalBonds_TempUnit
          • Set VariableSet FatalBonds_Damage[FatalBonds_Spell_ID] = FatalBonds_DamagePercent[FatalBonds_AbilityLvl]
          • Set VariableSet FatalBonds_Counter[FatalBonds_Spell_ID] = FatalBonds_Duration[FatalBonds_AbilityLvl]
          • Set VariableSet FatalBonds_GroupNode[FatalBonds_Spell_ID] = FatalBonds_Group_ID
          • Special Effect - Create a special effect attached to the FatalBonds_SFXBuff_AP of FatalBonds_Target[FatalBonds_Spell_ID] using FatalBonds_SFXBuff
          • Set VariableSet FatalBonds_BuffSFX[FatalBonds_Spell_ID] = (Last created special effect)
          • Unit Group - Add FatalBonds_Target[FatalBonds_Spell_ID] to FatalBonds_LinkedUnits[FatalBonds_GroupNode[FatalBonds_Spell_ID]]
          • Set VariableSet FatalBonds_GroupCount[FatalBonds_GroupNode[FatalBonds_Spell_ID]] = (FatalBonds_GroupCount[FatalBonds_GroupNode[FatalBonds_Spell_ID]] + 1)
          • Set VariableSet FatalBonds_SpellNode[(Custom value of FatalBonds_Target[FatalBonds_Spell_ID])] = FatalBonds_Spell_ID
          • -------- --------
          • Unit - Order FatalBonds_DummyCaster to Undead Necromancer - Cripple FatalBonds_Target[FatalBonds_Spell_ID]
          • Special Effect - Create a special effect attached to the FatalBonds_SFXTarget_AP of FatalBonds_Target[FatalBonds_Spell_ID] using FatalBonds_SFXTarget
          • Special Effect - Destroy (Last created special effect)
          • -------- --------
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FatalBonds_Instruction Equal to 1
            • Then - Actions
              • -------- --------
              • -------- indexing unit group --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • FatalBonds_GRecycledSize Equal to 0
                • Then - Actions
                  • Set VariableSet FatalBonds_GMaxIndex = (FatalBonds_GMaxIndex + 1)
                  • Set VariableSet FatalBonds_Group_ID = FatalBonds_GMaxIndex
                • Else - Actions
                  • Set VariableSet FatalBonds_GRecycledSize = (FatalBonds_GRecycledSize - 1)
                  • Set VariableSet FatalBonds_Group_ID = FatalBonds_GRecycledStack[FatalBonds_GRecycledSize]
              • Set VariableSet FatalBonds_GNodeNext[FatalBonds_Group_ID] = 0
              • Set VariableSet FatalBonds_GNodePrev[FatalBonds_Group_ID] = FatalBonds_GNodePrev[0]
              • Set VariableSet FatalBonds_GNodeNext[FatalBonds_GNodePrev[0]] = FatalBonds_Group_ID
              • Set VariableSet FatalBonds_GNodePrev[0] = FatalBonds_Group_ID
              • -------- --------
              • Set VariableSet FatalBonds_GroupCount[FatalBonds_Group_ID] = 0
              • Custom script: if (udg_FatalBonds_LinkedUnits[udg_FatalBonds_Group_ID] == null) then
              • Custom script: set udg_FatalBonds_LinkedUnits[udg_FatalBonds_Group_ID] = CreateGroup()
              • Custom script: endif
              • -------- --------
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • FatalBonds_Instruction Equal to 2
                • Then - Actions
                  • -------- --------
                  • -------- indexing lightning effect --------
                  • Set VariableSet FatalBonds_LCount = (FatalBonds_LCount + 1)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • FatalBonds_LCount Equal to 1
                    • Then - Actions
                      • Trigger - Turn on Fatal Bonds Lightning <gen>
                    • Else - Actions
                      • -------- this is not the first lightning effect; loop is already on --------
                  • -------- --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • FatalBonds_LRecycledSize Equal to 0
                    • Then - Actions
                      • Set VariableSet FatalBonds_LMaxIndex = (FatalBonds_LMaxIndex + 1)
                      • Set VariableSet FatalBonds_L_ID = FatalBonds_LMaxIndex
                    • Else - Actions
                      • Set VariableSet FatalBonds_LRecycledSize = (FatalBonds_LRecycledSize - 1)
                      • Set VariableSet FatalBonds_L_ID = FatalBonds_LRecycledStack[FatalBonds_LRecycledSize]
                  • Set VariableSet FatalBonds_LNodeNext[FatalBonds_L_ID] = 0
                  • Set VariableSet FatalBonds_LNodePrev[FatalBonds_L_ID] = FatalBonds_LNodePrev[0]
                  • Set VariableSet FatalBonds_LNodeNext[FatalBonds_LNodePrev[0]] = FatalBonds_L_ID
                  • Set VariableSet FatalBonds_LNodePrev[0] = FatalBonds_L_ID
                  • -------- --------
                  • Set VariableSet FatalBonds_Origin[FatalBonds_L_ID] = FatalBonds_PrevUnit
                  • Set VariableSet FatalBonds_Aim[FatalBonds_L_ID] = FatalBonds_TempUnit
                  • -------- storing unit that will be the origin of the next lightning effect --------
                  • Set VariableSet FatalBonds_PrevUnit = FatalBonds_Aim[FatalBonds_L_ID]
                  • Set VariableSet FatalBonds_LCounter[FatalBonds_L_ID] = 0.00
                  • -------- --------
                  • -------- xy adjustment --------
                  • Custom script: set x1 = GetUnitX(udg_FatalBonds_Origin[udg_FatalBonds_L_ID])
                  • Custom script: set y1 = GetUnitY(udg_FatalBonds_Origin[udg_FatalBonds_L_ID])
                  • Custom script: set x2 = GetUnitX(udg_FatalBonds_Aim[udg_FatalBonds_L_ID])
                  • Custom script: set y2 = GetUnitY(udg_FatalBonds_Aim[udg_FatalBonds_L_ID])
                  • -------- z adjustment --------
                  • Custom script: call MoveLocation(udg_FatalBonds_Loc, x1, y1)
                  • Custom script: set z1 = GetUnitFlyHeight(udg_FatalBonds_Origin[udg_FatalBonds_L_ID]) + udg_FatalBonds_LightningOffset + GetLocationZ(udg_FatalBonds_Loc)
                  • Custom script: call MoveLocation(udg_FatalBonds_Loc, x2, y2)
                  • Custom script: set z2 = GetUnitFlyHeight(udg_FatalBonds_Aim[udg_FatalBonds_L_ID]) + udg_FatalBonds_LightningOffset + GetLocationZ(udg_FatalBonds_Loc)
                  • -------- apply adjustment --------
                  • Custom script: set udg_FatalBonds_Lightning[udg_FatalBonds_L_ID] = AddLightningEx(udg_FatalBonds_LightningType, true, x1, y1, z1, x2, y2, z2)
                  • Lightning - Change color of FatalBonds_Lightning[FatalBonds_L_ID] to (FatalBonds_LRed FatalBonds_LGreen FatalBonds_LBlue) with FatalBonds_LAlpha alpha
                  • -------- --------
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • FatalBonds_Instruction Equal to 3
                    • Then - Actions
                      • -------- --------
                      • -------- deindexing unit --------
                      • Unit - Remove FatalBonds_Buff buff from FatalBonds_Target[FatalBonds_Spell_ID]
                      • Special Effect - Destroy FatalBonds_BuffSFX[FatalBonds_Spell_ID]
                      • Set VariableSet FatalBonds_Group_ID = FatalBonds_GroupNode[FatalBonds_Spell_ID]
                      • Unit Group - Remove FatalBonds_Target[FatalBonds_Spell_ID] from FatalBonds_LinkedUnits[FatalBonds_Group_ID].
                      • Set VariableSet FatalBonds_GroupCount[FatalBonds_Group_ID] = (FatalBonds_GroupCount[FatalBonds_Group_ID] - 1)
                      • -------- checking if unit group is empty --------
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FatalBonds_GroupCount[FatalBonds_Group_ID] Equal to 0
                        • Then - Actions
                          • Set VariableSet FatalBonds_Instruction = 4
                          • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)
                        • Else - Actions
                          • -------- unit group is not empty; do not deindex unit group --------
                      • -------- --------
                      • Set VariableSet FatalBonds_RecycledStack[FatalBonds_RecycledSize] = FatalBonds_Spell_ID
                      • Set VariableSet FatalBonds_RecycledSize = (FatalBonds_RecycledSize + 1)
                      • Set VariableSet FatalBonds_NodeNext[FatalBonds_NodePrev[FatalBonds_Spell_ID]] = FatalBonds_NodeNext[FatalBonds_Spell_ID]
                      • Set VariableSet FatalBonds_NodePrev[FatalBonds_NodeNext[FatalBonds_Spell_ID]] = FatalBonds_NodePrev[FatalBonds_Spell_ID]
                      • -------- --------
                      • Set VariableSet FatalBonds_SpellCount = (FatalBonds_SpellCount - 1)
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FatalBonds_SpellCount Equal to 0
                        • Then - Actions
                          • Trigger - Turn off Fatal Bonds Loop <gen>
                          • Trigger - Turn off Fatal Bonds Split Damage <gen>
                          • Trigger - Turn off Fatal Bonds Null Fatal <gen>
                        • Else - Actions
                          • -------- there are still spell instance(s) running; do not turn triggers off --------
                      • -------- --------
                    • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FatalBonds_Instruction Equal to 4
                        • Then - Actions
                          • -------- --------
                          • -------- deindexing unit group --------
                          • Custom script: call DestroyGroup(udg_FatalBonds_LinkedUnits[udg_FatalBonds_Group_ID])
                          • Custom script: set udg_FatalBonds_LinkedUnits[udg_FatalBonds_Group_ID] = null
                          • -------- --------
                          • Set VariableSet FatalBonds_GRecycledStack[FatalBonds_GRecycledSize] = FatalBonds_Group_ID
                          • Set VariableSet FatalBonds_GRecycledSize = (FatalBonds_GRecycledSize + 1)
                          • Set VariableSet FatalBonds_GNodeNext[FatalBonds_GNodePrev[FatalBonds_Group_ID]] = FatalBonds_GNodeNext[FatalBonds_Group_ID]
                          • Set VariableSet FatalBonds_GNodePrev[FatalBonds_GNodeNext[FatalBonds_Group_ID]] = FatalBonds_GNodePrev[FatalBonds_Group_ID]
                          • -------- --------
                        • Else - Actions
                          • -------- --------

  • Fatal Bonds Split Damage
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • (DamageEventTarget has buff FatalBonds_Buff) Equal to True
    • Actions
      • Set VariableSet FatalBonds_Spell_ID = FatalBonds_SpellNode[(Custom value of DamageEventTarget)]
      • -------- --------
      • Set VariableSet FatalBonds_TempReal = (DamageEventAmount x FatalBonds_Damage[FatalBonds_Spell_ID])
      • -------- dealing damage to other linked units --------
      • Set VariableSet FatalBonds_Group_ID = FatalBonds_GroupNode[FatalBonds_Spell_ID]
      • Trigger - Turn off Fatal Bonds Split Damage <gen>
      • Unit Group - Pick every unit in FatalBonds_LinkedUnits[FatalBonds_Group_ID] and do (Actions)
        • Loop - Actions
          • Set VariableSet FatalBonds_TempUnit = (Picked unit)
          • -------- making sure to not deal damage to the original --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FatalBonds_TempUnit Not equal to FatalBonds_Target[FatalBonds_Spell_ID]
            • Then - Actions
              • Unit - Cause DamageEventSource to damage FatalBonds_TempUnit, dealing FatalBonds_TempReal damage of attack type FatalBonds_AttackType and damage type FatalBonds_DamageType
            • Else - Actions
              • -------- do not deal damage to original target --------
      • Trigger - Turn on Fatal Bonds Split Damage <gen>

  • Fatal Bonds Null Fatal
    • Events
      • Game - DamageModifierEvent becomes Equal to 4.00
    • Conditions
      • (DamageEventTarget has buff FatalBonds_Buff) Equal to True
      • ((Life of DamageEventTarget) - DamageEventAmount) Less than 1.00
    • Actions
      • Set VariableSet DamageEventAmount = ((Life of DamageEventTarget) - 1.00)
      • -------- --------
      • Set VariableSet FatalBonds_Spell_ID = FatalBonds_SpellNode[(Custom value of DamageEventTarget)]
      • Set VariableSet FatalBonds_Instruction = 3
      • Trigger - Run Fatal Bonds Data <gen> (ignoring conditions)

  • Fatal Bonds Lightning
    • Events
    • Conditions
    • Actions
      • Custom script: local real x1
      • Custom script: local real y1
      • Custom script: local real z1
      • Custom script: local real x2
      • Custom script: local real y2
      • Custom script: local real z2
      • -------- --------
      • Set VariableSet FatalBonds_L_ID = 0
      • For each (Integer FatalBonds_LoopInt) from 1 to FatalBonds_LCount, do (Actions)
        • Loop - Actions
          • Set VariableSet FatalBonds_L_ID = FatalBonds_LNodeNext[FatalBonds_L_ID]
          • -------- --------
          • -------- xy adjustment --------
          • Custom script: set x1 = GetUnitX(udg_FatalBonds_Origin[udg_FatalBonds_L_ID])
          • Custom script: set y1 = GetUnitY(udg_FatalBonds_Origin[udg_FatalBonds_L_ID])
          • Custom script: set x2 = GetUnitX(udg_FatalBonds_Aim[udg_FatalBonds_L_ID])
          • Custom script: set y2 = GetUnitY(udg_FatalBonds_Aim[udg_FatalBonds_L_ID])
          • -------- z adjustment --------
          • Custom script: call MoveLocation(udg_FatalBonds_Loc, x1, y1)
          • Custom script: set z1 = GetUnitFlyHeight(udg_FatalBonds_Origin[udg_FatalBonds_L_ID]) + udg_FatalBonds_LightningOffset + GetLocationZ(udg_FatalBonds_Loc)
          • Custom script: call MoveLocation(udg_FatalBonds_Loc, x2, y2)
          • Custom script: set z2 = GetUnitFlyHeight(udg_FatalBonds_Aim[udg_FatalBonds_L_ID]) + udg_FatalBonds_LightningOffset + GetLocationZ(udg_FatalBonds_Loc)
          • Custom script: call MoveLightningEx(udg_FatalBonds_Lightning[udg_FatalBonds_L_ID], true, x1, y1, z1, x2, y2, z2)
          • -------- apply adjustment --------
          • -------- --------
          • Set VariableSet FatalBonds_LCounter[FatalBonds_L_ID] = (FatalBonds_LCounter[FatalBonds_L_ID] + FatalBonds_PeriodicTimer)
          • -------- checking if it is time to destroy lightning effect --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • (FatalBonds_Origin[FatalBonds_L_ID] is dead) Equal to True
                  • (FatalBonds_Aim[FatalBonds_L_ID] is dead) Equal to True
                  • (FatalBonds_Origin[FatalBonds_L_ID] has buff FatalBonds_Buff) Equal to False
                  • (FatalBonds_Aim[FatalBonds_L_ID] has buff FatalBonds_Buff) Equal to False
                  • FatalBonds_LCounter[FatalBonds_L_ID] Greater than or equal to FatalBonds_LightningDuration
            • Then - Actions
              • Lightning - Destroy FatalBonds_Lightning[FatalBonds_L_ID]
              • -------- --------
              • Set VariableSet FatalBonds_LRecycledStack[FatalBonds_LRecycledSize] = FatalBonds_L_ID
              • Set VariableSet FatalBonds_LRecycledSize = (FatalBonds_LRecycledSize + 1)
              • Set VariableSet FatalBonds_LNodeNext[FatalBonds_LNodePrev[FatalBonds_L_ID]] = FatalBonds_LNodeNext[FatalBonds_L_ID]
              • Set VariableSet FatalBonds_LNodePrev[FatalBonds_LNodeNext[FatalBonds_L_ID]] = FatalBonds_LNodePrev[FatalBonds_L_ID]
              • -------- --------
              • Set VariableSet FatalBonds_LCount = (FatalBonds_LCount - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • FatalBonds_LCount Equal to 0
                • Then - Actions
                  • Trigger - Turn off Fatal Bonds Lightning <gen>
                • Else - Actions
                  • -------- there are still lightning effects active; do not turn loop off --------
            • Else - Actions
              • -------- lightning duration has not ended; do not destroy --------
  • Unit Indexer
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call ExecuteFunc("InitializeUnitIndexer")
      • Custom script: endfunction
      • Custom script:
      • Custom script: function ClearUnitIndex takes nothing returns nothing
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Custom value of UDexUnits[UDex]) Equal to 0
        • Then - Actions
          • Set VariableSet UnitIndexLock[UDex] = (UnitIndexLock[UDex] - 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UnitIndexLock[UDex] Equal to 0
            • Then - Actions
              • Set VariableSet UDexNext[UDexPrev[UDex]] = UDexNext[UDex]
              • Set VariableSet UDexPrev[UDexNext[UDex]] = UDexPrev[UDex]
              • Set VariableSet UDexPrev[UDex] = 0
              • Set VariableSet UnitIndexEvent = 0.00
              • Set VariableSet UnitIndexEvent = 2.00
              • Set VariableSet UnitIndexEvent = 0.00
              • Set VariableSet UDexUnits[UDex] = No unit
              • Set VariableSet UDexNext[UDex] = UDexRecycle
              • Set VariableSet UDexRecycle = UDex
            • Else - Actions
        • Else - Actions
      • Custom script: endfunction
      • Custom script:
      • Custom script: function IndexUnit takes nothing returns boolean
      • Custom script: local integer pdex = udg_UDex
      • Custom script: local integer ndex
      • -------- - --------
      • -------- You can customize the following block - if conditions are false the (Matching unit) won't be indexed. --------
      • -------- - --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UnitIndexerEnabled Equal to True
          • (Custom value of (Matching unit)) Equal to 0
        • Then - Actions
          • Set VariableSet UDexWasted = (UDexWasted + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UDexWasted Equal to 32
            • Then - Actions
              • Set VariableSet UDexWasted = 0
              • Set VariableSet UDex = UDexNext[0]
              • Custom script: loop
              • Custom script: exitwhen udg_UDex == 0
              • Custom script: set ndex = udg_UDexNext[udg_UDex]
              • Custom script: call ClearUnitIndex()
              • Custom script: set udg_UDex = ndex
              • Custom script: endloop
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UDexRecycle Equal to 0
            • Then - Actions
              • Set VariableSet UDex = (UDexGen + 1)
              • Set VariableSet UDexGen = UDex
            • Else - Actions
              • Set VariableSet UDex = UDexRecycle
              • Set VariableSet UDexRecycle = UDexNext[UDex]
          • Set VariableSet UDexUnits[UDex] = (Matching unit)
          • Unit - Set the custom value of UDexUnits[UDex] to UDex
          • Set VariableSet UDexPrev[UDexNext[0]] = UDex
          • Set VariableSet UDexNext[UDex] = UDexNext[0]
          • Set VariableSet UDexNext[0] = UDex
          • Set VariableSet UnitIndexLock[UDex] = 1
          • Set VariableSet UnitIndexEvent = 0.00
          • Set VariableSet UnitIndexEvent = 1.00
          • Set VariableSet UnitIndexEvent = 0.00
          • Custom script: set udg_UDex = pdex
        • Else - Actions
      • Custom script: return false
      • Custom script: endfunction
      • Custom script:
      • Custom script: function InitializeUnitIndexer takes nothing returns nothing
      • Custom script: local integer i = 16
      • Custom script: local boolexpr b = Filter(function IndexUnit)
      • Custom script: local region re = CreateRegion()
      • Custom script: local trigger t = GetTriggeringTrigger()
      • Custom script: local rect r = GetWorldBounds()
      • Custom script: call RegionAddRect(re, r)
      • Custom script: call TriggerRegisterEnterRegion(t, re, b)
      • Custom script: call TriggerClearActions(t)
      • Custom script: call TriggerAddAction(t, function ClearUnitIndex)
      • Set VariableSet UnitIndexerEnabled = True
      • Custom script: loop
      • Custom script: set i = i - 1
      • Custom script: call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
      • Custom script: exitwhen i == 0
      • Custom script: endloop
      • Custom script: call RemoveRect(r)
      • Custom script: set re = null
      • Custom script: set r = null
      • Custom script: set t = null
      • Custom script: set b = null
      • Set VariableSet UnitIndexEvent = 3.00
      • Set VariableSet UnitIndexEvent = 0.00
  • Damage Engine Config
    • Events
    • Conditions
      • (UDexUnits[UDex] is A structure) Equal to False
    • Actions
      • -------- - --------
      • -------- This trigger's conditions let you filter out units you don't want detection for. --------
      • -------- NOTE: By default, units with Locust will not pass the check. --------
      • -------- TIP: The unit is called UDexUnits[UDex] and its custom value is UDex --------
      • -------- - --------
      • -------- Copy the Cheat Death Ability from Object Editor into your map and set the following variable respectively: --------
      • -------- - --------
      • Set VariableSet DamageBlockingAbility = Critical Strike 5
      • -------- - --------
      • -------- Copy the Detect Spell Damage Ability from Object Editor into your map and set the following variable respectively: --------
      • -------- - --------
      • Set VariableSet SpellDamageAbility = Critical Strike 2
      • -------- - --------
      • -------- You can add extra classifications here if you want to differentiate between your triggered damage --------
      • -------- Use DamageTypeExplosive (or any negative value damage type) if you want a unit killed by that damage to explode --------
      • -------- - --------
      • Set VariableSet DamageTypeExplosive = -1
      • Set VariableSet DamageTypeCriticalStrike = 1
      • Set VariableSet DamageTypeHeal = 2
      • Set VariableSet DamageTypeReduced = 3
      • Set VariableSet DamageTypeBlocked = 4
      • -------- - --------
      • -------- Leave the next Set statement disabled if you modified the Spell Damage Reduction item ability to 1.67 reduction --------
      • -------- Otherwise, if you removed that ability from Runed Bracers, you'll need to enable this line: --------
      • -------- - --------
      • Set VariableSet DmgEvBracers = Runed Bracers
      • -------- - --------
      • -------- Set the damage multiplication factor (1.00 being unmodified, increasing in damage over 1.00 and at 0 damage with 0.00) --------
      • -------- NOTE. With the default values, Runed Bracers is reduces 33%, Elune's Grace reduces 20% and Ethereal increases 67% --------
      • -------- - --------
      • Set VariableSet DAMAGE_FACTOR_BRACERS = 0.67
      • Set VariableSet DAMAGE_FACTOR_ELUNES = 0.80
      • Set VariableSet DAMAGE_FACTOR_ETHEREAL = 1.67
      • -------- - --------
      • -------- Do not enable any of the following lines as they are simply variable declarations to make copying easier --------
      • -------- - --------
      • Set VariableSet AfterDamageEvent = (DamageEvent + DamageModifierEvent)
      • Set VariableSet ClearDamageEvent = (This trigger)
      • Set VariableSet DamageEventAmount = DamageEventPrevAmt
      • Set VariableSet DamageEventOverride = NextDamageOverride
      • Set VariableSet DamageEventSource = DamageEventTarget
      • Set VariableSet DamageEventTrigger = DmgEvTrig
      • Set VariableSet DamageEventType = (LastDmgPrevType[0] + NextDamageType)
      • Set VariableSet DamageEventsWasted = DmgEvRecursionN
      • Set VariableSet DmgEvRunning = DmgEvStarted
      • Set VariableSet IsDamageSpell = LastDmgWasSpell[0]
      • Set VariableSet LastDamageHP = (Elapsed time for DmgEvTimer)
      • Set VariableSet LastDmgPrevAmount[0] = LastDmgValue[0]
      • Set VariableSet LastDmgSource[0] = LastDmgTarget[0]
      • Set VariableSet HideDamageFrom[0] = False
      • Set VariableSet UnitDamageRegistered[0] = False


JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=0
//===========================================================================
// Damage Engine lets you detect, amplify, block or nullify damage. It even
// lets you detect if the damage was physical or from a spell. Just reference
// DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
// necessary damage event data.
//
// - Detect damage: use the event "DamageEvent Equal to 1.00"
// - To change damage before it's dealt: use the event "DamageModifierEvent Equal to 1.00"
// - Detect damage after it was applied, use the event "AfterDamageEvent Equal to 1.00"
// - Detect spell damage: use the condition "IsDamageSpell Equal to True"
// - Detect zero-damage: use the event "DamageEvent Equal to 2.00" (an AfterDamageEvent will not fire for this)
//
// You can specify the DamageEventType before dealing triggered damage. To prevent an already-improbable error, I recommend running the trigger "ClearDamageEvent (Checking Conditions)" after dealing triggered damage from within a damage event:
// - Set NextDamageType = DamageTypeWhatever
// - Unit - Cause...
// - Trigger - Run ClearDamageEvent (Checking Conditions)
//
// You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
// - If the amount is modified to negative, it will count as a heal.
// - If the amount is set to 0, no damage will be dealt.
//
// If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
//
//===========================================================================
// Programming note about "integer i" and "udg_DmgEvRecursionN": integer i
// ranges from -1 upwards. "udg_DmgEvRecursionN" ranges from 0 upwards.
// "integer i" is always 1 less than "udg_DmgEvRecursionN"
//
function DmgEvResetVars takes nothing returns nothing
    local integer i = udg_DmgEvRecursionN - 2
    set udg_DmgEvRecursionN = i + 1
    if i >= 0 then
        set udg_DamageEventPrevAmt  = udg_LastDmgPrevAmount[i]
        set udg_DamageEventAmount   = udg_LastDmgValue[i]
        set udg_DamageEventSource   = udg_LastDmgSource[i]
        set udg_DamageEventTarget   = udg_LastDmgTarget[i]
        set udg_IsDamageSpell       = udg_LastDmgWasSpell[i]
        set udg_DamageEventType     = udg_LastDmgPrevType[i]
    endif
endfunction

function CheckDamagedLifeEvent takes boolean clear returns nothing
    if clear then
        set udg_NextDamageOverride = false
        set udg_NextDamageType = 0
    endif
    if udg_DmgEvTrig != null then
        call DestroyTrigger(udg_DmgEvTrig)
        set udg_DmgEvTrig = null
     
        if udg_IsDamageSpell then
            call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(udg_LastDamageHP, 0.41))
            if udg_LastDamageHP <= 0.405 then
                if udg_DamageEventType < 0 then
                    call SetUnitExploded(udg_DamageEventTarget, true)
                endif
                //Kill the unit
                call DisableTrigger(udg_DamageEventTrigger)
                call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
                call EnableTrigger(udg_DamageEventTrigger)
            endif
        elseif GetUnitAbilityLevel(udg_DamageEventTarget, udg_DamageBlockingAbility) > 0 then
            call UnitRemoveAbility(udg_DamageEventTarget, udg_DamageBlockingAbility)
            call SetWidgetLife(udg_DamageEventTarget, udg_LastDamageHP)
        endif
        if udg_DamageEventAmount != 0.00 and not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
            set udg_AfterDamageEvent = 0.00
            set udg_AfterDamageEvent = 1.00
            set udg_AfterDamageEvent = 0.00
        endif
        call DmgEvResetVars()
    endif
endfunction
 
function DmgEvOnExpire takes nothing returns nothing
    set udg_DmgEvStarted = false
    call CheckDamagedLifeEvent(true)
endfunction

function PreCheckDamagedLifeEvent takes nothing returns boolean
    call CheckDamagedLifeEvent(true)
    return false
endfunction

function OnUnitDamage takes nothing returns boolean
    local boolean override = udg_DamageEventOverride
    local integer i = udg_DmgEvRecursionN - 1
    local string s
    local real prevAmount
    local real life
    local real prevLife
    local unit u
    call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
    if i >= 0 then
        if i < 16 then
            set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
            set udg_LastDmgValue[i]     = udg_DamageEventAmount
            set udg_LastDmgSource[i]    = udg_DamageEventSource
            set udg_LastDmgTarget[i]    = udg_DamageEventTarget
            set udg_LastDmgWasSpell[i]  = udg_IsDamageSpell
            set udg_LastDmgPrevType[i]  = udg_DamageEventType
        else
            set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
            set s = s + "Trigger - Turn off (This Trigger)\n"
            set s = s + "Unit - Cause...\n"
            set s = s + "Trigger - Turn on (This Trigger)"
         
            //Delete the next couple of lines to disable the in-game recursion crash warnings
            call ClearTextMessages()
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
            return false
        endif
    endif
    set udg_DmgEvRecursionN     = i + 2
    set u                       = GetTriggerUnit()
    set prevAmount              = GetEventDamage()
    set udg_DamageEventSource   = GetEventDamageSource()
 
    set udg_DamageEventAmount   = prevAmount
    set udg_DamageEventTarget   = u
 
    set udg_DamageEventType     = udg_NextDamageType
    set udg_NextDamageType      = 0
    set udg_DamageEventOverride = udg_NextDamageOverride
    set udg_NextDamageOverride  = false
 
    if prevAmount == 0.00 then
        if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
            set udg_DamageEventPrevAmt = 0.00
            set udg_DamageEvent = 0.00
            set udg_DamageEvent = 2.00
            set udg_DamageEvent = 0.00
        endif
        call DmgEvResetVars()
    else
        if not udg_DmgEvStarted then
            set udg_DmgEvStarted = true
            call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
        endif
        set udg_IsDamageSpell = prevAmount < 0.00
        if udg_IsDamageSpell then
            set prevAmount = -udg_DamageEventAmount
            set life = 1.00
            if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
                set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
            endif
            if GetUnitAbilityLevel(u, 'Aegr') > 0 then
                set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
            endif
            if udg_DmgEvBracers != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
                //Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
                set i = 6
                loop
                    set i = i - 1
                    if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvBracers then
                        set life = life*udg_DAMAGE_FACTOR_BRACERS //0.67
                        exitwhen true
                    endif
                    exitwhen i == 0
                endloop
            endif
            set udg_DamageEventAmount = prevAmount*life
        endif
        set udg_DamageEventPrevAmt = prevAmount
        set udg_DamageModifierEvent = 0.00
        if not udg_DamageEventOverride then
            set udg_DamageModifierEvent = 1.00
            if not udg_DamageEventOverride then
                set udg_DamageModifierEvent = 2.00
                set udg_DamageModifierEvent = 3.00
            endif
        endif
        set udg_DamageEventOverride = override
        if udg_DamageEventAmount > 0.00 then
            set udg_DamageModifierEvent = 4.00
        endif
        set udg_DamageModifierEvent = 0.00
        if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
            set udg_DamageEvent = 0.00
            set udg_DamageEvent = 1.00
            set udg_DamageEvent = 0.00
        endif
        call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
     
        //All events have run and the damage amount is finalized.
        set life = GetWidgetLife(u)
        set udg_DmgEvTrig = CreateTrigger()
        call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
        if not udg_IsDamageSpell then
            if udg_DamageEventAmount != prevAmount then
                set life = life + prevAmount - udg_DamageEventAmount
                if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
                    set udg_LastDamageHP = life - prevAmount
                    call UnitAddAbility(u, udg_DamageBlockingAbility)
                endif
                call SetWidgetLife(u, RMaxBJ(life, 0.42))
            endif
            call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
        else
            set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
            set prevLife = life
            if life + prevAmount*0.75 > udg_LastDamageHP then
                set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
                call SetWidgetLife(u, life)
                set life = (life + udg_LastDamageHP)/2.00
            else
                set life = life + prevAmount*0.50
            endif
            set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
            call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
        endif
        set u = null
    endif
    return false
endfunction

function CreateDmgEvTrg takes nothing returns nothing
    set udg_DamageEventTrigger = CreateTrigger()
    call TriggerAddCondition(udg_DamageEventTrigger, Filter(function OnUnitDamage))
endfunction

function SetupDmgEv takes nothing returns boolean
    local integer i = udg_UDex
    local unit u
    if udg_UnitIndexEvent == 1.00 then
        set u = udg_UDexUnits[i]
        if GetUnitAbilityLevel(u, 'Aloc') == 0 and TriggerEvaluate(gg_trg_Damage_Engine_Config) then
            set udg_UnitDamageRegistered[i] = true
            call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
            call UnitAddAbility(u, udg_SpellDamageAbility)
            call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
        endif
        set u = null
    else
        set udg_HideDamageFrom[i] = false
        if udg_UnitDamageRegistered[i] then
            set udg_UnitDamageRegistered[i] = false
            set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
            if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
                set udg_DamageEventsWasted = 0
             
                //Rebuild the mass EVENT_UNIT_DAMAGED trigger:
                call DestroyTrigger(udg_DamageEventTrigger)
                call CreateDmgEvTrg()
                set i = udg_UDexNext[0]
                loop
                    exitwhen i == 0
                    if udg_UnitDamageRegistered[i] then
                        call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
                    endif
                    set i = udg_UDexNext[i]
                endloop
            endif
        endif
    endif
    return false
endfunction
 
//===========================================================================
function InitTrig_Damage_Engine takes nothing returns nothing
    local unit u = CreateUnit(Player(15), 'uloc', 0, 0, 0)
    local integer i = 16
 
    //Create this trigger with UnitIndexEvents in order add and remove units
    //as they are created or removed.
    local trigger t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
    call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
    call TriggerAddCondition(t, Filter(function SetupDmgEv))
    set t = null
 
    //Run the configuration trigger to set all configurables:
    if gg_trg_Damage_Engine_Config == null then
        //It's possible this InitTrig_ function ran first, in which case use ExecuteFunc.
        call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
    else
        call TriggerExecute(gg_trg_Damage_Engine_Config)
    endif
 
    //Create trigger for storing all EVENT_UNIT_DAMAGED events.
    call CreateDmgEvTrg()
 
    //Create GUI-friendly trigger for cleaning up after UnitDamageTarget.
    set udg_ClearDamageEvent = CreateTrigger()
    call TriggerAddCondition(udg_ClearDamageEvent, Filter(function PreCheckDamagedLifeEvent))
 
    //Disable SpellDamageAbility for every player.
    loop
        set i = i - 1
        call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
        exitwhen i == 0
    endloop
 
    //Preload abilities.
    call UnitAddAbility(u, udg_DamageBlockingAbility)
    call UnitAddAbility(u, udg_SpellDamageAbility)
    call RemoveUnit(u)
    set u = null
endfunction
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,178
Try creating the units at non-overlapping offsets near the spawn point. Even a random point in a spawn region might be sufficient. Creating units on top of each other (at same point) is computationally intensive. Although given how few you are creating at a time I doubt this could cause a crash.

Creating units will fire triggers from indexing systems and any other system that processes units on map entry. Such triggers could be indirectly the cause, in response to these spawn triggers creating units.

The spawn triggers seem to be repeating a lot of the same logic. For both efficiency and maintainability they could likely be merged into a single trigger that fires every 15 seconds and is data fed by arrays that are iterated over by loops. This is unlikely to help solve the crash though.
 
Last edited:
Level 11
Joined
May 7, 2008
Messages
300
EDIT:

@Cokemonkey
The map doesn't crash when I leave ghoul only, but the wave only lasts for 2 minutes so it wouldn't crash during that time. Usually crashes around 20th min when abomination wave comes (the triggers are identical, it's just the different unit spawns)


@DrSuperGood

I just did that, will post again if it helps!


Also:


On third thought (lel) I think this trigger is also problematic, what do you guys think?

This trigger basically displays gold granted to everyone once the final blow on a certain unit is made. In this example, Ghoul from wave 1. Also I noticed the space bar between call removelocation command (this code is nearly 10 years long, Blackrose from thehelper.net made it back in 2011).


  • Ghoul Gold 1
    • Events
      • Unit - A unit owned by Player 9 (Gray) Dies
    • Conditions
      • (Unit-type of (Dying unit)) Equal to |cff00ff00Ghoul
    • Actions
      • Player - Add 1 to Player 1 (Red).Current gold
      • Player - Add 1 to Player 2 (Blue).Current gold
      • Player - Add 1 to Player 3 (Teal).Current gold
      • Player - Add 1 to Player 4 (Purple).Current gold
      • Player - Add 1 to Player 5 (Yellow).Current gold
      • Player - Add 1 to Player 7 (Green).Current gold
      • Set VariableSet PN = (Player number of (Owner of (Dying unit)))
      • Set VariableSet DeadUnit[PN] = (Dying unit)
      • Floating Text - Create floating text that reads |cff80ff80+1 Gold|r above DeadUnit[PN] with Z offset 0.00, using font size 8.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
      • Floating Text - Change (Last created floating text): Disable permanence
      • Floating Text - Change the lifespan of (Last created floating text) to 3.00 seconds
      • Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
      • Floating Text - Set the velocity of (Last created floating text) to 80.00 towards 140.00 degrees
      • -------- ##################### --------
      • Set VariableSet TempTextLoc = (Position of DeadUnit[PN])
      • Set VariableSet TempText = (Last created floating text)
      • Trigger - Run HideFloatingText <gen> (ignoring conditions)
      • -------- ##################### --------


  • HideFloatingText
    • Events
    • Conditions
    • Actions
      • Floating Text - Hide TempText for (All players)
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • Set VariableSet TempPlayer = (Picked player)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (TempTextLoc is visible to TempPlayer.) Equal to True
            • Then - Actions
              • Set VariableSet TempForce = (Player group(TempPlayer))
              • Floating Text - Show TempText for TempForce
            • Else - Actions
      • Custom script: call DestroyForce( udg_TempForce )
      • Custom script: call RemoveLocation( udg_TempTextLoc )


I also made an alternative to the code above, do you think this trigger is better than the one above? I was planning on doing it for every wave, it isn't a problem for me, I'm just wondering if it's more PC friendly?

  • Ghoul Gold 1 Copy
    • Events
      • Unit - A unit owned by Player 9 (Gray) Dies
    • Conditions
      • (Unit-type of (Dying unit)) Equal to |cff00ff00Ghoul
    • Actions
      • Set VariableSet GhoulDead = (Position of (Dying unit))
      • Player - Add 1 to Player 1 (Red).Current gold
      • Player - Add 1 to Player 2 (Blue).Current gold
      • Player - Add 1 to Player 3 (Teal).Current gold
      • Player - Add 1 to Player 4 (Purple).Current gold
      • Player - Add 1 to Player 5 (Yellow).Current gold
      • Player - Add 1 to Player 7 (Green).Current gold
      • Floating Text - Create floating text that reads |cff80ff80+1 Gold|r at GhoulDead with Z offset 0.00, using font size 8.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
      • Floating Text - Set the velocity of (Last created floating text) to 80.00 towards 140.00 degrees
      • Floating Text - Change (Last created floating text): Disable permanence
      • Floating Text - Change the lifespan of (Last created floating text) to 3.00 seconds
      • Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds
      • Custom script: call RemoveLocation(udg_GhoulDead)
 
Last edited by a moderator:
Level 11
Joined
May 7, 2008
Messages
300
I've found out the solution!

One of my playtesters found the solution today, I couldn't believe what was the problem.


Modified Barrage from the Tinker....

basically if you make the spell a 5 level spell, make it do 0 damage and stun for 0.001 second it crashes the game after being spammed too much. Apparently it works fine until level 3, but after you level up the spell past that point, the game starts stuttering.

Tested it now in single player, custom lobby with everything disabled, it still crashes at the EXACT moment it did when i played online.

Well, at least I now what it is and I'll replace it with something else.

Thanks for all the help you guys provided, I really appreciate it.

Also don't use Barrage, it took me nearly 10 days to identify the problem ^^
 
Last edited by a moderator:
Level 19
Joined
Feb 27, 2019
Messages
563
The loop/Dot issue or a certain kind of it seems to only appear after a certain amount of game time. About ~8 minutes in my experience. Its related to low looping/Dot values such as 0 or close to 0.
 
Status
Not open for further replies.
Top