HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Text tag bug?

06-17-2009, 07:45 AM#1
TheWye
Hi, I have some questions regarding texttags and to make it short, I'll just show you my code:
Collapse JASS:
    struct WeaponCooldownFollowTimer_bungkusan
        unit orang
        texttag bar
        real duration
        real durationcounter = 0.00
        
        method onDestroy takes nothing returns nothing
            call DestroyTextTag(this.bar)
            set this.bar = null
        endmethod
    endstruct

    function WeaponCooldownFollowShooter_TimerPeriod takes nothing returns real
        return 0.02
    endfunction

    function WeaponCooldownFollowShooter takes nothing returns nothing
        local timer T = GetExpiredTimer()
        local WeaponCooldownFollowTimer_bungkusan data = WeaponCooldownFollowTimer_bungkusan(GetHandleInt(T, "FollowBungkusan"))
        
        if(data.durationcounter < data.duration)then
            call SetTextTagPosUnit(data.bar, data.orang, 0.00)
            set data.durationcounter = data.durationcounter + WeaponCooldownFollowShooter_TimerPeriod()
        else
            call PauseTimer(T)
            call FlushHandleLocals(T)
            call data.destroy()
        endif
        
        set T = null
    endfunction

    function WeaponCooldownStart takes timer CDTimer, unit shooter, real duration returns nothing
        local integer I = 10
        local string S = ""
        local timer followtimer
        local WeaponCooldownFollowTimer_bungkusan data
        local texttag TT
        
        //cooldown timer
        call TimerStart(CDTimer, duration, false, null)
        
        loop
            exitwhen(I <= 0)
            
            set TT = CreateTextTag()
            call ShowTextTagForceBJ( false, TT, GetPlayersAll() )
            call ShowTextTagForceBJ( true, TT, GetForceOfPlayer(GetOwningPlayer(shooter)) )
            call SetTextTagText(TT, S + "|", TextTagSize2Height(6.0))
            call SetTextTagPosUnit(TT, shooter, 0.00)
            call SetTextTagColor(TT, R2I(I2R(10 - I)/10.0 * 255.0), R2I(I2R(I)/10.0 * 255.0), 0, 255)
            call SetTextTagPermanent(TT, false)
            call SetTextTagLifespan(TT, I2R(I)/10.0 * duration)
            
            //follow timer
            //checks whether there is a timer attached to the shooter, if there isn't, creates a new timer
            if(GetHandleTimer(shooter, "FollowTimer" + I2S(10 - I)) != null)then
                set followtimer = GetHandleTimer(shooter, "FollowTimer" + I2S(10 - I))
            else
                set followtimer = CreateTimer()
                call SetHandleHandle(shooter, "FollowTimer" + I2S(10 - I), followtimer)
            endif
            call TimerStart(followtimer, WeaponCooldownFollowShooter_TimerPeriod(), true, function WeaponCooldownFollowShooter)
            
            set data = WeaponCooldownFollowTimer_bungkusan.create()
            call SetHandleInt(followtimer, "FollowBungkusan", data)
            set data.orang = shooter
            set data.bar = TT
            set data.duration = I2R(I)/10.0 * duration
            
            set I = I - 1
            set S = S + " "
        endloop
        
        set followtimer = null
    endfunction

Basically, this function creates bars on top of a unit whenever it shoots a weapon, and those bars represents the cooldown of a weapon. The bars vanishes one by one, and all the bars vanishes when the cooldown is finished (you can fire the weapon again).

The WeaponCooldownStart function initializes and creates the bars on top of the shooter while the WeaponCooldownFollowShooter moves the bars on top of the shooter so it follows the shooter continuously. These 2 functions work perfectly alone but when it is run together with this trigger, it has a bug :

Collapse JASS:
function Trig_Gold_Bounty_Actions takes nothing returns nothing
    local real f = -100.00
    local integer unitbounty = 0
    local player killingplayer = GetOwningPlayer(GetKillingUnit())
    local unit korban = GetTriggerUnit()
    local texttag bountyTextTag
    
    if(killingplayer != null)then
        //calculates the gold factor for the killed unit
        if (GetUnitTypeId(korban) == udg_CreepWaveType_1[udg_CurrentWave])then
            set f = udg_CreepWaveGoldFactorType_1[udg_CurrentWave]
        elseif (GetUnitTypeId(korban) == udg_CreepWaveType_2[udg_CurrentWave])then
            set f = udg_CreepWaveGoldFactorType_2[udg_CurrentWave]
        elseif (GetUnitTypeId(korban) == udg_CreepWaveType_3[udg_CurrentWave])then
            set f = udg_CreepWaveGoldFactorType_3[udg_CurrentWave]
        endif
        
        if( f != -100.00 ) then
            //calculates unit bounty
            set unitbounty = WyeR2I(f * I2R(Trig_Gold_Bounty_FindUnitBountySatuan(udg_CurrentWave)))//yang hasil dari functionnya diconvert jadi real dulu supaya bisa itung koma2. abis itu diconvert balik lagi abis dikaliin sama f jadi real
            call SetPlayerState(killingplayer, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(killingplayer, PLAYER_STATE_RESOURCE_GOLD) + unitbounty)
            
            set bountyTextTag = CreateTextTag()
            call ShowTextTagForceBJ( false, bountyTextTag, GetPlayersAll() )
            call ShowTextTagForceBJ( true, bountyTextTag, GetForceOfPlayer(killingplayer) )
            call SetTextTagText(bountyTextTag, "+" + I2S(unitbounty), TextTagSize2Height(8.0))
            call SetTextTagPosUnit(bountyTextTag, korban, 0.00)
            call SetTextTagColor(bountyTextTag, 255, 255, 0, 255)
            call SetTextTagPermanent(bountyTextTag, false)
            call SetTextTagLifespan(bountyTextTag, 2.0)
            call SetTextTagFadepoint(bountyTextTag, 1.5)
            call SetTextTagVelocityBJ(bountyTextTag, 50.0, 90.0)
        else
            call BJDebugMsg(GetUnitName(GetTriggerUnit()) + " gold factor not found")
        endif
    endif
    
    set bountyTextTag = null
    set korban = null
    set killingplayer = null
endfunction

This function basically calculates the bounty and creates a texttag above a killed unit when it is killed by a shooter.

The bug is, whenever the timer from the first trigger expires, it moves the cooldown bars above the shooter (which is what supposed to happen) BUT it also moves the bounty text tag above the shooter as well... is this supposed to happen? Is there a mistake in my code? Or is it just another annoying bug again??

EDIT : I found that everytime the follow timer expires, the data.bar (struct variable) actually refers to both the bounty texttag and the bar texttag. So whatever I do to the data.bar, it also applies to the bounty text tag. Does this even make sense??

Thanks in advance
06-17-2009, 08:51 AM#2
masda70
You should add a "set TT=null" at the end of WeaponCooldownStart

Two important things you should know about TextTags:
-Never destroy a TextTag that is not permanent and has a lifespan (is that what you are doing when you destroy a WeaponCooldownFollowTimer_bungkusan instance?) Only set the referencing variable to null.
-TextTags are limited to a number of 100. When you combine a lot of texttags with improper object destruction they start doing funny things.
06-17-2009, 08:59 AM#3
TheWye
Quote:
Originally Posted by masda70
You should add a "set TT=null" at the end of WeaponCooldownStart

Oh yeah, I forgot to put that in this one. The real function has that line though, but it still doesn't work. And I don't have that much texttags in the game. Even the first few texttags already act funny.
06-17-2009, 09:04 AM#4
masda70
Did you get rid of call DestroyTextTag(this.bar) ?

EDIT: You also use SetTextTagPosUnit at a moment where the TextTag in question might have already expired, due to possible inaccuracies in the use of timers to track down their expiration. If used on an already expired TextTag, it could move a completely different TextTag, such as the Gold_Bounty one. Try for example lowering the value of data.duration by 30 seconds then check if the problem still occurs.
06-17-2009, 10:04 AM#5
TheWye
OOOOOOOOOHH!!! I have never thought of that before!! Come to think of it, it totally makes sense. Let me try doing that for a while and see what happens.
And yeah, I have removed the DestroyTextTag(this.bar).

EDIT: hmm.... however, the SetUnitTextTagPosUnit function is placed inside an if/then/else and the if condition allows it to only be carried out when the texttag is still there... btw the data.duration is a small number, mostly less than 5 seconds.

EDIT: I tried changing some of the code in the WeaponCooldownFollowShooter function into this:
Collapse JASS:
    function WeaponCooldownFollowShooter takes nothing returns nothing
        local timer T = GetExpiredTimer()
        local WeaponCooldownFollowTimer_bungkusan data = WeaponCooldownFollowTimer_bungkusan(GetHandleInt(T, "FollowBungkusan"))
        
        set data.durationcounter = data.durationcounter + WeaponCooldownFollowShooter_TimerPeriod()
        if(data.durationcounter < data.duration)then
            call SetTextTagPosUnit(data.bar, data.orang, 0.00)
        else
            call PauseTimer(T)
            call FlushHandleLocals(T)
            call data.destroy()
        endif
        
        set T = null
    endfunction

and I found that the bug does not occur every time, but it still occurs every once in a while... any ideas on whats happening??
06-17-2009, 10:16 AM#6
masda70
EDIT: Make the TextTag permanent and don't give it a lifespan then manually destroy it after the supposed "lifespan" has expired, using DestroyTextTag
this time. The only reason you would do that is because in the end you are tracking down texttag's lifespan...
06-17-2009, 10:29 AM#7
TheWye
Just finished trying it a few seconds ago. It works, the bug doesn't occur. However, sometimes the last bar remains undestroyed for some reason....

Anyway, since you are being really helpful I gave a +rep for you :)

EDIT: AAAAAAAAAH FINALLY A SOLUTION!!

here, take a look what I've done with the code:
Collapse JASS:
    function WeaponCooldownFollowShooter takes nothing returns nothing
        local timer T = GetExpiredTimer()
        local WeaponCooldownFollowTimer_bungkusan data = WeaponCooldownFollowTimer_bungkusan(GetHandleInt(T, "FollowBungkusan"))
        local texttag TT = data.bar
        
        set data.durationcounter = data.durationcounter + WeaponCooldownFollowShooter_TimerPeriod()
        if(data.durationcounter < data.duration - 0.03)then //so the SetTextTagPosUnit does not fire when the duration is very close with the texttag expiration
            call SetTextTagPosUnit(data.bar, data.orang, 0.00)
        else
            call PauseTimer(T)
            call FlushHandleLocals(T)
            call data.destroy()
        endif
        
        set TT = null
        set T = null
    endfunction

I have redisabled the permanence of the texttag and added the lifespan again. I think its much better to depend on the texttag lifespan for cleaning up texttags if you don't want any undestroyed texttag in your map. But anyway, I wouldn't have found this solution without your help, so thanks a lot

Quote:
and I found that the bug does not occur every time, but it still occurs every once in a while... any ideas on whats happening??

I think the reason why sometimes the bug occurs is because sometimes the expiration time of the texttag is less than the time taken by the timer to expire as the counter adds up very small number at every iteration (0.02).
06-17-2009, 12:05 PM#8
masda70
Maybe if manual destruction didn't clear the last texttag, it was due to another problem we haven't sorted out yet.
06-17-2009, 01:31 PM#9
TheWye
Yeah, probably. But this one solved the problem pretty well, so I'm just gonna stick with this one. Anyway, thanks again :)