• 🏆 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!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

Adding regions to hashtables based on name

Level 3
Joined
Feb 25, 2013
Messages
16
I've run into some problems with regions and hashtables...

What I'm trying to achieve:
The map I'm creating has 4 regions per player. These are manually created via the World Editor and named "Player0n, player0s, player0e, player0w" and so on.
To prevent all the manual labour I want to access the udg_ for each region, and add it to a hashtable.

My understanding is that I need the handle (an integer afaik), so I build the different region names and use StringHash(regionName).

However, I'm getting syntax errors on every SaveRectHandle.

Is what I'm trying to achieve impossible?

JASS:
function Trig_AddRegions_Actions takes nothing returns nothing   
    local hashtable rgnTable = InitHashtable()
    local string rgnPrefix = "gg_rct_Player"
    local string rgnName = rgnPrefix
    local integer i = 1   
    loop
        set rgnName = rgnPrefix + I2S(i)
        call SaveRectHandle(rgnTable, i, 0, StringHash(rgnName + "n"))
        call SaveRectHandle(rgnTable, i, 1, StringHash(rgnName + "s"))
        call SaveRectHandle(rgnTable, i, 2, StringHash(rgnName + "e"))
        call SaveRectHandle(rgnTable, i, 3, StringHash(rgnName + "w"))       
        exitwhen i > CountPlayersInForceBJ(GetPlayersByMapControl(MAP_CONTROL_USER))   
        set i = i + 1
    endloop   
endfunction

//===========================================================================
function InitTrig_AddRegions takes nothing returns nothing
    local trigger gg_trg_AddRegions = CreateTrigger(  )
    call TriggerRegisterPlayerChatEvent(gg_trg_AddRegions, Player(0), "TEST", true)
    call TriggerAddAction( gg_trg_AddRegions, function Trig_AddRegions_Actions )
endfunction
 
Level 41
Joined
Feb 27, 2007
Messages
5,222
StringHash is a function that returns a unique integer for a given string input. Its intended use is for the two Parent and Child keys for the hashtable (think of them as X,Y coordinates of the data you’re saving), because the hashtable does not directly support using strings for these keys. You have put i and 0-3 for these keys.

The final argument for any hashtable save function is the object you wish to save which you are not giving (you’ve put a string there where it expects a rect handle). You cannot do what you want to do with variable names and a prefix because there is no way within the wc3 engine to convert a string directly into the name of a variable/object for use. Other languages may have this but not JASS (I believe not Lua either?). Your options are to hardcode in every single region for each player or to store them into some sort of array you loop over.

Technically there is a way to do what you are trying by using a hashtable (can take two string inputs and return an object), but that only works once you have already built the hashtable… which is what you’re trying to do here. Can’t use the hashtable for that until you’ve made the hashtable. vJASS textmacroes are another way you could make this work and be simpler but if you’re not familiar with those it may not be worth the effort.

The simplest-code solution is probably not to use pregenerated Regions but instead to create the 4 rects per player at map init using some math and/or their starting positions. Depends on your map, honestly.
 
Level 3
Joined
Feb 25, 2013
Messages
16
StringHash is a function that returns a unique integer for a given string input. Its intended use is for the two Parent and Child keys for the hashtable (think of them as X,Y coordinates of the data you’re saving), because the hashtable does not directly support using strings for these keys. You have put i and 0-3 for these keys.

The final argument for any hashtable save function is the object you wish to save which you are not giving (you’ve put a string there where it expects a rect handle). You cannot do what you want to do with variable names and a prefix because there is no way within the wc3 engine to convert a string directly into the name of a variable/object for use. Other languages may have this but not JASS (I believe not Lua either?). Your options are to hardcode in every single region for each player or to store them into some sort of array you loop over.

Technically there is a way to do what you are trying by using a hashtable (can take two string inputs and return an object), but that only works once you have already built the hashtable… which is what you’re trying to do here. Can’t use the hashtable for that until you’ve made the hashtable. vJASS textmacroes are another way you could make this work and be simpler but if you’re not familiar with those it may not be worth the effort.

The simplest-code solution is probably not to use pregenerated Regions but instead to create the 4 rects per player at map init using some math and/or their starting positions. Depends on your map, honestly.
That makes sense. I'll try to generate it with code then.

Thanks for the response :)
 
You can technically arbitrarily grab locations at random using hashtable SaveFogState/ConvertFogState type of stuff with handle ids ranging from 0x100000 and up, and iterate some thousands of times and checking if LoadRect will return non-null, but that just greps all the rects without you knowing their names. So coding is definitely the best way to avoid all that manual duplicated effort
 
Level 3
Joined
Feb 25, 2013
Messages
16
You can technically arbitrarily grab locations at random using hashtable SaveFogState/ConvertFogState type of stuff with handle ids ranging from 0x100000 and up, and iterate some thousands of times and checking if LoadRect will return non-null, but that just greps all the rects without you knowing their names. So coding is definitely the best way to avoid all that manual duplicated effort
The map is a "Storm the House"-like map, so the primary unit is completely static.


This made it logical to just have a global offset and spawn units in NESW based on the offset.

What I've made so far atleast works and does what I want it to. I'll work on adding rects and making it scalable later :)
It doesn't implement hashtable yet, but I wanted to get the Locations correct first.

JASS:
function GenerateRegions takes nothing returns nothing
    local unit u = GetEnumUnit()
    local location uLoc = GetUnitLoc(u)

    local location n = Location(GetLocationX(uLoc),GetLocationY(uLoc)+udg_PlayerRgnOffset)
    local location e = Location(GetLocationX(uLoc)+udg_PlayerRgnOffset,GetLocationY(uLoc))
    local location s = Location(GetLocationX(uLoc),GetLocationY(uLoc)-udg_PlayerRgnOffset)
    local location w = Location(GetLocationX(uLoc)-udg_PlayerRgnOffset,GetLocationY(uLoc))

    call CreateNUnitsAtLocFacingLocBJ( 1, 'hfoo', Player(PLAYER_NEUTRAL_AGGRESSIVE), n, GetUnitLoc(GetEnumUnit()) )
    call CreateNUnitsAtLocFacingLocBJ( 1, 'hfoo', Player(PLAYER_NEUTRAL_AGGRESSIVE), e, GetUnitLoc(GetEnumUnit()) )
    call CreateNUnitsAtLocFacingLocBJ( 1, 'hfoo', Player(PLAYER_NEUTRAL_AGGRESSIVE), s, GetUnitLoc(GetEnumUnit()) )
    call CreateNUnitsAtLocFacingLocBJ( 1, 'hfoo', Player(PLAYER_NEUTRAL_AGGRESSIVE), w, GetUnitLoc(GetEnumUnit()) )    
endfunction

function Trig_AddRegions_Actions takes nothing returns nothing    
    call ForGroupBJ( GetUnitsOfTypeIdAll(udg_CopterType), function GenerateRegions )
endfunction

//===========================================================================
function InitTrig_AddRegions takes nothing returns nothing
    local trigger gg_trg_AddRegions = CreateTrigger(  )
call TriggerRegisterPlayerChatEvent(gg_trg_AddRegions, Player(0), "TEST", true)
    call TriggerAddAction( gg_trg_AddRegions, function Trig_AddRegions_Actions )
endfunction

I basically just needed a nudge to do what's right ;)
 
Last edited:
Level 3
Joined
Feb 25, 2013
Messages
16
Just wanted to update with my solution that works for this purpose - aka generating rects with code and adding to hashtable.

The AdjustLocation is a bit funky imo, but it was the only way I could think of accounting for compass directions.

JASS:
globals
    trigger gg_trg_addRegions
    hashtable gg_tbl_playerRegions = InitHashtable()
endglobals

function AdjustLocation takes location loc, integer i returns location
    local integer array multiX
    local integer array multiY
    set multiX[0] = 0
    set multiX[1] = 1
    set multiX[2] = 0
    set multiX[3] = -1

    set multiY[0] = 1
    set multiY[1] = 0
    set multiY[2] = -1
    set multiY[3] = 0

    return Location(GetLocationX(loc)+(udg_PlayerRgnOffset * multiX[i]),GetLocationY(loc)+(udg_PlayerRgnOffset * multiY[i]))
endfunction


function GenerateSpawnRegions takes nothing returns nothing
    local unit u = GetEnumUnit()    
    local rect array rects_Spawn
    local real rectRadius = 300.0
    local location uLoc = GetUnitLoc(u)

    local integer count = 0
    local location center
    local location min
    local location max
    loop
        exitwhen count > 3
        set center = AdjustLocation(uLoc, count)
        set min = Location(GetLocationX(center)-rectRadius,GetLocationY(center)-rectRadius)
        set max = Location(GetLocationX(center)+rectRadius,GetLocationY(center)+rectRadius)
        call SaveRectHandle(gg_tbl_playerRegions, GetHandleId(GetOwningPlayer(u)), count, RectFromLoc(min, max))    
        set count = count + 1
    endloop        
endfunction



function Trig_AddRegions_Actions takes nothing returns nothing    
    call ForGroupBJ( GetUnitsOfTypeIdAll(udg_CopterType), function GenerateSpawnRegions )
endfunction

//===========================================================================
function InitTrig_AddRegions takes nothing returns nothing
    set gg_trg_AddRegions = CreateTrigger(  )    
    call TriggerAddAction( gg_trg_AddRegions, function Trig_AddRegions_Actions )
endfunction
 
Top