• 🏆 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] Struct onDestroy method not being called

Status
Not open for further replies.
As the title says, I have a struct with an onDestroy method and everything works up until I want to remove the struct. The onDestroy method doesn't get called. I filled it with BJDebugMsgs to check.

Here is the code. It's just a little test I was doing to make projectiles using structs.

JASS:
//************************************************************
//
// For more JASS scripts, visit us at [url=http://www.wc3JASS.com]WC3Jass.com :: Index[/url]
//
//============================================================
 
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction
 
// ===========================
function LocalVars takes nothing returns gamecache
    return InitGameCache("jasslocalvars.w3v")
endfunction
 
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction
 
function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes  handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction
 
function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
 
function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

//============================================================================

globals
    real array STRENGTH
    real array AGILITY
    constant real ARROW_BASESPEED = 10.0
endglobals

struct arrow
    unit owner
    unit u
    real speed
    trigger onHit
    timer t
    
    static method create takes unit owner returns arrow
        local arrow data = arrow.allocate()
        set data.owner = owner
        set data.u = CreateUnit( GetOwningPlayer( owner ), 'arro', GetUnitX( owner ), GetUnitY( owner ), GetUnitFacing( owner ) )
        set data.speed = ARROW_BASESPEED //TO EDIT
 
        set data.onHit = CreateTrigger()
        call TriggerRegisterUnitInRange( data.onHit, data.u, 64, null )
        call TriggerAddAction( data.onHit, function arrow.onHitFunc )
        call SetHandleInt( data.onHit, "arrowstruct", data )
        
        set data.t = CreateTimer()
        call TimerStart( data.t, 0.03, true, function arrow.doArrowMove )
        call SetHandleInt( data.t, "arrowstruct", data )
        return data
    endmethod
    
    method onDestroy takes nothing returns nothing
        call KillUnit( .u )
        debug call BJDebugMsg( "Arrow killed" )
        call RemoveUnit( .u )
        debug call BJDebugMsg( "Arrow Removed" )
        call PauseTimer( .t )
        debug call BJDebugMsg( "Move timer paused" )
        call FlushHandleLocals( .t )
        debug call BJDebugMsg( "Timer Locals flushed" )
        call FlushHandleLocals( .onHit )
        debug call BJDebugMsg( "onHit Trigger Locals Flushed" )
        call DestroyTrigger( .onHit )
        debug call BJDebugMsg( "Trigger Destroyed" )
        call DestroyTimer( .t )
        debug call BJDebugMsg( "Timer Destroyed" )
        set .u = null
        set .t = null
        set .onHit = null
    endmethod
    
    static method onHitFunc takes nothing returns nothing
        local unit p = GetTriggerUnit()
        local arrow ar = GetHandleInt( GetTriggeringTrigger(), "arrowstruct" )
        debug call BJDebugMsg( "An arrow hit!" )
        if not ( GetOwningPlayer( ar.u ) == GetOwningPlayer( p ) ) then
            call KillUnit( p )
            debug call BJDebugMsg( "Unit killed" )
            call ar.destroy()
            debug call BJDebugMsg( "Destroy function called" )
        endif
        set ar = 0
        set p = null
    endmethod
    
    static method doArrowMove takes nothing returns nothing
        local arrow a = GetHandleInt( GetExpiredTimer(), "arrowstruct" )
        local real x = GetUnitX( a.u ) + a.speed * Cos( GetUnitFacing( a.u ) * bj_DEGTORAD )
        local real y = GetUnitY( a.u ) + a.speed * Sin( GetUnitFacing( a.u ) * bj_DEGTORAD )
        call SetUnitX( a.u, x )
        call SetUnitY( a.u, y )
    endmethod
endstruct

The trigger which creates the struct:

JASS:
function Trig_CreateArrow_Actions takes nothing returns nothing
    local arrow a
    if GetSpellAbilityId() == 'fire' then
        set a = arrow.create( GetSpellAbilityUnit() )
    endif
endfunction

//===========================================================================
function InitTrig_CreateArrow takes nothing returns nothing
    set gg_trg_CreateArrow = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_CreateArrow, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( gg_trg_CreateArrow, function Trig_CreateArrow_Actions )
endfunction

The map is also attached, if that is easier.
 

Attachments

  • Archers.w3x
    23.3 KB · Views: 55
Level 17
Joined
Apr 27, 2008
Messages
2,455
I'm not accessing any handle vars after i flush them

static method onHitFunc said:
local arrow ar = GetHandleInt( GetTriggeringTrigger(), "arrowstruct" )

JASS:
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction

JASS:
function LocalVars takes nothing returns gamecache
    return InitGameCache("jasslocalvars.w3v")
endfunction
 
Level 12
Joined
Apr 27, 2008
Messages
1,228
Troll-Brain is quite correct.
If you init a new game cache each time, you will lose the old one.
And
JASS:
function LocalVars takes nothing returns gamecache
    return InitGameCache("jasslocalvars.w3v")
endfunction
is called every time you use Handle Variables.
Solution:
JASS:
function LocalVars takes nothing returns gamecache
    return udg_HandleGameCache
endfunction
And add to a trigger that runs at the initialization:
JASS:
    call FlushGameCache(InitGameCache("HadnleCache.x"))
    set udg_HandleGameCache=InitGameCache("HandleCache.x")
(examples)
Troll-Brain already linked to the proper thread. I am just clearifing.
@Troll-Brain ar is always 0(except when it is stored) because when you init a new game cache, the stored integer is not in that new Game cache.
 
Ok, I changed it but it now gives a message saying that the struct is null:

JASS:
globals
    gamecache HandleVars = InitGameCache( "handlevars.w3v" )
    real array STRENGTH
    real array AGILITY
    constant real ARROW_BASESPEED = 10.0
endglobals

//************************************************************
//
// For more JASS scripts, visit us at [url=http://www.wc3JASS.com]WC3Jass.com :: Index[/url]
//
//============================================================
 
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction
 
// ===========================
function LocalVars takes nothing returns gamecache
    return HandleVars
endfunction
 
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction
 
function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes  handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction
 
function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
 
function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

//============================================================================

struct arrow
    unit owner
    unit u
    real speed
    trigger onHit
    timer t
    
    static method create takes unit owner returns arrow
        local arrow data = arrow.allocate()
        set data.owner = owner
        set data.u = CreateUnit( GetOwningPlayer( owner ), 'arro', GetUnitX( owner ), GetUnitY( owner ), GetUnitFacing( owner ) )
        set data.speed = ARROW_BASESPEED //TO EDIT
 
        set data.onHit = CreateTrigger()
        call TriggerRegisterUnitInRange( data.onHit, data.u, 64, null )
        call TriggerAddAction( data.onHit, function arrow.onHitFunc )
        call SetHandleInt( data.onHit, "arrowstruct", data )
        
        set data.t = CreateTimer()
        call TimerStart( data.t, 0.03, true, function arrow.doArrowMove )
        call SetHandleInt( data.t, "arrowstruct", data )
        return data
    endmethod
    
    method onDestroy takes nothing returns nothing
        call RemoveUnit( .u )
        debug call BJDebugMsg( "Arrow Removed" )
        call PauseTimer( .t )
        debug call BJDebugMsg( "Move timer paused" )
        call FlushHandleLocals( .t )
        debug call BJDebugMsg( "Timer Locals flushed" )
        call FlushHandleLocals( .onHit )
        debug call BJDebugMsg( "onHit Trigger Locals Flushed" )
        call DestroyTrigger( .onHit )
        debug call BJDebugMsg( "Trigger Destroyed" )
        call DestroyTimer( .t )
        debug call BJDebugMsg( "Timer Destroyed" )
        set .u = null
        set .t = null
        set .onHit = null
    endmethod
    
    static method onHitFunc takes nothing returns nothing
        local unit p = GetTriggerUnit()
        local arrow ar = GetHandleInt( GetTriggeringTrigger(), "arrowstruct" )
        debug call BJDebugMsg( "An arrow hit!" )
        if ( GetOwningPlayer( ar.u ) != GetOwningPlayer( p ) ) then
            call KillUnit( p )
            debug call BJDebugMsg( "Unit killed" )
            call ar.destroy()
            debug call BJDebugMsg( "Destroy function called" )
        endif
        set p = null
    endmethod
    
    static method doArrowMove takes nothing returns nothing
        local arrow a = GetHandleInt( GetExpiredTimer(), "arrowstruct" )
        local real x = GetUnitX( a.u ) + a.speed * Cos( GetUnitFacing( a.u ) * bj_DEGTORAD )
        local real y = GetUnitY( a.u ) + a.speed * Sin( GetUnitFacing( a.u ) * bj_DEGTORAD )
        call SetUnitX( a.u, x )
        call SetUnitY( a.u, y )
    endmethod
endstruct
 

Attachments

  • des.JPG
    des.JPG
    233.3 KB · Views: 119
Level 11
Joined
Apr 6, 2008
Messages
760
Question: why are u using handle vars when there are much better options like CSData HSAS ABC ABCT are just few examples


also this is using a dynamic trigger which is very bad coz it leaks very much. i would recomend everytime u move the arrow to check if there is a near by unit like

JASS:
static method doArrowMove takes nothing returns nothing
        local arrow a = GetHandleInt( GetExpiredTimer(), "arrowstruct" )
        local real x = GetUnitX( a.u ) + a.speed * Cos( GetUnitFacing( a.u ) * bj_DEGTORAD )
        local real y = GetUnitY( a.u ) + a.speed * Sin( GetUnitFacing( a.u ) * bj_DEGTORAD )
        local group g = CreateGroup()
        local unit u
        call SetUnitX( a.u, x )
        call SetUnitY( a.u, y )

        call GroupEnumUnitsInRange(g,x,y,Aoe,Filter)
        set u = FirstOfGroup(g)
        
        if u != null then
              //dmg and stuff u wanna do
              call a.destroy
              set u = null
        endif

        call DestroyGroup(g)
        set g = null
    endmethod
 
Level 11
Joined
Apr 6, 2008
Messages
760
well i did clean up ur "spell" bit was bored ^^

JASS:
scope Arrow initializer Init

globals
    private constant integer Abil_id = 'A000' //or what ability rawcode is :P
    private constant real ARROW_BASESPEED = 30.//'RawCode'//
endglobals

private struct arrow
    unit owner
    unit u
    
    real cos //we have these since Cos and Sin is a heavy math function
    real sin 
    real speed
    
    timer t

    static method create takes nothing returns arrow
        local arrow data = arrow.allocate()
        local real facing
        set data.owner = GetTriggerUnit()
        set facing = GetUnitFacing(data.owner)*(3.14159/180.0) // RADTODEG
        set data.cos = Cos(facing)
        set data.sin = Sin(facing)
        set data.u = CreateUnit( GetOwningPlayer( data.owner ), 'arro', GetUnitX( data.owner ), GetUnitY( data.owner ),facing*(180./3.14159) )
        set data.speed = ARROW_BASESPEED //TO EDIT
        
        
        set data.t = CreateTimer()
        call TimerStart( data.t, 0.035, true, function arrow.doArrowMove )
        call SetCSData(data.t,data) //this is CSData by vex (better,easier and more secure to use then handle vars)
        return data
    endmethod

    
    static method doArrowMove takes nothing returns nothing
        local arrow a = GetCSData(GetExpiredTimer()) //this is how we get the struct with CSData
        local real x = GetUnitX(a.u)+a.speed*a.cos
        local real y = GetUnitY(a.u)+a.speed*a.sin
        local group g = CreateGroup()
        local unit u
        call SetUnitX(a.u,x) //u dont check map bounds if the arrow leaves the map in can cause a crash
        call SetUnitY(a.u,y)
        
        call GroupEnumUnitsInRange(g,x,y,64,Filter(function arrow.filter))
        set u = FirstOfGroup(g)
        
        if u != null then
            call UnitDamageTarget(a.owner,u,99999,false,false,null,null,null) //this will give the a.owner the kill (call KillUnit() dont do that)
            call a.destroy()
            set u = null
        endif
        
        call DestroyGroup(g)
        set g = null
    endmethod
    
    static method filter takes nothing returns boolean
        local timer t = GetExpiredTimer()
        local arrow a = GetCSData(t)
        local unit f = GetFilterUnit()
        local boolean ok = GetWidgetLife(f) > .405 and not IsUnitType(f,UNIT_TYPE_STRUCTURE)
        set f = null
        set t = null
        return ok
    endmethod
    
    method onDestroy takes nothing returns nothing
        call KillUnit(.u)
        call PauseTimer(.t)
        call DestroyTimer(.t)
        set .owner = null
        set .u = null
        set .t = null
        set .speed = 0.
    endmethod
endstruct

private function Conds takes nothing returns boolean
    local arrow a
    
    if GetSpellAbilityId()==Abil_id then
        set a = arrow.create()
    endif
    
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Filter(function Conds)) //we create the struct in the condition instead (faster)
endfunction

endscope
 
Level 12
Joined
Apr 27, 2008
Messages
1,228
Ciebron is right.
But, the problem with this:
JASS:
globals
    gamecache HandleVars = InitGameCache( "handlevars.w3v" )
endglobals
is that you cannot initialize a global game cache when creating it.
 
Status
Not open for further replies.
Top