HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

I2R(R2I()) condition

10-18-2008, 03:56 AM#1
Zerzax
In my spell I am running a timer at an interval of .04 seconds. In the callback function, I want to detect every time that the current expired time is a whole number, so that I can change a stored number. Every time the timer function runs, I add .04 to the stored time, so that every 25 intervals the current time is a whole number, and I can do my little action. I've used I2R(R2I(current time)) to round off the number and check if the rounded value and the actual value are equivalent. However, in game, the condition is not recognizing when the current time is equivalent to an integer. Here is the timer function, I would appreciate some help.

Collapse JASS:
        static method align takes nothing returns nothing
            local rage r=GetTimerData(GetExpiredTimer())
            local real targx=GetUnitX(r.enraged)
            local real targy=GetUnitY(r.enraged)
            
            set r.current_time=r.current_time + ALIGN_INTERVAL

            if r.current_time==I2R(R2I(r.current_time)) then
                set r.rage_meter=r.rage_meter-1
            endif
            
            call SetLightningColor(r.light, 1.,1.,1.,.1*r.rage_meter)
            call MoveLightning(r.light, false, r.castx, r.casty, targx, targy)
            call SetUnitFacing(r.caster, Atan2(targy-r.casty, targx-r.castx)*(180/pi))
            
            if r.current_time==STABLE_THRESHOLD then
                set r.finished=true
            endif
            
            if r.current_time==ENDTIME or r.rage_meter==0 then
                call r.release()
            endif

        endmethod
10-18-2008, 05:37 AM#2
Pyrogasm
Just use a counter integer variable and do a modulo check:
Collapse JASS:
    struct rage
         //Whatever else you have
        integer count = 0

        static method align takes nothing returns nothing
            local rage r=GetTimerData(GetExpiredTimer())
            local real targx=GetUnitX(r.enraged)
            local real targy=GetUnitY(r.enraged)
            
            set r.current_time=r.current_time + ALIGN_INTERVAL
            set r.count = r.count+1

            if ModuloInteger(r.count, 25) == 1 then
                set r.rage_meter=r.rage_meter-1
                set r.count = 0
            endif
            
            call SetLightningColor(r.light, 1.,1.,1.,.1*r.rage_meter)
            call MoveLightning(r.light, false, r.castx, r.casty, targx, targy)
            call SetUnitFacing(r.caster, Atan2(targy-r.casty, targx-r.castx)*(180/pi))
            
            if r.current_time==STABLE_THRESHOLD then
                set r.finished=true
            endif
            
            if r.current_time==ENDTIME or r.rage_meter==0 then
                call r.release()
            endif

        endmethod
    endstruct
10-18-2008, 05:44 AM#3
Ammorth
Reason why it probably isn't working is that floats are not 100% accurate and it may actually be slightly off and not exactly on. This is the nature of floating points.
10-18-2008, 12:27 PM#4
Zerzax
I've seen uses of Modulo for finding a remainder of zero, I thought that might be a good answer. Thank you Pyro and Ammorth, +rep
.


Eh Pyro, I can't give you more yet, I must "spread it around" a bit. You'll probably be helpful in the future, I don't doubt it :P.

EDIT 2: Well apparently the modulus satisfies the condition no matter what, so after 10 intervals the rage meter is 0 and the spell cuts off...

EDIT 3: I tried using BJDebugMsg and it turns out that because the dividend must be divided evenly by the divisor, it will always return 1 if r.count <= 25. Checking more...

EDIT 4: Yep, the modulus has to be zero, I'm pretty sure it fully works now! thanks again.
Collapse JASS:
if ModuloInteger(r.count, 25) == 0 then
    set r.count=0
    set r.rage_meter=r.rage_meter-1
endif
10-18-2008, 05:35 PM#5
Pyrogasm
Whoops. That's what I meant.
10-20-2008, 07:42 PM#6
Zerzax
I didnt want to start a new thread, so I'll just start this one up again. The spell works fine the first time it is cast, but any during subsequent times it is casted, the used struct instance is instantly destroyed twice for some reason. Here is the code, I will explain it a lil bit:
Collapse JASS:
// Uses TimerUtils and PUI

scope InfernalRage

    globals
        private constant integer ABILITY_ID='A000'
        private constant integer BLOODLUST_ID='A001'
        private constant integer FLAME_EFFECTS_ID='A003'
        private constant integer CRIPPLE_ID='A002'
        private constant integer LUST_BUFF_ID='B000'
        private constant integer DUMMY_ID='e000'
        
        private constant string ON_KILL_EFFECTS="Objects\\Spawnmodels\\Orc\\OrcLargeDeathExplode\\OrcLargeDeathExplode.mdl"
        private constant string FLAME_EFFECTS="Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
        private constant string LUST_EFFECTS="Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl"
        private constant string CRIPPLE_EFFECTS="Abilities\\Spells\\Undead\\Cripple\\CrippleTarget.mdl"
        private constant string RELEASE_EFFECTS="Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
        private constant string LIGHTNING_MODEL="AFOD"
        private constant string LUST_ORDER="bloodlust"
        private constant string CRIPPLE_ORDER="cripple"
        
        private constant real TOTAL_DURATION=0
        private constant real LIGHTNING_HEIGHT=75.00
        private constant real LIGHTNING_HIDE_HEIGHT=2000.00
        private constant real LUSTER_DURATION=2.00
        private constant real ALIGN_INTERVAL=0.04
        private constant real ENDTIME=20.00
        private constant real STABLE_THRESHOLD=10.00
        private constant real pi=bj_PI
        
    endglobals

    struct rage
        // a lightning stack - inspired by Vexorian's TimerUtils / CSSafety timer stack
        static lightning array light_array
        static integer light_index=0
        
        static method newLight takes real x1, real y1, real x2, real y2 returns lightning
            if .light_index==0 then
                set .light_array[0]=AddLightningEx(LIGHTNING_MODEL, false, x1, y1, LIGHTNING_HEIGHT, x2, y2, LIGHTNING_HEIGHT)
            else
                set .light_index=.light_index-1
                call MoveLightningEx(.light_array[.light_index], false, x1, y1, LIGHTNING_HEIGHT, x2, y2, LIGHTNING_HEIGHT)
            endif
            return .light_array[.light_index]
        endmethod
        
        static method releaseLight takes lightning l returns nothing
            call MoveLightningEx(l, false, 0, 0, LIGHTNING_HIDE_HEIGHT, 0, 0, LIGHTNING_HIDE_HEIGHT)
            set .light_array[.light_index]=l
            set .light_index=.light_index+1
        endmethod
    
        //! runtextmacro PUI()
    
        unit caster
        unit enraged
        player enraged_owner
        lightning light
        
        integer rage_meter=10
        real current_time=0
        integer count=0
        boolean finished=false
        boolean released=false
        
        real castx
        real casty
        
        timer align_timer

        static method align takes nothing returns nothing
            local rage r=GetTimerData(GetExpiredTimer())
            local real targx=GetUnitX(r.enraged)
            local real targy=GetUnitY(r.enraged)
            
            set r.current_time=r.current_time + ALIGN_INTERVAL            
            set r.count = r.count+1
            
            if ModuloInteger(r.count, 25) == 0 then
                set r.count = 0
                set r.rage_meter=r.rage_meter-1
            endif
            
            call SetLightningColor(r.light, 1.,1.,1.,.1*r.rage_meter)
            call MoveLightning(r.light, false, r.castx, r.casty, targx, targy)
            call SetUnitFacing(r.caster, Atan2(targy-r.casty, targx-r.castx)*(180/pi))
            
            if r.current_time==STABLE_THRESHOLD then
                set r.finished=true
            endif
            
            if r.current_time==ENDTIME or r.rage_meter==0 then
                set r.released=true
                call BJDebugMsg("Released either because of rage meter or time")
                call r.release()
            endif

        endmethod
        
        static method create takes unit cast, unit targ returns rage
            local rage r=rage.allocate()
            local player cast_owner=GetOwningPlayer(cast)
            local real targx=GetUnitX(targ)
            local real targy=GetUnitY(targ)
            local unit luster=CreateUnit(cast_owner, DUMMY_ID, targx, targy, 0)
            
            set r.caster=cast
            set r.castx=GetUnitX(cast)
            set r.casty=GetUnitY(cast)
            set r.enraged=targ
            set r.enraged_owner=GetOwningPlayer(targ)
            
            if GetLocalPlayer()==r.enraged_owner then 
                call SelectUnit(targ, false)
            endif
            if r.enraged_owner!=cast_owner then
                call SetUnitOwner(targ, cast_owner, false)
            endif
            if GetLocalPlayer()==cast_owner then
                call SelectUnit(cast, false)
                call SelectUnit(targ, true)
            endif
            
            set r.light=.newLight(r.castx, r.casty, targx, targy)
            call UnitApplyTimedLife(luster, 'BTLF', LUSTER_DURATION)
            call UnitAddAbility(luster, BLOODLUST_ID)
            call IssueTargetOrder(luster, LUST_ORDER, targ)
            call UnitAddAbility(targ, FLAME_EFFECTS_ID)
            
            set r.align_timer=NewTimer()
            call TimerStart(r.align_timer, ALIGN_INTERVAL, true, function rage.align)
            
            set luster=null
            
            return r
        endmethod
        
        private method onDestroy takes nothing returns nothing
            local unit crippler
            local player cast_owner=GetOwningPlayer(this.caster)
            
            call BJDebugMsg(I2S(this))
            
            call SetUnitPosition(this.caster, this.castx, this.casty)
            call SetUnitPosition(this.enraged, GetUnitX(this.enraged), GetUnitY(this.enraged))
            call DestroyEffect(AddSpecialEffectTarget(RELEASE_EFFECTS, this.enraged, "origin"))
            call UnitRemoveAbility(this.enraged, LUST_BUFF_ID)
            call UnitRemoveAbility(this.enraged, FLAME_EFFECTS_ID)
            call ReleaseTimer(this.align_timer)
            call .releaseLight(this.light)
            
            if GetLocalPlayer()==cast_owner then
                call SelectUnit(this.caster, true)
            endif
            
            if this.enraged_owner!=cast_owner then
                if GetLocalPlayer()==cast_owner then
                    call SelectUnit(this.enraged, false)
                endif
                call SetUnitOwner(this.enraged, this.enraged_owner, false)
                if GetLocalPlayer()==this.enraged_owner then
                    call SelectUnit(this.enraged, true)
                endif
            endif
            
            if not this.finished then
                set crippler=CreateUnit(Player(15), DUMMY_ID, this.castx, this.casty, 0)
                call UnitAddAbility(crippler, CRIPPLE_ID)
                call UnitApplyTimedLife(crippler, 'BTLF', 2.0)
                call IssueTargetOrder(crippler, CRIPPLE_ORDER, this.caster)
                set crippler=CreateUnit(Player(15), DUMMY_ID, this.castx, this.casty, 0)
                call UnitAddAbility(crippler, CRIPPLE_ID)
                call UnitApplyTimedLife(crippler, 'BTLF', 2.0)
                call IssueTargetOrder(crippler, CRIPPLE_ORDER, this.enraged)
            endif
            
            set crippler=null
        endmethod
        
        private static method onKill takes nothing returns boolean
            local rage r
            if GetUnitAbilityLevel(GetKillingUnit(), LUST_BUFF_ID)>0 then
                set r=rage[GetKillingUnit()]
                call BJDebugMsg("Kill gotten by enraged unit")
                call DestroyEffect(AddSpecialEffect(ON_KILL_EFFECTS, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit())))
                if r.rage_meter<10 then
                    set r.rage_meter=r.rage_meter+1
                endif
            endif
            return false
        endmethod
        
        private static method endCast takes nothing returns boolean
            local rage r
            if GetSpellAbilityId()==ABILITY_ID then
                set r=rage[GetTriggerUnit()]
                if not r.finished and not r.released then
                    call BJDebugMsg("Released because of endcast")
                    set r.released=true
                    call r.release()
                endif
            endif
            return false
        endmethod
        
        private static method onDeath takes nothing returns boolean
            local rage r
            if GetUnitAbilityLevel(GetTriggerUnit(), FLAME_EFFECTS_ID)>0 then
                set r=rage[GetTriggerUnit()]
                call BJDebugMsg("Released because of death of enraged")
                set r.released=true
                call r.release()
            endif
            return false
        endmethod

        private static method spellExecute takes nothing returns boolean
            local rage r
            if GetSpellAbilityId()==ABILITY_ID then
                set r=rage.create(GetTriggerUnit(), GetSpellTargetUnit())
                set rage[r.caster]=r
                set rage[r.enraged]=r
                call SetTimerData(r.align_timer, r)
            endif
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            local unit u=CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
            local trigger t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function rage.spellExecute))
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function rage.onKill))
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function rage.onDeath))
            set t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
            call TriggerAddCondition(t, Condition(function rage.endCast))
            
            call UnitAddAbility(u, ABILITY_ID)
            call UnitAddAbility(u, BLOODLUST_ID)
            call UnitAddAbility(u, FLAME_EFFECTS_ID)
            call UnitRemoveAbility(u, BLOODLUST_ID)
            call UnitRemoveAbility(u, ABILITY_ID)
            call UnitRemoveAbility(u, FLAME_EFFECTS_ID)
            
            call DestroyEffect(AddSpecialEffectTarget(RELEASE_EFFECTS, u, "origin"))
            call DestroyEffect(AddSpecialEffectTarget(ON_KILL_EFFECTS, u, "origin"))
            call DestroyEffect(AddSpecialEffectTarget(FLAME_EFFECTS, u, "origin"))
            call DestroyEffect(AddSpecialEffectTarget(LUST_EFFECTS, u, "origin"))
            call DestroyEffect(AddSpecialEffectTarget(CRIPPLE_EFFECTS, u, "origin"))
            
            set t=CreateTrigger()
            call RemoveUnit(u)
            set u=null
        endmethod

    endstruct

endscope

Basically, this spell will carry out an action and add bloodlust to a target unit. That unit is under the control of the caster for a certain amount of time. I have several conditions that will cut this link: If the enraged (targeted) unit dies, the spell is ended. If the caster stops casting (voluntary, dies, stunned, etc) then that also ends the spell and destroys the struct. If the "rage meter" equals zero, then the spell is also ended. So the first time the spell is cast, it works just like I need it. But the second time, the struct is destroyed instantly after casting for some reason. I tested it to try and find the source but all I know is that the first struct instance (1) is destroyed twice consecutively, causing double-free's abound and then directly causing the spell to end again because the caster's position is reset and the conditions are out of whack. Can anyone help me out with this?

EDIT: It's coming along, I'm now starting the timer after attaching data, that seems to have helped greatly. But the struct is still getting destroyed, and the create method is looping every 10 seconds.

EDIT 2: I fixed it. It had something to do with PUI's struct attachment, so I just reverted to using it's GetUnitIndex() and storing it in a hashtable.