Spellpack Boomerang and Orbital

Discussion in 'Spells' started by emjlr3, Mar 23, 2011.

  1. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    Boomerang v1.02

    Import Difficulty: Low

    Units Affected:
    Ground/Air

    Target Type: Unit/Point - Enemy, Ground, Terrain

    Spell Info
    :

    Toss your handy dandy boomerang toward the targeted location, which promptly returns to you, following an elliptical path. Units unlucky enough to wander nearby are dealt damage and disarmed for a short duration.

    Requirements
    :

    UnitAlive native
    TimerUtils
    GroupUtils
    Would also be a good idea to make use of Bound Sentinel

    JASS:
    scope Boomerang initializer Init
        //*********************************************************************
        //* Boomerang v1.02
        //* by: emjlr3
        //* ----------
        //*
        //* Requirements:
        //*     *A point target ability, like the "Boomerang" ability found in this map
        //*     *A dummy unit, like the "Caster Dummy" unit found in this map
        //*     *UnitAlive native, found in the custom script section of this map
        //*     *TimerUtils library
        //*         [url]http://www.wc3c.net/showthread.php?t=101322[/url]
        //*     *GroupUtils library
        //*         [url]http://www.wc3c.net/showthread.php?t=104464[/url]
        //*     *A copy of this trigger
        //*     *Although not required, the BoundSentinel library is recommended
        //*         [url]http://www.wc3c.net/showthread.php?t=102576[/url]
        //*
        //* (requires vJass)   More abilities at [url]http://www.thehelper.net/forums[/url]
        //*
        //*     Credits:
        //*     *Vexorian for his TimerUils library
        //*     *Rising_Dusk for his GroupUtils library
        //*
        //* Important:
        //*     *SFX strings can be set to "" for no effect.
        //*     *The boomerang returns to where the hero was, not where it is.  Using
        //*       an ability with a follow through time can help alleviate this problem
        //*       if you choose not to use the PAUSE option.
        //*     *Stacking casts of the buff will overwrite the previous cast only
        //*       when it's buff duration exceeds that left from the previous use.
        //*     *DISARMABIL and HARVEST need only be changed if you have modified
        //*       the base ability.  If so, copy and paste a new, un-edited version and
        //*       use it's rawcode for these values.
        //*
        //********************************************************************
    
        //==CONFIGURATION==\\
        globals
            private constant attacktype ATTACK          = ATTACK_TYPE_MAGIC // Ability damage attacktype
            private constant boolean    KILLTREES       = true // Have boomerang destroy trees in it's path
            private constant boolean    PAUSE           = false // Pause caster until boomerange returns
            private constant boolean    PRELOAD         = true // Preload DISARMABIL and effects, will use MISSILE as dummy unit
            private constant boolean    REMOVE          = true // Remove ABIL during boomerang movement
            private constant damagetype DAMAGE          = DAMAGE_TYPE_MAGIC // Ability damage damagetype
    
            private constant integer 	ABIL			= 'A005' // Boomerang ability rawcode
            private constant integer 	DISARMABIL		= 'Abun' // Cargo Hold (Orc Burrow) ability rawcode
            private constant integer    HARVEST         = 'Ahrl' // Harvest (Ghouls Lumber) ability rawcode
            private constant integer    MAXHANDLES      = 8190 // Map's max handle count, don't change if unsure
            private constant integer    MISSILE         = 'n000' // Boomerang dummy unit rawcode
            private constant integer    OFFSET          = 0x100000 // Map's first handle id, don't change if unsure
        
            private constant real       BUFFACC         = .15 // Periodic check on afflicted units for buff removal, lower values decrease performance
            private constant real       HEIGHT          = 40. // Boomerang fly height
            private constant real       RANGE           = 175. // Target and tree intersection range
            private constant real       TIMEOUT         = 0.03 // Periodic timer interval, lower values decrease performance
    	
            private constant string     BOOMSFX         = "Abilities\\Weapons\\BloodElfSpellThiefMISSILE\\BloodElfSpellThiefMISSILE.mdl" // Boomerang model
            private constant string     BOOMSFXAP       = "origin" // Attachment point for BOOMSFX on MISSILE
            private constant string		BUFFSFX			= "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl" // Target buff model
            private constant string     BUFFSFXAP       = "overhead" // Attachment point for BUFFSFX
            private constant string		TARGETSFX		= "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" // Boomerang strikes target model
            private constant string     TARGETSFXAP     = "chest" // Attachment point for TARGETSFX
        endglobals
        private function Damage takes integer level returns real
            return 100.*level // Damage dealt when the boomerang strikes a target
        endfunction
        private function Duration takes integer level returns real
            return 1.*level // Buff duration
        endfunction
        private function Life takes integer level, real distance returns real
            return .5+distance/500. // Time it takes the boomerang to finish it's trajectory, distance is the cast distance
        endfunction
        private function Targets takes unit caster, unit target returns boolean
            return true // Custom boomerang targeting boolean function
        endfunction
        private function Width takes integer level, real distance returns real
            return distance/2. // Radius of the boomerang arc, distance is the cast distance
        endfunction
    
        //==NO TOUCHING PAST THIS POINT==\\
        private keyword data
        private keyword disarm
        globals
            private boolexpr 			BOOL
            private group 				DISARMGROUP		= CreateGroup()
            private group 				GROUP   		= CreateGroup()
            private constant real       HALFPI          = bj_PI/2.
            private rect                RECT
            private unit 				TARG
            private data 				TEMPDATA
            private unit                TREEDUMMY
    	
            private data array			DATA
            private integer				DATACOUNT		= 0
            private timer 				DATATIMER		= CreateTimer()
    	
            private disarm array		UNITDISARM[MAXHANDLES]
        endglobals
        private function h2i takes unit u returns integer
            return GetHandleId(u)-OFFSET
        endfunction
        private struct disarm
            timer t
            unit u
            effect sfx
            real time=0.
            real max
    	
            method destroy takes nothing returns nothing
                call UnitRemoveAbility(.u,DISARMABIL)
                call DestroyEffect(.sfx)
                call ReleaseTimer(.t)
                call GroupRemoveUnit(DISARMGROUP,.u)
                call .deallocate()
            endmethod
        endstruct
        private struct data
            group g
            unit u
            unit boom
            effect sfx
            player p
            real face
            real x
            real y
            real a
            real cosa
            real sina
            real dist
            real width
            real xd
            real yd
            real damage
            real life
            real halfmaxticks
            integer i=0
            integer lvl
            integer maxticks
            boolean early=false
    	
            method destroy takes nothing returns nothing
                local integer i=1
            
                call ReleaseGroup(.g)
                static if REMOVE then
                call SetPlayerAbilityAvailable(.p,ABIL,true)
            endif
            static if PAUSE then
            call PauseUnit(.u,false)
        endif
        call DestroyEffect(.sfx)
        call RemoveUnit(.boom)
            
        if .early then
            loop
                exitwhen DATA[i]==this
                set i=i+1
            endloop
            set DATA[i]=DATA[DATACOUNT]
            set DATACOUNT=DATACOUNT-1
        endif
            
        call .deallocate()
    endmethod
    endstruct
    
    //=====================================================================
    private function KillTrees takes nothing returns boolean
        if IssueTargetOrder(TREEDUMMY,"harvest",GetFilterDestructable()) then
            call KillDestructable(GetFilterDestructable())
        endif
        return false
    endfunction
    private function KillTreesInRange takes real x, real y, real radius returns nothing
        call SetRect(RECT,x-radius,y-radius,x+radius,y+radius)
        call PauseUnit(TREEDUMMY,false)
        call EnumDestructablesInRect(RECT,Condition(function KillTrees),null)
        call PauseUnit(TREEDUMMY,true)
    endfunction
    private function Update takes nothing returns nothing
        local disarm d=GetTimerData(GetExpiredTimer())
    	
        if not UnitAlive(d.u) or d.time>=d.max then
            call d.destroy()
        else
            set d.time=d.time+BUFFACC
        endif
    endfunction
    private function Filt takes nothing returns boolean
        local data d=TEMPDATA
        local disarm di
        set TARG=GetFilterUnit()
    	
        if IsUnitEnemy(TARG,d.p) and UnitAlive(TARG) and not IsUnitInGroup(TARG,d.g) and Targets(d.u,TARG) then
            call GroupAddUnit(d.g,TARG)
            call DestroyEffect(AddSpecialEffectTarget(TARGETSFX,TARG,TARGETSFXAP))
            call UnitDamageTarget(d.u,TARG,d.damage,false,false,ATTACK,DAMAGE,null)
            call UnitAddAbility(TARG,DISARMABIL)
            call UnitMakeAbilityPermanent(TARG,true,DISARMABIL)
    		
            if not IsUnitInGroup(TARG,DISARMGROUP) then
                set di=disarm.create()
                set di.max=Duration(d.lvl)
                set di.t=NewTimer()
                set di.u=TARG
                set di.sfx=AddSpecialEffectTarget(BUFFSFX,TARG,BUFFSFXAP)
                call SetTimerData(di.t,di)
                call TimerStart(di.t,BUFFACC,true,function Update)
                set UNITDISARM[h2i(TARG)]=di
                call GroupAddUnit(DISARMGROUP,TARG)
            else
                set di=UNITDISARM[h2i(TARG)]
                if di.max-di.time<Duration(d.lvl) then
                    set di.time=0.
                    set di.max=Duration(d.lvl)
                endif
            endif
        endif
        return false
    endfunction
    private function Effects takes nothing returns nothing
        local data d
        local integer i=1
        local real r
        local real ra
        local real rb
        local real x
        local real y
    	
        loop
            exitwhen i>DATACOUNT
            set d=DATA[i]
    	
            set r=(1-I2R(d.i)/d.halfmaxticks)*bj_PI
            set ra=d.dist/2.*Cos(r)
            set rb=d.width/2.*Sin(r)
            set x=d.xd+ra*d.cosa-rb*d.sina
            set y=d.yd+ra*d.sina+rb*d.cosa
    		
            if d.i>=d.maxticks then
                static if KILLTREES then
                call KillTreesInRange(x,y,RANGE)
            endif
            call d.destroy()
            set DATA[i]=DATA[DATACOUNT]
            set DATACOUNT=DATACOUNT-1
            set i=i-1
        else
            static if KILLTREES then
            call KillTreesInRange(x,y,RANGE)
        endif
        set d.i=d.i+1
        call SetUnitX(d.boom,x)
        call SetUnitY(d.boom,y)
        call SetUnitFacing(d.boom,(d.a+r-HALFPI)*bj_RADTODEG)
        set TEMPDATA=d
        call GroupEnumUnitsInRange(GROUP,x,y,RANGE,BOOL)
    endif
    	
    set i=i+1
    endloop
    	
    if DATACOUNT==0 then
        call PauseTimer(DATATIMER)
    endif
    endfunction
    private function Actions takes nothing returns nothing
        local data d=data.create()
        local real dx
        local real dy
        
        set d.u=GetTriggerUnit()
        set d.p=GetOwningPlayer(d.u)
        set d.face=GetUnitFacing(d.u)
        set d.x=GetUnitX(d.u)
        set d.y=GetUnitY(d.u)
        set d.boom=CreateUnit(d.p,CasterId,d.x,d.y,d.face)
        set d.sfx=AddSpecialEffectTarget(BOOMSFX,d.boom,BOOMSFXAP)
        set dx=GetSpellTargetX()-d.x
        set dy=GetSpellTargetY()-d.y
        set d.dist=SquareRoot(dx*dx+dy*dy)
        set d.a=Atan2(dy,dx)
        set d.cosa=Cos(d.a)
        set d.sina=Sin(d.a)
        set d.xd=d.x+d.dist/2.*Cos(d.a)
        set d.yd=d.y+d.dist/2.*Sin(d.a)
        set d.lvl=GetUnitAbilityLevel(d.u,ABIL)
        set d.damage=Damage(d.lvl)
        set d.width=Width(d.lvl,d.dist)
        set d.life=Life(d.lvl,d.dist)
        set d.maxticks=R2I(d.life/TIMEOUT)
        set d.halfmaxticks=d.maxticks/2.
        set d.g=NewGroup()
        set DATACOUNT=DATACOUNT+1
        set DATA[DATACOUNT]=d
        if DATACOUNT==1 then
            call TimerStart(DATATIMER,TIMEOUT,true,function Effects)
        endif
    	
        call SetUnitPathing(d.boom,false)
        call UnitAddAbility(d.boom,'Aloc')
        call SetUnitFlyHeight(d.boom,HEIGHT,0.)
        static if REMOVE then
        call SetPlayerAbilityAvailable(d.p,ABIL,false)
    endif
    static if PAUSE then
    call PauseUnit(d.u,true)
    endif
    endfunction
    
    //======================================================================
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId()==ABIL then
            call Actions()
        endif
        return false
    endfunction
    private function Init takes nothing returns nothing
        local trigger t=CreateTrigger()
        local integer i=0
        
        loop
            exitwhen i>bj_MAX_PLAYERS
            call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            set i=i+1
        endloop
        call TriggerAddCondition(t,Condition(function Conditions))
        
        set BOOL=Condition(function Filt)
        static if PRELOAD then
        set TARG=CreateUnit(Player(15),MISSILE,0.,0.,0.)
        call UnitAddAbility(TARG,DISARMABIL)
        call UnitApplyTimedLife(TARG,'BTLF',.001)
        call Preload(BOOMSFX)
        call Preload(BUFFSFX)
        call Preload(TARGETSFX)
    endif
    static if KILLTREES then
    set RECT=Rect(0.,0.,100.,100.)
    set TREEDUMMY = CreateUnit(Player(15),MISSILE,0.,0.,0.)
    call ShowUnit(TREEDUMMY,false)
    call UnitAddAbility(TREEDUMMY,HARVEST)
    call UnitAddAbility(TREEDUMMY,'Aloc')
    call PauseUnit(TREEDUMMY,true)
    endif
    endfunction
    
    endscope


    Orbital v1.02

    Import Difficulty: Low

    Units Affected:
    Ground/Air

    Target Type: Instant

    Spell Info
    :

    Summon 4 orbitting masses of molten rock, which slowly circle, awaiting their masters command. They grow in size and power over 12 seconds, however, their massive energy can only be maintained for a short while.
    Upon expiration, deals damage to enemies or heals allies surrounding the impact zone.
    If their timer expires, they automatically target their summoner.

    Requirements
    :

    UnitAlive native
    Would also be a good idea to make use of Bound Sentinel

    JASS:
    scope Orbital initializer Init
        //*********************************************************************
        //* Orbital v1.02
        //* by: emjlr3
        //* ----------
        //*
        //* Requirements:
        //*     *An instant cast ability, like the "Orbital" ability found in this map
        //*     *A dummy unit, like the "Caster Dummy" unit found in this map
        //*     *UnitAlive native, found in the custom script section of this map
        //*     *A copy of this trigger
        //*     *Although not required, the BoundSentinel library is recommended
        //*         [url]http://www.wc3c.net/showthread.php?t=102576[/url]
        //*
        //* (requires vJass)   More abilities at [url]http://www.thehelper.net/forums[/url]
        //*
        //*     Credits:
        //*     *DotA for spell architecture inspiration
        //*
        //* Important:
        //*     *SFX strings can be set to "" for no effect.
        //*     *Instance limit of 8190/OMAX.
        //*     *One instance per unit.  This is, for the most part, rendered
        //*      safe through clever ability availability manipulation.
        //*
        //********************************************************************
    
        //==CONFIGURATION==\\
        globals
            private constant attacktype     ATTACK      = ATTACK_TYPE_MAGIC // Collision damage attacktype
            private constant boolean        CLK         = true // Rotate clockwise
            private constant boolean        PRELOAD     = true // Preload effects and TARGET ability, will use DUM as dummy unit
            private constant damagetype     DAMAGE      = DAMAGE_TYPE_MAGIC // Collision damage damagetype
    
            private constant integer 		ABIL	    = 'A002' // Orbital ability rawcode
            private constant integer        DUM         = 'n000' // Orbital dummy unit rawcode
            private constant integer        MAXHANDLES  = 8190 // Map's max handle count, don't change if unsure
            private constant integer        OFFSET      = 0x100000 // Map's first handle id, don't change if unsure
            private constant integer        OMAX        = 4 // Max number of orbitals
            private constant integer		TARGET	    = 'A004' // Target Orbitals ability rawcode
    	
            private constant real			SPEED	    = 18. // Speed at which orbitals follow targets, distance/TIMEOUT
            private constant real			TIMEOUT	    = .03 // Periodic timer interval
    	
            private constant string			MDL 	    = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl" // Orbital model
            private constant string         MDLAP       = "origin" // Attachment point for MDL
            private constant string			SFX		    = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl" // Effect on targets following collision
            private constant string         SND         = "Abilities\\Spells\\Other\\SoulBurn\\SoulBurn1.wav" // Sound to play when orbitals reach max strength
        endglobals
        private function Damage takes integer level, real currtime, real maxtime returns real
            // currtime is current elapsed time of spell, maxtime is the total time required for orbitals to reach maximum power
            return level*10+level*20*RMinBJ(currtime/maxtime,1.) // Damage/heal effect on applicable targets
        endfunction
        private function Distance takes integer level returns real
            return 200. // Orbitals rotation distance from caster
        endfunction
        private function Orbitals takes integer level returns integer
            return 4 // Number of orbitals
        endfunction
        private function Range takes integer level, real currtime, real maxtime returns real
            // currtime is current elapsed time of spell, maxtime is the total time required for orbitals to reach maximum power
            return 200.+100.*RMinBJ(currtime/maxtime,1.) // Collision effects radius
        endfunction
        private function ReachMax takes integer level returns real
            return 12.*.75 // Time required for orbitals to reach maximum strength
        endfunction
        private function ScaleIncrease takes integer level, real currtime, real maxtime returns real
            // currtime is current elapsed time of spell, maxtime is the total time required for orbitals to reach maximum power
            return .5+1.*RMinBJ(currtime/maxtime,1.) // Orbital model scale @ time currtime
        endfunction
        private function StartScale takes integer level returns real
            return .5 // Orbital initial model scale
        endfunction
        private function Targets takes unit caster, unit target returns boolean
            return true // Custom orbital targeting boolean function
        endfunction
        private function Time takes integer level returns real
            return 12. // Max timed life of orbitals
        endfunction
        private function Update takes integer level returns real
            return 360.*TIMEOUT // Rate at which orbitals coords around caster change, angle degrees/TIMEOUT
        endfunction
    
        //==NO TOUCHING PAST THIS POINT==\\
        private keyword data
        globals
            private boolexpr 				BOOL
            private integer					COUNT       = 0
            private data array 				DATA[MAXHANDLES]
            private data array 				DATASTACK
            private group					EXPLODE	    = CreateGroup()
            private group					GROUP	    = CreateGroup()
            private constant real			MODSPEED    = SPEED*SPEED
            private constant integer        RMAX        = OMAX+1
            private sound					SOUND
            private unit 					TARG
            private data					TEMPD
            private timer                   TIMER       = CreateTimer()
        endglobals
        private function Filt takes nothing returns boolean
            local data d=TEMPD
            local unit TARG=GetFilterUnit()
            local real r
            local real rr
    	
            if UnitAlive(TARG) and Targets(d.u,TARG) then
                set r=Damage(d.lvl,d.time,d.maxtime)
                if IsUnitEnemy(TARG,d.p) then
                    call UnitDamageTarget(d.u,TARG,r,false,false,ATTACK,DAMAGE,null)
                else
                    call SetWidgetLife(TARG,GetWidgetLife(TARG)+r)
                endif
            endif
            return false
        endfunction
        private struct data
            unit u
            unit target
            unit array orbital[RMAX]
            effect array sfx[RMAX]
            player p
            integer lvl
            integer stack
            integer mode=0
            integer array gotem[RMAX]
            integer count
            real time=0.
            real maxtime
            real update
            real array degree[RMAX]
            real reachmax
            real dist
            real startscale
            boolean max=false
    	
            method destroy takes nothing returns nothing
                local integer i=1
    		
                loop
                    exitwhen i>.count
    			
                    if .gotem[i]!=1 then
                        set TEMPD=this
                        call GroupEnumUnitsInRange(EXPLODE,GetUnitX(.orbital[i]),GetUnitY(.orbital[i]),Range(.lvl,.time,.maxtime),BOOL)
                        call DestroyEffect(AddSpecialEffect(SFX,GetUnitX(.orbital[i]),GetUnitY(.orbital[i])))
                        call DestroyEffect(.sfx[i])
                        call RemoveUnit(.orbital[i])
                    endif
    			
                    set i=i+1
                endloop
    		
                set DATASTACK[.stack]=DATASTACK[COUNT]
                set COUNT=COUNT-1
                call GroupRemoveUnit(GROUP,.u)
                call UnitRemoveAbility(.u,TARGET)
                call SetPlayerAbilityAvailable(.p,ABIL,true)
            
                call .deallocate()
            endmethod
        endstruct
    
        private function Effects takes nothing returns nothing
            local data d
            local integer i=1
            local integer c=1
            local real x
            local real y
            local real r
            local boolean b=true
    	
            loop
                exitwhen i>COUNT
                set d=DATASTACK[i]
    		
                if d.time>d.maxtime and d.mode==0 then
                    set d.mode=1
                    set d.target=d.u
                    call UnitRemoveAbility(d.u,TARGET)
                    call SetPlayerAbilityAvailable(d.p,ABIL,true)
                elseif d.mode==0 then
                    set x=GetUnitX(d.u)
                    set y=GetUnitY(d.u)
                    set r=ScaleIncrease(d.lvl,d.time,d.reachmax)
    			
                    loop
                        exitwhen c>d.count
    				
                        if UnitAlive(d.orbital[c]) and d.orbital[c]!=null then
                            set d.degree[c]=d.degree[c]+d.update
                            static if CLK then
                            call SetUnitFacing(d.orbital[c],d.degree[c]+90.)
                        else
                            call SetUnitFacing(d.orbital[c],d.degree[c]-90.)
                        endif
                        call SetUnitX(d.orbital[c],x+d.dist*Cos(d.degree[c]*bj_DEGTORAD))
                        call SetUnitY(d.orbital[c],y+d.dist*Sin(d.degree[c]*bj_DEGTORAD))
                        call SetUnitScale(d.orbital[c],r,r,r)
                    endif
    				
                    set c=c+1
                endloop
    			
                if d.time>=d.reachmax and not d.max then
                    if GetLocalPlayer()==d.p then
                        call StartSound(SOUND)
                        set d.max=true
                    endif
                endif
    			
                set d.time=d.time+TIMEOUT
            elseif d.mode==1 then
                loop
                    exitwhen c>d.count
                    
                    if UnitAlive(d.target) and d.target!=null then
                        if d.gotem[c]!=1 then
                            set x=GetUnitX(d.orbital[c])
                            set y=GetUnitY(d.orbital[c])
                            if Pow(GetUnitX(d.target)-x,2)+Pow(GetUnitY(d.target)-y,2)<=MODSPEED then
                                set TEMPD=d
                                call GroupEnumUnitsInRange(EXPLODE,x,y,Range(d.lvl,d.time,d.maxtime),BOOL)
                                call DestroyEffect(AddSpecialEffect(SFX,x,y))
                                call DestroyEffect(d.sfx[c])
                                call RemoveUnit(d.orbital[c])
                                set d.gotem[c]=1
                            else
                                set b=false
                                set r=Atan2(GetUnitY(d.target)-y,GetUnitX(d.target)-x)
                                call SetUnitFacing(d.orbital[c],r*bj_RADTODEG)
                                call SetUnitX(d.orbital[c],x+SPEED*Cos(r))
                                call SetUnitY(d.orbital[c],y+SPEED*Sin(r))
                            endif
                        endif
                    else
                        call d.destroy()
                        set i=i-1
                        exitwhen true
                    endif
                    
                    set c=c+1
                endloop
    			
                if b then
                    call d.destroy()
                    set i=i-1
                    exitwhen true
                endif
            endif
    		
            set i=i+1
        endloop
    	
        if COUNT==0 then
            call PauseTimer(TIMER)
        endif
    endfunction
    private function Actions takes nothing returns nothing
        local data d=data.create()
        local real x
        local real xu
        local real y
        local real yu
        local integer i=1
        
        set d.u=GetTriggerUnit()
        set d.p=GetOwningPlayer(d.u)
        set d.lvl=GetUnitAbilityLevel(d.u,ABIL)
        set d.update=Update(d.lvl)
        set d.maxtime=Time(d.lvl)
        set d.reachmax=ReachMax(d.lvl)
        set d.count=Orbitals(d.lvl)
        set d.dist=Distance(d.lvl)
        set d.startscale=StartScale(d.lvl)
    	
        set COUNT=COUNT+1
        set d.stack=COUNT
        if COUNT==1 then
            call TimerStart(TIMER,TIMEOUT,true,function Effects)
        endif
        set DATASTACK[COUNT]=d
    	
        call SetPlayerAbilityAvailable(d.p,ABIL,false)
        call UnitAddAbility(d.u,TARGET)
        call SetPlayerAbilityAvailable(d.p,TARGET,true)
        set xu=GetUnitX(d.u)
        set yu=GetUnitY(d.u)
        loop
            exitwhen i>d.count
    		
            set d.degree[i]=360.*i/I2R(d.count)
            set x=xu+d.dist*Cos(bj_DEGTORAD*d.degree[i])
            set y=yu+d.dist*Sin(bj_DEGTORAD*d.degree[i])
            static if CLK then
            set d.orbital[i]=CreateUnit(d.p,CasterId,x,y,d.degree[i]+90.)
        else
            set d.orbital[i]=CreateUnit(d.p,CasterId,x,y,d.degree[i]-90.)
        endif
        call SetUnitPathing(d.orbital[i],false)
        call SetUnitScale(d.orbital[i],d.startscale,d.startscale,d.startscale)
        set d.sfx[i]=AddSpecialEffectTarget(MDL,d.orbital[i],MDLAP)
        set d.gotem[i]=0
    		
        set i=i+1
    endloop
    	
    set DATA[GetHandleId(d.u)-OFFSET]=d
    call GroupAddUnit(GROUP,d.u)
    endfunction
    
    //======================================================================
    private function Conditions takes nothing returns boolean
        local data d
        
        if GetSpellAbilityId()==ABIL then
            call Actions()
        elseif GetSpellAbilityId()==TARGET then
            set d=DATA[GetHandleId(GetTriggerUnit())-OFFSET]
            set d.target=GetSpellTargetUnit()
            set d.mode=1
            call UnitRemoveAbility(d.u,TARGET)
            call SetPlayerAbilityAvailable(d.p,ABIL,true)
        elseif IsUnitInGroup(GetTriggerUnit(),GROUP) and GetTriggerEventId()==EVENT_PLAYER_UNIT_DEATH then
            set d=DATA[GetHandleId(GetTriggerUnit())-OFFSET]
            call d.destroy()
        endif
        return false
    endfunction
    private function Init takes nothing returns nothing
        local trigger t=CreateTrigger()
        local integer i=0
        
        loop
            exitwhen i>bj_MAX_PLAYERS
            call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_DEATH,null)
            set i=i+1
        endloop
        call TriggerAddCondition(t,Condition(function Conditions))
    	
        set BOOL=Condition(function Filt)
    	
        set SOUND=CreateSound(SND,false,false,true,10,10,"DefaultEAXON")
        call SetSoundDuration(SOUND,1579)
        call SetSoundChannel(SOUND,0)
        
        static if PRELOAD then
        set TARG=CreateUnit(Player(15),DUM,0.,0.,0.)
        call UnitAddAbility(TARG,TARGET)
        call UnitApplyTimedLife(TARG,'BTLF',.001)
        call Preload(MDL)
        call Preload(SFX)
    endif
    endfunction
    
    endscope


    ChangeLog:
    Changelog

    Boomerang:
    v1.02
    *Replaced onDestroy method with destroy method

    v1.01
    *Reorganized initial struct members storage for efficiency (d.a)
    *Removed needless GroupClear() call

    v1.00
    *Initial Release

    Orbital
    v1.02
    *Replaced onDestroy method with destroy method
    *Added custom user targeting function

    v1.01
    *Changed SetUnitState and GetUnitState to SetWidgetLife and GetWidgetLife
    *Updated distance calulation check
    *Stored orbital[c] coordinates when d.mode==1 and utilized where applicable
    *Properly uses ATTACK and DAMAGE configurables
    *Removed needless GroupClear() calls

    v1.00
    *Initial release
     

    Attached Files:

  2. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    no screens? :(


    Edit:

    I'm sorry but for me, is impossible to read your code..

    JASS:
    private struct data  
    	group g
        unit u
        unit boom
        effect sfx
        player p
        real face
    	real x
    	real y
    	real a
        real cosa
        real sina
        real dist
        real width
    	real xd
    	real yd
    	real damage
        real life
        real halfmaxticks
    	integer i=0
    	integer lvl
        integer maxticks
        boolean early=false


    >>

    JASS:
    
    private struct data  
        group g
        unit u
        unit boom
        effect sfx
        player p
        real face
        real x
        real y
        real a
        real cosa
        real sina
        real dist
        real width
        real xd
        real yd
        real damage
        real life
        real halfmaxticks
        integer i=0
        integer lvl
        integer maxticks
        boolean early=false
     
  3. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    You continue to have the same probs ;P.

    don't use this ever
    method onDestroy takes nothing returns nothing


    loop down
    exitwhen i>.count

    etc, etc.

    And yea, ur code's indenting is terrible ;P.
     
  4. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    I noticed the indenting, and I refuse to go through and update it in the thread, too many damned lines, feel free

    in the WE its indented properly (did you honestly think it wouldn't be? btw zero map downloads, so obviously not), no idea why the forums fudged it up - could have something to do with the fact that I coded a lot of it in Notepad++, and CnP'd to the WE, and from the WE to the thread topic post

    until I have a valid argument (that is actually explained in terms I understand) for why onDestroy is bad (as it wasn't two years ago), it remains - and the fact that its marginally slower then in-lining doesn't count (especially when it makes things more reader friendly when having to call it several times throughout the spell)

    loop down???
     
  5. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    You can easy replace

    JASS:
    method onDestroy takes nothing returns nothing
    // To
    method destroy takes nothing returns nothing


    Nest can explain better then me, because i really don't remember if is about a TriggerEvaculate/Execute and/or involve more things.
     
  6. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    I already explained onDestroy to you in detail in another thread... I'm not going to explain every single time you use it.
     
  7. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    i obviously didn't fully understand

    the only thing i got out of your explanation is that instead of a function call, its a trigger evaluation, which seems just about as fast to me, and since its only done once/spell.....

    and whats it matter if i loop up or loop down, the arithmetic and boolean evaluation exists regardless
     
  8. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    You understood perfectly then ;P.

    And it matters whether you loop up or down. It's compared to 0 vs comparing to a big number. As you loop up, it compares bigger and bigger numbers. As you loop down, it compares smaller and smaller.

    Check out linked lists that compare to 0 as sentinel vs ones that compare to the head. There's a pretty decent dif in speed ;P.
     
  9. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    JASS:
    exitwhen i>50

    is significantly slower then
    JASS:
    exitwhen i==0


    numbers!!!
     
  10. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    Slower by a tiny bit, yes ;P.
     
  11. PurgeandFire

    PurgeandFire zxcvmkgdfg

    Ratings:
    +513 / 0 / -0
    Interesting spells.

    Boomerang

    1) In this code:
    JASS:
    set d.a=Atan2(GetSpellTargetY()-d.y,GetSpellTargetX()-d.x)
        set d.cosa=Cos(d.a)
        set d.sina=Sin(d.a)
        set dx=GetSpellTargetX()-d.x
        set dy=GetSpellTargetY()-d.y


    dx could be moved to the top and d.a = Atan2(dy,dx)

    2) call GroupClear(GROUP)

    GroupEnum...etc auto-clears groups.

    3) Minor and doesn't make much of a difference, but GetOwningPlayer(d.u) can be simply GetTriggerPlayer().

    Orbital

    1) GroupClear()

    2) call SetUnitState(TARG,UNIT_STATE_LIFE,GetUnitState(TARG,UNIT_STATE_LIFE)+r) can simply be:
    call SetWidgetLife(TARG,GetWidgetLife(TARG)+r)

    3) Don't forget to null TARG in the filter function. ;D

    4) You have the variables ATTACK and DAMAGE defined, but you directly use ATTACK_TYPE_MAGIC and DAMAGE_TYPE_MAGIC in the UnitDamageTarget function.

    5) In the elseif d.mode==1 then block, you might want to set x/y = GetUnitX/Y(d.orbital[c]) since you retrieve their coordinates a few times.

    -----

    Mostly minor things. Overall, good job. :thup:
     
  12. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    @ GroupClear - better safe then sorry?

    is WidgetLife faster then UnitState?

    rest are solid suggestions!
     
  13. BlackRose

    BlackRose Forum User

    Ratings:
    +239 / 0 / -0
    I seem to experience this stuff too :(
     
  14. Laiev

    Laiev Hey Listen!!

    Ratings:
    +187 / 0 / -0
    yes
     
  15. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    updated - should be in an acceptable state
     
  16. Nestharus

    Nestharus o-o

    Ratings:
    +83 / 0 / -0
    It doesn't make sense that you are using onDestroy when you don't need the polymorphism...

    it'd literally be changing it to destroy and adding a deallocate call to the top of it and then you wouldn't generate useless functions and triggers, thus keeping the global scope from getting flooded with more useless crap. Not to mention it would save you a trigger evaluation.

    onDestroy is only useful if you need polymorphism as it allows you to store something like an object B that extends A as A and still destroy it properly (chained trigger conditions for proper destruction).

    Given that you aren't using inheritance in the slightest, onDestroy is totally pointless >.>.

    Keep in mind that more functions and globals will make a map's script run slower as it has to look through more garbage, so you really want to minimize how much you add to the map's global scope. Locals are fine, they will just slow down the function calls with the allocation, but you really want to minimize globals. It might not make a difference in your one script, but if someone has 30 scripts in their map that all minimize their impact, it will make quite a difference.

    Also, your argument on "it's fast enough" is poor. My insanely efficient systems all together in a simple hero line wars go on the verge of lagging because of the masses of units that are in the map (and that is with clever spawning to reduce the total number of units on the map at any given time). No more it's fast enough because my map is proof that that's a stupid argument, because even my insanely efficient stuff is barely fast enough. I had to actually recode some systems to work on entirely different ideas because they weren't fast enough ;p, and I had no periodic systems in the map, it was just the sheer number of units and events firing off every moment ;D.

    That map has made me even more of an efficiency freak because I had to really squeeze every little thing in order to prevent the map from lagging. The map itself had around 150 kb of code as well =P, and it's barely in alpha ;D.
     
  17. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    lol, to me, polymorphism is a form of biodiversity, or multiple genetic phenotypes for the same gene snippet within a given species population, anywho

    i can make that change - makes sense (first time)

    on the "its fast enough" argument, I claim DotA as exhibit B, which is terribly inefficient, and one of the biggest maps around, with the most things going on at once - and it runs just fine
     
  18. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    Polymorphism is the idea different structs can have the same methods that are different. For example, a violin and guitar can be played, but if they had a .play method, they'd be different methods.

    JASS:
    interface Chordophone
    	method play takes nothing returns nothing
    endinterface
    
    struct Violin
    
    	method play takes nothing returns nothing
    		call StartSound(gg_snd_ViolinNote)
    	endmethod
    endstruct
    
    struct Cello
    
    	method play takes nothing returns nothing
    		call StartSound(gg_snd_CelloNote)
    	endmethod
    endstruct
    
    // here's where it matters
    function PlayChordophone takes Chordophone instrument returns nothing
        call instrument.play()
    endfunction
    
    local Violin violin = Violin.create()
    local Cello cello = Cello.create()
    call PlayChordophone(violin) // it's okay, it extends Chordophone
    clal PlayChordophone(cello)  // still extends Chordophone
     
  19. NeuroToxin

    NeuroToxin New Member

    Ratings:
    +46 / 0 / -0
  20. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    v1.02

    should meet acceptance criteria

    *used that script aligner - overall the code looks better, buts its still far from ideal
     

Share This Page