HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Timer Stack Problem [Caster System]

08-26-2007, 07:21 AM#1
anXieTy
Heya!

I´ve got a serious problem since i involved the Caster System´s Timer Stack in my map.

I changed all CreateTimer() parts with NewTimer() and all DestroyTimer() with ReleaseTimer().

Finally, when i´m casting a spell which needs a custom function written by me, there might be a bug that one of 3 illusions created by the spell won´t disappear at a random time. I cant tell you any reason for this.

This is my function which is not working sometimes:
Collapse JASS:
function RemoveUnitSFXTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local real x = GetHandleReal (t,"x")
local real y = GetHandleReal (t,"y")
if x == 0 then
set x = GetUnitX (GetHandleUnit(t,"whichunit"))
endif
if y == 0 then
set y = GetUnitY (GetHandleUnit (t,"whichunit"))
endif
//call BJDebugMsg ("ruSFXt X - "+R2S(x))
//call BJDebugMsg ("ruSFXt Y - "+R2S(y))
//call BJDebugMsg ("ruSFXt - "+GetUnitName(GetHandleUnit (t,"whichunit")))
//call BJDebugMsg ("ruSFXt - "+GetHandleString(t,"sfx"))
//call BJDebugMsg ("ruSFXt - "+I2S(H2I(t)))
call RemoveUnit (GetHandleUnit (t,"whichunit"))
call DestroyEffect (AddSpecialEffect(GetHandleString(t,"sfx"),x,y))
call FlushHandleLocals (t)
call ReleaseTimer(t)
//call BJDebugMsg ("ruSFXt - End")
endfunction


function RemoveUnitSFXTimed takes unit whichunit,real timeout, string sfx,real x, real y returns nothing
local timer t = NewTimer()
call SetHandleHandle (t,"whichunit",whichunit)
call SetHandleString (t,"sfx",sfx)
//call BJDebugMsg ("ruSFXt - start")
if x != 0 then
call SetHandleReal (t,"x",x)
endif
if y != 0 then
call SetHandleReal (t,"y",y)
endif
call TimerStart (t,timeout,false,function RemoveUnitSFXTimedChild)
endfunction

Ok, in the spell im calling this function with the following code:
Collapse JASS:
call RemoveUnitSFXTimed (illusion[i],1.3,GetAbilityEffectById(TriBlade_SpellId(),EFFECT_TYPE_AREA_EFFECT,0),0,0)

So, one illusion of 3 isnt removed properly, but i know that my RemoveUnitSFXTimed function is called when one of the illusions isnt removed properly. It just doesnt go through the callback so i think it has something to do with the timerstack.

I am using the latest timerstack of the Caster System. If you´d like to see my spellcode just request it.

Greeting... anX
08-26-2007, 07:39 AM#2
DioD
add debug to your functions, code looks like ok.
08-26-2007, 07:46 AM#3
Pyrogasm
Why are you using KaTTaNa's HandleVar functions in addition to CSCache and the TimerStack functions?

Why not just use the CSCache functions?
08-26-2007, 07:52 AM#4
DioD
better do not use caster system just for timer stacks...
08-26-2007, 07:53 AM#5
anXieTy
Are there any problems with Kattana´s Handle Vars and Timer Stack? Well I will have a try with CSCache functions today.

Any other suggestions?

anX
08-26-2007, 08:01 AM#6
Pyrogasm
It's just that KaTTaNa's are outdated and inferior. In fact, the LocalVars() function usually leaks a gamecache.
08-26-2007, 09:23 AM#7
NightBreeze
Well if it doesn't remove the unit, it seems it doesn't have much to remove, right? Maybe you could load the unit into a variable inside the timer callback instead of using GetHandleUnit(t,"whichunit") thrice and check if the unit variable is null at the start of the function. This code seems to be alright, so I would suspect there is something wrong with the way the unit is passed to the RemoveUnitSFXTimed function. Perhaps there is an array issue? Anyways, DioD was right: More debugmessages :)
08-26-2007, 05:18 PM#8
Vexorian
DestroyTimer doesn't malfunction when you destroy a timer that was already destroyed.

But ReleaseTimer does.

this said if you have a double free somewhere you will make timer stack to break.

this version of CSSafety will detect double frees when you compile the map using debug mode:

Collapse JASS:
library CSSafety requires CSCache
//******************************************************************************************
//*
//* CSSafety 14.deb
//* ¯¯¯¯¯¯¯¯
//*
//*  Utilities to make things safer. Currently this simply includes a timer recycling
//* Stack. Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer
//* you no longer have to care about setting timers to null nor about timer related issues
//* with the handle index stack.
//*
//******************************************************************************************

    //==========================================================================================
    globals
        private timer array T
        private integer N = 0
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (N==0) then
            set T[N]=CreateTimer()
             debug call SetCSData(T[N],0)
            return T[N]
        endif
     set N=N-1
     debug call SetCSData(T[N],0)
     return T[N]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        debug if (GetCSData(t)==-178788822) then
        debug     call BJDebugMsg("you be damned ! double free!!!1")
        debug else
        debug     call SetCSData(t,-178788822)
        debug endif
        if (N==8191) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            set T[N]=t
            set N=N+1
        endif    
    endfunction

endlibrary
08-27-2007, 02:52 PM#9
anXieTy
thx vex + rep.

But i dunno if it is that useful because the bugs always happen when many of my heros are being played at the same time

i will try it.

greetz, anX
08-27-2007, 02:56 PM#10
Vexorian
that's actually more proof it is an issue with double frees, don't worry, although the manifestation of the bug might require a lot of instances, detecting the double free itself should require a single cast.
08-27-2007, 07:09 PM#11
anXieTy
ok, finally i casted a single cast... didnt create a double free or lets say it didnt show your messages. And what now? Dunno but in other custom functions there also are errors for example here:

Collapse JASS:
function ShowUnitTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local boolean b = GetAttachedBoolean (t,"show")
local boolean c = GetAttachedBoolean (t,"reselect")
local unit whichunit = GetAttachedUnit (t,"whichunit")
call ShowUnit (whichunit,b)
if c == true then
call SelectUnitAddForPlayer(whichunit,GetOwningPlayer(whichunit))
endif
call CleanAttachedVars (t)
call ReleaseTimer (t)
set whichunit = null
endfunction

function ShowUnitTimed takes unit whichunit,boolean show,real timeout,boolean reselect returns nothing
local timer t = NewTimer()
call AttachObject (t,"whichunit",whichunit)
call AttachBoolean (t,"show",show)
call AttachBoolean (t,"reselect",reselect)
call TimerStart (t,timeout,false,function ShowUnitTimedChild)
endfunction
This function is also used in my Tri Blade spell, which is coded like this:

Collapse JASS:
constant function TriBlade_SpellId takes nothing returns integer
    return 'A01M'
endfunction
constant function TriBlade_DummyId takes nothing returns integer
    return 'h00O'
endfunction
constant function TriBlade_DummyAbilityId takes nothing returns integer
    return 'A01L'
endfunction
function Trig_TriBlade_Conditions takes nothing returns boolean
    
    return  GetSpellAbilityId () == TriBlade_SpellId()
endfunction

function Trig_TriBlade_Actions2 takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit array illusion
local unit caster = GetAttachedUnit(t,"caster")
local unit target = GetAttachedUnit(t,"target")
local real x = GetUnitX(target)
local real y = GetUnitY(target)
local real x2
local real y2
local real dist = GetAttachedReal (t,"dist") - 10
local real angle2
local integer i = 1

call BJDebugMsg ("Tri Blade - Cast")

loop
    exitwhen i >=4
    set illusion[i] = GetAttachedUnit(t,"illusion"+I2S(i))
    set i = i + 1
endloop
set i = 1

loop
    exitwhen i >= 4
    set angle2 = 120*i
    set x2 = x + dist *Cos (angle2*bj_DEGTORAD)
    set y2 = y + dist *Sin (angle2*bj_DEGTORAD)
    call SetUnitX(illusion[i],x2)
    call SetUnitY(illusion[i],y2)    
    set i = i + 1
endloop
if dist <= 120 then                            
set i = 1
loop
    exitwhen i >=4
    call IssueTargetOrder (illusion[i],"attackonce",target)
    call RemoveUnitSFXTimed (illusion[i],1.3,GetAbilityEffectById(TriBlade_SpellId(),EFFECT_TYPE_AREA_EFFECT,0),0,0)
    set i = i + 1
endloop
call MakeUnitBleed (target,caster,10,GetAbilityEffectById(TriBlade_SpellId(),EFFECT_TYPE_AREA_EFFECT,1),7.0,1.0)

call CleanAttachedVars (t)
call ReleaseTimer(t)
call ShowUnitTimed (caster,true,1.2,true)
set caster = null
set target = null
else
call AttachReal (t,"dist",dist)
endif
endfunction



function Trig_TriBlade_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local timer t = NewTimer()
local unit array illusion 
local real x = GetUnitX(target)
local real y = GetUnitY(target)
local real x2
local real y2
local real dist = 200
local real angle
local real angle2 = 0
local real timeout = 0.02
local integer i = 1     
call ShowUnit (caster,false)
loop
    exitwhen i >= 4                
    set angle2 = 120*i
    set x2 = x + dist *Cos (angle2*bj_DEGTORAD)
    set y2 = y + dist *Sin (angle2*bj_DEGTORAD)
    set angle = bj_RADTODEG *Atan2((y2 - y), (x2 -x))
    set illusion[i] =  CreateUnit (GetOwningPlayer(caster),TriBlade_DummyId(),x2,y2,angle-180)
    call UnitAddAbility (illusion[i],'Aloc')
    call UnitAddAbility (illusion[i],TriBlade_DummyAbilityId ())
    call SetUnitAbilityLevel (illusion[i],TriBlade_DummyAbilityId (),GetUnitAbilityLevel (caster,TriBlade_SpellId()))
    call SetUnitAnimationByIndex (illusion[i],3)
    call DestroyEffect (AddSpecialEffect(GetAbilityEffectById(TriBlade_SpellId(),EFFECT_TYPE_AREA_EFFECT,0),x2,y2))
    call SetUnitFacing (illusion[i],angle-180)
    call AttachObject (t,"illusion"+I2S(i),illusion[i])
    
    set i = i + 1
endloop
call AttachReal (t,"dist",dist)
call AttachObject (t,"caster",caster)
call AttachObject (t,"target",target)
call TimerStart (t,timeout,true,function Trig_TriBlade_Actions2)   
endfunction

//==== Init Trigger NewTrigger ====
function InitTrig_TriBlade takes nothing returns nothing
    set gg_trg_TriBlade = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_TriBlade,EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(gg_trg_TriBlade, Condition(function Trig_TriBlade_Conditions))
    call TriggerAddAction(gg_trg_TriBlade, function Trig_TriBlade_Actions)
endfunction

Any suggestions?

Greetz, anX
08-28-2007, 02:35 AM#12
Vexorian
did you use debug mode?

You only need one double free to screw anything else up. This is mostly the reason I moved away from using dynamic timers at all, seriously, you can do every spell with only creating a single timer that is used by all the instances. Or you can use timeLib

try this one:
Collapse JASS:
library CSSafety requires CSCache
//******************************************************************************************
//*
//* CSSafety 14.fix
//* ¯¯¯¯¯¯¯¯
//*
//*  Utilities to make things safer. Currently this simply includes a timer recycling
//* Stack. Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer
//* you no longer have to care about setting timers to null nor about timer related issues
//* with the handle index stack.
//*
//******************************************************************************************

    //==========================================================================================
    globals
        private timer array T
        private integer N = 0
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (N==0) then
            set T[N]=CreateTimer()
            call SetCSData(T[N],0)
            return T[N]
        endif
     set N=N-1
     call SetCSData(T[N],0)
     return T[N]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        if (GetCSData(t)==-178788822) then
            debug call BJDebugMsg("you be damned ! double free!!!1")
            return
        else
            call SetCSData(t,-178788822)
        endif
        if (N==8191) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            set T[N]=t
            set N=N+1
        endif    
    endfunction

endlibrary


besides of showing an error message when it detects a double free (and it is debug mode) it will also prevent it from causing issues even if it is not debug mode. Try it with your map in the conditions where it used to have bugs.

You should also make sure the bugs were actually there before replacing CreateTimer and DestroyTimer.
08-28-2007, 06:44 PM#13
anXieTy
Well i just played it in MP with debug mode on... didnt show any message nor did grim show something. and the bug reappeared. Well, if i singlecast the spell 400 times on a target, everything is perfect.

It even seems that after the first time the bug appears everything is gettin messed up i.e. Revive Timers dont Revive heroes correctly nor are they being destroyed when they hit 0.

Some other spells show malfunctions...

I dont know why this is the way it is...

Well i am going to try the native timer functions now and tell you my results.

What can be wrong? I might post my complete header but its kinda long ^_^

greetz, anX

EDIT: Just got a double free. While Warden ( A Hero ;P) is in Diffugium (like windwalk , but additionally creating an illusion at her position at cast) is bleeding caused by Tri Blade (see above), when the bleed ends there is a double free. so far for now... here is my header with many useless crap :P

Collapse JASS:
// anXieTys useful custom fkts.
constant function MakeUnitBleed_Dummy takes nothing returns unit
    return udg_BuffDummy
endfunction
constant function MakeUnitBleed_DummyAbility takes nothing returns integer
    return 'A01Q' // Ability adding buff and effect
endfunction
constant function MakeUnitBleed_Buff takes nothing returns integer
    return 'B003' // Buff being removed after duration
endfunction
constant function MakeUnitBleed_Order takes nothing returns string
    return "drunkenhaze" // Buff being removed after duration
endfunction

function IsUnitRaisable takes unit corpse returns boolean
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),'h000',GetUnitX(corpse),GetUnitY(corpse),270.00)
if IssueTargetOrder (u,"raisedead",corpse) == true then
call RemoveUnit (u)
return true
else 
call RemoveUnit (u)
return false
endif
endfunction

function CreateUnits takes integer i, integer ut,player p, real X, real Y, real facing returns nothing
loop
    exitwhen i == 0
        call CreateUnit ( p , ut, X, Y, facing) 
        set i = i - 1
    endloop
endfunction                                      

function DistanceBetweenCoords takes real x1,real y1,real x2,real y2 returns real
    local real x = x2 - x1
    local real y = y2 -y1
    return SquareRoot(x *x + y * y)
endfunction

function CreateUnitsAtRectAndOrderToRect takes integer count,integer unittypeid,player p , rect cr,rect targetrect, string order, real f returns nothing
local real x = GetRectCenterX(cr)
local real y = GetRectCenterY(cr)
local real x2= GetRectCenterX(targetrect)
local real y2= GetRectCenterY(targetrect)
local unit u
local integer i = 0
loop                                                                                                       
    exitwhen i >=count
     set u = CreateUnit(p,unittypeid,x,y,f)
     call IssuePointOrder(u, order,x2,y2)
     set i = i+ 1
endloop
endfunction
    

function CoordsInMapArea takes real x,real y returns boolean
local rect r = bj_mapInitialPlayableArea
local real MaxX = GetRectMaxX(r)
local real MinX = GetRectMinX(r)
local real MaxY = GetRectMaxY(r)
local real MinY = GetRectMinY(r) 
if x >MaxX or x<MinX then
return false
elseif y >MaxY or y<MinY then
return false
endif
return true
endfunction


function GroupIssueOrderRectCenterXY takes group g, string order, rect r returns nothing
local real x = GetRectCenterX(r)
local real y = GetRectCenterY(r)
call GroupPointOrder(g,order,x,y)
endfunction

function UnitIssueOrderRectCenterXY takes unit u, string order, rect r returns nothing
local real x = GetRectCenterX(r)
local real y = GetRectCenterY(r)
call IssuePointOrder(u,order,x,y)
endfunction

function IsNormalAttack takes unit target returns boolean
    if GetUnitAbilityLevel(target, 'B001')>0 then //normal attacks
        call UnitRemoveAbility(GetTriggerUnit(), 'B001')
        return true
    elseif GetUnitAbilityLevel (target,'B00K')>0 then //orb (bleeding arrows)
        return true
    endif
    return false                     
endfunction               
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction

function H2TimerDialog takes handle h returns timerdialog
    return h
endfunction

///////////////////////////////////////
///Custom Stun System by blu_da_noob///
///////////////////////////////////////

//////////////////////////////////////
/////Generic Game Cache functions/////
//////////////////////////////////////

function Cache takes nothing returns gamecache
    if udg_cache == null then
        call FlushGameCache(InitGameCache("cache.w3v"))
        set udg_cache = InitGameCache("cache.w3v")
    endif
    return udg_cache
endfunction

function GetUnit takes string mission, string key returns unit
    return GetStoredInteger(Cache(),mission,key)
    return null
endfunction

function GetTimer takes string mission, string key returns timer
    return GetStoredInteger(Cache(),mission,key)
    return null
endfunction

function GetGroup takes string mission, string key returns group
    return GetStoredInteger(Cache(),mission,key)
    return null
endfunction

//////////////////////////////////////
///End Generic Game Cache Functions///
//////////////////////////////////////

///////////////////////////////
/////Stun System Functions/////
///////////////////////////////

//Configuration Functions//
function StunDummyId takes nothing returns integer
    return 'h00L' //The rawcode of your dummy caster unit
endfunction

function StunAbilityId takes nothing returns integer
    return 'A01D' //The rawcode of your stormbolt ability
endfunction

function StunBuffId takes nothing returns integer
    return 'B00D' //The rawcode of your custom stun buff
endfunction

function MinimumStunDuration takes nothing returns real
    return 0.2 //The minimum duration this system can stun
               //a unit for. May not be stable below this
               //value, and below 0.1 is not recomended at all
endfunction
//End of Configuration Functions//


function GetStunDummy takes unit target returns unit
    local gamecache gc = Cache()
    local group g
    local unit dummy
    local integer temp
    
    set g = GetGroup("CustomStunSystem","StunDummyGroup")
    if g == null then
        set g = CreateGroup()
        call StoreInteger(gc,"CustomStunSystem","StunDummyGroup",H2I(g))    
    endif
    set dummy = FirstOfGroup(g)
    if dummy == null then
        set dummy = CreateUnit(GetOwningPlayer(target),StunDummyId(),GetUnitX(target),GetUnitY(target),0)
    else
        call SetUnitX(dummy,GetUnitX(target))
        call SetUnitY(dummy,GetUnitY(target))
        call SetUnitOwner(dummy,GetOwningPlayer(target),false)
        call GroupRemoveUnit(g,dummy)
    endif
    call UnitAddAbility(dummy,'Aloc')
    call UnitAddAbility(dummy,StunAbilityId())
    
    set temp = H2I(dummy)
    set gc = null
    set g = null
    set dummy = null
    return temp
    return null
endfunction

function ReturnStunDummy takes nothing returns nothing
    local unit dummy = bj_groupRandomCurrentPick
    call TriggerSleepAction(5)
    call GroupAddUnit(GetGroup("CustomStunSystem","StunDummyGroup"),dummy)
    set dummy = null
endfunction

function RemoveUnitStun takes unit u returns boolean
    return UnitRemoveAbility(u,StunBuffId())
endfunction

function TimedRemoveStun takes nothing returns nothing
    local string timertable
    local unit target
    local string targettable
    local gamecache gc = Cache()
    
    set bj_crippledTimer[21] = GetExpiredTimer()
    set timertable = I2S(H2I(bj_crippledTimer[21]))
    set target = GetUnit(timertable,"stuntarget")
    set targettable = I2S(H2I(target))
    
    call UnitRemoveAbility(target,StunBuffId())
    
    call FlushStoredMission(gc,timertable)
    call DestroyTimer(bj_crippledTimer[21])
    call FlushStoredInteger(gc,targettable,"stuntimer")
     
    set target = null
    set gc = null
endfunction

function StunUnit takes unit target, real duration, boolean ignoremagicimmunity returns boolean
    local boolean immune = IsUnitType(target,UNIT_TYPE_MAGIC_IMMUNE)
    local boolean stun = (not immune) or ignoremagicimmunity
    local unit dummy
    local string targettable
    local string timertable
    local gamecache gc 
    
    if stun then
        set dummy = GetStunDummy(target)
        set stun = IssueTargetOrderById(dummy,852095,target)
        if stun then
            set gc = Cache()
            set duration = RMaxBJ(duration,MinimumStunDuration())
            set targettable = I2S(H2I(target))
            set bj_crippledTimer[21] = GetTimer(targettable,"stuntimer")
            if bj_crippledTimer[21] == null then
                set bj_crippledTimer[21] = CreateTimer()
                call StoreInteger(gc,targettable,"stuntimer",H2I(bj_crippledTimer[21]))
            endif
            set timertable = I2S(H2I(bj_crippledTimer[21]))
            call StoreInteger(gc,timertable,"stuntarget",H2I(target))
            call TimerStart(bj_crippledTimer[21],RMaxBJ(duration,TimerGetRemaining(bj_crippledTimer[21])),false,function TimedRemoveStun) 
        endif
        set bj_groupRandomCurrentPick = dummy
        call ExecuteFunc("ReturnStunDummy")    
    endif
    
    set dummy = null
    set gc = null
    return stun
endfunction

//////////////////////////////////
///End of Stun System Functions///
//////////////////////////////////


function GetPlayerNameColored takes player id returns string
 local playercolor col=GetPlayerColor(id)
 local string r=GetPlayerName(id)
    if col == PLAYER_COLOR_RED then
        set r="|cffff0000"+r+"|r"
    elseif col == PLAYER_COLOR_BLUE then
        set r="|cff0000ff"+r+"|r"
    elseif col == PLAYER_COLOR_CYAN then
        set r="|cff93ffc9"+r+"|r"
    elseif col == PLAYER_COLOR_PURPLE then
        set r="|cff400080"+r+"|r"
    elseif col == PLAYER_COLOR_YELLOW then
        set r="|cffffff00"+r+"|r"
    elseif col == PLAYER_COLOR_ORANGE then
        set r="|cffff8000"+r+"|r"
    elseif col == PLAYER_COLOR_GREEN then
        set r="|cff00c400"+r+"|r"
    elseif col == PLAYER_COLOR_PINK then
        set r="|cffff80c0"+r+"|r"
    elseif col == PLAYER_COLOR_LIGHT_GRAY then
        set r="|cff808080"+r+"|r"
    elseif col == PLAYER_COLOR_LIGHT_BLUE then
        set r="|cffc1c1ff"+r+"|r"                 
    elseif col == PLAYER_COLOR_AQUA then
        set r="|cff5e5e2f"+r+"|r"
    elseif col == PLAYER_COLOR_BROWN then
        set r="|cff004000"+r+"|r"
    else
        set r="|cff000000"+r+"|r"
    endif
 set col=null
 return r
endfunction

function SimError takes player ForPlayer, string msg returns nothing
 local sound error=CreateSoundFromLabel( "InterfaceError",false,false,false,10,10)
    if (GetLocalPlayer() == ForPlayer) then
        call ClearTextMessages()
        call DisplayTimedTextToPlayer( ForPlayer, 0.52, -1.00, 2.00, "|cffffcc00"+msg+"|r" )
        call StartSound( error )
    endif
 call KillSoundWhenDone( error)
 set error=null
endfunction

function AddFadingTextTag takes string text, real x, real y, integer red, integer green, integer blue, integer alpha returns nothing
    local texttag t = CreateTextTag()
    call SetTextTagText(t, text, 0.025)
    call SetTextTagPos(t, x, y, 0.00)
    call SetTextTagColor(t, red, green, blue, alpha)
    call SetTextTagVelocity(t, 0, 0.03)
    call SetTextTagVisibility(t, true)
    call SetTextTagFadepoint(t, 2)
    call SetTextTagLifespan(t, 3)
    call SetTextTagPermanent(t, false)
    set t = null
endfunction

function RemoveAbilityTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer abilid = GetAttachedInt(t,"abilid")
local unit target = GetAttachedUnit(t,"target")
call UnitRemoveAbility (target,abilid)
call CleanAttachedVars (t)
call ReleaseTimer(t)
endfunction
function RemoveAbilityTimed takes unit target, integer abilid, real duration returns nothing
local timer t = NewTimer()
call AttachObject (t,"target",target)
call AttachInt (t,"abilid",abilid)
call TimerStart(t,duration,false,function RemoveAbilityTimedChild)
endfunction

function GetFloorHeight takes real x, real y returns real
    local location whichUnitLocation = Location( x, y )
    local real z = GetLocationZ( whichUnitLocation )
    call RemoveLocation( whichUnitLocation )
    set whichUnitLocation = null
    return z
endfunction

function GetUnitZ takes unit whichUnit returns real
    local real z = ( GetFloorHeight( GetUnitX( whichUnit ), GetUnitY( whichUnit ) ) + GetUnitFlyHeight( whichUnit ) )
    set whichUnit = null
    return z
endfunction

function SetUnitZ takes unit whichUnit, real z returns nothing
    local boolean whichUnitHasNotAmrf = ( GetUnitAbilityLevel( whichUnit, 'Amrf' ) <= 0 )
    if ( whichUnitHasNotAmrf ) then
        call UnitAddAbility( whichUnit, 'Amrf' )
    endif
    call SetUnitFlyHeight( whichUnit, z - GetFloorHeight( GetUnitX( whichUnit ), GetUnitY( whichUnit ) ), 0.00 )
    if ( whichUnitHasNotAmrf ) then
        call UnitRemoveAbility( whichUnit, 'Amrf' )
    endif    
    set whichUnit = null
endfunction

function UnitAddAbilityTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = GetAttachedUnit (t,"u")
local integer abil = GetAttachedInt(t,"abil")
call UnitRemoveAbility (u,abil)
call CleanAttachedVars (t)
call ReleaseTimer(t)
endfunction

function UnitAddAbilityTimed takes unit whichunit, integer abil, real howlong returns nothing
local timer t = NewTimer()
local unit u = whichunit
call UnitAddAbility (u,abil)
call AttachObject (t,"u",u)
call AttachInt (t,"abil",abil)
call TimerStart (t,howlong,false,function UnitAddAbilityTimedChild)
endfunction

function SetUnitManaTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit whichunit = GetAttachedUnit (t,"whichunit")
local real mana = GetAttachedReal (t,"mana")
call SetUnitState (whichunit,UNIT_STATE_MANA,GetUnitState(whichunit,UNIT_STATE_MANA)+mana)
call CleanAttachedVars(t)
call ReleaseTimer(t)
set whichunit = null
endfunction

function SetUnitManaTimed takes unit whichunit, real addmana, real timeout returns nothing
local timer t = NewTimer()
call AttachObject (t,"whichunit",whichunit)
call AttachReal (t,"mana",addmana)
call TimerStart (t,timeout,false,function SetUnitManaTimedChild)
endfunction

function IssueImmediateOrderTimed takes unit u, integer orderid, real wait returns nothing
call PolledWait (wait)
call IssueImmediateOrderById (u,orderid)
endfunction

constant function Dizzyness_SpellId takes nothing returns integer
    return 'A01F'
endfunction

function MakeUnitDizzyChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit whichunit = GetAttachedUnit (t,"whichunit")
call UnitRemoveAbility (whichunit,Dizzyness_SpellId())
call CleanAttachedVars (t)
call ReleaseTimer(t)
set whichunit = null
endfunction

function MakeUnitDizzy takes unit whichunit, real duration, integer percentslow returns nothing
local timer t = NewTimer()
call UnitAddAbility (whichunit,Dizzyness_SpellId())
call SetUnitAbilityLevel (whichunit,Dizzyness_SpellId(),percentslow)
call AttachObject (t,"whichunit",whichunit)
call TimerStart (t,duration,false,function MakeUnitDizzyChild)
endfunction

function RemoveUnitTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
call RemoveUnit (GetAttachedUnit (t,"whichunit"))
call CleanAttachedVars (t)
call ReleaseTimer(t)
endfunction

function RemoveUnitSFXTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local real x = GetAttachedReal (t,"x")
local real y = GetAttachedReal (t,"y")
local unit u = GetAttachedUnit(t,"whichunit")
if x == 0 then
set x = GetUnitX (u)
endif
if y == 0 then
set y = GetUnitY (u)
endif
call RemoveUnit (u)
call DestroyEffect (AddSpecialEffect(GetAttachedString(t,"sfx"),x,y))
call CleanAttachedVars (t)
call DestroyTimer(t)
endfunction

function RemoveUnitTimed takes unit whichunit, real timeout returns nothing
local timer t = NewTimer()
call AttachObject (t,"whichunit",whichunit)
call TimerStart (t,timeout,false,function RemoveUnitTimedChild)
endfunction

function RemoveUnitSFXTimed takes unit whichunit,real timeout, string sfx,real x, real y returns nothing
local timer t = CreateTimer()
call AttachObject (t,"whichunit",whichunit)
call AttachString (t,"sfx",sfx)
if x != 0 then
call AttachReal (t,"x",x)
endif
if y != 0 then
call AttachReal (t,"y",y)
endif
call TimerStart (t,timeout,false,function RemoveUnitSFXTimedChild)
endfunction

function ShowUnitTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local boolean b = GetAttachedBoolean (t,"show")
local boolean c = GetAttachedBoolean (t,"reselect")
local unit whichunit = GetAttachedUnit (t,"whichunit")
call ShowUnit (whichunit,b)
if c == true then
call SelectUnitAddForPlayer(whichunit,GetOwningPlayer(whichunit))
endif
call CleanAttachedVars (t)
call DestroyTimer (t)
set whichunit = null
endfunction

function ShowUnitTimed takes unit whichunit,boolean show,real timeout,boolean reselect returns nothing
local timer t = CreateTimer()
call AttachObject (t,"whichunit",whichunit)
call AttachBoolean (t,"show",show)
call AttachBoolean (t,"reselect",reselect)
call TimerStart (t,timeout,false,function ShowUnitTimedChild)
endfunction

function UnitReduceArmorTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = GetAttachedUnit(t,"u")
call UnitRemoveAbility (u,'A01J')
call AttachObject (u,"URATtimer",null)
call CleanAttachedVars (t)
call ReleaseTimer (t)
set u = null
endfunction

function UnitReduceArmorTimed takes unit whichunit,real duration, integer reduction returns nothing
local timer t 
local real dur 
local real r 
if GetUnitAbilityLevel (whichunit,'A01J') > 0 then
set t = GetAttachedTimer (whichunit,"URATtimer")
set r = TimerGetRemaining(t) + duration
call PauseTimer(t)
call TimerStart (t,r,false,function UnitReduceArmorTimedChild)
else
set t = NewTimer()
call UnitAddAbility (whichunit,'A01J')
call SetUnitAbilityLevel (whichunit,'A01J',reduction)
call AttachObject (whichunit,"URATtimer",t)
call AttachObject (t,"u",whichunit)
call TimerStart (t,duration,false,function UnitReduceArmorTimedChild)
endif
endfunction

function DestroyTriggerTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local trigger tr = GetAttachedTrigger (t,"trigger")
call DestroyTrigger (tr)
call CleanAttachedVars (t)
call ReleaseTimer (t)
set tr = null
endfunction

function DestroyTriggerTimed takes trigger trig, real timeout returns nothing
local timer t =NewTimer()
call AttachObject (t,"trigger",trig)
call TimerStart (t,timeout,false,function DestroyTriggerTimedChild)
endfunction

function MakeUnitBleedChild takes nothing returns nothing
local timer t = GetExpiredTimer ()
local unit dmger =GetAttachedUnit (t,"dmger")
local unit target = GetAttachedUnit (t,"target")
local string str = GetAttachedString (t,"sfxpath")
local real dur = GetAttachedReal (t,"dur")
local real iv = GetAttachedReal (t,"iv")
local real dmg = GetAttachedReal (t,"dmg")
set dur = dur - iv
if dur > 0 and GetWidgetLife (target) >0.405 then
call DestroyEffect (AddSpecialEffectTarget(str,target,"origin"))
call UnitDamageTarget(dmger,target,dmg,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_MAGIC,null)
call AttachReal (t,"dur",dur)
else
call UnitRemoveAbility (target,MakeUnitBleed_Buff())
call CleanAttachedVars (t)
call ReleaseTimer(t)
set dmger = null
set target = null
set str = null
endif

endfunction

function MakeUnitBleed takes unit target,unit dmgdealer,real dmg,string sfxpath, real dur,real interval returns nothing
local timer t 
local unit dummy
local real r = 0
if GetAttachedTimer (target,"MUBtimer") != null then
set t = GetAttachedTimer (target,"MUBtimer")
call PauseTimer(t)
set r = GetAttachedReal (t,"dur")
set dur = dur + r
call AttachObject (target,"MUBtimer",t)
else
set t = NewTimer()
call AttachObject (target,"MUBtimer",t)
endif
set dummy = MakeUnitBleed_Dummy()
call UnitAddAbility(dummy,MakeUnitBleed_DummyAbility())
call SetUnitX (dummy,GetUnitX(target))
call SetUnitY(dummy,GetUnitY(target))
call IssueTargetOrder (dummy,MakeUnitBleed_Order(),target)
call AttachObject (t,"dmger",dmgdealer)
call AttachObject (t,"target",target)
call AttachString (t,"sfxpath",sfxpath)
call AttachReal (t,"dur",dur)
call AttachReal (t,"iv",interval)
call AttachReal (t,"dmg",dmg)

call TimerStart (t,interval,true,function MakeUnitBleedChild)
endfunction

function SetUnitTimeScalePercentTimedChild takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = GetAttachedUnit (t,"whichunit")
local real r = GetAttachedReal (t,"timescale")
call SetUnitTimeScale (u,r*0.01)
call CleanAttachedVars (t)
call ReleaseTimer(t)
endfunction

function SetUnitTimeScalePercentTimed takes unit whichunit,real timescale,real timeout returns nothing
local timer t = NewTimer()
call AttachObject (t,"whichunit",whichunit)
call AttachReal (t,"timescale",timescale)
call TimerStart (t,timeout,false,function SetUnitTimeScalePercentTimedChild)
endfunction

function GetLowestHpUnitFromGroup takes group g returns unit
local unit u
local unit a
set a = FirstOfGroup(g)
call GroupRemoveUnit(g,a)
loop
    set u = FirstOfGroup (g)
    exitwhen u == null
    if GetWidgetLife (u) < GetWidgetLife(a) then
    set a = u
    endif
    call GroupRemoveUnit(g,u)
endloop
return a
endfunction

and Diffugium:

Collapse JASS:
constant function Diffugium_SpellId takes nothing returns integer
    return 'A000' //Rawcode of the Spell
endfunction
constant function Diffugium_Dummy takes nothing returns integer
    return 'h000' // Rawcode of the dummy
endfunction
constant function Diffugium_Dummyitem takes nothing returns integer
    return 'I000' // Rawcode of the dummyitem added to the dummy
endfunction
constant function Diffugium_WardenUnitTypeId takes nothing returns integer
    return 'E000' // Rawcode of the dummyitem added to the dummy
endfunction
constant function Diffugium_PhyEscSpellId takes nothing returns integer
    return 'A016' // pysical escape ability if warden has activated so buff appears etc.
endfunction
constant function Diffugium_PhyEscBuffId takes nothing returns integer
    return 'B00B' // see above
endfunction

function Trig_Diffugium_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Diffugium_SpellId()
endfunction


function Trig_Diffugium_Conditions2 takes nothing returns boolean
    return GetUnitTypeId (GetTriggerUnit()) == Diffugium_WardenUnitTypeId() and IsUnitIllusion( GetTriggerUnit()) == true
endfunction

function  Trig_Diffugium_Actions2 takes nothing returns nothing
local unit illusion = GetTriggerUnit()
call SetUnitX (illusion,GetAttachedReal(GetPlayableMapRect(),"wardenx"))
call SetUnitY (illusion,GetAttachedReal(GetPlayableMapRect(),"wardeny"))
//if GetUnitAbilityLevel (caster,Diffugium_PhyEscSpellId()) > 0 and GetUnitAbilityLevel(caster,Diffugium_PhyEscBuffId())>0 then
//call UnitAddAbility (illusion,Diffugium_PhyEscSpellId())
//endif
call CleanAttachedVars (GetPlayableMapRect())
endfunction

function  Trig_Diffugium_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit ()
local unit u
local real X   = GetUnitX (caster)
local real Y   = GetUnitY (caster) 
call AttachReal (GetPlayableMapRect(),"wardenx",X)
call AttachReal (GetPlayableMapRect(),"wardeny",Y) 
set u = CreateUnit (GetOwningPlayer(caster), Diffugium_Dummy(), X, Y, 270)
call UnitApplyTimedLife (u, 'BTLF', 1.00 )
call UnitAddItemByIdSwapped(Diffugium_Dummyitem(),u)
call UnitUseItemTarget(u,bj_lastCreatedItem,caster)       
set caster = null
set u = null
endfunction
//=========================================================
function InitTrig_Diffugium takes nothing returns nothing
    local trigger t = CreateTrigger()
    
    set gg_trg_Diffugium = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Diffugium, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(gg_trg_Diffugium, Condition(function Trig_Diffugium_Conditions))
    call TriggerAddAction(gg_trg_Diffugium, function Trig_Diffugium_Actions)
    
    call TriggerRegisterEnterRectSimple (t,GetPlayableMapRect())
    call TriggerAddCondition (t, Condition (function Trig_Diffugium_Conditions2))
    call TriggerAddAction (t,function Trig_Diffugium_Actions2)
endfunction
08-30-2007, 02:25 PM#14
anXieTy
sry for doubleposting, but i now know the reason for the bug: Its my function MakeUnitBleed in my header. Why it causes a double free isnt solved yet. im on it, any suggestions?
08-30-2007, 05:14 PM#15
Vexorian
Collapse JASS:
if GetAttachedTimer (target,"MUBtimer") != null then
set t = GetAttachedTimer (target,"MUBtimer")
call PauseTimer(t)
set r = GetAttachedReal (t,"dur")
set dur = dur + r
call AttachObject (target,"MUBtimer",t)
else
set t = NewTimer()
call AttachObject (target,"MUBtimer",t)
endif


But the thing is that you never set "MUBtimer" to null on the unit once it dies. What happens is that probably the unit's handle id gets reused as a unit and then when bleed is applied it is considered it already got a timer, and possibly frees it again later...