Simple hashtable save/load issue.

Solu9

You can change this now in User CP.
Reaction score
216
I have been hesitating with using hashtables in my triggers but I thought the time had come to do so now. I wanted to start with something simple and thus I created a target point spell with a unit missile.

Triggers:

Trigger:
  • Bone Spear Ini
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set Hash_BoneSpear = (Last created hashtable)
      • Set Real_BoneSpear_Speed = 50.00


Trigger:
  • Bone Spear Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Bone Spear (Necromancer)
    • Actions
      • Hashtable - Save Handle Of(Target point of ability being cast) as 0 of (Key (Triggering player)) in Hash_BoneSpear
      • Set Point_BoneSpear_Create = (Position of (Triggering unit))
      • Set Point_BoneSpear_StartFacePoint = (Target point of ability being cast)
      • Unit - Create 1 Dummy Unit Bone Spear for (Owner of (Triggering unit)) at Point_BoneSpear_Create facing Point_BoneSpear_StartFacePoint
      • Unit - Turn collision for (Last created unit) Off
      • Unit - Pause (Last created unit)
      • Unit Group - Add (Last created unit) to UnitGroup_BoneSpear
      • Custom script: call RemoveLocation(udg_Point_BoneSpear_Create)
      • Custom script: call RemoveLocation(udg_Point_BoneSpear_StartFacePoint)


Trigger:
  • Bone Spear Travel Copy Copy
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in UnitGroup_BoneSpear and do (Actions)
        • Loop - Actions
          • Set Point_BoneSpear_End = (Load 0 of (Key (Owner of (Picked unit))) in Hash_BoneSpear)
          • Set Point_BoneSpear_Caster = (Position of (Picked unit))
          • Set Real_BoneSpear_Travel = (Distance between Point_BoneSpear_Caster and Point_BoneSpear_End)
          • Set Real_BoneSpear_Angle = (Angle from Point_BoneSpear_Caster to Point_BoneSpear_End)
          • Custom script: call RemoveLocation(udg_Point_BoneSpear_Caster)
          • Custom script: call RemoveLocation(udg_Point_BoneSpear_End)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Real_BoneSpear_Travel Greater than (Real_BoneSpear_Speed x 1.50)
            • Then - Actions
              • Set Point_BoneSpear_Caster = ((Position of (Picked unit)) offset by Real_BoneSpear_Speed towards Real_BoneSpear_Angle degrees)
              • Unit - Move (Picked unit) instantly to Point_BoneSpear_Caster
              • Custom script: call RemoveLocation(udg_Point_BoneSpear_Caster)
            • Else - Actions
              • Unit Group - Remove (Picked unit) from UnitGroup_BoneSpear
              • Hashtable - Clear all child hashtables of child (Key (Target point of ability being cast)) in Hash_BoneSpear
              • Unit - Remove (Picked unit) from the game


Now everything works like I want it to except the direction of the missile. It always travels to the center of the map. I know it has something to do with a point not set properly and in this case it kinda must be the hashtable.

It's probably really simple but I can't see what I am doing wrong.
 

KaerfNomekop

Swim, fishies. Swim through the veil of steel.
Reaction score
613
IIRC you can't use RemoveLocation on points saved in the hashtable, until you're done using them. Regardless of which reference (variable) you use to clear them.
 

Solu9

You can change this now in User CP.
Reaction score
216
IIRC you can't use RemoveLocation on points saved in the hashtable, until you're done using them. Regardless of which reference (variable) you use to clear them.

Won't it leak if I don't clear it every time I move the unit?

Edit:
I tried to disable the RemoveLocation and it does indeed works. But I am still concerned about leaks. It take that it is leaking now since I don't remove the point. So where should I put the RemoveLocation in the trigger to make sure it is leakless?
 

vypur85

Hibernate
Reaction score
803
If you understand the definition of leak correctly, then it doesn't leak. Just make sure you remove the point after you're not using it anymore.


Then again, this statement is made purely on my own common sense, judging from the definition.
 

Solu9

You can change this now in User CP.
Reaction score
216
I understand a leak as fx: A point that is created evey time an event occurs and does not get removed from the game at any point in time.

For my posted trigger I create a point every 0.5 second and does not remove it at all (if I disable the RemoveLocation). I could add the RemoveLocation under the "else" action, but from my knowledge it will only remove that last point at which I created the point, and not those created while the missile travels.
 

vypur85

Hibernate
Reaction score
803
I haven't really read through the whole trigger. But I'm assuming that you create the point only once and save it to hashtable, and then use it many times. As long as you do not lose reference to it, and overwrite it with another different point, then it's fine.
 

Imp Midna

Active Member
Reaction score
52
Defining something using an example only is usually a bad idea. just saying.

As for the point, vypur is right. You only create the point once and then load the handle of it into- and from your hashtable. Your variable is a point-handle variable, not a point-variable. Removing the point in the else is exactly the way you have to go.

Apart from that:
Trigger:
  • Actions
    • Hashtable - Save Handle Of(Target point of ability being cast) as 0 of (Key (Triggering player)) in Hash_BoneSpear
    • [...]
    • Hashtable - Clear all child hashtables of child (Key (Target point of ability being cast)) in Hash_BoneSpear


You are clearing an unrelated rather random primary key. is that intended?
 

Solu9

You can change this now in User CP.
Reaction score
216
As for the point, vypur is right. You only create the point once and then load the handle of it into- and from your hashtable. Your variable is a point-handle variable, not a point-variable. Removing the point in the else is exactly the way you have to go

So to elaborate every time I "set" the point I am in fact just moving it?


You are clearing an unrelated rather random primary key. is that intended?
Not at all intended. I found a Charge spell test map in which it is the casting unit that is moved and the target which is stored in the hashtable.
I tweaked it to fit my point-and-click spell needs. I kept the action just in case, since I had no idea what it did :)
 

Imp Midna

Active Member
Reaction score
52
So to elaborate every time I "set" the point I am in fact just moving it?

In this case, y.


Not at all intended. I found a Charge spell test map in which it is the casting unit that is moved and the target which is stored in the hashtable.
I tweaked it to fit my point-and-click spell needs. I kept the action just in case, since I had no idea what it did :)

It's actually some kind of leak removal if you will. In this case, you have a point handle saved in the child key "0" of the primary key "Key of (Owner of (Picked unit))". This handle will stay there until you remove it from there. The action "Clear all child hashtables of child "Key of (Owner of (Dad Caster))"" Will remove it from there. If you saved something with in the child key "1" of that primary key, it will also be removed. It removes every child for the given primary key. Therefor it's Jass-Name "Flush Child Hashtable".
 

Solu9

You can change this now in User CP.
Reaction score
216
So is it advisable to do the Clear in the end of the trigger or doesn't it really matter since i'm going to use the handle a lot?
 

Imp Midna

Active Member
Reaction score
52
It's definitely a good thing to get used to, but your right, since a new handle is gonna be placed in there every time the player casts the spell, it will be overwritten anyways so it really doesn't matter in this case. But as you are a beginner, i would recommend doing it. Just to get used to it.
 

Solu9

You can change this now in User CP.
Reaction score
216
Alright. Good info. Last I need for now is if the Clear Hash action in my trigger is correct?
 

Imp Midna

Active Member
Reaction score
52
Your clearing the wrong primary key. You filled it into "Key of (The casting Player)", but you are clearing "Key of (Some random Point"). You will have to clear the same primary key that you inserted the point handle into, which would be "Key of (Owner of (Picked Unit))"
 

Solu9

You can change this now in User CP.
Reaction score
216
Your clearing the wrong primary key. You filled it into "Key of (The casting Player)", but you are clearing "Key of (Some random Point"). You will have to clear the same primary key that you inserted the point handle into, which would be "Key of (Owner of (Picked Unit))"
Great! Thanks for explaining it in detail.
 

Solu9

You can change this now in User CP.
Reaction score
216
I thought I had got the hang of these simple hashtable usages but I am stuck with a new one.
I post in this thread because it is greatly related to the problem I had with the initial trigger.

Here we go:

Trigger:
  • Wolf Punch Ini
    • Events
    • Map initialization
    • Conditions
    • Actions
    • Hashtable - Create a hashtable
    • Set Hash_WolfPunch = (Last created hashtable)
    • Set Real_WolfPunch_Speed = 50.00


Trigger:
  • Wolf Punch Cast
    • Events
    • Unit - A unit Starts the effect of an ability
    • Conditions
    • (Ability being cast) Equal to Wolf Punch (Shaman)
    • Actions
    • Set Point_WolfPunch_Caster = (Position of (Triggering unit))
    • Set Point_WolfPunch_EndPoint = (Point_WolfPunch_Caster offset by 800.00 towards (Facing of (Triggering unit)) degrees)
    • Set Real_WolfPunch_Angle = (Angle from Point_WolfPunch_Caster to Point_WolfPunch_EndPoint)
    • Hashtable - Save Handle OfPoint_WolfPunch_EndPoint as 0 of (Key (Triggering unit)) in Hash_WolfPunch
    • Unit - Create 1 Dummy Unit Wolf Punch for (Owner of (Triggering unit)) at Point_WolfPunch_Caster facing Point_WolfPunch_EndPoint
    • Unit - Turn collision for (Last created unit) Off
    • Unit - Pause (Last created unit)
    • Unit Group - Add (Last created unit) to UnitGroup_WolfPunch


Trigger:
  • Wolf Punch Travel
    • Events
    • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
    • Unit Group - Pick every unit in UnitGroup_WolfPunch and do (Actions)
    • Loop - Actions
    • Set Point_WolfPunch_End = (Load 0 of (Key (Picked unit)) in Hash_WolfPunch)
    • Set Point_WolfPunch_Caster = (Position of (Picked unit))
    • Set Real_WolfPunch_Travel = (Distance between Point_WolfPunch_Caster and Point_WolfPunch_End)
    • Set Real_WolfPunch_Angle = (Angle from Point_WolfPunch_Caster to Point_WolfPunch_End)
    • Custom script: call RemoveLocation(udg_Point_WolfPunch_Caster)
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
    • Real_WolfPunch_Travel Greater than (Real_WolfPunch_Speed x 1.50)
    • Then - Actions
    • Set Point_WolfPunch_Caster = ((Position of (Picked unit)) offset by Real_WolfPunch_Speed towards Real_WolfPunch_Angle degrees)
    • Unit - Make (Picked unit) face Real_WolfPunch_Angle over 0.00 seconds
    • Unit - Move (Picked unit) instantly to Point_WolfPunch_Caster
    • Custom script: call RemoveLocation(udg_Point_WolfPunch_Caster)
    • Else - Actions
    • Unit Group - Remove (Picked unit) from UnitGroup_WolfPunch
    • Unit - Remove (Picked unit) from the game


The "missile" is created and moves. But again to the center of the map. So it is the hashtable that causes the problem again. What am I doing wrong?
 

vypur85

Hibernate
Reaction score
803
You should save the EndPoint as the Key of your dummy unit, not the caster. When your dummy is added into the Unitgroup, you loaded the key from picked unit (which is your dummy), which returns 'no point' naturally.
 

Solu9

You can change this now in User CP.
Reaction score
216
Alright I moved the "save hashtable" just under the "create dummy unit" and changed "key of triggering unit" to "key of last created unit". Like this:

Trigger:
  • Wolf Punch Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Wolf Punch (Shaman)
    • Actions
      • Set Point_WolfPunch_Caster = (Position of (Triggering unit))
      • Set Point_WolfPunch_EndPoint = (Point_WolfPunch_Caster offset by 800.00 towards (Facing of (Triggering unit)) degrees)
      • Set Real_WolfPunch_Angle = (Angle from Point_WolfPunch_Caster to Point_WolfPunch_EndPoint)
      • Unit - Create 1 Dummy Unit Wolf Punch for (Owner of (Triggering unit)) at Point_WolfPunch_Caster facing Point_WolfPunch_EndPoint
      • Hashtable - Save Handle OfPoint_WolfPunch_EndPoint as 0 of (Key (Last created unit)) in Hash_WolfPunch
      • Unit - Turn collision for (Last created unit) Off
      • Unit - Pause (Last created unit)
      • Unit Group - Add (Last created unit) to UnitGroup_WolfPunch
      • Custom script: call RemoveLocation(udg_Point_WolfPunch_Caster)
      • Custom script: call RemoveLocation(udg_Point_WolfPunch_End)


And it works now! What confuses me though is that in the trigger in the OP, which I got to work using "save key of triggering player" and "load key of picked unit" works as it should. And as I see it I do not save the key of that (bone spear dummy) unit in that trigger. So why do I need to do exactly that in this new trigger?

Nevertheless. Thanks vypur85 :)
 

vypur85

Hibernate
Reaction score
803
Uh. It's kinda hard to explain. You'll to understand the concept of hashtable and key handles. When you save something using some key handle, you'll need to load back the same key handle in another trigger.

In your case, you created the dummy and add it to a group in another trigger. In this case, your caster is not involved in anything in your other trigger. Only your dummy is involved. Which is why you need to save any information using your dummy handle. And load it up later using your dummy again.

I guess that's basically it.
 

Solu9

You can change this now in User CP.
Reaction score
216
But isn't that the same in this case?:

Trigger:
  • Bone Spear Cast Copy
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Bone Spear (Necromancer)
    • Actions
      • Hashtable - Save Handle Of(Target point of ability being cast) as 0 of (Key (Triggering player)) in Hash_BoneSpear
      • Set Point_BoneSpear_Create = (Position of (Triggering unit))
      • Set Point_BoneSpear_StartFacePoint = (Target point of ability being cast)
      • Unit - Create 1 Dummy Unit Bone Spear for (Owner of (Triggering unit)) at Point_BoneSpear_Create facing Point_BoneSpear_StartFacePoint
      • Unit - Turn collision for (Last created unit) Off
      • Unit - Pause (Last created unit)
      • Unit Group - Add (Last created unit) to UnitGroup_BoneSpear
      • Custom script: call RemoveLocation(udg_Point_BoneSpear_Create)
      • Custom script: call RemoveLocation(udg_Point_BoneSpear_StartFacePoint)


Trigger:
  • Bone Spear Travel Copy
    • Events
      • Time - Every 0.05 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in UnitGroup_BoneSpear and do (Actions)
        • Loop - Actions
          • Set Point_BoneSpear_End = (Load 0 of (Key (Owner of (Picked unit))) in Hash_BoneSpear)
          • Set Point_BoneSpear_Caster = (Position of (Picked unit))
          • Set Real_BoneSpear_Travel = (Distance between Point_BoneSpear_Caster and Point_BoneSpear_End)
          • Set Real_BoneSpear_Angle = (Angle from Point_BoneSpear_Caster to Point_BoneSpear_End)
          • Custom script: call RemoveLocation(udg_Point_BoneSpear_Caster)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Real_BoneSpear_Travel Greater than (Real_BoneSpear_Speed x 1.50)
            • Then - Actions
              • Set Point_BoneSpear_Caster = ((Position of (Picked unit)) offset by Real_BoneSpear_Speed towards Real_BoneSpear_Angle degrees)
              • Unit - Make (Picked unit) face Real_BoneSpear_Angle over 0.00 seconds
              • Unit - Move (Picked unit) instantly to Point_BoneSpear_Caster
              • Custom script: call RemoveLocation(udg_Point_BoneSpear_Caster)
              • Set UnitGroup_BoneSpear_Damage = (Units within 50.00 of (Position of (Picked unit)) matching ((((Matching unit) belongs to an enemy of (Owner of (Picked unit))) Equal to True) and (((Matching unit) is alive) Equal to True)))
              • Unit Group - Pick every unit in UnitGroup_BoneSpear_Damage and do (Actions)
                • Loop - Actions
                  • Unit - Cause Hero[(Player number of (Owner of (Triggering unit)))] to damage (Picked unit), dealing ((((Real((Intelligence of Hero[(Player number of (Owner of (Triggering unit)))] (Include bonuses)))) + ((Real((Level of Bone Spear (Necromancer) for Hero[(Player number of (Owner of (Triggering unit)))]))) x 50.00)) x (1.00 x (1.00 x 1.00))) x (1.00 x 1.00)) damage of attack type Chaos and damage type Unknown
              • Custom script: call DestroyGroup (udg_UnitGroup_BoneSpear_Damage)
            • Else - Actions
              • Unit Group - Remove (Picked unit) from UnitGroup_BoneSpear
              • Unit - Remove (Picked unit) from the game


If it is I don't understand why the Bone Spear trigger works with "triggering player" and the Wolf Punch don't.
 

Imp Midna

Active Member
Reaction score
52
You can imagine warcraft 3 hashtables as an array with an infinite size, each one containing an array with an infinite size, each one containing whatever you want to put in there. Like this:
normal array: array[1] = Triggering Unit
"hashtable": hashtable[1][1] = Triggering Unit
Pretty mindblowing at first, but you get used to it.
Now, a hashtable is called hashtable and not array, and that is because due to its (rather complex) implementation you can use an index of ANY size onto it without making it disgustingly big. In wc3, as you know, an array can only be used from 0 to ~8000. A hashtable can be used with ANY number you would like to put in.
When you use "Key (Triggering Unit)", you get a special id, a special unique number that clearly identifies this Unit. This number is quite huge, way too huge to be used as an array index. However, the hashtable doesnt care about its size. By using this unique number for the hashtables first index, you get a free array for pretty much every unit in your game. The action
Trigger:
  • Hashtable - Save Handle OfPoint_WolfPunch_EndPoint as 0 of (Key (Triggering unit)) in Hash_WolfPunch

translates into
Trigger:
  • Set Hash_WolfPunch[Key(Triggering unit)][0] = Point_WikfPunch_EndPoint

(if you would write it like an array)

What you did in your first trigger was:
Trigger:
  • Hashtable - Save Handle OfPoint_WolfPunch_EndPoint as 0 of (Key (Triggering unit)) in Hash_WolfPunch"
    • "Set Point_WolfPunch_End = (Load 0 of (Key (Picked unit)) in Hash_WolfPunch)"

Written like an array, it looks like this:
Trigger:
  • Set Hash_WolfPunch[Key(Triggering Unit)][0] = Point_WlfPunch_EndPoint
    • Set Point_WolfPunch_End = Hash_WolfPunch[Key(Picked unit][0]


Because Key(some unit) is always unique for this unit, and because the picked unit was not the triggering unit, Key (Triggering Unit) and Key (Picked Unit) were not the same either. As you know how arrays work, i guess you will see why this doesnt work.

In your other Trigger, you did NOT use Key (some unit) as an index. What you did was using Key (Owner of (some unit)) -> Key (some player). The thing with players is, that they are only 15 players out there in the entire game. Whether you get this player using Triggering Player or Owner of Picked unit does not matter, it still is the same player. And because it is the same player, Key (that player) will be the same both times, resulting in you using the right index. Fun fact: when someone with shared unit controlls uses this ability so that Triggering Player != Owner of (Triggering Unit), you trigger will fail again, because you save using Key (Triggering Player), but load using Owner of (Picked unit), which is Owner of (Triggering Unit) in the ability cast trigger.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top