System Knockback System

Vestras

Retired
Reaction score
248
Knockback System!
Fully follows the JESP standart, is MUI and is the first Knockback System with Group Knocking!

JASS/GUI: JASS
vJASS: Yes
Systems used: Attachable Vars (Don't complain, I'm not gonna edit)
MUI: Yes


Code:
JASS:
//****************************************************************************
//                                 Knockback System                          *
//                                  Author: Vestras                          *
//****************************************************************************

//****************************************************************************
//                                     Credits:                              *
//                          vile1, for teaching me the method.               *
//                            Vexorian, for Attachable Vars.                 *
//****************************************************************************

//****************************************************************************
//                                  Requirements:                            *
//                                  This trigger.                            *
//                                 Attachable Vars.                          *
//****************************************************************************

//***************************************************************************************************
// Function list:                                                                                   *
//                                                                                                  *
// KS_SingleKnock(KnockedUnit, KnockingSpeed, KnockingRange, KnockingAngle)                         *
// Used for single units.                                                                           *
//                                                                                                  *
// KS_GroupKnock(KnockingLocation, KnockingCaster, KnockingGroupRange, KnockingSpeed, KnockingRange)*
// Used for a group knock.                                                                          *
//***************************************************************************************************

library KnockbackSystem requires AttachableVars

// Configuration

private constant function ShowSFX takes nothing returns boolean
    return true // Whether you want the sfx to be showed or not. False if not
endfunction

private constant function GetInterval takes nothing returns real
    return 0.03 // The real which the timer that runs the movement is runned
endfunction

private constant function GetDust takes nothing returns string
    return "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" // Path of the dust model (Land)
endfunction

private constant function KillDestructables takes nothing returns boolean
    return true // Whether or not if destructables should be destroyed. false if not.
endfunction

// End of configuration

// Custom functions

private constant function KillTrees_True takes nothing returns boolean
    return true // Needed
endfunction

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

private function KillTrees takes real x, real y returns nothing
local rect R=Rect(x-200.*.5,y-200.*.5,x+200.*.5,y+200.*.5)
    call EnumDestructablesInRect(R, Condition(function KillTrees_True), function KillTrees_Real)
    call RemoveRect(R)
    set R = null
endfunction

private function SetUnitXY takes unit u, real x, real y returns nothing
     if x<GetRectMaxX(bj_mapInitialPlayableArea) and x>GetRectMinX(bj_mapInitialPlayableArea) and y<GetRectMaxY(bj_mapInitialPlayableArea) and y>GetRectMinY(bj_mapInitialPlayableArea) then
       call SetUnitX(u,x)
       call SetUnitY(u,y)
     endif
endfunction

// End of custom functions

// Globals. Do not touch
globals
    private location L
    private unit uni
    private real angl
    private real spd
    private real rang
endglobals
// End of globals

//**************************************************
// WARNING WARNING WARNING WARNING WARNING WARNING *
// DO ONLY EDIT BELOW IF YOU KNOW WHAT YOU DO      *
// WARNING WARNING WARNING WARNING WARNING WARNING *
//**************************************************

private function AddEffect takes nothing returns nothing
    local timer tmr = GetExpiredTimer()
    local string st = GetAttachmentTable(tmr)
    local unit attchto = GetTableUnit(st, "Knockback_Target")
    local real x=GetUnitX(attchto)
    local real y=GetUnitY(attchto)
    local real break = 1.25-.15
    local real speed = GetTableReal(st, "Knockback_Speed")
    call SetTableReal(st, "Knockback_Speed", speed-break)
         call DestroyEffect(AddSpecialEffect(GetDust(),x,y))
     if speed <= 0. then
      call DestroyTimer(tmr)
    endif
    set tmr = null
    set attchto = null
endfunction

private function Movement takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local string s = GetAttachmentTable(t)
    local unit u = GetTableUnit(s, "Knockback_Target")
    local real cos = GetTableReal(s, "Knockback_Cos")
    local real sin = GetTableReal(s, "Knockback_Sin")
    local real speed = GetTableReal(s, "Knockback_Speed")
    local real x = GetUnitX(u) + speed * cos
    local real y = GetUnitY(u) + speed * sin
    local real break = 1.25-.15
    if KillDestructables() == true then
        call KillTrees(x, y)
    endif
    if speed > 0. then
        call SetUnitXY(u, x, y)
        call SetTableReal(s, "Knockback_Speed", speed-break)
    else
        call DestroyTimer(t)
    endif
    set t = null
    set u = null
endfunction

function KS_SingleKnock takes unit whichUnit, real speed, real range, real angle returns nothing
    local timer t = CreateTimer()
    local timer tmr = CreateTimer()
    local string s = GetAttachmentTable(t)
    local string st = GetAttachmentTable(tmr)
    call SetTableReal(s, "Knockback_Cos", Cos(angle*.017453))
    call SetTableReal(s, "Knockback_Sin", Sin(angle*.017453))
    call SetTableReal(s, "Knockback_Speed", speed)
    call SetTableObject(s, "Knockback_Target", whichUnit)
    call SetTableObject(st, "Knockback_Target", whichUnit)
    call SetTableReal(st, "Knockback_Speed", speed)
    call SetTableInt(st, "Knockback_Int", 1)
       
       call TimerStart(t, GetInterval(), true, function Movement)
       if ShowSFX() then
          call TimerStart(tmr, 0.03, true, function AddEffect)
        endif
    if GetTableReal(s, "Knockback_Speed") <= 0. then
       call DestroyTimer(tmr)
         call DestroyTimer(t)
         set tmr = null
       set t = null
    endif
endfunction

private function KS_FilterEnemies takes nothing returns boolean
    return IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) != true and GetWidgetLife(GetFilterUnit()) > .405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(uni)) == true
endfunction

private function KS_Group takes nothing returns nothing
local location n = GetUnitLoc(GetEnumUnit())
local real angl = bj_RADTODEG * Atan2(GetLocationY(n) - GetLocationY(L), GetLocationX(n) - GetLocationX(L))
   call KS_SingleKnock(GetEnumUnit(), spd, rang, angl)
 call RemoveLocation(n)
set n = null
endfunction

function KS_GroupKnock takes location l, unit caster, real range, real speed, real r returns nothing
     local group g = CreateGroup()
     local real x = GetLocationX(l)
     local real y = GetLocationY(l)
     set spd = speed
     set rang = r
     set uni = caster
     set L = l
        call GroupEnumUnitsInRange(g, x, y, range, Condition(function KS_FilterEnemies))
    call ForGroup(g, function KS_Group)
    call RemoveLocation(L)
    set uni = null
    set L = null
endfunction

endlibrary

Screenshot:


I put 3 days of hard work to this, so please enjoy!
 

Oninuva

You can change this now in User CP.
Reaction score
221
Explain what this does please. I see a stormbolt in the screenshot.
 

Cheesy

some fucker
Reaction score
95
Very nice, however with the storm bolt knock back ability, the unit is knocked back before the storm bolt reaches him.
 

Flare

Stops copies me!
Reaction score
662
Very nice, however with the storm bolt knock back ability, the unit is knocked back before the storm bolt reaches him.

That's because it's only a demonstration of what the system does... the Stormbolt doesn't need to hit the target before the knockback takes place, because that's not what the system is about.

If it was a system which caused homing projectiles to knock back their targets however, that'd be a different story
 

waaaks!

Zinctified
Reaction score
255
another knockback system, let me count

Vile's knockback system
Uberplayer
emjlr3
Rising_Dusk
and you
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
We all got our custom knockback systems, few of us acually decided to submit them.
 

Flare

Stops copies me!
Reaction score
662
Ahm... you never seem to use the range argument. Just searched the code, and the only instances of the word range in the whole code are in the documentation section, and in the function declarations i.e.
JASS:
function KS_SingleKnock takes unit whichUnit, real speed, real range, real angle returns nothing

function KS_GroupKnock takes location l, unit caster, real range, real speed, real r returns nothing


Also, you should pause the timer before destroying it (IIRC destroying a repeating timer without pausing it first will allow it to run the callback when the timer expires)
Timer tutorial said:
Destroying An Unfinished Timer
This issue is a little more subtle than some of the others. If in some function you call DestroyTimer() on a timer that might not have finished running, it can fail some handle indicies as well as cause bugs in-game. Fortunately, the fix is relatively simple.
(full tutorial here)

Also, the speed argument should be taken as speed/sec, not speed/interval IMO. Makes everything easier to use (since it allows you to easily determine the initial speed, rather than having to work it out for yourself)
 

Vestras

Retired
Reaction score
248
This uses like two timers. Not good at all.

Why not?

Flare >

Thanks, I didn't know that. Gonna fix it. I don't use the range? I'll remove then.

And you yourself determine the interval. Cannot see a prob in that?

We all got our custom knockback systems, few of us acually decided to submit them. >

QFT.

another knockback system, let me count

Vile's knockback system
Uberplayer
emjlr3
Rising_Dusk
and you >

Each one for each personality.

Originally Posted by CheesyBeefy
Very nice, however with the storm bolt knock back ability, the unit is knocked back before the storm bolt reaches him.

That's because it's only a demonstration of what the system does... the Stormbolt doesn't need to hit the target before the knockback takes place, because that's not what the system is about.

If it was a system which caused homing projectiles to knock back their targets however, that'd be a different story >

QFT.

Explain what this does please. I see a stormbolt in the screenshot. >

Hmmz. What is knockback? I think it makes the units jump! ^_^

Thanks for the replies.
 

Flare

Stops copies me!
Reaction score
662

Two timers are more prone to lag than 1 timer. Let's say you have... 5 slides running simultaneously (which is a very possible scenario). With a single timer per instance, that's 5 timers (obviously). With 2 timers per instance, that's 10 timers. Do you really want 10 timers running simultaneously, when you could, just as easily, do it with 1 timer per instance (or 1 timer for every instance).

And you yourself determine the interval. Cannot see a prob in that?

It's for ease of use. I'd rather do "That unit is gonna start sliding at 700 speed" than "That unit is gonna start sliding at... *insert math here* " (and I'm sure many others woud rather it too). Keep all the math stuff within the system :)

Systems -should- be easy to use for the end-users, and forcing them to do math doesn't make it easier to use :D
 

Vestras

Retired
Reaction score
248
Two timers are more prone to lag than 1 timer. Let's say you have... 5 slides running simultaneously (which is a very possible scenario). With a single timer per instance, that's 5 timers (obviously). With 2 timers per instance, that's 10 timers. Do you really want 10 timers running simultaneously, when you could, just as easily, do it with 1 timer per instance (or 1 timer for every instance).



It's for ease of use. I'd rather do "That unit is gonna start sliding at 700 speed" than "That unit is gonna start sliding at... *insert math here* " (and I'm sure many others woud rather it too). Keep all the math stuff within the system :)

Systems -should- be easy to use for the end-users, and forcing them to do math doesn't make it easier to use :D

It is easy? where do they need math?
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
I say remake the whole system with a constant interval and a single timer running the whole thing. That'd make it a whole lot better.
 

Flare

Stops copies me!
Reaction score
662
where do they need math?

To calculate the initial speed/sec. With your speed argument, you are taking speed/interval, and that by itself doesn't tell you how fast the unit will be starting at. If I want to put in a value of 700 speed for the initial speed, I'm gonna have to get my hands dirty and actually figure that out.

Whereas if the system took the speed as speed/sec, and calculated the speed/interval by itself, HUZZAH! Everything's just been made easier.

I say remake the whole system with a constant interval and a single timer running the whole thing. That'd make it a whole lot better.

The interval is constant :p And I already tried the "use a single timer" thing, didn't work :rolleyes:
 
Reaction score
456
If you don't want to have same period for the effect, you can use modulo.

Example:
JASS:
library ModuloExample

    globals
        private constant real PERIOD = 0.03125
    endglobals

    private struct Data
        real r = PERIOD
    endstruct

    private function Run_handler takes nothing returns nothing
        local Data dat = GetTimerAttachment(GetExpiredTimer())
        
        if ModuloReal(dat.r, PERIOD * 2) == 0 then
        endif
        set dat.r = dat.r + PERIOD
    endfunction
    
    function Run takes nothing returns nothing
        local timer t = CreateTimer()
        local Data dat = Data.create()
        
        call SetTimerAttachment(t, dat)
        call TimerStart(t, PERIOD, true, function Run_handler)
        
        set t = null
    endfunction

endlibrary

Don't know if that actually works (of course it gives syntax error for the function names). But the idea is that every second time the timer expires, the if-actions are ran.
 

Vestras

Retired
Reaction score
248
To calculate the initial speed/sec. With your speed argument, you are taking speed/interval, and that by itself doesn't tell you how fast the unit will be starting at. If I want to put in a value of 700 speed for the initial speed, I'm gonna have to get my hands dirty and actually figure that out.

Whereas if the system took the speed as speed/sec, and calculated the speed/interval by itself, HUZZAH! Everything's just been made easier.



The interval is constant :p And I already tried the "use a single timer" thing, didn't work :rolleyes:

> To calculate the initial speed/sec. With your speed argument, you are taking speed/interval, and that by itself doesn't tell you how fast the unit will be starting at. If I want to put in a value of 700 speed for the initial speed, I'm gonna have to get my hands dirty and actually figure that out.

People gets more and more lazy. Is it so hard to go to the Windows Calculater?

> The interval is constant :p And I already tried the "use a single timer" thing, didn't work :rolleyes:

Yeah.

> Uberplayer

I don't get it.
 

Flare

Stops copies me!
Reaction score
662
I wasn't complaining about GC, I was saying that you could use a struct and pass that to the cache instead of passing 7 different things to the cache.

Anyway, can you justify the extra effort required to calculate the speed/sec? Why should this be used over any other KB system if you don't make it as easy-to-use as possible?

I don't even know what the modulo funtions does
Modulo is just division but it gives a remainder e.g. 18Mod4 = 2 (since 18/4 only goes 4 times, and leaves a remainder of 2)
 
Reaction score
456
Okay so. Modulo is remainder of divident and divider.

Examples:
2 mod 2 = 0
3 mod 2 = 1
5 mod 2 = 1
5 mod 3 = 2
10 mod 2 = 0
11 mod 2 = 1
12 mod 3 = 0
15 mod 14 = 1

You could think that it divides the divident by divider as many times it can and returns the rest of it to you.

In my code I set the value of r to PERIOD (0.03125). Every time the Run_handler is ran, r is increased by PERIOD.

First time the function is ran, r's values is 0.03125 so the boolean is false (0.03125 mod 0.0625 != 0).

But second time the function is ran, r's value is increased by 0.03125 so, it's value is this time 0.0625. The boolean is true (0.0625 mod 0.0625 == 0).
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top