[Log in / Register]
| News | Chat | Pastebin | Donations | Tutorials | Rules | Forums |
| Maps | Skins | Icons | Models | Spells | Tools | Jass | Packs | Hosted Projects | Starcraft II Modding | Starcraft II Resources | Galaxy Wiki |
(Keeps Hive Alive)
Go Back   The Hive Workshop > Spells


Reply
 
Thread Tools
The Hive Workshop Spells:
Boomerang! 1.4.1
by Deaod
Images
Highslide JS
Details
Uploaded:06:56, 4th Jun 2009
Last Updated:16:08, 28th Jan 2012
Keywords:None
Type:Target Ground, Target Object
Category:vJASS

Throw a deadly boomerang at your enemies.

This uses vJass (JassHelper 0.9.H.0 and up), GroupUtils, xefx, DestructableLib and IsTerrainWalkable.

In-Game-Commands:
- "-reset": spawns some footmen
- "-level <level>": Set the heros level to the level you specified. Note that you cannot decrease it that way.
- "-handleid": creates a location, displays its handlid-0x100001, and then destroys it.
- "-credits": displays credits
- "-commands": displays this list
- "-clear": removes all messages ingame


Changelog:
06/03/2009 - Version 1.0.0
- initial release

06/04/2009 - Version 1.1.0
- stopped the spell from triggering when using unrelated abilities
- added a minimum range
- added support for trees
- added damage reduction when hitting multiple targets
- added a hit sound
- switched from direct dummy unit usage to xe

06/05/2009 - Version 1.2.0
- fixed two bugs reported by -JonNny
- enabled damaging units more than once

06/08/2009 - Version 1.2.1
- some optimizations and the final touches (i hope this really is the final version)

06/15/2009 - Version 1.3.0
- added an option to launch a boomerang to the left side, either additionally or exclusively
- made the boomerang collide with unwalkable terrain (after checking for trees)

07/12/2009 - Version 1.3.1
- more and better comments
- made configuring valid targets easier
- boomerangs colliding with unwalkable terrain is now optional
- made an additional version where the calibration section uses formulae (for those who cant/dont want to configure arrays)

08/07/2009 - Version 1.3.3
- Compatibility with 1.24
- 1.3.2 was a private build for a request

06/10/2011 - Version 1.3.4
- fixed a few double frees

07/11/2011 - Version 1.4.0
- you can now change the width of the path of the boomerang ( BOOMERANG_FOCUS )
- you can now configure the spell to simply ignore trees.
- a few optimizations in the background (mainly using static ifs) and a bit of cleanup

01/28/2012 - Version 1.4.1
- boomerangs now follow unit targets
- each boomerang now has its own group of units it has already hit
- cleaned up the code with more meaningful names


Code (Array version) + Credits:
Jass:
library_once Boomerang requires GroupUtils, xefx, DestructableLib, IsTerrainWalkable
    
    private keyword Data // DO NOT TOUCH; configuration is below
    
    // Credits:
    //  - Ciebron for the inspiration
    //  - -JonNny for reporting some bugs
    //  - Rising_Dusk for his GroupUtils library
    //  - Anitarf for his IsTerrainWalkable library
    //  - Vexorian for JassHelper and xe
    //  - PipeDream for Grimoire
    //  - PitzerMike for JassNewGenPack and DestructableLib
    //  - MindWorX for JassNewGenPack
    //  - SFilip for TESH
    
    globals
        private constant    real                    TICK                    = 1./40 // granulation of boomerang movement
        private constant    integer                 AID                     = 'A000' // the ability triggering this spell
        private             real            array   DAMAGE                  // damage dealt by the boomerang to units it hits
        private             real            array   DAMAGE_ABSORPTION       // the damage dealt is reduced by this much everytime the boomerang hits a unit or a tree
        private constant    boolean                 DAMAGE_ABSORPTION_RELATIVE = true // is DAMAGE_ABSORPTION to be treated as an absolute value or a value relative to the current damage
        private constant    real                    DAMAGE_BOUNDARY         = 10. // once the damage is lower or equal to this, the boomerang stops flying
        private constant    string                  BOOMERANG_MODEL         = "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl" // this is the model representing the boomerang
        private constant    real                    BOOMERANG_COLLSIZE      = 96. // the AoE in which units are damaged by the boomerang
        private constant    real                    BOOMERANG_SIZE          = 1.25 // the scaling of the boomerang
        private constant    real                    BOOMERANG_HEIGHT        = 64. // the Z height of the boomerang
        private constant    real            array   BOOMERANG_SPEED         // the speed at which the boomerang moves // due to limitations this is only an approximation
        private constant    real                    BOOMERANG_FOCUS         = 2. // the higher this number the more focused is the path of the boomerang. Any value greater than 0 should work. Values below 2 might not look so good.
        private constant    boolean                 ALLOW_MULTIPLE_HITS     = true // if this is true, the boomerang can hit units more than once on his path
        private constant    boolean                 USE_RIGHT_BOOMERANG     = true // just avoid setting both to false, okay?
        private constant    boolean                 USE_LEFT_BOOMERANG      = true
        private constant    boolean                 COLLIDE_WITH_GROUND     = true // do boomerangs collide with unwalkable terrain?
        private             real            array   MIN_RANGE               // minimum throwing distance for the boomerang
        private constant    boolean                 IGNORE_TREES            = true // if true, the boomerang will just fly through the trees without doing anything
        private constant    boolean                 KILL_TREES              = false // if true, trees are killed once the boomerang hits one, if this is false, the boomerang stops flying
        private constant    string                  HIT_FX                  = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" // when the boomerang hits a unit, this effect is spawned on the unit hit
        private constant    string                  HIT_FX_ATTPT            = "chest" // the beforementioned effect will be attached to this point
        private constant    attacktype              ATTACK_TYPE             = ATTACK_TYPE_MAGIC // the attack type of the damage the boomerang deals
        private constant    damagetype              DAMAGE_TYPE             = DAMAGE_TYPE_MAGIC // the damage type of the damage the boomerang deals
        private constant    weapontype              WEAPON_TYPE             = WEAPON_TYPE_METAL_MEDIUM_SLICE // sound when boomerang hits a unit
    endglobals
    
    private function Damage takes integer level returns real // PROXY
        return DAMAGE[level]
    endfunction
    
    private function Damage_Absorption takes integer level returns real // PROXY
        return DAMAGE_ABSORPTION[level]
    endfunction
    
    private function Boomerang_Speed takes integer level returns real // PROXY
        return BOOMERANG_SPEED[level]
    endfunction
    
    private function Min_Range takes integer level returns real // PROXY
        return MIN_RANGE[level]
    endfunction
    
    private function SetUpSpellData takes nothing returns nothing
        set DAMAGE[1]=200. // initially deals 200 damage
        set DAMAGE[2]=275.
        set DAMAGE[3]=350.
        
        set DAMAGE_ABSORPTION[1]=0.16 // lowers damage dealt by 16% of current damage.
        set DAMAGE_ABSORPTION[2]=0.12
        set DAMAGE_ABSORPTION[3]=0.08
        
        set BOOMERANG_SPEED[1]=600.
        set BOOMERANG_SPEED[2]=600.
        set BOOMERANG_SPEED[3]=600.
        
        set MIN_RANGE[1]=200.
        set MIN_RANGE[2]=200.
        set MIN_RANGE[3]=200.
    endfunction
    
    private function ValidTarget takes unit u, Data s returns boolean
        return IsUnitType(u, UNIT_TYPE_DEAD)==false /*         Delimited Comments FTW
        */ and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false /*
        */ and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false /*
        */ and (s.boomerang!=1 or (not IsUnitInGroup(u, s.damaged1))) /*
        */ and (s.boomerang!=2 or (not IsUnitInGroup(u, s.damaged2))) /*
        */ and IsUnitEnemy(u, GetOwningPlayer(s.caster))
    endfunction
    
    // This is shit. Don't touch shit.
    
    globals
        private rect R=Rect(0,0,1,1)
        private Data tmps
        private boolean OverTree=false
    endglobals

    private struct Data
        unit caster // caster
        integer level
        real distance // planned distance
        real launchX // x-coord of launch point
        real launchY // y-coord
        unit target // targeted unit
        real targetX // targeted x-coord
        real targetY // targeted y-coord
        real currentAngle // current angle
        real baseAngle // base-angle
        integer boomerang
        xefx dum1 // boomerang dummy 1
        xefx dum2 // boomerang dummy 2
        boolean d1a // is dummy 1 active
        boolean d2a // is dummy 2 active
        real dam1 // damage dealt to the next unit the boomerang hits
        real dam2 // damage dealt to the next unit the boomerang hits
        group damaged1
        group damaged2
        boolean destroyed=false
        
        static boolexpr DamageFilter
        static boolexpr TreeFilter
        
        private integer i
        
        private static thistype array Structs
        private static timer T=CreateTimer()
        private static integer Count=0
        
        method onDestroy takes nothing returns nothing
            set .caster=null
            if .d1a then
                call .dum1.destroy()
            endif
            if .d2a then
                call .dum2.destroy()
            endif
            call ReleaseGroup(.damaged1)
            call ReleaseGroup(.damaged2)
            set .target=null
            set .destroyed=true
            // clean your struct here
            set thistype.Count=thistype.Count-1
            set thistype.Structs[.i]=thistype.Structs[thistype.Count]
            set thistype.Structs[.i].i=.i
            if thistype.Count==0 then
                call PauseTimer(thistype.T)
            endif
        endmethod
        
        private static method UnitDistCheck takes nothing returns nothing
        local unit u=GetEnumUnit()
        local real x=GetUnitX(u)
        local real y=GetUnitY(u)
        local real d1x=tmps.dum1.x
        local real d1y=tmps.dum1.y
        local real d2x=tmps.dum2.x
        local real d2y=tmps.dum2.y
            if tmps.boomerang==1 then
                if ((not tmps.d1a) or (x-d1x)*(x-d1x)+(y-d1y)*(y-d1y)>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) then
                    call GroupRemoveUnit(tmps.damaged1, u)
                endif
            elseif tmps.boomerang==2 then
                if ((not tmps.d2a) or (x-d2x)*(x-d2x)+(y-d2y)*(y-d2y)>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) then
                    call GroupRemoveUnit(tmps.damaged2, u)
                endif
            endif
            set u=null
        endmethod
        
        private static method DamageFilterFunc takes nothing returns boolean
        local unit u=GetFilterUnit()
            // check if unit is a valid target for damage
            if ValidTarget(u, tmps) then
                if tmps.boomerang==1 then // tmpd hold the current boomerang; 1 for left-wing, 2 for right-wing
                    // damage the unit; if the unit for some reason cant be damaged, dont continue
                    if UnitDamageTarget(tmps.caster, u, tmps.dam1, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
                        call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
                        call GroupAddUnit(tmps.damaged1, u)
                        static if DAMAGE_ABSORPTION_RELATIVE then
                            set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
                        else
                            set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
                        endif
                        if tmps.dam1<=DAMAGE_BOUNDARY then // damage has become too low
                            call tmps.dum1.destroy() // destroy the boomerang dummy
                            set tmps.d1a=false // mark that boomerang as destroyed
                            if not tmps.d2a then // if the other boomerang is dead as well, destroy the spells instance
                                call tmps.destroy()
                            endif
                        endif
                    endif
                elseif tmps.boomerang==2 then // pretty much the same here
                    if UnitDamageTarget(tmps.caster, u, tmps.dam2, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
                        call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
                        call GroupAddUnit(tmps.damaged2, u)
                        static if DAMAGE_ABSORPTION_RELATIVE then
                            set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
                        else
                            set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
                        endif
                        if tmps.dam2<=DAMAGE_BOUNDARY then
                            call tmps.dum2.destroy()
                            set tmps.d2a=false
                            if not tmps.d1a then
                                call tmps.destroy()
                            endif
                        endif
                    endif
                endif
            endif
            set u=null
            return false
        endmethod
        
        private static method TreeFilterFunc takes nothing returns boolean
        local destructable d=GetFilterDestructable()
        local real x
        local real y
        local real bx
        local real by
            if (not IsDestructableDead(d)) and IsDestructableTree(d) then // filter out dead and non-tree destructables
                set OverTree=true
                static if not IGNORE_TREES then
                    set x=GetWidgetX(d)
                    set y=GetWidgetY(d)
                    if tmps.boomerang==1 and tmps.d1a then // same as above
                        set bx=tmps.dum1.x
                        set by=tmps.dum1.y
                        if (x-bx)*(x-bx)+(y-by)*(y-by)<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then // tree must be inside the collision radius
                            static if KILL_TREES then // if the boomerang is allowed to kill trees, do so
                                call KillDestructable(d)
                                // but adjust the damage as if a unit had been hit
                                static if DAMAGE_ABSORPTION_RELATIVE then
                                    set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
                                else
                                    set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
                                endif
                                if tmps.dam1<=DAMAGE_BOUNDARY then // same as above
                                    call tmps.dum1.destroy()
                                    set tmps.d1a=false
                                    if not tmps.d2a then
                                        call tmps.destroy()
                                    endif
                                endif
                            else // if Trees may not be destroyed, destroy the boomerang instantly
                                call tmps.dum1.destroy()
                                set tmps.d1a=false
                                if not tmps.d2a then // and end the spell instance if appropriate
                                    call tmps.destroy()
                                endif
                            endif
                        endif
                    elseif tmps.boomerang==2 and tmps.d2a then // next section is the same as above
                        set bx=tmps.dum2.x
                        set by=tmps.dum2.y
                        if (x-bx)*(x-bx)+(y-by)*(y-by)<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then
                             static if KILL_TREES then
                                call KillDestructable(d)
                                static if DAMAGE_ABSORPTION_RELATIVE then
                                    set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
                                else
                                    set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
                                endif
                                if tmps.dam2<=DAMAGE_BOUNDARY then
                                    call tmps.dum2.destroy()
                                    set tmps.d2a=false
                                    if not tmps.d1a then
                                        call tmps.destroy()
                                    endif
                                endif
                            else
                                call tmps.dum2.destroy()
                                set tmps.d2a=false
                                if not tmps.d1a then
                                    call tmps.destroy()
                                endif
                            endif
                        endif
                    endif
                endif
            endif
            set d=null
            return false
        endmethod
        
        private static method Callback takes nothing returns nothing
        local integer i=thistype.Count-1
        local thistype s
        local real r
        local real x
        local real y
            loop
                exitwhen i<0
                set s=thistype.Structs[i]
                //
                // make the boomerang home, even if the caster moves
                if s.target!=null then
                    set s.targetX=GetUnitX(s.target)
                    set s.targetY=GetUnitY(s.target)
                endif
                set s.launchX=GetUnitX(s.caster)
                set s.launchY=GetUnitY(s.caster)
                set s.distance=SquareRoot( (s.targetX-s.launchX)*(s.targetX-s.launchX)+(s.targetY-s.launchY)*(s.targetY-s.launchY) )
                set s.baseAngle=Atan2(s.targetY-s.launchY, s.targetX-s.launchX)-bj_PI/(BOOMERANG_FOCUS*2)
                // functions for moving the boomerang:
                // r(a)=distance*Sin(BOOMERANG_FOCUS*a) // a is the angle and goes from 90 to 0 // distance from center point
                // x(a)=Cos(a)*r(a) // x and y coordinates in relation to the location it was cast.
                // y(a)=Sin(a)*r(a) // note that i inlined some things to allow casting the boomerang in all directions from any point on the map
                set r=(s.distance*Sin(BOOMERANG_FOCUS*s.currentAngle))
                set tmps=s
                static if ALLOW_MULTIPLE_HITS and USE_RIGHT_BOOMERANG then
                    set s.boomerang=1
                    call ForGroup(s.damaged1, function Data.UnitDistCheck)
                endif
                static if ALLOW_MULTIPLE_HITS and USE_LEFT_BOOMERANG then
                    set s.boomerang=2
                    call ForGroup(s.damaged2, function Data.UnitDistCheck)
                endif
                if s.d1a then // is dummy 1 active
                    set s.boomerang=1 // indicate were working with dummy 1
                    set x=s.launchX+(Cos(s.currentAngle+s.baseAngle)*r)
                    set y=s.launchY+(Sin(s.currentAngle+s.baseAngle)*r)
                    
                    set s.dum1.x=x
                    set s.dum1.y=y
                    
                    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
                    static if not COLLIDE_WITH_GROUND and IGNORE_TREES then
                    else
                        set OverTree=false
                        call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
                        call EnumDestructablesInRect(R, Data.TreeFilter, null)
                        static if COLLIDE_WITH_GROUND then
                            if not OverTree and not IsTerrainWalkable(x,y) and not s.destroyed then // if boomerangs collide with unwalkable terrain and the terrain is unwalkable
                                call s.dum1.destroy() // destroy the dummy
                                set s.d1a=false
                                if not s.d2a then // if the other dummy is inactive
                                    call s.destroy() // destroy this spell instance
                                endif
                            endif
                        endif
                    endif
                endif
                if s.d2a then
                    set s.boomerang=2
                    set x=s.launchX+(Cos(bj_PI/BOOMERANG_FOCUS-s.currentAngle+s.baseAngle)*r)
                    set y=s.launchY+(Sin(bj_PI/BOOMERANG_FOCUS-s.currentAngle+s.baseAngle)*r)
                    
                    set s.dum2.x=x
                    set s.dum2.y=y
                    
                    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
                    static if not COLLIDE_WITH_GROUND and IGNORE_TREES then
                    else
                        set OverTree=false
                        call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
                        call EnumDestructablesInRect(R, Data.TreeFilter, null)
                        static if COLLIDE_WITH_GROUND then
                            if not OverTree and not IsTerrainWalkable(x,y) and not s.destroyed then
                                call s.dum2.destroy()
                                set s.d2a=false
                                if not s.d1a then
                                    call s.destroy()
                                endif
                            endif
                        endif
                    endif
                endif
                
                set s.currentAngle=s.currentAngle-(TICK*(bj_PI/(BOOMERANG_FOCUS*2))*(Boomerang_Speed(s.level)/s.distance))
                if s.currentAngle<=0 or IsUnitType(s.caster, UNIT_TYPE_DEAD)==true then
                    // stop this spell when caster is dead or the boomerang is at the casters position again.
                    call s.destroy()
                endif
                // do your things here, dont forget to call s.destroy() somewhen
                //
                set i=i-1
            endloop
        endmethod
        
        static method SpellCond takes nothing returns boolean
            return GetSpellAbilityId()==AID
        endmethod
        
        static method create takes nothing returns thistype
        local thistype s=thistype.allocate()
            set s.target=GetSpellTargetUnit()
            if s.target==null or s.target==GetTriggerUnit() then
                set s.target=null
                set s.targetX=GetSpellTargetX()
                set s.targetY=GetSpellTargetY()
            else
                set s.targetX=GetUnitX(s.target)
                set s.targetY=GetUnitY(s.target)
            endif
            set s.caster=GetTriggerUnit()
            set s.launchX=GetUnitX(s.caster)
            set s.launchY=GetUnitY(s.caster)
            set s.level=GetUnitAbilityLevel(s.caster, AID)
            set s.distance=SquareRoot( ((s.targetX-s.launchX)*(s.targetX-s.launchX))+((s.targetY-s.launchY)*(s.targetY-s.launchY)) )
            if s.distance==0.0 then
                set s.baseAngle=(GetUnitFacing(s.caster)*bj_DEGTORAD)
            else
                set s.baseAngle=Atan2(s.targetY-s.launchY, s.targetX-s.launchX)
            endif
            if s.distance<Min_Range(s.level) then // enforce the minimum distance
                set s.distance=Min_Range(s.level)
                set s.targetX=s.launchX+s.distance*Cos(s.baseAngle)
                set s.targetY=s.launchY+s.distance*Sin(s.baseAngle)
            endif
            set s.baseAngle=s.baseAngle-(bj_PI/(BOOMERANG_FOCUS*2))
            set s.currentAngle=(bj_PI/BOOMERANG_FOCUS)
            set s.d1a=false
            set s.d2a=false
            static if USE_RIGHT_BOOMERANG then
                set s.d1a=true
                set s.dam1=Damage(s.level)
                
                set s.dum1=xefx.create(s.launchX, s.launchY, 0)
                set s.dum1.fxpath=BOOMERANG_MODEL
                set s.dum1.scale=BOOMERANG_SIZE
                set s.dum1.z=BOOMERANG_HEIGHT
                
                set s.damaged1=NewGroup()
            endif
            static if USE_LEFT_BOOMERANG then
                set s.d2a=true
                set s.dam2=Damage(s.level)
                
                set s.dum2=xefx.create(s.launchX, s.launchY, 0)
                set s.dum2.fxpath=BOOMERANG_MODEL
                set s.dum2.scale=BOOMERANG_SIZE
                set s.dum2.z=BOOMERANG_HEIGHT
                
                set s.damaged2=NewGroup()
            endif
            
            // initialize the struct here
            set thistype.Structs[thistype.Count]=s
            set s.i=thistype.Count
            if thistype.Count==0 then
                call TimerStart(thistype.T, TICK, true, function thistype.Callback)
            endif
            set thistype.Count=thistype.Count+1
            
            if (not s.d1a) and (not s.d2a) then
                call s.destroy()
                return 0
            endif
            return s
        endmethod
        
        private static method onInit takes nothing returns nothing
        local trigger t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function Data.SpellCond))
            call TriggerAddAction(t, function Data.create)
            
            set Data.DamageFilter=Condition(function Data.DamageFilterFunc)
            set Data.TreeFilter=Condition(function Data.TreeFilterFunc)
            
            call SetUpSpellData()
        endmethod
    endstruct

endlibrary

Code (Formula version) + Credits:
Jass:
library_once Boomerang requires GroupUtils, xefx, DestructableLib, IsTerrainWalkable
    
    private keyword Data // DO NOT TOUCH; configuration is below
    
    // Credits:
    //  - Ciebron for the inspiration
    //  - -JonNny for reporting some bugs
    //  - Rising_Dusk for his GroupUtils library
    //  - Anitarf for his IsTerrainWalkable library
    //  - Vexorian for JassHelper and xe
    //  - PipeDream for Grimoire
    //  - PitzerMike for JassNewGenPack and DestructableLib
    //  - MindWorX for JassNewGenPack
    //  - SFilip for TESH
    
    globals
        private constant    real                    TICK                    = 1./40 // granulation of movement
        private constant    integer                 AID                     = 'A000' // the ability triggering this spell
        private constant    boolean                 DAMAGE_ABSORPTION_RELATIVE = true // is DAMAGE_ABSORPTION to be treated as an absolute value or a value relative to the current damage
        private constant    real                    DAMAGE_BOUNDARY         = 10. // once the damage is lower or equal to this, the boomerang stops flying
        private constant    string                  BOOMERANG_MODEL         = "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl" // this is the model representing the boomerang
        private constant    real                    BOOMERANG_COLLSIZE      = 96. // the AoE in which units are damaged by the boomerang
        private constant    real                    BOOMERANG_SIZE          = 1.25 // the scaling of the boomerang
        private constant    real                    BOOMERANG_HEIGHT        = 64. // the Z height of the boomerang
        private constant    real                    BOOMERANG_FOCUS         = 2 // the higher this number the more focused is the path of the boomerang. Any value greater than 0 should work. Values below 2 might not look so good.
        private constant    boolean                 ALLOW_MULTIPLE_HITS     = true // if this is true, the boomerang can hit units more than once on his path
        private constant    boolean                 USE_RIGHT_BOOMERANG     = true // just avoid setting both to false, okay?
        private constant    boolean                 USE_LEFT_BOOMERANG      = true
        private constant    boolean                 COLLIDE_WITH_GROUND     = true // do boomerangs collide with unwalkable terrain?
        private constant    boolean                 IGNORE_TREES            = true // if true, the boomerang will just fly through the trees without doing anything
        private constant    boolean                 KILL_TREES              = false // if true, trees are killed once the boomerang hits one, if this is false, the boomerang stops flying
        private constant    string                  HIT_FX                  = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" // when the boomerang hits a unit, this effect is spawned on the unit hit
        private constant    string                  HIT_FX_ATTPT            = "chest" // the beforementioned effect will be attached to this point
        private constant    attacktype              ATTACK_TYPE             = ATTACK_TYPE_MAGIC // the attack type of the damage the boomerang deals
        private constant    damagetype              DAMAGE_TYPE             = DAMAGE_TYPE_MAGIC // the damage type of the damage the boomerang deals
        private constant    weapontype              WEAPON_TYPE             = WEAPON_TYPE_METAL_MEDIUM_SLICE // sound when boomerang hits a unit
    endglobals
    
    private function Damage takes integer level returns real
        return 125. + level * 75
    endfunction
    
    private function Damage_Absorption takes integer level returns real
        return 0.2 - level * 0.04
    endfunction
    
    private function Boomerang_Speed takes integer level returns real
        return 600.
    endfunction
    
    private function Min_Range takes integer level returns real
        return 200.
    endfunction
    
    private function ValidTarget takes unit u, Data s returns boolean
        return IsUnitType(u, UNIT_TYPE_DEAD)==false /*         Delimited Comments FTW
        */ and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false /*
        */ and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false /*
        */ and (s.boomerang!=1 or (not IsUnitInGroup(u, s.damaged1))) /*
        */ and (s.boomerang!=2 or (not IsUnitInGroup(u, s.damaged2))) /*
        */ and IsUnitEnemy(u, GetOwningPlayer(s.caster))
    endfunction
    
    // This is shit. Don't touch shit.
    
    globals
        private rect R=Rect(0,0,1,1)
        private Data tmps
        private boolean OverTree=false
    endglobals

    private struct Data
        unit caster // caster
        integer level
        real distance // planned distance
        real launchX // x-coord of launch point
        real launchY // y-coord
        unit target // targeted unit
        real targetX // targeted x-coord
        real targetY // targeted y-coord
        real currentAngle // current angle
        real baseAngle // base-angle
        integer boomerang // which boomerang is currently active
        xefx dum1 // boomerang dummy 1
        xefx dum2 // boomerang dummy 2
        boolean d1a // is dummy 1 active
        boolean d2a // is dummy 2 active
        real dam1 // damage dealt to the next unit the boomerang hits
        real dam2 // damage dealt to the next unit the boomerang hits
        group damaged1
        group damaged2
        boolean destroyed=false
        
        static boolexpr DamageFilter
        static boolexpr TreeFilter
        
        private integer i
        
        private static thistype array Structs
        private static timer T=CreateTimer()
        private static integer Count=0
        
        method onDestroy takes nothing returns nothing
            set .caster=null
            if .d1a then
                call .dum1.destroy()
            endif
            if .d2a then
                call .dum2.destroy()
            endif
            call ReleaseGroup(.damaged1)
            call ReleaseGroup(.damaged2)
            set .target=null
            set .destroyed=true
            // clean your struct here
            set thistype.Count=thistype.Count-1
            set thistype.Structs[.i]=thistype.Structs[thistype.Count]
            set thistype.Structs[.i].i=.i
            if thistype.Count==0 then
                call PauseTimer(thistype.T)
            endif
        endmethod
        
        private static method UnitDistCheck takes nothing returns nothing
        local unit u=GetEnumUnit()
        local real x=GetUnitX(u)
        local real y=GetUnitY(u)
        local real d1x=tmps.dum1.x
        local real d1y=tmps.dum1.y
        local real d2x=tmps.dum2.x
        local real d2y=tmps.dum2.y
            if tmps.boomerang==1 then
                if ((not tmps.d1a) or (x-d1x)*(x-d1x)+(y-d1y)*(y-d1y)>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) then
                    call GroupRemoveUnit(tmps.damaged1, u)
                endif
            elseif tmps.boomerang==2 then
                if ((not tmps.d2a) or (x-d2x)*(x-d2x)+(y-d2y)*(y-d2y)>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) then
                    call GroupRemoveUnit(tmps.damaged2, u)
                endif
            endif
            set u=null
        endmethod
        
        private static method DamageFilterFunc takes nothing returns boolean
        local unit u=GetFilterUnit()
            // check if unit is a valid target for damage
            if ValidTarget(u, tmps) then
                if tmps.boomerang==1 then // tmpd hold the current boomerang; 1 for left-wing, 2 for right-wing
                    // damage the unit; if the unit for some reason cant be damaged, dont continue
                    if UnitDamageTarget(tmps.caster, u, tmps.dam1, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
                        call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
                        call GroupAddUnit(tmps.damaged1, u)
                        static if DAMAGE_ABSORPTION_RELATIVE then
                            set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
                        else
                            set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
                        endif
                        if tmps.dam1<=DAMAGE_BOUNDARY then // damage has become too low
                            call tmps.dum1.destroy() // destroy the boomerang dummy
                            set tmps.d1a=false // mark that boomerang as destroyed
                            if not tmps.d2a then // if the other boomerang is dead as well, destroy the spells instance
                                call tmps.destroy()
                            endif
                        endif
                    endif
                elseif tmps.boomerang==2 then // pretty much the same here
                    if UnitDamageTarget(tmps.caster, u, tmps.dam2, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
                        call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
                        call GroupAddUnit(tmps.damaged2, u)
                        static if DAMAGE_ABSORPTION_RELATIVE then
                            set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
                        else
                            set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
                        endif
                        if tmps.dam2<=DAMAGE_BOUNDARY then
                            call tmps.dum2.destroy()
                            set tmps.d2a=false
                            if not tmps.d1a then
                                call tmps.destroy()
                            endif
                        endif
                    endif
                endif
            endif
            set u=null
            return false
        endmethod
        
        private static method TreeFilterFunc takes nothing returns boolean
        local destructable d=GetFilterDestructable()
        local real x
        local real y
        local real bx
        local real by
            if (not IsDestructableDead(d)) and IsDestructableTree(d) then // filter out dead and non-tree destructables
                set OverTree=true
                static if not IGNORE_TREES then
                    set x=GetWidgetX(d)
                    set y=GetWidgetY(d)
                    if tmps.boomerang==1 and tmps.d1a then // same as above
                        set bx=tmps.dum1.x
                        set by=tmps.dum1.y
                        if (x-bx)*(x-bx)+(y-by)*(y-by)<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then // tree must be inside the collision radius
                            static if KILL_TREES then // if the boomerang is allowed to kill trees, do so
                                call KillDestructable(d)
                                // but adjust the damage as if a unit had been hit
                                static if DAMAGE_ABSORPTION_RELATIVE then
                                    set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
                                else
                                    set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
                                endif
                                if tmps.dam1<=DAMAGE_BOUNDARY then // same as above
                                    call tmps.dum1.destroy()
                                    set tmps.d1a=false
                                    if not tmps.d2a then
                                        call tmps.destroy()
                                    endif
                                endif
                            else // if Trees may not be destroyed, destroy the boomerang instantly
                                call tmps.dum1.destroy()
                                set tmps.d1a=false
                                if not tmps.d2a then // and end the spell instance if appropriate
                                    call tmps.destroy()
                                endif
                            endif
                        endif
                    elseif tmps.boomerang==2 and tmps.d2a then // next section is the same as above
                        set bx=tmps.dum2.x
                        set by=tmps.dum2.y
                        if (x-bx)*(x-bx)+(y-by)*(y-by)<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then
                             static if KILL_TREES then
                                call KillDestructable(d)
                                static if DAMAGE_ABSORPTION_RELATIVE then
                                    set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
                                else
                                    set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
                                endif
                                if tmps.dam2<=DAMAGE_BOUNDARY then
                                    call tmps.dum2.destroy()
                                    set tmps.d2a=false
                                    if not tmps.d1a then
                                        call tmps.destroy()
                                    endif
                                endif
                            else
                                call tmps.dum2.destroy()
                                set tmps.d2a=false
                                if not tmps.d1a then
                                    call tmps.destroy()
                                endif
                            endif
                        endif
                    endif
                endif
            endif
            set d=null
            return false
        endmethod
        
        private static method Callback takes nothing returns nothing
        local integer i=thistype.Count-1
        local thistype s
        local real r
        local real x
        local real y
            loop
                exitwhen i<0
                set s=thistype.Structs[i]
                //
                // make the boomerang home, even if the caster moves
                if s.target!=null then
                    set s.targetX=GetUnitX(s.target)
                    set s.targetY=GetUnitY(s.target)
                endif
                set s.launchX=GetUnitX(s.caster)
                set s.launchY=GetUnitY(s.caster)
                set s.distance=SquareRoot( (s.targetX-s.launchX)*(s.targetX-s.launchX)+(s.targetY-s.launchY)*(s.targetY-s.launchY) )
                set s.baseAngle=Atan2(s.targetY-s.launchY, s.targetX-s.launchX)-bj_PI/(BOOMERANG_FOCUS*2)
                // functions for moving the boomerang:
                // r(a)=distance*Sin(BOOMERANG_FOCUS*a) // a is the angle and goes from 90 to 0 // distance from center point
                // x(a)=Cos(a)*r(a) // x and y coordinates in relation to the location it was cast.
                // y(a)=Sin(a)*r(a) // note that i inlined some things to allow casting the boomerang in all directions from any point on the map
                set r=(s.distance*Sin(BOOMERANG_FOCUS*s.currentAngle))
                set tmps=s
                static if ALLOW_MULTIPLE_HITS and USE_RIGHT_BOOMERANG then
                    set s.boomerang=1
                    call ForGroup(s.damaged1, function Data.UnitDistCheck)
                endif
                static if ALLOW_MULTIPLE_HITS and USE_LEFT_BOOMERANG then
                    set s.boomerang=2
                    call ForGroup(s.damaged2, function Data.UnitDistCheck)
                endif
                if s.d1a then // is dummy 1 active
                    set s.boomerang=1 // indicate were working with dummy 1
                    set x=s.launchX+(Cos(s.currentAngle+s.baseAngle)*r)
                    set y=s.launchY+(Sin(s.currentAngle+s.baseAngle)*r)
                    
                    set s.dum1.x=x
                    set s.dum1.y=y
                    
                    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
                    static if not COLLIDE_WITH_GROUND and IGNORE_TREES then
                    else
                        set OverTree=false
                        call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
                        call EnumDestructablesInRect(R, Data.TreeFilter, null)
                        static if COLLIDE_WITH_GROUND then
                            if not OverTree and not IsTerrainWalkable(x,y) and not s.destroyed then // if boomerangs collide with unwalkable terrain and the terrain is unwalkable
                                call s.dum1.destroy() // destroy the dummy
                                set s.d1a=false
                                if not s.d2a then // if the other dummy is inactive
                                    call s.destroy() // destroy this spell instance
                                endif
                            endif
                        endif
                    endif
                endif
                if s.d2a then
                    set s.boomerang=2
                    set x=s.launchX+(Cos(bj_PI/BOOMERANG_FOCUS-s.currentAngle+s.baseAngle)*r)
                    set y=s.launchY+(Sin(bj_PI/BOOMERANG_FOCUS-s.currentAngle+s.baseAngle)*r)
                    
                    set s.dum2.x=x
                    set s.dum2.y=y
                    
                    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
                    static if not COLLIDE_WITH_GROUND and IGNORE_TREES then
                    else
                        set OverTree=false
                        call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
                        call EnumDestructablesInRect(R, Data.TreeFilter, null)
                        static if COLLIDE_WITH_GROUND then
                            if not OverTree and not IsTerrainWalkable(x,y) and not s.destroyed then
                                call s.dum2.destroy()
                                set s.d2a=false
                                if not s.d1a then
                                    call s.destroy()
                                endif
                            endif
                        endif
                    endif
                endif
                
                set s.currentAngle=s.currentAngle-(TICK*(bj_PI/(BOOMERANG_FOCUS*2))*(Boomerang_Speed(s.level)/s.distance))
                if s.currentAngle<=0 or IsUnitType(s.caster, UNIT_TYPE_DEAD)==true then
                    // stop this spell when caster is dead or the boomerang is at the casters position again.
                    call s.destroy()
                endif
                // do your things here, dont forget to call s.destroy() somewhen
                //
                set i=i-1
            endloop
        endmethod
        
        static method SpellCond takes nothing returns boolean
            return GetSpellAbilityId()==AID
        endmethod
        
        static method create takes nothing returns thistype
        local thistype s=thistype.allocate()
            set s.target=GetSpellTargetUnit()
            if s.target==null or s.target==GetTriggerUnit() then
                set s.target=null
                set s.targetX=GetSpellTargetX()
                set s.targetY=GetSpellTargetY()
            else
                set s.targetX=GetUnitX(s.target)
                set s.targetY=GetUnitY(s.target)
            endif
            set s.caster=GetTriggerUnit()
            set s.launchX=GetUnitX(s.caster)
            set s.launchY=GetUnitY(s.caster)
            set s.level=GetUnitAbilityLevel(s.caster, AID)
            set s.distance=SquareRoot( ((s.targetX-s.launchX)*(s.targetX-s.launchX))+((s.targetY-s.launchY)*(s.targetY-s.launchY)) )
            if s.distance==0.0 then
                set s.baseAngle=(GetUnitFacing(s.caster)*bj_DEGTORAD)
            else
                set s.baseAngle=Atan2(s.targetY-s.launchY, s.targetX-s.launchX)
            endif
            if s.distance<Min_Range(s.level) then // enforce the minimum distance
                set s.distance=Min_Range(s.level)
                set s.targetX=s.launchX+s.distance*Cos(s.baseAngle)
                set s.targetY=s.launchY+s.distance*Sin(s.baseAngle)
            endif
            set s.baseAngle=s.baseAngle-(bj_PI/(BOOMERANG_FOCUS*2))
            set s.currentAngle=(bj_PI/BOOMERANG_FOCUS)
            set s.d1a=false
            set s.d2a=false
            static if USE_RIGHT_BOOMERANG then
                set s.d1a=true
                set s.dam1=Damage(s.level)
                
                set s.dum1=xefx.create(s.launchX, s.launchY, 0)
                set s.dum1.fxpath=BOOMERANG_MODEL
                set s.dum1.scale=BOOMERANG_SIZE
                set s.dum1.z=BOOMERANG_HEIGHT
                
                set s.damaged1=NewGroup()
            endif
            static if USE_LEFT_BOOMERANG then
                set s.d2a=true
                set s.dam2=Damage(s.level)
                
                set s.dum2=xefx.create(s.launchX, s.launchY, 0)
                set s.dum2.fxpath=BOOMERANG_MODEL
                set s.dum2.scale=BOOMERANG_SIZE
                set s.dum2.z=BOOMERANG_HEIGHT
                
                set s.damaged2=NewGroup()
            endif
            
            // initialize the struct here
            set thistype.Structs[thistype.Count]=s
            set s.i=thistype.Count
            if thistype.Count==0 then
                call TimerStart(thistype.T, TICK, true, function thistype.Callback)
            endif
            set thistype.Count=thistype.Count+1
            
            if (not s.d1a) and (not s.d2a) then
                call s.destroy()
                return 0
            endif
            return s
        endmethod
        
        private static method onInit takes nothing returns nothing
        local trigger t=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function Data.SpellCond))
            call TriggerAddAction(t, function Data.create)
            
            set Data.DamageFilter=Condition(function Data.DamageFilterFunc)
            set Data.TreeFilter=Condition(function Data.TreeFilterFunc)
        endmethod
    endstruct

endlibrary
Rating - 4.33 (3 votes)
(Hover and click)
Moderator Comments
Highly Recommended
17:17, 4th Jun 2009
hvo-busterkomo:
The effect looks great, and the scripting is done well. My only complaint is (more of a personal preference) is that you use arrays for the range and damage. It seems easier to do it the standard way (return level * 0.25 + 1, etc..).

If you have any questions regarding this review, feel free to send a PM to hvo-busterkomo.

This spell is approved and works properly.


Download Boomerang_1.4.1.w3x
(59.96 KB, 1316 Downloads)

Old 06-04-2009, 12:02 PM   #2 (permalink)
Registered User Aspard
The Witcher
 
Aspard's Avatar
 
Join Date: Jul 2008
Posts: 874
Aspard is a jewel in the rough (217)Aspard is a jewel in the rough (217)Aspard is a jewel in the rough (217)
In-Game commands are shown too late, I've already wanted to leave the game, but decided to look for commands here, I typed some in and only after that the system shown me them ;(

Nicely done, performs well, has good settings. Can't anything to you, cause working just with GUI.

In case there are already many boomerangs, 4/5
__________________
Aspard is offline   Reply With Quote
Old 06-04-2009, 01:48 PM   #3 (permalink)
Registered User f0rsAk3n
~Self-proclaimed VIP
 
f0rsAk3n's Avatar
 
Join Date: Nov 2008
Posts: 649
f0rsAk3n has a spectacular aura about (144)f0rsAk3n has a spectacular aura about (144)f0rsAk3n has a spectacular aura about (144)f0rsAk3n has a spectacular aura about (144)
I believe Hanky also made a Boomerang, though not exactly like this.

Good work
__________________
f0rsAk3n is offline   Reply With Quote
Old 06-04-2009, 04:40 PM   #4 (permalink)
Registered User xD.Schurke
10100111001
 
xD.Schurke's Avatar
 
Join Date: Feb 2006
Posts: 976
xD.Schurke is just really nice (352)xD.Schurke is just really nice (352)xD.Schurke is just really nice (352)
Hero Contest #2 - Winner: Archmaiden 
code looks good, only thing I would change is the TriggerRegisterAnyUnitEventBJ, because it leaks 14 boolexpr
hehe what I see in your create method is that you use thistype for the local var but not for the returned value ;) (but realy, who cares)

I give you 4/5
__________________
My Spells & Systems


xD.Schurke is offline   Reply With Quote
Old 06-04-2009, 10:13 PM   #5 (permalink)
Registered User -JonNny
Great Project comin soon
 
Join Date: Jan 2007
Posts: 614
-JonNny is just really nice (342)-JonNny is just really nice (342)-JonNny is just really nice (342)
I love the movement of the boomerang , awesome <3
May add some units to bash in the testmap

-But if its casted into trees or any other position near there it seems to malfunction due the boomerang flies into another dirrection with another range (may it ha sth to do with the min. castrange)

-If casted on the caster directly (e.g. hero icon) it buggs (missile does not move / is not destroyed )

When trying to save the testmap Jasshelpes displays an error which makes it not possible for me to test any changes (i guess its in XE, dunno - never used the dummy system)
error

Line 504 , Unexpected : "ARGBrecolor"
implement optional ARGBrecolor


As mention by hvo-busterkomo i would also prefer calculations instead of that array for the levels

Else, great work ! [4,5/5]
-JonNny is offline   Reply With Quote
Old 06-04-2009, 11:19 PM   #6 (permalink)
Registered User Deaod
GUI sucks. Don't use it.
 
Join Date: Nov 2007
Posts: 854
Deaod has disabled reputation
Thanks, -JonNny. I fixed both bugs.

as for the problem when saving: Im using JH 0.9.G.3, try updating JNGP (v5c) and JH (0.9.G.3).

If you prefer calculations, change the PROXY functions to your formula. You can then delete the arrays and the SetUp functions (dont forget to delete the calls to the SetUp function in the onInit method).

I also made the command list show directly after starting the map.

I dont really care about those 16 leaked null boolexprs. You should use an event stack for your map anyway.


Version 1.2.0
Deaod is offline   Reply With Quote
Old 06-06-2009, 09:54 PM   #7 (permalink)
Registered User creativeRPG
User
 
Join Date: May 2009
Posts: 30
creativeRPG is an unknown quantity at this point (0)
Hi and thx for programming such a nice spell,
but i am too stupid to import it into my map - and i want it, i need it *g* it fits perfectly into it.

i have importet the DUMMY, importet the ability boomerang! and importet all three trigger-trees.
then i added bommerang! to my hero,

but he only throws a piece of air... :(

thx
creativeRPG is offline   Reply With Quote
Old 06-07-2009, 05:49 AM   #8 (permalink)
Registered User Deaod
GUI sucks. Don't use it.
 
Join Date: Nov 2007
Posts: 854
Deaod has disabled reputation
okay, let me see:
  1. Copy over the Boomerang trigger, and all Libraries under External Libraries if you dont already have those in your map
  2. Either copy the Boomerang! ability over or recreate it yourself
  3. add the boomerang ability to your hero
  4. adjust the AID constant in the Boomerang trigger to the rawcode of your Boomerang! ability (Press CTRL+D in the Object Editor to display raw values; The Boomerang! ability should look like this: "A000:ANcl (Boomerang!)". The four characters before the double point (here: "A000") are the rawcode of the ability).
  5. Additionally make sure you adjusted the rawcodes in the xebasic library, expecially the one for the dummy unit.
Deaod is offline   Reply With Quote
Old 06-07-2009, 09:25 AM   #9 (permalink)
Registered User creativeRPG
User
 
Join Date: May 2009
Posts: 30
creativeRPG is an unknown quantity at this point (0)
Really thx,
this was short and very understoodable
hope i didn't put you out

best regards
(hope you will enjoy my map )
creativeRPG is offline   Reply With Quote
Old 07-01-2009, 04:15 PM   #10 (permalink)
Registered User Deuterium
-
 
Deuterium's Avatar
 
Join Date: Mar 2009
Posts: 1,981
Deuterium is a name known to all (669)Deuterium is a name known to all (669)Deuterium is a name known to all (669)
I'd be testing later :)
__________________
Deuterium is offline   Reply With Quote
Old 07-02-2009, 12:04 AM   #11 (permalink)
Registered User gunggang777
Learn vJass little to +er
 
gunggang777's Avatar
 
Join Date: Jul 2008
Posts: 202
gunggang777 is on a distinguished road (82)gunggang777 is on a distinguished road (82)
yeah good one 5/5
__________________
MasterHaosis In memoriam......
I very" inactive.... only browsing the site and not posting. so if i have a time i come again now i'am very busy
gunggang777 is offline   Reply With Quote
Old 07-03-2009, 03:51 PM   #12 (permalink)
Registered User Beast(TrOg)
Very Very Beastly
 
Beast(TrOg)'s Avatar
 
Join Date: Nov 2006
Posts: 156
Beast(TrOg) has little to show at this moment (36)Beast(TrOg) has little to show at this moment (36)Beast(TrOg) has little to show at this moment (36)Beast(TrOg) has little to show at this moment (36)
It's good but when you use boomerang and then blink somewhere, the boomerang distorts a bit.
__________________
Maps: BeasTD (Tower Defense), Hold the Line 2 (Team Hero Defense), Bombtastic (Minigame), AoES (AoS)
Other: FuryDarts (Spell)
Currently doing: AoES and Bombtastic final versions coming
Beast(TrOg) is offline   Reply With Quote
Old 07-05-2009, 06:54 AM   #13 (permalink)
Registered User Snowman
User
 
Join Date: Jul 2009
Posts: 3
Snowman is an unknown quantity at this point (0)
how do i import spells to my own map? the easy way plz
Snowman is offline   Reply With Quote
Old 07-12-2009, 06:04 AM   #14 (permalink)
Registered User Deaod
GUI sucks. Don't use it.
 
Join Date: Nov 2007
Posts: 854
Deaod has disabled reputation
Quote:
  1. Copy over the Boomerang trigger, and all Libraries under External Libraries if you dont already have those in your map
  2. Either copy the Boomerang! ability over or recreate it yourself
  3. add the boomerang ability to your hero
  4. adjust the AID constant in the Boomerang trigger to the rawcode of your Boomerang! ability (Press CTRL+D in the Object Editor to display raw values; The Boomerang! ability should look like this: "A000:ANcl (Boomerang!)". The four characters before the double point (here: "A000") are the rawcode of the ability).
  5. Additionally make sure you adjusted the rawcodes in the xebasic library, expecially the one for the dummy unit.
Version 1.3.1

Comiled with JNGP v5d (that means you need to update).
Deaod is offline   Reply With Quote
Old 08-05-2009, 11:16 AM   #15 (permalink)
Registered User DoOs_101
Original and Proud
 
DoOs_101's Avatar
 
Join Date: Aug 2004
Posts: 837
DoOs_101 has little to show at this moment (31)DoOs_101 has little to show at this moment (31)DoOs_101 has little to show at this moment (31)DoOs_101 has little to show at this moment (31)
TRhis doesn't work anymore in 1.24
__________________

My Website:

DoOs_101 is offline   Reply With Quote
Reply

Bookmarks

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[JASS] formula for a boomerang Ciebron Triggers & Scripts 15 06-05-2009 05:20 AM

All times are GMT. The time now is 08:31 AM.




Powered by vBulletin
Copyright 2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.5.1 PL2
Copyright © Ralle