System Extended Knockback System v2

wraithseeker

Tired.
Reaction score
122
Recently I have found out that EKB is very messy so I decided to make a v2 verison of it.

Requires AutoIndex, GroupUtils, BoundSentinel, IsDestructableTree, IsTerrainWalkable

Documention

JASS:
Knockback System                               By wraithseeker
   v 1.10

                                        Credits
                                    Vile for SFX.
                                    Dusk for GroupUtils.
                                    PitzerMike for IsDestructableLibrary.
                                    Anitarf for IsTerrainWalkable.
                                    Grim001 for AutoIndex
                                    Pyrogasm & Kenny for Inspiration!
                                    Kenny for another way of creation.
                                    Anyone who has helped me in one way or another.
                                                                    
                                Changelog v1.01
                                    
                - Removed d.destroy() and changed to d.release() for PUI issues.
                - Took away some silly parameters which was totally useless.
                - Optimized the code.
                            
                                Changelog v1.02
                - Optimized code.
                - Indented some code.
                - Added some extra parametres.
                - unit height no longer gets bugged when at the borders of the map.
                
                                Changelog v1.03
                - Removed some useless struct members.
                - Made method Terraincheck not public and not to take knock d for efficient purposes.
                - Updated documention.
                - Code now has comments and names are more friendly.
                
                                Changelog v1.04
                - Used PUI text macro to shorten things.
                - Removed some useless call.
                
                                Changelog v1.05
                - Fixed a serious bug.
                
                                Changelog v1.06
                - Remade the whole system.
                
                                Changelog v1.07
                - Added extra functions and optimized code.
                            
                                Changelog v1.08
                - Removed ParabolaZ and added another new way of jumping.
                - Removed useless stuff.
                - Ported to AutoIndex.
                                Changelog v1.09
                - Changed another way of jumping again.
                - Added 2 different ways of knocking which is distance and duration as variables.
                - Indented code and optimized it.
                - Ported some stuffs to struct
                - Added a new interface member OnChain.
                - Added GetUnitDist to check distance between units 
                  (idea by moyack but I took it as I think it is good for lazy people).
                - Now requires BoundSentinel
                
                                Changelog v1.10
                - Optimized create method stuffs.                
                
                
                
   A knockback system that is made to fulfill almost any knockbacking issues that you can have in a game. It can be customizable
   from a basic creation to a advanced creation.
                                                                                   
    requires                                                                      
        - A vJASS preprocessor (JassNewGenPack).            
        
        - The special effects found in the import/export section of this map.      
         They are the "Dust.mdx" for land, "SlideWater.mdx" for water and "DustAndRocks.mdx" for collision on cliffs.

        - GroupUtils, AutoIndex, IsDestructableLib, IsTerrainWalkable,BoundSentinel
    
    Pros
        - Can support many kind of stuffs you want in a knockback.
        - Leak free and efficient.
        - Made in vJASS.
        - Uses only a single timer and not many.
        - Knockback is realistic.
        - Allows you to knock a unit at a constant speed.
        - Knock a unit while flying.
        - Allows Chain knockbacks which is not found here.
        - You can use a constant knockback speed if you do not want any increment or decrement.
        - You can increase your knockback speed instead of decreasing for dashing spells mainly.
        - Allows interface usage.
        - very very configurable and is easy to use.
        - When a chained unit knocks another target, that target can have chaining properties too!
        
    Cons
        - requires 5 librarys
        - You can only use one UnitUserData indexing system and that is AutoIndex.
        - Might be hard to get used to the syntax.
                             
  Other Information                                                           
        - Angle is taken in Radians. But can easily be changed if you know how.    
        - Units will bounce back when they reach the map borders or just stay there if REFLECT is set to false.          
        - Remember to import the special effect into your map if you need them else it would look really plain.   
        - There are five functions that can be used in this system and more will be added soon if I have the time.               
                                                                                  
    Finding the Distances                                                             
        - This is some general knowledge on how to calculate the distance with a formula if you are using speed and decrement                                
        
        
                    The Formula to calculate the distance.
                    = -1 * V * V / (2 * A / Interval)
                
                 Variable V = The initial speed of the knocked unit which must be positive.
                 Variable A = The decrement of the speed per Interval which must be negative.
          Variable Interval = The timer period that function Update runs, known as TIME in the globals block.
          
          For example, we have initial speed value as 1000 and decrement as 15 and we subsitute them into the formula
          
                            -1 * 1000 * 1000 / (2 * -15 / 0.03) = 1000 distance (Use a calculator to assist you.)
                            
                Note that the default timer interval is 0.03 and try not to use periods that are too high else it will look choppy.
                            


                                                                                  
Implementation:                                                                
    - First off you need JNGP known as JassNewGenPack which is a modified world editor.  
    
    - Create a new trigger and then convert it to custom text and then you copy paste
    the code into the trigger.
    
    - Now you will either need to export the special effects from this map to  
    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 paths of the effect
    to the path in the system.     
    
    - It is not recommended if you do not know much about importing or exporting to change the paths
    else just use the paths in the system.

    - Save a map and hope for the best, if it fails report the bug to me.
    
                                                                                   
    NOTE: Please report any bugs to me at thehelper.net via PM or at the system thread. (Username: wraithseeker)
                                                                                 
    Usage                                                                        
        call Knock.create(source,t,angle,speed,decrement,distance,duration)        
                    
    Source      =   The unit who started the knockback.       
    Target      =   The unit that will be used in the knockback. 
    Angle       =   The angle that the unit will be knocked back.
    Speed       =   The initial speed of the unit.
    Decrement   =   The amount of speed reduced every interval.
    Distance    =   The amount of distance you want the unit to get knocked.
    Duration    =   The amount of time you want a unit to get knocked.
    
    
            Note that if you can only have one choice out of two choice that is
                                
                call Knock.create(source,t,angle,speed,decrement,0,0)
                    
                Use that method if you prefer using speed and decrement as the variables
                and remember to set distance and duration to 0 else you will have a error message.
                
                call Knock.create(source,t,angle,0,0,distance,duration)
                
                       
                Use that method if you prefer using distance and duration as the variables
                and remember to set speed and decrement to 0 else you will have a error message.
                    

            For example Atan2(cy-y,cx-x)
            
        X is the coordinate of unit Target
        Y is the coordinate of unit Target
        CX is the coordinate of unit source
        CY is the coordinate of unit source
            
    Decrement        =   The variable that will deduct from speed every interval.
    
    Increment        =   The Increment speed of the unit target per interval, usually you should set it to 0 or null
                            unless you have boolean increment set to true.
                        
    Tree             =   This boolean will decide whether you want the unit target to knock down trees or not
    
    Chain            =   if set to 0, it does nothing but if set to 1, it does normal chaining but if set to 2 it creates chaining
                            properties for units who has been chained by the unit target of the knockback.
                    
    Fly              =   This boolean will decide whether you want the unit target to fly while getting knocked back
                            and pathing will be disabled.
    LineDamage       =  The amount of damage dealed to units around the target unit who has got knocked back. 
                            A value of 0 and below means nothing will happen.
                            
    ToTarget         =  Whether you want the unit to get knocked to a unit
                            A default value means that it wont get knocked to a unit
                        
    Move             =  Whether you want a unit to be moved with SetUnitX or SetUnitY
    
    Mode             =  if set to 0, nothing will happen but if set to 1, unit target will get knocked at a constant speed
                            if set to 2, unit target will get knocked at a increasing speed.
                            
    SFX              =  The string of the SFX that you want to display else if it is not "".
    
    attachpoint      =  The point where you want to attach the effect, if you did not set it to a value,
                            the default would be "origin".
                            
    GRAVITY          = Default value is -981. Adjust it as you wish.
    
    z                = The higher the value, the higher the unit jumps.
    
    Example of Usage                                                            
                                                                                  
    call Knock.create(t,u,a,1000.00,15.00,0,0)                            
    call Knock.create(source,t,angle,800,20,0,0) 
                                                                                  
    The first one will cause the target unit of a spell to be knocked back 1000 distance away using the formula
    and it will have decrement speed of 15 per interval.
                                                                                   
    The second one will cause the target unit of a spell to be knocked back 480 distance away using the formula
    and it will have decrement speed of 20 per interval.         
    
    For the other Misc functions you can do it like this (Note that it must be used after call Knock.create(......)
    
        set d.Trees = true which means that it will kill trees upon impact with them.
        set d.Fly = true which means that unit target will fly.
        set .cos = Your angle values for the coordinate X.
        set .sin = Your angle values for the coordinate Y.
        and so on for other variables.
        
    Things to note for SFX
    
    When you do not set a SFX function, land SFX will not be displayed and you have to do a
    set thistype.SFX = "" for the land SFX to display.
        
interface usuage
        
    scope Tsmash initializer Init

private struct data extends Knock // extends our EKB struct

static method create takes nothing returns data
    local data d // initialize the struct
    local unit u = GetTriggerUnit() // caster
    local real x = GetUnitX(u) // X axis of caster
    local real y = GetUnitY(u) // Y axis of caster
    local location L = GetSpellTargetLoc() // location of target point
    local real lx = GetLocationX(L) // get dist between X axis
    local real ly = GetLocationY(L) // get distance between Y axis
    local real angle = Atan2(ly-y,lx-x) // find the angle
    set d = data.allocate(u,u,angle,0,0,1000,2.3) // create the struct using allocate which will be turned to .create.
    set d.fly = true // Fly!
    set d.Move = true // Moves the unit with SetUnitX & Y
    set d.ElevationA = GetRandomReal(30.,70.) // a random elevation angle for random height
    set u = null // null the unit after use
    return d // return struct that's right!
endmethod

method OnStop takes nothing returns nothing // we use the interface from our EKB struct which extends a interface
    local unit dummy  // initialize dummy
    local integer i = 0 // intialize i
    loop // loops through 5 instances and create a dummy unit with TSA 0.1 and cast thunder clap and then
            //apply a timed life duration of 1 second
        exitwhen i > 5
        call TriggerSleepAction(0.1)
        set dummy = CreateUnit(GetOwningPlayer(.target),'hfoo',GetUnitX(.target),GetUnitY(.target),0)
        call UnitAddAbility(dummy,'A002')
        call IssueImmediateOrder(dummy,"thunderclap")
        call UnitApplyTimedLife(dummy,'BTLF',1)
        set i = i + 1
    endloop
    set dummy = null // null dummy unit after use
endmethod
endstruct

globals
    private constant integer SPELL = 'A000' // our dummy spell
endglobals

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL // checks whether spell cast is equal to dummy spell
endfunction

private function Actions takes nothing returns nothing
    local data d = data.create() // create the struct!
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction(t, function Actions)
    call TriggerAddCondition(t,Condition(function Conditions))
endfunction

endscope

Always remember that you can always use other interface members but this provides only a interface example usuage of OnStop.

Other functions                                                           
                                                                                  
    call d.IsKnockedBack(Target)                                               
                                                                                   
    This function checks if the unit is currently sliding using this        
    system. It will return true if it is.                                        
                                                                                   
    call d.IsUnitSliding(Target)                                             
                                                                                   
    This function will stop the unit from sliding (using this system).      
    It also returns true if the unit is stopped.                                 
                          
    call GetUnitDist(your KB target, knock to target)
    
    This function will get distance between the two units if you call the function.
    
    These functions can be used in conjunction with each other, by checking if a 
      unit is sliding then stopping it if it is.
      
                    I hope you have a great day after reading this documention, enjoy EKB!


JASS:
library Knockback initializer Init requires DestructableLib, IsTerrainWalkable, GroupUtils, AutoIndex

globals
    private constant real    TIME         = 0.03                     // The timer interval.
    private constant real    CHAINRADIUS  = 160.00                   // The radius a unit can get chained when near the unit target.
    private constant real    SPEEDFACTOR  = 0.75                     // How much speed will be reduced when a unit chains another unit.
    private constant real    RADIUS       = 128.00                   // The radius to check tree.
    private constant real    HEIGHTLEVEL  = 200.00                   // The default height level that stops chaining or killing trees when in the air.
    private constant string  GROUND       = "MDX\\Dust.mdx"          // The effect for ground.
    private constant string  WATER        = "MDX\\SlideWater.mdx"    // The effect for water.
    private constant string  COLLISION    = "MDX\\DustAndRocks.mdx"  // The effect when at cliff
    private constant boolean REFLECT = false // whether you want the unit to get reflected back when they touch map boundaries
endglobals

globals
    private constant integer INVULNERABLE   = 'Avul'               
    private constant integer FLYID          = 'Amrf' 
    private rect     TreeRect               = null
    private boolexpr TreeCheck              = null
    private boolexpr ChainFilter            = null
    private boolexpr LineFilter             = null
    private real MapMaxX                    = 0
    private real MapMaxY                    = 0
    private real MapMinX                    = 0
    private real MapMinY                    = 0
endglobals

function GetUnitDist takes unit a, unit b returns real
    local real dx = GetUnitX(b) - GetUnitX(a)
    local real dy = GetUnitY(b) - GetUnitY(a)
        return SquareRoot(dx*dx+dy*dy)
endfunction

function GetUnitAngle takes unit a, unit b returns real
    local real tx = GetUnitX(b) - GetUnitX(a)
    local real ty = GetUnitY(b) - GetUnitY(a)
    return Atan2(ty,tx)
endfunction

function GetPointAngle takes real x, real y, real lx, real ly returns real
    local real dx = ly-y
    local real dy = lx-x
    return Atan2(dx,dy)
endfunction

function GetPointDist takes real x, real y, real lx, real ly returns real
    local real dx = ly-y
    local real dy = lx-x
    return SquareRoot(dx*dx+dy*dy)
endfunction

private interface face
    method OnPeriodic takes nothing returns nothing defaults nothing
    method OnStop takes nothing returns nothing defaults nothing
    method OnStopCondition takes nothing returns boolean defaults false
    method OnChain takes unit target returns nothing defaults nothing
    method OnChainCondition takes nothing returns boolean defaults false
    method OnLine takes unit target returns nothing defaults nothing 
endinterface
    

struct Knock extends face
    private static timer Timer    = CreateTimer()
    private static integer Count  = 0
    private static Knock array D
    private static Knock data
    private static integer array Entries
    private     integer      mode            = 0
    private     group        hit             = null
    private     group        linehit         = null
    private     real         VariableSpeed   = 0
    private     effect      effects          = null
    unit        source          = null
    unit        target          = null
    unit        dashtarget      = null
    real        cos             = 0
    real        sin             = 0 
    real        speed           = 0
    real        decrement       = 0
    real        Increment       = 0
    real        LineDamage      = 0
    real        z               = 0
    real        ElevationAngle  = 0.
    real        h               = 0.
    real        GRAVITY         = -981.
    real        vf              = 0.
    string      KSFX             = ""
    string      LineSFX         = ""
    string      attachpoint     = "origin"
    integer     chain           = 0
    integer     Mode            = 0
    boolean     fly             = false
    boolean     ToTarget        = false
    boolean     Move            = false
    boolean     trees           = false
    
static method KnockbackStop takes unit target returns boolean
    local Knock this
    local integer id = GetUnitId(target)
    local integer i = .Count - 1
    if .Entries[id] != 0 then
        if .Count > 0 then
            set .D<i>= .D[.Count]
        else 
            call PauseTimer(.Timer) 
        endif
        set .Entries[id] = .Entries[id] - 1
        call .destroy()
        return true
    endif
    return false
endmethod

static method IsKnockedBack takes unit target returns boolean
    local integer id = GetUnitId(target)
    return .Entries[id] != 0
endmethod
   
private static method CheckTrees takes nothing returns boolean
    return IsDestructableTree(GetFilterDestructable())
endmethod

private static method Trees takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endmethod

private static method ChainCheck takes nothing returns boolean
    local Knock this = .data
    return .target != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(.source)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false and GetWidgetLife(GetFilterUnit()) &gt; 0.405 and GetUnitAbilityLevel(GetFilterUnit(),INVULNERABLE) &lt;= 0
endmethod

private static method LineCheck takes nothing returns boolean
    local Knock this = .data
    return GetWidgetLife(GetFilterUnit()) &gt; 0.405 and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(.source)) and GetFilterUnit() != .source
endmethod

private method IsPointOutside takes real x, real y returns nothing
    local real tx = GetUnitX(.target) + 50 * .cos
    local real ty = GetUnitY(.target) + 50 * .sin
    if REFLECT and (x &gt; MapMaxX or y &gt; MapMaxY or x &lt; MapMinX or y &lt; MapMinY) and (tx &gt; MapMaxX or ty &gt; MapMaxY or tx &lt; MapMinX or ty &lt; MapMinY) then
        set .cos = -.cos
        set .sin = -.sin
    endif
endmethod
    
private method TerrainCheck takes nothing returns integer
    local real x = GetUnitX(.target)
    local real y = GetUnitY(.target)
    local real height = GetUnitFlyHeight(.target)
    if IsTerrainWalkable(x + 50.00 * .cos,y + 50.00 * .sin) == false then
        return 3
   elseif IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
        return 1
    elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
        return 2
    endif 
    return 0
endmethod

method operator ElevationA takes nothing returns real
    return .ElevationAngle
endmethod

method operator ElevationA= takes real a returns nothing
    local real speed = .speed/TIME
    set .ElevationAngle = a
    set .z = speed * Sin(.ElevationAngle*bj_DEGTORAD)
endmethod

method operator SFX takes nothing returns string
    return .KSFX
endmethod

method operator SFX= takes string s returns nothing
    set .KSFX = s
    if .KSFX == &quot;&quot; then
        if .mode == 1 then
            set .effects = AddSpecialEffectTarget(GROUND,.target,.attachpoint)
        elseif .mode == 2 then
            set .effects = AddSpecialEffectTarget(WATER,.target,.attachpoint)
        elseif .mode == 3 then
            set .effects = AddSpecialEffectTarget(COLLISION,.target,.attachpoint)
        endif
    elseif .KSFX != &quot;&quot; then
        call DestroyEffect(AddSpecialEffect(.KSFX,GetUnitX(.target),GetUnitY(.target)))
    endif
endmethod
            
static method create takes unit source, unit target, real angle, real speed, real decrement, real distance, real duration returns Knock
    local Knock d 
    local integer i = GetUnitId(target)
    local boolean b = false
    if speed &gt;= 0. and decrement &gt;= 0. and distance &lt;= 0. and duration &lt;= 0. then
        if target == null or source == null or speed &lt;= 0.00 or decrement &lt;= 0.00 then
            call BJDebugMsg(&quot;Invalid values for Knockback.!&quot;)
        endif
        set b = true
        set d = Knock.allocate()
        set d.speed         = speed * TIME
        set d.decrement     = decrement * TIME
    elseif target == null or source == null or distance &lt;= 0.00 or duration &lt;= 0.00 then
        call BJDebugMsg(&quot;Invalid values for Knockback.!&quot;)
    else
        set b = true
        set d = Knock.allocate()
        set duration = duration/TIME
        set d.decrement = 2.00*distance/(duration*duration)
        set d.speed = 2.00*distance/duration
    endif
    if b then
        set d.source        = source
        set d.target        = target
        set d.trees         = false
        set d.fly           = false
        set d.chain         = 0
        set d.ToTarget      = false
        set d.Move          = false
        set d.hit           = NewGroup()
        set d.linehit       = NewGroup()
        set d.sin           = Sin(angle)
        set d.cos           = Cos(angle)
        set d.LineDamage    = 0
        set d.Mode          = 0
        set d.VariableSpeed = 0
        set d.Increment     = 0
        set d.dashtarget    = null
        set d.VariableSpeed = d.speed
        set d.KSFX           = &quot;&quot;
        set d.LineSFX       = &quot;&quot;
        set d.attachpoint   = &quot;origin&quot;
        set d.mode = d.TerrainCheck()
        set d.GRAVITY = -981.
        set d.z = 0.
        set d.vf = 0.
        set d.h = 0.
        set .D[.Count] = d
        if .Count == 0 then
            call TimerStart(.Timer,TIME,true,function Knock.action)
        endif
        set .Count = .Count + 1
        set .Entries<i> = .Entries<i> + 1
    endif
    return d
endmethod
    
static method get takes unit u returns integer
    local integer id = GetUnitId(u)
    return .Entries[id]
endmethod

private method onDestroy takes nothing returns nothing
    call DestroyEffect(.effects)
    call ReleaseGroup(.hit)
    call ReleaseGroup(.linehit)
    call SetUnitFlyHeight(.target,GetUnitDefaultFlyHeight(.target),0)
    call SetUnitPathing(.target,true)
endmethod
        
private static method action takes nothing returns nothing
    local Knock d       = 0
    local Knock this    = 0
    local real    x     = 0.
    local real    y     = 0.
    local real    cx    = 0.
    local real    cy    = 0.
    local real    tx    = 0.
    local real    ty    = 0.
    local integer id    = 0
    local integer mode  = 0
    local real height   = 0.00
    local real angle    = 0.00
    local integer i     = 0
    local unit t        = null
    loop
        exitwhen i &gt;= .Count
        set this = .D<i>
        set id = GetUnitId(.target)
        set x = GetUnitX(.target)
        set y = GetUnitY(.target)
        set height = GetUnitFlyHeight(.target)
        set mode = .mode
        set .vf = .z + .GRAVITY * TIME
        set .h = .h + (.vf*.vf-.z*.z) / (2 * .GRAVITY)
        set .z = .vf
        if .OnPeriodic.exists then
            call .OnPeriodic.execute()
        endif
        
        if .ToTarget and not .OnStopCondition.exists then
            set cx = GetUnitX(.dashtarget)
            set cy = GetUnitY(.dashtarget)
            set angle = Atan2(cy-y,cx-x)
            set .sin = Sin(angle)
            set .cos = Cos(angle)
            if SquareRoot((cx - x) * (cx - x) + (cy - y) * (cy - y)) &lt;= 125 then
                if .OnStop.exists then
                    call .OnStop.execute()
                endif
                call .destroy()
                set .Count = .Count - 1
                set .Entries[id] = .Entries[id] - 1
                if .Count &gt; 0 then
                    set .D<i>= .D[.Count]
                    set i = i - 1 
                else 
                    call PauseTimer(.Timer) 
                endif
            endif
        
        elseif .OnStopCondition.exists and not .ToTarget then
            if .OnStopCondition() then
                if .OnStop.exists then
                    call .OnStop.execute()
                endif
                call .destroy()
                set .Count = .Count - 1
                set .Entries[id] = .Entries[id] - 1
                if .Count &gt; 0 then
                    set .D<i>= .D[.Count]
                    set i = i - 1 
                else 
                    call PauseTimer(.Timer) 
                endif
            endif
    
        elseif .speed &lt;= 0 and not .ToTarget and not .OnStopCondition.exists then
            if .OnStop.exists then
                call .OnStop.execute()
            endif
            call .destroy()
            set .Count = .Count - 1
            set .Entries[id] = .Entries[id] - 1
            if .Count &gt; 0 then
                set .D<i>= .D[.Count]
                set i = i - 1 
            else 
                call PauseTimer(.Timer) 
            endif
        else
        
        call .IsPointOutside(x,y)
        
        if height &gt;= HEIGHTLEVEL then
            set .chain = 0
            set .trees = false
        endif
    
        if .Mode == 0 then
            set x = x + .speed * .cos
            set y = y + .speed * .sin
        elseif .Mode == 1 then
            set x = x + .speed * .cos
            set y = y + .speed * .sin
        elseif .Mode == 2 then
            set x = x + .VariableSpeed *.cos
            set y = y + .VariableSpeed *.sin
            set .VariableSpeed = .VariableSpeed + .Increment
        endif
    
        if .Move then
            call SetUnitX(.target,x)
            call SetUnitY(.target,y)
        else
            call SetUnitPosition(.target,x,y)
        endif
     
        if .LineDamage &gt; 0 then
            set .data = this
            call GroupClear(ENUM_GROUP)
            call GroupEnumUnitsInRange(ENUM_GROUP,x,y,150,LineFilter)
            loop
                set t = FirstOfGroup(ENUM_GROUP)
                exitwhen t == null
                if not IsUnitInGroup(t,.linehit) then
                    if .OnLine.exists then
                        call .OnLine.execute(t)
                    endif
                    call UnitDamageTarget(.source,t,.LineDamage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null)
                    if .LineSFX != &quot;&quot; then
                        call DestroyEffect(AddSpecialEffect(.LineSFX,GetUnitX(t),GetUnitY(t)))
                    endif
                    call GroupAddUnit(.linehit,t)
                endif
                call GroupRemoveUnit(ENUM_GROUP,t)
            endloop
        endif
     
        if .trees then
            call SetRect(TreeRect,x-RADIUS,y-RADIUS,x+RADIUS,y+RADIUS)
            call EnumDestructablesInRect(TreeRect,TreeCheck,function Knock.Trees)
        endif
    
        set height = GetUnitFlyHeight(.target)   
        if .fly  then
            call UnitAddAbility(.target,FLYID)
            call UnitRemoveAbility(.target,FLYID)
            call SetUnitPathing(.target,false)
            call SetUnitFlyHeight(.target,.h,0)
        endif

        set height = GetUnitFlyHeight(.target)   
        if .KSFX == &quot;&quot; and height &gt;= GetUnitDefaultFlyHeight(.target) and height &lt;= GetUnitDefaultFlyHeight(.target) +1 then
            set .mode = .TerrainCheck()
            if .mode == 1 and (mode == 2 or mode == 3)then
                call DestroyEffect(.effects)
                set .effects = AddSpecialEffectTarget(GROUND,.target,.attachpoint)
            elseif .mode == 2 and (mode == 1 or mode == 3) then
                call DestroyEffect(.effects)
                set .effects = AddSpecialEffectTarget(WATER,.target,.attachpoint)
            elseif .mode == 3 and (mode == 1 or mode == 2) then
                call DestroyEffect(.effects)
                set .effects = AddSpecialEffectTarget(COLLISION,.target,.attachpoint)
            endif
        elseif .attachpoint != &quot;&quot; then
            call DestroyEffect(.effects)
            set .effects = AddSpecialEffectTarget(.SFX,.target,.attachpoint)
        else
            call DestroyEffect(AddSpecialEffect(.SFX,GetUnitX(.target),GetUnitY(.target)))
        endif

        if .chain == 1 or .chain == 2 then
            set .data = this
            call GroupClear(ENUM_GROUP)
            call GroupEnumUnitsInRange(ENUM_GROUP,x,y,CHAINRADIUS,ChainFilter)
            loop
                set t = FirstOfGroup(ENUM_GROUP)
                exitwhen t == null
                    if not IsUnitInGroup(t,.hit) then
                         if not .OnChainCondition.exists or .OnChainCondition() then
                            set cx = GetUnitX(t)
                            set cy = GetUnitY(t)
                            call GroupAddUnit(.hit,t)
                            set d = Knock.create(.source,t,Atan2(cy-y,cx-x),(.speed/TIME)*SPEEDFACTOR,(.decrement/TIME),0,0)
                            if .chain == 2 then
                                set d.chain = 2
                            endif
                            call GroupAddUnit(d.hit,.target)
                            if .OnChain.exists then
                                call .OnChain.execute(t)
                            endif
                            set .D[.Count] = d
                            set .Count = .Count + 1       
                        endif
                    endif
                call GroupRemoveUnit(ENUM_GROUP,t)
            endloop
        endif
        endif
        set .speed = .speed - .decrement
        set i = i + 1
    endloop
endmethod
    
static method onInit takes nothing returns nothing
    set TreeRect    = Rect(0.00,0.00,1.00,1.00)
    set TreeCheck   = Filter(function Knock.CheckTrees)
    set ChainFilter = Filter(function Knock.ChainCheck)
    set LineFilter  = Filter(function Knock.LineCheck)
endmethod
endstruct

private function Init takes nothing returns nothing
    set MapMaxX     = GetRectMaxX(bj_mapInitialPlayableArea)
    set MapMaxY     = GetRectMaxY(bj_mapInitialPlayableArea)
    set MapMinX     = GetRectMinX(bj_mapInitialPlayableArea)
    set MapMinY     = GetRectMinY(bj_mapInitialPlayableArea)     
endfunction

endlibrary</i></i></i></i></i></i></i>
 

Attachments

  • Extended Knockback System v1.10.w3x
    115 KB · Views: 202

Romek

Super Moderator
Reaction score
964
Couldn't you update the previous one instead of making a new thread?
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
I spammed poke and the chieftain lost pathability.

Edit: Nevermind, it's any time it's casted.
 

Kenny

Back for now.
Reaction score
202
I agree with Romek, you probably should have just updated your other EKB thread...

JASS:
private function ChainCheck takes nothing returns boolean
    local Knock d
    return d.target != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(d.source)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false and GetWidgetLife(GetFilterUnit()) &gt; 0.405 and GetUnitAbilityLevel(GetFilterUnit(),INVULNERABLE) &lt;= 0
endfunction

private function LineCheck takes nothing returns boolean
    local Knock d
    return GetWidgetLife(GetFilterUnit()) &gt; 0.405 and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(d.source)) and GetFilterUnit() != d.source
endfunction


Does that even work? You're not even initialising d... Also you dont need to check if GetFilterUnit() != .caster, if you are already checking if they are enemies.

You still need to make some of the struct members private so they can't be changed by child structs.

Also, are you sure using a group to move units works properly?

For example:

5 seconds of game time elapses - Begin a knockback that will last 10 seconds.
6 seconds of game time elapses - Begin a knockback on the same unit that will last 1 second.

By the 7th second of game time the second knockback instance has finished, and the unit will be removed from the knocked group.

But the first knockback instance still has 8 seconds left...

JASS:
if .LineDamage &gt; 0 then
        set Data = this
        call GroupEnumUnitsInRange(g,x,y,100,LineFilter)
        loop
            set t = FirstOfGroup(g)
            exitwhen t == null
            call UnitDamageTarget(.source,t,.LineDamage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null)
            call GroupRemoveUnit(g,t)
        endloop
    endif


I'm not sure your line damage will work properly, as a unit that has already been hit can still be hit again. You should fix that.

Also the radius for picking up units should be made a global constant at the very least.

Also, you are leaking a group every callback, just use ENUM_GROUP instead.
 

wraithseeker

Tired.
Reaction score
122
>Couldn't you update the previous one instead of making a new thread?

It has a different syntax, some might prefer the old one....

> I spammed poke and the chieftain lost pathability.
Edit: Nevermind, it's any time it's casted.

Since the tauren is flying, pathability should be gone.

>Kenny!

How do I do it without groups? I discussed it with you over at MSN remember about the weird syntax error and how mean the system was and not work for me using a static array timer loop.


Most of the fix have been done but I just didn't uploaded it as it is not final yet....
 

MagnaGuard

Active Member
Reaction score
49
All these knockback systems. I havn't seen one that does somthing I wish it could (add a parabola to have knockbacked units fly up as well ;))
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
He retained his unpathability even after it being casted on him. Same happened to the Dwarf.
 

wraithseeker

Tired.
Reaction score
122
Bump, update.
Added extra functionability like adding SFX and choosing attachment point and changed the way the system works.
 

BlackRose

Forum User
Reaction score
239
Nice! Is this a defence system or something :p, 15 minutes left. I see you used my 'partially' edited terrain lolz. Nice that you got some tooltips for your skills. I know it is a test map but, with Tauren Chieftain, both skills use T.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/
  • The Helper The Helper:
    I think we need to add something to the bottom of the front page that shows the Headline News forum that has a link to go to the News Forum Index so people can see there is more news. Do you guys see what I am saying, lets say you read all the articles on the front page and you get to the end and it just ends, no kind of link for MOAR!
  • The Helper The Helper:
    Happy Wednesday!
    +1
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    Sticking with the desserts for now the latest recipe is Fried Apple Pies - https://www.thehelper.net/threads/recipe-fried-apple-pies.194297/
  • The Helper The Helper:
    Finally finding about some of the bots that are flooding the users online - bytespider apparently is a huge offender here - ignores robots.txt and comes in from a ton of different IPs

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top