1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. We have recently started the 16th edition of the Mini Mapping Contest. The theme is mini RPG. Do check it out and have fun.
    Dismiss Notice
  4. Choose your ride to damnation in the 5th Special Effect Contest Poll.
    Dismiss Notice
  5. The winners of the 13th Techtree Contest have been announced!
    Dismiss Notice
  6. The 13th Music Contest Poll is up! Vote for the best tracks in this symphony of frost and flame.
    Dismiss Notice
  7. Race against the odds and Reforge, Don't Refund. The 14th Techtree Contest has begun!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Unit Pathing Detection

Discussion in 'Triggers & Scripts' started by Kam, Sep 11, 2011.

  1. Kam

    Kam

    Joined:
    Aug 3, 2004
    Messages:
    2,633
    Resources:
    23
    Models:
    8
    Icons:
    2
    Maps:
    13
    Resources:
    23
    Currently this spell does not detect obstacles or changes in cliff height so ground units can end up in the middle of trees or in deep water.

    Code (vJASS):

    //*****************************************************************
    //*  REPELLING FIELD
    //*  Created by Archmage Owenalacaster
    //*
    //*  Allows units with the specified buff to knockback enemies.
    //*
    //*  requires: -SmoothMovSys
    //*            -ProjSys
    //*            -BoolexprUtils
    //*            -GroupUtils
    //*            -TimerUtils
    //*****************************************************************
    scope RepellingField initializer Init
       
        globals
            private constant integer BUFF_ID     = 'BM07'
            private constant real MANA_PER_KNOCK =    1.0
            private constant real TIMER_INTERVAL =    0.2
            private constant real KNOCK_RANGE    =  700.0
            private constant real KNOCK_MAX_DIST =  800.0
            private constant real KNOCK_SPEED    =  700.0
            private constant real KNOCK_HEIGHT   =    0.0
            private constant string KNOCK_FX     = "Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdl"
            private constant string KNOCK_ATTACH = "origin"
        endglobals
       
    //===========================================================================
    //                       END OF CALIBRATION SECTION
    //===========================================================================
        globals
            private constant string STOP = "stop"
            private group    RepelGroup  = CreateGroup()
            private boolexpr RepelFilter = null
            private boolexpr RepelEffect = null
            private unit     RepelUnit   = null
        endglobals
       
        private function RepelFilterCond takes nothing returns boolean
            return GetUnitAbilityLevel(GetFilterUnit(),BUFF_ID) >= 1
        endfunction
       
        private function RepelEffectCond takes nothing returns boolean
            local unit u = GetFilterUnit()
            if IsUnitEnemy(u,GetOwningPlayer(RepelUnit)) and GetWidgetLife(u) > 0.405 and GetUnitState(RepelUnit,UNIT_STATE_MANA) >= MANA_PER_KNOCK and GetUnitMoveSpeed(u) > 0 and not(IsUnitType(u,UNIT_TYPE_STRUCTURE) or IsUnitType(u,UNIT_TYPE_SNARED) or IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or IsUnitProjectile(u)) then
                call SetUnitState(RepelUnit,UNIT_STATE_MANA,GetUnitState(RepelUnit,UNIT_STATE_MANA) - MANA_PER_KNOCK)
                call DestroyEffect(AddSpecialEffectTarget(KNOCK_FX,u,KNOCK_ATTACH))
                call KnockBack(u,KNOCK_MAX_DIST-GetUnitsDist(RepelUnit,u),GetUnitsAngle(RepelUnit,u),KNOCK_HEIGHT,KNOCK_SPEED,function Null,function Null,false)
                call IssueImmediateOrder(u,STOP)
            endif
            set u = null
            return false
        endfunction
       
        private function RepelEnemies takes nothing returns nothing
            set RepelUnit = GetEnumUnit()
            call GroupEnumUnitsInRange(ENUM_GROUP,GetUnitX(RepelUnit),GetUnitY(RepelUnit),KNOCK_RANGE,RepelEffect)
            set RepelUnit = null
        endfunction
       
        private function TimerAction takes nothing returns nothing
            local integer index = 0
            loop
                call GroupEnumUnitsOfPlayer(RepelGroup,Player(index),RepelFilter)
                call ForGroup(RepelGroup,function RepelEnemies)
                call GroupClear(RepelGroup)
                set index = index + 1
                exitwhen index == bj_MAX_PLAYER_SLOTS
            endloop
        endfunction
       
        private function Init takes nothing returns nothing
            call TimerStart(NewTimer(),TIMER_INTERVAL,true,function TimerAction)
            set RepelFilter = Condition(function RepelFilterCond)
            set RepelEffect = Condition(function RepelEffectCond)
        endfunction
    endscope
     


    TimerUtils
    Code (vJASS):

    library TimerUtils initializer init
    //*********************************************************************
    //* TimerUtils (red+blue+orange flavors for 1.24b+)
    //* ----------
    //*
    //*  To implement it , create a custom text trigger called TimerUtils
    //* and paste the contents of this script there.
    //*
    //*  To copy from a map to another, copy the trigger holding this
    //* library to your map.
    //*
    //* (requires vJass)   More scripts: htt://www.wc3c.net
    //*
    //* For your timer needs:
    //*  * Attaching
    //*  * Recycling (with double-free protection)
    //*
    //* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
    //* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
    //* SetTimerData(t,2)     : Attach value 2 to timer
    //* GetTimerData(t)       : Get the timer's value.
    //*                         You can assume a timer's value is 0
    //*                         after NewTimer.
    //*
    //* Multi-flavor:
    //*    Set USE_HASH_TABLE to true if you don't want to complicate your life.
    //*
    //* If you like speed and giberish try learning about the other flavors.
    //*
    //********************************************************************

    //================================================================
        globals
            //How to tweak timer utils:
            // USE_HASH_TABLE = true  (new blue)
            //  * SAFEST
            //  * SLOWEST (though hash tables are kind of fast)
            //
            // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true  (orange)
            //  * kinda safe (except there is a limit in the number of timers)
            //  * ALMOST FAST
            //
            // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
            //  * THE FASTEST (though is only  faster than the previous method
            //                  after using the optimizer on the map)
            //  * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
            //                     work)
            //
            private constant boolean USE_HASH_TABLE      = true
            private constant boolean USE_FLEXIBLE_OFFSET = false

            private constant integer OFFSET     = 0x100000
            private          integer VOFFSET    = OFFSET
                 
            //Timers to preload at map init:
            private constant integer QUANTITY   = 256
           
            //Changing this  to something big will allow you to keep recycling
            // timers even when there are already AN INCREDIBLE AMOUNT of timers in
            // the stack. But it will make things far slower so that's probably a bad idea...
            private constant integer ARRAY_SIZE = 8190

        endglobals

        //==================================================================================================
        globals
            private integer array data[ARRAY_SIZE]
            private hashtable     ht
        endglobals

        //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
        function SetTimerData takes timer t, integer value returns nothing
            static if(USE_HASH_TABLE) then
                // new blue
                call SaveInteger(ht,0,GetHandleId(t), value)
               
            elseif (USE_FLEXIBLE_OFFSET) then
                // orange
                static if (DEBUG_MODE) then
                    if(GetHandleId(t)-VOFFSET<0) then
                        call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                    endif
                endif
                set data[GetHandleId(t)-VOFFSET]=value
            else
                // new red
                static if (DEBUG_MODE) then
                    if(GetHandleId(t)-OFFSET<0) then
                        call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                    endif
                endif
                set data[GetHandleId(t)-OFFSET]=value
            endif        
        endfunction

        function GetTimerData takes timer t returns integer
            static if(USE_HASH_TABLE) then
                // new blue
                return LoadInteger(ht,0,GetHandleId(t) )
               
            elseif (USE_FLEXIBLE_OFFSET) then
                // orange
                static if (DEBUG_MODE) then
                    if(GetHandleId(t)-VOFFSET<0) then
                        call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                    endif
                endif
                return data[GetHandleId(t)-VOFFSET]
            else
                // new red
                static if (DEBUG_MODE) then
                    if(GetHandleId(t)-OFFSET<0) then
                        call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                    endif
                endif
                return data[GetHandleId(t)-OFFSET]
            endif        
        endfunction

        //==========================================================================================
        globals
            private timer array tT[ARRAY_SIZE]
            private integer tN = 0
            private constant integer HELD=0x28829022
            //use a totally random number here, the more improbable someone uses it, the better.
        endglobals

        //==========================================================================================
        function NewTimer takes nothing returns timer
            if (tN==0) then
                //If this happens then the QUANTITY rule has already been broken, try to fix the
                // issue, else fail.
                debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
                static if( not USE_HASH_TABLE) then
                    debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
                    set tT[0]=CreateTimer()
                    static if( USE_FLEXIBLE_OFFSET) then
                        if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    else
                        if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    endif
                endif
            else
                set tN=tN-1
            endif
            call SetTimerData(tT[tN],0)
         return tT[tN]
        endfunction

        //==========================================================================================
        function ReleaseTimer takes timer t returns nothing
            if(t==null) then
                debug call BJDebugMsg("Warning: attempt to release a null timer")
                return
            endif
            if (tN==ARRAY_SIZE) then
                debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

                //stack is full, the map already has much more troubles than the chance of bug
                call DestroyTimer(t)
            else
                call PauseTimer(t)
                if(GetTimerData(t)==HELD) then
                    debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                    return
                endif
                call SetTimerData(t,HELD)
                set tT[tN]=t
                set tN=tN+1
            endif    
        endfunction

        private function init takes nothing returns nothing
         local integer i=0
         local integer o=-1
         local boolean oops = false
         
            static if( USE_HASH_TABLE ) then
                set ht = InitHashtable()
                loop
                    exitwhen(i==QUANTITY)
                    set tT[i]=CreateTimer()
                    call SetTimerData(tT[i], HELD)
                    set i=i+1
                endloop
                set tN = QUANTITY
            else
                loop
                    set i=0
                    loop
                        exitwhen (i==QUANTITY)
                        set tT[i] = CreateTimer()
                        if(i==0) then
                            set VOFFSET = GetHandleId(tT[i])
                            static if(USE_FLEXIBLE_OFFSET) then
                                set o=VOFFSET
                            else
                                set o=OFFSET
                            endif
                        endif
                        if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
                            exitwhen true
                        endif
                        if (GetHandleId(tT[i])-o>=0)  then
                            set i=i+1
                        endif
                    endloop
                    set tN = i
                    exitwhen(tN == QUANTITY)
                    set oops = true
                    exitwhen not USE_FLEXIBLE_OFFSET
                    debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")              
                endloop
               
                if(oops) then
                    static if ( USE_FLEXIBLE_OFFSET) then
                        debug call BJDebugMsg("The problem has been fixed.")
                        //If this message doesn't appear then there is so much
                        //handle id fragmentation that it was impossible to preload
                        //so many timers and the thread crashed! Therefore this
                        //debug message is useful.
                    elseif(DEBUG_MODE) then
                        call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
                        call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
                        call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
                        call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
                    endif
                endif
            endif

        endfunction

    endlibrary
     


    smoothMovSys
    Code (vJASS):

    //******************************************************************************************
    //*
    //* Smooth movement script 2.0 By Moyack. 2009.
    //* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //*
    //* If you use this script, please be nice and give me credits.
    //*
    //* This library has been designed to simulate movement defined by kinematic parameters.
    //*
    //* You only need to define the Projectile unit, the position and velocity vectors,
    //* and the time the Projectile will fly. The rest is managed, controlled and destroyed
    //* automatically by the library.
    //*
    //* Additionally, you can define custom "events" when the Projectile starts, moves and ends,
    //* very convenient to set and control the Projectile behaviour.
    //*
    //* How to use:
    //* ¯¯¯¯¯¯¯¯¯¯¯
    //* This library has two data types, the vector and the Projectile structs.
    //*
    //*  Vector Struct: This struct allows you to group data in the way (X, Y, Z). The vector
    //*  type allows the following methods:
    //*
    //*   - create( real x, real y, real z) returns vector
    //*   - setdata(real x, real y, real z) returns nothing
    //*   - destroy() returns nothing
    //*
    //*  The first method allocates a new vector variable and sets at the same time data on it.
    //*  The second method allows you to change the data of the vector. The third method destroys
    //*  the vector. Example:
    //*
    //*  function VecStuff takes nothing returns nothing
    //*      local vector v = vector.create(0., 10., 20.) // creates a vector [0, 10, 20]
    //*      call v.setdata(15., 20., -10.) // changes the values in the vector v to [15, 20, -10]
    //*      call v.destroy() // destroys the vector, freeing the memory used.
    //*  endfunction
    //*
    //*  Projectile Struct: This one is the one in charge to do the nice things. This structs
    //*  allows the following methods:
    //*
    //* Aknowledges:
    //* ¯¯¯¯¯¯¯¯¯¯¯¯
    //*  - Mad[Lion] because he proposed the problem that inspire me to make this library.
    //*  - Pipedream because he had the same apporach, but opposite :) and for the idea of
    //*    partial factoring.
    //*  - Vexorian, PitzerMike and all the people involved in the development of vJASS.
    //*    without this tool, this idea would be still just that: an idea.
    //*
    //******************************************************************************************

    library SmoothMovSys
    //***************************************************************************************************************
    //* Configuration globals
    globals
        private constant real dt = 0.022 // sets the timer value
    endglobals
    //***************************************************************************************************************
    //* End configuration globals
    globals
        private unit       LastProj    = null // Stores temporally the last Projectile created / destroyed
        private vector     StartPos           // Stores the start position of the Projectile.
        private vector     StartVel           // Stores the start velocity of the Projectile.
        private vector     LastPos            // Stores the last position of the Projectile.
        private vector     LastVel            // Stores the last velocity of the Projectile.
        private Projectile MovingProj         // Stores the evaluating Projectile. Only for use when
    endglobals                                // it's called on the CodeMove function

    struct vector // struct used to manage vectors LOL!!!
        real x = 0.
        real y = 0.
        real z = 0.
       
        static method create takes real x, real y, real z returns vector
            local vector v = vector.allocate()
            set v.x = x
            set v.y = y
            set v.z = z
            return v
        endmethod
       
        method setdata takes real x, real y, real z returns nothing
            set .x = x
            set .y = y
            set .z = z
        endmethod
    endstruct

    private function GetA takes real x0, real x1, real v0, real v1, real tf returns real
        return (tf * (v1-v0) - 2 * (x1 - x0 - v0*tf)) / (tf*tf*tf)
    endfunction

    private function GetB takes real x0, real x1, real v0, real v1, real tf returns real
        return (3 * (x1 - x0 - v0*tf) - tf * (v1 - v0)) / (tf*tf)
    endfunction

    private function GetX takes real t, real A, real B, real C, real D returns real
        return D + t*(C + t*(B + t*A)) // thanks Pipedream for the idea :)
    endfunction

    private function GetV takes real t, real A, real B, real C returns real
        return (3*A*t + 2*B)*t + C // thanks Pipedream for the idea :)
    endfunction

    struct Projectile
        private static region reg = null
        private static trigger TRG = CreateTrigger()
        static group ProjGroup = CreateGroup()
        private static integer counter = 0
        private static Projectile array Projectiles
        private integer i
        unit p       // Projectile unit
        vector pi    // initial and final position coordinates
        vector pf
        vector vi    // initial and final velocity coordinates
        vector vf
        real tf = 0. // time that the Projectile will take to reach the final point
        vector vp    // variable position and velocity vars
        vector vv
        real t = 0.
        vector A     // Constant Values used for calculation stuff
        vector B
        // Miscelaneous data
        boolean DV // this flag indicates if the Projectile is destroyed with the input vectors
        boolean pU // this flag indicates if the unit used as projectile will be paused or not  
        trigger CodeMove //Stores the function name that will be evaluated every time any Projectile is moving
        trigger CodeEnd  //Stores the function name used when the Projectile dies.
       
        method SetData takes unit p, vector pi, vector pf, vector vi, vector vf, real t, boolean DV, boolean pU returns nothing
            local real Ax = GetA(pi.x, pf.x, vi.x, vf.x, t)
            local real Ay = GetA(pi.y, pf.y, vi.y, vf.y, t)
            local real Az = GetA(pi.z, pf.z, vi.z, vf.z, t)
            local real Bx = GetB(pi.x, pf.x, vi.x, vf.x, t)
            local real By = GetB(pi.y, pf.y, vi.y, vf.y, t)
            local real Bz = GetB(pi.z, pf.z, vi.z, vf.z, t)
           
            set .p = p
            call UnitAddAbility(p, 'Amrf')
            call UnitRemoveAbility(p, 'Amrf')
            call GroupAddUnit(Projectile.ProjGroup, p)
            if pU then
                call PauseUnit(p, true)
            endif
            set .pi = pi
            set .pf = pf
            set .vi = vi
            set .vf = vf
            set .A = vector.create(Ax, Ay, Az)
            set .B = vector.create(Bx, By, Bz)
            set .vp = vector.create(0., 0., 0.)
            set .vv = vector.create(0., 0., 0.)
            if t <= 0. then
                set .tf = dt
            else
                set .tf = t
            endif
            set .DV = DV
            set .pU = pU
        endmethod
       
        private method update takes nothing returns nothing
            local real x
            local real y
            local real z
            if .CodeMove != null then
                set MovingProj = this
                call TriggerExecute(.CodeMove)
            endif
            set .t = .t + dt
            set x = GetX(.t, .A.x, .B.x, .vi.x, .pi.x)
            set y = GetX(.t, .A.y, .B.y, .vi.y, .pi.y)
            set z = GetX(.t, .A.z, .B.z, .vi.z, .pi.z)
            call .vp.setdata(x, y, z)
            set x = GetV(.t, .A.x, .B.x, .vi.x)
            set y = GetV(.t, .A.y, .B.y, .vi.y)
            set z = GetV(.t, .A.z, .B.z, .vi.z)
            call .vv.setdata(x, y, z)
        endmethod
       
        method SetStartCode takes code start returns nothing
            if start != null then
                call TriggerClearActions(Projectile.TRG)
                set LastProj = .p
                call StartPos.setdata(.pi.x, .pi.y, .pi.z)
                call StartVel.setdata(.vi.x, .vi.y, .vi.z)
                call LastPos.setdata(.pf.x, .pf.y, .pf.z)
                call LastVel.setdata(.vf.x, .vf.y, .vf.z)
                call TriggerAddAction(Projectile.TRG, start)
                call TriggerExecute(Projectile.TRG)
            endif
        endmethod
       
        method SetMoveCode takes code move returns nothing
            if .CodeMove == null then
                set .CodeMove = CreateTrigger()
            else
                call TriggerClearActions(.CodeMove)
            endif
            call TriggerAddAction(.CodeMove, move)
        endmethod
       
        method SetEndCode takes code end returns nothing
            if .CodeEnd == null then
                set .CodeEnd = CreateTrigger()
            else
                call TriggerClearActions(.CodeEnd)
            endif
            call TriggerAddAction(.CodeEnd, end)
        endmethod
       
        method GetVelocity takes nothing returns vector
            local vector v = vector.create(0,0,0)
            local real x = GetV(.t, .A.x, .B.x, .vi.x)
            local real y = GetV(.t, .A.y, .B.y, .vi.y)
            local real z = GetV(.t, .A.z, .B.z, .vi.z)
            call v.setdata(x, y, z)
            return v
        endmethod
       
        method GetPosition takes nothing returns vector
            return vector.create(GetUnitX(.p), GetUnitY(.p), GetUnitFlyHeight(.p))
        endmethod
       
        private method onDestroy takes nothing returns nothing
            call GroupRemoveUnit(Projectile.ProjGroup, .p)
            if .pU then
                call PauseUnit(.p, false)
            endif
            set LastProj = .p
            set .p = null
            call StartPos.setdata(.pi.x, .pi.y, .pi.z)
            call StartVel.setdata(.vi.x, .vi.y, .vi.z)
            call LastPos.setdata(.pf.x, .pf.y, .pf.z)
            call LastVel.setdata(.vf.x, .vf.y, .vf.z)
            if .DV then
                call .pi.destroy()
                call .pf.destroy()
                call .vi.destroy()
                call .vf.destroy()
            endif
            call .A.destroy()
            call .B.destroy()
            call .vp.destroy()
            call .vv.destroy()
            if .CodeEnd != null then
                call TriggerExecute(.CodeEnd)
            endif
            set Projectile.counter = Projectile.counter - 1
            set Projectile.Projectiles[Projectile.counter].i = .i
            set Projectile.Projectiles[.i] = Projectile.Projectiles[Projectile.counter]
        endmethod    

        private static method Loop takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i >= Projectile.counter
                call Projectile.Projectiles[i].update()
                if Projectile.Projectiles[i].t > Projectile.Projectiles[i].tf or not IsPointInRegion(Projectile.reg, Projectile.Projectiles[i].vp.x, Projectile.Projectiles[i].vp.y) then
                    // destroys the Projectile data if it finished its time or if it's going to leave the playable map area
                    call Projectile.Projectiles[i].destroy()
                else // updates the location and angle facing of the unit
                    call SetUnitX(Projectile.Projectiles[i].p, Projectile.Projectiles[i].vp.x)
                    call SetUnitY(Projectile.Projectiles[i].p, Projectile.Projectiles[i].vp.y)
                    call SetUnitFlyHeight(Projectile.Projectiles[i].p, Projectile.Projectiles[i].vp.z, 0.)
                endif
                set i = i + 1
            endloop
        endmethod
       
        static method create takes unit p, vector pi, vector pf, vector vi, vector vf, real t, boolean DestroyVectors, boolean pauseUnit returns Projectile
            local Projectile pr = Projectile.allocate()
            call pr.SetData(p, pi, pf, vi, vf, t, DestroyVectors, pauseUnit)
            set pr.i = Projectile.counter
            set Projectile.Projectiles[Projectile.counter] = integer(pr)
            set Projectile.counter = Projectile.counter + 1
            return pr
        endmethod
       
        static method createSpherical takes unit p, vector pi, vector pf, vector vsi, vector vsf, real t, boolean DestroyVectors, boolean pauseUnit returns Projectile
        // this method allows you to define the speed vectors in spherical coordinates [r, t, f]
        // where r is the vector magnitude
        //       t is the angle in XY plane
        //       f is the angle in the plane defined by the vector R and the Z axis
        // ALL the angles are in RADIANS
        // when the Projectile is created, all the entered vectors will be changed to cartesian system
            call vsi.setdata(vsi.x * Cos(vsi.z) * Cos(vsi.y), vsi.x * Cos(vsi.z) * Sin(vsi.y), vsi.x * Sin(vsi.z))
            call vsf.setdata(vsf.x * Cos(vsf.z) * Cos(vsf.y), vsf.x * Cos(vsf.z) * Sin(vsf.y), vsf.x * Sin(vsf.z))
            return Projectile.create(p, pi, pf, vsi, vsf, t, DestroyVectors, pauseUnit)
        endmethod
       
        private static method onInit takes nothing returns nothing
            set Projectile.reg = CreateRegion()
            call RegionAddRect(Projectile.reg, bj_mapInitialPlayableArea)
            set StartPos = vector.create(0,0,0)
            set StartVel = vector.create(0,0,0)
            set LastPos = vector.create(0,0,0)
            set LastVel = vector.create(0,0,0)
            call TimerStart(CreateTimer(), dt, true, function Projectile.Loop)
        endmethod
    endstruct

    // functions used to get the the unit Projectile and the vectors in custom triggers
    // those functions are to be used when the unit starts and stops being a Projectile

    function GetUnitProjectile takes nothing returns unit
        return LastProj
    endfunction

    function GetInitPos takes nothing returns vector
        return vector.create(StartPos.x, StartPos.y, StartPos.z)
    endfunction

    function GetInitVel takes nothing returns vector
        return vector.create(StartVel.x, StartVel.y, StartVel.z)
    endfunction

    function GetLastPos takes nothing returns vector
        return vector.create(LastPos.x, LastPos.y, LastPos.z)
    endfunction

    function GetLastVel takes nothing returns vector
        return vector.create(LastVel.x, LastVel.y, LastVel.z)
    endfunction

    function GetMovingProjectile takes nothing returns Projectile
        // only to use when you need to get the projectile data in a CodeMove function
        return MovingProj
    endfunction

    function IsUnitProjectile takes unit u returns boolean
        return IsUnitInGroup(u, Projectile.ProjGroup)
    endfunction

    endlibrary
     

    ProjSys
    Code (vJASS):

    library ProjSys initializer InitMovSys requires GroupUtils, SmoothMovSys
    //***************************************************************************************************************
    //* Configuration globals
    globals
        private constant string MovDustFX   = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
        private constant string MovSplashFX = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
    endglobals
    //***************************************************************************************************************
    //* End configuration globals
       
    globals
        private constant rect TreeReg = Rect(0,0,1,1)
    endglobals

    private function KillTrees takes nothing returns nothing
        local destructable d = GetEnumDestructable()
        call KillDestructable(d)
        set d = null
    endfunction

    private function RemoveTreesInRange takes real x, real y, real r returns nothing
        call SetRect(TreeReg, x-r, y-r, x+r, y+r)
        call EnumDestructablesInRect(TreeReg, BOOLEXPR_TRUE, function KillTrees)
    endfunction

    private function MoveKnockBackBasic takes nothing returns nothing
        local Projectile P = GetMovingProjectile()
        local real x = GetUnitX(P.p)
        local real y = GetUnitY(P.p)
        if GetUnitFlyHeight(P.p) <= 0. then
            if not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
                call DestroyEffect(AddSpecialEffect(MovDustFX, x, y))
            elseif not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) then
                call DestroyEffect(AddSpecialEffect(MovSplashFX, x, y))
            endif
            if IsUnitType(P.p, UNIT_TYPE_GROUND) == true and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
                call SetUnitPosition(P.p, x, y)
                call P.destroy()
            endif
        endif
    endfunction

    private function MoveKnockBack takes nothing returns nothing
        local Projectile P = GetMovingProjectile()
        call MoveKnockBackBasic()
        call RemoveTreesInRange(GetUnitX(P.p), GetUnitY(P.p), 100.)
    endfunction

    private function MoveMissile takes nothing returns nothing
        local Projectile p = GetMovingProjectile()
        call SetUnitFacing(p.p, Atan2(p.vv.y, p.vv.x) * bj_RADTODEG)
    endfunction

    private function InitMovSys takes nothing returns nothing
        call Preload(MovDustFX)
        call Preload(MovSplashFX)
    endfunction

    private function ParabolicA takes real h, real d returns real
        // returns the coeficient A in the parabolic function
        // h, d, and x are in the same units of distance in WC3, which is more comfortable to use :)
        if d > 0. then
            return -4 * h / (d * d)
        else
            return 0.
        endif
    endfunction

    private function ParabolicB takes real h, real d returns real
        // returns the coeficient B in the parabolic function
        // h, d, and x are in the same units of distance in WC3, which is more comfortable to use :)
        // NOTE: THIS VALUE IS THE TAN OF THE PROJECTILE ANGLE
        if d > 0. then
            return 4 * h / d
        else
            return 0.
        endif
    endfunction

    //***********************************************************************************************
    //* public usage functions
    //***********************************************************************************************
    function Atan3 takes real x1, real y1, real x2, real y2 returns real
        local real a = Atan2(y2 - y1, x2 - x1)
        if a >= 0. then
            return a
        else
            return 2 * bj_PI + a
        endif
    endfunction

    function Dist takes real x1, real y1, real x2, real y2 returns real
        local real dx = x2 - x1
        local real dy = y2 - y1
        return SquareRoot( dx * dx + dy * dy )
    endfunction

    function GetParabolicAngle takes real h, real d returns real
        return Atan(ParabolicB(h, d))
    endfunction

    function GetUnitsAngle takes unit a, unit b returns real
        return Atan3(GetUnitX(a), GetUnitY(a), GetUnitX(b), GetUnitY(b))
    endfunction

    function GetUnitsDist takes unit a, unit b returns real
        return Dist(GetUnitX(a), GetUnitY(a), GetUnitX(b), GetUnitY(b))
    endfunction

    function GetUnitProjPoint takes unit u, real angle, real dist returns location
        local real x = GetUnitX(u) + dist * Cos(angle)
        local real y = GetUnitY(u) + dist * Sin(angle)
        return Location(x, y)
    endfunction

    //******************************************************************************
    //* KnockBack Function:
    //*   - unit t:      the unit that will become into a projectile
    //*   - real dist:   the distance that the projectile will move
    //*   - real dir:    the angle in the plane XY which indicates the movemente direction
    //*   - real height: the max height that the projectile will fly
    //*   - real speed:  the initial projectile speed. If height = 0 then the projectile will change the speed until reach 0
    //*   - Code InitCode: the name of the function that will be evaluated when the projectile starts moving
    //*   - Code EndCode: the name of the function that will be evaluated when the projectile stops.
    //*   - boolean DestroyDestructables: Indicates if the knockbacked units will destroy nearby trees while they move

    function KnockBack takes unit t, real dist, real dir, real height, real speed, code InitCode, code EndCode, boolean DestroyDestructables returns nothing
        local real a = GetParabolicAngle(height, dist)
        local real tm = dist / (speed * Cos(a))
        local vector p1 = vector.create(GetUnitX(t), GetUnitY(t), GetUnitFlyHeight(t))
        local vector p2 = vector.create(p1.x + dist * Cos(dir), p1.y + dist * Sin(dir), p1.z)
        local vector vi
        local vector vf
        local Projectile P
        if height > 0. then
            set vi = vector.create(speed, dir, a)
            set vf = vector.create(vi.x, vi.y, -vi.z)
        else
            set vi = vector.create(speed, dir, 0.)
            set vf = vector.create(0., 0., 0.)
        endif
        set P = Projectile.createSpherical(t, p1, p2, vi, vf, tm, true, true)
        call P.SetStartCode(InitCode)
        if DestroyDestructables then
            call P.SetMoveCode(function MoveKnockBack)
        else
            call P.SetMoveCode(function MoveKnockBackBasic)
        endif
        call P.SetEndCode(EndCode)
    endfunction

    //===========================================================================
    //  Null function; for use with KnockBack's InitCode and EndCode arguments
    //===========================================================================
        function Null takes nothing returns nothing
        endfunction
    endlibrary
     

    GroupUtils
    Code (vJASS):

    library GroupUtils initializer Init requires optional xebasic
    //******************************************************************************
    //* BY: Rising_Dusk
    //*
    //* This library is a combination of several features relevant to groups. First
    //* and foremost, it contains a group stack that you can access dynamic groups
    //* from. It also provides means to refresh groups and clear any shadow
    //* references within them. The included boolexprs are there for backwards
    //* compatibility with maps that happen to use them. Since the 1.24c patch,
    //* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
    //* performance gain to using the BOOLEXPR_TRUE constant.
    //*
    //* Instead of creating/destroying groups, we have moved on to recycling them.
    //* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
    //* remember to call ReleaseGroup on a group when you are done using it. If you
    //* fail to do so enough times, the stack will overflow and no longer work.
    //*
    //* GroupRefresh cleans a group of any shadow references which may be clogging
    //* its hashtable. If you remove a unit from the game who is a member of a unit
    //* group, it will 'effectively' remove the unit from the group, but leave a
    //* shadow in its place. Calling GroupRefresh on a group will clean up any
    //* shadow references that may exist within it. It is only worth doing this on
    //* groups that you plan to have around for awhile.
    //*
    //* Constants that can be used from the library:
    //*     [group]    ENUM_GROUP      As you might expect, this group is good for
    //*                                when you need a group just for enumeration.
    //*     [boolexpr] BOOLEXPR_TRUE   This is a true boolexpr, which is important
    //*                                because a 'null' boolexpr in enumeration
    //*                                calls results in a leak. Use this instead.
    //*     [boolexpr] BOOLEXPR_FALSE  This exists mostly for completeness.
    //*
    //* This library also includes a simple implementation of a group enumeration
    //* call that factors collision of units in a given area of effect. This is
    //* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
    //*
    //* In your map, you can just replace all instances of GroupEnumUnitsInRange
    //* with GroupEnumUnitsInArea with identical arguments and your spells will
    //* consider all units colliding with the area of effect. After calling this
    //* function as you would normally call GroupEnumUnitsInRange, you are free to
    //* do anything with the group that you would normally do.
    //*
    //* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
    //* variable below and the library will use that as the added radius to check.
    //* If you use xebasic, however, the script will automatically use xe's
    //* collision size variable.
    //*
    //* You are also able to use GroupUnitsInArea. This function returns all units
    //* within the area, no matter what they are, which can be convenient for those
    //* instances where you actually want that.
    //*
    //* Example usage:
    //*     local group MyGroup = NewGroup()
    //*     call GroupRefresh(MyGroup)
    //*     call ReleaseGroup(MyGroup)
    //*     call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
    //*     call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
    //*
    globals
        //If you don't have xebasic in your map, this value will be used instead.
        //This value corresponds to the max collision size of a unit in your map.
        private constant real    MAX_COLLISION_SIZE = 197.
        //If you are insane and don't care about any of the protection involved in
        //this library, but want this script to be really fast, set this to true.
        private constant boolean LESS_SAFETY        = false
    endglobals

    globals
        //* Constants that are available to the user
        group    ENUM_GROUP     = CreateGroup()
        boolexpr BOOLEXPR_TRUE  = null
        boolexpr BOOLEXPR_FALSE = null
    endglobals

    globals
        //* Hashtable for debug purposes
        private hashtable     ht     = InitHashtable()
        //* Temporary references for GroupRefresh
        private boolean       Flag   = false
        private group         Refr   = null
        //* Arrays and counter for the group stack
        private group   array Groups
        private integer       Count  = 0
        //* Variables for use with the GroupUnitsInArea function
        private real          X      = 0.
        private real          Y      = 0.
        private real          R      = 0.
        private hashtable     H      = InitHashtable()
    endglobals

    private function HookDestroyGroup takes group g returns nothing
        if g == ENUM_GROUP then
            call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
        endif
    endfunction

    debug hook DestroyGroup HookDestroyGroup

    private function AddEx takes nothing returns nothing
        if Flag then
            call GroupClear(Refr)
            set Flag = false
        endif
        call GroupAddUnit(Refr, GetEnumUnit())
    endfunction
    function GroupRefresh takes group g returns nothing
        set Flag = true
        set Refr = g
        call ForGroup(Refr, function AddEx)
        if Flag then
            call GroupClear(g)
        endif
    endfunction

    function NewGroup takes nothing returns group
        if Count == 0 then
            set Groups[0] = CreateGroup()
        else
            set Count = Count - 1
        endif
        static if not LESS_SAFETY then
            call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
        endif
        return Groups[Count]
    endfunction
    function ReleaseGroup takes group g returns boolean
        local integer id = GetHandleId(g)
        static if LESS_SAFETY then
            if g == null then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
                return false
            elseif Count == 8191 then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
                call DestroyGroup(g)
                return false
            endif
        else
            if g == null then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
                return false
            elseif not HaveSavedInteger(ht, 0, id) then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
                return false
            elseif LoadInteger(ht, 0, id) == 2 then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
                return false
            elseif Count == 8191 then
                debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
                call DestroyGroup(g)
                return false
            endif
            call SaveInteger(ht, 0, id, 2)
        endif
        call GroupClear(g)
        set Groups[Count] = g
        set Count         = Count + 1
        return true
    endfunction

    private function Filter takes nothing returns boolean
        return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
    endfunction

    private function HookDestroyBoolExpr takes boolexpr b returns nothing
        local integer bid = GetHandleId(b)
        if HaveSavedHandle(H, 0, bid) then
            //Clear the saved boolexpr
            call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
            call RemoveSavedHandle(H, 0, bid)
        endif
    endfunction

    hook DestroyBoolExpr HookDestroyBoolExpr

    private constant function GetRadius takes real radius returns real
        static if LIBRARY_xebasic then
            return radius+XE_MAX_COLLISION_SIZE
        else
            return radius+MAX_COLLISION_SIZE
        endif
    endfunction

    function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
        local real    prevX = X
        local real    prevY = Y
        local real    prevR = R
        local integer bid   = 0
       
        //Set variables to new values
        set X = x
        set Y = y
        set R = radius
        if filter == null then
            //Adjusts for null boolexprs passed to the function
            set filter = Condition(function Filter)
        else
            //Check for a saved boolexpr
            set bid = GetHandleId(filter)
            if HaveSavedHandle(H, 0, bid) then
                //Set the filter to use to the saved one
                set filter = LoadBooleanExprHandle(H, 0, bid)
            else
                //Create a new And() boolexpr for this filter
                set filter = And(Condition(function Filter), filter)
                call SaveBooleanExprHandle(H, 0, bid, filter)
            endif
        endif
        //Enumerate, if they want to use the boolexpr, this lets them
        call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
        //Give back original settings so nested enumerations work
        set X = prevX
        set Y = prevY
        set R = prevR
    endfunction

    function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
        local real prevX = X
        local real prevY = Y
        local real prevR = R
       
        //Set variables to new values
        set X = x
        set Y = y
        set R = radius
        //Enumerate
        call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
        //Give back original settings so nested enumerations work
        set X = prevX
        set Y = prevY
        set R = prevR
    endfunction

    private function True takes nothing returns boolean
        return true
    endfunction
    private function False takes nothing returns boolean
        return false
    endfunction
    private function Init takes nothing returns nothing
        set BOOLEXPR_TRUE  = Condition(function True)
        set BOOLEXPR_FALSE = Condition(function False)
    endfunction
    endlibrary
     
     

    Attached Files:

    Last edited: Sep 17, 2011
  2. busterkomo

    busterkomo

    Joined:
    Jun 17, 2007
    Messages:
    1,423
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Switch from code tags to JASS tags.
     
  3. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    We need the library which contains the function KnockBack, it's probably spelled SmoothMovSys.
     
  4. Kam

    Kam

    Joined:
    Aug 3, 2004
    Messages:
    2,633
    Resources:
    23
    Models:
    8
    Icons:
    2
    Maps:
    13
    Resources:
    23
    Updated with what you needed.
     
  5. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,125
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
  6. Kam

    Kam

    Joined:
    Aug 3, 2004
    Messages:
    2,633
    Resources:
    23
    Models:
    8
    Icons:
    2
    Maps:
    13
    Resources:
    23
    I'm not too familiar with Jass or this particular spell as it's not mine I'm afraid.
     
  7. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,125
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Have you looked at the resource? You could use it for all of your collision
    and knockback needs, does not require JASS knowledge as the API is
    completely GUI.
     
  8. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    I don't need the map, i need the libraries.
     
  9. Kam

    Kam

    Joined:
    Aug 3, 2004
    Messages:
    2,633
    Resources:
    23
    Models:
    8
    Icons:
    2
    Maps:
    13
    Resources:
    23
    I currently use this knockback system in my map, could I make use of it?

    Code (vJASS):

    library KBS initializer Init
    //***************************************************************************************
    //**                                                                                   **    
    //**    Knockback System (KBS)                                    **By kenny!**        **
    //**    v 2.04                                                                         **
    //**                                                                                   **
    //**    A system I made that can be used for knockback spells in a variety of          **
    //**    applications in a map. User defined variables make it easy to give the desired **
    //**    effect for any spell.                                                          **
    //**                                                                                   **
    //**    Requires:                                                                      **
    //**        - A vJASS preprocessor (NewGen Pack).                                      **
    //**        - No other systems.                                                        **
    //**        - A dummy unit. One can be found in this map ('u000')                      **
    //**        - The special effects found in the import/export section of this map.      **
    //**          They are the "Dust.mdx" and the "SlideWater.mdx". Or you can use your    **
    //**          own if you have some.                                                    **
    //**                                                                                   **
    //***************************************************************************************

    //***************************************************************************************
    //**                                                                                   **
    //**    Other Information:                                                             **
    //**        - Angle is taken in degrees. But can easily be changed if you know how.    **
    //**        - Units will be stopped if they hit the map borders.                       **
    //**        - Remember to import the special effect into your map if you need them.    **
    //**        - There are two functions that can be used in this system.                 **
    //**        - NEW FUNCTION: KBS_BeginCommon() is for all the common knockback uses in  **
    //**          your map. It only takes 4 parameters, and use global parameters for Area **
    //**          AllowMove and CheckPathing. Making it an icredibly easy function to use. **
    //**                                                                                   **
    //**    Sliding Distances:                                                             **
    //**        - This is some general knowledge on the different distances and how to     **
    //**          get them.                                                                **
    //**        - Sliding distances have changed in this version of the system.            **
    //**        - The function call (KBS_Begin/Ex) now takes distance and duration         **
    //**          parameters instead of startspeed and deceleration.                       **
    //**        - Therefore the parameter 'Distance' will set the length of the knockback  **
    //**          while the parameter 'Duration" will set the time it will take.           **
    //**                                                                                   **
    //***************************************************************************************

    //***************************************************************************************
    //**                                                                                   **
    //**    Implementation:                                                                **
    //**        - First off you need the NewGen world editor. This is a must.              **
    //**        - Copy this trigger into your map. Or make a trigger in your map and       **
    //**          convert it to custom text. Then copy this text into that trigger.        **
    //**        - Now you will either need to export the special effects from this map to  **
    //**          your map, or you will need your own. I do not take credit for the        **
    //**          special effects found in this map.                                       **
    //**        - Once you have the effects in your map. Find the configuration block      **
    //**          that is underneath all this green text. Change the WATER_SFX and the     **
    //**          DIRT_SFX strings to the ones in your map. These should be the same if    **
    //**          you exported the ones from this map.                                     **
    //**        - Make sure you have the dummy unit needed. It can be any unit, but i      **
    //**          suggest you use the one found in this map, it is labelled 'Dummy Unit'   **
    //**          or 'u000'.                                                               **
    //**        - Your done! Save your map, make a spell using this system and try it out. **
    //**                                                                                   **
    //**    NOTE: Please report any bugs to me at thehelper.net via PM. (User name: kenny!)**
    //**                                                                                   **
    //***************************************************************************************
                                                                               
    //***************************************************************************************
    //**                                                                                   **
    //**    Usage:                                                                         **
    //**                                                                                   **
    //**                call KBS_Begin(Target,Distance,Duration,Angle,Area)                **
    //**                                       or                                          **
    //**call KBS_BeginEx(Target,Distance,Duration,Angle,Effect,Area,AllowMove,CheckPathing)**
    //**                                       or                                          **
    //**                call KBS_BeginCommon(Target,Distance,Duration,Angle)               **
    //**                                                                                   **
    //**    - Target        - The unit that will be used in the knockback.                 **
    //**    - Distance      - The distance of the knockback (how far the unit will slide). **                
    //**    - Duration      - The duration of the knockback refers to how long the unit    **
    //**                      will slide for.                                              **
    //**    - Angle         - The angle that the unit will be knocked back at.             **
    //**                      THIS MUST BE IN DEGREES. <- Easier for GUI users.            **
    //**    - Effect        - This is only used in the extended function. If you do not    **
    //**                      want one of the predefined effects, you can choose your own. **
    //**                      However, this effect is attached to the unit and removed at  **
    //**                      the end, so non-looping or repeating effect wont work.       **
    //**    - Area          - This determines whether or not trees will be knocked down.   **
    //**                      For trees to be knocked down, a positive number (real) must  **
    //**                      be used, such as 150.00, which would give a radius of 150.00 **
    //**                      in which trees will be knocked down.                         **
    //**                      For trees to not be knocked down, a negative number (real)   **
    //**                      must be used, such as -150.00, which would create a radius   **
    //**                      that if a tree comes within it, the unit will stop moving.   **
    //**                      For none of those effects, the number 0 (0.00) can be used.  **
    //**                      This will just cause the units to "bounce" off trees.        **
    //**    -Allowmove      - This boolean will decided whether or not you want the unit   **
    //**                      to have the ability to move while being knocked back.        **
    //**                      "true" will allow them to move, while "false" will not.      **
    //**    -CheckPathing   - A boolean that, if true, will check for unpathable terrain   **
    //**                      such as a wall or cliff, or where doodads may be. If false   **
    //**                      it will ignore these changes and the unit will be pushed     **
    //**                      along the wall, cliff or doodad.                             **
    //**                                                                                   **
    //**       REMEMBER: Positive = trees destroyed, Negative = trees not destroyed.       **
    //**                                                                                   **
    //**    Example of Usage:                                                              **
    //**                                                                                   **
    //**  -call KBS_Begin(target,500.00,2.00,270.00,150.00)                                **
    //**    or                                                                             **
    //**  -call KBS_BeginEx(target,500.00,2.00,270.00,"Effect.mdl",-150.00,true,true)      **
    //**                                                                                   **
    //**    The first one will cause the target unit of a spell to be knocked back 500.00  **
    //**    range over a 2.00 second duration. It will be knocked back at an angle of      **    
    //**    270.00, which I'm pretty sure is south, and it will knock down trees within    **
    //**    a 150.00 radius around the unit.                                               **
    //**                                                                                   **
    //**    The bottom one of the two will do the exact same as the top, however you are   **
    //**    able to choose your own special effect that will be attached to the unit. As   **
    //**    you can see, the number for Area (destroying trees) is negative, therefore     **
    //**    no trees will be knocked down, and the unit will stop moving if it comes in    **
    //**    contact with a tree. There is also an extra boolean nearing end, this is for   **
    //**    allowing units to move while sliding. It is true, meaning units are allowed.   **
    //**    There is also an added feature that will check for pathable terrain and stop a **
    //**    unit if it comes in contact with things such as walls, cliffs or doodads.      **
    //**    If true, the unit will stop moving if it comes in direct contact will a wall   **
    //**    or cliff.                                                                      **
    //**                                                                                   **
    //***************************************************************************************

    //***************************************************************************************
    //**                                                                                   **
    //**    Other functions:                                                               **
    //**                                                                                   **
    //**    - call KBS_IsUnitSliding(Target)                                               **
    //**                                                                                   **
    //**    - This function checks if a picked unit is currently sliding using this        **
    //**      system. It will return true if it is.                                        **
    //**                                                                                   **
    //**    - call KBS_StopUnitSliding(Target)                                             **
    //**                                                                                   **
    //**    - This function will stop a picked unit from sliding (using this system).      **
    //**      It also returns true if the unit is stopped.                                 **
    //**                                                                                   **
    //**    - These functions can be used in conjunction with each other, by checking if a **
    //**      unit is sliding then stopping it if it is. The regular Storm Bolt spell in   **
    //**      the Test Spells section uses these two function in conjunction with each     **
    //**      other as an example.                                                         **
    //**                                                                                   **
    //***************************************************************************************      

    //***************************************************************************************
    //**                                                                                   **
    //**    Some points on checking for pathable terrain:                                  **
    //**                                                                                   **
    //**`   - The area specified for destroying trees must be at least 75.00-100.00 range  **
    //**      larger than the distance for checking for pathing, which is below in the     **
    //**      globals block.                                                               **
    //**    - When using KBS_BeginEx() and using either 0.00 or a negative value for the   **
    //**      parameter 'Area' (destroying trees) it is better to have CheckPathing as     **
    //**      FALSE. This is due to the fact that setting CheckPathing to TRUE will        **
    //**      override the 'Area' Parameter and stop a unit if it gets near trees.         **
    //**    - Basically what this means is that you cannot get a unit to just 'bounce' off **
    //**      trees anymore, it is either destroy trees or get stopped by them.            **
    //**                                                                                   **
    //***************************************************************************************

    globals
        // CONFIGURABLES:
       
        private constant integer DUMMY_ID       = 'u001'           // This is the dummy unit.
        private constant real    INTERVAL       = 0.04             // Recommended Interval.
        private constant string  ATTACH_POINT   = "origin"         // Attachment point for effects.
        private constant string  WATER_SFX      = "war3mapImported\\SlideWater.mdx" // Water slide effect.
        private constant string  DIRT_SFX       = "war3mapImported\\Dust.mdx"       // Ground slide effect.
        private constant string  COLLISION_SFX  = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl" // Collision effect when unit hits a wall/cliff etc.
        private constant boolean SHOW_COLLISION = true             // Whether or not effect will show if a unit collides with a wall/cliff etc.
       
        // The follow are used when using KBS_BeginCommon() for the common parameters that will be used.
       
        private constant real    COMMON_AREA          = 150.00  // The area in which trees will be destroyed.
        private constant boolean COMMON_ALLOW_MOVE    = true    // Whether or not to allow units to move.
        private constant boolean COMMON_CHECK_PATHING = true    // Whether or not to check pathing.
       
        // The following raw codes will probably not need to be changed for most maps.
       
        private constant integer HARVEST        = 'Ahrl' // Raw code for: Harvest(Ghouls Lumber).
        private constant integer LOCUST         = 'Aloc'  // Raw code for Locust ability.
       
        // The following value is used for terrain pathability checking.
       
        private constant real    DISTANCE       = 50.00
        // The above is the distance in front of the unit that terrain pathability
        // will be checked. A value of 40.00-50.00 is recommended. Also make sure
        // that this distance is AT LEAST 75.00-100.00 units less than the ranges set
        // for destroying trees, otherwise it wont work properly.
       
        // Example: Keep above distance at 50.00, and use 150.00 for knocking down trees.
        //          This seems to work very well.
       
        private constant real    MAX_RANGE      = 10.00
        // Max range for checking for pathability. I suggest you leave this alone.
    endglobals

    //***************************************************************************************
    //**                                                                                   **
    //**          DO NOT TOUCH BELOW HERE UNLESS YOU KNOW WHAT YOUR ARE DOING!             **
    //**                                                                                   **
    //***************************************************************************************    

    private keyword Data

    globals
        private Data     array D
        private boolean  array BA
        private item     array Hidden
        private integer  Hidden_max       = 0
        private integer  Total            = 0
        private real     Game_maxX        = 0.00
        private real     Game_minX        = 0.00
        private real     Game_maxY        = 0.00
        private real     Game_minY        = 0.00
        private rect     Rect1            = null
        private unit     Tree_dummy       = null
        private item     Item             = null
        private timer    Timer            = null
        private boolexpr Tree_filt        = null
    endglobals

    //=======================================================================
    private function Filter_items takes nothing returns nothing
        if IsItemVisible(GetEnumItem()) then
            set Hidden[Hidden_max] = GetEnumItem()
            call SetItemVisible(Hidden[Hidden_max],false)
            set Hidden_max = Hidden_max + 1
        endif
    endfunction

    // Thanks to Vexorian for original concept, and Anitarf for the up-to-date version. (Slightly modified).
    private function Check_pathability takes real x1, real y1 returns boolean
        local real x2 = 0.00
        local real y2 = 0.00
       
        call SetRect(Rect1,0.00,0.00,128.00,128.00)
        call MoveRectTo(Rect1,x1,y1)
        call EnumItemsInRect(Rect1,null,function Filter_items)

        call SetItemPosition(Item,x1,y1)
        set x2 = GetItemX(Item)
        set y2 = GetItemY(Item)
        call SetItemVisible(Item,false)

        loop
            exitwhen Hidden_max <= 0
            set Hidden_max = Hidden_max - 1
            call SetItemVisible(Hidden[Hidden_max],true)
            set Hidden[Hidden_max] = null
        endloop

        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < MAX_RANGE * MAX_RANGE
    endfunction

    //=======================================================================
    private function Destroy_trees takes nothing returns nothing
        if BA[0] then
            call KillDestructable(GetEnumDestructable()) // Used to destroy destructables.
        else
            set BA[1] = true
        endif
    endfunction

    // Thanks to PitzerMike for this function. (Modified Slightly).
    private function Check_trees takes nothing returns boolean
        local destructable dest   = GetFilterDestructable()
        local boolean      result = false
       
        if GetDestructableLife(dest) > 0.405 then
            call ShowUnit(Tree_dummy,true)
            call SetUnitX(Tree_dummy,GetWidgetX(dest))
            call SetUnitY(Tree_dummy,GetWidgetY(dest))
           
            set result = IssueTargetOrder(Tree_dummy,"harvest",dest)
            call IssueImmediateOrder(Tree_dummy,"stop")
            call ShowUnit(Tree_dummy,false)
           
            set dest = null
            return result
        endif
       
        set dest = null
        return result
    endfunction

    // Checks if a point is on the map boundaries.
    private function Outside_bounds takes real x, real y returns boolean
        return (x > Game_maxX or y > Game_maxY or x < Game_minX or y < Game_minY)
    endfunction

    //=======================================================================
    private struct Data
        unit    targ      = null
       
        real    speed     = 0.00
        real    decrease  = 0.00
        real    sin       = 0.00
        real    cos       = 0.00
        real    radius    = 0.00
       
        integer sfxmode   = 0
        effect  sfx       = null
       
        boolean custom    = false
        boolean allowmove = false
        boolean pathing   = false
        boolean forcestop = false
       
        // Checking for terrain type.
        method terrain takes nothing returns integer
            local real x = GetUnitX(.targ)
            local real y = GetUnitY(.targ)

            if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
                return 1
            elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
                return 2
            endif
           
            return 0
        endmethod
       
        static method create takes unit Target, real Distance, real Duration, real Angle, string Effect, real Area, boolean AllowMove, boolean CheckPathing returns Data
            local Data d = Data.allocate()
           
            // Allocates struct members to user defined variables.
            set d.targ      = Target
            set d.speed     = (2.00 * Distance) / (Duration + 1.00)
            set d.decrease  = d.speed / Duration
            set d.sin       = Sin(Angle)
            set d.cos       = Cos(Angle)
            set d.radius    = Area
            set d.allowmove = AllowMove
            set d.pathing   = CheckPathing
            set d.sfxmode   = d.terrain()
           
            if Effect != "" and Effect != null then
                set d.custom = true
            endif
           
            // Adding effects to the unit.
            if d.custom then
                set d.sfx = AddSpecialEffectTarget(Effect,d.targ,ATTACH_POINT)
            else
                if d.sfxmode == 1 then
                    set d.sfx = AddSpecialEffectTarget(DIRT_SFX,d.targ,ATTACH_POINT)
                elseif d.sfxmode == 2 then
                    set d.sfx = AddSpecialEffectTarget(WATER_SFX,d.targ,ATTACH_POINT)
                endif
            endif
           
            return d
        endmethod
       
        private method onDestroy takes nothing returns nothing
            set .targ = null
            if .sfx != null then
                call DestroyEffect(.sfx) // Destroys effects if needed.
                set .sfx = null
            endif
            set BA[0] = false
            set BA[1] = false
        endmethod
    endstruct

    //=======================================================================
    private function Update takes nothing returns nothing
        local integer i       = 1
        local integer newmode = 0
        local integer height  = 0
        local Data    d       = 0
        local real    x       = 0.00
        local real    y       = 0.00
        local real    newx    = 0.00
        local real    newy    = 0.00
           
        loop
            exitwhen i > Total
            set d = D[i]
            set newmode = d.sfxmode
           
            set x = GetUnitX(d.targ)
            set y = GetUnitY(d.targ)
           
            // Destroys trees if wanted, or stops the unit
            if d.radius != 0.00 then
                set BA[0] = d.radius > 0.00

                call SetRect(Rect1,x - d.radius,y - d.radius,x + d.radius,y + d.radius)
                call EnumDestructablesInRect(Rect1,Tree_filt,function Destroy_trees)
            endif
           
            // Checks for terrain pathability, such as walls and cliffs.
            if d.pathing then
                if Check_pathability(x + DISTANCE * d.cos,y + DISTANCE * d.sin) == false then
                    set height = 1
                    if SHOW_COLLISION then
                        call DestroyEffect(AddSpecialEffect(COLLISION_SFX,x,y))
                    endif
                endif
            endif

            if not d.custom then
                set d.sfxmode = d.terrain() // Checks for pathing again.                
               
                // Adds special effect if terrain changes.
                if d.sfxmode == 1 and newmode == 2 then
                    call DestroyEffect(d.sfx)
                    set d.sfx = AddSpecialEffectTarget(DIRT_SFX,d.targ,ATTACH_POINT)
                elseif d.sfxmode == 2 and newmode == 1 then
                    call DestroyEffect(d.sfx)
                    set d.sfx = AddSpecialEffectTarget(WATER_SFX,d.targ,ATTACH_POINT)
                endif
            endif
             
            if d.speed <= 0 or Outside_bounds(x,y) or BA[1] or height == 1 or d.forcestop == true then
                call d.destroy() // Finish knockback
                set D[i] = D[Total]
                set Total = Total - 1
                set i = i - 1
                set height = 0
            else
                set newx = x + d.speed * d.cos
                set newy = y + d.speed * d.sin
               
                // Allows unit to move while sliding, if specified.
                if d.allowmove then
                    call SetUnitX(d.targ,newx)
                    call SetUnitY(d.targ,newy)
                else
                    call SetUnitPosition(d.targ,newx,newy)
                endif
                   
                set d.speed = d.speed - d.decrease // Sets new speed.
            endif
               
            set i = i + 1
        endloop
       
        if Total <= 0 then
            call PauseTimer(Timer)
            set Total = 0
        endif
    endfunction

    //=======================================================================
    // Checks if a unit is sliding - returns true if it is.
    public function IsUnitSliding takes unit Target returns boolean
        local integer i = 1

        loop
            exitwhen i > Total
            if D[i].targ == Target then
                return true
            endif
            set i = i + 1
        endloop
       
        return false
    endfunction

    //=======================================================================
    // Stops a unit from sliding - returns true if a unit is stopped.
    public function StopUnitSliding takes unit Target returns boolean
        local integer i = 1

        loop
            exitwhen i > Total
            if D[i].targ == Target then
                set D[i].forcestop = true
                return true
            endif
            set i = i + 1
        endloop
       
        return false
    endfunction

    //=======================================================================
    // Extended function - gives the most customisation for a single unit knockback.
    public function BeginEx takes unit Target, real Distance, real Duration, real Angle, string Effect, real Area, boolean AllowMove, boolean CheckPathing returns boolean
        local Data d = 0
       
        if Target == null or Distance <= 0.00 or Duration <= 0.00 then
            debug if Target == null then
            debug call BJDebugMsg( "Error: Target is null" )
            debug elseif Distance <= 0.00 then
            debug call BJDebugMsg( "Error: Distance <= 0.00" )
            debug elseif Duration <= 0.00 then
            debug call BJDebugMsg( "Error: Duration <= 0.00" )
            debug endif
            debug call BJDebugMsg("Error: Invalid input in KBS_Begin(Ex)") // Error message.
            return false
        endif
       
        set d = Data.create(Target,Distance,(Duration/INTERVAL),(Angle * 0.01745328),Effect,Area,AllowMove,CheckPathing)
       
        set Total = Total + 1
        if Total == 1 then
            call TimerStart(Timer,INTERVAL,true,function Update)
        endif
        set D[Total] = d
       
        return true
    endfunction

    //=======================================================================
    // Basic function - Can be used in a wide variety of ways and gives a basic knockback.
    public function Begin takes unit Target, real Distance, real Duration, real Angle, real Area returns nothing    
        call BeginEx(Target,Distance,Duration,Angle,"",Area,false,false)
    endfunction

    // Common function - For all basic knockback needs, easy to use and simple to remember.
    public function BeginCommon takes unit Target, real Distance, real Duration, real Angle returns nothing
        call BeginEx(Target,Distance,Duration,Angle,null,COMMON_AREA,COMMON_ALLOW_MOVE,COMMON_CHECK_PATHING)
    endfunction

    //=======================================================================
    // Sets map boundries and sets timer, rect and filter.
    private function Init takes nothing returns nothing
        set Timer     = CreateTimer()
        set Rect1     = Rect(0.00,0.00,1.00,1.00)
        set Tree_filt = Filter(function Check_trees)
       
        // Map bounds
        set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
        set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
        set Game_minX = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
        set Game_minY = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
       
        // Boolean values for destroying trees
        set BA[0]     = false
        set BA[1]     = false
         
        // Creating unit for destroying trees
        set Tree_dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),DUMMY_ID,0.00,0.00,0.00)
        call SetUnitPathing(Tree_dummy,false)
        call ShowUnit(Tree_dummy,false)
        call UnitAddAbility(Tree_dummy,HARVEST)
        call UnitAddAbility(Tree_dummy,LOCUST)
       
        // Creating item for pathability checking
        set Item = CreateItem('ciri',0.00,0.00)
        call SetItemVisible(Item,false)  
    endfunction

    endlibrary
     
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,125
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Checking with an item is a bad idea because the item has a
    collision size of 16. Most units, like heroes for instance, have
    a larger collision size than that (32 is probably the average).

    The Infernal has a 48 collision size.

    What this means is that it is possible to knock a unit through
    an otherwise impassible place using Kenny's Knockback sys,
    meaning it is unsafe to use reliably in a map.

    In Knockback 2D is the first knockback system I've seen that
    actually checks for a myriad of collision sizes.
     
  11. Kam

    Kam

    Joined:
    Aug 3, 2004
    Messages:
    2,633
    Resources:
    23
    Models:
    8
    Icons:
    2
    Maps:
    13
    Resources:
    23
    So how do I indicate to the original spell that it should use your system instead of the SmoothMovSys it requires now?
     
  12. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,829
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    You need to look at the interfaces for both systems and alter the system to support the other. You can additionally use an adapter (where you wrap different but simlar functionality to the same format as the needed functionality).
     
  13. Troll-Brain

    Troll-Brain

    Joined:
    Apr 27, 2008
    Messages:
    2,372
    Resources:
    1
    JASS:
    1
    Resources:
    1
    You've copied/pasted the library SmoothMovSys two times.
    And unfortunately it seems that the KnockBack function you are using for the spell, is a part of the library ProjSys which is missing (or at least it isn't inside the versions of the already submitted libraries)

    EDIT :

    Woot, now that i can see it entirely, the code is really spaghetti and lame, i can't bear it !

    i could fix it but really when i see TriggerExecute instead of TriggerEvaluate, TriggerClearActions which doesn't clean up the memory and all, etc ...
    Just use a better one, maybe Bribe's one (not checked but it can't be worse than that)

    I suppose he will be kind enough to explain how to use it in details (or hurt him hardly)
     
    Last edited: Sep 17, 2011
  14. Kam

    Kam

    Joined:
    Aug 3, 2004
    Messages:
    2,633
    Resources:
    23
    Models:
    8
    Icons:
    2
    Maps:
    13
    Resources:
    23
    It's all there now.