• 🏆 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!

Nested Loops break Trigger

Status
Not open for further replies.
Level 2
Joined
Nov 3, 2021
Messages
11
I'm using a trigger that goes through many Nested Loops to change the status of many units that are "linked" to the triggering unit, but I think I've hit some operation limit or something in that sense. One of my trigger has been growing in size since there are a lot of things that need to happen simultaneously and the game needs to look up the unit in the hashtables to then find all dependencies attached to it. But now the trigger breaks if the loops are too big.
For example there's a loop that Goes Integer A (Max 9) -> Then enters a Loop for Integer B (Max 100) -> Then enters a Loop for Integer C (Max 100), with the loop breaking if it finds the specified dependency. But now, if it goes to Integer A=2, the code breaks, unless I change the maximum for Integer B and C to 50. But even at 50 the code breaks if Integer A is at 3 or more (If I lower the Integer B and C maximum values to lower, it will not break on higher values of Integer A).
The syntax is sound, but I feel like I'm hitting a World Editor limit instead. Any idea what I should do? I use the same nests in other triggers as well, but they're much smaller in content, and I suspect it might have to do with that, but I'm just not sure.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
Post your trigger.

But I don't think you should be running into an operation limit. Try creating new Loop variables for this specific trigger.
 
If it always goes to 100 in each loop, then you're making around 10k actions per loop. Depending on how heavy your loop is, it's not impossible for you to be hitting the OP limit. If you post your trigger someone might find a more efficient way of doing what you're currently doing. Have you tried printing a message when the loop breaks on purpose, so that you're sure that it's not breaking due to a logical mistake in the break condition that causes an early exit?
 
Level 2
Joined
Nov 3, 2021
Messages
11
Post your trigger.

But I don't think you should be running into an operation limit. Try creating new Loop variables for this specific trigger.
Alright, here it is, it's very long and probably very messy:

  • Build Death
    • Events
      • Unit - A unit Dies
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Dying unit)) Equal to Great Hall (Orc Tier 1)
          • (Unit-type of (Dying unit)) Equal to Stronghold (Orc Tier 2)
          • (Unit-type of (Dying unit)) Equal to Fortress (Orc Tier 3)
          • (Unit-type of (Dying unit)) Equal to War Mill (Orc - War Mill)
    • Actions
      • Set TempUnit = (Dying unit)
      • Set TempPlayer = (Owner of TempUnit)
      • Set TempPlayerNr = (Player number of TempPlayer)
      • Set TempUnitType = (Unit-type of TempUnit)
      • Set TempPoint = (Position of TempUnit)
      • Set TempInteger = 0
      • -------- Lumber --------
      • For each (Integer A) from 1 to MaxGreatHallPrime, do (Actions)
        • Loop - Actions
          • Game - Display to (All players) the text: (String((Integer((Substring((Load ((Integer A) x -1) of KeyGreatHallPrime from DatabaseBuilding[TempPlayerNr]), 1, 3))))))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • And - All (Conditions) are true
                • Conditions
                  • (Integer((Substring((Load (Integer A) of KeyGreatHallPrime from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                  • (Load (Integer A) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]) Equal to TempUnit
            • Then - Actions
              • Set TempInteger = 1
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempInteger Equal to 0
            • Then - Actions
              • For each (Integer B) from 1 to MaxSecondaryBuildings, do (Actions)
                • Loop - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • And - All (Conditions) are true
                        • Conditions
                          • (Integer((Substring((Load (Integer B) of KeyGreatHallSecondary from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                          • (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]) Equal to TempUnit
                    • Then - Actions
                      • Set TempInteger = 1
                      • Custom script: exitwhen true
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • TempInteger Equal to 0
                    • Then - Actions
                      • For each (Integer Integer_C) from 1 to MaxSecondaryBuildings, do (Actions)
                        • Loop - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • And - All (Conditions) are true
                                • Conditions
                                  • (Integer((Substring((Load Integer_C of KeyWarMill from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                                  • (Integer((Substring((Load Integer_C of KeyWarMill from DatabaseBuilding[TempPlayerNr]), 4, 6)))) Equal to (Integer B)
                                  • (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) Equal to TempUnit
                            • Then - Actions
                              • Set TempInteger = 1
                              • Custom script: exitwhen true
                            • Else - Actions
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • TempInteger Equal to 1
                    • Then - Actions
                      • Custom script: exitwhen true
                    • Else - Actions
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempInteger Equal to 1
            • Then - Actions
              • Set TempInteger = ((Load MultiboardLumber of TempPlayerNr from DatabaseMultiboard[TempPlayerNr]) - (Charges remaining in (Item carried by TempUnit of type Lumber)))
              • Multiboard - Set the text for (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr]) item in column MultiboardLumber, row ((Integer A) + 1) to (String(TempInteger))
              • Hashtable - Save TempInteger as MultiboardLumber of TempPlayerNr in DatabaseMultiboard[TempPlayerNr]
              • -------- Stone --------
              • Set TempInteger = ((Load MultiboardStone of TempPlayerNr from DatabaseMultiboard[TempPlayerNr]) - (Charges remaining in (Item carried by TempUnit of type Stone)))
              • Multiboard - Set the text for (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr]) item in column MultiboardStone, row ((Integer A) + 1) to (String(TempInteger))
              • Hashtable - Save TempInteger as MultiboardStone of TempPlayerNr in DatabaseMultiboard[TempPlayerNr]
              • -------- Tools --------
              • Set TempInteger = ((Load MultiboardTools of TempPlayerNr from DatabaseMultiboard[TempPlayerNr]) - (Charges remaining in (Item carried by TempUnit of type Tools)))
              • Multiboard - Set the text for (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr]) item in column MultiboardTools, row ((Integer A) + 1) to (String(TempInteger))
              • Hashtable - Save TempInteger as MultiboardTools of TempPlayerNr in DatabaseMultiboard[TempPlayerNr]
              • -------- Iron --------
              • Set TempInteger = ((Load MultiboardIron of TempPlayerNr from DatabaseMultiboard[TempPlayerNr]) - (Charges remaining in (Item carried by TempUnit of type Iron)))
              • Multiboard - Set the text for (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr]) item in column MultiboardIron, row ((Integer A) + 1) to (String(TempInteger))
              • Hashtable - Save TempInteger as MultiboardIron of TempPlayerNr in DatabaseMultiboard[TempPlayerNr]
              • -------- Rope --------
              • Set TempInteger = ((Load MultiboardRope of TempPlayerNr from DatabaseMultiboard[TempPlayerNr]) - (Charges remaining in (Item carried by TempUnit of type Rope)))
              • Multiboard - Set the text for (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr]) item in column MultiboardRope, row ((Integer A) + 1) to (String(TempInteger))
              • Hashtable - Save TempInteger as MultiboardRope of TempPlayerNr in DatabaseMultiboard[TempPlayerNr]
              • -------- Food --------
              • Set TempInteger = ((Load MultiboardFood of TempPlayerNr from DatabaseMultiboard[TempPlayerNr]) - (Charges remaining in (Item carried by TempUnit of type Food)))
              • Multiboard - Set the text for (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr]) item in column MultiboardFood, row ((Integer A) + 1) to (String(TempInteger))
              • Hashtable - Save TempInteger as MultiboardFood of TempPlayerNr in DatabaseMultiboard[TempPlayerNr]
              • Custom script: set udg_GetLocalPlayer = GetLocalPlayer()
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • TempPlayer Equal to GetLocalPlayer
                • Then - Actions
                  • Multiboard - Show (Load 0 of 0 in DatabaseMultiboard[TempPlayerNr])
                • Else - Actions
              • Custom script: exitwhen true
            • Else - Actions
      • For each (Integer A) from 1 to 6, do (Actions)
        • Loop - Actions
          • Item - Remove (Item carried by TempUnit in slot (Integer A))
      • -------- Find New Primary Building --------
      • For each (Integer A) from 1 to MaxGreatHallPrime, do (Actions)
        • Loop - Actions
          • Game - Display to (All players) the text: (String((Integer((Substring((Load ((Integer A) x -1) of KeyGreatHallPrime from DatabaseBuilding[TempPlayerNr]), 1, 3))))))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempUnit Equal to (Load (Integer A) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr])
            • Then - Actions
              • Set TempStringFinal = 000000000
              • Game - Display to (All players) the text: (String((Integer((Substring((Load ((Integer A) x -1) of KeyGreatHallPrime from DatabaseBuilding[TempPlayerNr]), 1, 3))))))
              • Unit - Remove (Load ((Integer A) x -1) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]) from the game
              • Hashtable - Save TempStringFinal as (Integer A) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]
              • Hashtable - Save TempStringFinal as ((Integer A) x -1) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]
              • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyGreatHallPrime, GetForLoopIndexA() )
              • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyGreatHallPrime, ( GetForLoopIndexA() * -1 ) )
              • -------- Secondary Great Halls --------
              • For each (Integer B) from 1 to MaxSecondaryBuildings, do (Actions)
                • Loop - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Integer((Substring((Load (Integer B) of KeyGreatHallSecondary from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • Or - Any (Conditions) are true
                            • Conditions
                              • (Level of Set Building for (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])) Equal to 1
                              • (Unit-type of (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])) Equal to Great Hall Foundation (Secondary Placement)
                        • Then - Actions
                          • Unit - Remove (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]) from the game
                          • Unit - Remove (Load ((Integer B) x -1) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]) from the game
                          • Set TempStringFinal = 000000000
                          • Hashtable - Save TempStringFinal as (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]
                          • Hashtable - Save TempStringFinal as ((Integer B) x -1) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]
                          • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyGreatHallSecondary, GetForLoopIndexB() )
                          • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyGreatHallSecondary, ( GetForLoopIndexB() * -1 ) )
                        • Else - Actions
                          • Unit - Remove Build Structure (Orc - Stronghold) from (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])
                          • Unit - Remove Build Structure (Orc - Great Hall Secondary) from (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])
                          • Unit - Remove Upgrade to Stronghold (Orc - Primary) from (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])
                          • Unit - Remove Reclaim Building (Great Hall) from (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])
                          • Unit - Add Reclaim Town Center to (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])
                          • Special Effect - Create a special effect attached to the overhead of (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]) using Objects\RandomObject\RandomObject.mdl
                          • Hashtable - Save Handle Of(Last created special effect) as (Integer B) of KeyGreatHallSecondary in DatabaseSpecialEffect[TempPlayerNr]
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Integer A) Less than or equal to 9
                            • Then - Actions
                              • Set TempStringPrime = (00 + (String((Integer A))))
                            • Else - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (Integer A) Less than or equal to 99
                                • Then - Actions
                                  • Set TempStringPrime = (0 + (String((Integer A))))
                                • Else - Actions
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • (Integer A) Less than or equal to 999
                                    • Then - Actions
                                      • Set TempStringPrime = (String((Integer A)))
                                    • Else - Actions
                          • Set TempStringSecondary = 000
                          • Set TempStringTertiary = 000
                          • Set TempStringFinal = ((TempStringPrime + TempStringSecondary) + TempStringTertiary)
                          • Hashtable - Save TempStringFinal as (Integer A) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]
                          • Hashtable - Save TempStringFinal as ((Integer A) x -1) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]
                    • Else - Actions
                  • -------- War Mill --------
                  • For each (Integer Integer_C) from 1 to MaxSecondaryBuildings, do (Actions)
                    • Loop - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • And - All (Conditions) are true
                            • Conditions
                              • (Integer((Substring((Load Integer_C of KeyWarMill from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                              • (Integer((Substring((Load Integer_C of KeyWarMill from DatabaseBuilding[TempPlayerNr]), 7, 9)))) Equal to 0
                        • Then - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Level of Set Building for (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr])) Equal to 1
                            • Then - Actions
                              • Unit - Remove (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                              • Unit - Remove (Load (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                              • Set TempStringFinal = 000000000
                              • Hashtable - Save TempStringFinal as Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                              • Hashtable - Save TempStringFinal as (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                              • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, udg_Integer_C )
                              • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, ( udg_Integer_C * -1 ) )
                            • Else - Actions
                              • Unit - Remove (Load (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                              • For each (Integer Integer_D) from 1 to MaxDecay, do (Actions)
                                • Loop - Actions
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • ((Load Integer_D of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]) is alive) Equal to False
                                    • Then - Actions
                                      • Game - Display to (All players) the text: Blows up
                                      • Hashtable - Save Handle Of(Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) as Integer_D of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]
                                      • Hashtable - Save Handle Of(Load (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) as (Integer_D x -1) of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]
                                      • Unit - Add Building Decay (No Primary) to (Load Integer_D of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr])
                                      • Special Effect - Create a special effect attached to the overhead of (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) using Abilities\Spells\Other\TalkToMe\TalkToMe.mdl
                                      • Hashtable - Save Handle Of(Last created special effect) as Integer_D of KeyDecayBuilding in DatabaseSpecialEffect[TempPlayerNr]
                                      • Unit - Change color of (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) to Black
                                      • Set TempStringFinal = 000000000
                                      • Hashtable - Save TempStringFinal as Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                                      • Hashtable - Save TempStringFinal as (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, udg_Integer_C )
                                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, (udg_Integer_C * -1))
                                      • Custom script: exitwhen true
                                    • Else - Actions
                        • Else - Actions
              • Skip remaining actions
            • Else - Actions
          • -------- Break Secondary Building --------
          • For each (Integer B) from 1 to MaxSecondaryBuildings, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • TempUnit Equal to (Load (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr])
                  • (Integer((Substring((Load (Integer B) of KeyGreatHallSecondary from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                • Then - Actions
                  • Set TempStringFinal = 000000000
                  • Unit - Remove (Load ((Integer B) x -1) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]) from the game
                  • Hashtable - Save TempStringFinal as (Integer B) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]
                  • Hashtable - Save TempStringFinal as ((Integer B) x -1) of KeyGreatHallSecondary in DatabaseBuilding[TempPlayerNr]
                  • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyGreatHallSecondary, GetForLoopIndexB() )
                  • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyGreatHallSecondary, ( GetForLoopIndexB() * -1 ) )
                  • -------- War Mill --------
                  • For each (Integer Integer_C) from 1 to MaxSecondaryBuildings, do (Actions)
                    • Loop - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • And - All (Conditions) are true
                            • Conditions
                              • (Integer((Substring((Load Integer_C of KeyWarMill from DatabaseBuilding[TempPlayerNr]), 1, 3)))) Equal to (Integer A)
                              • (Integer((Substring((Load Integer_C of KeyWarMill from DatabaseBuilding[TempPlayerNr]), 4, 6)))) Equal to (Integer B)
                        • Then - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Level of Set Building for (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr])) Equal to 1
                            • Then - Actions
                              • Unit - Remove (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                              • Unit - Remove (Load (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                              • Set TempStringFinal = 000000000
                              • Hashtable - Save TempStringFinal as Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                              • Hashtable - Save TempStringFinal as (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                              • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, udg_Integer_C )
                              • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, ( udg_Integer_C * -1 ) )
                            • Else - Actions
                              • For each (Integer Integer_D) from 1 to MaxDecay, do (Actions)
                                • Loop - Actions
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • ((Load Integer_D of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]) is alive) Equal to False
                                    • Then - Actions
                                      • Game - Display to (All players) the text: Blows up
                                      • Hashtable - Save Handle Of(Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) as Integer_D of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]
                                      • Hashtable - Save Handle Of(Load (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) as (Integer_D x -1) of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]
                                      • Unit - Add Building Decay (No Primary) to (Load Integer_D of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr])
                                      • Special Effect - Create a special effect attached to the overhead of (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) using Abilities\Spells\Other\TalkToMe\TalkToMe.mdl
                                      • Hashtable - Save Handle Of(Last created special effect) as Integer_D of KeyDecayBuilding in DatabaseSpecialEffect[TempPlayerNr]
                                      • Unit - Change color of (Load Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]) to Black
                                      • Set TempStringFinal = 000000000
                                      • Hashtable - Save TempStringFinal as Integer_C of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                                      • Hashtable - Save TempStringFinal as (Integer_C x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, udg_Integer_C )
                                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, (udg_Integer_C * -1))
                                      • Custom script: exitwhen true
                                    • Else - Actions
                        • Else - Actions
                  • Skip remaining actions
                • Else - Actions
      • -------- War Mill --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Level of Building Decay (No Primary) for TempUnit) Greater than 0
        • Then - Actions
          • For each (Integer A) from 1 to MaxDecay, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Load (Integer A) of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]) Equal to TempUnit
                • Then - Actions
                  • Unit - Remove (Load ((Integer A) x -1) of KeyDecayBuilding in DatabaseBuilding[TempPlayerNr]) from the game
                  • Special Effect - Destroy (Load (Integer A) of KeyDecayBuilding in DatabaseSpecialEffect[TempPlayerNr])
                  • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyDecayBuilding, GetForLoopIndexA() )
                  • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyDecayBuilding, ( GetForLoopIndexA() * -1 ) )
                  • Skip remaining actions
                • Else - Actions
        • Else - Actions
          • For each (Integer A) from 1 to MaxSecondaryBuildings, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Load (Integer A) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) Equal to TempUnit
                • Then - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Level of Set Building for (Load (Integer A) of KeyWarMill in DatabaseBuilding[TempPlayerNr])) Equal to 1
                    • Then - Actions
                      • Unit - Remove (Load (Integer A) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                      • Unit - Remove (Load ((Integer A) x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                      • Set TempStringFinal = 000000000
                      • Hashtable - Save TempStringFinal as (Integer A) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                      • Hashtable - Save TempStringFinal as ((Integer A) x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, GetForLoopIndexA() )
                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, ( GetForLoopIndexA() * -1 ) )
                      • Skip remaining actions
                    • Else - Actions
                      • Unit - Create 1 Demolish SFX Medium for TempPlayer at TempPoint facing Default building facing degrees
                      • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
                      • Custom script: call RemoveLocation (udg_TempPoint)
                      • Set TempStringFinal = 000000000
                      • Unit - Remove (Load ((Integer A) x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]) from the game
                      • Hashtable - Save TempStringFinal as (Integer A) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                      • Hashtable - Save TempStringFinal as ((Integer A) x -1) of KeyWarMill in DatabaseBuilding[TempPlayerNr]
                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, GetForLoopIndexA() )
                      • Custom script: call RemoveSavedHandle( udg_DatabaseBuilding[udg_TempPlayerNr], udg_KeyWarMill, ( GetForLoopIndexA() * -1 ) )
                      • Skip remaining actions
                • Else - Actions

If it always goes to 100 in each loop, then you're making around 10k actions per loop. Depending on how heavy your loop is, it's not impossible for you to be hitting the OP limit. If you post your trigger someone might find a more efficient way of doing what you're currently doing. Have you tried printing a message when the loop breaks on purpose, so that you're sure that it's not breaking due to a logical mistake in the break condition that causes an early exit?
Yes, I've been using text to see where it breaks, it breaks in the first loop nest when it enters Loop C if the MaxSecondaryBuilding integer is over a certain value. If it is at 100 (Default) anything that runs off Loop Integer A = 1 runs flawlessly, so I know it's not a syntax thing. The moment something would be on pos 2 in Loop Integer A, it causes the trigger to break (As it does no more operations) once it enters Loop C in the first nest, but it will run if I set MaxSecondaryBuildings to 50. But for 50 the trigger will break at the exact same point if it needs to reach Integer A = 3, but it will work if I go even lower with MaxSecondaryBuildings Integer, and so on.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
Alright, that's quite the trigger. So what is your goal here because if you're using Hashtables then you shouldn't need to even have For Loops. You can just Load exactly what you need and do with it as you please.

Some random notes:
  • Hashtable Arrays may be unnecessary. You can probably get away with 1 Hashtable per category. The Hashtable limit is 255.
  • You're using "AND - All Conditions are True" for no reason. By default all conditions must be true.
  • Custom script: exitwhen true <-- Does this work?
 
From looking at your code, it does seem possible that you would reach the OP limit. IIRC you could do a few hundred thousand hashtable lookups before the OP limit was reached, but since you're using GUI that means you're going to have a lot of additional inefficiencies from having to make superfluous function calls. So only being able to run your loop a couple of tens of thousands of times seems reasonable.

What I would recommend doing is completely redesigning your trigger. Could you explain what your storing in the hashtables? It seems like you're storing a list of units. Also, it seems like you're storing a String and a Unit in the same key? As far as I'm aware you're not able to store two values in the same key of a hashtable, even if they are of different types.

What is this trigger supposed to be doing? What I can gather is:

  • when a primary building dies, destroy all secondary buildings and war mills that are connected to it
  • when a secondary building dies, destroy all war mills that are connected to it
  • I'm not sure what's supposed to happen when a war mill dies?
  • I'm not sure what's being stored in key (Integer X) * -1? Like in the following line:

  • Remove (Load ((Integer A) x -1) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]) from the game


  • Custom script: exitwhen true <-- Does this work?

Yeah it works. Basically the same as a "break" statement in actual programming languages.
 
Level 2
Joined
Nov 3, 2021
Messages
11
Alright, that's quite the trigger. So what is your goal here because if you're using Hashtables then you shouldn't need to even have For Loops. You can just Load exactly what you need and do with it as you please.

Some random notes:
  • Hashtable Arrays may be unnecessary. You can probably get away with 1 Hashtable per category. The Hashtable limit is 255.
  • You're using "AND - All Conditions are True" for no reason. By default all conditions must be true.
  • Custom script: exitwhen true <-- Does this work?
Is there an easier way to find a certain unit within a hashtable? Frankly I might have been doing it wrong since I don't know any better. I use for loops because I need to find every dependency on a building, since I made my map with a "Tributary" system, if you will.
For example, there's the primary great halls, who serve as the city center and are built by colonists trained by great halls or spawned at the start of the game.
The secondary great halls act as districts and allow for the city's expansion. They can only be built by primary Great Halls.
The production buildings and houses (I've only worked on War Mills so far) can be built by either Primary Great Halls or Secondary Great Halls, and here's where the dependency system kicks in.
If I build a War Mill, I need the game to know it's a dependency of Primary Great Hall 2, let's say, that would be the Great Hall stored in the handle 2 of KeyGreatHallPrime (The integer variable is 1, I've chosen to have it like this in case I want to change the key for it to be easier and to not forget what key I used for what subset of buildings/units) in the Buildings Hashtable. But, if it's built by a secondary great hall, then the dependency goes as follow -> The War Mill belongs to the Secondary Great Hall which belong to the Primary Great Hall, and I want the game to be able to tell that. Which is why it goes through all these loops, maybe necessarily in some cases, because for example if the Primary Great Hall gets destroyed, or gets upgraded, etc., the game will need to find all dependencies and do something with them.
If there's a better way of doing this I would really like to know because it's terribly inefficient, but I can't think of another way to do this.
As for the AND thing yeah, I kind of went overboard, I just used it because it makes the conditions stand out a bit more from the rest of the text. The custom script does work, it breaks out of the most recent loop.

From looking at your code, it does seem possible that you would reach the OP limit. IIRC you could do a few hundred thousand hashtable lookups before the OP limit was reached, but since you're using GUI that means you're going to have a lot of additional inefficiencies from having to make superfluous function calls. So only being able to run your loop a couple of tens of thousands of times seems reasonable.

What I would recommend doing is completely redesigning your trigger. Could you explain what your storing in the hashtables? It seems like you're storing a list of units. Also, it seems like you're storing a String and a Unit in the same key? As far as I'm aware you're not able to store two values in the same key of a hashtable, even if they are of different types.

What is this trigger supposed to be doing? What I can gather is:

  • when a primary building dies, destroy all secondary buildings and war mills that are connected to it
  • when a secondary building dies, destroy all war mills that are connected to it
  • I'm not sure what's supposed to happen when a war mill dies?
  • I'm not sure what's being stored in key (Integer X) * -1? Like in the following line:

  • Remove (Load ((Integer A) x -1) of KeyGreatHallPrime in DatabaseBuilding[TempPlayerNr]) from the game




Yeah it works. Basically the same as a "break" statement in actual programming languages.
I'm storing units that depend on other units, the string is built out of 3 identifiers (First three digit represents the primary building, next three represent the secondary dependency and the last three the tertiary dependency), if it's a Primary Great Hall like I explained in the upper novella (lol), only the first three digits are used and the rest are 0s, if it's a Secondary Great Hall or production building/house built directly by the Primary Great Hall the first 6 digits will be used, and the last three will be 0s and if it's a production building or house built by the Secondary Great Hall it will use all digits. The identifiers are part of the dependency system I've developed. You can store an integer, a real, a boolean a string and a handle on the same spot of a hashtable without them conflicting, the conflict comes when you try to save another handle, be it unit, doodad, special effect, etc. since they all use the same storage, but the rest can be saved safely.
As for what the trigger does you're partially correct. Secondary buildings and war mills get destroyed only if they have the "Set" ability which means they haven't officially been built yet and resources used. I'm using a system that allows you to rotate the buildings first before placing them and using the resources, as an aesthetic choice. Building without the "Set" ability will cancel all production and become rescueable if War mills in this case, or gain the ability to become a new Primary Building/City Center if they're a Secondary Great Hall. Same for War Mills linked to Secondary Great Halls.
What's stored in the Integer x -1 is a special unit I use to imitate the pathing the building is supposed to have. Since you can't rotate buildings, I had to turn the building you actually interact with into a unit. There's also another benefit to this, if an immovable unit tries to use a tiny build ability, the game allows you to choose to build over the casting unit when you hover the placing model, and the immovable unit automatically gets moved to the side so the tiny unit building gets built. if I use these "Foundation" units that act as actual buildings, but have no model, the game will see they block pathing and are not the casting unit and thus won't allow you to build over the casting unit.
 
As far as I can tell, you should be able to use the following schema:

  • Store a Unit Group for primary/secondary buildings, which will contain their dependencies.
  • Store a Unit for secondary buildings/war mills, which will be their parent unit.

When you need to do something with dependent units, you can use a ForGroup loop to iterate over the stored groups. You can check whether a unit is dependent on another unit in constant time (by checking the hashtable). You can also check the parent of the parent in constant time.

I'm just not sure whether you can have nested ForGroup loops in GUI. This would be required to access the grandchildren. ForGroup loops are naturally safe against the OP limit, as they start new threads for each unit, so that's a nice bonus. ForGroup loops are bit finicky with local variables, but it doesn't look like you are using them, so that shouldn't be a problem.

You also cannot create groups in GUI, you would need to use a custom script to do that (store the created group in a variable and then save that variable to the hashtable in GUI).

I'm storing units that depend on other units, the string is built out of 3 identifiers (First three digit represents the primary building, next three represent the secondary dependency and the last three the tertiary dependency), if it's a Primary Great Hall like I explained in the upper novella (lol), only the first three digits are used and the rest are 0s, if it's a Secondary Great Hall or production building/house built directly by the Primary Great Hall the first 6 digits will be used, and the last three will be 0s and if it's a production building or house built by the Secondary Great Hall it will use all digits.

I don't really get what the string is supposed to be doing exactly, but in the schema above you could tell if a unit is a primary building if it has no parent. You can tell if its a secondary building if it has both a parent and a group of dependent units. And you can tell if it's a production building if it has a parent but not a group.

Since you can't rotate buildings, I had to turn the building you actually interact with into a unit.

Actually it's always been possible to rotate buildings, though before Reforged you needed to use a trick with Uproot in order to do it. Now it can easily be done with BlzSetUnitFacingEx followed by SetUnitPosition to redraw the unit and update its facing. Though this doesn't really solve your second problem, so I guess it doesn't apply here unless you have another solution.

You can store an integer, a real, a boolean a string and a handle on the same spot of a hashtable without them conflicting,

Oh, that's interesting. Makes me wonder how they implemented that internally. Maybe each element in the hashtable is actually an object that has a field for each of the base JASS types. Seems about as logical as having a non-optional OP Limit in your language :xxd:
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
Maybe this can help? I wasn't entirely sure how your map works so I made my own design and used the Tiny abilities to create a chain of structures linked to one another. The Peasant builds Structure A, which then builds B, which lastly builds C.
  • Build
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
    • Actions
      • -------- Order for using a Tiny ability: --------
      • Custom script: if GetIssuedOrderId() == 852619 then
      • Set VariableSet Builder = (Triggering unit)
      • Custom script: endif
  • Link Buildings
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • ((Triggering unit) is A structure) Equal to True
      • (Builder is A structure) Equal to True
    • Actions
      • -------- Link buildings together (-1 = child, -2 = parent): --------
      • Custom script: set udg_Handle = udg_Builder
      • Hashtable - Save Handle Of(Triggering unit) as -1 of (Key Handle.) in LinkHashtable.
      • Hashtable - Save Handle OfBuilder as -2 of (Key (Triggering unit).) in LinkHashtable.
  • Destroy Links
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A structure) Equal to True
    • Actions
      • -------- Kill child of dying building: --------
      • Unit - Kill (Load -1 of (Key (Triggering unit).) in LinkHashtable.)
To get a buildings Parent you Load -2 of it's Key. To get it's Child you Load -1 of it's Key.

As an example, I made it so when you destroy a building it's child is destroyed, creating a chain reaction. So if you destroy A then B is destroyed followed by C. If you destroy B then C is destroyed. If you destroy C then nothing happens since C can't build anything.

Hopefully I'm not overlooking some important details. Note that I had to use the stupid Issued order trigger because there's no Event Response to get a Constructor. You can get a Constructing/Constructed structure but not the unit that Constructed it, awesome stuff... You'd have to use a more elaborate system to detect the Builder if you wanted this to work without bugs.
 

Attachments

  • Hashtable Link.w3m
    18.1 KB · Views: 16
Last edited:
Level 2
Joined
Nov 3, 2021
Messages
11
As far as I can tell, you should be able to use the following schema:

  • Store a Unit Group for primary/secondary buildings, which will contain their dependencies.
  • Store a Unit for secondary buildings/war mills, which will be their parent unit.

When you need to do something with dependent units, you can use a ForGroup loop to iterate over the stored groups. You can check whether a unit is dependent on another unit in constant time (by checking the hashtable). You can also check the parent of the parent in constant time.

I'm just not sure whether you can have nested ForGroup loops in GUI. This would be required to access the grandchildren. ForGroup loops are naturally safe against the OP limit, as they start new threads for each unit, so that's a nice bonus. ForGroup loops are bit finicky with local variables, but it doesn't look like you are using them, so that shouldn't be a problem.

You also cannot create groups in GUI, you would need to use a custom script to do that (store the created group in a variable and then save that variable to the hashtable in GUI).



I don't really get what the string is supposed to be doing exactly, but in the schema above you could tell if a unit is a primary building if it has no parent. You can tell if its a secondary building if it has both a parent and a group of dependent units. And you can tell if it's a production building if it has a parent but not a group.



Actually it's always been possible to rotate buildings, though before Reforged you needed to use a trick with Uproot in order to do it. Now it can easily be done with BlzSetUnitFacingEx followed by SetUnitPosition to redraw the unit and update its facing. Though this doesn't really solve your second problem, so I guess it doesn't apply here unless you have another solution.



Oh, that's interesting. Makes me wonder how they implemented that internally. Maybe each element in the hashtable is actually an object that has a field for each of the base JASS types. Seems about as logical as having a non-optional OP Limit in your language :xxd:
The store unit group might be useful for what I'm trying to do, if anything it will prevent going over the OP limit again since I'll be searching only in that unit group. But it doesn't get over the problem I have with dependencies, but I might have found a workaround using custom values.

Maybe this can help? I wasn't entirely sure how your map works so I made my own design and used the Tiny abilities to create a chain of structures linked to one another. The Peasant builds Structure A, which then builds B, which lastly builds C.
  • Build
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
    • Actions
      • -------- Order for using a Tiny ability: --------
      • Custom script: if GetIssuedOrderId() == 852619 then
      • Set VariableSet Builder = (Triggering unit)
      • Custom script: endif
  • Link Buildings
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • ((Triggering unit) is A structure) Equal to True
      • (Builder is A structure) Equal to True
    • Actions
      • -------- Link buildings together (-1 = child, -2 = parent): --------
      • Custom script: set udg_Handle = udg_Builder
      • Hashtable - Save Handle Of(Triggering unit) as -1 of (Key Handle.) in LinkHashtable.
      • Hashtable - Save Handle OfBuilder as -2 of (Key (Triggering unit).) in LinkHashtable.
  • Destroy Links
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is A structure) Equal to True
    • Actions
      • -------- Kill child of dying building: --------
      • Unit - Kill (Load -1 of (Key (Triggering unit).) in LinkHashtable.)
To get a buildings Parent you Load -2 of it's Key. To get it's Child you Load -1 of it's Key.

As an example, I made it so when you destroy a building it's child is destroyed, creating a chain reaction. So if you destroy A then B is destroyed followed by C. If you destroy B then C is destroyed. If you destroy C then nothing happens since C can't build anything.

Hopefully I'm not overlooking some important details. Note that I had to use the stupid Issued order trigger because there's no Event Response to get a Constructor. You can get a Constructing/Constructed structure but not the unit that Constructed it, awesome stuff... You'd have to use a more elaborate system to detect the Builder if you wanted this to work without bugs.
I can't really do it that way since on the negative values I save certain work-around or cosmetic units, like I mentioned in a previous post the "fake foundation" unit. There's also the issue of different buildings being saved to different parent hashtables for convenience. Although the link trigger is very useful, I never thought about it like that.

So the work-around i found that would allow me to keep the dependencies would be saving the identifier as the custom value of the unit as follows:
1234567
With the green number representing primary, blue representing secondary and red representing tertiary that can b broken easily by converting it into a string, and then I can just pinpoit the unit I want instead of looping through the entire database each time. I know the integers can go to 9 characters in length (10 technically but that's a bit wonky). My question being, would using integers so large on every unit break something in Warcraft again?
Also thanks a lot guys for the help so far, you've been wonderful.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
Speaking from a "what's possible" point of view, you shouldn't need to use those strings. I feel like you're overcomplicating things and using unnecessary methods but maybe I'm just not understanding it.

It sounds like you can simplify a lot of this stuff but it'd require you to rework things to be done in a more proper way.
You may not be tapping into the full potential that Hashtables have to offer. I know I was using them wrong for a good while at first.

Also, I'm curious as to why you would need to ever loop through an entire database. As long as you have reference to a Unit/Player then you have reference to all of the data saved to that thing. A Unit can get you a Player, a Player can get you a Unit. Worst case you'd be Looping over a Unit Group or something that belongs to a specific player, like looping over all of a player's current structures, and loading data from those when needed. But most of the time I imagine you can simply Load data from an Event Response -> (Triggering unit), (Triggering player), etc... and not have to mess with multiple databases worth of Loops (sure, you may have a couple Loops here and there but they'd most likely be much smaller and not nested).
 
Last edited:
So the work-around i found that would allow me to keep the dependencies would be saving the identifier as the custom value of the unit as follows:
1234567
Anything that you can solve using a custom value can also be solved equivalently using a Hashtable, by storing the number in the hashtable using the unit's Handle ID as the key. Or in this case, you can store a string, as you had been already doing. I just don't understand what the string is for, though. Are you using it just to identify which unit is the parent?

You should also be saving the "pathing shadow unit" using the handle ID of the parent unit, then you can also access it in constant time without needing to loop over the database.
 
Status
Not open for further replies.
Top