• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Chain knockback

Status
Not open for further replies.
Level 6
Joined
Nov 10, 2006
Messages
181
JASS:
 Chain knockback[/b]

[code=jass]//Formula to calculate Distance = -1 * V * V / (2 * A / Interval)  Where variable V is speed and A is decrement.//
//                            Note! V must be positive and A must be negative!!!!                             //
library Knockback initializer Init requires BoundSentinel, DestructableLib, IsTerrainWalkable
globals
    private constant real TIME = 0.03
    private constant real CHAINRADIUS = 100
    private constant real SPEEDFACTOR = 0.75
    private constant string GROUND = "MDX\\Dust.mdx"
    private constant string WATER = "MDX\\SlideWater.mdx"
    private constant string COLLISION = "MDX\\DustAndRocks.mdx"
    private constant string ATTACHPOINT = "origin"
    private constant integer Invulnerable = 'Avul'
    private constant real RADIUS = 180
    private timer Timer = CreateTimer()
    private group chain = CreateGroup()
    private integer Count = 0
    private integer array Knocker
    private rect TreeRect
    private boolexpr TreeCheck
    private boolexpr ChainFilter
    private unit source
    private unit target
endglobals

private function CheckTrees takes nothing returns boolean
    if IsDestructableTree(GetFilterDestructable()) then
        return true
    endif
    return false
endfunction

private function Trees takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

private function ChainCheck takes nothing returns boolean
return true
//return target != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(source)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), Invulnerable) <= 0
endfunction

private struct Knock
    unit source
    unit target
    integer EffectMode
    group hit
    real x
    real y
    real cos
    real sin
    real speed
    real decrement
    effect effects
    boolean Terrain
    boolean Trees
    boolean AllowMove
    boolean constantspeed
    boolean chain
    
    
    
public method TerrainCheck takes Knock d returns integer
        local real x = GetUnitX(d.target)
        local real y = GetUnitY(d.target)
        if IsTerrainWalkable(x + 50.00 * d.cos,y + 50.00 * d.sin) == false then
            return 3
        else
            if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
                return 1
            elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
                return 2
            endif
        endif 
        return 0
        endmethod
        
static method create takes unit source, unit target, real angle,real speed, real decrement, boolean KillTree, boolean Allowmove, boolean ConstantSpeed,boolean Chain returns Knock
    local Knock d = Knock.allocate()
    local real x
    local real y
    set d.source = source
    set d.target = target
    set d.Trees = KillTree
    set d.constantspeed = ConstantSpeed
    set d.AllowMove = Allowmove
    set d.chain = Chain
    if d.chain == true then
        set d.hit = CreateGroup()
    endif
    set x = GetUnitX(d.target)
    set y = GetUnitY(d.target)
    set d.speed = speed * TIME
    set d.decrement = decrement * TIME
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.EffectMode = d.TerrainCheck(d)
    if chain == null then
    set chain = CreateGroup()
    endif
    if d.EffectMode == 1 then
        set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
    endif
    if d.EffectMode == 2 then
        set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
    endif
    if d.EffectMode == 3 then
     set d.effects = AddSpecialEffectTarget(COLLISION,d.target,ATTACHPOINT)
    endif
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
    call DestroyEffect(this.effects)
    endmethod
        endstruct
        
private function Update takes nothing returns nothing
    local Knock d = 0
    local Knock c = 0
    local unit u
    local unit t
    local real sx
    local real sy 
    local real x 
    local real y 
    local real cx
    local real cy
    local integer i = Count - 1
    local integer mode = 0
    loop
        exitwhen i < 0
        set d = Knocker[i]
        set u = d.target
        set sx = GetUnitX(u)
        set sy = GetUnitY(u)
        set x = sx + d.speed * d.cos
        set y = sy + d.speed * d.sin
        if d.speed <= 0 then
            call d.destroy()
            set Count = Count - 1
            if Count < 0 then
                call PauseTimer(Timer)
                set Count = 0
            else
                set Knocker[i] = Knocker[Count]
            endif
        endif
        set mode = d.EffectMode
            if d.AllowMove == true then
                call SetUnitX(d.target,x)
                call SetUnitY(d.target,y)
            else
                call SetUnitPosition(u,x,y)
            endif
            if d.Trees == true then
                call SetRect(TreeRect,sx-RADIUS,sy-RADIUS,sx+RADIUS,sy+RADIUS)
                call EnumDestructablesInRect(TreeRect,TreeCheck,function Trees)
            endif
            set d.EffectMode = d.TerrainCheck(d)
            if d.EffectMode == 1 and (mode == 2 or mode == 3) then
                call DestroyEffect(d.effects)
                set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
            elseif d.EffectMode == 2 and (mode == 1 or mode == 3) then
                call DestroyEffect(d.effects)
                set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
            elseif d.EffectMode == 3 and (mode == 1 or mode == 2) then
                call DestroyEffect(d.effects)
                set d.effects = AddSpecialEffectTarget(COLLISION,d.target,ATTACHPOINT)
            endif
            if d.chain == true then
            call BJDebugMsg("Working!")
                set sx = GetUnitX(u)
                set sy = GetUnitY(u)
                set source = d.source
                set target = d.target
                call GroupEnumUnitsInRange(chain,sx,sy,CHAINRADIUS,ChainFilter)
                loop
                set t = FirstOfGroup(chain)
                exitwhen t == null
                    if not IsUnitInGroup(t,d.hit) then
                        set cx = GetUnitX(t)
                        set cy = GetUnitY(t)
                        call GroupAddUnit(d.hit,t)
                        set c = Knock.create(d.source,t, Atan2(cy-sy,cx-sx), d.speed*SPEEDFACTOR, d.decrement, d.Trees,d.AllowMove,d.constantspeed,d.chain)
                        call GroupAddUnit(c.hit, u)
                        set Knocker[Count] = c
                        set Count = Count + 1
                    endif
                     call GroupRemoveUnit(chain, t)
                endloop
                endif
            if d.constantspeed == true then
            else
                set d.speed = d.speed - d.decrement
            endif
            set i = i - 1
        endloop
    set u = null
endfunction

function KnockbackStop takes unit target returns boolean
    local integer i = Count - 1
    local Knock d = 0
    local boolean b = false
    loop
        exitwhen i < 0
        set d = Knocker[i]
        if d.target == target then
            call d.destroy()
            set Count = Count - 1
            if Count < 0 then
                call PauseTimer(Timer)
                set Count = 0
            else
                set Knocker[i] = Knocker[Count]
            endif
            set b = true
        endif
        set i = i - 1
    endloop
    return b
endfunction

function IsKnockedBack takes unit target returns boolean
    local integer i = Count - 1
    local Knock d = 0
    loop
        exitwhen i < 0
        set d = Knocker[i]
        if d.target == target then
            return true
        endif
        set i = i - 1
    endloop
    return false
endfunction

function KnockbackTarget takes unit source, unit target, real angle, real speed, real decrement,boolean KillTree, boolean AllowMove, boolean ConstantSpeed,boolean Chain returns boolean
    local Knock d = 0
    if target == null or source == null or speed == null or decrement == null then
        call BJDebugMsg("Invalid Values!")
        return false
        endif
    set d = Knock.create(source,target,angle,speed,decrement,KillTree,AllowMove,ConstantSpeed,Chain)
    if Count == 0 then
    call TimerStart(Timer,TIME,true,function Update)
    endif
    set Knocker[Count] = d
    set Count = Count + 1
    return true
endfunction


private function Init takes nothing returns nothing
    set TreeRect = Rect(0,0,1,1)
    set TreeCheck = Filter(function CheckTrees)
    set ChainFilter = Filter(function ChainCheck)
endfunction
endlibrary

JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'TEST'
endfunction

private function Actions takes nothing returns nothing
    local unit u = GetSpellTargetUnit()
    local unit t = GetTriggerUnit()
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local real tx = GetUnitX(t)
    local real ty = GetUnitY(t)
    local real a = Atan2(y-ty,x-tx)
    call KnockbackTarget(t,u,a,1000,15,true,false,false,true)
    set u = null
    set t = null
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

I included the Test spell in there too , it is a single target unit spell.

EDIT : Alright, made it have no conditions and guess what happened when a unit comes near? The effects gets created and never destroyed and the unit gets stuck at the location forever and cannot hit people. After 3 unit got this bug , the warcraft hanged so I used windows task manager, I assumed that there was a infinite loop.
 
Last edited:
Level 6
Joined
Nov 10, 2006
Messages
181
Updated code

JASS:
//Formula to calculate Distance = -1 * V * V / (2 * A / Interval)  Where variable V is speed and A is decrement.//
//                            Note! V must be positive and A must be negative!!!!                             //
library Knockback initializer Init requires BoundSentinel, DestructableLib, IsTerrainWalkable
globals
    private constant real TIME = 0.03
    private constant real CHAINRADIUS = 100
    private constant real SPEEDFACTOR = 0.50
    private constant string GROUND = "MDX\\Dust.mdx"
    private constant string WATER = "MDX\\SlideWater.mdx"
    private constant string COLLISION = "MDX\\DustAndRocks.mdx"
    private constant string ATTACHPOINT = "origin"
    private constant integer Invulnerable = 'Avul'
    private constant real RADIUS = 180
    private timer Timer = CreateTimer()
    private integer Count = 0
    private integer array Knocker
    private rect TreeRect
    private boolexpr TreeCheck
    private boolexpr ChainFilter
    private unit source
    private unit target
endglobals

private function CheckTrees takes nothing returns boolean
    if IsDestructableTree(GetFilterDestructable()) then
        return true
    endif
    return false
endfunction

private function Trees takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction

private function ChainCheck takes nothing returns boolean
return target != GetFilterUnit()
endfunction

private struct Knock
    unit source
    unit target
    integer EffectMode
    group hit
    real x
    real y
    real cos
    real sin
    real speed
    real decrement
    effect effects
    boolean Terrain
    boolean Trees
    boolean AllowMove
    boolean constantspeed
    boolean chain
    
    
    
public method TerrainCheck takes Knock d returns integer
        local real x = GetUnitX(d.target)
        local real y = GetUnitY(d.target)
        if IsTerrainWalkable(x + 50.00 * d.cos,y + 50.00 * d.sin) == false then
            return 3
        else
            if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
                return 1
            elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
                return 2
            endif
        endif 
        return 0
        endmethod
        
static method create takes unit source, unit target, real angle,real speed, real decrement, boolean KillTree, boolean Allowmove, boolean ConstantSpeed,boolean Chain returns Knock
    local Knock d = Knock.allocate()
    local real x
    local real y
    set d.source = source
    set d.target = target
    set d.Trees = KillTree
    set d.constantspeed = ConstantSpeed
    set d.AllowMove = Allowmove
    set d.chain = Chain
    set d.hit = NewGroup()
    set x = GetUnitX(d.target)
    set y = GetUnitY(d.target)
    set d.speed = speed * TIME
    set d.decrement = decrement * TIME
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.EffectMode = d.TerrainCheck(d)
    if d.EffectMode == 1 then
        set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
    endif
    if d.EffectMode == 2 then
        set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
    endif
    if d.EffectMode == 3 then
     set d.effects = AddSpecialEffectTarget(COLLISION,d.target,ATTACHPOINT)
    endif
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
    call DestroyEffect(this.effects)
    call ReleaseGroup(this.hit)
    endmethod
        endstruct
        
private function Update takes nothing returns nothing
    local Knock d = 0
    local Knock c = 0
    local unit u
    local unit t
    local real sx
    local real sy 
    local real x 
    local real y 
    local real cx
    local real cy
    local integer i = Count - 1
    local integer mode = 0
    loop
        exitwhen i < 0
        set d = Knocker[i]
        set u = d.target
        set sx = GetUnitX(u)
        set sy = GetUnitY(u)
        set x = sx + d.speed * d.cos
        set y = sy + d.speed * d.sin
        if d.speed <= 0 then
            call d.destroy()
            set Count = Count - 1
            if Count < 0 then
                call PauseTimer(Timer)
                set Count = 0
            else
                set Knocker[i] = Knocker[Count]
            endif
        endif
        set mode = d.EffectMode
            if d.AllowMove == true then
                call SetUnitX(d.target,x)
                call SetUnitY(d.target,y)
            else
                call SetUnitPosition(u,x,y)
            endif
            if d.Trees == true then
                call SetRect(TreeRect,sx-RADIUS,sy-RADIUS,sx+RADIUS,sy+RADIUS)
                call EnumDestructablesInRect(TreeRect,TreeCheck,function Trees)
            endif
            set d.EffectMode = d.TerrainCheck(d)
            if d.EffectMode == 1 and (mode == 2 or mode == 3) then
                call DestroyEffect(d.effects)
                set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
            elseif d.EffectMode == 2 and (mode == 1 or mode == 3) then
                call DestroyEffect(d.effects)
                set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
            elseif d.EffectMode == 3 and (mode == 1 or mode == 2) then
                call DestroyEffect(d.effects)
                set d.effects = AddSpecialEffectTarget(COLLISION,d.target,ATTACHPOINT)
            endif
            if d.chain == true then
                set sx = GetUnitX(u)
                set sy = GetUnitY(u)
                set source = d.source
                set target = d.target
                call GroupEnumUnitsInRange(ENUM_GROUP,sx,sy,CHAINRADIUS,ChainFilter)
                loop
                set t = FirstOfGroup(ENUM_GROUP)
                exitwhen t == null
                    if not IsUnitInGroup(t,d.hit) then
                        set cx = GetUnitX(t)
                        set cy = GetUnitY(t)
                        call GroupAddUnit(d.hit,t)
                        set c = Knock.create(d.source,t, Atan2(cy-sy,cx-sx), d.speed*SPEEDFACTOR, d.decrement, d.Trees,d.AllowMove,d.constantspeed,d.chain)
                        call GroupAddUnit(c.hit, u)
                        set Knocker[Count] = c
                        set Count = Count + 1
                        call BJDebugMsg("working now")
                    endif
                     call GroupRemoveUnit(ENUM_GROUP, t)
                endloop
                endif
            if d.constantspeed == true then
            else
                set d.speed = d.speed - d.decrement
            endif
            set i = i - 1
        endloop
    set u = null
    set t = null
endfunction

function KnockbackStop takes unit target returns boolean
    local integer i = Count - 1
    local Knock d = 0
    local boolean b = false
    loop
        exitwhen i < 0
        set d = Knocker[i]
        if d.target == target then
            call d.destroy()
            set Count = Count - 1
            if Count < 0 then
                call PauseTimer(Timer)
                set Count = 0
            else
                set Knocker[i] = Knocker[Count]
            endif
            set b = true
        endif
        set i = i - 1
    endloop
    return b
endfunction

function IsKnockedBack takes unit target returns boolean
    local integer i = Count - 1
    local Knock d = 0
    loop
        exitwhen i < 0
        set d = Knocker[i]
        if d.target == target then
            return true
        endif
        set i = i - 1
    endloop
    return false
endfunction

function KnockbackTarget takes unit source, unit target, real angle, real speed, real decrement,boolean KillTree, boolean AllowMove, boolean ConstantSpeed,boolean Chain returns boolean
    local Knock d = 0
    if target == null or source == null or speed == null or decrement == null then
        call BJDebugMsg("Invalid Values!")
        return false
        endif
    set d = Knock.create(source,target,angle,speed,decrement,KillTree,AllowMove,ConstantSpeed,Chain)
    if Count == 0 then
    call TimerStart(Timer,TIME,true,function Update)
    endif
    set Knocker[Count] = d
    set Count = Count + 1
    return true
endfunction


private function Init takes nothing returns nothing
    set TreeRect = Rect(0,0,1,1)
    set TreeCheck = Filter(function CheckTrees)
    set ChainFilter = Filter(function ChainCheck)
endfunction
endlibrary

Some updates and sometimes warcraft hang because of some effect or knocks , made sure that the target wouldn't get grouped again but it still doesn't get knocked back.
 
Status
Not open for further replies.
Top