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. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. 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.

Knockback Unit

Discussion in 'Graveyard' started by Silvenon, Jun 17, 2007.

  1. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Knockback(Ex)


    Credits to:

    Fixes:
    • renamed UnitKnockback/UnitKnockback_Execute into Knockback/Knockback_Execute (actually made Execute public)
    • removed UnitKnockbackWithLightning, found out it's useless
    • optimized the performance, it only uses Jass NewGen Pack now
    • added KnockbackEx function
    • changed the parameters a bit, added new feature(s)
    • uses initializer Init_Knockback, so no map initialization trigger is required anymore
    More fixes:
    • this version actually works
    • doesn't use the stupid CheckPathability function anymore
    • has a different system instead
    • added instructions for GUI users
    • removed the CSSafety requirement
    • renamed Knockback_Init function
    • added a new radius feature (look in the parameter explanation)
    • removed the Data returnage, PP was right, it can only screw things up
    • modified the code, so now it uses create and onDestroy methods, I hope it's more readable now

    Code (vJASS):
    library Knockback initializer Init

    // **************************************************************************
    // **                                                                      **
    // ** Knockback(Ex)                                                        **
    // ** —————————————                                                        **
    // **                                                                      **
    // ** A function made for efficient knockbacking                           **
    // **                                                                      **
    // ** By: Silvenon                                                         **
    // **                                                                      **
    // **************************************************************************

    //=======================================//
    //Credits to PitzerMike for this function//
    //=======================================//

    globals
        private constant integer DUMMY_ID = 'h000'
    endglobals

    private function TreeFilter takes nothing returns boolean
        local destructable d = GetFilterDestructable()
        local boolean i = IsDestructableInvulnerable(d)
        local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, GetWidgetX(d), GetWidgetY(d), 0)
        local boolean result = false

        call UnitAddAbility(u, 'Ahrl')

        if i then
            call SetDestructableInvulnerable(d, false)
        endif

        set result = IssueTargetOrder(u, "harvest", d)
        call RemoveUnit(u)

        if i then
          call SetDestructableInvulnerable(d, true)
        endif

        set u = null
        set d = null
        return result
    endfunction

    //===========================================================================

    globals
        private timer Tim = CreateTimer()
        private integer Total = 0
        private boolexpr Cond = null

        private integer array Ar
        private boolean array BoolAr

        private real MAX_X
        private real MAX_Y
        private real MIN_X
        private real MIN_Y
    endglobals

    private constant function Interval takes nothing returns real
        return 0.04
    endfunction

    private function KillTree takes nothing returns nothing
        if BoolAr[0] then
            call KillDestructable(GetEnumDestructable())
        else
            set BoolAr[1] = true
        endif
    endfunction

    public struct Data
        unit u
        real d1
        real d2

        real sin
        real cos

        real r

        string s = ""
        effect e = null
       
        static method create takes unit u, integer q, real d, real a, real r, integer t, string s, string p returns Data
            local Data dat = Data.allocate()

            set dat.u = u
            set dat.d1 = 2 * d / (q + 1)
            set dat.d2 = dat.d1 / q

            set dat.sin = Sin(a)
            set dat.cos = Cos(a)

            set dat.r = r

            if s != "" and s != null then
                if t == 2 then
                    if p != "" and p != null then
                        set dat.e = AddSpecialEffectTarget(s, u, p)
                    else
                        set dat.e = AddSpecialEffectTarget(s, u, "chest")
                    endif
                elseif t == 1 then
                    set dat.s = s
                endif
            endif

            call SetUnitPosition(u, GetUnitX(u), GetUnitY(u))
            call PauseUnit(u, true)

            if Total == 0 then
                call TimerStart(Tim, Interval(), true, function Data.Execute)
            endif

            set Total = Total + 1
            set Ar[Total - 1] = dat

            return dat
        endmethod
       
        static method Execute takes nothing returns nothing
            local Data dat
            local integer i = 0
            local real x
            local real y
            local rect r
            local real rad
           
            loop
                exitwhen i >= Total
                set dat = Ar[i]
               
                if dat.s != "" and dat.s != null then
                    set x = GetUnitX(dat.u)
                    set y = GetUnitY(dat.u)
                   
                    call DestroyEffect(AddSpecialEffect(dat.s, x, y))
                   
                    set x = x + dat.d1 * dat.cos
                    set y = y + dat.d1 * dat.sin
                else
                    set x = GetUnitX(dat.u) + dat.d1 * dat.cos
                    set y = GetUnitY(dat.u) + dat.d1 * dat.sin
                endif
               
                if dat.r != 0 then
                    set BoolAr[0] = dat.r > 0
                   
                    set rad = dat.r
                   
                    if not BoolAr[0] then
                        set rad = rad * (-1)
                    endif
                   
                    set r = Rect(x - rad, y - rad, x + rad, y + rad)

                    call EnumDestructablesInRect(r, Cond, function KillTree)
                    call RemoveRect(r)
                   
                    set r = null
                endif
               
                if (x < MAX_X and y < MAX_Y and x > MIN_X and y > MIN_Y) and not BoolAr[1] then
                    call SetUnitX(dat.u, x)
                    call SetUnitY(dat.u, y)
                endif
               
                set dat.d1 = dat.d1 - dat.d2
               
                if dat.d1 <= 0 or (x > MAX_X or y > MAX_Y or x < MIN_X or y < MIN_Y) or BoolAr[1] then
                    set Ar[i] = Ar[Total - 1]
                    set Total = Total - 1
                   
                    call dat.destroy()
                endif
               
                set i = i + 1
            endloop
           
            if Total == 0 then
                call PauseTimer(Tim)
            endif
        endmethod
       
        method onDestroy takes nothing returns nothing
            if .e != null then
                call DestroyEffect(.e)
            endif

            call PauseUnit(.u, false)

            set BoolAr[0] = false
            set BoolAr[1] = false
        endmethod
    endstruct

    function KnockbackEx takes unit u, real d, real a, real w, real r, integer t, string s, string p returns nothing
        call Data.create(u, R2I(w / Interval()), d, a, r, t, s, p)
    endfunction

    function Knockback takes unit u, real d, real a, real w returns nothing
        call Data.create(u, R2I(w / Interval()), d, a, 0, 0, "", "")
    endfunction

    private function Init takes nothing returns nothing
        set Cond = Filter(function TreeFilter)

        set BoolAr[0] = false
        set BoolAr[1] = false

        set MAX_X = GetRectMaxX(bj_mapInitialPlayableArea) - 64
        set MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea) - 64
        set MIN_X = GetRectMinX(bj_mapInitialPlayableArea) + 64
        set MIN_Y = GetRectMinY(bj_mapInitialPlayableArea) + 64
    endfunction

    endlibrary



    Are you getting tired of writing a knockback code every single time when you want to knockback something? If the answer is yes, then this is what you need. This function knockbacks a unit according to the parameters below:
    • unit u = the unit being knockbacked
    • real d = the distance the unit is knockbacked to
    • real a = the angle (direction of the knockback) in RADIANS
    • real w = the duration of the knockback
    • real r = the radius of tree destroying, but there's a new trick: if you put a negative number as the radius, the unit will stop knockbacking when it gets in that range of a tree and if you put a positive value, the unit will destroy trees when it gets in that range of a tree (if you don't want any of those, just put 0)
    • integer t = type 0 means no effect (both s and p should be ""), type 1 means periodic effect (meaning you should put "" as the attachment point, parameter p), type 2 means a special effect attached on the unit and destroyed when the knockback is finished (this is where you use parameter p)
    • string s = the path to the special effect you want to use (periodic), that effect will be destroyed immediatelly (you can put "" if you don't want a special effect)
    • string p = used for type 2, it's the attachment point of the effect, "chest" is default (so if you put "", it will be "chest")

    Instructions:

    Create a dummy unit according to one of these two tutorials: 1, 2 (I suggest the second one). You MUST change the DUMMY_ID constant integer to that unit's id.

    You can deprivate any function from the library you want to use, I just made it like that so it doesn't interfere with other stuff. If you don't want any special effects and tree destroying, you can use Knockback function that takes only the first 4 parameters.

    If you by any chance have angle in degrees and want to use it for this function, when calling the function just multiply that angle with bj_DEGTORAD, example:

    call Knockback(u, d, a * bj_DEGTORAD, w)



    You can change the interval of execution in the Knockback_Interval constant (default is 0.035).

    Example of usage:

    [jass=JASS]function Test takes nothing returns nothing
    local unit c = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    local real x1 = GetUnitX(c)
    local real y1 = GetUnitY(c)
    local real x2 = GetUnitX(t)
    local real y2 = GetUnitY(t)
    local real dist = 500
    local real angle = Atan2(y2 - y1, x2 - x1)
    local real dur = 2
    call Knockback(t, dist, angle, dur)
    set c = null
    set t = null
    endfunction[/jass]


    [trigger=GUI]Test
    Events
    Unit - A unit starts the effect of an ability
    Conditions
    (Ability being cast) Equal to <Some Spell>
    Actions
    Set Caster = (Triggering unit)
    Set Target = (Target unit of ability being cast)
    Set Distance = (400 + (100 x (Ability level of <Some Spell> for Caster)))
    Set Angle = (Facing angle of Caster)
    Set Duration = 2
    Custom script: call Knockback(udg_Target, udg_Distance, udg_Angle * bj_DEGTORAD, udg_Duration)[/trigger]



    On the same principle you use the KnockbackEx function


    Requires: Jass NewGen Pack

    Find more about the function in my Knockback tutorial

    Please report any errors.
     
    Last edited: Jul 28, 2008
  2. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    If you are using CSCache, how come that you don't use tables instead? they are faster, and else i don't really see any reason not to use handle vars, as people here are more familiar with them.

    I suggest you to use constant functions, as it is faster than attaching stuff and much easier to change.
     
  3. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Please explain what do you mean by using constants. I changed it into handle vars. Btw I never learned using tables because I switched to structs after handle vars.
     
  4. PurplePoot

    PurplePoot

    Joined:
    Dec 14, 2005
    Messages:
    11,161
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    What he means by using Constants is instead of doing something like this

    Code (vJASS):
    function myFunc takes paramArgs returns type
        local integer imaconstant = 5
        call DoSomething( imaconstant )
    endfunction


    do something like

    Code (vJASS):
    constant function imaconstant takes nothing returns integer
        return 5
    endfunction

    function myFunc takes paramArgs returns type
        call DoSomething( imaconstant() )
    endfunction


    Tools like Vexorian's Map Optimizer inline the call anyways, and it makes the function more readable+easier to change, as you know by looking at the top what alot of the things do (if you name the functions well)

    Anyways, you should probably also remove the damage option from the function. It's clogging up the parameters with stuff that will often be left unused, and is much less flexible than UnitDamageTarget.

    It's probably better to either

    A) rename that UnitKnockbackEx and have a copy without the damage feature
    B) remove it altogether

    In other news, I'll approve this if those're fixed. (well, the second one, the first one doesn't matter that much)
     
  5. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    *Removed damage options. Heh my original function didn't have damage options, but I thought people would want that, but now I realize it was a mistake (I thought damage over time was better).

    I know what are constants and how to use them, but can you please explain how would i use constants in my knockback functions?? What values would constants return? How would it then allow different knocbacks? Please give more details, as I would love to improve it.
     
  6. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Code (vJASS):

    constant function Knockback_Interval takes nothing returns real
        return 0.035
    endfunction
    //Instead of attaching the stuff just do like this:
     
        call TimerStart(t, Knockback_Interval(), true, function UnitKnockback_Execute)
     


    Interval is VERY important to be changeable as a constant.
     
  7. PurplePoot

    PurplePoot

    Joined:
    Dec 14, 2005
    Messages:
    11,161
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    ~approved, now let's see if I can find out how~
     
  8. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Then you mean constant only for the interval then? Why is it so important to be able to change it? Btw I already told how to do it, but ok......*fixed, that better?
     
  9. PurplePoot

    PurplePoot

    Joined:
    Dec 14, 2005
    Messages:
    11,161
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Yeah, better.

    The reason it's better to make the interval changeable is that an interval of .04 or so is more practical for speed, so this alows the user to balance the efficiency they want with the smoothness they want.
     
  10. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Ok. No feedback people? No one wants to comment this brilliant function?? :(:(

    EDIT: Is it ok if I add IsTerrainPathable? Because it can get annoying if the unit gets knocked outside the playable map area.
     
    Last edited: Jun 20, 2007
  11. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
  12. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Yeah, I have vJass version too, sry :p *fixed

    EDIT: Added UnitKnockbackWithLightning for knockbacking with a lightning bolt (I use that quite often)
     
  13. HappyTauren

    HappyTauren

    Joined:
    Nov 3, 2006
    Messages:
    8,413
    Resources:
    87
    Models:
    61
    Icons:
    23
    Packs:
    1
    Tutorials:
    2
    Resources:
    87
    om6 h4x tis l33t, sir my use it in my map plz? AWsUM THX

    +REP
     
  14. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Isn't flushing one thing manually with SetHandleInt(t,"kd", 0) faster than flushing it all with FlushHandleLocals()?
     
    Last edited: Jun 30, 2007
  15. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Last edited: Jul 1, 2007
  16. Zergleb

    Zergleb

    Joined:
    Dec 14, 2004
    Messages:
    84
    Resources:
    1
    Maps:
    1
    Resources:
    1
    You have a bunch of functions that start with alot of typing and then a _ and then a sub name for the function. Why not just set these into scopes and then make them public? it does the same thing but if you use the function inside the scope you don't have to type or read all of that.
     
  17. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Well now I already done it, typed and read all of it. So it won't make any difference when I change it, though I'm not sure what you mean.
     
  18. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    A way to make it more efficient would be getting rid of the Cos() and Sin() in the Excecute function.

    Code (vJASS):

        local real x=GetUnitX(SomeUnitWhoKnocks)
        local real y=GetUnitY(SomeUnitWhoKnocks)
        local real x2=GetUnitX(SomeKnockedUnit)
        local real y2=GetUnitY(SomeKnockedUnit)
        local real angle=Atan2(y2-y, x2-x)
        local real xspeed
        local real yspeed
        local real speed= 22 //How long the unit is knocked every 0.035 second or whatever the interval is
        set xspeed=speed*Cos(angle)
        set yspeed=speed*Sin(angle)
     

    How to use then in a periodical trigger.
    Code (vJASS):

        call SetUnitX(u, GetUnitX(u)+xspeed)
        call SetUnitY(u, GetUnitY(u)+yspeed)
     

    instead of calling Cos() and Sin() every time the unit is moved you can just add x/yspeed onto it's current position.
     
  19. Silvenon

    Silvenon

    Joined:
    Nov 22, 2006
    Messages:
    1,233
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    But the speed lowers with each execution, shouldn't I just put Cos(angle) and Sin(angle) in a variable? Then it won't be calling Cos and Sin in every execution. I will update this function when I get back from the holidays, that would be in 2 weeks. You probably noticed that it's missing an important update: prevent going outside the playable map area. :)
     
  20. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Doesn't it keep having the same speed untill it has reached the distance?

    Well, if it doesn't, then yes, storing Cos/Sin would be more efficient than calling it each time the timer expires.