• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Help with own Knockback system

Status
Not open for further replies.
Level 2
Joined
Apr 29, 2013
Messages
21
Hello guys,

I would ask you to help me please by my own Knockback-System. I've constructed it for my project, cuz I want to make as much as possible by myself.

JASS:
library Knockback initializer OnInit

    globals
        unit array KU
        location array KCL
        location array KTL
        real array KD
        real array KA
        integer KIndex
        timer KT
    endglobals
    
    private function CleanArray takes integer i returns nothing
        local integer x = 0
        loop
            exitwhen x > KIndex
            if(x >= i and x < KIndex) then
                set KU[x] = KU[x+1]
                set KCL[x] = KCL[x+1]
                set KTL[x] = KTL[x+1]
                set KD[x] = KD[x+1]
                set KA[x] = KA[x+1]
            elseif(x == KIndex) then
                set KU[x] = null
                call RemoveLocation(KCL[x])
                call RemoveLocation(KTL[x])
                set KD[x] = 0.
                set KA[x] = 0.
                set KIndex = KIndex - 1
            endif
            set x = x + 1
        endloop
    endfunction
    
    private function KnockLoop takes nothing returns nothing
        local location l
        local integer i = 0
        loop
            exitwhen i > KIndex
            if(DistanceBetweenPoints(KCL[i], KTL[i]) < KD[i]) then
                set l = PolarProjectionBJ(KTL[i], 25., KA[i])
                set KTL[i] = l
                call SetUnitPositionLoc(KU[i], l)
                call RemoveLocation(l)
            else
                call RemoveLocation(l)
                call CleanArray(i)
            endif
            set i = i + 1
        endloop
    endfunction
    
    function Knockback takes unit c,unit t,real d returns nothing
        set KCL[KIndex] = GetUnitLoc(c)
        set KTL[KIndex] = GetUnitLoc(t)
        set KD[KIndex] = d
        set KA[KIndex] = AngleBetweenPoints(KCL[KIndex], KTL[KIndex])
        set KU[KIndex] = t
        set KIndex = KIndex + 1
    endfunction
    
    function OnInit takes nothing returns nothing
        set KT = CreateTimer()
        call TimerStart(KT, .03, true, function KnockLoop)
    endfunction
    
endlibrary

this is the code

I know that points leak and I'll change this until it work.
If globals don't selfexplain themselves(logically they don't :vw_unimpressed:):

KU -> Knocked Unit
KCL -> Location of the Knocking Unit
KTL -> Location of the KU(maybe a bit unnecessary)
KD -> Max Knockback Distance
KA -> Angle(for PolarProjection maybe unnecessary too)
KIndex -> I think this is selfexplaining
KT -> the running Timer

I thank in advance for your help. I watched yesterday and today over it, but couldn't find the mistake. Maybe school is too stressy at the moment^^.
 
Level 2
Joined
Apr 29, 2013
Messages
21
Nothing happens...
Unit A casts Ability -> Rushs trough Units -> Damages Units but they don't move...

btw in the Ability is: call Knockback(udg_CS_Caster, GetEnumUnit(), 200.)
where udg_CS_Caster is the casting unit

And yes I know that locations leak but I already said I'll change this when I get it working.
 
Level 2
Joined
Apr 29, 2013
Messages
21
JASS:
function Trig_Cassandra_Schattensprint_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00R'
endfunction

function CS_Dmg takes nothing returns nothing
    if(Check_Target(udg_CS_Caster, GetEnumUnit()) and IsUnitInGroup(GetEnumUnit(), udg_CS_Hit) == false) then
        call GroupAddUnit(udg_CS_Hit, GetEnumUnit())
        call DoDmgM(udg_CS_Caster, GetEnumUnit(), 25., 45., 'A00R', 0., 0, 0.)
        call FleshSound(GetEnumUnit())
        call Knockback(udg_CS_Caster, GetEnumUnit(), 200.)
    endif
endfunction

function CS_Run takes nothing returns nothing
    local location l = GetUnitLoc(udg_CS_Caster)
    local location l2 = PolarProjectionBJ(l, 48., udg_CS_Angle)
    local group g = GetUnitsInRangeOfLocAll(196., l)
    if(udg_CS_CurDist < udg_CS_Dist) then
        set udg_CS_CurDist = udg_CS_CurDist + 48.
        call ForGroup(g, function CS_Dmg)
        call SetUnitPositionLoc(udg_CS_Caster, l2)
        if(GetRandomInt(0, 5) == 0) then
            call RushSound(udg_CS_Caster)
        endif
    else
        call DestroyTimer(udg_CS_Timer)
        call DestroyGroup(g)
        set g = GetUnitsInRangeOfLocAll(196., l2)
        call ForGroup(g, function CS_Dmg)
        call DestroyGroup(udg_CS_Hit)
        call UnitRemoveAbility(udg_CS_Caster, 'A00W')
        call SetUnitVertexColorBJ(udg_CS_Caster, 100., 100., 100., 0.)
        call SetUnitPathing(udg_CS_Caster, true)
    endif
    call RemoveLocation(l)
    call RemoveLocation(l2)
    call DestroyGroup(g)
endfunction

function Trig_Cassandra_Schattensprint_Actions takes nothing returns nothing
    local location l = GetUnitLoc(GetTriggerUnit())
    local location l2 = GetSpellTargetLoc()
    set udg_CS_Caster = GetTriggerUnit()
    set udg_CS_Dist = DistanceBetweenPoints(l, l2)
    set udg_CS_CurDist = 0.
    set udg_CS_Angle = AngleBetweenPoints(l, l2)
    set udg_CS_Timer = CreateTimer()
    set udg_CS_Hit = CreateGroup()
    call RemoveLocation(l)
    call RemoveLocation(l2)
    call SetUnitVertexColorBJ(GetTriggerUnit(), 5., 5., 5., 60.)
    call SetUnitPathing(GetTriggerUnit(), false)
    call UnitAddAbility(GetTriggerUnit(), 'A00W')
    call TimerStart(udg_CS_Timer, .03, true, function CS_Run)
endfunction
 
function InitTrig_Cassandra_Schattensprint takes nothing returns nothing
    set gg_trg_Cassandra_Schattensprint = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Cassandra_Schattensprint, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Cassandra_Schattensprint, Condition( function Trig_Cassandra_Schattensprint_Conditions ) )
    call TriggerAddAction( gg_trg_Cassandra_Schattensprint, function Trig_Cassandra_Schattensprint_Actions )
endfunction

yes the locations i know :)
will be done later

also there are a few functions from my librarys but they work i know from other spells^^

Edit: No I didn't use debug messages cuz I don't know how to use them^^. I'm anyway a beginner with Jass/vJass and currently working on a custom Damage System that replaces the Blizzard one(even Autoattacks). That means I'm already done only the OnDeath() rewards are missing^^. The Knockback system isn't that important at the moment but it would be nice if it's already fixed when i need it.
 
Last edited:
To use debug messages is easy. Place one saying running or a number and if it shows that portion of code is running. If it doesn't then that code is not running.

Why even start with locations it makes everything more complicated and spells longer when you go back to change out locations for reals ?

You shouldn't use TriggerAddAction. Do everything from the conditions block. Check out my tutorial Converting GUI to Efficient Jass. It will give you some pointers. You also have to null all handles at end of function or they cause a leak. Anything used twice or more should be stored in a variable.

It is hard to tell what is and is not running. You should clean up the spell then try to find out what is and is not working.
This is also not MUI it could be breaking because of that.

If you want to fix it then clean it up try using debug messages and post the results.
 
Level 7
Joined
Mar 6, 2006
Messages
282
In your Knockback library:

JASS:
globals
        unit array KU
        location array KCL
        location array KTL
        real array KD
        real array KA
        integer KIndex     // <------
        timer KT
    endglobals

Set that to zero, since it's the starting index. When your Knockback function is called, it's failing here, because it was never set:

JASS:
function Knockback takes unit c,unit t,real d returns nothing
        set KCL[KIndex] = GetUnitLoc(c)
        set KTL[KIndex] = GetUnitLoc(t)
        set KD[KIndex] = d
        set KA[KIndex] = AngleBetweenPoints(KCL[KIndex], KTL[KIndex])
        set KU[KIndex] = t
        set KIndex = KIndex + 1   //  <----
endfunction

Also, I believe that you're supposed to increment the index before setting the variables:

JASS:
function Knockback takes unit c,unit t,real d returns nothing
        set KIndex = KIndex + 1   //  <----
        set KCL[KIndex] = GetUnitLoc(c)
        set KTL[KIndex] = GetUnitLoc(t)
        set KD[KIndex] = d
        set KA[KIndex] = AngleBetweenPoints(KCL[KIndex], KTL[KIndex])
        set KU[KIndex] = t
endfunction

Lastly (I could be wrong here, idk vJass that well) your de-indexing function CleanArray() looks kind of weird. It doesn't look like it decreases your iterator from KnockLoop() which means that it skips units that should be accounted for.

There's still more that needs fixing, but it's not as dire as the above mentioned stuff, so you should post your triggers again after you've fixed some of it.
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
BPower is right.
Then if you're talking about indexing you have to make the integer index to -1 and increment before setting or start it to 0 and increment after setting.

Don't use locations as said above but use coordinates which are faster.
Try to avoid some BJs :
JASS:
DistanceBetweenPoints -> Atan2(y1-y2, x1-x2)
PolarProjection -> set x = x+Cos(angle)*offset
                    -> set y = y+Sin(angle)*offset
DistanceBetweenPoints -> SquareRoot((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
 
Level 2
Joined
Apr 29, 2013
Messages
21
Thanks for your help.
1. Yes without setting KIndex it cannot be increased. Now I see -.-.
2. Also yes, the spell isn't MUI but this has the reason that it don't has to be. To say it in a different way: every hero can only be chosen once, so there is no need too make spells mui.
3. Ok I started Jass just by converting Triggers so I never read your tutorial cuz I want to do as much as possibile by myself also the Jass leaning but I think I'll watch over it.
4. I now told twice that I exchange locations with reals when the system works. I would be nice if you don't say it over and over. I already know it :)
5. I just was lazy to replace some BJs :) I wanted to do so when I finished the entire script cuz there are also triggers from my beginning with Jass which are really unefficient even more than the spell above :)

JASS:
    private function CleanArray takes integer i returns nothing
        local integer x = 0
        loop
            exitwhen x > KIndex
            if(x >= i and x < KIndex) then   <----- i = the removed index, all thats above will be moved one index lower
                set KU[x] = KU[x+1]
                set KCL[x] = KCL[x+1]
                set KTL[x] = KTL[x+1]
                set KD[x] = KD[x+1]
                set KA[x] = KA[x+1]
            elseif(x == KIndex) then          <----- and the last one will be deleted (already KIndex-1 so no need)
                set KU[x] = null
                call RemoveLocation(KCL[x])
                call RemoveLocation(KTL[x])
                set KD[x] = 0.
                set KA[x] = 0.
                set KIndex = KIndex - 1
            endif
            set x = x + 1
        endloop
    endfunction
whats wrong withi this o_O? I made a mistake? it schould be working
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
Its the wrong place to discuss this, but JassHelper/vJass is not bound to JNGP. JNGP is only a WE modification that USES JassHelper/vJass. But you can run JassHelper standalone without the need for JNGP. In fact vJass existed long before JNGP.
 
Level 2
Joined
Apr 29, 2013
Messages
21
So here it is, but it still doesn't work :(
JASS:
library Knockback initializer OnInit

    globals
        unit array KU
        real array KCX
        real array KCY
        real array KTX
        real array KTY
        real array KD
        real array KA
        integer KIndex
        timer KT
    endglobals
   
    private function CleanArray takes integer i returns nothing
        local integer x = 0
        loop
            exitwhen x > KIndex
            if(x >= i and x < KIndex) then
                set KU[x] = KU[x+1]
                set KCX[x] = KCX[x+1]
                set KCY[x] = KCY[x+1]
                set KTX[x] = KTX[x+1]
                set KTY[x] = KTY[x+1]
                set KD[x] = KD[x+1]
                set KA[x] = KA[x+1]
            elseif(x == KIndex) then
                set KU[x] = null
                set KCX[x] = 0.
                set KCY[x] = 0.
                set KTX[x] = 0.
                set KTY[x] = 0.
                set KD[x] = 0.
                set KA[x] = 0.
                set KIndex = KIndex - 1
            endif
            set x = x + 1
        endloop
    endfunction
   
    private function KnockLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > KIndex
            if(SquareRoot(Pow(KTX[i] - KCX[i], 2) + Pow(KTY[i] -KCY[i], 2)) < KD[i]) then
                set KTX[i] = KTX[i] + 25. * Cos(KA[i] * bj_DEGTORAD)
                set KTY[i] = KTY[i] + 25. * Sin(KA[i] * bj_DEGTORAD)
                call SetUnitPosition(KU[i], KTX[i], KTY[i])
            else
                call CleanArray(i)
            endif
            set i = i + 1
        endloop
    endfunction
   
    function Knockback takes unit c,unit t,real d returns nothing
        set KCX[KIndex] = GetUnitX(c)
        set KCY[KIndex] = GetUnitY(c)
        set KTX[KIndex] = GetUnitX(t)
        set KTY[KIndex] = GetUnitY(t)
        set KD[KIndex] = d
        set KA[KIndex] = bj_RADTODEG * Atan2(KTY[KIndex] - KCY[KIndex], KTX[KIndex] - KCX[KIndex])
        set KU[KIndex] = t
        set KIndex = KIndex + 1
    endfunction
   
    function OnInit takes nothing returns nothing
        set KT = CreateTimer()
        call TimerStart(KT, .03, true, function KnockLoop)
    endfunction
   
endlibrary
 
Level 2
Joined
Apr 29, 2013
Messages
21
JASS:
library Knockback

    globals
        unit array KU
        real array KCX
        real array KCY
        real array KTX
        real array KTY
        real array KD
        real array KA
        integer KIndex = -1
        timer KT
    endglobals
   
    private function CleanArray takes integer i returns nothing
        local integer x = 0
        loop
            exitwhen x > KIndex
            if(x >= i and x < KIndex) then
                set KU[x] = KU[x+1]
                set KCX[x] = KCX[x+1]
                set KCY[x] = KCY[x+1]
                set KTX[x] = KTX[x+1]
                set KTY[x] = KTY[x+1]
                set KD[x] = KD[x+1]
                set KA[x] = KA[x+1]
            elseif(x == KIndex) then
                set KU[x] = null
                set KCX[x] = 0.
                set KCY[x] = 0.
                set KTX[x] = 0.
                set KTY[x] = 0.
                set KD[x] = 0.
                set KA[x] = 0.
                set KIndex = KIndex - 1
            endif
            set x = x + 1
        endloop
    endfunction
   
    private function KnockLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > KIndex
            if(SquareRoot(Pow(KTX[i] - KCX[i], 2) + Pow(KTY[i] - KCY[i], 2)) < KD[i]) then
                set KTX[i] = KTX[i] + 25. * Cos(KA[i] * bj_DEGTORAD)
                set KTY[i] = KTY[i] + 25. * Sin(KA[i] * bj_DEGTORAD)
                call SetUnitX(KU[i], KTX[i])
                call SetUnitY(KU[i], KTY[i])
            else
                call CleanArray(i)
            endif
            set i = i + 1
        endloop
        if(KIndex == -1) then
            call DestroyTimer(KT)
        endif
    endfunction
   
    function Knockback takes unit c,unit t,real d returns nothing
        set KIndex = KIndex + 1
        set KCX[KIndex] = GetUnitX(c)
        set KCY[KIndex] = GetUnitY(c)
        set KTX[KIndex] = GetUnitX(t)
        set KTY[KIndex] = GetUnitY(t)
        set KD[KIndex] = d
        set KA[KIndex] = bj_RADTODEG * Atan2(KTY[KIndex] - KCY[KIndex], KTX[KIndex] - KCX[KIndex])
        set KU[KIndex] = t
        if(KIndex == 0) then
            set KT = CreateTimer()
            call TimerStart(KT, .03, true, function KnockLoop)
        endif
    endfunction
   
endlibrary

now better? (still only works sometimes)

Edit: Works.(Mistake in Spell, too little distance^^)
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
JASS:
library Knockback

    globals
        unit array KU
        real array KCX
        real array KCY
        real array KTX
        real array KTY
        real array KD
        real array KA
        integer KIndex = -1
        timer KT
    endglobals
   
    private function CleanArray takes integer i returns nothing
        local integer x = 0
        loop
            exitwhen x > KIndex
            if(x >= i and x < KIndex) then
                set KU[x] = KU[x+1]
                set KCX[x] = KCX[x+1]
                set KCY[x] = KCY[x+1]
                set KTX[x] = KTX[x+1]
                set KTY[x] = KTY[x+1]
                set KD[x] = KD[x+1]
                set KA[x] = KA[x+1]
            elseif(x == KIndex) then
                set KU[x] = null
                set KCX[x] = 0.
                set KCY[x] = 0.
                set KTX[x] = 0.
                set KTY[x] = 0.
                set KD[x] = 0.
                set KA[x] = 0.
                set KIndex = KIndex - 1
            endif
            set x = x + 1
        endloop
    endfunction
   
    private function KnockLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > KIndex
            if(SquareRoot(Pow(KTX[i] - KCX[i], 2) + Pow(KTY[i] - KCY[i], 2)) < KD[i]) then
                set KTX[i] = KTX[i] + 25. * Cos(KA[i] * bj_DEGTORAD)
                set KTY[i] = KTY[i] + 25. * Sin(KA[i] * bj_DEGTORAD)
                call SetUnitX(KU[i], KTX[i])
                call SetUnitY(KU[i], KTY[i])
            else
                call CleanArray(i)
            endif
            set i = i + 1
        endloop
        if(KIndex == -1) then
            call DestroyTimer(KT)
        endif
    endfunction
   
    function Knockback takes unit c,unit t,real d returns nothing
        set KIndex = KIndex + 1
        set KCX[KIndex] = GetUnitX(c)
        set KCY[KIndex] = GetUnitY(c)
        set KTX[KIndex] = GetUnitX(t)
        set KTY[KIndex] = GetUnitY(t)
        set KD[KIndex] = d
        set KA[KIndex] = bj_RADTODEG * Atan2(KTY[KIndex] - KCY[KIndex], KTX[KIndex] - KCX[KIndex])
        set KU[KIndex] = t
        if(KIndex == 0) then
            set KT = CreateTimer()
            call TimerStart(KT, .03, true, function KnockLoop)
        endif
    endfunction
   
endlibrary

now better? (still only works sometimes)

Edit: Works.(Mistake in Spell, too little distance^^)

I'll give you some design advice, to make the system more capable.
Currently you calculate the angle of knockback every single time, which is a huge waste. Currently this limits the amount of projectiles.

Split the knockback into X and Y. Then when a unit is knocked you convert angle and speed into X speed and Y speed.
That way the loop becomes much easier.
SetUnitX(u) = GetUnitX(u) + SpeedX

I have a system like that, but with 3 coordinates(height too) and it starts to lag earlier from the amount of models than the actual system itself.
 
JASS:
if(x >= i and x < KIndex) then
                set KU[x] = KU[x+1]
                set KCX[x] = KCX[x+1]
                set KCY[x] = KCY[x+1]
                set KTX[x] = KTX[x+1]
                set KTY[x] = KTY[x+1]
                set KD[x] = KD[x+1]
                set KA[x] = KA[x+1]
            elseif(x == KIndex) then
                set KU[x] = null
                set KCX[x] = 0.
                set KCY[x] = 0.
                set KTX[x] = 0.
                set KTY[x] = 0.
                set KD[x] = 0.
                set KA[x] = 0.
                set KIndex = KIndex - 1
            endif

You decrement index in only one of these.

By the way, you can just set inst[x] = inst[maxIndex] and set maxIndex--

There is no reason to keep the stack in order.

Edit: If that still doesn't fix it, try printing a debug message right above "call cleanarray" - I bet you'll see it's being cleaned before you intended ;)
 
Status
Not open for further replies.
Top