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

StarCraft Systems (GUI) - need MUI help!

Status
Not open for further replies.

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
I like the Shield interaction with the auras and other abilities :peasant-ok-hand:

But I've got to be brutally honest, I wouldn't recommend this to anyone in it's current state. The performance is going to be really bad with how it's setup.

Some tips:
1) Creating Unit Groups is expensive, but Adding/Removing single Units to and from a Unit Group is a decent bit better:
  • Events
    • Unit - A unit enters map
  • Conditions
    • has shields ability = true
  • Actions
    • Unit Group - Add (Triggering unit) to ShieldsGroup
  • Events
    • Unit - A unit dies
  • Conditions
    • has shields ability = true
  • Actions
    • Unit Group - Remove (Triggering unit) from ShieldsGroup
2) Using Ability Fields to act as data storage is... unique. Yes, we will call it unique. Lol. But in all honestly, I advise against this but I applaud you for your creativity. I feel like if you can manage to throw together a system using this convoluted method for storing data then a Unit Indexer should be a walk in the park for you (learn it or I'll egg your house!).

3) If Then Else statements have an easy optimization. GOOD:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • 1 Less than or equal to 3
    • Then - Actions
      • Game - Display to (All players) for 30.00 seconds the text: Efficiency!
    • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • 2 Less than or equal to 3
        • Then - Actions
          • Game - Display to (All players) for 30.00 seconds the text: Efficiency!
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • 3 Less than or equal to 3
            • Then - Actions
              • Game - Display to (All players) for 30.00 seconds the text: Efficiency!
            • Else - Actions
BAD:
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • 1 Less than or equal to 3
      • Then - Actions
        • Game - Display to (All players) for 30.00 seconds the text: NOT Efficient!
      • Else - Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • 2 Less than or equal to 3
      • Then - Actions
        • Game - Display to (All players) for 30.00 seconds the text: NOT Efficient!
      • Else - Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • 3 Less than or equal to 3
      • Then - Actions
        • Game - Display to (All players) for 30.00 seconds the text: NOT Efficient!
      • Else - Actions
4) Store things in variables! You're calling a function whenever you reference an Event Response.

But that's just me being a stickler, if you had fun making it and it works then feel free to ignore this :p
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
Hey! Thanks for the sweet replies!
1) Creating Unit Groups is expensive, but Adding/Removing single Units to and from a Unit Group is a decent bit better:
I did that in the previous version but I remember reading somewhere groups might leak if they are not destroyed each time even when you add/remove units (creates a leak each time?).
So I decided to not do that at all for the unit indexing, hence why I went with the get unit ability reals:
  • Shield On
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Entering unit)) Equal to Footman
          • (Unit-type of (Entering unit)) Equal to Dragonhawk
    • Actions
      • -------- Unit's Ground Texture=Unit's Name --------
      • -------- Damage of Protoss Armour Increase and Decrease=Unit's Default Armour +Upgrade --------
      • -------- Casting Time of Protoss Shields and Armour=Unit's Current/Max Armour Value --------
      • -------- Cooldown of Protoss Shields and Armour=Unit's Max Shield Value --------
      • -------- Custom Value of Unit=Unit's Current Shield Value --------
      • -------- Damage of Protoss Shields and Armour (Dummy Bolean Aura)=Unit's Default Max Shield --------
      • -------- Damage of Protoss Shields and Armour=Unit's Default Max Shield +Upgrade --------
      • -------- Cast Range of Protoss Shields and Armour=Shield FX Offset --------
      • -------- Duration (Normal) of Protoss Shields and Armour=Shield FX Height --------
      • -------- Duration (Hero) of Protoss Shields and Armour=Shield FX Scale --------
      • -------- SHIELDS ON --------
      • -------- Add and Hide Shield Abilities --------
      • Unit - Add Protoss Shields and Armour (Max Value) to (Entering unit)
      • Unit - For (Entering unit), Ability Protoss Shields and Armour (Max Value), Hide ability: True
      • Unit - Add Protoss Shield Value & Armour Increase and Decrease (Dummy Bolean Aura) to (Entering unit)
      • Unit - For (Entering unit), Ability Protoss Shield Value & Armour Increase and Decrease (Dummy Bolean Aura), Hide ability: True
      • Unit - Add Protoss Shield Regeneration & Value Increase and Decrease (Dummy Bolean Ability) to (Entering unit)
      • Unit - For (Entering unit), Ability Protoss Shield Regeneration & Value Increase and Decrease (Dummy Bolean Ability), Hide ability: True
      • Unit - Add Protoss Shield Armour Increase and Decrease (Dummy Bolean Ability) to (Entering unit)
      • Unit - For (Entering unit), Ability Protoss Shield Armour Increase and Decrease (Dummy Bolean Ability), Hide ability: True
      • -------- Zealot Shield - Max Value (+shield value upgrade) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Entering unit)) Equal to Footman
        • Then - Actions
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shield Value & Armour Increase and Decrease (Dummy Bolean Aura))'s Real Level Field: Damage ('Hbz2') of Level: 0 to 300.00
          • -------- Shield FX Offset, Height and Scale --------
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Cast Range ('aran') of Level: 0 to 50.00
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Duration - Normal ('adur') of Level: 0 to 50.00
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Duration - Hero ('ahdu') of Level: 0 to 0.50
        • Else - Actions
      • -------- Scout Shield - Max Value (+shield value upgrade) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Entering unit)) Equal to Dragonhawk
        • Then - Actions
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shield Value & Armour Increase and Decrease (Dummy Bolean Aura))'s Real Level Field: Damage ('Hbz2') of Level: 0 to 400.00
          • -------- Shield FX Offset, Height and Scale --------
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Cast Range ('aran') of Level: 0 to 75.00
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Duration - Normal ('adur') of Level: 0 to 50.00
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Duration - Hero ('ahdu') of Level: 0 to 1.00
        • Else - Actions
      • -------- Tassadar Shield - Max Value (+shield value upgrade) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Entering unit)) Equal to Far Seer
        • Then - Actions
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shield Value & Armour Increase and Decrease (Dummy Bolean Aura))'s Real Level Field: Damage ('Hbz2') of Level: 0 to 600.00
          • -------- Shield FX Offset, Height and Scale --------
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Cast Range ('aran') of Level: 0 to 75.00
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Duration - Normal ('adur') of Level: 0 to 50.00
          • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Duration - Hero ('ahdu') of Level: 0 to 1.00
        • Else - Actions
      • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Damage ('Hbz2') of Level: 0 to ((Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shield Value & Armour Increase and Decrease (Dummy Bolean Aura))'s Real Level Field Damage ('Hbz2'), of Level: 0) + (((Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss
      • Unit - For Unit (Entering unit), Set cooldown of ability Protoss Shields and Armour (Max Value), Level: 0 to (Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Damage ('Hbz2'), of Level: 0)
      • -------- Shield Armour (+upgrade/research bonus) and Unit Name --------
      • Unit - Set Unit: (Entering unit)'s String Field: Ground Texture ('uubs') to Value: (Name of (Entering unit))
      • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shield Armour Increase and Decrease (Dummy Bolean Ability))'s Real Level Field: Damage ('Hbz2') of Level: 0 to (5.00 x (Real((Current research level of Protoss Shields Armour Upgrade for (Owner of (Entering unit))))))
      • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Casting Time ('acas') of Level: 0 to (Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shield Armour Increase and Decrease (Dummy Bolean Ability))'s Real Level Field Damage ('Hbz2'), of Level: 0)
      • Unit - Set the custom value of (Entering unit) to (Integer((Ability Cooldown of (Entering unit) for ability Protoss Shields and Armour (Max Value), Level: 0.)))
      • Unit - Set Name of (Entering unit) to ((Unit: (Entering unit)'s String Field: Ground Texture ('uubs')) + ( ( + ((String((Integer((Ability Cooldown of (Entering unit) for ability Protoss Shields and Armour (Max Value), Level: 0.))))) + (/ + ((String((Integer((Ability Cooldown of (Entering unit) fo
Sorry for the incompleteness of some triggers. They are so long that the editor doesn't know how to copy them properly.
Or wait, are you saying I should just use one or few unit groups to add units and remove from even for the periodic event triggers instead of creating them each time and destroying them?
I feel like if you can manage to throw together a system using this convoluted method for storing data then a Unit Indexer should be a walk in the park for you (learn it or I'll egg your house!).
Ha-ha. Didn't that require loops and arrays? I kind of want to stay away from that for now :D
3) If Then Else statements have an easy optimization. GOOD:
Oh, I had no idea it will work the same way as if putting them one after the other as in the BAD example.
4) Store things in variables! You're calling a function whenever you reference an Event Response.
Wouldn't that need arrays and loops again 😅? Are you referring to the calls for location and group destruction? Is that slowing the game or something?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
1) No, a memory leak is when you lose reference to something and as a result can't get rid of it. If you want to keep something, like a Unit Group, for the entire game then that's perfectly fine and actually recommended. I should add that it depends on what that something is as well. Basic things like Integers, Reals, Booleans, Strings won't leak in GUI. You need to worry about Points, Unit Groups, Player Groups, and Special Effects.

An example, let's say you had a file on your computer's hard drive that was permanently "hidden" so you couldn't delete it but it was still taking up space. That would be a memory leak. But if you had access to that file, say it was sitting on your desktop, then at any time you can delete it and recycle it so it's NOT a memory leak. It's actually smart to store things in memory. That's why most systems have an Initialization/Setup trigger which create and store a bunch of data. At the exact moment that the data is created there's a performance hit, but after that initial hiccup you will see huge performance improvements because you can now access that data whenever you want instead of creating/destroying it over and over again.

2) You're already using Loops, for example when you do this:
  • Unit Group - Pick every unit in ShieldGroup and do (Actions)
    • Loop - Actions
^ That's a loop, easy as pie. Arrays are extremely simple as well, it's a way to use one variable to store a lot of data at once. The way you're using Custom Value/Ability Fields is basically the same way you would use an Array.

For example, this trigger can be greatly improved using an Array:
  • -------- let's track Player 1's heroes --------
  • Set Variable Player1_Hero1 = Paladin
  • Set Variable Player1_Hero2 = Archmage
  • Set Variable Player1_Hero3 = Mountain King
  • -------- let's track Player 2's heroes --------
  • Set Variable Player2_Hero1 = Blademaster
  • Set Variable Player2_Hero2 = Far Seer
  • Set Variable Player2_Hero3 = Tauren Chieftain
In our map, each Player controls 3 Heroes at a time, and we track those Heroes using the above Unit variables. Why is this bad design? 1) It creates a lot of variables and bloats our map. 2) It is very difficult to work with since we now have 3 variables per player to deal with.

How does an Array help fix this? Simple, we can combine all 3 of those Unit variables into one single Unit Array variable:
  • -------- let's track Player 1's heroes the smart way --------
  • Set Variable Player1_Hero[1] = Paladin
  • Set Variable Player1_Hero[2] = Archmage
  • Set Variable Player1_Hero[3] = Mountain King
  • -------- let's track Player 2's heroes the smart way --------
  • Set Variable Player2_Hero[1] = Blademaster
  • Set Variable Player2_Hero[2] = Far Seer
  • Set Variable Player2_Hero[3] = Tauren Chieftain
We've now reduced our variable usage from 6 to 2 and end up with the same exact result as well as the ability to give our Players a near infinite number of Heroes. This is smart and dynamic and opens up the door to many neat tricks that an [index] allows for. For example, let's kill a random hero that belongs to Player 1:
  • Unit - Kill Player1_Hero[Random integer number between 1 and 3]
Then by using a Unit Indexer we can push the power of our Arrays even further because we can now use the [Index] to represent an individual Unit.

3) Regarding the If Then Else statements, sorry for not going into detail. Here's a better example.

The trigger below is GOOD because we're asking two questions, is our Dying unit a Footman or a Rifleman, but we only ask the Rifleman question IF the unit wasn't already confirmed to be a Footman. Therefore, whenever a Footman dies, the bottom half of the trigger doesn't happen. There's no reason to ask a silly question like "Is it a Rifleman?" when you've already confirmed that it's a Footman. The less your trigger has to do the more efficient it will be:
  • Events
    • Unit - A unit dies
  • Conditions
  • Actions
  • Set Variable DyingUnitType = (Unit-type of (Dying unit))
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • DyingUnitType Equal to Footman
    • Then - Actions
      • Game - Display to (All players) for 30.00 seconds the text: Efficiency!
    • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DyingUnitType Equal to Rifleman
        • Then - Actions
          • Game - Display to (All players) for 30.00 seconds the text: Efficiency!
        • Else - Actions
This other trigger is BAD because we're asking both questions, was it a Footman or a Rifleman, even if we've already confirmed that it was a Footman. That's pretty dumb, a unit can't be two different Unit-Types at once, so doing it like this is a waste:
  • Events
    • Unit - A unit dies
  • Conditions
  • Actions
  • Set Variable DyingUnitType = (Unit-type of (Dying unit))
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • DyingUnitType Equal to Footman
      • Then - Actions
        • Game - Display to (All players) for 30.00 seconds the text: NOT Efficient!
      • Else - Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • DyingUnitType Equal to Rifleman
      • Then - Actions
        • Game - Display to (All players) for 30.00 seconds the text: NOT Efficient!
      • Else - Actions

4) I was referring to the idea of storing Event Responses in variables when able. For example:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • Ability equal to blah blah blah
  • Actions
    • Set Variable Caster = (Casting unit)
    • Unit - Set life of Caster to to (life of Caster + 100.00)
    • Unit - Create 1 Footman for (Owner of Caster) at SomePoint...
    • Unit - Add Critical Strike to Caster
In the above trigger we're referencing Caster 5 times in total. This is more efficient than referencing the Event Response (Casting unit) 5 times, because Event Responses are a slower operation. Referencing a variable is always faster. It can also make for cleaner and less buggy triggers.

However, if your trigger has a Wait action then sometimes it's better to use the Event Response since most of those will be tracked safely over time. Or, you can use a technique called shadowing to turn your Variable into a sort of hybrid Local/Global variable temporarily which resolves this issue (most of the time).
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
This other trigger is BAD because we're asking both questions, was it a Footman or a Rifleman, even if we've already confirmed that it was a Footman. That's pretty dumb, a unit can't be two different Unit-Types at once, so doing it like this is a waste:
Sure but if for instance more buffs are active at the same time and they all need to be checked, the first being active will lead to the subsequent not to be checked because the Else doesn't run anymore.
^ That's a loop, easy as pie. Arrays are extremely simple as well, it's a way to use one variable to store a lot of data at once. The way you're using Custom Value/Ability Fields is basically the same way you would use an Array.
Alright, so I'm using that one loop :D
Yeah, I tried finding a substitute for arrays. The system works for all units and players at the same time.
1) No, a memory leak is when you lose reference to something and as a result can't get rid of it. If you want to keep something, like a Unit Group, for the entire game then that's perfectly fine and actually recommended. I should add that it depends on what that something is as well. Basic things like Integers, Reals, Booleans, Strings won't leak in GUI. You need to worry about Points, Unit Groups, Player Groups, and Special Effects.
Would having lots of units in the same unit group at a time not slow down the game in comparison with a few unit groups that are created and destroyed designed for less units at a time in them (e.g. units with a certain buff)?
Would picking the units in an already made group which you add and remove units to leak if the group is not destroyed? If it is destroyed you lose all the group and its units. So I guess array loops are the workaround for less or no unit groups.
In the above trigger we're referencing Caster 5 times in total. This is more efficient than referencing the Event Response (Casting unit) 5 times, because Event Responses are a slower operation. Referencing a variable is always faster. It can also make for cleaner and less buggy triggers.
Is this also useful for Picked Unit?

Thanks again, Uncle!
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
1) Of course, if things are mutually exclusive then you would separate them. You only use the ELSE when it applies, like in my example.

2) The substitute certainly works but it's very poor on performance compared to my method.

3) Permanently storing things in memory is not going to slow the game down. This is the strategy all games use to keep the game performing well. Trust me, if you can manage a permanent Unit Group instead of using a temporary one (set/use/destroy) then it's ALWAYS the better option.

Constantly doing resource intensive tasks like creating a Unit Group, checking over every single unit in the map to see if it meets your Conditions, adding those eligible units to the Unit Group, and then proceeding to Destroy the Unit Group is going to slow the game down.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
3) Storing things in memory is not going to slow the game down. This is the strategy all games use to keep the game performing well. Trust me, if you can manage a permanent Unit Group instead of using a temporary one (set/use/destroy) then it's ALWAYS the better option.

Constantly doing resource intensive tasks like creating a Unit Group, checking over every single unit in the map to see if it meets your Conditions, adding those eligible units to the Unit Group, and then proceeding to the Destroy the Unit Group is going to slow the game down.
Neat. Only if I could figure out a way to use one unit group since I use periodicals and have no idea how to reference the units in that one group in these triggers.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Neat. Only if I could figure out a way to use one unit group since I use periodicals and have no idea how to reference the units in that one group in these triggers.
A unit enters map -> It has shields -> Add it to ShieldsGroup (a global variable you've created)
A unit dies -> It has shields -> Remove it from ShieldsGroup

You don't have to Set ShieldsGroup EVER. The Unit Group exists just by creating the variable for it.

You can then reference it like you'd reference any global variable:
  • Unit Group - Pick every unit in ShieldsGroup
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
I know that but I thought picking the group leaks if not destroyed.
No, picking through a Unit Group does not leak.

Memory leaks have to do with objects that are being stored in memory. A Unit Group is an object, a Point is an object, a Player Group, a Special Effect, etc... These objects are tracked using variables:
  • Set Variable TempGroup = (Units in playable map area matching...)
^ This has done two things.
1) Created a new Unit Group object which contains any Units matching the desired conditions.
2) Told the TempGroup variable to track that newly created Unit Group object.

TempGroup will continue to track the Unit Group object for the rest of the game until we either Set TempGroup again (creating and tracking a new object) or Destroy the object it's currently tracking (Custom script). But it's perfectly fine to leave it be and let it exist, in fact that's what I'm suggesting you do for ShieldGroup.

Then if your variable stops tracking the object (and the object wasn't destroyed/removed) you will have created a memory leak. That's why you need to Destroy/Remove said object BEFORE telling it's variable to track something else. Also, note that Destroy/Remove do essentially the same thing in this case.

So the ShieldGroup variable will always track the same Unit Group object and therefore the memory leak issue will never apply to it.

Also, note how I'm suggesting that you never Set ShieldGroup in the first place. That's because when you create the variable in the variable editor the game automatically creates and begins tracking a new Unit Group object for you. That's why you're able to Add units to it without ever Setting it.

I go into greater detail about leaks here:

Also, my explanation is geared towards GUI users and may not fully apply if you begin venturing into Jass/Lua territory.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
No, picking through a Unit Group does not leak.
Thank you. Tweaked the triggers a little.
Sadly the regeneration one still makes the game lag when many shield units are on the map. Tried playing with other real fields and the same happens. Until I find a workaround, dynamic indexing would be the best solution but I'm too tired to redo the whole thing.
Also, don't have an idea for the cast abilities group since it requires a new zone on each cast so there's this group being created and destroyed each time.
 

Attachments

  • StarCraftLikeSystems (UncleAdvice).w3m
    88.5 KB · Views: 1

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Thank you. Tweaked the triggers a little.
Sadly the regeneration one still makes the game lag when many shield units are on the map. Tried playing with other real fields and the same happens. Until I find a workaround, dynamic indexing would be the best solution but I'm too tired to redo the whole thing.
Also, don't have an idea for the cast abilities group so there's that group being created and destroyed each time.
Dynamic indexing doesn't really make sense to me here.

You can keep most of what you have already. You just need to transition from using Custom Value/Ability Fields to Array variables of the same type. So if you were using Cast Range to represent a unit's current shields, you could instead transition that into a Real Array variable called CurrentShields[] or whatever. Then you can use the Unit Indexing method to take advantage of that Array.
  • Unit Group - Pick every unit in ShieldGroup and do (Actions)
    • Loop - Actions
      • Set Variable CurrentShields[Custom value of (Picked unit)] = 100.00
Alternatively, a Hashtable can store the data to the units, but I personally cannot stand working with these. The Unit Indexer method is so clean and easy.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Oh, I thought that's what you call the array and loop system thingy.
Yeah, hash tables are another level of horror to me.
In my map I take advantage of Unit Groups + Unit Indexing. It's extremely similar to what you're doing now.

Dynamic indexing is when you use Arrays + For Loops to manage your data as well as manage the Index stuff yourself. This method is really useful in cases where you want your effects to stack.

A good use case for this method:
A spell that applies a 10 second damage over time effect to a unit and only has a 1 second cooldown. But instead of overwriting the previous 10 second DoT effect when you cast it again on the same target, it applies an entirely new instance of the spell and the unit will now take damage from both instances.

Shadow Demon's shadow poison come to mind if you're familiar with DotA 2.

Most "over time" spells in Warcraft 3 use a non-Dynamic Indexing approach. That is, Buffs overwrite one another if they have the same buff id. For example, if you cast Acid Bomb on a group of enemies, then your Ally casts it on that same group 1 second later, your allies Acid Bomb will overwrite your own Acid Bomb. This is for obvious balancing reasons and I think the devs didn't feel like dealing with the headache of stacking effects (although, there is the Firelord's Incinerate ability). This type of effect can be achieved easily with the Unit Indexer method.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
OK, so I did this but I have no idea how to reference these in the Unit Group in the other triggers:
  • Shield On
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Entering unit)) Equal to Footman
          • (Unit-type of (Entering unit)) Equal to Dragonhawk
    • Actions
      • Set VariableSet ShieldIndex = (ShieldIndex + 1)
      • Set VariableSet ShieldUnitVar[ShieldIndex] = (Entering unit)
      • -------- SHIELDS ON --------
      • -------- Add to Main Group --------
      • Unit Group - Add ShieldUnitVar[ShieldIndex] to ShieldGroup
      • -------- Add and Hide Shield Abilities --------
      • Unit - Add Protoss Shields and Armour (Max Value) to ShieldUnitVar[ShieldIndex]
      • Unit - For ShieldUnitVar[ShieldIndex], Ability Protoss Shields and Armour (Max Value), Hide ability: True
      • -------- Zealot Shield - Max Value (+shield value upgrade) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of ShieldUnitVar[ShieldIndex]) Equal to Footman
        • Then - Actions
          • Set VariableSet ShieldValueDefaultMax[ShieldIndex] = 300.00
          • -------- Shield FX Offset, Height and Scale --------
          • Set VariableSet ShieldFXOffset[ShieldIndex] = 50.00
          • Set VariableSet ShieldFXHeight[ShieldIndex] = 50.00
          • Set VariableSet ShieldFXScale[ShieldIndex] = 0.50
        • Else - Actions
          • -------- Scout Shield - Max Value (+shield value upgrade) --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of ShieldUnitVar[ShieldIndex]) Equal to Dragonhawk
            • Then - Actions
              • Set VariableSet ShieldValueDefaultMax[ShieldIndex] = 400.00
              • -------- Shield FX Offset, Height and Scale --------
              • Set VariableSet ShieldFXOffset[ShieldIndex] = 75.00
              • Set VariableSet ShieldFXHeight[ShieldIndex] = 50.00
              • Set VariableSet ShieldFXScale[ShieldIndex] = 1.00
            • Else - Actions
              • -------- Tassadar Shield - Max Value (+shield value upgrade) --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of ShieldUnitVar[ShieldIndex]) Equal to Far Seer
                • Then - Actions
                  • Set VariableSet ShieldValueDefaultMax[ShieldIndex] = 600.00
                  • -------- Shield FX Offset, Height and Scale --------
                  • Set VariableSet ShieldFXOffset[ShieldIndex] = 75.00
                  • Set VariableSet ShieldFXHeight[ShieldIndex] = 50.00
                  • Set VariableSet ShieldFXScale[ShieldIndex] = 1.00
                • Else - Actions
      • Set VariableSet ShieldValueDefaultMaxNUpg[ShieldIndex] = (ShieldValueDefaultMax[ShieldIndex] + ((ShieldValueDefaultMax[ShieldIndex] / 100.00) x (10.00 x (Real((Current research level of Protoss Shields Value Upgrade for (Owner of ShieldUnitVar[ShieldIndex])))))))
      • Set VariableSet ShieldValueMax[ShieldIndex] = ShieldValueDefaultMaxNUpg[ShieldIndex]
      • -------- Shield Armour (+upgrade/research bonus) and Unit Name --------
      • Set VariableSet ShieldArmourDefault[ShieldIndex] = (5.00 x (Real((Current research level of Protoss Shields Armour Upgrade for (Owner of ShieldUnitVar[ShieldIndex])))))
      • Set VariableSet ShieldArmourMax[ShieldIndex] = ShieldArmourDefault[ShieldIndex]
      • Set VariableSet ShieldValueCurrent[ShieldIndex] = ShieldValueMax[ShieldIndex]
      • Set VariableSet ShieldUnitNameStr[ShieldIndex] = (Name of ShieldUnitVar[ShieldIndex])
      • Unit - Set Name of ShieldUnitVar[ShieldIndex] to (ShieldUnitNameStr[ShieldIndex] + ( ( + ((String((Integer(ShieldValueCurrent[ShieldIndex])))) + (/ + ((String((Integer(ShieldValueMax[ShieldIndex])))) + ( + + ((String((Integer(ShieldArmourMax[ShieldIndex])))) + ))))))))
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
-------> U N I T - I N D E X E R <--------

Dynamic indexing doesn't make sense here.

Luckily, it's a couple of simple changes to adjust that trigger to work with the Unit Indexer:
  • Set VariableSet ShieldUnitVar = (Entering unit)
  • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
Then in the Unit Group and anywhere else that you adjust a Unit's shield arrays it would look like this:
  • Unit Group - Pick every unit in ShieldGroup and do (Actions)
    • Loop - Actions
      • Set VariableSet ShieldUnitVar = (Picked unit)
      • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
      • -------- You can now reference ALL of the (Picked unit)'s data by simply putting ShieldIndex into the [Index] of any Array Variable --------
      • Set VariableSet ShieldValueCurrent[ShieldIndex] = (ShieldValueCurrent[ShieldIndex] + 100.00)
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
Dynamic indexing doesn't make sense here.
I wrote to you sometime earlier that I'm not too bright :D

Thanks a lot for the constant help!

So, the game automatically sets an unique custom value for each unit with no possibility of the values matching?

Then, I have to convert all triggers to something like the second you wrote, even this one?
  • Shield Units Exclude on Death
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Dying unit)) Equal to 1
    • Actions
      • Set VariableSet DyingUnitVar = (Dying unit)
      • Unit Group - Remove DyingUnitVar from ShieldGroup.
Also, trying to figure out condition use for referenced units:
  • Shield PreDamage
    • Events
      • Unit - A unit About to take damage
    • Conditions
      • (Damage taken) Less than or equal to (Real((Custom value of (Damage Target))))
      • (Level of Protoss Shields and Armour (Max Value) for (Damage Target)) Equal to 1
    • Actions
      • Event Response - Set Damage Type of Unit Damaged Event to Magic
Basically, I should put the conditions after picking the units like in your example? Don't mind the first condition, it's outdated.
Like this?
  • Shield PreDamage
    • Events
      • Unit - A unit About to take damage
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Damage Target)) Equal to 1
    • Actions
      • Unit Group - Pick every unit in ShieldGroup and do (Actions)
        • Loop - Actions
          • Set VariableSet ShieldUnitVar = (Picked unit)
          • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Damage taken) Less than or equal to ShieldValueCurrent[ShieldIndex]
            • Then - Actions
              • Event Response - Set Damage Type of Unit Damaged Event to Magic
            • Else - Actions
Shouldn't I somehow reference the Damage Target with the index?
Seems to act upon all of them. Tested with a game text message and when hitting one Footman, I get messages equal to how many Footmen are trained.

Could I use the same unit group somehow for the shield abilities or a new one without having to create and recreate it each time or is it OK the way it is?
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
So, the game automatically sets an unique custom value for each unit with no possibility of the values matching?
Correct. So if your map was empty of units and then you were to create 3 Footman, the system would assign the first footman a custom value of 1, the second footman a custom value of 2, and the third footman a custom value of 3. It's really simple when you think about it, it's basically just numbering your units.

Then if footman #1 were to die, his custom value (1) would become available again. This means that if you created a 4th unit it could either be given a custom value of 4 OR a custom value of 1 since that's now available again. It does this "recycling" of unused custom values in order to prevent the Custom Value from ever going over 32,768, which is the highest value an array [Index] can go. In other words, by recycling unused custom values it allows the system to support up to 32,768 concurrent units at a time (I highly doubt anyone's map ever comes close to this).


This trigger doesn't reference any Arrays so it can remain exactly the same:
  • Shield Units Exclude on Death
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Dying unit)) Equal to 1
    • Actions
      • Set VariableSet DyingUnitVar = (Dying unit)
      • Unit Group - Remove DyingUnitVar from ShieldGroup.
There's no reason you can't use ShieldGroup or check if a Unit has an ability.


This trigger when modified to use the Unit Indexing approach would look like this:
  • Shield PreDamage
    • Events
      • Unit - A unit About to take damage
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Damage Target)) Equal to 1
      • (Damage taken) Less than or equal to ShieldValueCurrent[Custom value of (Damage Target)]
    • Actions
      • Event Response - Set Damage Type of Unit Damaged Event to Magic

I'm not too sure what you were trying to achieve here:
  • Shield PreDamage
    • Events
      • Unit - A unit About to take damage
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Damage Target)) Equal to 1
    • Actions
      • Unit Group - Pick every unit in ShieldGroup and do (Actions)
        • Loop - Actions
          • Set VariableSet ShieldUnitVar = (Picked unit)
          • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Damage taken) Less than or equal to ShieldValueCurrent[ShieldIndex]
            • Then - Actions
              • Event Response - Set Damage Type of Unit Damaged Event to Magic
            • Else - Actions
Why are you using a Pick Every Unit action here? What were you trying to do?

Like I said before, you can use ShieldGroup throughout all of your triggers without ever needing to Set it. Simply Add/Remove units as they enter the map and die so that it only ever contains living units with the Shield ability.


Here's an example of how you could use ShieldGroup. Let's regenerate 1 Shield every second:
  • Shield PreDamage
    • Events
      • Time - Every 1.00 second
    • Conditions
    • Actions
      • Unit Group - Pick every unit in ShieldGroup and do (Actions)
        • Loop - Actions
          • Set VariableSet ShieldUnitVar = (Picked unit)
          • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
          • Set VariableSet ShieldValueCurrent[ShieldIndex] = (ShieldValueCurrent[ShieldIndex] + 1.00)
          • -------- then proceed to update their name and whatever else you may want to do --------
Note that ShieldUnitVar and ShieldIndex aren't actually required, they're just there to make your life easier and make the triggers more efficient and readable.

You could very well just do this:
  • Set VariableSet ShieldValueCurrent[(Custom value of (Picked unit))] = (ShieldValueCurrent[(Custom value of (Picked unit))] + 1.00)
But I don't recommend it.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
Why are you using a Pick Every Unit action here? What were you trying to do?
It was just an example if for instance I'd want to make the shield FX to appear on each unit. I'm having trouble understanding how to reference the units in other triggers with or without using unit groups.

For instance, how does
  • (Damage taken) Less than or equal to ShieldValueCurrent[Custom value of (Damage Target)]
know how to be reference to
  • Shield On
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Entering unit)) Equal to Footman
          • (Unit-type of (Entering unit)) Equal to Dragonhawk
    • Actions
      • Set VariableSet ShieldUnitVar = (Entering unit)
      • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
      • -------- SHIELDS ON --------
      • -------- Add to Main Group --------
      • Unit Group - Add ShieldUnitVar to ShieldGroup
      • -------- Add and Hide Shield Abilities --------
      • Unit - Add Protoss Shields and Armour (Max Value) to ShieldUnitVar
      • Unit - For ShieldUnitVar, Ability Protoss Shields and Armour (Max Value), Hide ability: True
      • -------- Zealot Shield - Max Value (+shield value upgrade) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of ShieldUnitVar) Equal to Footman
        • Then - Actions
          • Set VariableSet ShieldValueDefaultMax[ShieldIndex] = 300.00
          • -------- Shield FX Offset, Height and Scale --------
          • Set VariableSet ShieldFXOffset[ShieldIndex] = 50.00
          • Set VariableSet ShieldFXHeight[ShieldIndex] = 50.00
          • Set VariableSet ShieldFXScale[ShieldIndex] = 0.50
        • Else - Actions
          • -------- Scout Shield - Max Value (+shield value upgrade) --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of ShieldUnitVar) Equal to Dragonhawk
            • Then - Actions
              • Set VariableSet ShieldValueDefaultMax[ShieldIndex] = 400.00
              • -------- Shield FX Offset, Height and Scale --------
              • Set VariableSet ShieldFXOffset[ShieldIndex] = 75.00
              • Set VariableSet ShieldFXHeight[ShieldIndex] = 50.00
              • Set VariableSet ShieldFXScale[ShieldIndex] = 1.00
            • Else - Actions
              • -------- Tassadar Shield - Max Value (+shield value upgrade) --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of ShieldUnitVar) Equal to Far Seer
                • Then - Actions
                  • Set VariableSet ShieldValueDefaultMax[ShieldIndex] = 600.00
                  • -------- Shield FX Offset, Height and Scale --------
                  • Set VariableSet ShieldFXOffset[ShieldIndex] = 75.00
                  • Set VariableSet ShieldFXHeight[ShieldIndex] = 50.00
                  • Set VariableSet ShieldFXScale[ShieldIndex] = 1.00
                • Else - Actions
      • Set VariableSet ShieldValueDefaultMaxNUpg[ShieldIndex] = (ShieldValueDefaultMax[ShieldIndex] + ((ShieldValueDefaultMax[ShieldIndex] / 100.00) x (10.00 x (Real((Current research level of Protoss Shields Value Upgrade for (Owner of ShieldUnitVar)))))))
      • Set VariableSet ShieldValueMax[ShieldIndex] = ShieldValueDefaultMaxNUpg[ShieldIndex]
      • -------- Shield Armour (+upgrade/research bonus) and Unit Name --------
      • Set VariableSet ShieldArmourDefault[ShieldIndex] = (5.00 x (Real((Current research level of Protoss Shields Armour Upgrade for (Owner of ShieldUnitVar)))))
      • Set VariableSet ShieldArmourMax[ShieldIndex] = ShieldArmourDefault[ShieldIndex]
      • Set VariableSet ShieldValueCurrent[ShieldIndex] = ShieldValueMax[ShieldIndex]
      • Set VariableSet ShieldUnitNameStr[ShieldIndex] = (Name of ShieldUnitVar)
      • Unit - Set Name of ShieldUnitVar to (ShieldUnitNameStr[ShieldIndex] + ( ( + ((String((Integer(ShieldValueCurrent[ShieldIndex])))) + (/ + ((String((Integer(ShieldValueMax[ShieldIndex])))) + ( + + ((String((Integer(ShieldArmourMax[ShieldIndex])))) + ))))))))
?
Here's an example of how you could use ShieldGroup. Let's regenerate 1 Shield every second:
I understand how it works with periodicals. That seems easy but what about with this?
  • Shield Damage
    • Events
      • Unit - A unit Takes damage
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Damage Target)) Equal to 1
    • Actions
      • Set VariableSet DamageTargetVar = (Damage Target)
      • Set VariableSet DamageSourceVar = (Damage source)
      • -------- SHIELD DAMAGED --------
      • -------- Damage Lesser than Shield Armour --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Less than or equal to (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Casting Time ('acas'), of Level: 0)
          • (Custom value of DamageTargetVar) Greater than or equal to (Integer((Damage taken)))
        • Then - Actions
          • Event Response - Set Damage of Unit Damaged Event to 0.00
        • Else - Actions
      • -------- Damage Greater than Shield Armour --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Greater than (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Casting Time ('acas'), of Level: 0)
          • (Custom value of DamageTargetVar) Greater than or equal to (Integer((Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Casting Time ('acas'), of Level: 0)))
        • Then - Actions
          • Event Response - Set Damage of Unit Damaged Event to ((Damage taken) - (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Casting Time ('acas'), of Level: 0))
        • Else - Actions
      • -------- Damage Greater than Shield Value --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Greater than (Real((Custom value of DamageTargetVar)))
          • (Custom value of DamageTargetVar) Greater than or equal to 0
        • Then - Actions
          • Event Response - Set Damage of Unit Damaged Event to ((Damage taken) - (Real((Custom value of DamageTargetVar))))
          • Unit - Set the custom value of DamageTargetVar to 0
        • Else - Actions
      • -------- Damage Lesser than Shield Value --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Less than or equal to (Real((Custom value of DamageTargetVar)))
          • (Custom value of DamageTargetVar) Greater than 0
        • Then - Actions
          • Unit - Set the custom value of DamageTargetVar to ((Custom value of DamageTargetVar) - (Integer((Damage taken))))
          • Event Response - Set Damage of Unit Damaged Event to 0.00
        • Else - Actions
      • Unit - Set Name of DamageTargetVar to ((Unit: DamageTargetVar's String Field: Ground Texture ('uubs')) + ( ( + ((String((Custom value of DamageTargetVar))) + (/ + ((String((Integer((Ability Cooldown of DamageTargetVar for ability Protoss Shields and Armour (Max Value), Level: 0.))))) + ( + + ((St
      • -------- Shield FX --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Custom value of DamageTargetVar) Greater than 0
        • Then - Actions
          • Set VariableSet ShieldFXPoint = ((Position of DamageTargetVar) offset by (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Cast Range ('aran'), of Level: 0) towards (Angle from (Position of DamageTargetVar) to (Position
          • Special Effect - Create a special effect at ShieldFXPoint using BoneArmorCasterTC.mdx
          • Set VariableSet ShieldFX01 = (Last created special effect)
          • Special Effect - Create a special effect at ShieldFXPoint using HexagonShield.mdx
          • Set VariableSet ShieldFX02 = (Last created special effect)
          • Special Effect - Set Height of ShieldFX01 to: ((Default flying height of DamageTargetVar) + (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Duration - Normal ('adur'), of Level: 0))
          • Special Effect - Set Height of ShieldFX02 to: ((Default flying height of DamageTargetVar) + (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Duration - Normal ('adur'), of Level: 0))
          • Special Effect - Set Scale of ShieldFX01 to (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Duration - Hero ('ahdu'), of Level: 0)
          • Special Effect - Set Scale of ShieldFX02 to (Ability: (Unit: DamageTargetVar's Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field Duration - Hero ('ahdu'), of Level: 0)
          • Special Effect - Set Yaw of ShieldFX01 to: (Radians((Angle from (Position of DamageTargetVar) to (Position of DamageSourceVar))))
          • Special Effect - Set Yaw of ShieldFX02 to: (Radians((Angle from (Position of DamageTargetVar) to (Position of DamageSourceVar))))
          • Special Effect - Set Alpha of ShieldFX01 to 255
          • Special Effect - Set Alpha of ShieldFX02 to 255
          • Special Effect - Set Color of ShieldFX01 to color of (Player((Random integer number between 1 and 28)))
          • Special Effect - Set Color of ShieldFX02 to color of (Player((Random integer number between 1 and 28)))
          • Special Effect - Set Time Scale of ShieldFX01 to 8.00
          • Special Effect - Set Time Scale of ShieldFX02 to 8.00
          • Special Effect - Set Time of ShieldFX01 to 0.10
          • Special Effect - Set Time of ShieldFX02 to 0.10
          • Special Effect - Destroy ShieldFX01
          • Special Effect - Destroy ShieldFX02
          • Custom script: call RemoveLocation (udg_ShieldFXPoint)
        • Else - Actions
Since I set the FX offset, scale and height in the first trigger where the units get indexed, should I redo it instead in the Damaged shield/units trigger? Otherwise how do I reference the exact unit (type) from the first trigger?

My gripe is, make sole unit event triggers run without groups and those which loop the group with the pick unit stuff?
For instance, if unit group picking acts like loops, then I'd still have to create and recreate the second group for the AoE abilities, right since it also needs a new region for the casting point/area, right?

Sorry, I'm bugging you and thanks.



EDIT: I think I might be starting to get the hang of this.

EDIT 2: Something bugs the current shield on hit if you damage a full shield unit after leaving one with 0. The full one becomes 0 and sometimes the 0 one becomes half the shield or so.
  • Shield Damage
    • Events
      • Unit - A unit Takes damage
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Damage Target)) Equal to 1
    • Actions
      • Set VariableSet ShieldUnitVar = (Damage Target)
      • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
      • Set VariableSet DamageSourceVar = (Damage source)
      • -------- SHIELD DAMAGED --------
      • -------- Damage Lesser than Shield Armour --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Less than or equal to ShieldArmourMax[ShieldIndex]
          • ShieldValueCurrent[ShieldIndex] Greater than or equal to (Damage taken)
        • Then - Actions
          • Game - Display to (All players) the text: Damage Lesser than ...
          • Event Response - Set Damage of Unit Damaged Event to 0.00
        • Else - Actions
      • -------- Damage Greater than Shield Armour --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Greater than ShieldArmourMax[ShieldIndex]
          • ShieldValueCurrent[ShieldIndex] Greater than or equal to ShieldArmourMax[ShieldIndex]
        • Then - Actions
          • Game - Display to (All players) the text: Damage Greater than...
          • Event Response - Set Damage of Unit Damaged Event to ((Damage taken) - ShieldArmourMax[ShieldIndex])
        • Else - Actions
      • -------- Damage Greater than Shield Value --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Greater than ShieldValueCurrent[ShieldIndex]
          • ShieldValueCurrent[ShieldIndex] Greater than or equal to 0.00
        • Then - Actions
          • Game - Display to (All players) the text: Damage Greater than...
          • Event Response - Set Damage of Unit Damaged Event to ((Damage taken) - ShieldValueCurrent[ShieldIndex])
          • Set VariableSet ShieldValueCurrent[ShieldIndex] = 0.00
        • Else - Actions
      • -------- Damage Lesser than Shield Value --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Less than or equal to ShieldValueCurrent[ShieldIndex]
          • ShieldValueCurrent[ShieldIndex] Greater than 0.00
        • Then - Actions
          • Game - Display to (All players) the text: Damage Lesser than ...
          • Set VariableSet ShieldValueCurrent[ShieldIndex] = (ShieldValueCurrent[ShieldIndex] - (Damage taken))
          • Event Response - Set Damage of Unit Damaged Event to 0.00
        • Else - Actions
      • Unit - Set Name of ShieldUnitVar to (ShieldUnitNameStr[ShieldIndex] + ( ( + ((String((Integer(ShieldValueCurrent[ShieldIndex])))) + (/ + ((String((Integer(ShieldValueMax[ShieldIndex])))) + ( + + ((String((Integer(ShieldArmourMax[ShieldIndex])))) + ))))))))
      • -------- Shield FX --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ShieldValueCurrent[ShieldIndex] Greater than 0.00
        • Then - Actions
          • Set VariableSet ShieldFXPoint = ((Position of ShieldUnitVar) offset by ShieldFXOffset[ShieldIndex] towards (Angle from (Position of ShieldUnitVar) to (Position of DamageSourceVar)) degrees.)
          • Special Effect - Create a special effect at ShieldFXPoint using BoneArmorCasterTC.mdx
          • Set VariableSet ShieldFX01 = (Last created special effect)
          • Special Effect - Create a special effect at ShieldFXPoint using HexagonShield.mdx
          • Set VariableSet ShieldFX02 = (Last created special effect)
          • Special Effect - Set Height of ShieldFX01 to: ((Default flying height of ShieldUnitVar) + ShieldFXHeight[ShieldIndex])
          • Special Effect - Set Height of ShieldFX02 to: ((Default flying height of ShieldUnitVar) + ShieldFXHeight[ShieldIndex])
          • Special Effect - Set Scale of ShieldFX01 to ShieldFXScale[ShieldIndex]
          • Special Effect - Set Scale of ShieldFX02 to ShieldFXScale[ShieldIndex]
          • Special Effect - Set Yaw of ShieldFX01 to: (Radians((Angle from (Position of ShieldUnitVar) to (Position of DamageSourceVar))))
          • Special Effect - Set Yaw of ShieldFX02 to: (Radians((Angle from (Position of ShieldUnitVar) to (Position of DamageSourceVar))))
          • Special Effect - Set Alpha of ShieldFX01 to 255
          • Special Effect - Set Alpha of ShieldFX02 to 255
          • Special Effect - Set Color of ShieldFX01 to color of (Player((Random integer number between 1 and 28)))
          • Special Effect - Set Color of ShieldFX02 to color of (Player((Random integer number between 1 and 28)))
          • Special Effect - Set Time Scale of ShieldFX01 to 8.00
          • Special Effect - Set Time Scale of ShieldFX02 to 8.00
          • Special Effect - Set Time of ShieldFX01 to 0.10
          • Special Effect - Set Time of ShieldFX02 to 0.10
          • Special Effect - Destroy ShieldFX01
          • Special Effect - Destroy ShieldFX02
          • Custom script: call RemoveLocation (udg_ShieldFXPoint)
        • Else - Actions
EDIT 3: OK, so debugged with damage taken variables and it seems the damage is fine but the current shield is put to 0 even if it's full.
For some reason it considers the damage greater than shield.
Funny that the custom value is 0 for every unit for some reason.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
To answer your question "How does this know how to reference this other thing"?

Here's an example:
  • Unit - Create 1 Footman...
  • Set Variable AbilityPower[Custom value of (Last created unit)] = 100.00
Let's say that the Footman had a Custom Value of 1. The actual result would be this:
  • Set Variable AbilityPower[1] = 100.00
Now if you understand Arrays you'll understand that you can use a different [Index] to store more data to that same variable. The [Index] basically represents a unique copy of the variable.

Now remember that each unit has a unique Custom Value thanks to the Unit Indexer. That means that each unit has their own copy of the variable as well.

So as long as you have access to the Footman you will also have access to all of it's Arrays that you've Set using it's Custom Value. These are global variables which means that they will remain active and accessible for the entire game. You can overwrite them, decrease them, increase them, etc...

Here's an Ability Power example if you're familiar with League of Legends:
  • Events
    • Unit - A unit Acquires an item
  • Conditions
    • (Item-type of (Item being manipulated) Equal to Staff of Power
  • Actions
    • Set Variable AbilityPower[Custom value of (Triggering unit)] = (AbilityPower[Custom value of (Triggering unit)] + 50.00)
When a unit acquires the Staff of Power it gains 50.00 Ability Power.

Now let's apply the Ability Power to our Storm Bolt ability:
  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Storm Bolt
  • Actions
    • Unit - Cause (Casting unit) to damage (Target unit of ability being cast) dealing (100.00 + AbilityPower[Custom value of (Casting unit)])...
It's really quite simple, if you have access to a unit then you have access to it's custom value, therefore you have access to ANY and ALL variables that you've Set using that custom value.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Looks like they have the same custom value or you accidentally plugged in the wrong custom value in your Array somewhere.

It's easy to test their Custom Values, just display a text message when you select a unit -> Display Custom value of (Triggering unit).

Also, double check to make sure that you actually have a Unit Indexer in that map.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Lol!

So the transition from your old method to the new Unit Indexing method should be as easy as this:

Take an existing Action:
  • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Cast Range ('aran') of Level: 0 to 75.00
Replace it with an Array that holds the same data (75.00):
  • Set Variable CastRange[(Custom value of (Entering unit))] = 75.00
I'm not sure what Cast Range was supposed to represent so I named the Array CastRange. Obviously you'd want to name it accordingly.

Rinse and repeat.
 

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
I'm not sure what Cast Range was supposed to represent so I named the Array CastRange. Obviously you'd want to name it accordingly.
Cast Range for that ability was for default armour.
What do you think about this method? :xxd:
  • Set VariableSet ShieldUnitVar = (Entering unit)
  • Set VariableSet UnitIndexerInteg = (UnitIndexerInteg + 1)
  • Unit - Set the custom value of ShieldUnitVar to UnitIndexerInteg
  • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
Where UnitIndexerInteg's default value is -30000
1677973637836.png

EDIT: grrrr.... it seems negative values don't work. The name change goes 0 on values.
 

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
Why use that method?
Because I didn't implement Bribe's Unit Indexer because you know me and custom script stuff don't get along too well. Or am I being stupid again :D?
I guess a loop (from 1 to UnitIndexInteg) could be used when a unit is trained/enters the playable map area that checks which of the shield units is dead so it can take its custom value. Then the dead is removed from the game. Also when a unit dies, UnitIndexerInteg could go back to 0 if it reaches the limit.

EDIT: a bit of a sloppy way to do it:
  • Set VariableSet ShieldUnitVar = (Entering unit)
  • -------- Take Dead Unit's Index --------
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • DeadUnitCustomValue Greater than 0
    • Then - Actions
      • Unit - Set the custom value of ShieldUnitVar to DeadUnitCustomValue
      • Set VariableSet DeadUnitCustomValue = 0
    • Else - Actions
      • Set VariableSet UnitIndexerInteg = (UnitIndexerInteg + 1)
      • Unit - Set the custom value of ShieldUnitVar to UnitIndexerInteg
  • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
  • Shield Units Exclude on Death
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Level of Protoss Shields and Armour (Max Value) for (Dying unit)) Equal to 1
    • Actions
      • Set VariableSet ShieldUnitVar = (Dying unit)
      • Unit Group - Remove ShieldUnitVar from ShieldGroup.
      • Set VariableSet DeadUnitCustomValue = (Custom value of ShieldUnitVar)
      • -------- Reset Indexer Value --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UnitIndexerInteg Equal to 32760
        • Then - Actions
          • Set VariableSet UnitIndexerInteg = 0
        • Else - Actions
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
You don't have to do anything weird like use Custom script. You just copy and paste the Unit Indexer trigger into your map and you're good to go.

This is all the system is doing:
A unit enters the map -> the system increases an integer by 1 -> it assigns that integer to the unit's custom value.

A unit leaves the map -> The system stores that unit's custom value in an "unused" list. That custom value can now be assigned to a new unit.

Again:

All you need to do is go over all of your existing Actions where you're storing data:
  • Ability - Set Ability: (Unit: (Entering unit)'s Ability with Ability Code: Protoss Shields and Armour (Max Value))'s Real Level Field: Cast Range ('aran') of Level: 0 to 75.00
And replace them with an Array Variable that holds the same data (75.00 in this case):
  • Set Variable CastRange[(Custom value of (Entering unit))] = 75.00
That's literally all you need to do. Just make sure you actually have a Unit Indexer in your map this time, otherwise the unit's Custom Value will be 0 (the default value for every unit). Do NOT set Custom Value yourself, that's the system's job and you're just wasting your time and energy by doing so.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
Until I make my own indexing recycling, I'll put the map with Bribe's Unit Indexer as per Uncle's advice.
Replaced every ability real as Uncle suggested with variables.
 

Attachments

  • StarCraftLikeSystems (UncleAdvice - Unit Indexer).w3m
    83.1 KB · Views: 2

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Until I make my own indexing recycling, I'll put the map with Bribe's Unit Indexer as per Uncle's advice.
Replaced every ability real as Uncle suggested with variables.
But why would you make your own :p It's a very simple system that does the work for you, there's no way to really make it any better.

Anyway, I looked over your map and it looks pretty good, seems like you adapted everything to use Unit Indexing properly.

There are some issues though:

1) Shield Damage has inefficient usage of If Then Else statements. You ask a bunch of mutually exclusive questions like "is X > Y" then proceed to ask "is X < Y". This is where the ELSE part comes in handy, allowing you to only ask questions if you still haven't found the answer that you were looking for. You're doing this properly in the Shield On trigger.

2) Shield Damage leaks 4 points in the Shield FX section at the bottom. Anywhere you see the words (Position of) you're leaking a Point.

3) Throughout all of the triggers you have unnecessary If Then Else statements with empty conditions:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
    • Then - Actions
      • -------- do stuff --------
    • Else - Actions
I'm guessing you're using these to act like folders?

4) Your Value over Shield Fix seems out of place:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • ShieldValueCurrent[ShieldIndex] Greater than ShieldValueMax[ShieldIndex]
    • Then - Actions
      • Set VariableSet ShieldValueCurrent[ShieldIndex] = ShieldValueMax[ShieldIndex]
    • Else - Actions
Shouldn't this occur AFTER you adjust the Shields of units. By doing it before, the unit could have excess shields for an entire second before it fixes itself.

5) Shield Abilities leaks a Point with it's usage of (Target point of ability being cast). Also, it's suffering from inefficient usage of If Then Else statements (pretty much every trigger is).

6) I'm not too sure I'm following this one:
  • Shield PreDamage
    • Events
      • Unit - A unit About to take damage
    • Conditions
      • (Level of Protoss Shields and Armour (Dummy Ability) for (Damage Target)) Equal to 1
      • (Damage taken) Less than or equal to ShieldValueCurrent[(Custom value of (Damage Target))]
    • Actions
      • Event Response - Set Damage Type of Unit Damaged Event to Magic
Damage dealt to the Shield is changed to Magic damage? Fair enough, but note that this isn't accounting for cases where a Unit takes some damage to their Shield and some damage bypasses it and hits their Life. I understand if you don't care about that as it could just be considered a feature.

7) I imagine you could reduce the number of questions you ask (If Then Else statements) with clever use of Conditions and variables. At the moment there seems to be a good amount of redundancy and unnecessary operations.

I would definitely focus on cleaning up all of the memory leaks first. Then you can worry about everything else I stated, if you care to do so.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
But why would you make your own :p It's a very simple system that does the work for you, there's no way to really make it any better.
Because I like stuff to be as much GUI as possible for us peasants :D Also, I like doing stuff myself :p
1) Shield Damage has inefficient usage of If Then Else statements.
Oops, forgot to tweak that stuff throughout the other triggers.
I'm guessing you're using these to act like folders?
Yes, some and the others, I didn't really want to risk moving stuff improperly since the trigger editor doesn't offer a nice way to do it.
Shouldn't this occur AFTER you adjust the Shields of units. By doing it before, the unit could have excess shields for an entire second before it fixes itself.
If I place it after the +1 stuff, for some reason the shield gets one point above and remains.
Also, it's suffering from inefficient usage of If Then Else statements (pretty much every trigger is).
Yeah, however it doesn't really look nice if the list just goes on and on as the triggers get sent to the right and you can't read them whole unless you move the scrollbar. Tweaked them though.
Damage dealt to the Shield is changed to Magic damage? Fair enough, but note that this isn't accounting for cases where a Unit takes some damage to their Shield and some damage bypasses it and hits their Life. I understand if you don't care about that as it could just be considered a feature.
I chose Magic because it ignores armour so I could use the shield's armour in the calculation. I'd need a formula to check for that post shield damage I guess.
Added this:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Damage taken) Greater than ShieldValueCurrent[ShieldIndex]
    • Then - Actions
    • Else - Actions
      • Event Response - Set Damage Type of Unit Damaged Event to Magic
7) I imagine you could reduce the number of questions you ask (If Then Else statements) with clever use of Conditions and variables. At the moment there seems to be a good amount of redundancy and unnecessary operations.
Not that smart, I'm afraid :D
 

Attachments

  • StarCraftLikeSystems (UncleAdvice - Unit Indexer).w3m
    83.4 KB · Views: 2

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
It is a GUI system, hence why you don't need to use any code to use it and everything is handled using GUI variables. But if you'd like to make your own, go for it, the only challenging part about the system is the recycling. Technically if your map never creates more than 32,768 units then you don't even need recycling since the array limit would never be reached.

Anyway, this doesn't make any sense:
  • Shield PreDamage
    • Events
      • Unit - A unit About to take damage
    • Conditions
      • (Level of Protoss Shields and Armour (Dummy Ability) for (Damage Target)) Equal to 1
      • (Damage taken) Less than or equal to ShieldValueCurrent[(Custom value of (Damage Target))]
    • Actions
      • Set VariableSet ShieldUnitVar = (Damage Target)
      • Set VariableSet ShieldIndex = (Custom value of ShieldUnitVar)
      • -------- Damage Greater than Shield Value? --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Damage taken) Greater than ShieldValueCurrent[ShieldIndex]
        • Then - Actions
        • Else - Actions
          • Event Response - Set Damage Type of Unit Damaged Event to Magic
Why did you add an If Then Else here that's asking the same exact question you just asked in the Conditions. If you wanted to account for what I said about excess damage bypassing the Shield you'd probably need to calculate Armor yourself this way you can split the damage in half.

Looking at Shield Damage, I think the math checks out but you may be making some small mistakes.
  • ShieldValueCurrent[ShieldIndex] Greater than 0.00
Pretty sure that's supposed to be >= not >.

Also, the Shield FX only appears if the Shields are > 0 after taking damage, but that doesn't make much sense since the unit may very well have just blocked some damage with it's shield. Not a major issue of course.

Shield and Shield Armour Upgrade seems to be misusing the If Then Else statements. Remember, you only use ELSE for cases where things are mutually exclusive. A unit can have all of those buffs at the same time so they are NOT mutually exclusive. Your goal is to only ask questions that aren't already answered, for example, if I ask "Is this unit a Footman?" and it turns out to be True then I know that it's not ANY other Unit-Type therefore it'd be a waste of time to ask "Is this unit a Rifleman?" one Action later.

In Shield Auras and Abilities Upgrades you are properly using the If Then Else statements. When a unit learns a skill, it's learning ONE particular skill, therefore only one of those If Then Else statements that check which Skill it was can be True.
 
Last edited:

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,848
Anyway, this doesn't make any sense:
Wow, yeah, I forgot about the first conditions. It was good as it was before, the magic applying only if the damage is less or equal to the shield value.
But yeah, some calculations would still need to be thought of. Not sure it's that important though.
Also, the Shield FX only appears if the Shields are > 0 after taking damage, but that doesn't make much sense since the unit may very well have just blocked some damage with it's shield. Not a major issue of course.
The point is for the shield FX not to appear if the damage is higher than the shield value because then you get the FX even if you are hit when having something like 1 points of shield which is kind of forever due to the shield regeneration being constant once in 1 seconds.
Shield and Shield Armour Upgrade seems to be misusing the If Then Else statements.
Oh darn, didn't pay attention to the buff conditions.

Thanks a lot!
 

Attachments

  • StarCraftLikeSystems (UncleAdvice - Unit Indexer).w3m
    83.1 KB · Views: 4
Status
Not open for further replies.
Top