• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Solved] Hashtables and unit Groups

Status
Not open for further replies.
Level 7
Joined
Sep 19, 2012
Messages
204
Hey guys,

i am currently working on a group movement system for one of my maps and so far everything works fine...except one thing.
This is the first time im using hashtables as core of a system and i dont understand how to use them with Unit Groups.

I tried this:
  • Unit Group - Add (Last created unit) to (Load 9 of (Custom value of (Casting unit)) in Grp_UnitHashArray[(Player number of (Owner of (Casting unit)))])
wich led to no units being in the designated group.

And i tried this:
  • Actions
    • Unit Group - Add (Last created unit) to Grp_TempUnitGroup
    • Hashtable - Save Handle OfGrp_TempUnitGroup as 9 of (Custom value of (Casting unit)) in Grp_UnitHashArray[(Player number of (Owner of (Casting unit)))]
wich stuffed all unit from the supposedly different groups (numbered by the custom value of the commander) into one single group.
before you ask: yes i tried clearing the temp group of old units before using it again...wich led to no units being in the groups at all -.-

i hope i could clarify what the problem is...otherwise ill have to post my complete WIP triggers :)

greetings

CodeBlack
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,206
You need to create a new group object for each mapping in the hash table.

The unit group type is a handle to a unit group object. It is not a complex container for units, that is what the unit group object it references is.

A new empty unit group can be created with the CreateGroup() function. This is what most GUI actions that return a group use internally. Remember to destroy it at the end of its useful life before losing track of all references to it.

What you probably want to do is check if a unit group mapping exists and if so then add units to that unit group. If the mapping does not exist create a new unit group, map it to the value and then add units to the unit group. Before removing the mapping one must also destroy the unit group using DestroyGroup(group).
 
Level 7
Joined
Sep 19, 2012
Messages
204
You need to create a new group object for each mapping in the hash table.

The unit group type is a handle to a unit group object. It is not a complex container for units, that is what the unit group object it references is.

Ooooh so this is what i was getting wrong. Thank you very much :) +rep

Edit: i just stumbled across a new problem. How do i reference a units custom value in custom script?
 
Last edited:
Level 7
Joined
Sep 19, 2012
Messages
204
Nvm i found out how to do it...now im stuck with removing a dying unit from this group again...
tried this:
  • Unit Group - Remove (Dying unit) from (Load 9 of (Custom value of (Dying unit)) in Grp_UnitHashArray[(Player number of (Owner of (Dying unit)))])
Value 9 is always the Group object of the Group, the custom value defines wich of the groups it belongs to (every player has 10 right now) and the player number defines the hashtable it is stored in, so i can use it for multiple players.

adding the units into the groups seems to be working but when i try to remove one nothing happens.

Edit: seems like the custom value of a unit dying gets reset to 0...is that possible? because when i try to put out the number of unit in the group of the dying unit it says 0, but when i manually put out the number of the specific group the unit is in it says 8 (wich is the full amount of units before death)
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,206
Edit: seems like the custom value of a unit dying gets reset to 0...is that possible? because when i try to put out the number of unit in the group of the dying unit it says 0, but when i manually put out the number of the specific group the unit is in it says 8 (wich is the full amount of units before death)
Yes it is quite possible. I know buffs and such get removed the instant a unit dies.

To confirm, I suggest a test map where you do not use an indexing system, explicitly force a unit to a known custom value and then on that unit's death read out the custom value associated with it.

Also to make it clear in case there was a misunderstanding, when one gets a unit group from a hashtable you are reading back the handle value to the unit group object. As such one must not destroy the unit group object if one wants to continue using it. This is a different pattern from your usual GUI unit group functions that create a new unit group every time they are run which must be destroyed after use to prevent leaks.
 
Level 7
Joined
Sep 19, 2012
Messages
204
Just tested...its NOT the value resetting on death.

I guess ill post my whole triggerwork then? or any more ideas what it could be?

as i already said...adding the units into the group is working fine, but removing them seems to behave strange/ not work at all
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
you dont need hashtables for this, you can shorten your code down by a lot. One solution would be to give every unit on your map its own custom value. when you cast the spell, every member of the army gets the same id as the caster and gets added to the unit group UnitGroup[id of the caster]. now, whenever a unit of the group dies, you can refer to its group by loading its custom value (UnitGroup[Custom value of triggering unit]) and you can simply remove it from the group. after that, you can check if the group is empty, and because you still have the custom value of your last unit that died, you can also refer to the leader (because he has the same custom value) and make him vulnerable and stuff... i can create example triggers if you want me to or if you didnt understand what i'm trying to say
 
Level 7
Joined
Sep 19, 2012
Messages
204
you dont need hashtables for this, you can shorten your code down by a lot. One solution would be to give every unit on your map its own custom value. when you cast the spell, every member of the army gets the same id as the caster and gets added to the unit group UnitGroup[id of the caster]. now, whenever a unit of the group dies, you can refer to its group by loading its custom value (UnitGroup[Custom value of triggering unit]) and you can simply remove it from the group. after that, you can check if the group is empty, and because you still have the custom value of your last unit that died, you can also refer to the leader (because he has the same custom value) and make him vulnerable and stuff... i can create example triggers if you want me to or if you didnt understand what i'm trying to say

example would be great. Im using the hastables for later features im planning as well....so...i need them anyways xD why not use them....

Edit:
i just read your post again and i think you misunderstand how the system is supposed to work. The ability i just a placeholder for later...later on the Commander will spawn and his squad will automatically be generated around him. and as i already said there are some more complex features i want to add later on...
 
Last edited:
Level 17
Joined
Mar 21, 2011
Messages
1,597
example would be great. Im using the hastables for later features im planning as well....so...i need them anyways xD why not use them....
but you get the same result, you get a unit group return via id. i really feel like there is no need for hashtables at all. you can still use them, however.

set INDEX = 0


unit enters map

if unit type equal to leader

set LEADER[INDEX] = triggering unit
make LEADER[INDEX] invulnerable
set LEADER[INDEX] movementspeed
set custom value of LEADER[INDEX] to INDEX
loop -> Create n Units and give them custom value of INDEX -> add those units to Group[INDEX]
set INDEX = INDEX + 1


unit dies

if unit type equal to spawn

set ID = custom value of triggering unit
remove triggering unit from Group[INDEX]
if Group[INDEX] is empty -> make LEADER[INDEX] vulnerable -> set LEADER[INDEX] movementspeed... and so on
 
Level 7
Joined
Sep 19, 2012
Messages
204
but you get the same result, you get a unit group return via id. i really feel like there is no need for hashtables at all. you can still use them, however.

set INDEX = 0


unit enters map

if unit type equal to leader

set LEADER[INDEX] = triggering unit
make LEADER[INDEX] invulnerable
set LEADER[INDEX] movementspeed
set custom value of LEADER[INDEX] to INDEX
loop -> Create n Units and give them custom value of INDEX -> add those units to Group[INDEX]
set INDEX = INDEX + 1


unit dies

if unit type equal to spawn

set ID = custom value of triggering unit
remove triggering unit from Group[INDEX]
if Group[INDEX] is empty -> make LEADER[INDEX] vulnerable -> set LEADER[INDEX] movementspeed... and so on

the problem is: as far as i understand this system i cant really use it to specify a units position in its formation. Later on the units will change their positions based on losses and selected formation. This way it would be hard to find out wich of the units is on position 1, wich on 2 etc...
i could run this parallel to the rest to get the morale thing going, but that would just make everything more complicated i guess...
thanks for the idea anyways :) I hope i can find a solution to the hashtable problem otherwise its going to be hard :/
 
Level 17
Joined
Mar 21, 2011
Messages
1,597
I hope i can find a solution to the hashtable problem otherwise its going to be hard :/
its actually pretty easy to do, i'll create an example when i have time

edit: i just took another look on your triggers. it is so confusing :D you have to explain what exactly you want to achieve. it looks like a mess right now, that is probably the reason why its not working
 
Last edited:
Level 7
Joined
Sep 19, 2012
Messages
204
its actually pretty easy to do, i'll create an example when i have time

edit: i just took another look on your triggers. it is so confusing :D you have to explain what exactly you want to achieve. it looks like a mess right now, that is probably the reason why its not working

As i said...its still an early prototype...thats why its a bit chaos xD
The aim is to create a system that gives every commander a group of units that follow him around...on top of that i decided to add some extra systems you can turn on and off as you wish. For example: commander is invulnerable as long as his group is alive, the group gets weaker the more people die etc. Etc.

If you want to know wich part does what i can add more docunentation and upload a new version.
 
Level 7
Joined
Sep 19, 2012
Messages
204
Sooo...
@GIMLI_2 Here you go....i just reworked some parts of the system and added a bit more documentation...i hope it helps Oo
since i switched over the Group react part to hashtables as well it doesnt work now....stuck on the same group problem i guess. Hope someone can solve it.
 

Attachments

  • Group Movement Proto.w3x
    45.2 MB · Views: 82
Level 17
Joined
Mar 21, 2011
Messages
1,597
"Scans through the Groups and puts out the first empty one"

what is this supposed to do?

i also dont get what you are trying to do in your initialization. you create 10 hashtables and 120 unit groups? why dont you create them when actually needed? or get rid of unit groups in general and save units into your hashtable.

best would be to create an example of a running in-game hashtable (at least how it should be), that would help me understand what you are trying to do
 
Level 7
Joined
Sep 19, 2012
Messages
204
"Scans through the Groups and puts out the first empty one"

what is this supposed to do?

i also dont get what you are trying to do in your initialization. you create 10 hashtables and 120 unit groups? why dont you create them when actually needed? or get rid of unit groups in general and save units into your hashtable.

best would be to create an example of a running in-game hashtable (at least how it should be), that would help me understand what you are trying to do

1. : This checks through the 10 Groups available and put out the number of the first empty one, so you can use it for the group about to be created.

2. : 12 hashtables to be exact...one for every player. then i sort the existing 10 groups per player into the tables... I just got around the custom code for create group this way....wanted to find out how to do this properly later...
The Groups are needed for some actions (like checking how many of the units are still alive). the units themselves get saved to the hashtables as well. 1-8 is units 9 is the unit group that holds them all and 10 is the commander.

Basically a commander gets spawned and then the create trigger adss him to the hastable before he spawns 3 rows of units around him and adds them to their respective hashtable and unit group wich is then saved to the hashtable as well. Then the other triggers can refer to all units in one group or to a specific single unit in the group just as i need it in this moment.

when you give the commander a move or attack order the group gets their move/ attack orders according to his destination and thus follow him around in formation.

While the group is alive the commander is invulnerable; this way i want to prevent commander sniping (without him you cant control the group anymore). When the group is empty this invulnerability gets removed (this is what is not working rn)

3. : i dont really know what you mean by "a running example" if i could make a running version of what i want to do, then i wouldnt have this problem :p

greetings

CodeBlack

EDIT: just noticed i can reduce it all down to one hastable (im stupid XD) but first i need to check how to properly create groups via custom commands etc...this doesnt solve my problem though
 
Last edited:
CodeBlack, if you accept vjass solution there is a proposal for you
JASS:
globals
    hashtable           hash=InitHashtable()
    unit                      temp_leader=null
endglobals
function Msg takes string s returns nothing
    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 15.00, s)
endfunction

function RegisterUnit_Loop takes nothing returns nothing
    call SaveUnitHandle(hash, GetHandleId(GetEnumUnit()), 2, temp_leader)//connect unit with its Leader
endfunction
function RegisterLeadersGroup takes unit leader, group g returns nothing
    local integer h_id=GetHandleId(leader)
    call SaveGroupHandle(hash, h_id, 0, g)
    call SaveInteger(hash, h_id, 1, CountUnitsInGroup(g))
    set temp_leader=leader
    call ForGroup(g, function RegisterUnit_Loop)
endfunction

//leader
function IsLeader takes unit u returns boolean
    return GetUnitTypeId(u)=='hkni' or GetUnitTypeId(u)=='eshd' //your leader's
endfunction

function GetLeadersGroup takes unit leader returns group
    return LoadGroupHandle(hash, GetHandleId(leader), 0)
endfunction

function GetLeadersGroupCount takes unit leader returns integer
    return LoadInteger(hash, GetHandleId(leader), 1)
endfunction


//unit
function IsUnitInLeaderGroup takes unit u returns boolean
    return HaveSavedHandle(hash, GetHandleId(u), 2)
endfunction

function GetUnitsLeader takes unit u returns unit
    return LoadUnitHandle(hash, GetHandleId(u), 2)
endfunction


function GroupMemberDies takes unit u returns nothing
    local unit leader=null
    local integer leaderHandleId=0
    local integer count=0

    if IsUnitInLeaderGroup(u) then
        set leader=GetUnitsLeader(u)
        set leaderHandleId=GetHandleId(leader)
        //update group count:
        set count=LoadInteger(hash, leaderHandleId, 1)
        set count=count-1
        call SaveInteger(hash, leaderHandleId, 1, count)
        call Msg("group count "+I2S(count))
        //clean:
        call RemoveSavedHandle(hash, GetHandleId(u), 2)
        if count==0 then
            call SetUnitInvulnerable(leader, false)
        endif
       
    elseif IsLeader(u) then
        call Msg("Leader dies")
   
        call FlushChildHashtable(hash, leaderHandleId)
    endif
    set leader=null
endfunction

  • createSquad
    • Events
      • Time - Elapsed game time is 2.00 seconds
    • Conditions
    • Actions
      • Set Leader = Leader Knight 0001 <gen>
      • Unit - Make Leader Invulnerable
      • Unit - Create 3 Footman for Player 2 (Blue) at (Position of Leader) facing 0.00 degrees
      • Set LeadersGroup = (Last created unit group)
      • -------- --- --------
      • Custom script: call RegisterLeadersGroup(udg_Leader, udg_LeadersGroup)
      • -------- --- --------
  • dies
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Custom script: call GroupMemberDies(GetTriggerUnit())
      • -------- --- --------
  • LeaderSmart
    • Events
      • Unit - A unit owned by Player 1 (Red) Is issued an order targeting a point
    • Conditions
      • (Issued order) Equal to (Order(smart))
    • Actions
      • Custom script: if IsLeader(GetOrderedUnit()) and GetLeadersGroupCount(GetOrderedUnit())>0 then
      • Set Leader = (Ordered unit)
      • Custom script: set udg_LeadersGroup=GetLeadersGroup(udg_Leader)
      • -------- --- --------
      • Unit Group - Pick every unit in LeadersGroup and do (Actions)
        • Loop - Actions
          • Unit - Order (Picked unit) to Move To (Target point of issued order)
      • -------- --- --------
      • -------- --- --------
      • Custom script: endif
As you see its using hadleId of a unit as hashtable parent key which is unique number for any object in warcraft.
 

Attachments

  • LeaderAndGroup.w3x
    19.4 KB · Views: 39
Level 7
Joined
Sep 19, 2012
Messages
204
CodeBlack, if you accept vjass solution there is a proposal for you
JASS:
globals
    hashtable           hash=InitHashtable()
    unit                      temp_leader=null
endglobals
function Msg takes string s returns nothing
    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 15.00, s)
endfunction

function RegisterUnit_Loop takes nothing returns nothing
    call SaveUnitHandle(hash, GetHandleId(GetEnumUnit()), 2, temp_leader)//connect unit with its Leader
endfunction
function RegisterLeadersGroup takes unit leader, group g returns nothing
    local integer h_id=GetHandleId(leader)
    call SaveGroupHandle(hash, h_id, 0, g)
    call SaveInteger(hash, h_id, 1, CountUnitsInGroup(g))
    set temp_leader=leader
    call ForGroup(g, function RegisterUnit_Loop)
endfunction

//leader
function IsLeader takes unit u returns boolean
    return GetUnitTypeId(u)=='hkni' or GetUnitTypeId(u)=='eshd' //your leader's
endfunction

function GetLeadersGroup takes unit leader returns group
    return LoadGroupHandle(hash, GetHandleId(leader), 0)
endfunction

function GetLeadersGroupCount takes unit leader returns integer
    return LoadInteger(hash, GetHandleId(leader), 1)
endfunction


//unit
function IsUnitInLeaderGroup takes unit u returns boolean
    return HaveSavedHandle(hash, GetHandleId(u), 2)
endfunction

function GetUnitsLeader takes unit u returns unit
    return LoadUnitHandle(hash, GetHandleId(u), 2)
endfunction


function GroupMemberDies takes unit u returns nothing
    local unit leader=null
    local integer leaderHandleId=0
    local integer count=0

    if IsUnitInLeaderGroup(u) then
        set leader=GetUnitsLeader(u)
        set leaderHandleId=GetHandleId(leader)
        //update group count:
        set count=LoadInteger(hash, leaderHandleId, 1)
        set count=count-1
        call SaveInteger(hash, leaderHandleId, 1, count)
        call Msg("group count "+I2S(count))
        //clean:
        call RemoveSavedHandle(hash, GetHandleId(u), 2)
        if count==0 then
            call SetUnitInvulnerable(leader, false)
        endif
      
    elseif IsLeader(u) then
        call Msg("Leader dies")
  
        call FlushChildHashtable(hash, leaderHandleId)
    endif
    set leader=null
endfunction

  • createSquad
    • Events
      • Time - Elapsed game time is 2.00 seconds
    • Conditions
    • Actions
      • Set Leader = Leader Knight 0001 <gen>
      • Unit - Make Leader Invulnerable
      • Unit - Create 3 Footman for Player 2 (Blue) at (Position of Leader) facing 0.00 degrees
      • Set LeadersGroup = (Last created unit group)
      • -------- --- --------
      • Custom script: call RegisterLeadersGroup(udg_Leader, udg_LeadersGroup)
      • -------- --- --------
  • dies
    • Events
      • Unit - A unit Dies
    • Conditions
    • Actions
      • Custom script: call GroupMemberDies(GetTriggerUnit())
      • -------- --- --------
  • LeaderSmart
    • Events
      • Unit - A unit owned by Player 1 (Red) Is issued an order targeting a point
    • Conditions
      • (Issued order) Equal to (Order(smart))
    • Actions
      • Custom script: if IsLeader(GetOrderedUnit()) and GetLeadersGroupCount(GetOrderedUnit())>0 then
      • Set Leader = (Ordered unit)
      • Custom script: set udg_LeadersGroup=GetLeadersGroup(udg_Leader)
      • -------- --- --------
      • Unit Group - Pick every unit in LeadersGroup and do (Actions)
        • Loop - Actions
          • Unit - Order (Picked unit) to Move To (Target point of issued order)
      • -------- --- --------
      • -------- --- --------
      • Custom script: endif
As you see its using hadleId of a unit as hashtable parent key which is unique number for any object in warcraft.

do i understand that correctly?
the jass part takes over the group handling and the rest runs by my normal triggers? (sorry im a noob at jass)
 
Level 7
Joined
Sep 19, 2012
Messages
204
mostly: yes,
having a Leader you have access to unit group (connected to this Leader) and to the group count.
You can "Pick all unit in group" and make desired formation etc,
also I gave an example when a Leader is issued "smart" order
Basically, you don't need your custom values of a unit / player numbers.

alright...i will try it out and see if i can implement it when i fail to do it with triggers; but my initial aim was to do it completely in triggers and not in jass.

tank you ^^
 
Status
Not open for further replies.
Top