Knockback system

wraithseeker

Tired.
Reaction score
122
JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private integer Count = 0
    private integer array Knocker
endglobals

private struct Knock
    unit source
    unit target
    real x
    real y
    group g
    real cos
    real sin
    real distance
    real speed
    real decrement
    timer Timer
    effect mode

static method create takes unit source, unit target, real angle, real initialspeed, real decrement, real distance returns Knock
    local Knock d = Knock.allocate()
    set d.source = source
    set d.target = target
    set d.speed = initialspeed
    set d.decrement = decrement
    set d.distance = distance
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    return d
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u
    local integer i = Count - 1
    local real sx
    local real sy
    local real tx
    local real ty
    loop
        exitwhen i > Count
        set u = d.target
        set sx = GetUnitX(u)
        set sy = GetUnitY(u)
    if d.speed <= 0 then
        call d.destroy()
        set Count = Count - 1
    if Count > 0 then
        call ReleaseTimer(d.Timer)
        set Count = 0
    else
        set Knocker<i> = Knocker[Count]
    endif
    else
        set tx = sx * d.speed * d.cos
        set ty = sy * d.speed * d.cos
        call SetUnitPosition(u,tx,ty)
        set d.speed = d.speed - d.decrement
        set i = i - 1
        endif
    endloop
    set u = null
endfunction

public function KnockTarget takes unit source, unit target , real angle, real initialspeed, real decrement , real distance returns boolean
    local data d = 0
    if target == null or source == null or initialspeed == null or distance == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle*0.01745328,initialspeed,decrement,distance)
    set Count = Count + 1
    if Count == 1 then
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,0.03,true,function Update)
    endif
    return true
endfunction


private function Init takes nothing returns nothing
endfunction
endlibrary</i>


Right now , it says in KnockTarget , d is not a type that allows synax.

I am sure that you will notice many errors in this code. This is my first system and I hope I can finish this without my brain turning into flames.
 

wraithseeker

Tired.
Reaction score
122
JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private integer Count = 0
    private integer array Knocker
endglobals

private struct Knock
    unit source
    unit target
    real x
    real y
    group g
    real cos
    real sin
    real distance
    real speed
    real decrement
    timer Timer
    effect mode

static method create takes unit source, unit target, real angle, real initialspeed, real decrement, real distance returns Knock
    local Knock d = Knock.allocate()
    set d.source = source
    set d.target = target
    set d.speed = initialspeed
    set d.decrement = decrement
    set d.distance = distance
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u
    local integer i = Count - 1
    local real sx
    local real sy
    local real tx
    local real ty
    loop
        exitwhen i &gt; Count
        set u = d.target
        set sx = GetUnitX(u)
        set sy = GetUnitY(u)
    if d.speed &lt;= 0 then
        call d.destroy()
        set Count = Count - 1
    if Count &lt; 0 then
        call ReleaseTimer(d.Timer)
        set Count = 0
    else
        set Knocker<i> = Knocker[Count]
    endif
    else
        set tx = sx + d.speed * d.cos
        set ty = sy + d.speed * d.cos
        call SetUnitPosition(u,tx,ty)
        set d.speed = d.speed - d.decrement
        set i = i - 1
        endif
    endloop
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real initialspeed, real decrement , real distance returns boolean
    local Knock d = 0
    if target == null or source == null or initialspeed == null or distance == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle*0.01745328,initialspeed,decrement,distance)
    set Count = Count + 1
    if Count == 1 then
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,0.03,true,function Update)
    endif
    return true
endfunction


private function Init takes nothing returns nothing
endfunction
endlibrary</i>

JASS:
scope KnockTest initializer Init
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    call KnockbackTarget(t,u,Atan2(x-tx,y-ty),1000,20,800)
    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 )
endfunction

endscope

Code updated , right now I have no synax error , but when I tried to call it in the example above , it will not knock the target and instead , the target immediately went to the top right hand corner of the map.
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
function KnockbackTarget is a public function (public function KnockbackTarget) that's in library Knockback and the function where you call it from is out of the library, so you must use Knockback_KnockbackTarget instead of KnockbackTarget (I can only explain that much, you should read JassHelper's Readme (or Manual ) for more information )
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
I see that you move the target with very high speed. 1000. 980, 960,... equal to the "real" speed of (1/0.03 = 33.33333, just ignore the decimal -> 33) 33000, 32340, 31680,... -> no wonder it hits the corner of the map.
 

wraithseeker

Tired.
Reaction score
122
So how do I fix it?

Code update , I want help!

JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private integer Count = 0
    private integer array Knocker
endglobals

private struct Knock
    unit source
    unit target
    real x
    real y
    group g
    real cos
    real sin
    real distance
    real speed
    real decrement
    timer Timer
    effect mode

static method create takes unit source, unit target, real angle, real initialspeed, real decrement, real distance returns Knock
    local Knock d = Knock.allocate()
    set d.source = source
    set d.target = target
    set d.speed = initialspeed
    set d.decrement = decrement
    set d.distance = distance
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u
    local integer i = Count - 1
    local real sx
    local real sy
    local real x
    local real y
    local real tx
    local real ty
    loop
        exitwhen i &gt; Count
        set u = d.target
        set sx = GetUnitX(u)
        set sy = GetUnitY(u)
    if d.speed &lt;= 0 then
        call d.destroy()
        set Count = Count - 1
    if Count &lt; 0 then
        call ReleaseTimer(d.Timer)
        set Count = 0
    else
        set Knocker<i> = Knocker[Count]
    endif
    else
        set x = sx + d.speed * d.cos
        set y = sy + d.speed * d.sin
        call SetUnitPosition(u,x,y)
        set d.speed = d.speed - d.decrement
        set i = i - 1
        endif
    endloop
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real initialspeed, real decrement , real distance returns boolean
    local Knock d = 0
    if target == null or source == null or initialspeed == null or distance == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle*0.01745328,initialspeed,decrement,distance)
    set Count = Count + 1
    if Count == 1 then
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,0.03,true,function Update)
    endif
    return true
endfunction


private function Init takes nothing returns nothing
endfunction
endlibrary</i>

JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;AHtb&#039;
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    call KnockbackTarget(t,u,Atan2(x-tx,y-ty),600,20,800)
    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
 

Flare

Stops copies me!
Reaction score
662
1) In your create method, multiply initialspeed (and decrease) by your timer interval.

2) Your distance parameter is a bit useless, I can't see it being used anywhere, and it can't really fit in with your setup e.g.
Code:
50 speed
10 dec
500 distance

<values are Current Speed (total distance travelled)>

-> 50 (50)
-> 40 (90)
-> 30 (120)
-> 20 (130)
-> 10 (140)
-> 0 (140)
Actual distance travelled is nowhere near the given parameter, and it never will be unless you do the math and figure out what values will allow actual distance to coincide with specified distance.

3) In your example, your angle is incorrect I think. Atan2 (Bx - Ax, By - Ay) returns the angle from A to B - currently, your target would probably slide straight through/past the caster (if it wasn't for point #4)

4) You should give an indication that the function takes the angle in degrees (since you're doing bj_DEGTORAD inside the starting function). Again, your test suffers here - you give the angle in radians, then you try and convert from degrees to radians, so your angle value is messed up horribly :|
 

wraithseeker

Tired.
Reaction score
122
Did the changes , now it moves but it moves randomly and sometimes it doesn't even bother to move.

JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;AHtb&#039;
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    call KnockbackTarget(t,u,Atan2(x-tx,y-ty),600,20,800)
    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

JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private integer Count = 0
    private integer array Knocker
    private real TIME = 0.04
endglobals

private struct Knock
    unit source
    unit target
    real x
    real y
    group g
    real cos
    real sin
    real distance
    real speed
    real decrement
    timer Timer
    effect mode

static method create takes unit source, unit target, real angle, real initialspeed, real decrement, real distance returns Knock
    local Knock d = Knock.allocate()
    set d.source = source
    set d.target = target
    set d.speed = initialspeed * TIME
    set d.decrement = decrement * TIME
    set d.distance = distance
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u
    local integer i = Count - 1
    local real sx
    local real sy
    local real x
    local real y
    local real tx
    local real ty
    loop
        exitwhen i &gt; Count
        set u = d.target
        set sx = GetUnitX(u)
        set sy = GetUnitY(u)
    if d.speed &lt;= 0 then
        call d.destroy()
        set Count = Count - 1
    if Count &lt; 0 then
        call ReleaseTimer(d.Timer)
        set Count = 0
    else
        set Knocker<i> = Knocker[Count]
    endif
    else
        set x = sx + d.speed * d.cos
        set y = sy + d.speed * d.sin
        call SetUnitPosition(u,x,y)
        set d.speed = d.speed - d.decrement
        set i = i - 1
        endif
    endloop
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real initialspeed, real decrement , real distance returns boolean
    local Knock d = 0
    if target == null or source == null or initialspeed == null or distance == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle*0.01745328,initialspeed,decrement,distance)
    set Count = Count + 1
    if Count == 1 then
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,TIME,true,function Update)
    endif
    return true
endfunction


private function Init takes nothing returns nothing
endfunction
endlibrary</i>


Well it still doesn't work...
 

Flare

Stops copies me!
Reaction score
662
Did the changes
Really?
Code:
call KnockbackTarget(t,u,[B]Atan2(x-tx,y-ty)[/B],600,20,800)
Code:
set d = Knock.create(source,target,angle[B]*0.01745328[/B],initialspeed,decrement,distance)
1) Giving the parameter in radians, then internally converting it from degrees to radians - see point 4 in previous post

2) Wrong order of subtraction for the Atan2 parameters - it should be tx-x and ty-y. As it is, you're getting the angle from the target, to the caster. It should be the other way around (else, you're not knocking back, you're pulling towards :p) - see point 3 in previous post

As regards not moving at all, I can't really see why...


Also, that Count variable is pointless since you are using a separate timer per instance
 

wraithseeker

Tired.
Reaction score
122
Code updated , which you could probaby say ... revamped :p

For the angle part , I am sure I am correct , fixed.

Right now , the knockbacking works fine but angle is screwed up.

JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private real TIME = 0.04
endglobals

private struct Knock
    unit source
    unit target
    real x
    real y
    real cos
    real sin
    real speed
    real decrement
    timer Timer
    effect mode

static method create takes unit source, unit target, real angle, real initialspeed, real decrement returns Knock
    local Knock d = Knock.allocate()
    set d.source = source
    set d.target = target
    set d.speed = initialspeed * TIME
    set d.decrement = decrement * TIME
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
call ReleaseTimer(.Timer)
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u = d.target
    local real sx = GetUnitX(u)
    local real sy = GetUnitY(u)
    local real x = sx + d.speed * d.cos
    local real y = sy + d.speed * d.sin
    local real tx
    local real ty
    if d.speed &lt;= 0 then
        call d.destroy()
    endif
        set x = sx + d.speed * d.cos
        set y = sy + d.speed * d.sin
        call SetUnitPosition(u,x,y)
        set d.speed = d.speed - d.decrement
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real initialspeed, real decrement returns boolean
    local Knock d = 0
    if target == null or source == null or initialspeed == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle,initialspeed,decrement)
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,TIME,true,function Update)
    return true
endfunction


private function Init takes nothing returns nothing
endfunction
endlibrary


JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;AHtb&#039;
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    call KnockbackTarget(t,u,Atan2(y-ty,x-tx),1000,20)
    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
 

Flare

Stops copies me!
Reaction score
662
For the angle part , I am sure I am correct , fixed.
Have some enlightenment.

180px-Slope_picture.svg.png

Let's take that as an example, red line being the knockback path, going from bottom-left to top-right

d34ecb7a8fd0c49baf75fbe43c1b047c.png

The formula to calculate slope - for Atan2, it'd be
JASS:
... = Atan2 (deltaY, deltaX)


9d9e1aa5fab25d8dd659c0fb6e83234b.png

And if we substitute some variables for delta X/Y - x1/y1 are the starting coordinates, x2/y2 are the ending coordinates. Presumably, your knockback is supposed to push the unit towards (Angle between Caster and Target) i.e.
attachment.php

(Red line being the direction in which the target will slide)

If we take an example from another spell
JASS:
//
    local unit c = GetTriggerUnit ()
    local location l = GetSpellTargetLoc ()
    local real x = GetUnitX (c)
    local real y = GetUnitY (c)
    local real tx = GetLocationX (l)
    local real ty = GetLocationY (l)
    local real dx = tx - x
    local real dy = ty - y
    local real angle = Atan2 (dy, dx)
//Alternatively, local real angle = Atan2 (ty - y, tx - x)


Just try change 'x - tx' to 'tx - x' (and the same for 'y - ty') and see what happens. If it doesn't work, I will be very suprised

but angle is screwed up.
Screwed up in what way? Is it going at a completely random angle, or just the reverse of what it should be?
 

Attachments

  • Drawing.jpg
    Drawing.jpg
    13.7 KB · Views: 230

wraithseeker

Tired.
Reaction score
122
A completely random angle, you're right.

Used your advice here:

JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;AHtb&#039;
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    call KnockbackTarget(t,u,Atan2(ty-y,tx-x),1000,20)
    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


Randomness , Randomness , Random angle , tried it out 3 times.
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
@wraithseeker, sorry, I have class until now. :)

I really didn't know about the "speed decrement".
You should check out my Knockback.
I use "Sin" from 0 to 90 (degree)

my knockback func takes unit u,real dur, real dist,real ang, string sfx, integer sfxskip

I use 0.04s timer so there is (dur / 0.04) ticks.
"Sin" from 0 to 90 so each tick, the "Sin-Angle" (let just call it "sin" will increase by ( 90 / tick )
the distance knockback will be the offset from the position before knockback with distance of dist * SinBJ( sin * tick ) towards angle of knockback
But if this unit is currently "moving" ( I mean other slide/knockback ) -> so offset from the position before knockback will cause some bugs.
So I will take the offset from current position with distance of dist * SinBJ( sin * tick ) - dist * SinBJ( sin * (tick-1) ) ( current tick's dist minus previous tick's dist)

My explanation is confusing, right ? I'm not good at explaining things :nuts:
Just have a look at the code. ( I use CSSafety and CSData)

JASS:
scope Knockback

private struct data
    unit u
    real x
    real y
    real mx
    real my
    
    integer tick = 0
    integer tickmax
    real sin
    
    real ang
    real dist

    string sfx = &quot;Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl&quot;
    integer skip = 0
    integer skipmax = 1
    
    timer t
endstruct

private function KnockE takes nothing returns boolean
    local data d = GetCSData(GetExpiredTimer())
    local real dis
    local real x
    local real y
    if d.tick &lt;= d.tickmax then
    
        if d.skip == 0 then
            call DestroyEffect( AddSpecialEffect( d.sfx ,d.mx,d.my) )
            set d.skip = d.skipmax
        endif
        if d.skip &gt; 0 then
            set d.skip = d.skip - 1
        endif
        
        set d.tick = d.tick + 1
    
        set x = GetUnitX(d.u)
        set y = GetUnitY(d.u)
        set dis = d.dist * SinBJ( d.sin * d.tick )-d.dist * SinBJ( d.sin * (d.tick-1) )
        set d.mx = x + dis * Cos( d.ang* bj_DEGTORAD )
        set d.my = y + dis * Sin( d.ang* bj_DEGTORAD )
        call DestroyTreesInCircle( d.mx, d.my,150.00 )
        call SetUnitPosition(d.u, d.mx, d.my )

    endif
    
    if d.tick &gt; d.tickmax then
        call ReleaseTimer(d.t)
        call d.destroy()
        return true
    endif
    return false
endfunction

public function Knock takes unit u,real dur, real dist,real ang, string sfx, integer sfxskip returns nothing
    local data d = data.create()
    set d.u = u
    set d.x = GetUnitX(u)
    set d.y = GetUnitY(u)
    set d.mx = d.x
    set d.my = d.y
    set d.tickmax = R2I(dur / 0.04)
    set d.sin = 90. / d.tickmax
    set d.dist = dist
    set d.ang = ang
    if sfx != &quot;&quot; then
        set d.sfx = sfx
    endif
    set d.skipmax = sfxskip
    if sfxskip == 0 then
        set d.skipmax = 1
    endif
    set d.skip = d.skipmax
    set d.t = NewTimer()
    call SetCSData(d.t,d)
    call TimerStart(d.t, 0.04 ,true,function KnockE)
endfunction

endscope


With the above Knockback, unit u will be "moved" a distance of dist over dur seconds towards angle ang and the speed is fast -> slow

JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;AHtb&#039;
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    call Knockback_Knock(t,1.,1000.,,Atan2(ty-y,tx-x) * bj_RADTODEG ,&quot;&quot;,1) 
    //knock 1000 distance over 1 second towards.... angle )
    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


P/S: I use degree, I really can't use radian
 

wraithseeker

Tired.
Reaction score
122
I have no idea why do you use SinBJ and CosBJ when there are natives? I also don't understand what your code is doing, sorry I only work with distance or speed only and not duration. Thanks for trying to help me either way, repped.
 

wraithseeker

Tired.
Reaction score
122
Bump. Angle still doesn't work .. sigh..

Code update!

JASS:
scope KnockTest initializer Init

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;TEST&#039;
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 = GetUnitY(t)
    local real ty = GetUnitX(t)
    local real a = Atan2(y-ty,x-tx)*57.29582
    call KnockbackTarget(t,u,a,1000,30)
    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


JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private real TIME = 0.04
endglobals

private struct Knock
    unit source
    unit target
    real x
    real y
    real cos
    real sin
    real speed
    real decrement
    timer Timer
    effect mode

static method create takes unit source, unit target, real angle, real initialspeed, real decrement returns Knock
    local Knock d = Knock.allocate()
    set d.source = source
    set d.target = target
    set d.speed = initialspeed * TIME
    set d.decrement = decrement * TIME
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
call ReleaseTimer(.Timer)
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u = d.target
    local real sx = GetUnitX(u)
    local real sy = GetUnitY(u)
    local real x = sx + d.speed * d.cos
    local real y = sy + d.speed * d.sin
    local real tx
    local real ty
    if d.speed &lt;= 0 then
        call d.destroy()
    endif
        set x = sx + d.speed * d.cos
        set y = sy + d.speed * d.sin
        call SetUnitPosition(u,x,y)
        set d.speed = d.speed - d.decrement
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real initialspeed, real decrement returns boolean
    local Knock d = 0
    if target == null or source == null or initialspeed == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,(angle*0.01745328),initialspeed,decrement)
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,TIME,true,function Update)
    return true
endfunction


private function Init takes nothing returns nothing
endfunction
endlibrary
 

wraithseeker

Tired.
Reaction score
122
Well now code works and I need and want to know why does destroying of special effect work with this line and how do I calculate how much distance it will get knocked back.

JASS:
library Knockback initializer Init requires BoundSentinel, TimerUtils, DestructableLib

globals
    private constant real TIME = 0.03
    private constant string GROUND = &quot;MDX\\KnockbackDust.mdx&quot;
    private constant string WATER = &quot;MDX\\KnockbackWater.mdx&quot;
    private constant string ATTACHPOINT = &quot;origin&quot;
    private constant real RADIUS = 180
    private rect TreeRect
    private boolexpr TreeCheck
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 struct Knock
    unit source
    unit target
    real x
    real y
    real cos
    real sin
    real speed
    real decrement
    timer Timer
    effect effects
    boolean Terrain
    integer EffectMode
    boolean Trees
    
 public method TerrainCheck takes Knock d returns integer
        local real x = GetUnitX(d.target)
        local real y = GetUnitY(d.target)
        if IsTerrainPathingType(x, y, TERRAIN_PATHING_LAND) then
            return 1
        elseif IsTerrainPathingType(x, y, TERRAIN_PATHING_SHALLOW) then
            return 2
        endif
        return 0
    endmethod

static method create takes unit source, unit target, real angle, real initialspeed, real decrement, boolean KillTree 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.EffectMode = d.TerrainCheck(d)
    set x = GetUnitX(d.target)
    set y = GetUnitY(d.target)
    set d.speed = initialspeed * TIME
    set d.decrement = decrement * TIME
    set d.sin = Sin(angle)
    set d.cos = Cos(angle)
    set d.Timer = NewTimer()
    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
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
call DestroyEffect(this.effects)
call ReleaseTimer(this.Timer)
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u = d.target
    local real sx = GetUnitX(u)
    local real sy = GetUnitY(u)
    local real x = sx + d.speed * d.cos
    local real y = sy + d.speed * d.sin
    local integer mode = d.EffectMode
    if d.speed &lt;= 0 then
        call d.destroy()
    endif
    if d.Trees == true then
        call SetRect(TreeRect,sx-RADIUS,sy-RADIUS,sx+RADIUS,sy+RADIUS)
        call EnumDestructablesInRect(TreeRect,TreeCheck,function Trees)
        endif
    call SetUnitPosition(u,x,y)
    set d.EffectMode = d.TerrainCheck(d)
    if d.EffectMode == 1 and mode == 2 then
        call DestroyEffect(d.effects)
        set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
   endif
   if d.EffectMode == 2 and mode == 1 then
        call DestroyEffect(d.effects)
        set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
    endif
        set d.speed = d.speed - d.decrement
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real initialspeed, real decrement,boolean KillTree returns boolean
    local Knock d = 0
    if target == null or source == null or initialspeed == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle,initialspeed,decrement,KillTree)
    call SetTimerData(d.Timer,integer (d))
    call TimerStart(d.Timer,TIME,true,function Update)
    return true
endfunction


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


JASS:
 local integer mode = d.EffectMode
mode == 1 
mode == 2


Extracted from the code which the destroying of special effect worked after adding this few lines.
 

wraithseeker

Tired.
Reaction score
122
I felt a need to get this up since most people think that the question has been solved but it hasn't.

Now I want to know why doesn't this create dust Rock effect.

I had a brainwave , when the knocked unit gets knocked to the end of a blizzard cliff or a unit, it gets bounced back (Need physis system?)

The map is attached above.

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, TimerUtils, DestructableLib, TerrainPathability

globals
    private constant real TIME = 0.03
    private constant string GROUND = &quot;MDX\\Dust.mdx&quot;
    private constant string WATER = &quot;MDX\\SlideWater.mdx&quot;
    private constant string COLLISION = &quot;MDX\\DustAndRocks.mdx&quot;
    private constant string ATTACHPOINT = &quot;origin&quot;
    private constant real RADIUS = 180
    private rect TreeRect
    private boolexpr TreeCheck
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 struct Knock
    unit source
    unit target
    real x
    real y
    real cos
    real sin
    real speed
    real decrement
    timer Timer
    effect effects
    boolean Terrain
    integer EffectMode
    boolean Trees
    boolean AllowMove
    
 public method TerrainCheck takes Knock d returns integer
        local real x = GetUnitX(d.target)
        local real y = GetUnitY(d.target)
        if IsTerrainPathingType(x, y, TERRAIN_PATHING_LAND) then
            return 1
        elseif IsTerrainPathingType(x, y, TERRAIN_PATHING_SHALLOW) then
            return 2
        elseif IsTerrainWalkable(x,y) then
            return 3
        endif
        return 0
    endmethod

static method create takes unit source, unit target, real angle, real speed, real decrement, boolean KillTree, boolean Allowmove 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.AllowMove = Allowmove
    set d.EffectMode = d.TerrainCheck(d)
    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.Timer = NewTimer()
    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(GROUND,d.target,ATTACHPOINT)
    endif
    return d
    endmethod
    
method onDestroy takes nothing returns nothing
    call DestroyEffect(this.effects)
    call ReleaseTimer(this.Timer)
    endmethod
        endstruct

private function Update takes nothing returns nothing
    local Knock d = Knock(GetTimerData(GetExpiredTimer()))
    local unit u = d.target
    local real sx = GetUnitX(u)
    local real sy = GetUnitY(u)
    local real x = sx + d.speed * d.cos
    local real y = sy + d.speed * d.sin
    local integer mode = d.EffectMode
    if d.speed &lt;= 0 then
        call d.destroy()
    endif
    if d.Trees == true then
        call SetRect(TreeRect,sx-RADIUS,sy-RADIUS,sx+RADIUS,sy+RADIUS)
        call EnumDestructablesInRect(TreeRect,TreeCheck,function Trees)
        endif
        if d.AllowMove == true then
        call SetUnitX(d.target,x)
        call SetUnitY(d.target,y)
        else
    call SetUnitPosition(u,x,y)
    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
        set d.speed = d.speed - d.decrement
    set u = null
endfunction

function KnockbackTarget takes unit source, unit target , real angle, real speed, real decrement,boolean KillTree,boolean AllowMove returns boolean
    local Knock d = 0
    if target == null or source == null or speed == null or decrement == null then
        call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    set d = Knock.create(source,target,angle,speed,decrement,KillTree,AllowMove)
    call SetTimerData(d.Timer,d)
    call TimerStart(d.Timer,TIME,true,function Update)
    return true
endfunction


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



JASS:
library IsTerrainWalkable initializer Init

//*****************************************************************
//*  IsTerrainWalkable
//*
//*  rewritten in vJass by: Anitarf
//*  original implementation: Vexorian
//*
//*  A function for checking if a point is pathable for ground
//*  units (it does so by attempting to move an item there and
//*  checking where it ended up), typically used to stop sliding
//*  units before they end up stuck in trees. If the point is not
//*  pathable, the function will also determine the nearest point
//*  that is (the point where the item ends up).
//*****************************************************************

    globals
        // this value is how far from a point the item may end up for the point to be considered pathable
        private constant real MAX_RANGE = 10.0
        
        // the following two variables are set to the position of the item after each pathing check
        // that way, if a point isn&#039;t pathable, these will be the coordinates of the nearest point that is
        public real X = 0.0
        public real Y = 0.0

    
// END OF CALIBRATION SECTION    
// ================================================================

        private rect r
        private item check
        private item array hidden
        private integer hiddenMax = 0
    endglobals

    private function Init takes nothing returns nothing
        set check = CreateItem(&#039;ciri&#039;,0,0)
        call SetItemVisible(check,false)
        set r = Rect(0.0,0.0,128.0,128.0)
    endfunction

    private function HideBothersomeItem takes nothing returns nothing
        if IsItemVisible(GetEnumItem()) then
            set hidden[hiddenMax]=GetEnumItem()
            call SetItemVisible(hidden[hiddenMax],false)
            set hiddenMax=hiddenMax+1
        endif
    endfunction

// ================================================================
    
    function IsTerrainWalkable takes real x, real y returns boolean
        // first, hide any items in the area so they don&#039;t get in the way of our item
        call MoveRectTo(r, x,y)
        call EnumItemsInRect(r,null,function HideBothersomeItem)
        // try to move the check item and get it&#039;s coordinates
        call SetItemPosition(check,x,y)//this unhides the item...
        set X = GetItemX(check)
        set Y = GetItemY(check)
        call SetItemVisible(check,false)//...so we must hide it again
        // before returning, unhide any items that got hidden at the start
        loop
            exitwhen hiddenMax&lt;=0
            set hiddenMax=hiddenMax-1
            call SetItemVisible(hidden[hiddenMax],true)
            set hidden[hiddenMax]=null
        endloop
        // return pathability status
        return (x-X)*(x-X)+(y-Y)*(y-Y) &lt; MAX_RANGE*MAX_RANGE
    endfunction
    
endlibrary
 

Viikuna

No Marlo no game.
Reaction score
265
JASS:
set a.sin=Sin(angle)
set a.cos=Cos(angle)

When knockback begins, just store angles cos and sin components.
They tell you to which direction that poor guy is knocked.

JASS:
set a.x=SafeX(a.x+a.velocity*a.cos)
set a.y=SafeY(a.y+a.velocity*a.sin)

The pattern to calculate next x and y values is simply:

old value + distance * angles sin or cos component

cos works for x and sin works for y.

How to calculate angle from A to B:

JASS:
local real dx=Bx-Ax
local real dy=By-Ay
local real angle=Atan2(dy,dx)


edit. hmm, I think I missied some posts.

Anyways, you should maybe use one timer and struct array for this, instead of TimerUtils
 

wraithseeker

Tired.
Reaction score
122
Why only one timer and struct array?

You still haven't told me about the dust effect not appearing.

What is velocity?

I don't have a distance variable here though. Using speed.

How to calculate angle from A to B:


JASS:

local real dx=Bx-Ax
local real dy=By-Ay
local real angle=Atan2(dy,dx)

What's that for ? What is Ax, Ay and dy , dx.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top