• 🏆 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] Creating regions: optimization required.

Status
Not open for further replies.
Level 8
Joined
Mar 10, 2009
Messages
213
Context: I'm using either hand-placed (for small maps) or generated regions for TBS gameplay. When I worked with tiny maps, everything was rather easy and done via several loops within loops. However, now I'm working with the largest map (3 500 640 regions) and can't use Wc3 in-built loops (ForLoopA etc.) without game crashing every time.

And I can't create/remove them dynamically, because:
a) I suck at this (well, for now),
b) I require some regions to be kept at all times for quest events/doodad interaction etc.

Q: Why do you use waits?

A: ForLoopA bugs like hell with my array 0 to 1869 and onwards (1870 (horizontal) x1872 (vertical) matrix), and timers add too much trouble for a single player game. I'd consider them, if I ever convert this project to MUI. Past data generation stage I use timers almost for every other purpose, avoiding waits.

I'm posting below the most simplified version of my *working* triggers. While they do work, it takes forever to generate all the regions. Generating a row is OK, since I use 0.01 waits only with modulo, reducing the total amount of time required to generate a row to a minimum (mod of 1870=0, divisor 187).

The real trouble for me starts with column generation, that bugs if wait is inferior to 2 sec.

I can't have 1 hour-long region generation, that's beyond reasonable.
Would anyone know any proper way to optimize my crazy triggers?
Thank you for your attention.
  • INI Create Regions Setup
    • Events
      • Time - Elapsed game time is 2.00 seconds
    • Conditions
    • Actions
      • -------- Set Rect Size --------
      • Set Const_RectWidth = 32.00
      • Set Const_RectHeight = Const_RectWidth
      • -------- /Set Rect Size --------
      • -------- Set BL coordinates --------
      • Set Const_BL_L = -29920.00
      • Set Const_BL_B = -30208.00
      • Set Const_BL_R = (Const_BL_L + Const_RectWidth)
      • Set Const_BL_T = (Const_BL_B + Const_RectHeight)
      • -------- /Set BL coordinates --------
      • -------- Set Cell Limits --------
      • Set Const_FirstCellIndex = 0
      • Set Const_Limit_Cells_Per_Row = 1870
      • Set Const_Limt_Cells_Per_Column = (Const_Limit_Cells_Per_Row + 2)
      • Set Const_LastCellIndex = (Const_Limit_Cells_Per_Row x Const_Limt_Cells_Per_Column)
      • -------- /Set Cell Limits --------
      • -------- Run Main --------
      • Trigger - Run INI Create Regions Main <gen> (checking conditions)
      • -------- /Run Main --------
      • Custom script: call DestroyTrigger( GetTriggeringTrigger() )

  • INI Create Regions Main
    • Events
    • Conditions
    • Actions
      • -------- Create a row --------
      • Set FctVAR_ColumnCounter = 0
      • Trigger - Run subINI Create Regions Column Counter <gen> (checking conditions)
      • -------- /Create a row --------
      • Custom script: call DestroyTrigger( GetTriggeringTrigger() )
  • subINI Create Regions Column Counter
    • Events
    • Conditions
    • Actions
      • -------- Create Column --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FctVAR_ColumnCounter Less than Const_Limt_Cells_Per_Column
        • Then - Actions
          • -------- Set B and T --------
          • Set FctVAR_TempB = (Const_BL_B + (Const_RectHeight x (Real(FctVAR_ColumnCounter))))
          • Set FctVAR_TempT = (Const_BL_T + (Const_RectHeight x (Real(FctVAR_ColumnCounter))))
          • Set FctVAR_ColumnCounter = (FctVAR_ColumnCounter + 1)
          • -------- /Set B and T --------
          • -------- Clean up --------
          • Set FctVAR_RowCounter = -1
          • -------- /Clean up --------
          • -------- Create Row --------
          • Trigger - Run subINI Create Regions Row Counter <gen> (checking conditions)
          • -------- /Create Row --------
          • Wait 2.00 seconds
          • Trigger - Run (This trigger) (checking conditions)
        • Else - Actions
          • Custom script: call DestroyTrigger( GetTriggeringTrigger() )
      • -------- /Create Column --------
  • subINI Create Regions Row Counter
    • Events
    • Conditions
    • Actions
      • -------- Create Row --------
      • Set FctVAR_RowCounter = (FctVAR_RowCounter + 1)
      • Set FctVAR_TempL = (Const_BL_L + (Const_RectWidth x (Real(FctVAR_RowCounter))))
      • Set FctVAR_TempR = (Const_BL_R + (Const_RectWidth x (Real(FctVAR_RowCounter))))
      • Set Region_Array[(((FctVAR_ColumnCounter - 1) x Const_Limit_Cells_Per_Row) + FctVAR_RowCounter)] = (Region(FctVAR_TempL, FctVAR_TempB, FctVAR_TempR, FctVAR_TempT))
      • -------- /Create Row --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FctVAR_RowCounter Less than (Const_Limit_Cells_Per_Row - 1)
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (FctVAR_RowCounter mod (Const_Limit_Cells_Per_Row / 10)) Equal to 0
            • Then - Actions
              • Wait 0.01 seconds
            • Else - Actions
          • Trigger - Run (This trigger) (checking conditions)
        • Else - Actions
          • Game - Display to Player Group - Player 1 (Red) for 1.00 seconds the text: (Columns created: + (String(FctVAR_ColumnCounter)))
          • Game - Display to Player Group - Player 1 (Red) for 1.00 seconds the text: (Generating regions: + ((String(((Real(FctVAR_ColumnCounter)) x 0.05))) + %))
          • Trigger - Turn off (This trigger)
 
Last edited:
Level 8
Joined
Mar 10, 2009
Messages
213
I've tried this myself:
  • INI Create Regions Setup
    • Events
      • Time - Elapsed game time is 2.00 seconds
    • Conditions
    • Actions
      • -------- Set Rect Size --------
      • Set Const_RectWidth = 32.00
      • Set Const_RectHeight = Const_RectWidth
      • -------- /Set Rect Size --------
      • -------- Set BL coordinates --------
      • Set Const_BL_L = -29920.00
      • Set Const_BL_B = -30208.00
      • Set Const_BL_R = (Const_BL_L + Const_RectWidth)
      • Set Const_BL_T = (Const_BL_B + Const_RectHeight)
      • -------- /Set BL coordinates --------
      • -------- Set Cell Limits --------
      • Set Const_FirstCellIndex = 0
      • Set Const_Limit_Cells_Per_Row = 1870
      • Set Const_Limt_Cells_Per_Column = (Const_Limit_Cells_Per_Row + 2)
      • Set Const_LastCellIndex = (Const_Limit_Cells_Per_Row x Const_Limt_Cells_Per_Column)
      • -------- /Set Cell Limits --------
      • -------- Run Main --------
      • Trigger - Run INI Create Regions Main <gen> (checking conditions)
      • -------- /Run Main --------
      • Custom script: call DestroyTrigger( GetTriggeringTrigger() )
  • INI Create Regions Main
    • Events
    • Conditions
    • Actions
      • -------- Create Column --------
      • -------- Set B and T --------
      • Set FctVAR_ColumnCounter = 0
      • Set FctVAR_TempB = (Const_BL_B + (Const_RectHeight x (Real(FctVAR_ColumnCounter))))
      • Set FctVAR_TempT = (Const_BL_T + (Const_RectHeight x (Real(FctVAR_ColumnCounter))))
      • Set FctVAR_ColumnCounter = (FctVAR_ColumnCounter + 1)
      • -------- /Set B and T --------
      • -------- Clean up --------
      • Set FctVAR_RowCounter = -1
      • -------- /Clean up --------
      • -------- Create Row --------
      • Trigger - Run subINI Create Regions Row Counter <gen> (checking conditions)
      • -------- /Create Row --------
      • Custom script: call DestroyTrigger( GetTriggeringTrigger() )
      • -------- /Create Column --------
  • subINI Create Regions Row Counter
    • Events
    • Conditions
    • Actions
      • -------- Create Row --------
      • Set FctVAR_RowCounter = (FctVAR_RowCounter + 1)
      • Set FctVAR_TempL = (Const_BL_L + (Const_RectWidth x (Real(FctVAR_RowCounter))))
      • Set FctVAR_TempR = (Const_BL_R + (Const_RectWidth x (Real(FctVAR_RowCounter))))
      • Set Region_Array[(((FctVAR_ColumnCounter - 1) x Const_Limit_Cells_Per_Row) + FctVAR_RowCounter)] = (Region(FctVAR_TempL, FctVAR_TempB, FctVAR_TempR, FctVAR_TempT))
      • -------- /Create Row --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FctVAR_RowCounter Less than (Const_Limit_Cells_Per_Row - 1)
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (FctVAR_RowCounter mod (Const_Limit_Cells_Per_Row / 10)) Equal to 0
            • Then - Actions
              • Wait 0.01 seconds
            • Else - Actions
          • Trigger - Run (This trigger) (checking conditions)
        • Else - Actions
          • Game - Display to Player Group - Player 1 (Red) for 1.00 seconds the text: (Columns created: + (String(FctVAR_ColumnCounter)))
          • Game - Display to Player Group - Player 1 (Red) for 1.00 seconds the text: (Generating regions: + ((String(((Real(FctVAR_ColumnCounter)) x 0.05))) + %))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FctVAR_ColumnCounter Less than Const_Limt_Cells_Per_Column
            • Then - Actions
              • -------- Set B and T --------
              • Set FctVAR_TempB = (Const_BL_B + (Const_RectHeight x (Real(FctVAR_ColumnCounter))))
              • Set FctVAR_TempT = (Const_BL_T + (Const_RectHeight x (Real(FctVAR_ColumnCounter))))
              • Set FctVAR_ColumnCounter = (FctVAR_ColumnCounter + 1)
              • -------- /Set B and T --------
              • -------- Clean up --------
              • Set FctVAR_RowCounter = -1
              • -------- /Clean up --------
              • Wait 0.01 seconds
              • -------- Create Row --------
              • Trigger - Run (This trigger) (checking conditions)
              • -------- /Create Row --------
            • Else - Actions
              • Custom script: call DestroyTrigger( GetTriggeringTrigger() )

No difference, whatsoever =( (except for aesthethics)

The only 'solution' that comes to mind is limiting player's starting area to a X by Y map portion and generating the rest during the game, in the background.
 
Last edited:
Level 8
Joined
May 21, 2019
Messages
435
The first question that comes to mind is this:
Why are you even generating all of these regions to begin with? It seems extremely excessive and memory intensive compared to simply generating them dynamically.
You say its TBS (Turn based strategy?), so does that mean that the movement is restricted to moving across a certain amount of regions per "turn"?
If so, why not just generate these movement patterns whenever a unit is selected, based on offsets from their current location?

Is there something else to this? It'd be helpful if you clarified the entire context.
 
Level 39
Joined
Feb 27, 2007
Messages
4,992
Everything Cespie said plus: what in the flying fuck could you possibly need three million 32x32 regions for? Even if you managed to create them all you would need 100 different (yes) region arrays to hold them, and they would use more than 1.5gb of memory. If it took 1 second to cross a region it would take 60 minutes to traverse the entire map from one side to the other once. TBS though it may be... that your play space is that large is also ridiculous to me; nobody will ever use or explore all of it in any reasonable amount of time.

As the biggest map size possible is 512x512, I'm pretty confident you've got a map nearly that large but have divided each terrain tile into its own 4x4 grid to give you the full 1872x1872 play space that results in the ludicrous number of regions. I think part of the issue is that you are confused about how big a standard wc3 terrain tile is: they are 128x128, not 32x32. With this in mind you probably mean to have a 128-width grid, which reduces the total number of regions by a factor of 16 to 218790. Still excessive but possibly manageable with 8 arrays of regions. Even if you did manage to downscale the entire game to 1/4 scale... terrain would look awful, cliffs would be massive, and you'd definitely hit the destructible limit (which afaik can't be surpassed any more).

It is my intuition that you are wrong about whatever you think you need this for.

That aside there is one more important thing to talk about: there's absolutely no reason to destroy your triggers once they fire. In fact it appears you're destroying some of them after the first time they're run but then attempting to run them again! The only reason this isn't borking the whole process is that GetTriggeringTrigger() doesn't refer to the trigger you call it in... it's the trigger that originally went off to start the whole chain reaction: the one with the elapsed time event. In the triggers that are manually run and have waits in them it might even return null. Don't destroy your triggers; it confers no tangible benefit, especially when you are creating a ludicrous number of regions right before.


And use [trigger] tags not [code] tags: How To Post Your Trigger
 
Level 8
Joined
Mar 10, 2009
Messages
213
use trigger tags not code tags
Thank you, I've updated the posts.

what in the flying fuck could you possibly need three million 32x32 regions for?
I wanted a massive open world space for NPC behaviour simulation (partially done).

I think part of the issue is that you are confused about how big a standard wc3 terrain tile is: they are 128x128, not 32x32. With this in mind you probably mean to have a 128-width grid, which reduces the total number of regions by a factor of 16 to 218790. Still excessive but possibly manageable with 8 arrays of regions. Even if you did manage to downscale the entire game to 1/4 scale... terrain would look awful, cliffs would be massive

Yeah, upscaling looks like the best idea. Thank you for professional perspective and info on arrays! I trust your wisdom and experience. Appreciated. I just wanted to replicate my XP with the large map. The tiny map that I've managed to make function without issues has 6 240 (placed manually for ballistic angle adjustment for bullets/projectiles) regions and runs well. I also block some regions for custom path algorithms, so that an NPC doesn't attempt to go to an unaccessible area (for example).
full
That being said, I think 78x80 size isn't that bad after all. I'll just produce more tiny maps to make up for the space loss and link them together in a campaign-like fashion. The reason I've downscaled so much is to use more or less realistic proportions for objects like buildings, trees and towers compared to the unit size, and I wanted to keep some margin for some potentially large objects. It just works. (c) Todd Howard


and you'd definitely hit the destructible limit (which afaik can't be surpassed any more)
Ouch. Well, that's a big one. Changes everything. I'll reconsider the entire design and stick to the idea of multiple maps expressed above.


That aside there is one more important thing to talk about: there's absolutely no reason to destroy your triggers once they fire. In fact it appears you're destroying some of them after the first time they're run but then attempting to run them again! The only reason this isn't borking the whole process is that GetTriggeringTrigger() doesn't refer to the trigger you call it in... it's the trigger that originally went off to start the whole chain reaction: the one with the elapsed time event. In the triggers that are manually run and have waits in them it might even return null. Don't destroy your triggers; it confers no tangible benefit, especially when you are creating a ludicrous number of regions right before.
Well, if the memory gain is insignificant, I'll stop doing it. I'm trying to destroy the triggers that I don't use any more, once the condition is matched. Normally, I didn't notice running any of those that should've been destroyed.

If so, why not just generate these movement patterns whenever a unit is selected, based on offsets from their current location?
I'll think about doing it this way then. I'll try to create a dynamic grid around each unit, based on its maximum movement range (like 10 x 10 32 grid, for example) and see if it works well.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
4,992
My comment about destructible limit was presuming you to be terraining a normal wc3 style map but just tiny. Looks like it’s pretty sparse and you just want units to be small in relation to the environment so my comment may not apply.

Could you show some of the irregular region areas like you said you’ve used? Your screenshot makes it clear how the regions are laid out but not what you are doing with them. If you know the grid width (32) that should be all you need to ‘simulate’ the grid for movement/attack/target-finding purposes, no?

If you want to dive into the JASS way to do this, regions are actually collections of rects and afaik don’t have to be contiguous. Rects can also be moved. That might allow you to easily create and pre-place some no-go rects in the proper places and then be able to check if a next move is disallowed by seeing if it’s in that super region. Food for thought.
 
Level 8
Joined
Mar 10, 2009
Messages
213
Food for thought.
Yeah, I know. You're 100% right. I'll finish all other in-game things left to develop at the moment, then will be back at the dynamic rects. I guess I was too lazy not willing to delve deeper:p into dynamic stuff.
I got carried away with the idea of using fixed or pre-placed rects as a grid.
Now that I think of it: your idea makes sense, I just missed it in the blind spot: I'll just use bare coordinates for everything. and add/remove points. Thank you a lot for setting me straight.
 
Last edited:

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,188
If you want to dive into the JASS way to do this, regions are actually collections of rects and afaik don’t have to be contiguous. Rects can also be moved. That might allow you to easily create and pre-place some no-go rects in the proper places and then be able to check if a next move is disallowed by seeing if it’s in that super region. Food for thought.
Regions are a collection of map cells, probably in a two way relationship.

If map cells are mapped to too many regions Warcraft III becomes extremely buggy. For example the path finder will completely break and units will literally start to walk into walls.

If you want to map data to pieces of a grid then use arrays directly. For sparse information use a hashtable.

If array size is an issue one can migrate to Lua. Lua tables support both a continuous list operating mode for non-sparse data and a table operating mode for sparse data. Both modes have no realistic limit (the application will likely crash due to insufficient memory before it is reached).
 
Status
Not open for further replies.
Top