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

[vJASS] Logic Challenge or Impossible?

Status
Not open for further replies.
Level 10
Joined
Jun 17, 2014
Messages
236
Iam trying to make a feature on my system that use hashtable, but i stuck when i got a problem,
is this possible but hard, or impossible?, i've been trying much hard to solve.

here is :
JASS:
        static method getDataFromUnit takes unit un returns integer
            return LoadInteger(ht,2,???) // don't know :(
        endmethod

cause i want to make its use like this :
JASS:
library burntilt
...
    function onCast takes nothing returns boolean
        call DoT.create(...)
        call TimerStart(...,function onPeriod)
    endfunction
 
    function onPeriod takes nothing return nothing
        local DoT i = 0
        set i = DoT.getDataFromUnit(p)
        loop
            exitwhen HasBuff(getTargetFromData(i),'buff')
            set i = DoT.getDataFromUnit(p)
        endloop
        if IsUnitStunned(p) then
            set i.damage = i.damage*2
            set i.duration = 3
            ...
        else
            i.terminate()
...
endlibrary

the whole code :
JASS:
library DamageoverInterval /*

Created by userid907
*/requires SpeedChange,/* to manage buff-with DoT

Constructor :
static method create takes unit source,unit target, real damage, real duration, real interval, integer buffid,attacktype atk returns thistype
    - source is damaging unit, and target is damaged unit, damage is total damage dealed, interval is interval between damage,
      buffid is buff id of the DoT but you can make it with no buff too, atk is the attacktype of damage dealed

Destructor :
method terminate takes nothing returns nothing
    - finish the DoT instantly or set the duration to 0.

Available member for your own use :
        unit source
        unit target
        real duration
        real interval
        real damage
        integer buffid
        real counter
        attacktype atk
     */
// CONFIGURATION
    globals
        private constant player DUMMYOWNER = Player( 15 ) // the owner of DUMMYID unit
        private constant real FPS = 0.0312500 // higher value = worth fps, weird effect move, lower value = worst fps, great effect move
        private constant boolean TESTMODE = false // IF TRUE THE DEBUG MESSAGE WILL COME OUT
        private constant boolean TESTMODELOOP = false
        private hashtable ht
    endglobals
// ENDCONFIGURATION
    struct DoT
        unit source
        unit target
        real duration
        real interval
        real damage
        integer buffid
        real counter
        attacktype atk
        private real actduration
        private static integer dindex
        private static timer period
        private static thistype array datas
        method destroy takes nothing returns nothing
            //Wash leaks
            call EndSpeedModifById( .target, .buffid )
            set .target = null
            set .atk = null
            set .source = null
            call RemoveSavedHandle(ht,this,1)
            call RemoveSavedInteger(ht,this,2)
            call RemoveSavedHandle(ht,this,0)
            if dindex == -1 then
                call PauseTimer( period )
            endif
            call this.deallocate( )
        endmethod
        method terminate takes nothing returns nothing
            set this.duration = 0.
        endmethod

        static method getSourceFromData takes integer rdata returns unit
            if LoadUnitHandle(ht,0,rdata) != null then
                return LoadUnitHandle(ht,0,rdata)
            endif
            debug call BJDebugMsg(SCOPE_PREFIX + " : getUnitFromData returned null")
            return null
        endmethod
 
        static method getTargetFromData takes integer rdata returns unit
            if LoadUnitHandle(ht,1,rdata) != null then
                return LoadUnitHandle(ht,1,rdata)
            endif
            debug call BJDebugMsg(SCOPE_PREFIX + " : getUnitFromData returned null")
            return null
        endmethod
 
        static method getDataFromUnit takes unit un returns thistype
            return LoadInteger(ht,2,???) // don't know :(
        endmethod
 
        static method isDataAttachedToUnit takes unit t,integer data returns boolean
            local thistype this = data
            return t == this.source or t == this.target
        endmethod
 
        static method create takes unit source,unit target, real damage, real duration, real interval, integer buffid,attacktype atk returns thistype
            local thistype this
            set this = thistype.allocate( )
            set .atk = atk
            set .source = source
            set .target = target
            if HaveSavedInteger(ht,0,this) or HaveSavedHandle(ht,1,this) or HaveSavedInteger(ht,2,this) then
                call RemoveSavedInteger(ht,2,this)
                call RemoveSavedHandle(ht,0,this)
                call RemoveSavedHandle(ht,1,this)
            endif
            call SaveUnitHandle(ht,0,this,.source)
            call SaveUnitHandle(ht,1,this,.target)
            call SaveInteger(ht,2,this,this)
            call BJDebugMsg(GetUnitName(LoadUnitHandle(ht,0,this)))
            call BJDebugMsg(GetUnitName(LoadUnitHandle(ht,1,this)))
            call BJDebugMsg(I2S(LoadInteger(ht,2,this)))
            set .duration = duration
            set .interval = interval
            set .damage = damage
            set .buffid = buffid
            set .actduration = .duration
            set .counter = .interval
            call ChangeUnitSpeed2( .target, 1.0 , .duration , false , buffid, false )
            if TESTMODE then
                call BJDebugMsg( "Created DoT on : " + GetUnitName( target ) + " With : " + R2S( damage ) + " damage " + R2S( interval ) + " interval " + R2S( .damage / ( .actduration / .interval ) ) + " damage per interval and " + I2S( buffid ) + " as buffid" )
            endif
            set dindex = dindex + 1
            set datas[dindex] = this
            if dindex == 0 then
                call TimerStart( period, FPS, true, function thistype.periodic )
            endif
            return this
        endmethod
        static method periodic takes nothing returns nothing
            local integer i = 0
            local thistype this
            loop
                exitwhen i > dindex
                set this = datas[i]
                if .counter <= 0. then
                    set .counter = .interval
                    call BJDebugMsg("this : " +I2S(this))
                    call BJDebugMsg("gdfu : " +I2S(DoT.getDataFromUnit(.source)))
                    call BJDebugMsg("getSourceFromData : " +GetUnitName(DoT.getSourceFromData(DoT.getDataFromUnit(.source))))
                    call BJDebugMsg("getTargetFromData : " +GetUnitName(DoT.getTargetFromData(DoT.getDataFromUnit(.target))))
                    if TESTMODE then
                    call BJDebugMsg("reached counter 0")
                    endif
                    if .damage > 0 then
                        call UnitDamageTarget(.source,.target,.damage / ( .actduration / .interval ),true,false,.atk,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                        if TESTMODE then
                        call BJDebugMsg("damage > 0")
                        endif
                    else
                        call SetUnitState(.target,UNIT_STATE_LIFE,GetUnitState(.target,UNIT_STATE_LIFE) + (.damage / ( .actduration / .interval )*-1))
                        if TESTMODE then
                        call BJDebugMsg("damage < 0")
                        endif
                    endif
                else
                    set .counter = .counter - FPS
                endif
            if TESTMODELOOP then
                call BJDebugMsg(".counter = " + R2S(.counter) + " .duration = " + R2S(.duration))
            endif
                set .duration = .duration - FPS
                if .duration <= 0 then
                    set datas[i] = datas[dindex]
                    set i = i - 1
                    set dindex = dindex - 1
                    call this.destroy( )
                endif
                set i = i + 1
            endloop
        endmethod
        static method onInit takes nothing returns nothing
            set dindex = -1
            set period = CreateTimer( )
            set ht = InitHashtable()
        endmethod
    endstruct
endlibrary

i know getDataFromUnit() will return not exact value when the unit is affected with 2 or more DoT.
but i know how to solve it
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,013
native LoadInteger takes hashtable table, integer parentKey, integer childKey returns integer
Generally speaking you can choose whatever you want for parentKey and childKey; it's up to you to decide how you want to organize it. I use parentKey to show what I'm storing and the HandleId of some object as the childKey.
JASS:
globals
    private integer STRUCT_A_KEY = 0 //could be any integer
    private integer STRUCT_B_KEY = 1
endglobals

//...

local structa TempA
local structb TempB
call SaveInteger(MyHashTable, STRUCT_A_KEY, GetHandleId(someunit), TempA)
call SaveInteger(MyHashTable, STRUCT_B_KEY, GetHandleId(someunit), TempB)
 
Level 10
Joined
Jun 17, 2014
Messages
236
If you want bind data to the unit use GetHandleId(u) as key.

look at "cause i want to make its use like this " on main thread, that's what i want.
GetHandleId(u) will not access the this on external use, i've try it before.
let me say it again :
i know getDataFromUnit() will return not exact value when the unit is affected with 2 or more DoT.
but i know how to solve it

native LoadInteger takes hashtable table, integer parentKey, integer childKey returns integer
Generally speaking you can choose whatever you want for parentKey and childKey; it's up to you to decide how you want to organize it. I use parentKey to show what I'm storing and the HandleId of some object as the childKey.
JASS:
globals
    private integer STRUCT_A_KEY = 0 //could be any integer
    private integer STRUCT_B_KEY = 1
endglobals

//...

local structa TempA
local structb TempB
call SaveInteger(MyHashTable, STRUCT_A_KEY, GetHandleId(someunit), TempA)
call SaveInteger(MyHashTable, STRUCT_B_KEY, GetHandleId(someunit), TempB)

Don't know what you mean

You need a different approach.
The DoT (assuming it is damage over time) can be on a unit multiple times.
So you need a list of all the DoTs on a unit.
Then you loop through that list and do whatever you want.

how to do it?
i know getDataFromUnit() will return not exact value when the unit is affected with 2 or more DoT.
but i know how to solve it
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
If you insist:
JASS:
function foo takes Unit u returns nothing
    local LinkedListIterator it = u.getBuffs().newIterator()
    local DamageOverTime dot
    local Damage d
   
    loop
        exitwhen not it.hasNext()
        set dot = it.current
       
        if (dot.getType() == DamageOverTime.typeid) then
            set d = Damage.create(dot.source, u, .damage[dot])
            set d.attackType = AttackType.NORMAL
            set d.damageType = DamageType.PHYSICAL
            set d.hitType = HitType.HIDDEN
            call d.apply()
        endif
    endloop
   
    call it.clear()
endfunction
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Not necessarily.

You need some sort of collection of buffs for each unit.
If you do that with a normal array (with a static limit to the number of buffs per unit) or use a dynamically sized array doesnt really matter.
However, apart from the normal array (with that annoying limit on it), the best option that I know of is a linked list.
Linked list implementations in JASS are extremely easy.
You can use another sort of dynamically sized array like an arraylist, but how I can see that arraylists would work, it is not going to be really nice.

Another approach would be to have a timer per buff instance.
That way, you do not need lists for this particular feature.
You just need a pointer from that timer to the buff instance.
The buff instance then has to contain a reference to the unit and the data to do whatever it is required to do.
This approach might be much better performance wise, logic wise and it is easy to code the basics.
To not have any flaws though, is not so simple to make.
 
Status
Not open for further replies.
Top