AoE Knockback (Using Extended Knockback System) lags badly

mylemonblue

You can change this now in User CP.
Reaction score
7
Title says it all. Using the search function, I found several knockback systems/abilities, but most of them were designed for a single unit. I found this:
http://www.thehelper.net/forums/showthread.php?t=123389&highlight=knockback
And downloaded it. Testing it, the War Stomp triggered spell works like I was hoping it would, but after 3 or 4 casts, the game slows to an unplayable crawl. While testing it, NewGen throws up tons of lines saying
"Attempt to destroy struct of type Knockback_Knock"

I did not want to post this in the original thread(s) because they were over a year old so I'm asking it here. What would I have to change to keep it from slowing down so badly?

JASS:
scope Warstomp initializer Init

globals
    private constant integer SPELL = 'Awrg'
    private boolexpr b
endglobals

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL
endfunction

private function Check takes nothing returns boolean
    return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING)
endfunction

private function Actions takes nothing returns nothing
    local unit t
    local real ux
    local real uy
    local real angle
    local unit source = GetTriggerUnit()
    local group g = CreateGroup()
    local real x = GetUnitX(source)
    local real y = GetUnitY(source)
    call GroupEnumUnitsInRange(g,x,y,600,b)
    loop
        set t = FirstOfGroup(g)
        exitwhen t == null
        set ux = GetUnitX(t)
        set uy = GetUnitY(t)
        set angle = Atan2(uy-y,ux-x)
        call KnockbackTargetEx(source,t,angle,600,20,0,0,0,true,false,false,true,false,false)
        call GroupRemoveUnit(g,t)
    endloop
    set source = null
    set t = null
    call DestroyGroup(g)
    set g = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Conditions ) )
    call TriggerAddAction( t, function Actions )
    set b = Condition(function Check)
endfunction

endscope

(This is not including the four library triggers needed to have it work, which were included in the demo map)
 

Rllulium

New Member
Reaction score
10
The way you use GroupEnumUnitsInRange is probably your problem. Take a look at the Leakless Groups Tutorial.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
Also, instead of FirstOfGroup() use ForGroup(). But I am not sure if that'd be a large source of lag or not.
 

Rllulium

New Member
Reaction score
10
Also, instead of FirstOfGroup() use ForGroup(). But I am not sure if that'd be a large source of lag or not.
Neither of these should be needed. (Check out the guide I mentioned)

I was thinking something along these lines, I have not tried this trigger in action thou:
JASS:
scope Warstomp initializer Init

private function Check takes nothing returns boolean
    if IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) then
        call KnockbackTargetEx(GetTriggerUnit(),GetFilterUnit(),Atan2(GetUnitY(GetFilterUnit())-GetUnitY(GetTriggerUnit()),GetUnitX(GetFilterUnit())-GetUnitX(GetTriggerUnit()),600,20,0,0,0,true,false,false,true,false,false)
    return false
endfunction

private function Conditions takes nothing returns boolean
    if GetSpellAbilityId() == 'Awrg' then
        call GroupEnumUnitsInRange(g, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()),600,Filter(function Check))
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(t, Condition( function Conditions))
endfunction

endscope
 

Laiev

Hey Listen!!
Reaction score
188
I'm not sure, but that knockback is a bit old, what can lag is the move in knockback in the system, also that debug message is a bit strange :banghead:

Try using this from Rising_Dusk, is good and no lag, at least for me :rolleyes:

@PurgeandFire

[ljass]FirstOfGroup()[/ljass] don't cause lags

@Rllulium

What is wrong with 'this way'? Oo The only thing which he don't need is that global boolexpr, in version 1.24 or something like this, the blizzard fix the leak of boolexpr :p


EDIT: the 'chain effect' of the knockback is the reason, change to false and test D:
 

mylemonblue

You can change this now in User CP.
Reaction score
7
@Laiev: Thanks, I'tll try his system :)
I think the reason why that debug message popped up was it was trying to clear a group in the wrong way (Correct me?), since it threw one message per unit hit by the knockback :?

@Rllulium: I'll take a look at the guide. Thank you :)
 

Laiev

Hey Listen!!
Reaction score
188
probably some fail in that system :rolleyes:

need see what is wrong with the system related with your system/spell :p
 

mylemonblue

You can change this now in User CP.
Reaction score
7
I copied the libraries into my test map and changed some values and added a few lins, and I figured that was why it was lagging. But when I opened the original map and tested it without changing anything (Except for the Spell rawcode) it still lagged o_O

On the topic of Rising_Dusk's system, NewGen complains about a number of coding errors when I try saving the demo map, with nothing being changed. o_O
I'm using 1.21, and do not want to update to 1.24 because it breaks some of my favorite maps.
EDIT: And yes, Reinventing the Craft is disabled :p
EDIT 2: Using the 1.23 patch doesn't work either. Is there some file in NewGen or library I need to update?
 

Laiev

Hey Listen!!
Reaction score
188
oh god, 1.21 is so bad o.õ and every map can be playable in 1.24 <if you convert it, of course>

well, idk so, because the system of rising dusk is compatible with 1.24

the only thing which i say to you is update jasshelper
 

mylemonblue

You can change this now in User CP.
Reaction score
7
I've updated JassHelper (I think... I followed a link to Vexorian's file, but I do not know if it was the latest version or not).

And how can you update a map for 1.24 when you cannot open it? (I mean Protected maps of course.)

One moment while I try the 1.24 patch :p
(I've already +rep'd you once, will it work a second time? o_O )

Patching to 1.24d worked :D
Now I just need to update my maps... The protected ones I mean :(
 

Laiev

Hey Listen!!
Reaction score
188
One moment while I try the 1.24 patch :p
(I've already +rep'd you once, will it work a second time? o_O )
after 24hours lol or 1 week, don't remember :rolleyes:

Patching to 1.24d worked :D
Now I just need to update my maps... The protected ones I mean :(
Every map which continue in development was updated to 1.24

find the last update or find the author and ask him:p
 

mylemonblue

You can change this now in User CP.
Reaction score
7
There's the problem. Terminus Arena from what I can tell is no longer being updated (The original site that it was being hosted on is dead, and the new site that it's being "Hosted" on doesn't even have the download lnik or update log).

As for Seasons of Uncertainty, the creator has left the mapmaking community and has already stated he is no longer going to update his map :?

I figure the fastest workaround is asking rising dusk if he still has a copy of his sytem that works with 1.23
 

mylemonblue

You can change this now in User CP.
Reaction score
7
Importing systems is (usually) not a big deal for me. I know the generall basics of what to do and what not to do, and the past few systems I have used involved several of those you mentioned.

I'll give it a shot Kenny :D
 

Kenny

Back for now.
Reaction score
202
Awesome. I'll post the code for the knockback system here. If you need any help with using it, you can ask via pm or in this thread.

Edit:

Here it is:
JASS:
library KB requires AIDS, T32, Event, Status, TerrainPathability, DestructableLib

    globals
        private constant string GROUND_SFX_PATH    = &quot;Dust.mdl&quot;
        private constant string WATER_SFX_PATH     = &quot;SlideWater.mdl&quot;
        private constant string COLLISION_SFX_PATH = &quot;Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl&quot;
        private constant string SFX_ATTACH_PATH    = &quot;origin&quot;
    endglobals
    
    private function ShowEffects takes unit whichUnit returns boolean
        return IsUnitType(whichUnit,UNIT_TYPE_FLYING) == false
    endfunction
    
    globals
        private unit Knocked = null
        private unit Source  = null
    endglobals
    
    private struct KnockData extends array
        //! runtextmacro AIDS()
        
        knockback knock
        
        private method AIDS_onCreate takes nothing returns nothing
            set this.knock = 0
        endmethod
        
        private method AIDS_onDestroy takes nothing returns nothing
            set this.knock = 0
        endmethod
        
    endstruct
    
    struct knockback
    
        readonly unit    source = null
        readonly unit    target = null
        
        private  effect  unitFx = null
        private  real    xSpeed = 0.00
        private  real    ySpeed = 0.00
        private  real    xDecel = 0.00
        private  real    yDecel = 0.00
        private  real    radius = 0.00
        private  integer kbTick = 0
        private  Event   destEv = 0
        
        private  boolean fxMode = false
        private  boolean showFx = false
        private  boolean finish = false
        private  boolean moving = false
        private  boolean checks = false
        private  boolean dispFx = false
        
        private static rect     tempRect = null
        private static real     mapMaxX  = 0.00
        private static real     mapMaxY  = 0.00
        private static real     mapMinX  = 0.00
        private static real     mapMinY  = 0.00
        private static thistype tempData = 0

        method operator checkPathability= takes boolean whichFlag returns nothing
            set this.checks = whichFlag
        endmethod
        
        method operator displayHitEffect= takes boolean whichFlag returns nothing
            set this.dispFx = whichFlag
        endmethod
        
        method registerOnFinish takes trigger whichTrig returns nothing
            call this.destEv.register(whichTrig)
        endmethod
        
        method forceTerminate takes nothing returns nothing
            set this.finish = true
        endmethod
        
        private method destroy takes nothing returns nothing
            set Knocked = this.target
            set Source  = this.source
            
            call this.destEv.fire()
            
            if this.unitFx != null then
                call DestroyEffect(this.unitFx)
                set this.unitFx = null
            endif
            
            call Status[this.target].removeUnpath()
            call Status[this.target].removeDisable()
            set KnockData[this.target].knock = 0
            
            set this.source = null
            set this.target = null
            
            call this.destEv.destroy()
            call this.deallocate()
        endmethod    
        
        private static method enumTrees takes nothing returns nothing
            local destructable dest = GetEnumDestructable()
            local thistype     this = thistype.tempData
            
            if not IsDestructableDead(dest) then
                if IsDestructableTree(dest) then
                    if this.radius &gt; 0.00 then
                        call KillDestructable(dest)
                    else
                        if this.dispFx then
                            call DestroyEffect(AddSpecialEffect(COLLISION_SFX_PATH,GetWidgetX(dest),GetWidgetY(dest)))
                        endif
                        set this.finish = true
                    endif
                endif
            endif
            
            set dest = null
        endmethod
                    
        private method periodic takes nothing returns nothing
            local real    newx = GetUnitX(this.target) + this.xSpeed
            local real    newy = GetUnitY(this.target) + this.ySpeed
            local boolean flag = this.fxMode
            
            if this.kbTick == T32_Tick or this.finish or (newx &gt; thistype.mapMaxX or newy &gt; thistype.mapMaxY or newx &lt; thistype.mapMinX or newy &lt; thistype.mapMinY) then
                call this.stopPeriodic()
                call this.destroy()
            else    
                call SetUnitX(this.target,newx)
                call SetUnitY(this.target,newy)
                
                if this.radius != 0.00 then
                    set thistype.tempData = this
                    call SetRect(thistype.tempRect,newx - this.radius,newy - this.radius,newx + this.radius,newy + this.radius)
                    call EnumDestructablesInRect(thistype.tempRect,null,function thistype.enumTrees)
                endif
                
                if this.checks then
                    if not IsTerrainWalkable(newx,newy) then
                        if this.dispFx then
                            call DestroyEffect(AddSpecialEffect(COLLISION_SFX_PATH,newx,newy))
                        endif
                        set this.finish = true
                    endif
                endif
                
                if this.showFx then
                    set this.fxMode = IsTerrainLand(newx,newy)
                    if this.fxMode and not flag then
                        call DestroyEffect(this.unitFx)
                        set this.unitFx = AddSpecialEffectTarget(GROUND_SFX_PATH,this.target,SFX_ATTACH_PATH)
                    elseif not this.fxMode and flag then
                        call DestroyEffect(this.unitFx)
                        set this.unitFx = AddSpecialEffectTarget(WATER_SFX_PATH,this.target,SFX_ATTACH_PATH)
                    endif
                endif
                
                if this.xSpeed * (this.xSpeed - this.xDecel) &lt;= 0.00 then
                    set this.xSpeed = 0.00
                    set this.xDecel = 0.00
                else
                    set this.xSpeed = this.xSpeed - this.xDecel
                endif
                if this.ySpeed * (this.ySpeed - this.yDecel) &lt;= 0.00 then
                    set this.ySpeed = 0.00
                    set this.yDecel = 0.00
                else
                    set this.ySpeed = this.ySpeed - this.yDecel
                endif
            endif
        endmethod
        
        implement T32x
        
        private static method oldInstance takes unit sourceUnit, unit targetUnit, real distance, real duration, real angle, real destRadius returns nothing
            local thistype this = KnockData[targetUnit].knock
            local real     dur  = (duration / T32_PERIOD)
            local real     spd  = (2.00 * distance) / (dur + 1.00)
            local real     dec  = spd / dur
            local real     cos  = Cos(angle)
            local real     sin  = Sin(angle)
            
            set this.source = source
            set this.xSpeed = this.xSpeed + (spd * cos)
            set this.ySpeed = this.ySpeed + (spd * sin)
            set this.xDecel = this.xDecel + (dec * cos)
            set this.yDecel = this.yDecel + (dec * sin)
            set this.radius = destRadius
            set this.kbTick = T32_Tick + R2I(dur)
        endmethod            
        
        private static method newInstance takes unit sourceUnit, unit targetUnit, real distance, real duration, real angle, real destRadius returns nothing
            local thistype this = thistype.allocate()
            local real     dur  = (duration / T32_PERIOD)
            local real     spd  = (2.00 * distance) / (dur + 1.00)
            local real     dec  = spd / dur
            local real     cos  = Cos(angle)
            local real     sin  = Sin(angle)
            
            set this.source = sourceUnit
            set this.target = targetUnit
            set this.xSpeed = spd * cos
            set this.ySpeed = spd * sin
            set this.xDecel = dec * cos
            set this.yDecel = dec * sin
            set this.radius = destRadius
            set this.kbTick = T32_Tick + R2I(dur)
            set this.showFx = ShowEffects(this.target)
            set this.destEv = Event.create()
            
            if this.showFx then
                set this.fxMode = IsTerrainLand(GetUnitX(this.target),GetUnitY(this.target))
                if this.fxMode then
                    set this.unitFx = AddSpecialEffectTarget(GROUND_SFX_PATH,this.target,SFX_ATTACH_PATH)
                else
                    set this.unitFx = AddSpecialEffectTarget(WATER_SFX_PATH,this.target,SFX_ATTACH_PATH)
                endif
            endif
                       
            call Status[this.target].addUnpath()
            call Status[this.target].addDisable()
            set KnockData[this.target].knock = this
            
            call this.startPeriodic()
        endmethod
        
        static method create takes unit sourceUnit, unit targetUnit, real distance, real duration, real angle, real destRadius returns thistype
            if sourceUnit == null or targetUnit == null or distance &lt;= 0.00 or duration &lt;= 0.00 then
                debug call BJDebugMsg(&quot;|cFFFF0000Error using knockback:|r Invalid arguments for knockback.&quot;)
            else
                if KnockData[targetUnit].knock == 0 then
                    call thistype.newInstance(sourceUnit,targetUnit,distance,duration,angle,destRadius)
                else
                    call thistype.oldInstance(sourceUnit,targetUnit,distance,duration,angle,destRadius)
                endif
            endif
            return KnockData[targetUnit].knock
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.mapMaxX  = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
            set thistype.mapMaxY  = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
            set thistype.mapMinX  = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
            set thistype.mapMinY  = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
            set thistype.tempRect = Rect(0.00,0.00,1.00,1.00)
        endmethod
        
    endstruct
    
    public function IsKnockedBack takes unit whichUnit returns boolean
        return KnockData[whichUnit].knock &gt; 0
    endfunction
    
    public function StopKnockback takes unit whichUnit returns boolean
        if IsKnockedBack(whichUnit) then
            call KnockData[whichUnit].knock.forceTerminate()
            return true
        endif
        return false
    endfunction
    
    public function GetKnockedUnit takes nothing returns unit
        return Knocked
    endfunction
    
    public function GetKnockingUnit takes nothing returns unit
        return Source
    endfunction
    
endlibrary


This system actually has a couple of advantages over Dusk's. For one, it actually works properly with knockbacks stacking, and it is probably more efficient. It also allows greater control over knockbacks.

Edit:

Basic usage tutorial:

JASS:
call knockback.create(sourceUnit,targetUnit,distance,duration,angle,destructableRadius)


Source unit - The source of the knockback (obviously).
Target unit - The unit you want to knockback.
Distance - How far you want the target unit to travel.
Duration - How long you want the knockback to last.
Angle - The angle at which the knockback will move (in radians).
Destructable radius - Radius around the target unit in which destructables will be detected. A negative radius will stop units when trees get in the way, while a positive number will destroy trees. Inputing 0.00 will make it do neither. A value of +150.00/-150.00 is usually a good value to use.

Advanced usage tutorial:

JASS:
local knockback kb = 0

set kb = knockback.create(sourceUnit,targetUnit,distance,duration,angle,destructableRadius)

set kb.checkPathability = true // Set this to true if you want the knockback to stop if there is unpathable terrain.

set kb.displayHitEffect = true // Set this to true if you want to show an impact effect when a unit hits unpathable terrain.

call kb.registerOnFinish(whichTrigger) // This will register a trigger to fire when the knockback ends.

// If you register a trigger with the above method, the &#039;actions&#039; function of that trigger may access:

KB_GetKnockedUnit() // The unit that was knocked back.

KB_GetKnockingUnit() // The source unit of the knockback.

// And just in case:

call kb.forceTerminate() // This will stop a knockback if needed.


You can also use:

JASS:
KB_IsKnockedBack(whichUnit)

// and:

KB_StopKnockback(whichUnit)
 

mylemonblue

You can change this now in User CP.
Reaction score
7
Knockback spells/systems really vary in complexity don't they? I've seen one that only has a couple hundred lines of code and reuires only NewGen, and then I see advanced systems like this! *whistles*

Anyhoo. So I copied everything and double checked to make sure I did not leave anything out. Everything saved fine, except for one line in the Event system. JassHelper insists that something is wrong.
"Member redeclared: destroy"
 

Kenny

Back for now.
Reaction score
202
That may be due to your JassHelper version. What version do you have?

Also the knockback system I posted does everything a knockback system should do and nothing more. However there is one thing is doesn't have: the ability to group units around the knockback unit every interval. If you need that, I can add it. I just never found a legitimate use for it.
 

mylemonblue

You can change this now in User CP.
Reaction score
7
In the readme provided with the installer it says
Code:
pjass 07.11.2007 (v 1.0k)
.

I had followed a link to the download page for JASSHelper and downloaded it from there, but I wasn't sure if that was the most recent version or not.

And thanks so much for the help, it is very kind of you :)
God, looking at these spells and awesome things requiring these systems and libraries I keep thinking "How the hell would I ever have ben able to do that!?" and feel so useless. I applaud the people who put in so much hard work into these systems for the rest of the mapmaking community :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top