HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

spinning units in a pivot doesn't really spin

09-07-2009, 10:51 PM#1
Sinnergy
Hi, I have made a spell in my map that creates two tornadoes that circles around my hero from 0 degrees to 360 degrees (even if the hero is moving), the first tornado circles around the caster using some math formulas, while the other tornado depends on the first tornado, so that they will be opposite to each other no matter what.

The tornadoes have slow revolution rate, now the problem is, when my caster moves to the east, the tornadoes stop circling around when they are about to reach the north and south part, and when my caster tends to face backwards (0 degrees to 180 degrees), the tornadoes quickly circles around, when my caster stops, it circles normally like I wanted.

Looks like the tornadoes depend on the caster's facing angle, that is why it sometimes fuck up. How do I make this work?

The code (there are some function calls from other libraries, so please ignore them, for I believe that they do nothing about the circling part)
Collapse JASS:
scope EyeStorm initializer init
    globals
        private constant integer spell = 'A02S'
        private constant string light = "CHIM"
        private constant string hit = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
        private constant integer dumid = 'e009'
    endglobals
    
    private struct data
        unit c
        unit t1
        unit t2
        timer d1
        timer d2
        timer s
        integer l
        real elapsed
        //Circling Part
        static method dum1 takes nothing returns boolean
            local timer t = GetExpiredTimer()
            local data d = GetTimerData(t)
            local real x = GetUnitX(d.c)
            local real y = GetUnitY(d.c)
            local real a = 57.29582 * Atan3(x,y,GetUnitX(d.t1),GetUnitY(d.t1))
            local real px = x + 200 * Cos((a+2) * bj_DEGTORAD)
            local real py = y + 200 * Sin((a+2) * bj_DEGTORAD)
            call SetUnitX(d.t1,px)
            call SetUnitY(d.t1,py)
            set t = null
            return true
        endmethod
        
        static method dum2 takes nothing returns boolean
            local timer t = GetExpiredTimer()
            local data d = GetTimerData(t)
            local real x = GetUnitX(d.t1)
            local real y = GetUnitY(d.t1)
            local real a = Atan3(x,y,GetUnitX(d.c),GetUnitY(d.c))
            local real px = x + 400 * Cos(a)
            local real py = y + 400 * Sin(a)
            call SetUnitX(d.t2,px)
            call SetUnitY(d.t2,py)
            set t = null
            return true
        endmethod
       //End of Circling part
        
        static method strike takes nothing returns boolean
            local timer t = GetExpiredTimer()
            local data d = GetTimerData(t)
            local group g = NewGroup()
            local unit u
            local xedamage xe = xedamage.create()
            local real x = GetUnitX(d.t1)
            local real y = GetUnitY(d.t1)
            local real ux
            local real uy
            local real a
            set xe.dtype = DTWATER
            call xe.useSpecialEffect(hit,"origin")
            set xe.exception = UNIT_TYPE_STRUCTURE
            call GroupEnumUnitsInRange(g,x,y,250.0,BOOLEXPR_TRUE)
            loop
                set u = FirstOfGroup(g)
                exitwhen u == null
                if IsUnitEnemy(u, GetOwningPlayer(d.c)) and GetWidgetLife(u) > 0.405 and not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) then
                    call LightningUnitTarget(d.t1,I2R(GetRandomInt(400,500)),u,0.0,light,0.6,1.0,1.0,1.0)
                    call xe.damageTarget(d.c,u,20*d.l+60)
                    call DestroyEffect(AddSpecialEffectTarget(hit,u,"origin"))
                    if not IsUnitType(u,UNIT_TYPE_HERO) and not (GetUnitTypeId(u) == 'n004' or GetUnitTypeId(u) == 'n009') then
                        set ux = GetUnitX(u)
                        set uy = GetUnitY(u)
                        set a = 57.29582 * Atan3(x,y,ux,uy)
                        call KnockbackTarget(d.c,u,a,800.0,800.0,false,false,false)
                    endif
                endif
                call GroupRemoveUnit(g,u)
            endloop
            call ReleaseGroup(g)
            set u = null
            set g = null
            set g = NewGroup()
            set x = GetUnitX(d.t2)
            set y = GetUnitY(d.t2)
            call GroupEnumUnitsInRange(g,x,y,250.0,BOOLEXPR_TRUE)
            loop
                set u = FirstOfGroup(g)
                exitwhen u == null
                if IsUnitEnemy(u, GetOwningPlayer(d.c)) and GetWidgetLife(u) > 0.405 and not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) then
                    call LightningUnitTarget(d.t2,I2R(GetRandomInt(400,500)),u,0.0,light,0.6,1.0,1.0,1.0)
                    call xe.damageTarget(d.c,u,20*d.l+60)
                    call DestroyEffect(AddSpecialEffectTarget(hit,u,"origin"))
                    if not IsUnitType(u,UNIT_TYPE_HERO) and not (GetUnitTypeId(u) == 'n004' or GetUnitTypeId(u) == 'n009') then
                        set ux = GetUnitX(u)
                        set uy = GetUnitY(u)
                        set a = 57.29582 * Atan3(x,y,ux,uy)
                        call KnockbackTarget(d.c,u,a,800.0,800.0,false,false,false)
                    endif
                endif
                call GroupRemoveUnit(g,u)
            endloop
            call ReleaseGroup(g)
            call xe.destroy()
            set u = null
            set g = null
            set t = null
            return true
        endmethod
        
        static method tDestroy takes nothing returns boolean
            local timer t = GetExpiredTimer()
            local data d = GetTimerData(t)
            set d.elapsed = d.elapsed + 0.035
            if d.elapsed >= 20.0 or GetWidgetLife(d.c) <= 0.405 then
                set d.elapsed = 0.0
                call KillUnit(d.t1)
                call KillUnit(d.t2)
                call ReleaseTimer(d.d1)
                call ReleaseTimer(d.d2)
                call ReleaseTimer(d.s)
                call ReleaseTimer(t)
                call d.destroy()
            endif
            set t = null
            return true
        endmethod
        
        static method create takes unit c, integer l returns data
            local data d = data.allocate()
            local timer t = NewTimer()
            local real px1 = GetUnitX(c) + 200 * Cos(57.29582 * 0)
            local real py1 = GetUnitY(c) + 200 * Sin(57.29582 * 0)
            local real px2 = GetUnitX(c) + 200 * Cos(57.29582 * 180)
            local real py2 = GetUnitY(c) + 200 * Sin(57.29582 * 180)
            set d.c = c
            set d.l = l
            set d.t1 = CreateUnit(GetOwningPlayer(d.c),dumid,px1,py1,0.0)
            set d.t2 = CreateUnit(GetOwningPlayer(d.c),dumid,px2,py2,0.0)
            set d.d1 = NewTimer()
            set d.d2 = NewTimer()
            set d.s = NewTimer()
            call UnitApplyTimedLife(d.t1,'BTLF',20.0)
            call UnitApplyTimedLife(d.t2,'BTLF',20.0)
            call TimerStart(d.d1,0.035,true,function data.dum1)
            call TimerStart(d.d2,0.035,true,function data.dum2)
            call TimerStart(d.s,0.75,true,function data.strike)
            call TimerStart(t,0.035,true,function data.tDestroy)
            call SetTimerData(d.d1,d)
            call SetTimerData(d.d2,d)
            call SetTimerData(d.s,d)
            call SetTimerData(t,d)
            set t = null
            return d
        endmethod
    endstruct
    
    private function act takes nothing returns nothing
        local data d = data.create(SpellEvent.CastingUnit,GetUnitAbilityLevel(SpellEvent.CastingUnit,spell))
        call shake()
    endfunction
    
    private function init takes nothing returns nothing
        call RegisterSpellEffectResponse(spell,act)
    endfunction
endscope

Thank you.
09-08-2009, 09:28 AM#2
0zyx0
Nothing seems to depend on the facing angle of anything. The problem is, when the caster is moving, the angle between the caster and the tornadoes changes. That can make the variable a get the same value as it had the last time the method was called, or a much higher value than expected.

Here is a picture describing the problem; Note that the tornado hasn't moved in the time between A and B, but the caster has.

Click image for larger version

Name:	Tornado spell.gif
Views:	22
Size:	4.4 KB
ID:	45517

To solve this, store the angle that should be between the caster and the tornado in a variable, and increment that variable every time you are going to move the tornado.
Attached Images
File type: gifTornado spell.gif (4.4 KB)