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

[JASS] Hashtable Question

Status
Not open for further replies.
Level 17
Joined
Apr 27, 2008
Messages
2,455
No, you can use the same keys, as long it's not the same type.
But for the "widget" type, if you store a "widget", and then store a derivated type of "widget", such as "unit", then the previous stored "widget" will be overwritten to the "unit".
I have not tested but i suppose it's the same with "agent", and all other types which can be extended in other types (if there are more of them, check common.j and hashtable functions)

EDIT : Note that i have not tested what i claim, even for widget, i just remember a way to "typecast" widget <-> derivated type of widget, since the return bug is dead, and the lack of some response events (Get...)

But anyway, nothing forbids you to test it by yourself.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
There is a map attached as well.

JASS:
scope FS1 initializer Init_FlameShackles

    globals
        private constant integer ABILITY_ID  = 'A000'
        private constant integer   DUMMY_ID  = 'e000'
        private constant string   LIGHTNING  = "LEAS"

        private constant real RADIUS         = 300.00000
        private constant real DURATION       =   5.50000

        private constant real MAX_HEIGHT     = 200.00000
        private constant real HEIGHT_RATE    = 100.00000
        private constant real TIMER_INTERVAL =   0.03125

        private integer index = 0
        private integer array temp_this

        private hashtable hash = InitHashtable()
        private constant timer tim = CreateTimer()
    endglobals

    private struct flame extends array
        player owner
        unit dummy
        unit caster
        real cast_x
        real cast_y
        real dummy_z
        integer count

        implement Alloc

        static method filter takes player owner, unit u returns boolean
            return IsUnitEnemy(u,owner) and IsUnitType(u,UNIT_TYPE_GROUND)
        endmethod

        static method rise takes nothing returns nothing
            local thistype this
            local integer a = 0
            local integer b
            local lightning l
            local unit u
            local real x1
            local real y1
            local real z1
            local real x2
            local real y2
            local real z2

            loop
                exitwhen a == index
                set this = temp_this[a]

                set x1 = this.cast_x
                set y1 = this.cast_y
                set z1 = this.dummy_z
                if z1 < MAX_HEIGHT then
                    set z1 = z1 + MAX_HEIGHT * TIMER_INTERVAL/1.500
                    call SetUnitFlyHeight(this.dummy,z1,0.0)
                    set this.dummy_z = z1
                endif
                set b = 1
                loop
                    exitwhen b >= this.count
                    @set u = LoadUnitHandle(hash,this,b)@
                    set x2 = GetUnitX(u)
                    set y2 = GetUnitY(u)
                    set z2 = GetUnitFlyHeight(u)
                    @set l = LoadLightningHandle(hash,this,b)@
                    call MoveLightningEx(l,false,x1,y1,z1 + 48.,x2,y2,z2)
                    set b = b + 1
                endloop
                set u = null
                set l = null

                set a = a + 1
            endloop
        endmethod

        static method start takes unit caster, real x1, real y1 returns nothing
            local thistype this = thistype.allocate()
            local unit temp_u
            local lightning l
            local integer i = 1
            local real x2
            local real y2
            local real z2

            set temp_this[index] = this
            set this.owner = GetOwningPlayer(caster)
            set this.caster = caster
            set this.cast_x = x1
            set this.cast_y = y1
            set this.dummy_z = 0.0
            set this.dummy = CreateUnit(this.owner,DUMMY_ID,x1,y1,0)
            call GroupEnumUnitsInRange(bj_lastCreatedGroup,x1,y1,RADIUS,null)
            //set i = 1
            loop
                set temp_u = FirstOfGroup(bj_lastCreatedGroup)
                set this.count = i
                exitwhen temp_u == null
                if filter(this.owner,temp_u) then
                    set x2 = GetUnitX(temp_u)
                    set y2 = GetUnitY(temp_u)
                    set z2 = GetUnitFlyHeight(temp_u)
                    set l = AddLightningEx(LIGHTNING,false,x1,y1,0,x2,y2,z2)
                    @call SaveUnitHandle( hash , this , i , temp_u )@
                    @call SaveLightningHandle( hash , this , i , l )@
                    set i = i + 1
                endif
                call GroupRemoveUnit(bj_lastCreatedGroup,temp_u)
            endloop
            if index == 0 then
                call TimerStart(tim,TIMER_INTERVAL,true,function flame.rise)
            endif
            set index = index + 1
        endmethod
    endstruct

    private function FlameShackles_Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABILITY_ID then
            call flame.start(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        endif
        return false
    endfunction

    private function Init_FlameShackles takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,function FlameShackles_Conditions)
        set t = null
    endfunction

endscope
JASS:
scope FS2 initializer Init_FlameShackles

    globals
        private constant integer ABILITY_ID  = 'A001'
        private constant integer   DUMMY_ID  = 'e000'
        private constant string   LIGHTNING  = "LEAS"

        private constant real RADIUS         = 300.00000
        private constant real DURATION       =   5.50000

        private constant real MAX_HEIGHT     = 200.00000
        private constant real HEIGHT_RATE    = 100.00000
        private constant real TIMER_INTERVAL =   0.03125

        private integer index = 0
        private integer array temp_this

        private hashtable hash = InitHashtable()
        private constant timer tim = CreateTimer()
    endglobals

    private struct flame extends array
        player owner
        unit dummy
        unit caster
        real cast_x
        real cast_y
        real dummy_z
        integer count

        implement Alloc

        static method filter takes player owner, unit u returns boolean
            return IsUnitEnemy(u,owner) and IsUnitType(u,UNIT_TYPE_GROUND)
        endmethod

        static method rise takes nothing returns nothing
            local thistype this
            local integer a = 0
            local integer b
            local lightning l
            local unit u
            local real x1
            local real y1
            local real z1
            local real x2
            local real y2
            local real z2

            loop
                exitwhen a == index
                set this = temp_this[a]

                set x1 = this.cast_x
                set y1 = this.cast_y
                set z1 = this.dummy_z
                if z1 < MAX_HEIGHT then
                    set z1 = z1 + MAX_HEIGHT * TIMER_INTERVAL/1.500
                    call SetUnitFlyHeight(this.dummy,z1,0.0)
                    set this.dummy_z = z1
                endif
                set b = 1
                loop
                    exitwhen b >= this.count
                    @set u = LoadUnitHandle(hash,this,b)@
                    set x2 = GetUnitX(u)
                    set y2 = GetUnitY(u)
                    set z2 = GetUnitFlyHeight(u)
                    @set l = LoadLightningHandle(hash,this,b)@
                    call MoveLightningEx(l,false,x1,y1,z1 + 48.,x2,y2,z2)
                    set b = b + 1
                endloop
                set u = null
                set l = null

                set a = a + 1
            endloop
        endmethod

        static method start takes unit caster, real x1, real y1 returns nothing
            local thistype this = thistype.allocate()
            local unit temp_u
            local lightning l
            local integer i = 1
            local real x2
            local real y2
            local real z2

            set temp_this[index] = this
            set this.owner = GetOwningPlayer(caster)
            set this.caster = caster
            set this.cast_x = x1
            set this.cast_y = y1
            set this.dummy_z = 0.0
            set this.dummy = CreateUnit(this.owner,DUMMY_ID,x1,y1,0)
            call GroupEnumUnitsInRange(bj_lastCreatedGroup,x1,y1,RADIUS,null)
            //set i = 1
            loop
                set temp_u = FirstOfGroup(bj_lastCreatedGroup)
                set this.count = i
                exitwhen temp_u == null
                if filter(this.owner,temp_u) then
                    set x2 = GetUnitX(temp_u)
                    set y2 = GetUnitY(temp_u)
                    set z2 = GetUnitFlyHeight(temp_u)
                    set l = AddLightningEx(LIGHTNING,false,x1,y1,0,x2,y2,z2)
                    @call SaveLightningHandle( hash , this , i , l )@
                    @call SaveUnitHandle( hash , this , i , temp_u )@
                    set i = i + 1
                endif
                call GroupRemoveUnit(bj_lastCreatedGroup,temp_u)
            endloop
            if index == 0 then
                call TimerStart(tim,TIMER_INTERVAL,true,function flame.rise)
            endif
            set index = index + 1
        endmethod
    endstruct

    private function FlameShackles_Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABILITY_ID then
            call flame.start(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        endif
        return false
    endfunction

    private function Init_FlameShackles takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,function FlameShackles_Conditions)
        set t = null
    endfunction

endscope
JASS:
scope FS3 initializer Init_FlameShackles

    globals
        private constant integer ABILITY_ID  = 'A002'
        private constant integer   DUMMY_ID  = 'e000'
        private constant string   LIGHTNING  = "LEAS"

        private constant real RADIUS         = 300.00000
        private constant real DURATION       =   5.50000

        private constant real MAX_HEIGHT     = 200.00000
        private constant real HEIGHT_RATE    = 100.00000
        private constant real TIMER_INTERVAL =   0.03125

        private integer index = 0
        private integer array temp_this

        private hashtable hash = InitHashtable()
        private constant timer tim = CreateTimer()
    endglobals

    private struct flame extends array
        player owner
        unit dummy
        unit caster
        real cast_x
        real cast_y
        real dummy_z
        integer count

        implement Alloc

        static method filter takes player owner, unit u returns boolean
            return IsUnitEnemy(u,owner) and IsUnitType(u,UNIT_TYPE_GROUND)
        endmethod

        static method rise takes nothing returns nothing
            local thistype this
            local integer a = 0
            local integer b
            local lightning l
            local unit u
            local real x1
            local real y1
            local real z1
            local real x2
            local real y2
            local real z2

            loop
                exitwhen a == index
                set this = temp_this[a]

                set x1 = this.cast_x
                set y1 = this.cast_y
                set z1 = this.dummy_z
                if z1 < MAX_HEIGHT then
                    set z1 = z1 + MAX_HEIGHT * TIMER_INTERVAL/1.500
                    call SetUnitFlyHeight(this.dummy,z1,0.0)
                    set this.dummy_z = z1
                endif
                set b = 1
                loop
                    exitwhen b >= this.count
                    @set u = LoadUnitHandle(hash,this,b)@
                    set x2 = GetUnitX(u)
                    set y2 = GetUnitY(u)
                    set z2 = GetUnitFlyHeight(u)
                    @set l = LoadLightningHandle(hash,this,-b)@
                    call MoveLightningEx(l,false,x1,y1,z1 + 48.,x2,y2,z2)
                    set b = b + 1
                endloop
                set u = null
                set l = null

                set a = a + 1
            endloop
        endmethod

        static method start takes unit caster, real x1, real y1 returns nothing
            local thistype this = thistype.allocate()
            local unit temp_u
            local lightning l
            local integer i = 1
            local real x2
            local real y2
            local real z2

            set temp_this[index] = this
            set this.owner = GetOwningPlayer(caster)
            set this.caster = caster
            set this.cast_x = x1
            set this.cast_y = y1
            set this.dummy_z = 0.0
            set this.dummy = CreateUnit(this.owner,DUMMY_ID,x1,y1,0)
            call GroupEnumUnitsInRange(bj_lastCreatedGroup,x1,y1,RADIUS,null)
            //set i = 1
            loop
                set temp_u = FirstOfGroup(bj_lastCreatedGroup)
                set this.count = i
                exitwhen temp_u == null
                if filter(this.owner,temp_u) then
                    set x2 = GetUnitX(temp_u)
                    set y2 = GetUnitY(temp_u)
                    set z2 = GetUnitFlyHeight(temp_u)
                    set l = AddLightningEx(LIGHTNING,false,x1,y1,0,x2,y2,z2)
                    @call SaveLightningHandle( hash , this , -i , l )@
                    @call SaveUnitHandle( hash , this , i , temp_u )@
                    set i = i + 1
                endif
                call GroupRemoveUnit(bj_lastCreatedGroup,temp_u)
            endloop
            if index == 0 then
                call TimerStart(tim,TIMER_INTERVAL,true,function flame.rise)
            endif
            set index = index + 1
        endmethod
    endstruct

    private function FlameShackles_Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABILITY_ID then
            call flame.start(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        endif
        return false
    endfunction

    private function Init_FlameShackles takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,function FlameShackles_Conditions)
        set t = null
    endfunction

endscope
On the left is the lightning overwrite unit and the middle is unit overwrite lightning and on the right is working perfectly
126172-albums1967-picture57942.jpg
 

Attachments

  • fs2.w3x
    29 KB · Views: 72
Level 17
Joined
Apr 27, 2008
Messages
2,455
Ok, jass is just weirder once again.

This work as expected :

JASS:
scope Test initializer init

    globals
        private hashtable Hash_t
    endglobals

    private function init takes nothing returns nothing
        local integer i = 1
        local integer i_temp
        local real r = 2.0
        local real r_temp
        
        set Hash_t = InitHashtable()
        call SaveInteger(Hash_t,1,2,i)
        call SaveReal(Hash_t,1,2,r)
        call SaveTimerHandle(Hash_t,1,2,CreateTimer())
        set i_temp = LoadInteger(Hash_t,1,2)
        call BJDebugMsg(I2S(i_temp))
        set r_temp = LoadReal(Hash_t,1,2)
        call BJDebugMsg(R2S(r_temp))
        call BJDebugMsg(I2S(GetHandleId(LoadTimerHandle(Hash_t,1,2))))
    endfunction
    
endscope

I've done only this test, so what i assume is that "parent" types (types which aren't extended from an other one) can safely share the same keys, while you can't with the extended ones.

So you could use the same keys for :

- bool
- integer
- real
- string

And you couldn't for all the other ones, since they all are extended from "agent", where "agent" is extended from "handle".
Ofc i'm talking about types which can be stored in an hashtable, since you can't store "handle", nor "code".

More tests would be good, and i would say even needed, but this assumption would be right, and i don't want to do more anyway :p
 
This is my conclusion as per Troll-Brain's test and your test map...

the real, integer and handle were able to save without overlapping even with the same key used since the three are of different data types... while the unit handle overlapped with the lightning handle because they are of the same data type which in this case is handle...
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
This would make it annoying for flushing data. So basically we have parents, children and real/ boolean/ handle/ integer/ string... Honestly I can't see why they did that. Well, if they can l spend this much creativity on hashtables maybe they can take the time to ensure they don't crash the gui.
 
Level 9
Joined
Aug 26, 2010
Messages
573
I have tested that about a year ago because of an incident when I got 2 values in 1 cell without a bug but haven't found it useful. I think using this fact will ruin API or at least will make code unreadable often... And if u do u have to be very carefull about types - to look not to save 2+ handles in one slot. But it will be usefull with Table made by Bribe - now u know that u can store more info in one cell making it a bit similar to structs, like name of item(str), its price(real), struct with its stats(int), flag if it is protected(bool) and itself(item->handle).
And btw negative is same thing as adding a const in fact - it is just very-very big positive.
 
That is I.

Yeah, what Blizzard's coders did here is pretty smart on their part. This way, they would only have to declare hashtable classes using some template for integers, reals, strings, booleans and handles. (Maybe agents too)

At the same time, it's stupid because they didn't consider how data could be overwritten <.<

By the way, I don't think this is news D:
I read some old thread a few weeks ago about Troll-Brain or Bribe stating how cool the Hashtable casting thing is pretty cool. You would save a widget and load a unit using the same keys.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Well, on a second though that actually makes sense.
Because i already knew that you could "typecast" widget <-> unit/destructable/item.

I've just forgotten this new type introduced in the newest patches, "agent".

And i've never experienced it, since i mostly only store integers (struct instances, index of units, whatever ...)
And sometimes "boolean", ... , but it was always a "parent" type.

Anyway it seems a good reminder, if we see how people react to this information.

And btw this information is more for GUI than (v)Jass, at least once you're experienced with structs, indexers and such (v)Jass shit.
 
Status
Not open for further replies.
Top