• 🏆 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!

[JASS] Jass Knockback System

Status
Not open for further replies.
Level 4
Joined
Jan 19, 2008
Messages
69
I was unable to find a vanilla JASS knockback system and so I decided to create my own. Still feeling somewhat inexperienced I was hoping that someone would look over my code, provide suggestions and perhaps point out my mistakes.

It currently works pretty well, but there are probably ways that I can improve it further? (I'm especially thinking about efficiency here as the system currently has just about all the functionality that I desire)

JASS:
function Frictional_Knockback_System_Callback takes nothing returns nothing
    local unit u = GetEnumUnit()
    local real x
    local real y
    local real x1
    local real y1
    local real angle
    local real friction
    local integer id = GetHandleId(u)
    local real speed = LoadReal(udg_FKS_Hashtable, id, 0)

    if GetWidgetLife(u) > 0.405 and speed > 0. then
        set x = GetUnitX(u)
        set y = GetUnitY(u)
        set angle = LoadReal(udg_FKS_Hashtable, id, 1)
        set x1 = ProjectX(x, speed, angle)
        set y1 = ProjectY(y, speed, angle)
        if IsTerrainPathable(x1, y1, PATHING_TYPE_WALKABILITY) == false then
            call SetUnitX(u, x1)
            call SetUnitY(u, y1)
            set friction = LoadReal(udg_FKS_Hashtable, id, 2)
            set speed = speed - friction
            call SaveReal(udg_FKS_Hashtable, id, 0, speed)
        else
            call DestroyEffect(LoadEffectHandle(udg_FKS_Hashtable, id, 3))
            call GroupRemoveUnit(udg_FKS_Group, u)
            call FlushChildHashtable(udg_FKS_Hashtable, id)
            set udg_FKS_Count = udg_FKS_Count - 1
        endif
    else
        call DestroyEffect(LoadEffectHandle(udg_FKS_Hashtable, id, 3))
        call GroupRemoveUnit(udg_FKS_Group, u)
        call FlushChildHashtable(udg_FKS_Hashtable, id)
        set udg_FKS_Count = udg_FKS_Count - 1
    endif

    set u = null
endfunction

function Frictional_Knockback_System_Actions takes nothing returns nothing
    if udg_FKS_Count > 0 then
        call ForGroup(udg_FKS_Group, function Frictional_Knockback_System_Callback)
    else
        call DisableTrigger(GetTriggeringTrigger())
    endif
endfunction

//===========================================================================
function InitTrig_Frictional_Knockback_System takes nothing returns nothing
    set gg_trg_Frictional_Knockback_System = CreateTrigger()
    call TriggerRegisterTimerEventPeriodic(gg_trg_Frictional_Knockback_System, 0.03125)
    call TriggerAddAction(gg_trg_Frictional_Knockback_System, function Frictional_Knockback_System_Actions)
endfunction

Thanks in advance!
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Please post the function that adds the knockback to the unit.

• Inline the projection functions.
IsTerrainPathable ignores destructables and units.
• Try replacing the periodic trigger with a periodic timer.

EDIT: forgot to suggest something on point bullet 2; Try using the pathability checking library from the jass section.
 
Level 4
Joined
Jan 19, 2008
Messages
69
Please post the function that adds the knockback to the unit.

• Inline the projection functions.
IsTerrainPathable ignores destructables and units.
• Try replacing the periodic trigger with a periodic timer.

EDIT: forgot to suggest something on point bullet 2; Try using the pathability checking library from the jass section.

Hey chobibo, thanks for your time.

The function that adds the knockback to the unit(s) could look something like this:
JASS:
        set g = CreateGroup()
        call GroupEnumUnitsInRange(g, x, y, 450, null)
        loop
            set target = FirstOfGroup(g)
            exitwhen target == null
            if IsUnitEnemy(target, GetOwningPlayer(caster)) and GetWidgetLife(target) > 0.405 then
                set id = GetHandleId(target)
                set x1 = GetUnitX(target)
                set y1 = GetUnitY(target)
                set sfx = AddSpecialEffectTarget("Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl", target, "origin")
                call GroupAddUnit(udg_FKS_Group, target)
                call SaveReal(udg_FKS_Hashtable, id, 0, 50.)
                call SaveReal(udg_FKS_Hashtable, id, 1, AngleBetweenXY(x,y,x1,y1))
                call SaveReal(udg_FKS_Hashtable, id, 2, 2.5)
                call SaveEffectHandle(udg_FKS_Hashtable, id, 3, sfx)
                set udg_FKS_Count = udg_FKS_Count + 1
                call EnableTrigger(gg_trg_Frictional_Knockback_System)
                set sfx = null
            endif
            call GroupRemoveUnit(g, target)
            set target = null
        endloop
        call DestroyGroup (g)
        set g = null

Regarding your suggestions:
1) alright i'll inline it, but just to clarify - is it more effcient to inline the functions? if not, then what is the purpose of doing so?

2) i was aware of the fact that IsTerrainPathable ignores destructables and units but i could not find any vanilla JASS alternatives. It did not bother me particularly though as there are no destructables on my map. That being said, I did check the JASS section for a pathability checking library but I could find no such thing /sadface.

3) Would a periodic timer be more efficient even though the trigger is disabled when not in use?
 
Overall, it is nice so far. Chobibo already mentioned some of it. Definitely check up on the pathability thing. This is the old vanilla JASS resource that was used for it:
JASS:
function CheckPathabilityTrickGet takes nothing returns nothing
    set bj_rescueChangeColorUnit = bj_rescueChangeColorUnit or GetEnumItem() != udg_CheckItem
endfunction
 
function CheckPathabilityTrick takes real x, real y returns boolean
    local integer i = 30
    local real X
    local real Y
    local rect r
 
    if udg_CheckItem == null then
        set udg_CheckItem = CreateItem( 'ciri', x, y )
    endif
 
    call SetItemPosition( udg_CheckItem, x, y )
    set X = GetItemX( udg_CheckItem ) - x
    set Y = GetItemY( udg_CheckItem ) - y
 
    if X * X + Y * Y <= 100 then
        return true
    endif
 
    set r = Rect( x - i, y - i, x + i, y + i )
 
    set bj_rescueChangeColorUnit = false
 
    call EnumItemsInRect( r, null, function CheckPathabilityTrickGet )
 
    call RemoveRect( r )
    set r = null
 
    return bj_rescueChangeColorUnit
endfunction
 
function CheckPathability takes real x, real y returns boolean
    local boolean b = CheckPathabilityTrick( x, y )
    call SetItemVisible( udg_CheckItem, false )
    return b
endfunction
  • CheckPathability Setup
    • Events
      • Map Initialization
    • Conditions
    • Actions
      • Custom script: set udg_CheckItem = CreateItem('ciri', 0, 0)
      • Custom script: call SetItemVisible(udg_CheckItem, false)
Original code by Vexorian. I took the base code from:
http://peeeq.de/code.php?id=2202
And I adapted it to vanilla JASS.

You would call it like so: if CheckPathability(x, y) then

  • You could compact the "set friction = ...", "set speed = speed - friction", "call SaveReal(...)" down to a one-liner. But that sacrifices readability. It would be up to you.
  • You have a bit of repeated code. This isn't necessarily a matter of performance, but it is generally a good habit to do away with repeated code wherever possible. You could put the code in a separate function, but I prefer a different way. It would look like this:
    JASS:
    function Frictional_Knockback_System_Callback takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real x
        local real y
        local real x1
        local real y1
        local real angle
        local real friction
        local integer id = GetHandleId(u)
        local real speed = LoadReal(udg_FKS_Hashtable, id, 0)
        local boolean endKnockback = false // declare this
    
        if GetWidgetLife(u) > 0.405 and speed > 0. then
            set x = GetUnitX(u)
            set y = GetUnitY(u)
            set angle = LoadReal(udg_FKS_Hashtable, id, 1)
            set x1 = ProjectX(x, speed, angle)
            set y1 = ProjectY(y, speed, angle)
            if IsTerrainPathable(x1, y1, PATHING_TYPE_WALKABILITY) == false then
                call SetUnitX(u, x1)
                call SetUnitY(u, y1)
                set friction = LoadReal(udg_FKS_Hashtable, id, 2)
                set speed = speed - friction
                call SaveReal(udg_FKS_Hashtable, id, 0, speed)
            else
                set endKnockback = true // add this
            endif
        else
            set endKnockback = true // add this
        endif
    
        if endKnockback then // end knockback
            call DestroyEffect(LoadEffectHandle(udg_FKS_Hashtable, id, 3))
            call GroupRemoveUnit(udg_FKS_Group, u)
            call FlushChildHashtable(udg_FKS_Hashtable, id)
            set udg_FKS_Count = udg_FKS_Count - 1
        endif
    
        set u = null
    endfunction
    Technically, you still have repeated code. :p But it is a little better this way, IMO, especially for readability and modularity. If you need to change what happens when the knockback ends, you only need to edit it in one area!
  • alright i'll inline it, but just to clarify - is it more effcient to inline the functions? if not, then what is the purpose of doing so?
    Inlining is faster than calling a function directly. But it isn't always a good option to inline. In this case, it is convenient to inline because it involves only one line. But in cases where the function is several lines, it is better to call the function directly, even if it is a BJ (such as TriggerRegisterAnyUnitEventBJ). For example, your AngleBetweenXY is fine to use.
  • Switch TriggerRegisterTimerEventPeriodic to:
    JASS:
    call TriggerRegisterTimerEvent(gg_trg_Frictional_Knockback_System, 0.03125, true)
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Oh, you're inlining the knockback function. Looks okay.

1. Yes, that's the reason why I suggested it.
2. Ahh, the one here in Hive is GUI... Try this.
3. IIRC, it uses less resources and processing time.

EDIT: PurgeandFire's post is right.
 
Level 4
Joined
Jan 19, 2008
Messages
69
Sorry to rain on your parade however http://www.hiveworkshop.com/forums/...d-mms-3d-242031/?prev=search=kb3d&d=list&r=20...

Good luck with this though, looking good except where's the configurable's?

This was never intended to be a public resource for other people to use (don't get me wrong if you want to use it feel to do so!) I made it for my own project which does not require a lot of functionality, and as such there was no need for other complex features or configurables - I did not need these myself! I do feel that my first post makes this somewhat clear.

Regarding the system which you have linked; it looks pretty amazing - but once again - I don't need all these features in my map. However thanks for making me aware of its existence. I'll definitely keep it in the back of my mind in case I'll ever need a knockback system with more features.
 
Status
Not open for further replies.
Top