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

Userfriendliest and bugleast Knockback System

Level 11
Joined
Nov 4, 2007
Messages
337
peq - jass-Code #1327 //***************************************************************************************************************

I have seen many knockback systems yet.
But almost all GUI-Triggerers complained about them,
because they are too complicated.
Also the other ones have bugs.
Units can slide into water.
So I created a new Knockback System;
The safest Knockback-System.
The easiest one to implement.
And the easiest one to make it fit your needs.

So here my System and its propertie;

* ================================ *
* P R O S A N D C O N T R A S *
* ================================ *
compared to other knockback-systems
[+]
1. This knockback system is very userfriendly. You cant make any other knockback-system fit your
needs as easily as with this.
2. This knockback system is also very userfriendly for only GUI-users, because there are unique options
which make it easy with GUI-triggers to knockback units.
You dont need any custom scripts.
3. This system has a great performance.
It uses structs, clears them up, uses coordinates, rects to kill trees, nulls local variables,
creates only two dummies and keeps them, uses fast Attachment Systems (3 systems choosable.
All made by me) and so on.
4. The implemantion is very easy, because of the used libraries.
5. This system is clear of bugs.
When units hit trees, the trees get killed.
When units hit water, the knockback stops.
Units cant slide out of the map.
6. This systems has the most parameters.
Other Systems have a constant speed and friction and
you can just choose how long you want to knockback them
(range). With this system you can also chose the speed and
friction.

[-]
1. You have to create two dummy-units to use the knockback-system
2. There are faster systems


=======================================================
Help for non-JASSers

==== E N G L I S H ====
I prepared some GUI-functions for people who dont understand JASS.
These GUI-functions are found beneath 'Additional Service'.
In the trigger 'Global Knockback Parameters' there is a list with units.
If you want a unit to knockback another unit, you can put that unit
that shall knockback in the list and set Speed, Range and Friction
to the values you want.
Alternatively you can declare the variables with the prefix Glob
and run the trigger 'GlobalKnockBack' then.

==== D E U T S C H ====
Für Diejenigen, die kein JASS können, habe ich einige GUI-Funktionen
vorbereitet, welche unter 'Additional Service' gefunden werden können.
In dem Auslöser 'Global Knockback Parameters' befindet sich eine Liste mit
Einheiten. Wenn du willst, dass eine Einheit eine andere zurückschleudert,
kannst du diese Einheit in die dortige Liste eintragen, inklusive der Daten
'Speed', 'Range' und 'Friction'.
Alternativ kannst du auch die Variablen mit dem Präfix Glob bestimmen
und den Auslöser 'GlobalKnockBack' starten.

==== You should check the header* of the map and configure the Knockback_System ====
==== Du solltest unbedingt in den Header* der map schauen und das System konfigurieren ====

*header is the little scroll on top of all trigger folders with the name of your map.
*Der Header ist die kleine Rolle über allen Auslöser-Ordnern und hat den Namen deiner Map.

PLEASE GIVE CREDITS WHEN YOU USE THIS KNOCKBACK SYSTEM
BITTE GIB MIR CREDITS, WENN DU DIESES SYSTEM BENUTZT.

=================================================


And now the code:

JASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                         K N O C K B A C K                                                   *
//*                                            Actual Code                                                      *
//*                                               v1.27                                                         *
//*                                                                                                             *
//*                                           By: Mr.Malte                                                      *
//*                                                                                                             *
//***************************************************************************************************************
// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//¤  Requires JassNewGenPack Editor!
//¤  Knockback System v1.27
//¤  Requires swimming dummy 
//¤  Requires non-swimming dummy
//¤
//¤¤ USED SYSTEMS:
//¤
//¤     - My TSA  System; Timer Struct Attachment.
//¤     - My HA   System; Handle Attachment.
//¤     - My GCSA System; Game Cache Struct Attachment.
//¤ SPECIAL THANKS TO:
//¤
//¤     - Vexorian for JassNewGenPack
//¤     - Mueslirocker for SetUnitXY function
// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//!                 Note!
// If you want one of my systems, TimerStructAttachment or HandleAttachment, give credits!
//! GIVE CREDITS WHEN YOU USE THE KNOCKBACK SYSTEM!
// If you want to edit the knockback System, change the values of the section
// "Configurable Data".

function GlobalKnockBack takes nothing returns nothing
    call Knockback(udg_GlobOpfer, udg_GlobAtk, udg_GlobSpeed, udg_GlobDistanz, udg_GlobFriction, udg_GlobShowEffects)
endfunction

library KnockBackFunctions initializer KnockBackInit
globals
//                  * ================================== *
//                  * C O N F I G U R A B L E    D A T A *
//                  * ================================== *
    //!                  ====TIMER_INTERVAL====
    // A higher number will increase the performance
    // A lower number will make the knockback smoother
    // !Note!- if you increase the speed of the timer, the units wont slide faster.
    // The speed will be kept.
    //!                  ====DUST_DENSITY====
    // Every TIMER_INTERVAL * DUST_DENSITY seconds a dust effect is shown.
    // Decrease DUST_DENSITY to create more densitive dust (recommended for fast-speed-knockbacks)
    // Increase DUST_DENSITY to increase the performance ( recommended, when many units at the same time are knocked back)
    //!                   ====DUST_MODEL====
    // This Model is the dust that is shown. If you want to know the string of another model, go
    // to the object-editor, chose your model somewhere and press ok.
    // If you click on your model-field again, the string of the model is shown
    // at 'Own'.
    // Important: You must replace '\' in the string with '\\'
    //!                   ====DUMMY_TYPE[1]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //!                   ====DUMMY_TYPE[2]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //!                   ====DUMMY_TYPE[3]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //!                    ====ATTACHMENT_SYSTEM=====
    //!               JUST FOR FUNCTIOM 'PRECISE_KNOCKBACK'
    // This is the system which is used to attach the parameters
    // of the knockback to the timer that works with them.
    //   Possible options;
    //    "TimerStructAttachment"
    //    "HandleAttachment"
    //    "Game Cache"
    // If ATTACHMENT_SYSTEM is "TimerStructAttachment",
    // the Knockback-System is not as fast as with "HandleAttachment", but you can run 500 knockbacks (configurable)
    // at the same time without any bugs.
    // If ATTACHMENT_SYSTEM is "HandleAttachment",
    // the system is faster, that means less lag, but if
    // your map contains a lot of units and timers and blablabla. Handles.
    // Then bugs could appear.
    // Actually this system is the best choice, because of the speed.
    // But if the system says "HandleAttachment System is too full." somewhen,
    // you should swap to the TimerStructAttachment or Game Cache
    // If ATTACHMENT_SYSTEM is "Game Cache",
    // you use the savest, but also the slowest Attachment System.
    // You should only use this, if both systems, TSA and HA give error
    // messages.
    //!                    ====KILLTREE_RADIUS=====
    // Units destroy trees in this radius.
    // You can also use the function Precise_Knockback to make an individual KILLTRE_RADIUS for each knockback.
    //!                    ====PERCENTAGE_KNOCKBACK=====
    // This number should be 1.00
    // 1.00 means, that the speed and friction of all Knockbacks is
    // Speed*1.00 and Friction*1.00
    //!                    =====MOVEBACK=====
    // If a unit hits water, it gets moved back by this amount.
    // Choose a higher number if units stuck in water often.
    //!                    =====UNBLOCK_FACTOR=====
    // If this is 0, units slide through everything and kill trees.
    // If this is 1, units don't slide through water
    // If this is 2 or higher, units can't do anything while
    // They are knocked back, and the knockback stops if a unit
    // can't stand on the new point. That means the Knockback is stopped
    // by: Cliffs, units, water, doodads. By everything.
    // If the UNBLOCK_FACTOR is higher than 0 and the unit is not moved, the knockback
    // stops instantly.
    //!                   =====DISPLAYCREDITS_AFTER=====
    // After this real the game tells you that the Knockbacksystem was made by me.
    
private constant real           TIMER_INTERVAL       =  0.033
private constant integer        DUST_DENSITY         =  11
private constant string         DUST_MODEL           =  "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant boolean        REQUIRES_UNBLOCKED   =  true
private constant integer        DUMMY_TYPE1          =  'e000'
private constant integer        DUMMY_TYPE2          =  'e001'
private constant integer        DUMMY_TYPE3          =  'e002'
private constant real           KILLTREE_RADIUS      =  110.
private          real           PERCENTAGE_KNOCKBACK =  2.00
private constant real           MOVEBACK             =  30.
private constant integer        UNBLOCK_FACTOR       =  1
private constant string         AUTHOR               =  "Mr.Malte"
endglobals

private function h2i takes handle h returns integer
    return h
    return 0
endfunction
private function Debug takes string s returns nothing
call DisplayTextToForce(GetPlayersAll(),s)
endfunction
// End of Handle Attachment System

public struct KB
    boolean bool
    integer kbi
    integer up
    real    speed
    real    distanz
    real    friction
    real    slidet
    real    killtreerange
    real    sin
    real    cos
    unit    atk
    unit    opfer
endstruct

globals
    private real minx
    private real maxx
    private real miny
    private real maxy
    private integer CC
    private unit array Converted
    private integer Total = 0
    private KB array Knockbackdata
    private boolexpr Treekillcondition = null
    private timer KnockbackTimer = CreateTimer()
    private boolean IsTimerRunning = false
    private integer up = 0
    private boolean showdust = false
    private boolean Knock_GoOn = true
    private unit IsWalkableDummy = null
    private boolean IsStolen = false
endglobals

function SetUnitXY takes unit u, real x, real y returns boolean
    if x < minx then
    set u = null
        return false
    elseif x > maxx then
    set u = null
        return false
    else
        call SetUnitX(u, x)
    endif
    if y < miny then
    set u = null
        return false
    elseif y > maxy then
    set u = null
        return false
    else
        call SetUnitY (u, y)
    endif
        set u = null
    return false
endfunction

function IsPointWalkable takes real newx, real newy returns boolean
// SetUnitPosition setzt eine Einheit auf eine Stelle, wenn diese
// für die Einheit begehbar ist.
// Die Einheit wird also auf die neuen Koordinaten mit der Funktion
// gesetzt. Wenn die Koordinaten der Einheit nach SetUnitPosition
// newx und newy entsrpechen, kann die Einheit dort stehen.
    call SetUnitPosition(IsWalkableDummy,newx,newy)
    if ( GetUnitX(IsWalkableDummy) == newx and GetUnitY(IsWalkableDummy) == newy ) then
    return true
    endif
    return false
endfunction

private function IsWaterLoc takes real x, real y returns boolean
    call SetUnitPosition(udg_Knockback_Dummy1,x,y)
    call SetUnitPosition(udg_Knockback_Dummy2,x,y)
    if ( GetUnitX(udg_Knockback_Dummy1) == GetUnitX(udg_Knockback_Dummy2) and GetUnitY(udg_Knockback_Dummy1) == GetUnitY(udg_Knockback_Dummy2)) then
    return false
    endif
    return true
endfunction

public function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc'
endfunction

function UpdateTotal takes nothing returns nothing
    local integer i = 201
    loop
    exitwhen i == 0
    set i = i - 1
    if ( Knockbackdata[i] != null ) then
        set Total = i
        set i = 200
        return
    endif
    endloop
endfunction

private function KillTree takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction
// STRUCT FUNCTIONS
function PushUp_Structs takes integer fromwhere returns nothing
    local integer i = fromwhere
        loop
    set i = i + 1
    exitwhen i > Total
    set Knockbackdata[i-1] = Knockbackdata[i]
        endloop
    set Total = Total - 1
endfunction

function FreePlace_Structs takes integer ib returns nothing
    set Knockbackdata[ib] = Knockbackdata[Total]
    set Total = Total - 1
endfunction
// ENDSTRUCT FUNCTIONS
private function KnockbackCreate takes nothing returns nothing
    //local timer expired    =   GetExpiredTimer()  // Used for the system - means not important for you
    local integer structNR
    local KB knock         
    local integer ib        =   0                  // Used for the system
    local real x           =   0.                 // Used for the system
    local real y           =   0.                 // Used for the system
    local real newx        =   0.                 // Used for the system
    local real newy        =   0.                 // Used for the system
    local effect e         =   null               // Used for the system
    local real angle       =   0.                 // Used for the system
    local real speed       //=   knock.speed        // The speed the target slides with
    local real distanz     //=   knock.distanz      // Distance the target unit slides
    local boolean bool     //=   knock.bool         // Show effects? True and the effect is shown.
    local rect killtrees
    if ( IsStolen == true ) then
    return
    endif
    if (up == DUST_DENSITY) then // up is used to count up an integer. When the integer hits the constant integer DUST_DENSITY (basic 7), the dust is shown - to decrease lagg
            set up = 0
            set showdust = true
    endif
    
                                loop
                                
    set ib = ib + 1
    exitwhen ib > Total
    set Knock_GoOn = true
    set knock              =   Knockbackdata[ib]
    set speed = knock.speed
    set distanz = knock.distanz
    set bool = knock.bool
    set x                  =   GetUnitX(knock.opfer)
    set y                  =   GetUnitY(knock.opfer)
    set angle              =   Atan2(y - GetUnitY(knock.atk), x - GetUnitX(knock.atk))
    set killtrees          =   Rect(x - knock.killtreerange, y - knock.killtreerange, x + knock.killtreerange, y + knock.killtreerange)    
    set speed              =   speed - knock.friction
    set knock.speed        =   speed
    set knock.slidet       =   knock.slidet + speed
    set newx = x + speed * knock.cos
    set newy = y + speed * knock.sin
    call EnumDestructablesInRect(killtrees, Treekillcondition, function KillTree)
    call RemoveRect(killtrees)
    // Stop Knockback when unit hits water
        if ( UNBLOCK_FACTOR == 1 ) then
            if ( IsWaterLoc(newx,newy) ) then
            set newx = x - MOVEBACK * knock.cos
            set newy = y - MOVEBACK * knock.sin
            call SetUnitXY(knock.opfer, newx, newy)
            call PauseUnit(knock.opfer,false)
            call RemoveRect(killtrees)
            //set Knockbackdata[Total] = Knockbackdata[ib]
            //set Total = Total - 1
            set killtrees = null
            call knock.destroy()
            //set Total = Total - 1
            call FreePlace_Structs(ib)
            //call PushUp_Structs(ib)
            set Knock_GoOn = false
            //set expired = null
            endif
        endif
        if ( UNBLOCK_FACTOR == 2 ) then
            if ( IsPointWalkable(newx,newy) == false ) then
                call PauseUnit(knock.opfer,false)
                call RemoveRect(killtrees)
                set killtrees = null
                call knock.destroy()
                call FreePlace_Structs(ib)
                //call PushUp_Structs(ib)
                //set Total = Total - 1
                set Knock_GoOn = false
            endif
        endif
    // Change coordinates
    
                                if ( Knock_GoOn == true ) then
                                
    call SetUnitXY(knock.opfer, newx, newy)
    call RemoveRect(killtrees)
    set killtrees = null
    // Show dust
        if ( showdust ) then
        if ( knock.bool == true ) then
            call DestroyEffect(AddSpecialEffect(DUST_MODEL, x, y))
        endif
        endif
    // End knockback?
    if ( speed < 0.1 ) then
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            call FreePlace_Structs(ib)
            //call PushUp_Structs(ib)
            //set Total = Total - 1
    endif
    if ( knock.slidet > distanz ) then
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            call FreePlace_Structs(ib)
            //call PushUp_Structs(ib)
            //set Total = Total - 1
        endif
        
                                endif
                                endloop
                                
    
    if ( Total == 0 ) then
    call PauseTimer(KnockbackTimer)
    set IsTimerRunning = false
    endif
    set up = up + 1
    set showdust = false
endfunction

function Knockback takes unit opfer, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local KB knock      =    KB.create()
    local real angle
    set speed = (speed * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set friction = (friction * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set knock.opfer     =    opfer
    set knock.atk       =    atk
    set knock.speed     =    speed
    set knock.distanz   =    distanz
    set knock.slidet    =    10.
    set knock.friction  =    friction
    set knock.bool      =    b
    set knock.killtreerange = KILLTREE_RADIUS
    set Total = Total + 1
    set angle              =   Atan2(GetUnitY(opfer) - GetUnitY(atk), GetUnitX(opfer) - GetUnitX(atk))
    set knock.cos = Cos(angle)
    set knock.sin = Sin(angle)
    if ( Total > 200 ) then
        call UpdateTotal()
    endif
    set Knockbackdata[Total] = knock
    if ( IsTimerRunning == false ) then
    call TimerStart(KnockbackTimer, TIMER_INTERVAL, true, function KnockbackCreate)
    set IsTimerRunning = true
    endif
    set opfer = null
    set b = false
endfunction

function SetKnockbackFactor takes real newPercentage returns nothing
    set PERCENTAGE_KNOCKBACK = newPercentage
endfunction

private function KnockBackInit takes nothing returns nothing
    local location temppoint = null
    set minx = GetRectMinX(bj_mapInitialPlayableArea)
    set maxx = GetRectMaxX(bj_mapInitialPlayableArea)
    set miny = GetRectMinY(bj_mapInitialPlayableArea)
    set maxy = GetRectMaxY(bj_mapInitialPlayableArea)
        if ( 0 == 0 ) then
    set temppoint = GetRectCenter(GetPlayableMapRect())
    
    call CreateUnitAtLoc(Player(15),DUMMY_TYPE1,temppoint,270.)
    set udg_Knockback_Dummy1 = GetLastCreatedUnit()
    call CreateUnitAtLoc(Player(15),DUMMY_TYPE2,temppoint,270.)
    set udg_Knockback_Dummy2 = GetLastCreatedUnit()
    //set IsWalkableDummy = CreateUnitAtLoc(Player(15),DUMMY_TYPE3,temppoint,270.)
    call RemoveLocation(temppoint)
        endif
    set Treekillcondition = Filter(function TreeFilter)
endfunction

function Trig_Knockback_Init_Dummies_Func002A takes nothing returns nothing
    set udg_Knockback_Dummy2 = GetEnumUnit()
endfunction

function Trig_Knockback_Init_Dummies_Func003A takes nothing returns nothing
    set udg_Knockback_Dummy1 = GetEnumUnit()
endfunction

function Knockback_Init takes nothing returns nothing
    call ForGroupBJ( GetUnitsOfTypeIdAll(DUMMY_TYPE2), function Trig_Knockback_Init_Dummies_Func002A )
    call ForGroupBJ( GetUnitsOfTypeIdAll(DUMMY_TYPE1), function Trig_Knockback_Init_Dummies_Func003A )
endfunction

function ConvertToUnitArray takes nothing returns nothing
    set CC = CC + 1
    set Converted[CC] = GetEnumUnit()
endfunction

function KnockbackGroup takes group g, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local integer i = 0
    call ForGroup(g, function CovertToUnitArray)
    
    loop
    set i = i + 1
    exitwhen i > CC
    call Knockback(Converted[i],atk,speed,distanz,friction,b)
    set Converted[i] = null
    endloop
endfunction


endlibrary


// End of Knockback System by Mr.Malte

Important!
The example map:

http://www.speedyshare.com/700977292.html
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
816
- dont EVER use gamecache in this sort of libraries. use a stack for knockback structs and iterate through them, when a timer expires.
- dont destroy timers (i mean, how long do timer recycling systems exist? Use TimerUtils.)
- fucking indent properly. I cant follow your code.
- use debug keyword for debugging messages
- move performance related constants into globals
- dont use locations, if you already use vJass. Theres no need for those performance eaters
- your SetUnitXY is ... fail. How about using globals for min/max x/y? How about handling invalid values gracefully, instead of aborting the whole function?
- you dont clean up properly.
- i suggest you learn to love structs, especially their method syntax (including static methods)

This is probably the worst approach to a knockback system i have seen in ages (well, i didnt look at the GUI versions). I suggest a core rewrite to either use a static timer or at least TimerUtils. And this time dont screw up your deallocation algorithm, the one you currently use is ... bullshit (inefficient, unsafe and error-prone).
 
Level 11
Joined
Nov 4, 2007
Messages
337
1. Why not. If you like, you can change the global var 'AttachmentSystem'. Then you dont need the game cache
2. Why not destroy timers? I don't like code from others in my map.
3. KnockbackCreate is the important function
4. Ok. But why?
5. I use globals
6. Where do I use locations? I just use one location for the init.
So the system creates one location and destroys it for a whole game.
7. Hmm. Ok
8. What do I not clean?
9. What are static methods? But I use structs...
10. Ok. I am gonna change my deallocation-alghoritm
Have you testes the map?
Doesn't seem so.
In the map, I explain what is good about many timers.

What's Lmfao?
Ok. Maybe the performance of this system is bad, but performance is not all.
It is much more userfriendly than other systems.
Especially for GUI-users
If you dont want to run 600 knokbacks at the same time, this doe bot lag.

Edit:
Just read


//! ====ATTACHMENT_SYSTEM=====
// This is the system which is used to attach the parameters
// of the knockback to the timer that works with them.
// Possible options;
// "TimerStructAttachment"
// "HandleAttachment"
// "Game Cache"
// If ATTACHMENT_SYSTEM is "TimerStructAttachment",
// the Knockback-System is not as fast as with "HandleAttachment", but you can run 500 knockbacks (configurable)
// at the same time without any bugs.
// If ATTACHMENT_SYSTEM is "HandleAttachment",
// the system is faster, that means less lag, but if
// your map contains a lot of units and timers and blablabla. Handles.
// Then bugs could appear.
// Actually this system is the best choice, because of the speed.
// But if the system says "HandleAttachment System is too full." somewhen,
// you should swap to the TimerStructAttachment or Game Cache
// If ATTACHMENT_SYSTEM is "Game Cache",
// you use the savest, but also the slowest Attachment System.
// You should only use this, if both systems, TSA and HA give error
// messages.

edit 2: Did you actually read the comments?

Over Game Cache Attachment

// These functions were used to attach the struct to my timer,
// before I implemented the Timer Struct Attachment System made by me.
// I don't know, how effective the new system is. But it is bugfree
// and a lot more effective than game cache.
// I also have an alternative system which is also made by me but
// fast like hell. The bad thing about that system is, that it's susceptible
// to bugs.
 
Level 14
Joined
Nov 18, 2007
Messages
816
first off: ive seen other systems handling more than a thousand units sliding (without collision, thats something for physic engines). If this can barely handle 200 units (i guess so, because of if ( Total > 200 ) then), then youre using the wrong approach.

1.) This doesnt need an attachment system at all. Just look at other peoples code and learn from it.
2.) Where did you learn about coding in Jass? This was one of the first things i read about. Okay, there are some bugs that may occur when you destroy timers (but in your case, you dont even need to destroy a timer). Why use TimerUtils? Because its THE system for recycling timers. And can attach one integer to a timer. I suggest you search for it at wc3campaigns.net.
3.) You dont seem to get my point.
JASS:
    call SetUnitXY(knock.opfer, newx, newy)
    call RemoveRect(killtrees)
    set killtrees = null
        if (knock.up == DUST_DENSITY) then // up is used to count up an integer. When the integer hits the constant integer DUST_DENSITY (basic 7), the dust is shown - to decrease lagg
            set knock.up = 0
        if ( knock.bool == true ) then
            call DestroyEffect(AddSpecialEffect(DUST_MODEL, x, y))
        endif
        endif
    if ( speed < 0.1 ) then
Does this indentation follow ANY kind of logic?
4.) Cause i dont want to see debug messages popping up in my finished map.
5.) local real interval = 0.04 if ( Total > 200 ) then Liar. And those struck me first.
6.) Whats so bad about using coordinates? Oh, and your Init function is bugged. CreateUnitAtLoc() doesnt set bj_lastCreatedUnit.
8.) Go Figure. I bet youre gonna have some BIG problems once two units slide. If you cant figure out why, rewrite your code. And use a new approach.
9.) What you use are parallel arrays. Read about methods in the JassHelper manual.
10.) I dont care what you explain in your map. Your approach is wrong, and inefficient, and Knockback systems have been done much cleaner and faster already.

And no, i did NOT read your comments. The fact, that you destroy timers made me ignore them completely. And honestly, i dont care. I can read your code, and i have seen similar attachment systems and knockback systems already, so i know the basic workings of them.

Supporting GUI users is ... well, i wouldnt do it. They can learn (v)Jass too, you know? Its not like scripting in vJass is some kind of magic.
 
Level 11
Joined
Nov 4, 2007
Messages
337
If you don't care what I write in my map, then you wont understand the system at all.
In the map there are some GUI-Triggers which are important for the system.
if ( Total > 200 ) then
doesnt mean anything.
If total hits 200, the system can still run knockbacks, it just decreases the Total number.
So that the Knockbacks dont hit 8192.
8192 is the limitation of knockbacks.
6.) Whats so bad about using coordinates
I use coordinates :O
1.) I know different ways to make a knockback.
But just read:
[+/-]
1. The system creates for each knockback a single timer. I dont think
that is too bad, but it is not the fastest way possible.
But it could also be useful, because you can use different intervals
for knockbacks. If you want to make really fast knockbacks, you could
lower the timer expiration time for that knockback and keep it for
the other ones. This could make the performance compared to the prettiness
much better.
3.) Yes.
It is better performance. It doesn't show a special effect every time.
5.) local real interval = 0.04
is actually not needed.
Just for the PreciseKnockback funtion.
But it's highly rare that you have to use it.
8.) Check out the test map.
I have a Hellbeast with fireburn.
And I knockback all units in a range of 220.
There are no problems with many knockbacks.
9.) Ok
10.) Maybe cleaner and faster.
But not much.
And does it make a big difference?
Mine is much more userfriendly. Speed is not all.
Why do most people use ABC instead of HSAS?

Supporting GUI..
I guess 90% of the mappers use GUI.
Maybe you are right about the speed. But I also have another system which is faster.
I am gonna read the Manual about methods and I'll use a single timer.
Then I dont need TimerUtils.
 
Level 8
Joined
Aug 6, 2008
Messages
451
Yea, better to use static timer loop and struct arrays with single timer for stuff like this ( You have no use for attachment systems really ). And someone else just submitted a knockback system like that: link
 
Level 14
Joined
Nov 18, 2007
Messages
816
JASS:
library Test initializer Init
    
    globals
        private constant real TICK=0.03125 // 32/sec
    endglobals
    
    globals
        private timer T
    endglobals
    
    private struct KockData
        // blah blah insert your needed variables here
        integer i
        
        static KnockData array Structs
        static integer Count=0
        
        static method Callback takes nothing returns nothing
        local integer i=0
            loop
                exitwhen i>=KnockData.Count
                //your code here
                set i=i+1
            endloop
        endmethod
        
        static method Create takes nothing returns KnockData // you might want to adjust the parameters
        local KnockData s=KnockData.allocate()
            //do some shit.
            set s.i=KnockData.Count
            
            if KnockData.Count==0 then
                call TimerStart(T, TICK, true, function KnockData.Callback)
            endif
            set KnockData.Structs[KnockData.Count]=s
            set KnockData.Count=KnockData.Count+1
            return s
        endmethod
        
        method onDestroy takes nothing returns nothing
            // clean up your struct here
            set KnockData.Count=KnockData.Count-1
            set KnockData.Structs[.i]=KnockData.Structs[KnockData.Count]
            set KnockData.Structs[.i].i=.i
            if KnockData.Count==0 then
                call PauseTimer(T)
            endif
        endmethod
    endstruct
    
    private function Init takes nothing returns nothing
        set T=CreateTimer()
    endfunction
    
endlibrary
Maybe use this template. This should work and is afaik the fastest way to do it.

Edit: i was about to write my own Knockback system anyway, so this comes in handy for me as well.

Another Edit:
[...]Speed is not all.
Why do most people use ABC instead of HSAS?[...]
Because ABC was the first system ever using a hashing algorithm. ABC is rock solid, whereas HSAS is for speed freaks. And if you ask me, you dont need to use ABC or HSAS in most situations (PUI or TimerUtils are faster anyway, though they are limited to specific handles).

And yes, if the second knockback lasts longer than the first, and theyre allocated linearly, then the third knockback will overwrite the second, when started before the second one ends.

you remove killrects twice (KnockbackCreate).
you leak t's handle id (Knockback, PreciseKnockback).
you leak temppoints's handle id (Init).
 
Last edited:
Level 11
Joined
Nov 4, 2007
Messages
337
I still dont know why I should use methods, but I remade the core:
JASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                         K N O C K B A C K                                                   *
//*                                            Actual Code                                                      *
//*                                               v1.25                                                         *
//*                                                                                                             *
//*                                           By: Mr.Malte                                                      *
//*                                                                                                             *
//***************************************************************************************************************
// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//¤  Requires JassNewGenPack Editor!
//¤  Knockback System v1.24
//¤  Requires swimming dummy 
//¤  Requires non-swimming dummy
//¤
//¤¤ USED SYSTEMS:
//¤
//¤     - My TSA  System; Timer Struct Attachment.
//¤     - My HA   System; Handle Attachment.
//¤     - My GCSA System; Game Cache Struct Attachment.
//¤ SPECIAL THANKS TO:
//¤
//¤     - Vexorian for JassNewGenPack
//¤     - Cedi for SetUnitXY function
// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//!                 Note!
// If you want one of my systems, TimerStructAttachment or HandleAttachment, give credits!
//! GIVE CREDITS WHEN YOU USE THE KNOCKBACK SYSTEM!
// If you want to edit the knockback System, change the values of the section
// "Configurable Data".

function GlobalKnockBack takes nothing returns nothing
    call Knockback(udg_GlobOpfer, udg_GlobAtk, udg_GlobSpeed, udg_GlobDistanz, udg_GlobFriction, udg_GlobShowEffects)
endfunction

library KnockBackFunctions initializer KnockBackInit
globals
//                  * ================================== *
//                  * C O N F I G U R A B L E    D A T A *
//                  * ================================== *
    //!                  ====TIMER_INTERVAL====
    // A higher number will increase the performance
    // A lower number will make the knockback smoother
    // !Note!- if you increase the speed of the timer, the units wont slide faster.
    // The speed will be kept.
    //!                  ====DUST_DENSITY====
    // Every TIMER_INTERVAL * DUST_DENSITY seconds a dust effect is shown.
    // Decrease DUST_DENSITY to create more densitive dust (recommended for fast-speed-knockbacks)
    // Increase DUST_DENSITY to increase the performance ( recommended, when many units at the same time are knocked back)
    //!                   ====DUST_MODEL====
    // This Model is the dust that is shown. If you want to know the string of another model, go
    // to the object-editor, chose your model somewhere and press ok.
    // If you click on your model-field again, the string of the model is shown
    // at 'Own'.
    // Important: You must replace '\' in the string with '\\'
    //!                    ====REQUIRES_UNBLOCKED=====
    // If this boolean is true, the unit gets only moved if the new location of the unit is not blocked.
    // That would prevent that units slide into water, but they can still slide on cliffs.
    // If the new position of the unit is blocked and the boolean is true, the knockback stops instantly.
    //!                   ====DUMMY_TYPE[1]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    //!                   ====DUMMY_TYPE[2]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    //!                    ====KILLTREE_RADIUS=====
    // Units destroy trees in this radius.
    // You can also use the function Precise_Knockback to make an individual KILLTRE_RADIUS for each knockback.
    //!                    ====PERCENTAGE_KNOCKBACK=====
    // This number should be 1.00
    // 1.00 means, that the speed and friction of all Knockbacks is
    // Speed*1.00 and Friction*1.00
    //!                    =====MOVEBACK=====
    // If a unit hits water, it gets moved back by this amount.
    // Choose a higher number if units stuck in water often.
    
private constant real           TIMER_INTERVAL       =  0.035
private constant integer        DUST_DENSITY         =  11
private constant string         DUST_MODEL           =  "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant boolean        REQUIRES_UNBLOCKED   =  true
private constant integer        DUMMY_TYPE1          =  'e000'
private constant integer        DUMMY_TYPE2          =  'e001'
private constant real           KILLTREE_RADIUS      =  110.
private constant real           PERCENTAGE_KNOCKBACK =  2.00
private constant real           MOVEBACK             =  30.
endglobals

private function h2i takes handle h returns integer
    return h
    return 0
endfunction
private function Debug takes string s returns nothing
call DisplayTextToForce(GetPlayersAll(),s)
endfunction
// End of Handle Attachment System

public struct KB
    boolean bool
    integer kbi
    integer up
    real    speed
    real    distanz
    real    friction
    real    slidet
    real    killtreerange
    real    sin
    real    cos
    unit    atk
    unit    opfer
endstruct

globals
    private real minx
    private real maxx
    private real miny
    private real maxy
    private integer Total = 0
    private KB array Knockbackdata
    private boolexpr Treekillcondition = null
    private timer KnockbackTimer = CreateTimer()
    private boolean IsTimerRunning = false
    private integer up = 0
    private boolean showdust = false
endglobals

function SetUnitXY takes unit u, real x, real y returns boolean
    if x < minx then
        return false
    elseif x > maxx then
        return false
    else
        call SetUnitX(u, x)
    endif
    if y < miny then
        return false
    elseif y > maxy then
        return false
    else
        call SetUnitY (u, y)
    endif
    return false
endfunction

private function IsWaterLoc takes real x, real y returns boolean
    call SetUnitPosition(udg_Knockback_Dummy1,x,y)
    call SetUnitPosition(udg_Knockback_Dummy2,x,y)
    if ( GetUnitX(udg_Knockback_Dummy1) == GetUnitX(udg_Knockback_Dummy2) and GetUnitY(udg_Knockback_Dummy1) == GetUnitY(udg_Knockback_Dummy2)) then
    return false
    endif
    return true
endfunction

public function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc'
endfunction

function UpdateTotal takes nothing returns nothing
    local integer i = 201
    loop
    exitwhen i == 0
    set i = i - 1
    if ( Knockbackdata[i] != null ) then
        set Total = i
        set i = 200
        return
    endif
    endloop
endfunction

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

private function KnockbackCreate takes nothing returns nothing
    local timer expired    =   GetExpiredTimer()  // Used for the system - means not important for you
    local integer structNR
    local KB knock         
    local integer ib        =   0                  // Used for the system
    local real x           =   0.                 // Used for the system
    local real y           =   0.                 // Used for the system
    local real newx        =   0.                 // Used for the system
    local real newy        =   0.                 // Used for the system
    local effect e         =   null               // Used for the system
    local real angle       =   0.                 // Used for the system
    local real speed       //=   knock.speed        // The speed the target slides with
    local real distanz     //=   knock.distanz      // Distance the target unit slides
    local boolean bool     //=   knock.bool         // Show effects? True and the effect is shown.
    local rect killtrees
    if (up == DUST_DENSITY) then // up is used to count up an integer. When the integer hits the constant integer DUST_DENSITY (basic 7), the dust is shown - to decrease lagg
            set up = 0
            set showdust = true
    endif
    
                                loop
                                
    set ib = ib + 1
    exitwhen ib > Total
    set knock              =   Knockbackdata[ib]
    set speed = knock.speed
    set distanz = knock.distanz
    set bool = knock.bool
    set x                  =   GetUnitX(knock.opfer)
    set y                  =   GetUnitY(knock.opfer)
    set angle              =   Atan2(y - GetUnitY(knock.atk), x - GetUnitX(knock.atk))
    set killtrees          =   Rect(x - knock.killtreerange, y - knock.killtreerange, x + knock.killtreerange, y + knock.killtreerange)    
    set speed              =   speed - knock.friction
    set knock.speed        =   speed
    set knock.slidet       =   knock.slidet + speed
    set newx = x + speed * knock.cos
    set newy = y + speed * knock.sin
    call EnumDestructablesInRect(killtrees, Treekillcondition, function KillTree)
    call RemoveRect(killtrees)
    // Stop Knockback when unit hits water
        if ( REQUIRES_UNBLOCKED == true ) then
            if ( IsWaterLoc(newx,newy) ) then
            set newx = x - MOVEBACK * knock.cos
            set newy = y - MOVEBACK * knock.sin
            call SetUnitXY(knock.opfer, newx, newy)
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            set Total = Total - 1
            set expired = null
            endif
        endif
    // Change coordinates
    call SetUnitXY(knock.opfer, newx, newy)
    call RemoveRect(killtrees)
    set killtrees = null
    // Show dust
        if ( showdust ) then
        if ( knock.bool == true ) then
            call DestroyEffect(AddSpecialEffect(DUST_MODEL, x, y))
        endif
        endif
    // End knockback?
    if ( speed < 0.1 ) then
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            set Total = Total - 1
    endif
    if ( knock.slidet > distanz ) then
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            set Total = Total - 1
        endif
        
                                endloop
                                
    
    if ( Total == 0 ) then
    call PauseTimer(KnockbackTimer)
    set IsTimerRunning = false
    endif
    set up = up + 1
    set showdust = false
endfunction

function Knockback takes unit opfer, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local KB knock      =    KB.create()
    local real angle
    set speed = (speed * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set friction = (friction * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set knock.opfer     =    opfer
    set knock.atk       =    atk
    set knock.speed     =    speed
    set knock.distanz   =    distanz
    set knock.slidet    =    10.
    set knock.friction  =    friction
    set knock.bool      =    b
    set knock.killtreerange = KILLTREE_RADIUS
    set Total = Total + 1
    set angle              =   Atan2(GetUnitY(opfer) - GetUnitY(atk), GetUnitX(opfer) - GetUnitX(atk))
    set knock.cos = Cos(angle)
    set knock.sin = Sin(angle)
    if ( Total > 200 ) then
        call UpdateTotal()
    endif
    set Knockbackdata[Total] = knock
    if ( IsTimerRunning == false ) then
    call TimerStart(KnockbackTimer, TIMER_INTERVAL, true, function KnockbackCreate)
    set IsTimerRunning = true
    endif
    set opfer = null
    set b = false
endfunction

private function KnockBackInit takes nothing returns nothing
    set minx = GetRectMinX(bj_mapInitialPlayableArea)
    set maxx = GetRectMaxX(bj_mapInitialPlayableArea)
    set miny = GetRectMinY(bj_mapInitialPlayableArea)
    set maxy = GetRectMaxY(bj_mapInitialPlayableArea)
    local location temppoint = null
    call Debug("Knockback System by Mr.Malte")
        if ( REQUIRES_UNBLOCKED ) then
    set temppoint = GetRectCenter(GetPlayableMapRect())
    call CreateUnitAtLoc(Player(15),DUMMY_TYPE1,temppoint,270.)
    set udg_Knockback_Dummy1 = GetLastCreatedUnit()
    call CreateUnitAtLoc(Player(15),DUMMY_TYPE2,temppoint,270.)
    set udg_Knockback_Dummy2 = GetLastCreatedUnit()
    call RemoveLocation(temppoint)
        endif
    set Treekillcondition = Filter(function TreeFilter)
endfunction
endlibrary


// End of Knockback System by Mr.Malte

Is this code better?

edit: 10 mins.
The Ill have changed the SetUnitXY function.
 
Level 8
Joined
Aug 6, 2008
Messages
451
Methods just make your code easier to read. You dont really need to use them for anything.

( Unless you are working with interfaces and stuff like that. )
 
Level 11
Joined
Nov 4, 2007
Messages
337
New Version.
JASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                         K N O C K B A C K                                                   *
//*                                            Actual Code                                                      *
//*                                               v1.27                                                         *
//*                                                                                                             *
//*                                           By: Mr.Malte                                                      *
//*                                                                                                             *
//***************************************************************************************************************
// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//¤  Requires JassNewGenPack Editor!
//¤  Knockback System v1.27
//¤  Requires swimming dummy 
//¤  Requires non-swimming dummy
//¤
//¤¤ USED SYSTEMS:
//¤
//¤     - My TSA  System; Timer Struct Attachment.
//¤     - My HA   System; Handle Attachment.
//¤     - My GCSA System; Game Cache Struct Attachment.
//¤ SPECIAL THANKS TO:
//¤
//¤     - Vexorian for JassNewGenPack
//¤     - Mueslirocker for SetUnitXY function
// ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//!                 Note!
// If you want one of my systems, TimerStructAttachment or HandleAttachment, give credits!
//! GIVE CREDITS WHEN YOU USE THE KNOCKBACK SYSTEM!
// If you want to edit the knockback System, change the values of the section
// "Configurable Data".

function GlobalKnockBack takes nothing returns nothing
    call Knockback(udg_GlobOpfer, udg_GlobAtk, udg_GlobSpeed, udg_GlobDistanz, udg_GlobFriction, udg_GlobShowEffects)
endfunction

library KnockBackFunctions initializer KnockBackInit
globals
//                  * ================================== *
//                  * C O N F I G U R A B L E    D A T A *
//                  * ================================== *
    //!                  ====TIMER_INTERVAL====
    // A higher number will increase the performance
    // A lower number will make the knockback smoother
    // !Note!- if you increase the speed of the timer, the units wont slide faster.
    // The speed will be kept.
    //!                  ====DUST_DENSITY====
    // Every TIMER_INTERVAL * DUST_DENSITY seconds a dust effect is shown.
    // Decrease DUST_DENSITY to create more densitive dust (recommended for fast-speed-knockbacks)
    // Increase DUST_DENSITY to increase the performance ( recommended, when many units at the same time are knocked back)
    //!                   ====DUST_MODEL====
    // This Model is the dust that is shown. If you want to know the string of another model, go
    // to the object-editor, chose your model somewhere and press ok.
    // If you click on your model-field again, the string of the model is shown
    // at 'Own'.
    // Important: You must replace '\' in the string with '\\'
    //!                   ====DUMMY_TYPE[1]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //!                   ====DUMMY_TYPE[2]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //!                   ====DUMMY_TYPE[3]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //!                    ====ATTACHMENT_SYSTEM=====
    //!               JUST FOR FUNCTIOM 'PRECISE_KNOCKBACK'
    // This is the system which is used to attach the parameters
    // of the knockback to the timer that works with them.
    //   Possible options;
    //    "TimerStructAttachment"
    //    "HandleAttachment"
    //    "Game Cache"
    // If ATTACHMENT_SYSTEM is "TimerStructAttachment",
    // the Knockback-System is not as fast as with "HandleAttachment", but you can run 500 knockbacks (configurable)
    // at the same time without any bugs.
    // If ATTACHMENT_SYSTEM is "HandleAttachment",
    // the system is faster, that means less lag, but if
    // your map contains a lot of units and timers and blablabla. Handles.
    // Then bugs could appear.
    // Actually this system is the best choice, because of the speed.
    // But if the system says "HandleAttachment System is too full." somewhen,
    // you should swap to the TimerStructAttachment or Game Cache
    // If ATTACHMENT_SYSTEM is "Game Cache",
    // you use the savest, but also the slowest Attachment System.
    // You should only use this, if both systems, TSA and HA give error
    // messages.
    //!                    ====KILLTREE_RADIUS=====
    // Units destroy trees in this radius.
    // You can also use the function Precise_Knockback to make an individual KILLTRE_RADIUS for each knockback.
    //!                    ====PERCENTAGE_KNOCKBACK=====
    // This number should be 1.00
    // 1.00 means, that the speed and friction of all Knockbacks is
    // Speed*1.00 and Friction*1.00
    //!                    =====MOVEBACK=====
    // If a unit hits water, it gets moved back by this amount.
    // Choose a higher number if units stuck in water often.
    //!                    =====UNBLOCK_FACTOR=====
    // If this is 0, units slide through everything and kill trees.
    // If this is 1, units don't slide through water
    // If this is 2 or higher, units can't do anything while
    // They are knocked back, and the knockback stops if a unit
    // can't stand on the new point. That means the Knockback is stopped
    // by: Cliffs, units, water, doodads. By everything.
    // If the UNBLOCK_FACTOR is higher than 0 and the unit is not moved, the knockback
    // stops instantly.
    //!                   =====DISPLAYCREDITS_AFTER=====
    // After this real the game tells you that the Knockbacksystem was made by me.
    
private constant real           TIMER_INTERVAL       =  0.033
private constant integer        DUST_DENSITY         =  11
private constant string         DUST_MODEL           =  "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant boolean        REQUIRES_UNBLOCKED   =  true
private constant integer        DUMMY_TYPE1          =  'e000'
private constant integer        DUMMY_TYPE2          =  'e001'
private constant integer        DUMMY_TYPE3          =  'e002'
private constant real           KILLTREE_RADIUS      =  110.
private          real           PERCENTAGE_KNOCKBACK =  2.00
private constant real           MOVEBACK             =  30.
private constant integer        UNBLOCK_FACTOR       =  1
private constant string         AUTHOR               =  "Mr.Malte"
endglobals

private function h2i takes handle h returns integer
    return h
    return 0
endfunction
private function Debug takes string s returns nothing
call DisplayTextToForce(GetPlayersAll(),s)
endfunction
// End of Handle Attachment System

public struct KB
    boolean bool
    integer kbi
    integer up
    real    speed
    real    distanz
    real    friction
    real    slidet
    real    killtreerange
    real    sin
    real    cos
    unit    atk
    unit    opfer
endstruct

globals
    private real minx
    private real maxx
    private real miny
    private real maxy
    private integer CC
    private unit array Converted
    private integer Total = 0
    private KB array Knockbackdata
    private boolexpr Treekillcondition = null
    private timer KnockbackTimer = CreateTimer()
    private boolean IsTimerRunning = false
    private integer up = 0
    private boolean showdust = false
    private boolean Knock_GoOn = true
    private unit IsWalkableDummy = null
    private boolean IsStolen = false
endglobals

function SetUnitXY takes unit u, real x, real y returns boolean
    if x < minx then
    set u = null
        return false
    elseif x > maxx then
    set u = null
        return false
    else
        call SetUnitX(u, x)
    endif
    if y < miny then
    set u = null
        return false
    elseif y > maxy then
    set u = null
        return false
    else
        call SetUnitY (u, y)
    endif
        set u = null
    return false
endfunction

function IsPointWalkable takes real newx, real newy returns boolean
// SetUnitPosition setzt eine Einheit auf eine Stelle, wenn diese
// für die Einheit begehbar ist.
// Die Einheit wird also auf die neuen Koordinaten mit der Funktion
// gesetzt. Wenn die Koordinaten der Einheit nach SetUnitPosition
// newx und newy entsrpechen, kann die Einheit dort stehen.
    call SetUnitPosition(IsWalkableDummy,newx,newy)
    if ( GetUnitX(IsWalkableDummy) == newx and GetUnitY(IsWalkableDummy) == newy ) then
    return true
    endif
    return false
endfunction

private function IsWaterLoc takes real x, real y returns boolean
    call SetUnitPosition(udg_Knockback_Dummy1,x,y)
    call SetUnitPosition(udg_Knockback_Dummy2,x,y)
    if ( GetUnitX(udg_Knockback_Dummy1) == GetUnitX(udg_Knockback_Dummy2) and GetUnitY(udg_Knockback_Dummy1) == GetUnitY(udg_Knockback_Dummy2)) then
    return false
    endif
    return true
endfunction

public function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc'
endfunction

function UpdateTotal takes nothing returns nothing
    local integer i = 201
    loop
    exitwhen i == 0
    set i = i - 1
    if ( Knockbackdata[i] != null ) then
        set Total = i
        set i = 200
        return
    endif
    endloop
endfunction

private function KillTree takes nothing returns nothing
    call KillDestructable(GetEnumDestructable())
endfunction
// STRUCT FUNCTIONS
function PushUp_Structs takes integer fromwhere returns nothing
    local integer i = fromwhere
        loop
    set i = i + 1
    exitwhen i > Total
    set Knockbackdata[i-1] = Knockbackdata[i]
        endloop
    set Total = Total - 1
endfunction

function FreePlace_Structs takes integer ib returns nothing
    set Knockbackdata[ib] = Knockbackdata[Total]
    set Total = Total - 1
endfunction
// ENDSTRUCT FUNCTIONS
private function KnockbackCreate takes nothing returns nothing
    //local timer expired    =   GetExpiredTimer()  // Used for the system - means not important for you
    local integer structNR
    local KB knock         
    local integer ib        =   0                  // Used for the system
    local real x           =   0.                 // Used for the system
    local real y           =   0.                 // Used for the system
    local real newx        =   0.                 // Used for the system
    local real newy        =   0.                 // Used for the system
    local effect e         =   null               // Used for the system
    local real angle       =   0.                 // Used for the system
    local real speed       //=   knock.speed        // The speed the target slides with
    local real distanz     //=   knock.distanz      // Distance the target unit slides
    local boolean bool     //=   knock.bool         // Show effects? True and the effect is shown.
    local rect killtrees
    if ( IsStolen == true ) then
    return
    endif
    if (up == DUST_DENSITY) then // up is used to count up an integer. When the integer hits the constant integer DUST_DENSITY (basic 7), the dust is shown - to decrease lagg
            set up = 0
            set showdust = true
    endif
    
                                loop
                                
    set ib = ib + 1
    exitwhen ib > Total
    set Knock_GoOn = true
    set knock              =   Knockbackdata[ib]
    set speed = knock.speed
    set distanz = knock.distanz
    set bool = knock.bool
    set x                  =   GetUnitX(knock.opfer)
    set y                  =   GetUnitY(knock.opfer)
    set angle              =   Atan2(y - GetUnitY(knock.atk), x - GetUnitX(knock.atk))
    set killtrees          =   Rect(x - knock.killtreerange, y - knock.killtreerange, x + knock.killtreerange, y + knock.killtreerange)    
    set speed              =   speed - knock.friction
    set knock.speed        =   speed
    set knock.slidet       =   knock.slidet + speed
    set newx = x + speed * knock.cos
    set newy = y + speed * knock.sin
    call EnumDestructablesInRect(killtrees, Treekillcondition, function KillTree)
    call RemoveRect(killtrees)
    // Stop Knockback when unit hits water
        if ( UNBLOCK_FACTOR == 1 ) then
            if ( IsWaterLoc(newx,newy) ) then
            set newx = x - MOVEBACK * knock.cos
            set newy = y - MOVEBACK * knock.sin
            call SetUnitXY(knock.opfer, newx, newy)
            call PauseUnit(knock.opfer,false)
            call RemoveRect(killtrees)
            //set Knockbackdata[Total] = Knockbackdata[ib]
            //set Total = Total - 1
            set killtrees = null
            call knock.destroy()
            //set Total = Total - 1
            call FreePlace_Structs(ib)
            //call PushUp_Structs(ib)
            set Knock_GoOn = false
            //set expired = null
            endif
        endif
        if ( UNBLOCK_FACTOR == 2 ) then
            if ( IsPointWalkable(newx,newy) == false ) then
                call PauseUnit(knock.opfer,false)
                call RemoveRect(killtrees)
                set killtrees = null
                call knock.destroy()
                call FreePlace_Structs(ib)
                //call PushUp_Structs(ib)
                //set Total = Total - 1
                set Knock_GoOn = false
            endif
        endif
    // Change coordinates
    
                                if ( Knock_GoOn == true ) then
                                
    call SetUnitXY(knock.opfer, newx, newy)
    call RemoveRect(killtrees)
    set killtrees = null
    // Show dust
        if ( showdust ) then
        if ( knock.bool == true ) then
            call DestroyEffect(AddSpecialEffect(DUST_MODEL, x, y))
        endif
        endif
    // End knockback?
    if ( speed < 0.1 ) then
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            call FreePlace_Structs(ib)
            //call PushUp_Structs(ib)
            //set Total = Total - 1
    endif
    if ( knock.slidet > distanz ) then
            call PauseUnit(knock.opfer,false)
            call knock.destroy()
            call FreePlace_Structs(ib)
            //call PushUp_Structs(ib)
            //set Total = Total - 1
        endif
        
                                endif
                                endloop
                                
    
    if ( Total == 0 ) then
    call PauseTimer(KnockbackTimer)
    set IsTimerRunning = false
    endif
    set up = up + 1
    set showdust = false
endfunction

function Knockback takes unit opfer, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local KB knock      =    KB.create()
    local real angle
    set speed = (speed * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set friction = (friction * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set knock.opfer     =    opfer
    set knock.atk       =    atk
    set knock.speed     =    speed
    set knock.distanz   =    distanz
    set knock.slidet    =    10.
    set knock.friction  =    friction
    set knock.bool      =    b
    set knock.killtreerange = KILLTREE_RADIUS
    set Total = Total + 1
    set angle              =   Atan2(GetUnitY(opfer) - GetUnitY(atk), GetUnitX(opfer) - GetUnitX(atk))
    set knock.cos = Cos(angle)
    set knock.sin = Sin(angle)
    if ( Total > 200 ) then
        call UpdateTotal()
    endif
    set Knockbackdata[Total] = knock
    if ( IsTimerRunning == false ) then
    call TimerStart(KnockbackTimer, TIMER_INTERVAL, true, function KnockbackCreate)
    set IsTimerRunning = true
    endif
    set opfer = null
    set b = false
endfunction

function SetKnockbackFactor takes real newPercentage returns nothing
    set PERCENTAGE_KNOCKBACK = newPercentage
endfunction

private function KnockBackInit takes nothing returns nothing
    local location temppoint = null
    set minx = GetRectMinX(bj_mapInitialPlayableArea)
    set maxx = GetRectMaxX(bj_mapInitialPlayableArea)
    set miny = GetRectMinY(bj_mapInitialPlayableArea)
    set maxy = GetRectMaxY(bj_mapInitialPlayableArea)
        if ( 0 == 0 ) then
    set temppoint = GetRectCenter(GetPlayableMapRect())
    
    call CreateUnitAtLoc(Player(15),DUMMY_TYPE1,temppoint,270.)
    set udg_Knockback_Dummy1 = GetLastCreatedUnit()
    call CreateUnitAtLoc(Player(15),DUMMY_TYPE2,temppoint,270.)
    set udg_Knockback_Dummy2 = GetLastCreatedUnit()
    //set IsWalkableDummy = CreateUnitAtLoc(Player(15),DUMMY_TYPE3,temppoint,270.)
    call RemoveLocation(temppoint)
        endif
    set Treekillcondition = Filter(function TreeFilter)
endfunction

function Trig_Knockback_Init_Dummies_Func002A takes nothing returns nothing
    set udg_Knockback_Dummy2 = GetEnumUnit()
endfunction

function Trig_Knockback_Init_Dummies_Func003A takes nothing returns nothing
    set udg_Knockback_Dummy1 = GetEnumUnit()
endfunction

function Knockback_Init takes nothing returns nothing
    call ForGroupBJ( GetUnitsOfTypeIdAll(DUMMY_TYPE2), function Trig_Knockback_Init_Dummies_Func002A )
    call ForGroupBJ( GetUnitsOfTypeIdAll(DUMMY_TYPE1), function Trig_Knockback_Init_Dummies_Func003A )
endfunction

function ConvertToUnitArray takes nothing returns nothing
    set CC = CC + 1
    set Converted[CC] = GetEnumUnit()
endfunction

function KnockbackGroup takes group g, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local integer i = 0
    call ForGroup(g, function CovertToUnitArray)
    
    loop
    set i = i + 1
    exitwhen i > CC
    call Knockback(Converted[i],atk,speed,distanz,friction,b)
    set Converted[i] = null
    endloop
endfunction


endlibrary

And new map:

http://www.speedyshare.com/185641407.html
 
Level 14
Joined
Nov 18, 2007
Messages
816
too...many...functions... ...must stop reading... ...code...killing...my brain...

Seriously: organize your code. Then start indenting your code in a sane way. And then remove useless junk. And then start reading tutorials about vJass. And then rewrite your system, or better: trash it.
 
Level 11
Joined
Nov 4, 2007
Messages
337
This works fine and is GUI-user friendly.
I haven't experienced any lags yet.
Speed is not all ;-)
I think the knockback looks fine, you can start unique knockbacks (not just time)
and it's fast enough. I don't think anybody will start more than 500 Knockbacks.

But if you want me to increase the speed. Be construtive then!
What shall I change about the code?
 
Level 11
Joined
Nov 4, 2007
Messages
337
Hey.

I tried to make a good Knockback system.
I have better things to do than make perfect
code for my systems.
I made an effort and tried to create a good system
for especially noobs to use.
I know, the vJass Pros of you can make code
that is a lot mor efficient. But that is not what I want.
There are areas of improvement in my codes, I know.
You con't have to understand that.
I tried to create a different kind of Knockback System;
A System which is easy to understand and use by noobs.
Especially by people who never use JASS, just GUI.
If you think, you can do it better, make your own system.
But keep in mind: Mine is easier to use. And I guess it is not
much slower.
What do I make wrong?
You can't read the code, because I don't use methods.
Write your own if you don't appreciate my work.
If I was a noob, would I learn a new language just for a
Knockback system?
No!
And Dynasti:
Your code is more efficient, but bugged.
-Units can slide into water
-There is no option that allows the user to stop the
knockback when the unit hits another unit
-It is hard to understand for noobs.
-Knockbacks look all the same way like;
No unique options for friction and speed
So.
Maybe it's because the Internet makes the
inhibition threshold become smaller...
However, it's wrong to write 'I laugh at you'. That's not a
nice way to deal with people who try to support
noobs.

MfG, MapperMalte

edit: My post is not meant aggressive! Maybe it sounds like that.
If so, Im sorry.
 
Level 3
Joined
Apr 24, 2008
Messages
53
Knockback System - Wc3campaigns

Oh I'm sorry, looks like a knock back system that doesn't have 1000000 lines of useless crap to achieve its purpose. But wait is it NOOB FRIENDLY "like" your horrible mess of a coded system?

JASS:
call KnockbackTarget(Attacker,TempU,Angle1,Angle2,false,false,false)

Oh that looks simple enough!

But wait a second...is it bugfree to?

Of course, check it out!

So what you're saying is this system here is a waste of space and is completely irrelevant as a system?

Indeed!

Alright dude, just accept that your code is horrible, graveyard this and comeback when you've learned how to code properly.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
Parantheses?

die runden klammern.
eckige sind brackets.

for alle those who dont speak german: i just told him whats Parantheses and Brackets are.
 
Level 15
Joined
Aug 18, 2007
Messages
1,390
By user friendly he means, that you can set up the knockback distance, speed, friction, everything else with GUI. It is meant for people (like me) that don't understand a thing of jass. If you dont think its good, atleast tell him what to improve... you know, constructive critism.

And really, this is meant for Gui users, and wich gui'er (if you can say that) will ever look at a jass code? noone. Who cares if its messy? its fast, has alot of options, and best of all, it works!

so in the end, i just wonna say: Goodjob Mappermalte, have a rep.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
so i can write the fuckies code and get rep for it aslong its easy to use for guiers and just works anyhow...

if yes, hive get ready to be spammed with five-min-systems.
 
Level 14
Joined
Nov 18, 2007
Messages
816
[...]And really, this is meant for Gui users, and wich gui'er (if you can say that) will ever look at a jass code?[...]
I use GUI for really simple tasks, although i prefer using vJass
[...]Who cares if its messy?[...]
I do. Because you might want to learn something from it.
[...]its fast, has alot of options, and best of all, it works![...]
You know what? I know at least two other systems, one of which is already in this very database that are faster, have as many or even more options and work as well. The difference is: Those are decently coded. And: they didnt copy other peoples code without mentioning the original author.
 
Level 14
Joined
Nov 18, 2007
Messages
816
okay:

@Lord_of_sausage:
1.) Maybe I wouldnt want to learn anything from this code, but other people, who are not as proficient, may want to. They will inhabit bad coding practices.

2.) it migt be true that there are people to whom its more convenient to use GUI, but the majority of GUI users wont use this. They will use a GUI knockback instead. And those who are interested in using this will find that there are already a few better systems out there, which by the way offer a similar API.

3.) If you would care to read through the other pages, you would be able to see ive already been posting constructive criticism.
Im sick of this system: noone needs it and its inferior coding wise.

@MapperMalte:
Then tell me WHY you didnt give credits to the one whose code you copied?

TEC_Ghost already posted a link to the Knockback System i would be referring to now (Its Rising_Dusk's). And if you look at the header closely, youll notice some kind of similarity.

Oh, and what was that link supposed to show me? Just another random WC3 modding community.
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
By user friendly he means, that you can set up the knockback distance, speed, friction, everything else with GUI. It is meant for people (like me) that don't understand a thing of jass. If you dont think its good, atleast tell him what to improve... you know, constructive critism.

You cannot constructively criticise a certain level of shit. Destructive creation is the only way forward, ie: scrap it and rewrite it.
 
Level 20
Joined
Apr 22, 2007
Messages
1,960
If you're going to use a script in your map, sure, who gives a shit about efficiency.

If you're going to submit a script to this database, efficiency is important, as much as simplicity, documentation and reader-friendliness. Probably more but I forgot.

In other words, no.
 
Level 22
Joined
Dec 31, 2006
Messages
2,216
MapperMalte said:
By the way: The guy called Mueslirocker is more genious than anyone else here.
How can you know that? There are a lot of smart guys here, f.ex. HINDYhat, he created a really nice entity engine. I don't know if Mueslirocker is good, but I'm pretty sure he can't create a better entity engine without asking for a lot of help.
 
Level 22
Joined
Dec 31, 2006
Messages
2,216
He may be smart, but you can't say he is smarter than everyone here at the hive.
And my brother never asks for help, he also studied math and informatics, he gives some help, but he is not a genius.
 
Level 11
Joined
Nov 4, 2007
Messages
337
[hide]
JASS:
library KnockBackFunctions initializer KnockBackInit
globals
// * ================================== *
// * C O N F I G U R A B L E D A T A *
// * ================================== *
    //! ====TIMER_INTERVAL====
    // A higher number will increase the performance
    // A lower number will make the knockback smoother
    // !Note!- if you increase the speed of the timer, the units wont slide faster.
    // The speed will be kept.
    //! ====DUST_DENSITY====
    // Every TIMER_INTERVAL * DUST_DENSITY seconds a dust effect is shown.
    // Decrease DUST_DENSITY to create more densitive dust (recommended for fast-speed-knockbacks)
    // Increase DUST_DENSITY to increase the performance ( recommended, when many units at the same time are knocked back)
    //! ====DUST_MODEL====
    // This Model is the dust that is shown. If you want to know the string of another model, go
    // to the object-editor, chose your model somewhere and press ok.
    // If you click on your model-field again, the string of the model is shown
    // at 'Own'.
    // Important: You must replace '\' in the string with '\\'
    //! ====DUMMY_TYPE[1]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //! ====DUMMY_TYPE[2]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //! ====DUMMY_TYPE[3]=====
    // If RETQUIRES_UNBLOCKED is true, you need two dummy units;
    // DUMMY_TYPE[1] must be able to swim.
    // DUMMY_TYPE[2] mustn't be able to swim.
    // DUMMY_TYPE[3] must be like a normal unit; It must have collision and needs movetype walking.
    //! ====ATTACHMENT_SYSTEM=====
    //! JUST FOR FUNCTIOM 'PRECISE_KNOCKBACK'
    // This is the system which is used to attach the parameters
    // of the knockback to the timer that works with them.
    // Possible options;
    // "TimerStructAttachment"
    // "HandleAttachment"
    // "Game Cache"
    // If ATTACHMENT_SYSTEM is "TimerStructAttachment",
    // the Knockback-System is not as fast as with "HandleAttachment", but you can run 500 knockbacks (configurable)
    // at the same time without any bugs.
    // If ATTACHMENT_SYSTEM is "HandleAttachment",
    // the system is faster, that means less lag, but if
    // your map contains a lot of units and timers and blablabla. Handles.
    // Then bugs could appear.
    // Actually this system is the best choice, because of the speed.
    // But if the system says "HandleAttachment System is too full." somewhen,
    // you should swap to the TimerStructAttachment or Game Cache
    // If ATTACHMENT_SYSTEM is "Game Cache",
    // you use the savest, but also the slowest Attachment System.
    // You should only use this, if both systems, TSA and HA give error
    // messages.
    //! ====KILLTREE_RADIUS=====
    // Units destroy trees in this radius.
    // You can also use the function Precise_Knockback to make an individual KILLTRE_RADIUS for each knockback.
    //! ====PERCENTAGE_KNOCKBACK=====
    // This number should be 1.00
    // 1.00 means, that the speed and friction of all Knockbacks is
    // Speed*1.00 and Friction*1.00
    //! =====MOVEBACK=====
    // If a unit hits water, it gets moved back by this amount.
    // Choose a higher number if units stuck in water often.
    //! =====UNBLOCK_FACTOR=====
    // If this is 0, units slide through everything and kill trees.
    // If this is 1, units don't slide through water
    // If this is 2 or higher, units can't do anything while
    // They are knocked back, and the knockback stops if a unit
    // can't stand on the new point. That means the Knockback is stopped
    // by: Cliffs, units, water, doodads. By everything.
    // If the UNBLOCK_FACTOR is higher than 0 and the unit is not moved, the knockback
    // stops instantly.

private constant real TIMER_INTERVAL = 0.033
private constant integer DUST_DENSITY = 11
private constant string DUST_MODEL = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant integer DUMMY_TYPE1 = 'e000'
private constant integer DUMMY_TYPE2 = 'e001'
private constant integer DUMMY_TYPE3 = 'e002'
private constant real KILLTREE_RADIUS = 110.
private real PERCENTAGE_KNOCKBACK = 2.00
private constant real MOVEBACK = 30.
private constant integer UNBLOCK_FACTOR = 1
private constant string AUTHOR = "Mr.Malte"
endglobals

private function h2i takes handle h returns integer
    return h
    return 0
endfunction
private function Debug takes string s returns nothing
call DisplayTextToForce(GetPlayersAll(),s)
endfunction
// End of Handle Attachment System

public struct KB
    boolean bool
    integer kbi
    integer up
    real speed
    real distanz
    real friction
    real slidet
    real killtreerange
    real sin
    real cos
    unit atk
    unit opfer
endstruct

globals
    private real minx
    private real maxx
    private real miny
    private real maxy
    private integer CC
    private unit array Converted
    private integer Total = 0
    private KB array Knockbackdata
    private boolexpr Treekillcondition = null
    private timer KnockbackTimer = CreateTimer()
    private boolean IsTimerRunning = false
    private integer up = 0
    private boolean showdust = false
    private boolean Knock_GoOn = true
    private unit IsWalkableDummy = null
endglobals

function SetUnitXY takes unit u, real x, real y returns boolean
    if x < minx then
    set u = null
        return false
    elseif x > maxx then
    set u = null
        return false
    else
        call SetUnitX(u, x)
    endif
    if y < miny then
set u = null
        return false
    elseif y > maxy then
    set u = null
        return false
    else
        call SetUnitY (u, y)
    endif
        set u = null
    return false
endfunction

function IsPointWalkable takes real newx, real newy returns boolean
// SetUnitPosition setzt eine Einheit auf eine Stelle, wenn diese
// für die Einheit begehbar ist.
// Die Einheit wird also auf die neuen Koordinaten mit der Funktion
// gesetzt. Wenn die Koordinaten der Einheit nach SetUnitPosition
// newx und newy entsrpechen, kann die Einheit dort stehen.
    call SetUnitPosition(IsWalkableDummy,newx,newy)
    if ( GetUnitX(IsWalkableDummy) == newx and GetUnitY(IsWalkableDummy) == newy ) then
    return true
    endif
    return false
endfunction

private function IsWaterLoc takes real x, real y returns boolean
    call SetUnitPosition(udg_Knockback_Dummy1,x,y)
call SetUnitPosition(udg_Knockback_Dummy2,x,y)
    if ( GetUnitX(udg_Knockback_Dummy1) == GetUnitX(udg_Knockback_Dummy2) and GetUnitY(udg_Knockback_Dummy1) == GetUnitY(udg_Knockback_Dummy2)) then
    return false
    endif
    return true
endfunction

public function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc'
endfunction

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

function FreePlace_Structs takes integer ib returns nothing
    set Knockbackdata[ib] = Knockbackdata[Total]
    set Total = Total - 1
endfunction

private function KnockbackCreate takes nothing returns nothing
    //local timer expired = GetExpiredTimer() // Used for the system - means not important for you
    local integer structNR
    local KB knock
    local integer ib = 0 // Used for the system
    local real x = 0. // Used for the system
    local real y = 0. // Used for the system
    local real newx = 0. // Used for the system
    local real newy = 0. // Used for the system
    local effect e = null // Used for the system
    local real speed //= knock.speed // The speed the target slides with
    local real distanz //= knock.distanz // Distance the target unit slides
    local boolean bool //= knock.bool // Show effects? True and the effect is shown.
    local rect killtrees
    if (up == DUST_DENSITY) then
            set up = 0
            set showdust = true
    endif

                                loop

    set ib = ib + 1
    exitwhen ib > Total
    set Knock_GoOn = true
    set knock = Knockbackdata[ib]
    set speed = knock.speed
    set distanz = knock.distanz
    set bool = knock.bool
    set x = GetUnitX(knock.opfer)
    set y = GetUnitY(knock.opfer)
    set killtrees = Rect(x - knock.killtreerange, y - knock.killtreerange, x + knock.killtreerange, y + knock.killtreerange)
    set speed = speed - knock.friction
    set knock.speed = speed
    set knock.slidet = knock.slidet + speed
    set newx = x + speed * knock.cos
    set newy = y + speed * knock.sin
    call EnumDestructablesInRect(killtrees, Treekillcondition, function KillTree)
    call RemoveRect(killtrees)
        if ( UNBLOCK_FACTOR == 1 ) then
            if ( IsWaterLoc(newx,newy) ) then
            set newx = x - MOVEBACK * knock.cos
            set newy = y - MOVEBACK * knock.sin
            call SetUnitXY(knock.opfer, newx, newy)
            set killtrees = null
            call knock.destroy()
            call FreePlace_Structs(ib)
            set Knock_GoOn = false
            endif
        endif
        if ( UNBLOCK_FACTOR == 2 ) then
            if ( IsPointWalkable(newx,newy) == false ) then
                set killtrees = null
                call knock.destroy()
                call FreePlace_Structs(ib)
                set Knock_GoOn = false
            endif
        endif
        call RemoveRect(killtrees)

                                if ( Knock_GoOn == true ) then

    call SetUnitXY(knock.opfer, newx, newy)
    call RemoveRect(killtrees)
    set killtrees = null
    
        if ( showdust ) then
        if ( knock.bool == true ) then
            call DestroyEffect(AddSpecialEffect(DUST_MODEL, x, y))
        endif
        endif
        
    if ( speed < 0.1 ) then
        call PauseUnit(knock.opfer,false)
        call knock.destroy()
        call FreePlace_Structs(ib)
    endif
    
    if ( knock.slidet > distanz ) then
        call PauseUnit(knock.opfer,false)
        call knock.destroy()
        call FreePlace_Structs(ib)
        endif

endif
                                endloop


    if ( Total == 0 ) then
    call PauseTimer(KnockbackTimer)
    set IsTimerRunning = false
    endif
    set up = up + 1
    set showdust = false
endfunction

function Knockback takes unit opfer, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local KB knock = KB.create()
    local real angle
    set speed = (speed * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set friction = (friction * (TIMER_INTERVAL*25)) * PERCENTAGE_KNOCKBACK
    set knock.opfer = opfer
    set knock.atk = atk
    set knock.speed = speed
    set knock.distanz = distanz
    set knock.slidet = 10.
    set knock.friction = friction
    set knock.bool = b
    set knock.killtreerange = KILLTREE_RADIUS
    set Total = Total + 1
    set angle = Atan2(GetUnitY(opfer) - GetUnitY(atk), GetUnitX(opfer) - GetUnitX(atk))
    set knock.cos = Cos(angle)
    set knock.sin = Sin(angle)
    set Knockbackdata[Total] = knock
    if ( IsTimerRunning == false ) then
    call TimerStart(KnockbackTimer, TIMER_INTERVAL, true, function KnockbackCreate)
    set IsTimerRunning = true
    endif
    set opfer = null
    set b = false
endfunction

function SetKnockbackFactor takes real newPercentage returns nothing
    set PERCENTAGE_KNOCKBACK = newPercentage
endfunction

private function KnockBackInit takes nothing returns nothing
    local location temppoint = null
    set minx = GetRectMinX(bj_mapInitialPlayableArea)
    set maxx = GetRectMaxX(bj_mapInitialPlayableArea)
    set miny = GetRectMinY(bj_mapInitialPlayableArea)
    set maxy = GetRectMaxY(bj_mapInitialPlayableArea)
        if ( 0 == 0 ) then
set temppoint = GetRectCenter(GetPlayableMapRect())

    call CreateUnitAtLoc(Player(15),DUMMY_TYPE1,temppoint,270.)
    set udg_Knockback_Dummy1 = GetLastCreatedUnit()
call CreateUnitAtLoc(Player(15),DUMMY_TYPE2,temppoint,270.)
    set udg_Knockback_Dummy2 = GetLastCreatedUnit()
    call RemoveLocation(temppoint)
endif
    set Treekillcondition = Filter(function TreeFilter)
endfunction

function Trig_Knockback_Init_Dummies_Func002A takes nothing returns nothing
set udg_Knockback_Dummy2 = GetEnumUnit()
endfunction

function Trig_Knockback_Init_Dummies_Func003A takes nothing returns nothing
    set udg_Knockback_Dummy1 = GetEnumUnit()
endfunction

function Knockback_Init takes nothing returns nothing
    call ForGroupBJ( GetUnitsOfTypeIdAll(DUMMY_TYPE2), function Trig_Knockback_Init_Dummies_Func002A )
    call ForGroupBJ( GetUnitsOfTypeIdAll(DUMMY_TYPE1), function Trig_Knockback_Init_Dummies_Func003A )
endfunction

function ConvertToUnitArray takes nothing returns nothing
    set CC = CC + 1
    set Converted[CC] = GetEnumUnit()
endfunction

function KnockbackGroup takes group g, unit atk, real speed, real distanz, real friction, boolean b returns nothing
    local integer i = 0
    call ForGroup(g, function CovertToUnitArray)

    loop
    set i = i + 1
    exitwhen i > CC
    call Knockback(Converted[i],atk,speed,distanz,friction,b)
    set Converted[i] = null
    endloop
endfunction

endlibrary
[/hide]


better?
 
Level 19
Joined
Aug 24, 2007
Messages
2,888
are you WTFBOOM sure about this is for GUI users lol
If I was gonna make a system about it Id just say
Dont ever read this code just copy it to an empty trigger and do this in another trigger
Set KBdist = <distance you want>
Set KBspd = <KB speed you want>
Set KBdir = <Direction you want>
Custom script: call Runkb()
 
Level 11
Joined
Nov 4, 2007
Messages
337
Ok.
I recoded the system completely.
Not it should be a lot faster.
JASS:
[hide]
library Knockback
    globals
    
        //! R E Q U I R E D   C H A N G E S
        private integer dummyid = 'e001'
        private integer windwalkId = 'A001'
        
        //! O P T I O N A L   C H A N G E S
        private real Multiplicator = 1.00
        private constant real INTERVAL = 0.28
        private constant integer LCM = 900
        
        //! D O   N O T   C H A N G E!
        private integer up = 0
        private integer Total = 0
        knock array KnockBackData
        ConstantKnockback array ConstantKnockbackData
        private real minx
        private real maxx
        private real miny
        private real maxy
        private string array stores
        private integer ident = 0
        private boolexpr Treekillcondition
        private integer dustcounter  = 0
        private unit Dummy
        private boolean IsTimerRunning
        private timer Timer
    endglobals

struct knock
    unit target
    real cos
    real sin
    real slidet
    real speed
    integer Ident
endstruct

struct ConstantKnockback
    real friction
    real range
    real speed
    real killtreerange
    string Effect
    integer density
endstruct

private function SetUnitXY takes unit u, real x, real y returns boolean



        if (x < minx or x > maxx or y < miny or y > maxy ) then
            return false
        endif

        //! CHECK IF LOC IS NOT BLOCKED
        call SetUnitX(Dummy,x)
        call SetUnitY(Dummy,y)
        if ( GetUnitX(Dummy) != x or GetUnitY(Dummy) != y ) then
            return false
        endif

    call SetUnitX(u,x)
    call SetUnitY(u,y)
    return true

endfunction

private function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc'
endfunction

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

// Checks if a number is a multiple of another number
function IsMultiple takes integer a, integer b returns boolean
    if ( R2I(1.*a/b) == a/b ) then
        return true
    else
        return false
    endif
endfunction

function knockback takes nothing returns nothing
    local knock k
    local ConstantKnockback ck
    local integer i = 0
    local real OneOP = 0.
    local rect r
    local real x
    local real y
    
    set dustcounter = dustcounter + 1
    
    if ( dustcounter > LCM ) then
        set dustcounter = 0
    endif
    
    loop
        //! DECLARE STRUCTS
        set i = i + 1
        exitwhen i > Total
        set k = KnockBackData[i]
        set ck = ConstantKnockbackData[k.Ident]
        
        //! MOVE TARGET
        set x = GetUnitX(k.target)
        set y = GetUnitY(k.target)
        call SetUnitXY(k.target,x+k.speed*k.cos,y+k.speed*k.sin)
        set k.slidet = k.slidet + k.speed
        set k.speed = k.speed - ck.friction
        
        //! DESTROY TREES
        set r = Rect(x - ck.killtreerange, y - ck.killtreerange, x + ck.killtreerange, y + ck.killtreerange)
        call EnumDestructablesInRect(r, Treekillcondition, function KillTree)
        call RemoveRect(r)
        
        //! ADD SPECIAL EFFECT
        if ( IsMultiple(dustcounter,ck.density) ) then
            call DestroyEffect(AddSpecialEffect(ck.Effect,x,y))
        endif
        
        //! END KNOCKBACK WHEN FRICTION TURNS TOO LOW OR THE RANGE IS HIT
        if ( k.speed < 0.1 or k.slidet > ck.range ) then
            call k.destroy()
            set KnockBackData[i] = KnockBackData[Total]
            set Total = Total - 1
            
            if ( Total < 1 ) then
                set IsTimerRunning = false
                call PauseTimer(Timer)
            endif
            
        endif
        
    endloop
    
endfunction

// Start knockback
function Knockback takes integer knocktype, unit target, real xWhere, real yWhere returns nothing
    local knock k = knock.create()
    local ConstantKnockback ck = ConstantKnockbackData[knocktype]
    local real angle = Atan2(GetUnitY(target) - yWhere, GetUnitX(target) - xWhere)
    set k.target = target
    set k.cos = Cos(angle)
    set k.sin = Sin(angle)
    set k.speed = ck.speed
    set k.Ident = knocktype
    set Total = Total + 1
    set KnockBackData[Total] = k
    
    if ( IsTimerRunning == false ) then
        set IsTimerRunning = true
        call TimerStart(Timer,INTERVAl,true, function knockback)
    endif
endfunction

// Converts the name of a knockback into the fitting number
function Comfort takes string s returns integer
    local integer i = 0
    loop
        set i = i + 1
        exitwhen i > ident
        if ( s == stores[i] ) then
            return i
        endif
    endloop
    return -1
endfunction

// Add a knockback to the data storage.
function StoreIdentifier takes string s returns nothing
    set ident = ident + 1
    set stores[ident] = s
endfunction

function Preprocess takes nothing returns nothing
    local integer i = 0
    local ConstantKnockback A
    
    loop
        set i = i + 1
        exitwhen i > up
        
        set A = ConstantKnockbackData[i]
        set A.friction = A.friction * Multiplicator
        set A.speed = A.speed * Multiplicator
    endloop
    
endfunction

private function Init takes nothing returns nothing
    local ConstantKnockback A
    
    set A = ConstantKnockback.create()
    set up = up + 1
    set A.friction = 0.1
    set A.range = 700.
    set A.speed = 40.
    set A.killtreerange = 110.
    set A.Effect = ""
    set A.density = 11
    call StoreIdentifier("Knockback A")
    set ConstantKnockbackData[up] = A
    
    set A = ConstantKnockback.create()
    set up = up + 1
    set A.friction = 0.5
    set A.range = 300.
    set A.speed = 15.
    set A.killtreerange = 90.
    set A.Effect = ""
    set A.density = 8
    call StoreIdentifier("Knockback B")
    set ConstantKnockbackData[up] = A
    
    
    
    
    
    
    if ( Multiplicator != 1. ) then
        call Preprocess()
    endif
    
    set Timer = CreateTimer()
    set Dummy = CreateUnit(Player(15),dummyid,0.,0.,0.)
    call IssueImmediateOrderById(Dummy,windwalkid)
    set Treekillcondition = Filter(function TreeFilter)
    set minx = GetRectMinX(bj_mapInitialPlayableArea)
    set maxx = GetRectMaxX(bj_mapInitialPlayableArea)
    set miny = GetRectMinY(bj_mapInitialPlayableArea)
    set maxy = GetRectMaxY(bj_mapInitialPlayableArea)
    call TimerStart(Timer,INTERVAL,true,function knockback)
endfunction

endlibrary
[/hide]

I think this is performant, because it saves a lot of global struct vars ==>
much less data.

You can decide if you run
Knockback(1,Target,x,y) or first run
call StoreIdentifier("Wanted A")
and then
Knockback(Comfort("Wanted A"),Target,x,y).

Option B is more comfortable, but A is faster.
 
Last edited:
Top