• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Trigger] Garrison into neutral building and change ownership

Status
Not open for further replies.
Level 2
Joined
Sep 1, 2018
Messages
13
In my map, neutral buildings are taken over by garrisoning a player's unit inside. Emphasis is on commandeering existing structures. Building or repairing will be time and resource-intensive by design.

The solution I've worked out is susceptible to abuse and another avenue is needed.

The Big Picture

Before I explain the issue, let me first share the different neutral buildings and how they will function differently:
  • orc burrow
    • +10 food
    • Cargo Hold ability modified to hold 1 peon-type unit.
      • haven't decided if I want an attack function from this.
    • Creates a farmstead (Undead blight) on which other farms cannot be constructed.
  • watchtower
    • based on orc burrow unit.
    • Cargo Hold ability modified to hold 4 or more ground units (non-peon).
      • attack-speed increases as more units are added (just like orc burrow).
  • lumber mill
    • Cargo Hold ability modified to hold 1 peon-type unit.
  • town hall
    • uncertain how I want to use the Cargo Hold ability and which type of units it will garrison, but this will enable players to harvest gold from a gold mine.

My ad hoc solution

I gave the orc peon an ability based on Purge (orc shaman). When targeting a neutral structure, that neutral structure will cast Devour (kodo) on the casting peon.

Here's that trigger. Note that by "farm" I mean "burrow":

  • Control Farm
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
      • ((Ability being cast) Equal to Control Farm ) and (((Owner of (Target unit of ability being cast)) Equal to Neutral Passive) and ((Unit-type of (Target unit of ability being cast)) Equal to Orc Burrow))
    • Actions
      • Unit - Change ownership of (Target unit of ability being cast) to (Owner of (Casting unit)) and Change color
      • Unit - Order (Target unit of ability being cast) to Orc Kodo Beast - Devour (Casting unit)
So far, so good. But I want the farm to return to neutral when a peon leaves the burrow.

I thought I figured this out with the second trigger:

  • Lose Control of Farm
    • Events
      • Unit - A unit Finishes casting an ability
    • Conditions
      • (Ability being cast) Equal to Stand Down
      • (Unit-type of (Triggering unit)) Equal to Orc Burrow
    • Actions
      • Unit - Change ownership of (Casting unit) to Neutral Passive and Change color
The problem

This works if you select the burrow and click the ability "Stand Down". It doesn't work if you unload the peon by clicking on the portrait. He leaves the burrow and it stays in your ownership.

My assessment

From playing with this and doing a little reading, it seems unlikely that I can do this using straight-forward Events and Conditions. (Update: I was wrong, it is possible using order(unload).)

A Hive member asked about this very thing in March 2010: how can i make a unit garrison inside a neutral building and change its owner

That's where I got the idea to use Devour, because I couldn't get Battle Stations to work. If anyone has ideas to make Battle Stations work without Devour, I'm all ears (think it'd be a bit more elegant, no?).

Others alluded to solutions involving "custom data to control units count (0-4)". That same user made an other post I didn't really understand:

There is Loading Unit pointer and unit custom value.

Custom value should be equal to loaded units count.

+1 per each loaded unit
-1 per each unloaded unit

And no need custom ability for this. Just script. Try in vJass section how to refer to easiest refer to loaded unit.

I'm assuming this guy is right (or close).

Asking for guidance
  • Can someone help me understand the above comment?
  • Will that comment, or any others in that thread, help solve my problem?
  • Is there a simple enough solution that you can modify my triggers to work or write sample code?
  • Do I need to be directed to a particular tutorial in order to help myself?

I've done my best to research this problem further but didn't turn anything up. If anyone is able to find and share a relevant thread, that'd be great.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,010
I'm not sure that it specifically interfaces with the Battle Stations ability, but you can use this system to enable a "unit is unloaded" event. It may fire and give you the right information. If it does, what the user you quoted meant was:
  • Events
    • -------- loaded event --------
    • Game - CargoEvent Becomes Equal to 1.00
  • Conditions
  • Actions
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • Custom value of CargoTransportUnit[UDex] equal to 0 //it had no loaded units before
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Owner of (Triggering Unit)) and change color
    • Set Custom Value of CargoTransportUnit[UDex] to ((Custom value of CargoTransportUnit[UDex]) + 1)
  • -------- --------
  • -------- --------
  • Events
    • -------- unloaded loaded event --------
    • Game - CargoEvent Becomes Equal to 2.00
  • Conditions
  • Actions
    • Set Custom Value of CargoTransportUnit[UDex] to ((Custom value of CargoTransportUnit[UDex]) - 1)
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • Custom value of CargoTransportUnit[UDex] equal to 0 //it's empty now
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Neutral Passive) and change color
 
Level 2
Joined
Sep 1, 2018
Messages
13
Thanks for interpreting that.

The difficulty stems from there being an available loading event:

  • Events
  • Unit - Peon Is loaded into a transport
But no unloading event.

So if I'm understanding properly, if a loading event takes place (above), there is a way to detect the number of units inside of a transport. We need some work-around.

As I'm typing this, suddenly have an idea:

  • Events
  • Unit - Peon Starts the effect of an ability.
I'll put a dummy passive aura on the peon which should only be active when not transported (right)? That should allow me to make use of the code above, then.

Have to go right now, will need to test this later.
 
Level 13
Joined
Jul 15, 2007
Messages
763
Unloading via click isn't an ability cast (hopefully for obvious reasons), you should find that the following event triggers when you click something out of a burrow:

[Trigger=]
Melee Initialization
Events
Unit - A unit Is issued an order targeting an object
Conditions
(Issued order) Equal to (Order(unload))
Actions
Game - Display to (All players) the text: Hello

[/Trigger]
 
Level 2
Joined
Sep 1, 2018
Messages
13
Unloading via click isn't an ability cast (hopefully for obvious reasons), you should find that the following event triggers when you click something out of a burrow:

[Trigger=]
Melee Initialization
Events
Unit - A unit Is issued an order targeting an object
Conditions
(Issued order) Equal to (Order(unload))
Actions
Game - Display to (All players) the text: Hello

[/Trigger]

Well that wasn't obvious to me because I wasn't acquainted with order strings, but it certainly is obvious now.

Great, this works. Big thanks.

Only change I made is the Action, of course:

  • Actions
    • Unit - Change ownership of (Triggering unit) to Neutral Passive and Change color
I used "Triggering Unit" because I didn't see anything more specific (ie. "Unit Issuing Order"). Don't know if that's the syntax I should be using, but it worked.

Now, I need to update the code so that it'll work for structures that can hold more than one unit.

Questions:
  • I suppose I need to use arrays to store and modify the amount of units in a given garrison, right?
  • Do I need to be creating variables for the garrisons (buildings) or the units inside the garrison?
  • Are "Unit Group" variables the key to making this thing work?



Update: I'm lost
(Not sure this qualified as an exception to the double posting rule, so just updating this existing post.)

Alright, I've spent the last couple of hours trying to apply what Pyrogasm shared.

I assume your journeyman mapmaker would be able to quickly digest and replicate that trigger. However, I'm basically a complete beginner and was unable to.

So I'm going to show exactly where I'm getting lost, and hopefully someone can make things nice and basic for me.

Disclaimer: I don't have any experience with arrays or the UDex system. That should come as no surprise.

What's up with CargoEvent?

  • Events
    • Game - CargoEvent Becomes Equal to 1.00
This means there's another trigger in which CargoEvent becomes equal to 1.00, right? I'm guessing it would start something like this:

  • Events
    • Unit - A unit Is loaded into a transport
Aside from making a guess at how this hypothetical trigger might start, I have no clue what the rest of it would look like. Please correct me if I'm off. Moving along.

Confused about CargoTransportUnit[]

  • Events
    • Game - CargoEvent Becomes Equal to 1.00
  • Conditions
  • Actions
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Custom value of CargoTransportUnit[UDex] equal to 0 //it had no loaded units before
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Owner of (Triggering Unit)) and change color
    • Set Custom Value of CargoTransportUnit[UDex] to ((Custom value of CargoTransportUnit[UDex]) + 1)
To my untrained eye it looks like were first doing an integer comparison with CargoTransportUnit[UDex] followed by a unit comparison.

I'm not able to replicate this, as the variable CargoTransportUnit[] is not an option for a unit comparison.

As mentioned, I'm not sure how UDex plays into this. I don't know the first thing about putting a UDex system in place. It's a system, right? (I don't know what that means.) If that's a simpler way of making this happen than using default settings on World Editor, then I'm open to learning it.

Though in my early planning stages, this Garrisoning function is probably the most complex triggering I plan to do with my map and I'm hoping for smooth sailing from here. So my preference is the most accessible thing to learn and apply.

Unloading Event

  • Events
    • -------- unloaded loaded event --------
    • Game - CargoEvent Becomes Equal to 2.00
  • Conditions
  • Actions
    • Set Custom Value of CargoTransportUnit[UDex] to ((Custom value of CargoTransportUnit[UDex]) - 1)
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • Custom value of CargoTransportUnit[UDex] equal to 0 //it's empty now
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Neutral Passive) and change color
Wait -- an unloading event occurred, but instead of the CargoEvent value going down, as I would expect... it goes up to 2.00. Which means I don't know what the CargoEvent variable is supposed to represent.

Clearly there's a triggering technique that I don't know about here. You can probably see how limited my knowledge is on this subject.

Thanks for reading. Anyone that feels like they can help, would love if you chimed in.
 
Last edited:
Level 13
Joined
Jul 15, 2007
Messages
763
I took a more in depth look at your problem. There's lots of ways you could make a solution:

  • Use variables to track loaded/unloaded units.
  • Use Hashtables.
  • Use a property you can dynamically update on your burrows (e.g. custom value (not recommended) or an ability).
I put together a very basic system using a hashtable that works. Oddly enough there was no order string or ability cast when a unit loads into a burrow, but the event for when a unit is transported works. When units are unloaded via standdown this also doesn't trigger order events but when this ability is cast one can assume that all garrisoned units will be emptied out.

You can look at my solution and see if it helps you. It's not perfect but i believe it does what you want.
 

Attachments

  • Orc Burrows.w3x
    18.6 KB · Views: 39
Level 39
Joined
Feb 27, 2007
Messages
5,010
I'm real sorry @Hankxiety I fucked up and said "use this system" and then forgot to include a link to it: GUI Unit Event v2.5.2.0

That should resolve all your confusion and questions about where CargoEvent come from and all the unit variables being assigned properly. They are created and set by the system! The additional problem with this that I did not forsee before I posted last time is that it already uses Custom Value to keep track of which unit is which (where UDex comes into play-- you're right it is a unit indexer system), so you cannot use that to store the amount of units currently loaded inside. Instead you will have to create a new integer array variable and use that instead with UDex as the array index we're modifying.

  • Events
    • -------- loaded event --------
    • Game - CargoEvent Becomes Equal to 1.00
  • Conditions
  • Actions
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • GARRISON_ARRAY[UDex] equal to 0 //it had no loaded units before
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Owner of (Triggering Unit)) and change color
      • Else - Actions
    • Set GARRISON_ARRAY[UDex] to (GARRISON_ARRAY[UDex] + 1)
  • -------- --------
  • -------- --------
  • Events
    • -------- unloaded event --------
    • Game - CargoEvent Becomes Equal to 2.00
  • Conditions
  • Actions
    • Set GARRISON_ARRAY[UDex] to (GARRISON_ARRAY[UDex] - 1)
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • GARRISON_ARRAY[UDex] equal to 0 //it's empty now
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Neutral Passive) and change color
      • Else - Actions

Now, you CAN also just use the order trigger that pick-a-chew posted above, but be aware that if you start using Custom Value to store information in your map you won't be able to use any other system/resource that requires the use of it (usually through a unit indexer) without breaking everything you've done that uses CV. Not generally recommended, though if you feel confident you won't run into this issue you can avoid having to import the system I linked. It would look like this:

  • Events
    • Unit - A unit Is loaded into a transport
  • Conditions
  • Actions
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • (Custom Value of (Loading Unit)) equal to 0 //it had no loaded units before, loading unit is the building it's going into
      • Then - Actions
        • Unit - Change ownership of (Loading Unit) to (Owner of (Triggering Unit)) and change color
      • Else - Actions
    • Unit - Set Custom Value of (Loading Unit) to (Custom Value of (Loading Unit) + 1)
  • -------- --------
  • -------- --------
  • Events
    • Unit - A unit Is issued an order targeting an object
  • Conditions
    • (Issued order) Equal to (Order(unload))
  • Actions
    • Unit - Set Custom Value of (Target unit of issued order) to (Custom Value of (Target unit of issued order) - 1)
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • (Custom Value of (Target unit of issued order)) equal to 0 //it's empty now
      • Then - Actions
        • Unit - Change ownership of (Target Unit of Issued Order) to (Neutral Passive) and change color
      • Else - Actions
You can also use a hashtable as pick-a-chew suggested to avoid having to import the system and circumventing any issues with using Custom Value for this in your map.
 
Level 2
Joined
Sep 1, 2018
Messages
13
I put together a very basic system using a hashtable that works.

It's not perfect but i believe it does what you want.

That makes it perfect, in my eyes. Thanks for going through the trouble, major help.

Now that it works, I want to make sure I understand it.

I spent a couple of hours testing and digesting your code. A lot of questions popped up as I was reading it, but was able to find answers for most of them.

Namely, I wasn't sure how hashtables were different from arrays. This tutorial cleared that up in an eloquent way:

imagine a variable array that is complicated enough to store data not just along the X axis, but also the Y.

I was really confused by the following actions until I realized that '0' was not referencing the 'x-axis', as I would expect from an array, but the 'y-axis'. Right?

  • Set GarrisonInteger = (Load 0 of (Key (Transporting unit)) from GarrisonHash)
  • Set GarrisonInteger = (GarrisonInteger + 1)
  • Hashtable - Save GarrisonInteger as 0 of (Key (Transporting unit)) in GarrisonHash
Still fuzzy on what's meant by a Handle -- that's the 'x-axis', right? And if that's right, then Handle ID is referring to it's position on the 'x-axis'?. Some clarity on that would be illuminating, no doubt.

What's also unresolved for me is where and when, exactly, "Transporting Unit" is added to the hashtable "GarrisonHash". I'm gonna make a guess in the commented sections below. Am I right?

Code:
ACTION TYPE: Set Variable
        VARIABLE: GarrisonInteger
        FUNCTION: Hashtable - Load Integer Value (hashtable)    // this function can be used to designate where on the 'y-axis' we are storing data, yes?
            VALUE: 0
            FUNCTION: Hashtable - Get Handle ID                        // my guess: this the function we use to call an existing handle or set a new one.
                FUNCTION: Event Response - Transporting Unit       // my guess: if transporting unit does not yet belong to the hashtable, it's assigned a Handle ID here.
        VARIABLE: GarrisonHash

Hope that's easy enough to read. I used this formatting to try to understand the triggers.




Okay, now I can see why those values were set for the real variable "CargoEvent". The System Description says a whole lot that explains that trigger:

In each event, the custom value of the "triggering unit" is equal to UDex

  • Detect when a unit is loaded into a transport
    Event: Game - CargoEvent Becomes Equal to 1.00
    Unit: CargoTransportUnit[UDex] --- The unit who loaded this unit

  • Detect when a unit is unloaded from a transport
    Event: Game - CargoEvent Becomes Equal to 2.00
    Unit: CargoTransportUnit[UDex] --- The unit who unloaded this unit

Now that's been cleared up. As for your other point, I think I follow:

(Cargo Event) already uses Custom Value to keep track of which unit is which ... so you cannot use that to store the amount of units currently loaded inside.

you will have to create a new integer array variable and use that instead with UDex as the array index we're modifying.

So I take that to mean this trigger cannot work using just one array, even if we're harnessing the power of UDex.

  • We need an array to refer the different garrisons: CargoEvent[UDex]
  • We need another array to store the number of units in the garrisons stored in CargoEvent[UDex]: GARRISON_ARRAY[UDex]
The alternative, then, is a hashtable which can store these two data points -- the garrison and the number of units in the garrison -- on the x- and y-axes, respectively.

I think I'm getting the hang of this. Thank you!

 
Last edited:
Level 13
Joined
Jul 15, 2007
Messages
763




That makes it perfect, in my eyes. Thanks for going through the trouble, major help.

Now that it works, I want to make sure I understand it.

I spent a couple of hours testing and digesting your code. A lot of questions popped up as I was reading it, but was able to find answers for most of them.

Namely, I wasn't sure how hashtables were different from arrays. This tutorial cleared that up in an eloquent way:



I was really confused by the following actions until I realized that '0' was not referencing the 'x-axis', as I would expect from an array, but the 'y-axis'. Right?

  • Set GarrisonInteger = (Load 0 of (Key (Transporting unit)) from GarrisonHash)
  • Set GarrisonInteger = (GarrisonInteger + 1)
  • Hashtable - Save GarrisonInteger as 0 of (Key (Transporting unit)) in GarrisonHash
Still fuzzy on what's meant by a Handle -- that's the 'x-axis', right? And if that's right, then Handle ID is referring to it's position on the 'x-axis'?. Some clarity on that would be illuminating, no doubt.

What's also unresolved for me is where and when, exactly, "Transporting Unit" is added to the hashtable "GarrisonHash". I'm gonna make a guess in the commented sections below. Am I right?

Code:
ACTION TYPE: Set Variable
        VARIABLE: GarrisonInteger
        FUNCTION: Hashtable - Load Integer Value (hashtable)    // this function can be used to designate where on the 'y-axis' we are storing data, yes?
            VALUE: 0
            FUNCTION: Hashtable - Get Handle ID                        // my guess: this the function we use to call an existing handle or set a new one.
                FUNCTION: Event Response - Transporting Unit       // my guess: if transporting unit does not yet belong to the hashtable, it's assigned a Handle ID here.
        VARIABLE: GarrisonHash

Hope that's easy enough to read. I used this formatting to try to understand the triggers.




Okay, now I can see why those values were set for the real variable "CargoEvent". The System Description says a whole lot that explains that trigger:





Now that's been cleared up. As for your other point, I think I follow:





So I take that to mean this trigger cannot work using just one array, even if we're harnessing the power of UDex.

  • We need an array to refer the different garrisons: CargoEvent[UDex]
  • We need another array to store the number of units in the garrisons stored in CargoEvent[UDex]: GARRISON_ARRAY[UDex]
The alternative, then, is a hashtable which can store these two data points -- the garrison and the number of units in the garrison -- on the x- and y-axes, respectively.

I think I'm getting the hang of this. Thank you!


Different people may tell you different things but i personally visualize a hashtable as a table... I think of it this way as I'm scientifically minded (axis for me is limited to numbers). In the hashtable i made for you, it's simply:

Hashtable0
Handle of transporting unitGarrisonInt

To my mind it's arbitrary as to what you use for the X and Y axis. What's more important is that when you save a variable, you load it from the place you saved it, so if you saved at 0 of Key(Transporting Unit) you load from that location and not Key(Transporting Unit) of 0.

Handle is simply a reference to an entity that isn't a value by its default nature, integers/reals can simply be saved into a hashtable without any problem. However if you want to save something more complex, such as a unit or special effect, they need to be referenced in a way that can be recalled so they are given an ID. This is, at least my interpretation of it (i am not a coder by profession :p).

I think of hashtables as dynamic constructs. You can add handles to them as you want and they will exist in there until you clear them out with: [Trigger=]
Hashtable - Clear all child hashtables of child (Handle) in Hashtable
[/Trigger]
 
Level 39
Joined
Feb 27, 2007
Messages
5,010
So I take that to mean this trigger cannot work using just one array, even if we're harnessing the power of UDex.
  • We need an array to refer the different garrisons: CargoEvent[UDex]
  • We need another array to store the number of units in the garrisons stored in CargoEvent[UDex]: GARRISON_ARRAY[UDex]
The alternative, then, is a hashtable which can store these two data points -- the garrison and the number of units in the garrison -- on the x- and y-axes, respectively.
No, CargoEvent is just a real variable that is used by the system I linked in order to "create" the "unit is unloaded" event. It doesn't need to be an array and you don't need to touch it at all once it's in your variable editor; the system does all of that for you. When it detects an unload event it will automatically set the value of CargoEvent to 2.00, which acts as the "event" that fires your unloading trigger. Similarly, when it detects a load event it will automatically set the value of CargoEvent to 1.00, which acts as the "event" that fires your loading trigger.

The reason we need GARRISON_ARRAY is because if you try to set the Custom Value of the holding units it will mess up the Unit Indexer because the UI uses Custom Value of each unit to give each unit a unique ID. Because each unit has a unique ID, you can be sure that if you use that ID as an array index to keep track of the units garrisoned inside that specific unit nothing will randomly overwrite it because of a unit-ID-number collision.

I did make another error here when I suggested you do GARRISON_ARRAY[UDex]. The error is using UDex. As the system specifies, the UDex = Custom Value of (unit that triggered the event) which will be the unit that is being garrisoned, not the unit it's garrisoned in. We want to store the information about how many units are inside under the unit index of the garrison itself, else when we try to set it GARRISON_ARRAY[] +/-1 we'll be getting different UDex numbers for each unit that loads in and out! To solve this, we'll make one more integer variable and store into it the custom value of the garrison itself (its unit id). Then we use that as the index for our array:

  • Events
    • -------- loaded event --------
    • Game - CargoEvent Becomes Equal to 1.00 //no array here
  • Conditions
  • Actions
    • Set GARRISON_ID = (Custom Value of (CargoTransportUnit[UDex]))
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • GARRISON_ARRAY[GARRISON_ID] equal to 0 //it had no loaded units before
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Owner of (Triggering Unit)) and change color
      • Else - Actions
    • Set GARRISON_ARRAY[GARRISON_ID] to (GARRISON_ARRAY[GARRISON_ID] + 1)
  • -------- --------
  • -------- --------
  • Events
    • -------- unloaded event --------
    • Game - CargoEvent Becomes Equal to 2.00
  • Conditions
  • Actions
    • Set GARRISON_ID = (Custom Value of (CargoTransportUnit[UDex]))
    • Set GARRISON_ARRAY[GARRISON_ID] to (GARRISON_ARRAY[GARRISON_ID] - 1)
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditoins
        • GARRISON_ARRAY[GARRISON_ID] equal to 0 //it's empty now
      • Then - Actions
        • Unit - Change ownership of CargoTransportUnit[UDex] to (Neutral Passive) and change color
      • Else - Actions
You're very close to fully understanding hashtables. I will attempt to nudge you in the right direction by giving an example. It is fine to think of the 2 keys for hashtables as 2 dimensions of integers in X/Y space, but you can also use a string for either of them to make it more clear to you what you are storing at a particular location. I'm gonna call your hashtable Table[x][y] for now, simulating the two axes/keys you could use to store... but be aware that the way GUI lays it out is written as Store y of x in TABLE, not Store x of y in TABLE, so the order you'd use would be reversed of my example. This is important because you usually want to put all the information about "one" object under a single x key for that object with ys holding the different data you want to store, not under a single y with multiple xes because that becomes very hard to flush the data from and clean up when necessary.

Say you generate kill gold and lumber manually for all units/players on your map and want to store the data of what each unit-type is worth in gold and lumber in your Table[x][y] instead of an array. A rudimentary way to do this would be to cache the data on map init in something like this:
indexvalue

Table[x=0][y=0]
"footman"

Table[x=0][y=1]
10

Table[x=0][y=2]
1

Table[x=1][y=0]
"knight"

Table[x=1][y=1]
35

Table[x=1][y=2]
6
But here you have to know which index is for gold, [x][1], and which for lumber, [x][2], and to get the right data for each unit type you have to loop through the list of [x][0]s checking the unit's name vs what you cached. Not very efficient or easy to use, highly prone to user error. The next step would be to use "gold" and "lumber" as your y keys and then instead store everything under x values that are the Unit-Type of the unit. All unit-types are actually integers in base-36 (all letters + 0-9) and 'hfoo', for example, is the integer 813480. Normally 813480 is way outside array bounds (up to 8191 pre-1.29, larger after) so you can't use it as an array index like GoldAmount['hfoo'] but hashtables have no restrictions on the integers/strings used for keys in them. Note GUI won't let you use '' characters in integer input fields so you can't use this directly but it's just an example. You can then store it this way instead
indexvalue

Table[x='hfoo'][y="gold"]
10

Table[x='hfoo'][y="lumber"]
1

Table[x='hkni'][y="gold"]
35

Table[x='hkni'][y="lumber"]
6
So when you want to know how much gold to give a player for killing a unit you just grab its unit type integer and then look up Table[ThatInteger]["gold"]. Nifty. Now we go one level deeper: suppose you want to be able to modify the resources that a specific individual footman or knight should reward on death. Perhaps every time a unit kills another unit it gains 1 additional lumber as a bounty. To do this we'll keep the table we used above but also store information about any specific units in a second separate table. We'll use the Unit ID (not unit-type) of the specific unit as our x value! Where do we get such a unit ID for that specific unit? That's where the Hashtable - Get Handle ID function comes in; it spits out a unique number for each unit that we can use. Every time you use that function on the same unit you'll get the same number. This function reads as Key (<some handle>) in GUI once you've used it, so don't get confused there. When we need to modify a unit's bonus bounty on it killing another unit we'll do these steps
  • Load Table[x=Key(<killer>)][y="bonus lumber"] into some variable
  • Increase that variable by 1
  • Save that variable into Table[x=Key(<killer>)][y="bonus lumber"]
When we then need to properly figure out how much gold and lumber to give we'll load data from the cached values and from the value specific to the unit. If no value has been saved in Table[x][y] prior to it being loaded it will return 0, "", or null depending on the type of variable you're trying to load so there is no problem that may cause error here.
  • Load Table[x=Key(<dying unit>)][y="bonus lumber"] into var1
  • Load Table[x=Unit Type(<dying unit>)][y="gold"] into var2
  • Load Table[x=Unit Type(<dying unit>)][y="lumber"] into var3
  • Give the proper player var2 gold and var3+var1 lumber
 
Level 2
Joined
Sep 1, 2018
Messages
13
Different people may tell you different things but i personally visualize a hashtable as a table...
In the hashtable i made for you, it's simply:

Hashtable0
Handle of transporting unitGarrisonInt

Okay, that's easy enough to understand. A hashtable can just as easily be visualized as a table.

Handle is simply a reference to an entity that isn't a value by its default nature, integers/reals can simply be saved into a hashtable without any problem. However if you want to save something more complex, such as a unit or special effect, they need to be referenced in a way that can be recalled so they are given an ID.

Thanks for these explanations, I think I'm getting the hang of it.

In fact, I needed to make use of Handles to meet a new requirement:
- It should change owner of the transporting unit to Neutral Passive when GarrisonInteger = 0.

So, I added this line to your GarrisonLoad trigger:

  • Hashtable - Save Handle Of(Transporting unit) as 1 of (Key (Transporting unit)) in GarrisonHash
And then added these lines to your GarrisonUnload trigger:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Load GarrisonInteger of (Key (Triggering unit)) from GarrisonHash) Equal to 0
    • Then - Actions
      • Wait 0.10 game-time seconds
      • Unit - Change ownership of (Load 1 of (Key (Ordered unit)) in GarrisonHash) to Neutral Passive and Change color
    • Else - Actions
      • Do nothing
For GarrisonStandDown I removed the If statement and changed 'Ordered unit' to 'Casting unit'.

For a while it was changing the ownership of the ground unit exiting the garrison to Neutral Passive. That's until I added the 'Wait' action, which solved the problem.

I suppose, then, the table would look something like this:

GarrisonHash
01
GarrisonInteger(Transporting Unit)

I'm wondering if saving the Handle of (Transporting Unit) was redundant, though that's how I managed to make this work how I wanted it to.




When it detects an unload event it will automatically set the value of CargoEvent to 2.00, which acts as the "event" that fires your unloading trigger. Similarly, when it detects a load event it will automatically set the value of CargoEvent to 1.00, which acts as the "event" that fires your loading trigger.

Got it. It's good to see examples of Real Variables being used like this. I'm certain I'll run into this again.

Seems the advantage of using UDex is you don't have to define certain variables ('cargoEvent', in this case) which aren't defined in the GUI.

The reason we need GARRISON_ARRAY is because if you try to set the Custom Value of the holding units it will mess up the Unit Indexer because the UI uses Custom Value of each unit to give each unit a unique ID.

So if I'm understanding correctly, that means we're changing the custom value of the units position in the array, not the custom value of the unit... because only one custom value can be assigned to an object, and custom values are used to designate unit IDs.

I haven't installed UDex yet (worried I might break something...) but let me see if I can demonstrate my comprehension of your code by consolidating those two events into a single trigger.

  • Events
    • Game - cargoEvent Becomes Greater than 0.00
  • Conditions
  • Actions
    • Set garrisonID = (Custom Value of (cargoEvent[UDex]))
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If (Conditions)
        • garrisonArray[garrisonID] equal to 0 and cargoEvent equal to 1.00
      • Then - Actions
        • Unit - Change ownership of cargoTransportUnit[UDex] to (Owner of (Triggering Unit)) and change color.
      • Else - Actions
        • Set garrisonArray[garrisonID] to (garrisonArray[garrisonID] + 1)
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • cargoEvent equal to 2.00 and garrisonArray[garrisonID] equal to 0
      • Then - Actions
        • Unit - Change ownership of cargoTransportUnit[UDex] to (Neutral Passive) and change color.
      • Else - Actions
Thanks, both of you. Now I can move on to other map requirements.

Hope this was instructive for anyone else that followed along.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,010
The Unit Indexer that GUI Unit Event 2.5.2.0 uses isn't a separate system, it's part of that system. If you've properly installed it it will have included the unit indexer trigger(s) and now you can get any unit's index whenever you want by loading its Custom Value.

CargoEvent is not an array variable; it's a single real varaible that GUIUE sets. You don't need to make it yourself because it will be automatically generated by virtue of being used in the triggers you need to copy over to get GUIUE working; if you are going to use GUIUE you should delete any such variables you've made yourself before importing/installing it. At any rate Custom Value of (CargoEvent[UDex]) is not correct syntax because Custom Value expects a unit and you're giving it a real (only units and items have custom values, no other variable/object types). You have the right idea but the reference to the unit you're trying to get to is actually CargoTransportUnit[UDex]. When a load/unload event is detected the system also sets UDex and the correct CargoTransportUnit[] before changing CargoEvent to 1.00/2.00 so that you can use Triggering Unit to access the unit that is being loaded/unloaded and CargoTransportUnit[UDex] to access the unit that is doing the loading/unloading.

It's not more efficient to have it all in one trigger. In fact, from a code execution standpoint it's actually less because of the If-blocks. You do save a negligible number of bytes by having fewer triggers, but map size isn't really a Bnet concern any more. I wouldn't recommend using "Greater than 0.00" because I have no idea what the system does internally between events. It might set the variable to 1.5 as an intermediary step or something else entirely; you should only use "equal to" for variable-setting-based events. Your example trigger also does not decrease garrisonArray when units unload, so it will never properly change back to neutral passive. All of it in a single trigger would actually look like this:

  • Events
    • Game - CargoEvent Becomes equal to 1.00
    • Game - CargoEvent Becomes equal to 2.00
  • Conditions
  • Actions
    • Set garrisonID = (Custom Value of (CargoTransportEvent[UDex]))
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If (Conditions)
        • CargoEvent equal to 1.00
      • Then - Actions
        • If (All conditions are true) then do (Then Actions) else do (Else Actions)
          • If (Conditions)
            • garrisonArray[garrisonID] equal to 0
          • Then - Actions
            • Unit - Change ownership of cargoTransportUnit[UDex] to (Owner of (Triggering Unit)) and change color.
          • Else - Actions
        • Set garrisonArray[garrisonID] to (garrisonArray[garrisonID] + 1)
      • Else - Actions
    • If (All conditions are true) then do (Then Actions) else do (Else Actions)
      • If (Conditions)
        • CargoEvent equal to 2.00
      • Then - Actions
        • Set garrisonArray[garrisonID] to (garrisonArray[garrisonID] - 1)
        • If (All conditions are true) then do (Then Actions) else do (Else Actions)
          • If (Conditions)
            • garrisonArray[garrisonID] equal to 0
          • Then - Actions
            • Unit - Change ownership of cargoTransportUnit[UDex] to (Neutral Passive) and change color.
          • Else - Actions
      • Else - Actions
 
Level 2
Joined
Sep 1, 2018
Messages
13
Can really tell you're scrutinizing my code! I appreciate that. You're catching a lot of things I'm sure I would have learned about the hard way later on.

The Unit Indexer that GUI Unit Event 2.5.2.0 uses isn't a separate system, it's part of that system. If you've properly installed it it will have included the unit indexer trigger(s) and now you can get any unit's index whenever you want by loading its Custom Value.

Okay, that's a helpful distinction.

CargoEvent is not an array variable; it's a single real varaible that GUIUE sets.

Understood. Looks like I just made a mistake when trying to replicate your code, typing 'CargoEvent' instead of 'CargoTransportUnit[UDex]' when setting variable 'garrisonID'.

It's not more efficient to have it all in one trigger. In fact, from a code execution standpoint it's actually less because of the If-blocks. You do save a negligible number of bytes by having fewer triggers, but map size isn't really a Bnet concern any more.

Good to know that, as well. Here I was, thinking I was being clever.

I wouldn't recommend using "Greater than 0.00" because I have no idea what the system does internally between events. It might set the variable to 1.5 as an intermediary step or something else entirely; you should only use "equal to" for variable-setting-based events.

Also a good point.

Your example trigger also does not decrease garrisonArray when units unload, so it will never properly change back to neutral passive.

Oh, I did miss that. Geez.
 
Status
Not open for further replies.
Top