• 🏆 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!
  • ✅ Time to vote for the top 3 models! The POLL for Hive's 6th HD Modeling Contest: Mechanical is now open! 📅 Poll close on July 16, 2024! 🔗 Cast your vote now!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

[JASS] SetTimerData bugged?

Status
Not open for further replies.
Level 9
Joined
May 28, 2007
Messages
365
JASS:
So this is my spell code. For some strange reason, when I cast it, I get the debug SetTimerData message that says 

SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer

This occurs, even if the very first spell in the map that uses TimerUtils is this one (so it doesn't have to do with leaking timers).

Another strange thing is that when I cast it and there are people around me, it'll give the message multiple times, the same amount of times as there are people around me. 

I've added a little debug message, to see if the source of this spell. Here comes another strange thing.

I get the error message from TimerUtils BEFORE the debug message I placed there, which is strange because my debug message is supposed to appear before.

What's up?

'Also, I'm sure my spell is not coded well. If you can give me tips on what I can do to maximize performance, can you let me know? (It's not giving me any performance errors, but I'd like to learn as much as I can with this)

[code=jass]scope Flush
function Trig_Flush_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00F'
endfunction

private struct data 
    real x
    real y
    unit caster
    group g
    real v    
endstruct

private function Filt takes nothing returns boolean
    local data d = GetTimerData(GetExpiredTimer())
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(d.caster)) and GetWidgetLife(GetFilterUnit())>0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_ANCIENT) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL)
endfunction

private function RemoveEffect takes nothing returns nothing
    call UnitRemoveAbility(GetEnumUnit(), 'A00G')
endfunction

private function AddEffect takes nothing returns nothing
    call UnitAddAbility(GetEnumUnit(), 'A00G')
endfunction

private function FlushKnockback takes nothing returns nothing
    local data d = GetTimerData(GetExpiredTimer())
    local group g = NewGroup()
    local real angle
    local real x
    local real y 
    local unit target
    set d.v = d.v*0.925
    
    call GroupAddGroup(d.g, g)
    call GroupRemoveUnit(g, d.caster)      
    
    loop
        set target = FirstOfGroup(g)
        exitwhen target == null
            set angle = AngleBetweenPointsXY(d.x, d.y, GetUnitX(target), GetUnitY(target))
            set x = PolarProjectionX(GetUnitX(target), d.v, angle)
            set y = PolarProjectionY(GetUnitY(target), d.v, angle)
            if IsTerrainWalkable(x,y) then
                call SetUnitX(target, x)
                call SetUnitY(target, y)
            endif
            call GroupRemoveUnit(g, target)        
    endloop
    
    call ReleaseGroup(g)
    
    if d.v < 5 then
        call ForGroup(d.g, function RemoveEffect)
        call d.destroy()
        call PauseTimer(GetExpiredTimer())
        call ReleaseTimer(GetExpiredTimer())
    endif
endfunction

function Trig_Flush_Actions takes nothing returns nothing
    local timer tt = NewTimer()
    local unit u = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    local real coBonus = 1+GetPlayerTechCount(GetOwningPlayer(u), 'R007', true)*0.03
    local real amount = 125+10*GetHeroLevel(u)+GetUnitStat(u, SPELL_POWER)*0.5*coBonus
    local boolexpr filter = Condition(function Filt)
    local data d = data.create()
    
    call UnitRemoveBuffs(u, false, true)
    call HealUnit(t, amount)
    set d.caster = u
    set d.g = NewGroup()
    set d.v = 30
    set d.x = GetUnitX(t)
    set d.y = GetUnitY(t)
    
    call GroupEnumUnitsInRange(d.g, d.x, d.y, 250, filter)
    call ForGroup(d.g, function AddEffect)        
    
    debug call BJDebugMsg("Assigning timer data...")
    call SetTimerData(tt, d)  
    call TimerStart(tt, 0.03, true, function FlushKnockback)
    
    call DestroyBoolExpr(filter)    
    set filter = null
endfunction

//===========================================================================
function InitTrig_Flush takes nothing returns nothing
    set gg_trg_Flush = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventNL( gg_trg_Flush, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Flush, Condition( function Trig_Flush_Conditions ) )
    call TriggerAddAction( gg_trg_Flush, function Trig_Flush_Actions )
endfunction
endscope
 
Level 9
Joined
Nov 28, 2008
Messages
704
Alternatively, write your own timer data system. It's not like it's difficult. You can't use this, but this is how simple it really is. >.>

JASS:
library TimerFunctions initializer Init:
    private hashtable Hash
    private void Init():
        Hash = InitHashtable()
        
    private function interface TimerFunction takes timerdata t returns nothing
    
    struct timerdata extends extraneous_data:
        private timer Timer
        private TimerFunction Function
        
        static void TimerTick():
            timerdata td = timerdata(LoadInteger(Hash, GetHandleId(GetExpiredTimer()), 0))
            td.Function.evaluate(td)
            td.destroy()
        
        #define <timerdata.create>(TimerFunction) = timer##data.create(TimerFunction, 0.05)
        static timerdata create(TimerFunction t, real timeout):
            timerdata td = timerdata.allocate()
            td.Timer = timers.Get()
            td.Function = t
            TimerStart(td.Timer, timeout, false, function timerdata.TimerTick)
            SaveInteger(Hash, GetHandleId(td.Timer), 0, int(td))
            return td
            
        void destroy():
            timers.Give(this.Timer)
            FlushChildHashtable(Hash, GetHandleId(this.Timer))
            this.deallocate()

JASS:
struct extraneous_data:
    real real1 //Add more as the map needs it.
    int int1

But its not even likely to be the system, because Im sure more people would have complained by now. Try asking on WC3C under it's own forum topic for the system.
 
Level 9
Joined
Mar 25, 2005
Messages
252
You run this:
JASS:
private function Filt takes nothing returns boolean
    local data d = GetTimerData(GetExpiredTimer())
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(d.caster)) and GetWidgetLife(GetFilterUnit())>0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_ANCIENT) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL)
endfunction
for every unit in a 250 range of the triggering unit in an EVENT_PLAYER_UNIT_SPELL_EFFECT event. I have never tried it but GetExpiredTimer() probably returns null here which confuses the GetTimerData function.

What you should do In my opinion is to simply pass the data in a global variable.

Also, you don't need to, and often shouldn't, destroy boolean expressions. Although in your case it makes little difference.
 
Status
Not open for further replies.
Top