System Knockback System

Reaction score
456
_ __ ___ ____ _____ ____ ___ __ _
Knockback System v2.0
¯ ¯¯ ¯¯¯ ¯¯¯¯ ¯¯¯¯¯ ¯¯¯¯ ¯¯¯ ¯¯ ¯

by Überplayer


I didn't see anyone submitting a system like this, so I decided to create one. I hope that you all know
what is the basic idea of a knockback system?

Instruction Trigger:
JASS:
v.2.0

Made by: Überplayer
Credits: Cohadar - for ABC, BorderSafety and help

Requirements:
¯¯¯¯¯¯¯¯¯¯¯¯¯
-NewGen editor (required)
-ABC (required)
-BorderSafety (required)

Importing:
¯¯¯¯¯¯¯¯¯¯
If you don't already have ABC v5.1 and BorderSafety v1.0 in your map, then
copy the "ABC" and "BorderSafety" triggers to your map. Finally, you can copy 
the "Knockback System" trigger to your map. 

How to use?:
¯¯¯¯¯¯¯¯¯¯¯¯
whichUnit   = the unit which knocks a unit back and deals damage
target      = the unit which is knocked back
direction   = the direction where the unit is knocked back
distance    = the distance how far the unit is knocked back
damage      = the damage which is dealt during the knockback
duration    = the duration of the knock back
effectPath  = model string of the effect
attachPoint = where the effect is attached
_____________________________
function UnitKnockTarget takes unit whichUnit, unit target, real direction, real distance, real damage, real duration returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
_____________________________
function UnitKnockTargetEx takes unit whichUnit, unit target, real direction, real distance, real damage, real duration, real effectPath, real attachPoint returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
KnockbackUnit(..) function uses the default strings from the
systems configuration menu.


System Trigger:
JASS:
library Knockback uses ABC, BorderSafety

globals
    private constant real INTERVAL = 0.035
    private constant string EFFECT_STRING = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl"
    private constant string ATTACH_POINT = "origin"
endglobals

private struct KBdata
    unit whichUnit
    unit target
    string effectPath
    string attachPoint

    effect knockEffect
    real dx
    real dy
    real periodDamage
        
    timer clock = CreateTimer()
    integer ticks
        
    static method create takes unit whichUnit, unit target, real direction, real distance, real damage, real duration, string effectPath, string attachPoint returns KBdata
        local KBdata dat = KBdata.allocate()
            
        set dat.whichUnit = whichUnit
        set dat.target = target
        set dat.effectPath = effectPath
        set dat.attachPoint = attachPoint
        set dat.dx = ((distance / duration) * INTERVAL) * Cos(direction * bj_DEGTORAD)
        set dat.dy = ((distance / duration) * INTERVAL) * Sin(direction * bj_DEGTORAD)
        set dat.periodDamage = ((damage / duration) * INTERVAL)
        set dat.ticks = R2I(duration / INTERVAL)
        
        call SetTimerStructA(dat.clock, dat)

        return dat
    endmethod
        
    method onDestroy takes nothing returns nothing
        call ClearTimerStructA(.clock)
        call PauseTimer(.clock)
        call DestroyTimer(.clock)
        call DestroyEffect(.knockEffect)
        call SetUnitPathing(.target, true)
    endmethod
endstruct
    
private function KnockbackUnit_Handler takes nothing returns nothing
    local timer exprTimer = GetExpiredTimer()
    local KBdata dat = GetTimerStructA(exprTimer)
    local real x
    local real y
        
    if (dat.ticks <= 0) then
        call dat.destroy()
    else
        set dat.ticks = dat.ticks - 1
        set x = GetUnitX(dat.target) + dat.dx
        set y = GetUnitY(dat.target) + dat.dy
        call DestroyEffect(dat.knockEffect)
        set dat.knockEffect = AddSpecialEffectTarget(dat.effectPath, dat.target, dat.attachPoint)
        call SetUnitPosition(dat.target, x, y)
        call UnitDamageTarget(dat.whichUnit, dat.target, dat.periodDamage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
        call SetUnitPathing(dat.target, false)
    endif
        
    set exprTimer = null
endfunction
    
function UnitKnockTargetEx takes unit whichUnit, unit target, real direction, real distance, real damage, real duration, string effectPath, string attachPoint returns nothing
    local KBdata dat = KBdata.create(whichUnit, target, direction, distance, damage, duration, effectPath, attachPoint) 
    call SetUnitPathing(dat.target, false)
    call TimerStart(dat.clock, INTERVAL, true, function KnockbackUnit_Handler)
    set dat.knockEffect = AddSpecialEffectTarget(dat.effectPath, dat.target, dat.attachPoint)
endfunction

function UnitKnockTarget takes unit whichUnit, unit target, real direction, real distance, real damage, real duration returns nothing
    call UnitKnockTargetEx(whichUnit, target, direction, distance, damage, duration, EFFECT_STRING, ATTACH_POINT)
endfunction
    
endlibrary

(thanks Cohadar x).. the code was shortened alot)

The Map:
 

Attachments

  • [System] - Knockback v2.0.w3x
    42.6 KB · Views: 598
Reaction score
456
Code:
GUI Knockback
    Events
        Unit - A unit Starts the effect of an ability
    Conditions
    Actions
        Custom script:    call KnockbackUnitEx(GetTriggerUnit(), GetUnitFacing(GetTriggerUnit()), 300.00, 0.55, "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", "origin")
Same as JASS.
 

Cohadar

master of fugue
Reaction score
209
LoL I see you were serious when you said you are learning ABC.

A few comments:

Using //! prefix for library commands has no sense.
It is a compatibility leftover from ages ago, just remove it or it might add unnecessary confusion.

I see you learned how to use timer ticks, +rep instantly. :)

NEVER put interval as an argument to a function
better way is to put it as a constant so each user has to configure it only once, not pass it every time he wants to call your function
JASS:
globals
    // suggested values in range 0.02 .. 0.04
    private constant integer INTERVAL = 0.035 
endglobals



Advice:
For functions with many arguments you should always have a short default version.
Not many people would want to change that default dust effect that comes with knockback
JASS:
// you must admit this looks much simpler to use
function KnockbackUnit takes unit whichUnit, real angle, real distance, real duration returns nothing

// and for users who want it advanced they can have it
function KnockbackUnitEx takes unit whichUnit, real angle, real distance, real duration string effectPath, string attachPoint returns nothing

Add default effect and attach point as constants at library header as well.


This can be optimized
JASS:
set x = dat.whichUnitX + dat.currentDistance * Cos(dat.angle * bj_DEGTORAD)
set y = dat.whichUnitY + dat.currentDistance * Sin(dat.angle * bj_DEGTORAD)

like this:
JASS:
// dx and dy are how much unit moves during one timer tick
set x = GetUnitX(dat.whichUnit) + dat.dx
set y = GetUnitY(dat.whichUnit) + dat.dy


dx and dy are constant for each individual knockback parameters so you can calculate them in create:
JASS:
set dat.dx = distance/duration * INTERVAL * Cos(angle * bj_DEGTORAD)
set dat.dy = distance/duration * INTERVAL * Sin(angle * bj_DEGTORAD)

PS: when you name something "angle" it usually means it is in radians
I suggest you change that parameter name to "facing"


Btw indenting whole library is a bad idea:
no no
JASS:
library Knockback uses ABC
    private struct KBdata
        unit whichUnit
        real angle
        real distance


this is better:
JASS:
library Knockback uses ABC

private struct KBdata
    unit whichUnit
    real angle
    real distance


Line length is a scarce resource, do not waste it.
 
Reaction score
456
Thanks Cohadar! I see that this time there was no critical thingies :p..

>NEVER put interval as an argument to a function
Good idea, that also shortens the argument list pretty well :).

>Using //! prefix for library commands has no sense.
Lol.. I saw it in caster system.. just a.. okay, I remove it.

>default dust effect that comes with knockback
That's not a default effect ;O.. But I can make it default.

>I see you learned how to use timer ticks, +rep instantly.
Hihi.. I had problems with that, as first I divided distance with duration x)..

>Add default effect and attach point as constants at library header as well.
But.. if a user want's to use this for different kind of spells? And all of them doesn't use the same effects?

>dx and dy are constant for each individual knockback parameters so you can calculate them in create
I shall do that.

>Btw indenting whole library is a bad idea
Okay.. I am just a perfectionist who has to do everything to make his code look better..:p

>Well I hope you survived this CA
I did survive! Yipee!

.. this post looks messy.. :(.. I also updated Fade System.
 

Cohadar

master of fugue
Reaction score
209
But.. if a user want's to use this for different kind of spells? And all of them doesn't use the same effects?

That is why I told you to have a basic and an extended function.

Even blizzard does that:
JASS:
native MoveLightning takes lightning whichBolt, boolean checkVisibility, real x1, real y1, real x2, real y2 returns boolean
native MoveLightningEx takes lightning whichBolt, boolean checkVisibility, real x1, real y1, real z1, real x2, real y2, real z2 returns boolean


(there are around 20 Ex functions in jass)
 

hell_knight

Playing WoW
Reaction score
126
Very useful +rep
Can you make it take another real that is damage? and that damage is done per interval to them or you can divide it by the total intervals
 
Reaction score
456
Changes:
-Everything Cohadar noted on.

Version 1.2 is out.. quite fast update in my opinion.

>(there are around 20 Ex functions in jass)
Okay, so I am not the only one x)

>Can you make it take another real that is damage? and that damage is done per interval to them or you can divide it by the total intervals
I shall do that. But what player would be the damager?
 

Cohadar

master of fugue
Reaction score
209
Very useful +rep
Can you make it take another real that is damage? and that damage is done per interval to them or you can divide it by the total intervals

No this is bad idea.
It has no point because this is a system for moving unit's not for damaging them.

Also it is pointless to do a damage-over-time on passive ability that is actually a "hammer in the head kind of stuff"

Besides damage on knockback is done at the end of a knockback as far as I know.
 
Reaction score
456
>No this is bad idea.
Might be.. and there is still the problem: "Who is the damager?". I wouldn't like to add new argument for it :p..

>Besides damage on knockback is done at the end of a knockback as far as I know.
DotA's Barathrum has a Greater Bash spell, where the unit takes damage during the knockback.. But that's just DotA.
 

Cohadar

master of fugue
Reaction score
209
And this is not a spell but a general purpose system...

EDIT: Why is your default effect string empty?
Put default knockback dust effect there ffs.

EDIT2:
JASS:
(KnockbackUnitEx() has less arguments than KnockbackUnits(). It uses
the default EFFECT_STRING and ATTACH_POINT from the "Knockback System"'s
configuration menu.)


Omg NO. Ex means extended, extended functions have MORE arguments, not the other way around.
 
Reaction score
456
>Put default knockback dust effect there ffs.
Forgot that, as I began to write the instructions :p..

>Omg NO. Ex means extended, extended functions have MORE arguments, not the other way around.
Hehe.. I'm a bit embrassed now :eek:..

I shall edit it right away!
 

emjlr3

Change can be a good thing
Reaction score
395
this should run off 1 timer
 

emjlr3

Change can be a good thing
Reaction score
395
i don't know how to use Grim to time shit, however, Vex/ Pipe/ etc. say its faster.....
i have not seen their numbers, but....

and then there are no need for superfluous systems for its usage
 

Cohadar

master of fugue
Reaction score
209
i don't know how to use Grim to time shit, however, Vex/ Pipe/ etc. say its faster.....
i have not seen their numbers, but....

and then there are no need for superfluous systems for its usage

Well I know how to use both ways:
http://hilton.gw.oiccam.com/showthread.php?t=97732

And I can tell you, speed difference is absolutely irrelevant compared to code bloath.
ABC is faster than gamecache as you know, I really see no need for more speed.
 
Reaction score
456
If I had only one timer, I could only attach 3 structures to it (A, B and C). If someone proves it's faster to use one timer for each structure, I can change it..
 

Cohadar

master of fugue
Reaction score
209
No it is actually possible to attach 8190 structs to a single timer if you use a method I shown in link in my previous post.

The only problem is that this "array loop method" is much more complicated to use than ABC. (and it does not have debug messages and other nifty stuff)
 
Reaction score
456
Okay well.. I think I leave this the way it is.. then?

>The only problem is that this "array loop method" is much more complicated to use than ABC.
I used it before ABC, because Vexorian uses it in his tutorial "Link".
 

Cohadar

master of fugue
Reaction score
209
Attachment is not the problem of your knockback,
it is a general problem with moving stuff and that is that unit's leave playable map area.
(That black boundaries you use are not fooling anyone)

So I suggest you remove boundaries and try this:
JASS:
//==============================================================================
//  BorderSafety by Cohadar - v1.0
//==============================================================================
//
//  PURPOUSE:
//       * Automatically prevents crashes caused by units getting off map
//       * Also provides a set of functions for safe unit movement
//
//  FUNCTION LIST:
//         GetSafeX(real) -> real
//         GetSafeY(real) -> real
//
//         SetUnitXSafe(unit, x)
//         SetUnitYSafe(unit, x)
//         SetUnitPositionSafe(unit, x, y)
//
//  PROS: 
//       * Safe Set functions preserve current orderId of unit
//         This means that units can attack while being moved with triggers.
//
//       * You will get a warning message if some unit tries to get off-map
//         It means that you were using unsafe functions somewhere.
//
//       * Units that try to leave map will be automatically returned on map border
//
//  CONS:
//       * none?
//
//  HOW TO IMPORT:
//       * Just create a trigger named BorderSafety
//       * convert it to text and replace the whole trigger text with this one
//
//==============================================================================

library BorderSafety initializer Init

globals
    real MapMaxX
    real MapMaxY
    real MapMinX
    real MapMinY
endglobals


//==============================================================================
function GetSafeX takes real x returns real
    if x < MapMinX then
        return MapMinX
    endif
    if x > MapMaxX then
        return MapMaxX
    endif
    return x
endfunction

//==============================================================================
function GetSafeY takes real y returns real
    if y < MapMinY then
        return MapMinY
    endif
    if y > MapMaxY then
        return MapMaxY
    endif
    return y
endfunction

//==============================================================================
function SetUnitXSafe takes unit whichUnit, real x returns nothing
    if x < MapMinX then
        call SetUnitX(whichUnit, MapMinX)
    elseif x > MapMaxX then
        call SetUnitX(whichUnit, MapMaxX)
    else
        call SetUnitX(whichUnit, x)
    endif
endfunction

//==============================================================================
function SetUnitYSafe takes unit whichUnit, real y returns nothing
    if y < MapMinY then
        call SetUnitY(whichUnit, MapMinY)
    elseif y > MapMaxY then
        call SetUnitY(whichUnit, MapMaxY)
    else
        call SetUnitY(whichUnit, y)
    endif
endfunction

//==============================================================================
function SetUnitPositionSafe takes unit whichUnit, real x, real y returns nothing
    if x < MapMinX then
        call SetUnitX(whichUnit, MapMinX)
    elseif x > MapMaxX then
        call SetUnitX(whichUnit, MapMaxX)
    else
        call SetUnitX(whichUnit, x)
    endif
    
    if y < MapMinY then
        call SetUnitY(whichUnit, MapMinY)
    elseif y > MapMaxY then
        call SetUnitY(whichUnit, MapMaxY)
    else
        call SetUnitY(whichUnit, y)
    endif
endfunction


//===========================================================================
private function PreventLeaving takes nothing returns nothing
    local unit whichUnit = GetTriggerUnit()
    local real x = GetUnitX(whichUnit)
    local real y = GetUnitY(whichUnit)
    call SetUnitPositionSafe(whichUnit, x, y)
    
    debug call BJDebugMsg("Unit: " + GetUnitName(whichUnit) + " tried to leave playable map area.")
    set whichUnit = null
endfunction


//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    set MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set MapMinY = GetRectMinY(bj_mapInitialPlayableArea)    
    
    call TriggerRegisterLeaveRectSimple( trig, bj_mapInitialPlayableArea )
    call TriggerAddAction( trig, function PreventLeaving )
endfunction    

endlibrary
 
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