[Trigger] Remove Wait? Creep Respawn GUI

Level 21
Joined
Mar 16, 2008
Messages
955
Trying to remove the Wait actions from this trigger in hopes or reducing desyncs or other bugs, just to be extra cautious. I was thinking assign the dying units to a variable, checking the array for any unused slots, then use 100s of timers to track their individual respawn time then clear the unit variable. But there must be a better way. Any constructive thoughts appreciated. Thank you.

  • Creep Respawn Loop
    • Events
      • Unit - A unit owned by Neutral Hostile Dies
    • Conditions
      • (Unit-type of (Dying unit)) Not equal to Fire Golem (Elemental Boss)
      • (Unit-type of (Dying unit)) Not equal to Leviathan (Elemental Boss)
      • (Unit-type of (Dying unit)) Not equal to Storm (Elemental Boss)
      • (Unit-type of (Dying unit)) Not equal to Penguin King (Elemental Boss)
      • (Unit-type of (Dying unit)) Not equal to Queen of Suffering
      • (Unit-type of (Dying unit)) Not equal to Skull Reaver (Standard)
      • (Unit-type of (Dying unit)) Not equal to Infernal Machine
      • (Unit-type of (Dying unit)) Not equal to Genie
      • (Unit-type of (Dying unit)) Not equal to Satan Incarnate
      • (Unit-type of (Dying unit)) Not equal to Tide Shaman Giant
    • Actions
      • Set VariableSet I = (I + 1)
      • Set VariableSet Creep[I] = (Dying unit)
      • Wait 595.00 seconds
      • Set VariableSet J = (J + 1)
      • Unit - Create 1 (Unit-type of Creep[J]) for Neutral Hostile at (Position offset by (X[(Custom value of Creep[J])], Y[(Custom value of Creep[J])])) facing Angle[(Custom value of Creep[J])] degrees
      • Unit - Set the custom value of (Last created unit) to (Custom value of Creep[J])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Map Center <gen> contains (Last created unit)) Equal to True
        • Then - Actions
          • Unit - Remove (Last created unit) from the game
        • Else - Actions

  • Creep Respawn Initial Start
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Creeps = (Units owned by Neutral Hostile.)
      • Unit Group - Remove Fire Golem (Elemental Boss) 0687 <gen> from Creeps.
      • Unit Group - Remove Penguin King (Elemental Boss) 0693 <gen> from Creeps.
      • Unit Group - Remove Storm (Elemental Boss) 0802 <gen> from Creeps.
      • Unit Group - Remove Leviathan (Elemental Boss) 0803 <gen> from Creeps.
      • Unit Group - Remove Skull Reaver (Standard) 0232 <gen> from Creeps.
      • Unit Group - Remove Queen of Suffering 0775 <gen> from Creeps.
      • Unit Group - Remove Queen of Suffering 0774 <gen> from Creeps.
      • Unit Group - Remove Infernal Machine 0772 <gen> from Creeps.
      • Unit Group - Remove Infernal Machine 0385 <gen> from Creeps.
      • Unit Group - Remove Skull Reaver (Standard) 0579 <gen> from Creeps.
      • Unit Group - Remove Faceless Priest 0516 <gen> from Creeps.
      • Unit Group - Remove Genie 1507 <gen> from Creeps.
      • Unit Group - Pick every unit in (Units in Hell Region <gen>) and do (Actions)
        • Loop - Actions
          • Unit Group - Remove (Picked unit) from Creeps.
      • Unit Group - Pick every unit in (Units in (Playable map area) owned by Neutral Hostile) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of (Picked unit)) Equal to Tide Shaman Giant
            • Then - Actions
              • Unit Group - Remove (Picked unit) from Creeps.
            • Else - Actions
      • Unit Group - Pick every unit in Creeps and do (Actions)
        • Loop - Actions
          • Set VariableSet I = (I + 1)
          • Unit - Set the custom value of (Picked unit) to I
          • Set VariableSet Angle[I] = (Facing of (Picked unit))
          • Set VariableSet Position = (Position of (Picked unit))
          • Set VariableSet X[I] = (X of Position)
          • Set VariableSet Y[I] = (Y of Position)
          • Custom script: call RemoveLocation(udg_Position)
      • Set VariableSet I = 0
      • Set VariableSet Position = (Center of (Entire map))
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
A single repeating Timer that periodically updates, say 25 times per second, which tracks the elapsed time for dead units in an Array. Think of a Dynamic Indexing setup where you Index dying units.
  • Set Variable Respawn_Time[X] = (Respawn_Time[X] - 0.04)
  • If Respawn_Time[X] Less than 0.01 Then (Unit - Create 1 Respawn_UnitType[X] at Respawn_Point[X]) Else (do nothing)
Or create a new Timer for each dead unit that respawns them once it expires. This would require something like a Hashtable to store the Timers and would ideally be handled in Jass since GUI doesn't offer the same level of control over timers.

But this has been done 1000's of times before, I imagine there's many threads on Hive about respawning creeps effectively.

Edit: Also, using Custom Value yourself is usually a mistake. You likely want to be using a Unit Indexer instead which offers the same functionality with far less limitations. However, you probably don't want the recycling functionality or at the very least you'll want to control it, that way dead units don't get their custom values recycled (which would break your above triggers). You could edit the system to be more flexible and work with your map, it's really not a complicated system at all.
 
Last edited:
Level 29
Joined
Sep 26, 2009
Messages
2,594
Instead of huge lists of units in your condiitions just create a dummy passive ability called "Not Respawnable" and give it to units which should not respawn.
You can do it via object editor for some boss units, etc., but also via trigger for units in Hell Region.
In your respawn trigger you can then just check that unit does not have this "Not Respawnable" ability.

I also see that you omit more units in your init trigger than you do in your respawn trigger. For example the respawn trigger will run for Tide Shaman Giant, even though there are no data stored for that unit in your arrays.

Finally, I also see one potential issue for crash: Your respawn location.
You currently respawn units at this location: (Position offset by (X[(Custom value of Creep[J])], Y[(Custom value of Creep[J])]))
  • The "Position" is a point variable that you seem to reuse in multiple places.
  • You take whatever location is in Position variable and you offset it by unit's original X and Y coordinates.
  • If there is currently nothing in Position variable, then that's fine, the location will be [0,0] offset by your X[] and Y[] arrays
  • but if there is some location in Position variable, then you have an object with some [x,y] values that you further offset by X[] and Y[] arrays, potentially respawning units outside map bounds, crashing your map.
 
Level 21
Joined
Mar 16, 2008
Messages
955
Thanks both of you. Verring a bit off topic for this thread but I think maybe we should just scrap that trigger. I created a new draft trigger. I haven't tested. Just an idea. Does anyone have any comments?

  • New Respawn Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet creep_hashtable = (Last created hashtable)
      • Unit Group - Pick every unit in (Units in Main Map Region <gen> owned by Neutral Hostile) and do (Actions)
        • Loop - Actions
          • -------- unit type (hashtable) --------
          • Hashtable - Save Handle Of(Picked unit) as creep_counter of 0 in creep_hashtable.
          • -------- unit alive or not (unit array size 1000) --------
          • Set VariableSet creep_array[(creep_counter + 1)] = (Picked unit)
          • -------- point of unit (hashtable) --------
          • Hashtable - Save Handle Of(Position of (Picked unit)) as (creep_counter + 1) of 1 in creep_hashtable.
          • -------- unit facing (hashtable) --------
          • Hashtable - Save (Facing of (Picked unit)) as (creep_counter + 1) of 2 in creep_hashtable.

  • New Respawn Creep
    • Events
      • Game - The in-game time of day becomes Equal to 6.00
    • Conditions
    • Actions
      • For each (Integer creep_A_interger) from 1 to creep_counter, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (creep_array[creep_A_interger] is dead) Equal to True
            • Then - Actions
              • Unit - Create 1 (Unit-type of (Load creep_A_interger of 0 in creep_hashtable.)) for Neutral Hostile at ((Load creep_A_interger of 1 in creep_hashtable.) offset by (0.00, 0.00)) facing (Load creep_A_interger of 2 from creep_hashtable.) degrees
            • Else - Actions
 
Level 29
Joined
Sep 26, 2009
Messages
2,594
init trigger won't work the way you expect it, because you never increment creep_counter, so you just overwrite data over and over.
What you should do first inside the "Pick Every Unit" is increment creep_counter and then save all data in your hashtable under same parent key (creep_counter), omit the "+1" from creep_counter when storing data into hashtable.

In your init trigger, based on the comment "unit alive or not (unit array size 1000)": Are you setting size for that unit array? Because if so, then that is unneeded. Size 1 will work perfectly as it does not mean length of the array, it means number of initialized values in the array.

In your respawn trigger you never assign the newly created unit into creep_array, so after you respawn creep even if the creep is alive next day a new one will be spawned next to it.

Edit: also, why do you keep putting "Point with offset" everywhere? Why reference original spawn point offset by nothing, instead of just referencing the original spawn point directly?
 
Level 21
Joined
Mar 16, 2008
Messages
955
Is there a way to simply reference a point in GUI without the off set part?

1742850342588.png
 
Level 21
Joined
Mar 16, 2008
Messages
955
yes, you don't select a function, you select the variable from drop down above
But it's not a simple point variable. It's saved in the hashtable. Should I just make it a non-hashtable point variable? or maybe I'm just confused how this all works.

EDIT:
Here's where the triggers are now. I think I added everything you mentioned.
  • New Respawn Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet creep_hashtable = (Last created hashtable)
      • Unit Group - Pick every unit in (Units in Main Map Region <gen> owned by Neutral Hostile) and do (Actions)
        • Loop - Actions
          • -------- increment counter --------
          • Set VariableSet creep_counter = (creep_counter + 1)
          • -------- unit type (hashtable) --------
          • Hashtable - Save Handle Of(Picked unit) as creep_counter of 1 in creep_hashtable.
          • -------- unit alive or not (unit array size 1000) --------
          • Set VariableSet creep_array[creep_counter] = (Picked unit)
          • -------- point of unit (hashtable) --------
          • Set VariableSet creep_point[creep_counter] = (Position of (Picked unit))
          • -------- unit facing (hashtable) --------
          • Hashtable - Save (Facing of (Picked unit)) as creep_counter of 2 in creep_hashtable.
  • New Respawn Creep
    • Events
      • Game - The in-game time of day becomes Equal to 6.00
    • Conditions
    • Actions
      • For each (Integer creep_A_interger) from 1 to creep_counter, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (creep_array[creep_A_interger] is dead) Equal to True
            • Then - Actions
              • Unit - Create 1 (Unit-type of (Load creep_A_interger of 1 in creep_hashtable.)) for Neutral Hostile at creep_point[creep_A_interger] facing (Load creep_A_interger of 2 from creep_hashtable.) degrees
              • Set VariableSet creep_array[creep_A_interger] = (Last created unit)
              • Special Effect - Create a special effect attached to the origin of creep_array[creep_A_interger] using Abilities\Spells\Undead\RaiseSkeletonWarrior\RaiseSkeleton.mdl
              • Special Effect - Destroy (Last created special effect)
            • Else - Actions

1742851877824.png


1742851891625.png


1742851905292.png


1742851916957.png


1742851928228.png
 
Last edited:
Level 29
Joined
Sep 26, 2009
Messages
2,594
I kind of fail to see where the confusion lies.
The location parameter for "Create unit" function is the very same type as the first parameter for "Point with offset" function - the list of available functions and point variables is same in both places.

I mean, even in your screenshot from this post:
Is there a way to simply reference a point in GUI without the off set part?

the first function is "Hashtable - Load Location Handle"... the very same function you have used in "Point with offset" :D
 
Level 21
Joined
Mar 16, 2008
Messages
955
Oh I think I was just confused on some of the GUI options for hashtables. I don't use them very often. Anyway I switched it to a point variable array. Do you have a comment on that? Do you think the triggers are worth testing in game at this point?

Thanks a lot for helping. :thumbs_up:
 
Level 29
Joined
Sep 26, 2009
Messages
2,594
Well, I think you should either stick to arrays or hashtables, instead of mixing both.
You store unit's location and the unit itself in arrays, but you also store the unit itself and its facing angle in hashtable.
At the very least you redundantly store unit twice (once in array, second time in hashtable).

What I think you should do is detect when unit dies and store its unit-type so that you know what type of unit to re-create. Because right now I do think you may experience issues with units being disposed before game time hits 6AM. Or at least you should test this out.
  • some units do not decay (i.e. Banshee)
  • default decay time for units is 180 seconds, after which they disappear
If you kill unit at 06:05 in the morning, will it properly revive at 06:00 next day or will it fail because the unit completely decayed?

---
As for the variable array size: As I wrote earlier, setting array size to 1000 does nothing, it just bloats your code. Array size means how many items in the array are initialized with values, but the maximum size of the array is always static. I think it used to be 65 535 (2^16) but since Reforged it is actually bigger.

As for what I mean by array being initialized: If I have a variable array of type "unit group" and I set size to 1000, this will be the code added to war3map.j script:
JASS:
set i=0
loop
    exitwhen ( i > 1000 )
    set udg_InitializedArray[i]=CreateGroup()
    set i=i + 1
endloop
The code above is executed at map initialization. From it, you can see that I prematurely created 1000 unit groups. However during map play I could still assign a new unit group for example under index 25000 without any issues (except it would be impractical).
 
Level 21
Joined
Mar 16, 2008
Messages
955
If you kill unit at 06:05 in the morning, will it properly revive at 06:00 next day or will it fail because the unit completely decayed?
Let's say it decays. Then it will be considered dead in the unit array. and it will rez a unit of that handle (this is hashtable speak for unit-type is my understanding) at 6 am the next morning regardless of the decay status of the previous unit. Correct me if I'm wrong.

The code above is executed at map initialization. From it, you can see that I prematurely created 1000 unit groups. However during map play I could still assign a new unit group for example under index 25000 without any issues (except it would be impractical).
Well some variable types need to size pre-specified. I can't think of any examples off the top of my head. So I just pre-size all arrays to be safe.

EDIT: I'll just scrap the hashtable. It's too confusing for me.
 
Level 29
Joined
Sep 26, 2009
Messages
2,594
Let's say it decays. Then it will be considered dead in the unit array. and it will rez a unit of that handle (this is hashtable speak for unit-type is my understanding) at 6 am the next morning regardless of the decay status of the previous unit. Correct me if I'm wrong.
handle is not unit type. It is internal identifier of an ingame object. Special effects have handles, unit groups have handles, timers have handles, etc.
I don't know if unit will properly be recreated at 6AM if killed too long ago (i.e. 6:05AM previous day). That's why I wrote you should test it out.

Well some variable types need to size pre-specified. I can't think of any examples off the top of my head. So I just pre-size all arrays to be safe.
Actually, no array needs to have size specified. Specifying size is just a convenient way to pre-initialize a number of items in the array.
The types you may have meant are Unit Groups and Timers, but again, setting their size is just out of convenience, not because it is required.
Taking unit groups as example, you can create a fresh new unit group during game time by using
  • Custom script: set udg_MyUnitGroupArray[1001] = CreateGroup()
This just assigned a fresh new unit group to MyUnitGroupArray under index 1001.
 
Top