Spellpack Boomerang and Orbital

emjlr3

Change can be a good thing
Reaction score
395
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
    //*         <a href="http://www.wc3c.net/showthread.php?t=101322" target="_blank" class="link link--external" rel="nofollow ugc noopener">http://www.wc3c.net/showthread.php?t=101322</a>
    //*     *GroupUtils library
    //*         <a href="http://www.wc3c.net/showthread.php?t=104464" target="_blank" class="link link--external" rel="nofollow ugc noopener">http://www.wc3c.net/showthread.php?t=104464</a>
    //*     *A copy of this trigger
    //*     *Although not required, the BoundSentinel library is recommended
    //*         <a href="http://www.wc3c.net/showthread.php?t=102576" target="_blank" class="link link--external" rel="nofollow ugc noopener">http://www.wc3c.net/showthread.php?t=102576</a>
    //*
    //* (requires vJass)   More abilities at <a href="http://www.thehelper.net/forums" class="link link--internal">http://www.thehelper.net/forums</a>
    //*
    //*     Credits:
    //*     *Vexorian for his TimerUils library
    //*     *Rising_Dusk for his GroupUtils library
    //*
    //* Important:
    //*     *SFX strings can be set to &quot;&quot; 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&#039;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&#039;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&#039;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			= &#039;A005&#039; // Boomerang ability rawcode
        private constant integer 	DISARMABIL		= &#039;Abun&#039; // Cargo Hold (Orc Burrow) ability rawcode
        private constant integer    HARVEST         = &#039;Ahrl&#039; // Harvest (Ghouls Lumber) ability rawcode
        private constant integer    MAXHANDLES      = 8190 // Map&#039;s max handle count, don&#039;t change if unsure
        private constant integer    MISSILE         = &#039;n000&#039; // Boomerang dummy unit rawcode
        private constant integer    OFFSET          = 0x100000 // Map&#039;s first handle id, don&#039;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         = &quot;Abilities\\Weapons\\BloodElfSpellThiefMISSILE\\BloodElfSpellThiefMISSILE.mdl&quot; // Boomerang model
        private constant string     BOOMSFXAP       = &quot;origin&quot; // Attachment point for BOOMSFX on MISSILE
        private constant string		BUFFSFX			= &quot;Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl&quot; // Target buff model
        private constant string     BUFFSFXAP       = &quot;overhead&quot; // Attachment point for BUFFSFX
        private constant string		TARGETSFX		= &quot;Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl&quot; // Boomerang strikes target model
        private constant string     TARGETSFXAP     = &quot;chest&quot; // 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&#039;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,&quot;harvest&quot;,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&gt;=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&lt;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&gt;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&gt;=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,&#039;Aloc&#039;)
    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&gt;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,&#039;BTLF&#039;,.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,&#039;Aloc&#039;)
call PauseUnit(TREEDUMMY,true)
endif
endfunction

endscope</i></i></i></i>


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 &quot;Orbital&quot; ability found in this map
    //*     *A dummy unit, like the &quot;Caster Dummy&quot; 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
    //*         <a href="http://www.wc3c.net/showthread.php?t=102576" target="_blank" class="link link--external" rel="nofollow ugc noopener">http://www.wc3c.net/showthread.php?t=102576</a>
    //*
    //* (requires vJass)   More abilities at <a href="http://www.thehelper.net/forums" class="link link--internal">http://www.thehelper.net/forums</a>
    //*
    //*     Credits:
    //*     *DotA for spell architecture inspiration
    //*
    //* Important:
    //*     *SFX strings can be set to &quot;&quot; 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	    = &#039;A002&#039; // Orbital ability rawcode
        private constant integer        DUM         = &#039;n000&#039; // Orbital dummy unit rawcode
        private constant integer        MAXHANDLES  = 8190 // Map&#039;s max handle count, don&#039;t change if unsure
        private constant integer        OFFSET      = 0x100000 // Map&#039;s first handle id, don&#039;t change if unsure
        private constant integer        OMAX        = 4 // Max number of orbitals
        private constant integer		TARGET	    = &#039;A004&#039; // 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 	    = &quot;Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl&quot; // Orbital model
        private constant string         MDLAP       = &quot;origin&quot; // Attachment point for MDL
        private constant string			SFX		    = &quot;Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl&quot; // Effect on targets following collision
        private constant string         SND         = &quot;Abilities\\Spells\\Other\\SoulBurn\\SoulBurn1.wav&quot; // 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&gt;.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&gt;COUNT
            set d=DATASTACK<i>
		
            if d.time&gt;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&gt;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&gt;=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&gt;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)&lt;=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&gt;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&gt;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,&quot;DefaultEAXON&quot;)
    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,&#039;BTLF&#039;,.001)
    call Preload(MDL)
    call Preload(SFX)
endif
endfunction

endscope</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>


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
 

Attachments

  • emjlr3 - Boomerang_Orbital_C.w3x
    138.1 KB · Views: 656

Laiev

Hey Listen!!
Reaction score
188
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
 

Nestharus

o-o
Reaction score
84
You continue to have the same probs ;P.

don't use this ever
[ljass]method onDestroy takes nothing returns nothing[/ljass]


loop down
[ljass]exitwhen i>.count[/ljass]

etc, etc.

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

emjlr3

Change can be a good thing
Reaction score
395
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???
 

Laiev

Hey Listen!!
Reaction score
188
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.
 

Nestharus

o-o
Reaction score
84
I already explained onDestroy to you in detail in another thread... I'm not going to explain every single time you use it.
 

emjlr3

Change can be a good thing
Reaction score
395
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
 

Nestharus

o-o
Reaction score
84
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 addition and boolean evaluation exists regardless

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.
 

emjlr3

Change can be a good thing
Reaction score
395
JASS:
exitwhen i&gt;50

is significantly slower then
JASS:
exitwhen i==0


numbers!!!
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
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)[ljass]call GroupClear(GROUP)[/ljass]

GroupEnum...etc auto-clears groups.

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

Orbital

1) GroupClear()

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

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 [ljass]elseif d.mode==1 then[/ljass] 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:
 

emjlr3

Change can be a good thing
Reaction score
395
@ GroupClear - better safe then sorry?

is WidgetLife faster then UnitState?

rest are solid suggestions!
 

BlackRose

Forum User
Reaction score
239
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

I seem to experience this stuff too :(
 

emjlr3

Change can be a good thing
Reaction score
395
updated - should be in an acceptable state
 

Nestharus

o-o
Reaction score
84
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.
 

emjlr3

Change can be a good thing
Reaction score
395
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
 

tooltiperror

Super Moderator
Reaction score
231
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 [ljass].play[/ljass] 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&#039;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&#039;s okay, it extends Chordophone
clal PlayChordophone(cello)  // still extends Chordophone
 

emjlr3

Change can be a good thing
Reaction score
395
v1.02

should meet acceptance criteria

*used that script aligner - overall the code looks better, buts its still far from ideal
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    I tried to extract it but the thing is pretty stuck. Idk what else I can use this for
  • Varine Varine:
    I'll throw it into my scrap stuff box, I'm sure can be used for something
  • Varine Varine:
    I have spare parts for like, everything BUT that block lol. Oh well, I'll print this shit next week I guess. Hopefully it fits
  • Varine Varine:
    I see that, despite your insistence to the contrary, we are becoming a recipe website
  • Varine Varine:
    Which is unique I guess.
  • The Helper The Helper:
    Actually I was just playing with having some kind of mention of the food forum and recipes on the main page to test and see if it would engage some of those people to post something. It is just weird to get so much traffic and no engagement
  • The Helper The Helper:
    So what it really is me trying to implement some kind of better site navigation not change the whole theme of the site
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top