System Extended Knockback System


The document is here, let it speak for itself.

JASS:
Knockback System                               By wraithseeker
   v 1.05                                                              
                                                                                
                       
                Special Thanks to Pyrogasm and Kenny! for guiding me and helping me with this system
                            Credits to Vile for the knockback effects
                            Credits to Moyack for the ParabolaZ function
                            Credits to Rising_Dusk for GroupUtils
                            Credits to PitzerMike for IsDestructableLibrary
                            Credits to Cohadar for PUI
                            Credits to Anitarf for IsTerrainWalkable
                            
                            And credits to the others who have helped me in a way or another.
            
                                Changelog v1.01
                                    
                - Removed d.destroy() and changed to d.release() for PUI issues.
                - Took away some silly parameters which was totally useless.
                - Optimized the code.
                            
                                Changelog v1.02
                - Optimized code.
                - Indented some code.
                - Added some extra parametres.
                - unit height no longer gets bugged when at the borders of the map.
                
                                Changelog v1.03
                - Removed some useless struct members.
                - Made method Terraincheck not public and not to take knock d for efficient purposes.
                - Updated documention.
                - Code now has comments and names are more friendly.
                
                                Changelog v1.04
                - Used PUI text macro to shorten things.
                - Removed some useless call.
                
                                Changelog v1.05
                - Fixed a serious bug
                
                
    A system I made that can be used for knockback spells in a variety of          
    applications in a map. User defined variables make it easy to give the desired 
    effect for any spell.                                                          
                                                                                   
    requires                                                                      
        - A vJASS preprocessor (JNGP).            
        
        - The special effects found in the import/export section of this map.      
         They are the "Dust.mdx" for land, "SlideWater.mdx" for water, "TornadoMissle.mdx" for air and "DustAndRocks.mdx" for collision on cliffs.

        - GroupUtils, PUI, IsDestructableLib, IsTerrainWalkable
    
    Pros
        - Can support many kind of stuffs you want in a knockback
        - Leak free and efficient
        - Made in vJASS
        - Uses only a single timer and not many
        - Knockback is realistic
        - Allows you to knock a unit at a constant speed
        - Knock a unit while flying with a ParabolaZ
        - Allows Chain knockbacks which is not found here
        - You can use a constant knockback speed if you do not want any increment or decrement
        - You can increase your knockback speed instead of decreasing for dashing spells mainly.
        
    Cons
        - requires 4 library
                             
  Other Information                                                           
        - Angle is taken in Radians. But can easily be changed if you know how.    
        - Units will stop when they reach the map borders.                   
        - Remember to import the special effect into your map if you need them else it would look really plain.   
        - There are five functions that can be used in this system and more will be added soon if I have the time.               
                                                                                  
    Finding the Distances                                                             
        - This is some general knowledge on how to calculate the distance with a formula.                                 
        
        
                    The Formula to calculate the distance.
                    = -1 * V * V / (2 * A / Interval)
                
                 Variable V = The initial speed of the knocked unit which must be positive.
                 Variable A = The decrement of the speed per Interval which must be negative.
          Variable Interval = The timer period that function Update runs, known as TIME in the globals block.
          
          For example, we have initial speed value as 1000 and decrement as 15 and we subsitute them into the formula
          
                            -1 * 1000 * 1000 / (2 * -15 / 0.03) = 1000 distance
                            
                Note that the default timer interval is 0.03 and it is reccomended that you do not change it at all.
                            


                                                                                  
Implementation:                                                                
    - First off you need JNGP known as JassNewGenPack which is a modified world editor.  
    
    - Create a new trigger and then convert it to custom text and then you copy paste
    the code into the trigger.
    
    - Now you will either need to export the special effects from this map to  
    special effects found in this map.   
    
    - Once you have the effects in your map. Find the configuration block      
    that is underneath all this green text. Change the paths of the effect
    to the path in the system.     
    
    It is not recommended if you do not know much about importing or exporting to change the paths
    else just use the paths in the system.

    - Save a map and then see if you get any errors, if you do not have it, then congratulations,
    you have successfully brought the system over to your map.
    
                                                                                   
    NOTE: Please report any bugs to me at thehelper.net via PM. (Username: wraithseeker)
                                                                                 

    Usage                                                                        

        call KnockbackTargetEx(source,t,angle,speed,decrement,increment,KillTree,AllowMove,ConstantSpeed,chain,Fly,Increase)         
                    
       Source  =   The unit who started the knockback.       
       Target  =   The unit that will be used in the knockback. 
       Angle   =   The angle that the unit will be knocked back.

            For example Atan2(cy-y,cx-x)
            
        X is the coordinate of unit Target
        Y is the coordinate of unit Target
        CX is the coordinate of unit source
        CY is the coordinate of unit source
            
    Decrement        =   The speed decrement every interval.
    
    Increment        =   The Increment speed of the unit target per interval, usually you should set it to 0 or null
                        unless you have boolean increment set to true.
                        
    ChainHeight      =   The amount of height the unit target can have before it stops chaining units and killing trees
                        Meaning if a unit reaches a certain height lets say 200, chaining and killing trees will be disabled but
                        before it reaches 200, chaining and killing trees are enabled.
                        
    MaxHeight        =   The maximum height you want a unit to have while in the air, leave this to 0 if you want
                        the system to count it for you. Best left alone.
                        
    Tree             =   This boolean will decide whether you want the unit target to knock down trees or not
                      
    Move             =   This boolean will decide whether you want the unit target to move while getting knocked back.   
                        
                    
    ConstantSpeed    =   This boolean will decide whether you want the unit target to move at a constant speed or not.
                      
    Chain        =   This boolean will decide whether you want the unit target to chain knock units or not.
                    
    Fly          =   This boolean will decide whether you want the unit target to fly while getting knocked back
                    and pathing will be disabled.

    Increase         =   This boolean will decide whether you want to increase the unit target speed per interval.
                    
                                                                               
    Example of Usage                                                            
                                                                                  
    call KnockbackTargetEx(t,u,a,1000.00,15.00,0,true,false,false,true,true,false)                            
    call KnockbackTargetEx(source,t,angle,800,20,0,true,false,false,true,true,false) 
                                                                                  
    The first one will cause the target unit of a spell to be knocked back 1000 distance away using the formula
    and it will have decrement speed of 15 per interval. There is no Increment as boolean Increment is set to
    false. boolean KillTree is set to true so trees will be killed and the unit will not be allowed to move due
    to the boolean AllowMove being set to false. Units will be chained and they will also fly but not increase
    their speed.
                                                                                   
    The second one will cause the target unit of a spell to be knocked back 480 distance away using the formula
    and it will have decrement speed of 20 per interval. There is no Increment as boolean Increment is set to
    false. boolean KillTree is set to true so trees will be killed and the unit will not be allowed to move due
    to the boolean AllowMove being set to false. Units will be chained and they will also fly but not increase
    their speed.                                                      

                                                                                 
Other functions                                                           
                                                                                  
    call IsKnockedBack(Target)                                               
                                                                                   
    This function checks if the unit is currently sliding using this        
    system. It will return true if it is.                                        
                                                                                   
    call IsUnitSliding(Target)                                             
                                                                                   
    This function will stop the unit from sliding (using this system).      
    It also returns true if the unit is stopped.                                 
                                                                                  
    These functions can be used in conjunction with each other, by checking if a 
      unit is sliding then stopping it if it is.


The System


JASS:




library Knockback initializer Init requires DestructableLib, IsTerrainWalkable, GroupUtils, PUI

globals
    private constant real    TIME         = 0.03                     // The timer interval.
    private constant real    CHAINRADIUS  = 100.00                   // The radius a unit can get chained when near the unit target.
    private constant real    SPEEDFACTOR  = 0.75                     // How much speed will be reduced when a unit chains another unit.
    private constant real    RADIUS       = 128.00                   // The radius to check tree.
    private constant real    HEIGHTLEVEL  = 200.00                   // The default height level that stops chaining or killing trees when in the air.
    private constant real    FACTOR       = 0.3333                   // Or 0.25 for smoothness.
    private constant string  GROUND       = "MDX\\Dust.mdx"          // The effect for ground.
    private constant string  WATER        = "MDX\\SlideWater.mdx"    // The effect for water.
    private constant string  COLLISION    = "MDX\\DustAndRocks.mdx"  // The effect when at cliff
    private constant string  FLYING       = "MDX\\TornadoMissile.mdx"// The effect when flying
    private constant string  ATTACHPOINT  = "origin"                 // The attachment point for effects
endglobals

globals
    private integer  array Knocker       
    private integer  Count                  = 0
    private constant integer INVULNERABLE   = 'Avul'               
    private constant integer FLYID          = 'Amrf' 
    private timer    Timer                  = null
    private rect     TreeRect               = null
    private boolexpr TreeCheck              = null
    private boolexpr ChainFilter            = null
    private unit     Target                 = null
    private unit     Source                 = null
    private real MapMaxX                    = 0
    private real MapMaxY                    = 0
    private real MapMinX                    = 0
    private real MapMinY                    = 0
endglobals

private function CheckTrees takes nothing returns boolean
    return IsDestructableTree(GetFilterDestructable())
endfunction

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

private function ChainCheck takes nothing returns boolean
    return Target != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(Source)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(),INVULNERABLE) <= 0
endfunction

function ParabolaZ takes real h, real d, real x returns real
  return (4 * h / d) * (d - x) * (x / d)
endfunction

private function IsPointOutside takes real x, real y returns boolean
    return (x > MapMaxX or y > MapMaxY or x < MapMinX or y < MapMinY)
endfunction

public struct Knock
 //! runtextmacro PUI()
    unit        source          = null
    unit        target          = null
    integer     mode            = 0
    group       hit             = null
    real        cos             = 0
    real        sin             = 0 
    real        speed           = 0
    real        decrement       = 0
    real        distance        = 0
    real        movedistance    = 0
    real        cspeed          = 0  
    real        icspeed         = 0
    real        maxheight       = 0
    real        chainheight     = 0
    effect      effects         = null
    boolean     terrain         = false
    boolean     trees           = false
    boolean     allowmove       = false
    boolean     constantspeed   = false
    boolean     chain           = false
    boolean     fly             = false
    boolean     increase        = false
    
    method TerrainCheck takes nothing returns integer
        local real x = GetUnitX(this.target)
        local real y = GetUnitY(this.target)
    
        if IsTerrainWalkable(x + 50.00 * this.cos,y + 50.00 * this.sin) == false then
            return 3
        else
            if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
                return 1
            elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
                return 2
            endif
        endif 
        
        return 0
    endmethod
        
    static method create takes unit source, unit target, real angle, real speed, real decrement, real increment, real maxheight, real chainheight, boolean tree, boolean move, boolean constantspeed, boolean chain, boolean fly, boolean increase returns Knock
        local Knock d = Knock.allocate()
        set d.source        = source
        set d.target        = target
        set d.trees         = tree
        set d.fly           = fly
        set d.icspeed       = increment
        set d.maxheight     = maxheight
        set d.chainheight   = chainheight
        set d.constantspeed = constantspeed
        set d.cspeed        = speed
        set d.allowmove     = move
        set d.chain         = chain
        set d.movedistance  = 0
        set d.hit           = NewGroup()
        set d.speed         = speed * TIME
        set d.decrement     = decrement * TIME
        set d.sin           = Sin(angle)
        set d.cos           = Cos(angle)
        set d.distance      = -1 * speed * speed / (2 * -decrement / TIME)
        
        if d.fly == true then
            call SetUnitPathing(d.target,false)
            call UnitAddAbility(d.target,FLYID)
            call UnitRemoveAbility(d.target,FLYID)
            set d.effects = AddSpecialEffectTarget(FLYING,d.target,ATTACHPOINT)
        else
            set d.mode    = d.TerrainCheck()
            if d.mode == 1 then
                set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
            elseif d.mode == 2 then
                set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
            elseif d.mode == 3 then
                set d.effects = AddSpecialEffectTarget(COLLISION,d.target,ATTACHPOINT)
            endif
        endif
        
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(this.effects)
        call ReleaseGroup(this.hit)
        call SetUnitPathing(this.target,true)
    endmethod
endstruct
        
private function Update takes nothing returns nothing
    local Knock   d    = 0
    local Knock   c    = 0
    local unit    t    = null
    local real    x    = 0.00
    local real    y    = 0.00
    local real    cx   = 0.00
    local real    cy   = 0.00
    local integer mode = 0
    local integer i    = Count - 1
    local real height = 0
    
    loop
        exitwhen i < 0
        set d = Knocker<i>
        set mode = d.mode
        set d.movedistance = d.movedistance + d.speed
        
        set x = GetUnitX(d.target)
        set y = GetUnitY(d.target)
        set height = GetUnitFlyHeight(d.target)
        if d.chainheight == 0 then
            if height &gt;= HEIGHTLEVEL then
                set d.chain = false
                set d.trees = false
            endif
        else
        if height &gt;= d.chainheight then
            set d.chain = false
            set d.trees = false
        endif
            endif
        if d.speed &lt;= 0 or IsPointOutside(x,y) then
            call d.release()
            set Knocker<i> = Knocker[Count]
            call SetUnitFlyHeight(d.target,0,0)
            if i &lt; 0 then
                call PauseTimer(Timer)
                set Count = 0
            else
                set Knocker<i> = Knocker[Count]
            endif
        else
            if d.constantspeed == true then
                set x = x + d.cspeed * d.cos
                set y = y + d.cspeed * d.sin
            else
                if d.increase == true then
                    set x = x + d.cspeed * d.cos
                    set y = y + d.cspeed * d.sin
                else
                    set x = x + d.speed * d.cos
                    set y = y + d.speed * d.sin
                endif
            endif

            if d.allowmove == true then
                call SetUnitX(d.target,x)
                call SetUnitY(d.target,y)
            else
                call SetUnitPosition(d.target,x,y)
            endif
            
            if d.trees == true then
                    call SetRect(TreeRect,x-RADIUS,y-RADIUS,x+RADIUS,y+RADIUS)
                    call EnumDestructablesInRect(TreeRect,TreeCheck,function Trees)
                endif
            
            if d.fly == true then
                if d.maxheight == 0 then
                    call SetUnitFlyHeight(d.target,ParabolaZ(FACTOR*d.distance,d.distance,d.movedistance),0)
                else
                    call SetUnitFlyHeight(d.target,ParabolaZ(d.maxheight,d.distance,d.movedistance),0)
            endif
            
            else
                set d.mode = d.TerrainCheck()
                if d.mode == 1 and (mode == 2 or mode == 3) then
                    call DestroyEffect(d.effects)
                    set d.effects = AddSpecialEffectTarget(GROUND,d.target,ATTACHPOINT)
                elseif d.mode == 2 and (mode == 1 or mode == 3) then
                    call DestroyEffect(d.effects)
                    set d.effects = AddSpecialEffectTarget(WATER,d.target,ATTACHPOINT)
                elseif d.mode == 3 and (mode == 1 or mode == 2) then
                    call DestroyEffect(d.effects)
                    set d.effects = AddSpecialEffectTarget(COLLISION,d.target,ATTACHPOINT)
                endif
            endif
            
            if d.chain == true then
                set Target = d.target
                set Source = d.source
                call GroupEnumUnitsInRange(ENUM_GROUP,x,y,CHAINRADIUS,ChainFilter)
                loop
                    set t = FirstOfGroup(ENUM_GROUP)
                    exitwhen t == null
                    if not IsUnitInGroup(t,d.hit) then
                        set cx = GetUnitX(t)
                        set cy = GetUnitY(t)
                        call GroupAddUnit(d.hit,t)
                        set c = Knock.create(d.source,t,Atan2(cy-y,cx-x),(d.speed/TIME)*SPEEDFACTOR,(d.decrement/TIME),d.icspeed,d.maxheight,d.chainheight,d.trees,d.allowmove,d.constantspeed,d.chain,d.fly,d.increase)
                        call GroupAddUnit(c.hit,d.target)
                        set Knocker[Count] = c
                        set Count = Count + 1
                    endif
                    call GroupRemoveUnit(ENUM_GROUP,t)
                endloop
            endif
            
            if d.increase == true then
                set d.cspeed = d.cspeed + d.icspeed
            endif
            
            set d.speed = d.speed - d.decrement
        endif
        
        set i = i - 1
    endloop

    set t = null
endfunction

function KnockbackStop takes unit target returns boolean
    local Knock d = Knock[target]
    local integer i = Count - 1
    if d != 0 then
        call d.release()
        set Knocker<i> = Knocker[Count]
        if i &lt;= 0 then
            call PauseTimer(Timer)
        endif
            return true
    endif
    return false
endfunction

function IsKnockedBack takes unit target returns boolean
  return Knock[target] != 0
endfunction

function KnockbackTargetEx takes unit source, unit target, real angle, real speed, real decrement, real increment,real maxheight,real chainheight, boolean tree, boolean move, boolean constantspeed,boolean chain,boolean fly,boolean increase returns boolean
    local Knock d = 0
    
    if target == null or source == null or speed == null or decrement == null then
       debug call BJDebugMsg(&quot;Invalid Values!&quot;)
        return false
    endif
    
    set d = Knock.create(source,target,angle,speed,decrement,increment,maxheight,chainheight,tree,move,constantspeed,chain,fly,increase)
    set Knock[target] = d
    
    if Count == 0 then
        call TimerStart(Timer,TIME,true,function Update)
    endif
    
    set Knocker[Count] = d
    set Count = Count + 1
    
    return true
endfunction

function KnockbackTarget takes unit source, unit target, real angle, real speed, real decrement, boolean tree returns nothing
    call KnockbackTargetEx(source,target,angle,speed,decrement,0,0,0,tree,false,false,false,false,false)
endfunction

function KnockbackTargetFly takes unit source, unit target, real angle, real speed, real decrement, boolean tree, boolean fly returns nothing
    call KnockbackTargetEx(source,target,angle,speed,decrement,0,0,0,tree,false,false,false,fly,false)
endfunction

function KnockbackTargetFlyEx takes unit source, unit target, real angle, real speed, real decrement, real increment, real maxheight,real chainheight,boolean tree, boolean move, boolean constantspeed, boolean chain, boolean increase returns nothing
    call KnockbackTargetEx(source,target,angle,speed,decrement,increment,maxheight,chainheight,tree,move,constantspeed,chain,true,increase)
endfunction

function KnockbackChain takes unit source, unit target, real angle, real speed, real decrement, boolean tree returns nothing
    call KnockbackTargetEx(source,target,angle,speed,decrement,0,0,0,tree,false,false,true,false,false)
endfunction

function KnockbackConstant takes unit source, unit target, real angle, real speed, real decrement, boolean tree returns nothing
    call KnockbackTargetEx(source,target,angle,speed,decrement,0,0,0,tree,false,true,false,false,false) // note, use the formula to calculate the distance that it is going to reach
endfunction

function KnockBackIncrease takes unit source, unit target, real angle, real speed, real increment, real decrement, boolean tree returns nothing
    call KnockbackTargetEx(source,target,angle,speed,decrement,increment,0,0,tree,false,false,false,false,true)
endfunction

private function Init takes nothing returns nothing
    set Timer       = CreateTimer()
    set TreeRect    = Rect(0.00,0.00,1.00,1.00)
    set TreeCheck   = Filter(function CheckTrees)
    set ChainFilter = Filter(function ChainCheck)
    set MapMaxX     = GetRectMaxX(bj_mapInitialPlayableArea)
    set MapMaxY     = GetRectMaxY(bj_mapInitialPlayableArea)
    set MapMinX     = GetRectMinX(bj_mapInitialPlayableArea)
    set MapMinY     = GetRectMinY(bj_mapInitialPlayableArea)     
endfunction

endlibrary  </i></i></i></i>
 

Attachments

You put a great deal of work into this and you managed to create a system with a lot of diversity. However:

Your naming convention is horrid. I have no idea what those constant variables do. You should at least take the time to make some comments.

Your specialized functions takes a boolean, which is odd. KnockbackTargetFly should for example statically set fly to true.

On a final note, good job and keep it up :thup:
 

Romek

Super Moderator
Staff member
> //! run textmacro PUI()
That works?

So, how is this 'extended'?
What makes this any better than the current ones we have?
 

Flare

Stops copies me!
Staff member
> //! run textmacro PUI()
That works?
Well, wraithseeker is using GetUnitIndex for the array index rather than the unit, so it probably doesn't (unless overloading arrays would still allow you to use integer indexes directly, which seems unlikely)

After a quick look, two things I noticed:
JASS:
function IsKnockedBack takes unit target returns boolean
    local integer i = Count - 1
    local Knock   d = Index[GetUnitIndex(target)]
    return d != 0
endfunction

1) What's i there for?
2) Why not inline it to
JASS:
function IsKnockedBack takes unit whichUnit returns boolean
  return Knock (Index[GetUnitIndex(whichUnit)]) != 0
endfunction
//or, store knocked-back units in a group and:
function IsKnockedBack takes unit whichUnit returns boolean
  return IsUnitInGroup (whichUnit, KnockedUnits) //not sure if that's the order of the arguments
endfunction


JASS:
function KnockbackStop takes unit target returns boolean
    local integer i = Count - 1
    local Knock   d = Index[GetUnitIndex(target)]
    local boolean b = false
    
    if d != 0 then
        call d.destroy()
        set Count = Count - 1
        if Count &lt; 0 then
            call PauseTimer(Timer)
            set Count = 0
        else
//asfddsgf
            set Knocker<i> = Knocker[Count]
        endif
        set b = true
    endif
    
    return b
endfunction
</i>

1) Count should never be less than 0, so that if will, from the looks of things, will never return true - you can't have -1 instances :p
2) That 'set Knocker = Knocker[Count]' line looks a bit useless - let's say Count is 10 when we call the function, and i is 9

We get into the first if, Count becomes 9, i remains the same

Second if evaluates to false, we go to the else component
We set the data for Knocker to the data of Knocker[Count] - bear in mind, i and Count are the same, so you are essentially doing
JASS:
set Knocker<i> = Knocker</i>

(from the looks of things)
which is somewhat unnecessary
 

Romek

Super Moderator
Staff member
> Never used PUI before? What a shame. ^^
Ever used a textmacro before? I thought not.
 
JASS:

function KnockbackStop takes unit target returns boolean
    local Knock d = Index[GetUnitIndex(target)]
    if d != 0 then
        call d.destroy()
        set Count = Count - 1
        if Count &lt;= 0 then
            call PauseTimer(Timer)
            set Count = 0
        endif
            return true
    endif
    return false
endfunction


Are you suggesting that I should do this?

The IsKnockbacked part has been cleared.
 

Romek

Super Moderator
Staff member

Azlier

Old World Ghost
I heard you were never supposed to destroy PUI structs directly, you were supposed to use .release(), or am I missing something here?
 
> //! run textmacro PUI()
That works?

So, how is this 'extended'?
What makes this any better than the current ones we have?
Yes, it works, I have checked with pyrogasm and I believe he is correct.

For the check trees part, you're correct, forgot to optimize it.


It allows you to knock a unit at a constant speed or knock a unit while flying at the same time. It also allows chain knockbacks which is not found here. You can use a constant knockback speed if you do not want any increment or decrement. You can increase your knockback speed instead of decreasing for dashing spells mainly.

However, there will still be more to come soon, adding some Ex function for fly, chain and other stuffs :)

Cohadar said:
UnitData can be ANY struct you wish you just need to remember one simple rule:
do NOT destroy PUI struct yourself, the PUI system will destroy them automatically when unit is removed from game.
If for some reason you need to de-attach and destroy the struct from unit use .release() method that is generated by PUI textmacro.

For any further details check the examples in PUI test map.
Will fix it.
 

Romek

Super Moderator
Staff member
> Yes, it works, I have checked with pyrogasm and I believe he is correct.
I don't even think he uses vJass, so he can't exactly 'check' much for you in this case.

to run textmacros, you use:
JASS:
//! runtextmacro NAME (Arg1, Arg2)

Not
JASS:
//! run textmacro NAME (Arg1, Arg2)


You getting that wrong, and everything working fine shows you don't even need that textmacro. You're using the old-fashion PUI method anyway.

> knock a unit while flying at the same time
It can knockback a flying unit? That's nothing amazing is it?
Explain what you mean here...

> t also allows chain knockbacks which is not found here.
Explain.

> IsTerrainWalkable(x + 50.00 * d.cos,y + 50.00 * d.sin)
Why all those offsets? Why don't the other x/y checks in the same function use those 'new' x/y's?
 
Taken directly from cohadar's test struct

JASS:
scope StructExample initializer Init

//===========================================================================
//  If we want struct to be attachable to units we run PUI macro inside it.
//  Putting 5-6 unit properties inside struct is more efficient 
//  than declaring 5 PUI_PROPERTY each for itself
//===========================================================================
private struct UnitData
    //! runtextmacro PUI()
    integer KillCounter = 0
    
    // you can add any of your own stuff here
endstruct

//===========================================================================
private function Actions takes nothing returns nothing
    local unit killer = GetKillingUnit()
    local UnitData dat = UnitData[killer]
    
    // is struct attached?
    if dat == 0 then
        set dat = UnitData.create() // if not create it
        set UnitData[killer] = dat // and attach it
    endif

    // again it is simple
    set dat.KillCounter = dat.KillCounter + 1
    
    // do some other stuff with your struct...
endfunction

//===========================================================================
//  WARNING: never destroy PUI structs directly use release() method instead
//  In fact it is best that you never destroy them manually,
//  they will be destroyed automatically when unit is removed from game.
//===========================================================================

//===========================================================================
private function Conditions takes nothing returns boolean
    return true
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( trig, Condition( function Conditions ) )
    call TriggerAddAction( trig, function Actions )
endfunction

endscope


> It knocks a unit while flying and it allows you to fly over cliffs or from water to land.

> Chain knockback as in, when the target knockbacked unit touches a unit, the unit will get knocked back with a speedfactor that will decrease it's speed by alittle bit which is configurable.

to run textmacros, you use:


JASS:
//! runtextmacro NAME (Arg1, Arg2)Not


//! run textmacro NAME (Arg1, Arg2)


Actually, doesn't the Jass Helper already recognise it which was indicated by the gray colour of the text but changed to be safe, then sorry.
 

Romek

Super Moderator
Staff member
And cohadars code uses //! runtextmacro, not //! run textmacro.
There is a difference you know, and showing me things that proves me correct isn't helping.

> Actually, doesn't the Jass Helper already recognise it which was indicated by the gray colour of the text but changed to be safe, then sorry.
No. It makes everything after a //! grey. It even makes "//! IlikeSpoons" grey.

You might as well remove that textmacro.


I'll test/review the system in more detail later.
 
I changed the textmacro to what you suggested by sticking those words together.

Thanks to everyone who tried to help :)


Version 1.01 is released, check the documention for the changelog
 

Pyrogasm

There are some who would use any excuse to ban me.
Romek said:
I don't even think he uses vJass, so he can't exactly 'check' much for you in this case.
Bull shit. Don't believe me and I'll prove it to you. You are, however, on the whole correct that the //! runtextmacro PUI() is entirely pointless in this case. I didn't really pay much attention to it because he wasn't getting any issues from it and I simply didn't think about it :p

The way you are doing things, wraith, is this:
JASS:
local integer PUIIndex = GetUnitIndex(target)
set Indexes[UIndex] = d

Technically speaking, this can be done in one line: set Indexes[GetUnitIndex(target)] = d and the PUI textmacro allows you to shorten this further by adding some method operators to the struct and letting you type this instead: set Indexes[target] = d.

The method operator simply looks like this:
JASS:
static method operator [] takes integer k returns integer
    return Knock[GetUnitIndex(k)] //Knock because that's where you called the textmacro
endmethod //In all actuality you should be using this for Index[] but that wouldn't work out nicely

static method operator []= takes unit k, integer v returns integer
    set Knock[GetUnitIndex(k)] = v
endmethod

Or something like that; take a look at that textmacro in PUI. At any rate, it simply allows you to shorten things, but since you've already done it the long way (and you would have to make Indexes a struct in order to use it properly), there's no need to have it. Thus, you should get rid of the textmacro call because it's not doing anything.


JASS:
call BJDebugMsg(&quot;Invalid Values!&quot;)

Should be a debug call.

If you really wanted to use the PUI textmacro and make use of delegate, you could make the index setting and getting a bit more elegant, but it's not necessary.
 

Pyrogasm

There are some who would use any excuse to ban me.
Before someone comes and tries to figure out why, it's because you removed the PUI textmacro from the struct.

Using the textmacro in the struct 'makes' it a "PUI struct", and you need to use .release() on "PUI structs." But if the struct is no longer a PUI struct, then you don't need to worry about it. The error you were getting was that the method didn't exist, which is because you removed the textmacro.


Also, regarding the set Knock = Knock[Count] thing: that line needs to exist. Think of it this way: Count = 5 and i = 1. Knock[2] is destroyed and now you have an empty hole in the set of used indexes in the array. Doing it without that line, Knock[0, 2, 3] are unchanged, Knock[1] = 0, and Knock[4] is totally disregarded because Count decreases by one, so in the next iteration of the timer, it will do unnecessary work on Knock[1], and Knock[4] won't move like it should.

You need to re-organize the array, so you move the last index (4) to the slot that has been freed up (1) and decrease Count by one. Sure, sometimes it will do nothing, but not all the time, and it's really not inefficient. In my opinion it would be more inefficient to add another elseif to check if i == Count and silly things like that.
 

Flare

Stops copies me!
Staff member
JASS:

function KnockbackStop takes unit target returns boolean
    local Knock d = Index[GetUnitIndex(target)]
    if d != 0 then
        call d.destroy()
        set Count = Count - 1
        if Count &lt;= 0 then
            call PauseTimer(Timer)
            set Count = 0
        endif
            return true
    endif
    return false
endfunction


Are you suggesting that I should do this?

The IsKnockbacked part has been cleared.
Sort of... - the problem you have there is that, for the main knockback actions, you're looping through each array slot, from 1 to Count (or Count to 1, whatever). When we manually destroy an index, the value of GetUnitIndex won't correspond to that unit's array slot for the knockback (e.g. GetUnitIndex may be 35, but the unit may occupy the 25th slot in the Knocker array), so you can't do
JASS:
set Knocker[loopIterator] = Knocker[Count]
set Count = Count - 1

which can lead to things messing up when you decrease Count.

What you could do is add a boolean var to the struct (let's say it's called shouldFinish). When someone calls KnockbackStop...
JASS:
function KnockbackStop takes unit target returns boolean
  local Knock d = Knock (Index[GetUnitIndex (target)])
  if d != 0 then
    set d.shouldFinish = true
    return true
  endif
  return false
endfunction

and add 'or d.shouldFinish' to the if conditions here
JASS:
        if d.speed &lt;= 0 or IsPointOutside(x,y) then //This line!
            call d.release()
            set Count = Count - 1
            if Count &lt; 0 then
                call PauseTimer(Timer)
                set Count = 0
            else
                set Knocker<i> = Knocker[Count]
            endif
</i>

I can't really think of any other suitable method that retains MUI capabilities without breaking anything. Won't give you instantaneous stoppage, but who is going to notice a knockback stopping 0.02 to 0.03 seconds later than it should have :p

Also, you should do 'set Knocker = Knocker[Count]' before decreasing Count, otherwise it's worthless.
JASS:
if d.speed &lt;= 0 or IsPointOutside (x, y) or d.shouldFinish then
  call d.release ()
  set Knocker<i> = Knocker[Count] //We need to move the instance at the very top to the newly available array, not the one just below it <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue    :p" data-shortname=":p" />
  set Count = Count - 1
  if Count == 0 then //If Count is less than 0, you are doing something wrong
    call PauseTimer (whichTimer)
  endif
... //other stuff</i>
 

Pyrogasm

There are some who would use any excuse to ban me.
You're only right about the last part, Flare. He should decrease Count after setting Knock = Knock[Count].

The rest, however, is not correct. Take a look at how he's using Index[]. He uses the PUI index of a unit (which is static and does not change) to get the appropriate struct from an integer array. It doesn't matter what index of Knock[] corresponds to a certain struct d, because Index[IndexOf(target)] will always = d.

It's just misleading because he called the array "Index[]", when he really should have called it "Unit2Knock[]" or something.
 

Viikuna

No Marlo no game.
Make knockback struct public, so people can use their own attachment/indexing systems for attaching knockback struct to unit.
(or you could have some wrapper for that )

You have too many user functions, it looks horrible. You should use stuct syntax instead of these stupid functions.

Instead of putting boolean Chain as an argument, have it as an struct member, so people can easily change it when they want, instead of remembering some functions with hundreds of arguments..

I generally hate all the systems that use 'hard to remember' -user functions instead of nice and smooth struct syntax.

This is what it should look like:

JASS:
 
local knockback k=knockback.create( knocked, knocker, velocity, angle )
// if you dont wanna use default values, you can change them:
set k.friction=.5 // in case you dont wanna use default friction value
set k.chain=true // allows chain knockback
// like this
 
Viikuna said:
(You could have some wrapper for that )
> How?

Viikuna said:
I generally hate all the systems that use 'hard to remember' -user functions instead of nice and smooth struct syntax
> So instead of calling KnockbackTargetEx, just call the struct create method directly? I will.

Still, I don't really understand what you mean..

Viikuna said:
Instead of putting boolean Chain as an argument, have it as an struct member, so people can easily change it when they want, instead of remembering some functions with hundreds of arguments..
> I need them for flying and some other things and I don't really get what you are doing since it needs to take the user defined boolean chain and then setting d.chain to chain.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    The old shoutbox wasn't supported anymore. We updated xenForo, so it had to be replaced.
  • jonas jonas:
    let's see if everyone finds it... the nice thing about the shoutbox was I could check on it even while logged out, but the existence of this one is hidden when you're not logged in
  • Ghan Ghan:
    We can fix that.
  • Ghan Ghan:
    Chat should show on the sidebar when not logged in now.
  • Ghan Ghan:
    (You'll still need to log in to post messages)
  • Ghan Ghan:
    Test!
  • tom_mai78101 tom_mai78101:
    I must be in a test server.
  • tom_mai78101 tom_mai78101:
    Nice, Twitter tweets embedding now works
  • Wizard Wizard:
    Yup.
  • Ghan Ghan:
    Excellent.
  • Ghan Ghan:
    @tom_mai78101 Hello there.
  • Ghan Ghan:
    Tagging works in the chat too.
  • tom_mai78101 tom_mai78101:
    @Ghan Missed it.
  • Wizard Wizard:
    Still fixing things here and there. Added widgets to the portal, will make it match the ones here on the forum index tomorrow.
  • Ghan Ghan:
    The venerable World Editor Tutorials site has been converted to HTTPS at last.
  • jonas jonas:
    cool
  • jonas jonas:
    and I can even edit my messages, nice
  • seph ir oth seph ir oth:
    GENERAL CHIT CHAT, YOU ARE A BOLD ONE
  • Ghan Ghan:
    Hello there
  • The Helper The Helper:
    this new chatbox is great and the forum software update is great too
    +1
  • The Helper The Helper:
    upgrade has fixed forum registration spam problem
  • tom_mai78101 tom_mai78101:
    Something tells me we might be able to customize the chatbox a bit, considering that there's a gap under every message.
  • Wizard Wizard:
    Going to deploy a fix soon, just had to take some time for myself this weekend.
  • Varine Varine:
    Unbelievable. Time for yourself? How dare you!

    Staff online

    Members online

    Affiliates

    Hive Workshop
    Top