• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

[Solved] Turn item collision back on for windwalk

Level 5
Joined
Jun 19, 2018
Messages
43
It's been many years since Blizzard fixed item jump with windwalk by turning collision off for items. Previously items had had no collision with units unless the unit is in windwalk. After the fix there is no collision of items what so ever. It broke many maps.
My map that I wish to port to Reforged relies on that windwalk mechanic and I want to try my best to keep it.

My idea was to create a temp collision under item that is only affecting a unit with windwalk. The unit in windwalk needs to be teleported to the closest point without collision the moment collision is recalculated. Temp collision could be either a invisible unit or destructible (even path blocker) or it could be SetTerrainPathable function.

The ideal way is to have a unit in windwalk have collision in a terrain cell containing an item and other units to not have it. The dirty way is turn collision on for everyone but only for the moment of a "jump" to happen.

I have no idea how to do the clean way with units or destructibles. But I had an idea how to make it work with SetTerrainPathable.
Morph a unit in windwalk into same one but with amphibious movement type (very annoying actually since i have to make and unmake abilities permanents, remove permanent abilities that supposed to be removed) and set the groud to be unwalkable for amphibious:
call SetTerrainPathable(x,y, PATHING_TYPE_AMPHIBIOUSPATHING, false)
My plan crumbled because for some reason an amphibious unit in windwalk can walk over no-amphiby-terrain as long as windwalk is on.

Do you guys have any clues how to make my ideas work, or know any other ways to unfix windwalk jump?
 
I might have a slightly weird idea: let's try to trick the engine to resolve the moment where it is forced to fix an invalid position. I used a two-step 'sneeze' effect. First, I use SetUnitX/Y to teleport the unit 150 range forward, intentionally jamming them into the cliff geometry. Immediately after, I call SetUnitPosition on those exact same coordinates. This 'wakes up' the pathing engine, panics because the unit is out of bounds, and forcefully snaps them to the nearest valid ground; which is usually the top of the cliff.

Here is the code so far:

Code:
scope WindWalkJump initializer Init

    globals
        // ================= CONFIG =================
        private constant integer SPELL_ID = 'AOwk'  // Rawcode of Wind Walk ability
        private constant integer BUFF_ID = 'BOwk'   // Rawcode of Wind Walk buff
        private constant real PERIOD = 0.03         // Refresh rate
        private constant real ITEM_RADIUS = 64.0    // How close to the item to jump
        private constant real JUMP_DIST = 150.0     // How far to jump over the cliff
      
        // ================= SYSTEM =================
        private group ActiveGroup = CreateGroup()
        private trigger PeriodicTrigger = CreateTrigger()
        private rect CheckRect = Rect(0,0,0,0)
      
        // Globals for the Item Filter callback
        private boolean ItemFound = false
        private real ScanX = 0.0
        private real ScanY = 0.0
    endglobals

    // ==============================
    // CORE JUMP LOGIC
    // ==============================
    private function DoJump takes unit u returns nothing
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real angle = GetUnitFacing(u) * bj_DEGTORAD
      
        local real targetX = x + JUMP_DIST * Cos(angle)
        local real targetY = y + JUMP_DIST * Sin(angle)

        // 1. Force unit into the cliff geometry
        call SetUnitX(u, targetX)
        call SetUnitY(u, targetY)
      
        // 2. Wake up pathing to snap them to the top (or bottom)
        call SetUnitPosition(u, targetX, targetY)
      
        // Optional special effect so you know it triggered
        call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl", targetX, targetY))
    endfunction

    // ==============================
    // ITEM DETECTION LOGIC
    // ==============================
    private function ItemFilter takes nothing returns nothing
        local item it = GetEnumItem()
        local real dx = GetItemX(it) - ScanX
        local real dy = GetItemY(it) - ScanY
      
        // Pythagorean distance check: dx^2 + dy^2 <= r^2
        if (dx*dx + dy*dy <= ITEM_RADIUS*ITEM_RADIUS) then
            set ItemFound = true
        endif
        set it = null
    endfunction

    private function CheckForItems takes unit u returns boolean
        set ScanX = GetUnitX(u)
        set ScanY = GetUnitY(u)
        set ItemFound = false
      
        // Move the global rect to the unit and scan for items
        call SetRect(CheckRect, ScanX - ITEM_RADIUS, ScanY - ITEM_RADIUS, ScanX + ITEM_RADIUS, ScanY + ITEM_RADIUS)
        call EnumItemsInRect(CheckRect, null, function ItemFilter)
      
        return ItemFound
    endfunction

    // ==============================
    // PERIODIC LOOP
    // ==============================
    private function LoopEnum takes nothing returns nothing
        local unit u = GetEnumUnit()

        // If they lost the buff, remove them from the tracking group
        if GetUnitAbilityLevel(u, BUFF_ID) == 0 then
            call GroupRemoveUnit(ActiveGroup, u)
        else
            // If they still have the buff, check for items
            if CheckForItems(u) then
                call DoJump(u)
            endif
        endif
        set u = null
    endfunction

    private function Periodic takes nothing returns nothing
        call ForGroup(ActiveGroup, function LoopEnum)
      
        // Shut down timer if no one is Wind Walking (Optimized CPU)
        if FirstOfGroup(ActiveGroup) == null then
            call DisableTrigger(PeriodicTrigger)
        endif
    endfunction

    // ==============================
    // EVENT TRIGGERS
    // ==============================
    private function OnCastConditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction

    private function OnCastActions takes nothing returns nothing
        local unit u = GetTriggerUnit()
      
        // Add to tracking group
        if not IsUnitInGroup(u, ActiveGroup) then
            call GroupAddUnit(ActiveGroup, u)
        endif
      
        // Wake up the periodic timer
        call EnableTrigger(PeriodicTrigger)
        set u = null
    endfunction

    // ==============================
    // INIT
    // ==============================
    private function Init takes nothing returns nothing
        local trigger castTrig = CreateTrigger()
        local integer i = 0
      
        // Register Cast Event for all players
        loop
            call TriggerRegisterPlayerUnitEvent(castTrig, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
      
        call TriggerAddCondition(castTrig, Condition(function OnCastConditions))
        call TriggerAddAction(castTrig, function OnCastActions)
      
        // Setup Periodic Trigger (Starts disabled)
        call TriggerRegisterTimerEvent(PeriodicTrigger, PERIOD, true)
        call TriggerAddAction(PeriodicTrigger, function Periodic)
        call DisableTrigger(PeriodicTrigger)
    endfunction

endscope

And here is the result:

I guess this can be expanded and tweaked further...

On second thought, I'm deffo using this for my map lol, tnx azgarol for even bringing this up :D
 
Last edited:
I might have a slightly weird idea: let's try to trick the engine to resolve the moment where it is forced to fix an invalid position. I used a two-step 'sneeze' effect. First, I use SetUnitX/Y to teleport the unit 150 range forward, intentionally jamming them into the cliff geometry. Immediately after, I call SetUnitPosition on those exact same coordinates. This 'wakes up' the pathing engine, panics because the unit is out of bounds, and forcefully snaps them to the nearest valid ground; which is usually the top of the cliff.

Here is the code so far:

Code:
scope WindWalkJump initializer Init

    globals
        // ================= CONFIG =================
        private constant integer SPELL_ID = 'AOwk'  // Rawcode of Wind Walk ability
        private constant integer BUFF_ID = 'BOwk'   // Rawcode of Wind Walk buff
        private constant real PERIOD = 0.03         // Refresh rate
        private constant real ITEM_RADIUS = 64.0    // How close to the item to jump
        private constant real JUMP_DIST = 150.0     // How far to jump over the cliff
     
        // ================= SYSTEM =================
        private group ActiveGroup = CreateGroup()
        private trigger PeriodicTrigger = CreateTrigger()
        private rect CheckRect = Rect(0,0,0,0)
     
        // Globals for the Item Filter callback
        private boolean ItemFound = false
        private real ScanX = 0.0
        private real ScanY = 0.0
    endglobals

    // ==============================
    // CORE JUMP LOGIC
    // ==============================
    private function DoJump takes unit u returns nothing
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local real angle = GetUnitFacing(u) * bj_DEGTORAD
     
        local real targetX = x + JUMP_DIST * Cos(angle)
        local real targetY = y + JUMP_DIST * Sin(angle)

        // 1. Force unit into the cliff geometry
        call SetUnitX(u, targetX)
        call SetUnitY(u, targetY)
     
        // 2. Wake up pathing to snap them to the top (or bottom)
        call SetUnitPosition(u, targetX, targetY)
     
        // Optional special effect so you know it triggered
        call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl", targetX, targetY))
    endfunction

    // ==============================
    // ITEM DETECTION LOGIC
    // ==============================
    private function ItemFilter takes nothing returns nothing
        local item it = GetEnumItem()
        local real dx = GetItemX(it) - ScanX
        local real dy = GetItemY(it) - ScanY
     
        // Pythagorean distance check: dx^2 + dy^2 <= r^2
        if (dx*dx + dy*dy <= ITEM_RADIUS*ITEM_RADIUS) then
            set ItemFound = true
        endif
        set it = null
    endfunction

    private function CheckForItems takes unit u returns boolean
        set ScanX = GetUnitX(u)
        set ScanY = GetUnitY(u)
        set ItemFound = false
     
        // Move the global rect to the unit and scan for items
        call SetRect(CheckRect, ScanX - ITEM_RADIUS, ScanY - ITEM_RADIUS, ScanX + ITEM_RADIUS, ScanY + ITEM_RADIUS)
        call EnumItemsInRect(CheckRect, null, function ItemFilter)
     
        return ItemFound
    endfunction

    // ==============================
    // PERIODIC LOOP
    // ==============================
    private function LoopEnum takes nothing returns nothing
        local unit u = GetEnumUnit()

        // If they lost the buff, remove them from the tracking group
        if GetUnitAbilityLevel(u, BUFF_ID) == 0 then
            call GroupRemoveUnit(ActiveGroup, u)
        else
            // If they still have the buff, check for items
            if CheckForItems(u) then
                call DoJump(u)
            endif
        endif
        set u = null
    endfunction

    private function Periodic takes nothing returns nothing
        call ForGroup(ActiveGroup, function LoopEnum)
     
        // Shut down timer if no one is Wind Walking (Optimized CPU)
        if FirstOfGroup(ActiveGroup) == null then
            call DisableTrigger(PeriodicTrigger)
        endif
    endfunction

    // ==============================
    // EVENT TRIGGERS
    // ==============================
    private function OnCastConditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction

    private function OnCastActions takes nothing returns nothing
        local unit u = GetTriggerUnit()
     
        // Add to tracking group
        if not IsUnitInGroup(u, ActiveGroup) then
            call GroupAddUnit(ActiveGroup, u)
        endif
     
        // Wake up the periodic timer
        call EnableTrigger(PeriodicTrigger)
        set u = null
    endfunction

    // ==============================
    // INIT
    // ==============================
    private function Init takes nothing returns nothing
        local trigger castTrig = CreateTrigger()
        local integer i = 0
     
        // Register Cast Event for all players
        loop
            call TriggerRegisterPlayerUnitEvent(castTrig, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
     
        call TriggerAddCondition(castTrig, Condition(function OnCastConditions))
        call TriggerAddAction(castTrig, function OnCastActions)
     
        // Setup Periodic Trigger (Starts disabled)
        call TriggerRegisterTimerEvent(PeriodicTrigger, PERIOD, true)
        call TriggerAddAction(PeriodicTrigger, function Periodic)
        call DisableTrigger(PeriodicTrigger)
    endfunction

endscope

And here is the result:

I guess this can be expanded and tweaked further...

On second thought, I'm deffo using this for my map lol, tnx azgarol for even bringing this up :D
I am puzzled because this doesn't look like it emulates how it was behaving in 1.26 and as of now I don't see how in could be tweaked to behave like it is used to. However it gave me an idea that I could "forcefully snap the unit to the nearest valid ground" myself without using pathing engine. I only need to find the closest pathable cell that also doesn't contain an item.
 
I am puzzled because this doesn't look like it emulates how it was behaving in 1.26 and as of now I don't see how in could be tweaked to behave like it is used to. However it gave me an idea that I could "forcefully snap the unit to the nearest valid ground" myself without using pathing engine. I only need to find the closest pathable cell that also doesn't contain an item.
Yeah fair point, but even if we approximate the solver, I guess we’ll never match the original internal resolution logic.... at that point it’s more about choosing which approximation gives the best gameplay feel atp. Do let me know if you manage to handle it, I'd like to see it, cheers!
 
Yeah fair point, but even if we approximate the solver, I guess we’ll never match the original internal resolution logic.... at that point it’s more about choosing which approximation gives the best gameplay feel atp. Do let me know if you manage to handle it, I'd like to see it, cheers!
I would share it if I succeed. I don't hope my approximation would be in any way perfect either, I just don't get the cliff idea, because originally this gimmic was used for blademaster to squeeze between diagonally placed buildings into enemy base, not to teleport forward by touching an item:
 
I figured that blocking PATHING_TYPE_PEONHARVESTPATHING works the way I want. It blocks windwalk and allows everything else. It seems workers returning resources and windwalk uses the same pathing. I no longer need to change movement type, so I guess I will make it work eventually
 
I figured that blocking PATHING_TYPE_PEONHARVESTPATHING works the way I want. It blocks windwalk and allows everything else. It seems workers returning resources and windwalk uses the same pathing. I no longer need to change movement type, so I guess I will make it work eventually
I was going into that territory last night, after trying to basically summon a black hole in my room, but eventually gave up tired, I’ll let you know if I manage to pull up something.
 
JASS:
function gg_trg_Actions takes nothing returns nothing
    call SetTerrainPathableBJ( GetItemLoc(GetManipulatedItem()), PATHING_TYPE_PEONHARVESTPATHING, false )
endfunction

//===========================================================================
function InitTrig takes nothing returns nothing
    set gg_trg = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg, EVENT_PLAYER_UNIT_DROP_ITEM )
    call TriggerAddAction( gg_trg, function gg_trg_Actions )
endfunction
This works. It's not a system yet, just a little test, but it proved working.
I need to modify PATHING_TYPE_PEONHARVESTPATHING every time an item is relocated.
Also i think I need to SetTerrainPathableBJ in several cells at once in a bigger area, because it required more precision where to place an item to jump.
 
JASS:
function gg_trg_Actions takes nothing returns nothing
    call SetTerrainPathableBJ( GetItemLoc(GetManipulatedItem()), PATHING_TYPE_PEONHARVESTPATHING, false )
endfunction

//===========================================================================
function InitTrig takes nothing returns nothing
    set gg_trg = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg, EVENT_PLAYER_UNIT_DROP_ITEM )
    call TriggerAddAction( gg_trg, function gg_trg_Actions )
endfunction
This works. It's not a system yet, just a little test, but it proved working.
I need to modify PATHING_TYPE_PEONHARVESTPATHING every time an item is relocated.
Also i think I need to SetTerrainPathableBJ in several cells at once in a bigger area, because it required more precision where to place an item to jump.
I’m away from my desk right now, but a few quick thoughts on scaling this into a full system:

If we toggle this for items/buildings, we have to be 100% surgical with the cleanup otherwise the Blademaster becomes a runner of an invisible maze. If an item is moved/destroyed and we don't 'erase' the blocked pathing, the map will eventually fill up with invisible walls that only wind walkers hit.

As you noted, precision is key. A single cell (32x32) might be too small; maybe try to paint a 2 \times 2 or 3 \times 3 grid around the object's origin to ensure the unit doesn't just glide through the corners.

I assume that toggling terrain pathing is usually fine, but if we do it for every item relocation in a busy map, I'm worried about tiny CPU hitches.


Code:
local real x = GetItemX(it)
local real y = GetItemY(it)
local real i = -32.0
local real j

loop
    exitwhen i > 32.0
    set j = -32.0
    loop
        exitwhen j > 32.0
        // This paints a 64x64 area (4 cells)
        call SetTerrainPathable(x + i, y + j, PATHING_TYPE_PEONHARVESTPATHING, false)
        set j = j + 32.0
    endloop
    set i = i + 32.0
endloop
 
Here's two maps to experiment with. Forgive me but it's ChatGPT again, too lazy to write code that I know I've written before.

The first map works but the main issue I see is that it shoves you too far away when there isn't a gap to clear (a jump).

The second map has the beginnings of an idea where you would test positions around the Hero and find the one that's furthest away and push the Hero there. It utilizes a hidden Item to test pathing, a trick used in knockback systems and other movement systems. It currently doesn't take the angle between the Hero and the Item being dropped into consideration, which would be necessary to recreate a proper "shove away" effect.
 

Attachments

My code was buggy because at the moment drop item event is triggered, the item lcoation is not yet updated. so I added TriggerSleepAction but now I have to manually update location as was suggested above:
JASS:
function Trig_WindwalkDropItem_Actions takes nothing returns nothing
    local item i = GetManipulatedItem()
    local unit u = GetManipulatingUnit()
    local location l
    call TriggerSleepAction( 0.00 )
    set l = GetItemLoc(i)
    call SetTerrainPathableBJ( GetItemLoc(i), PATHING_TYPE_PEONHARVESTPATHING, false )
    if UnitHasBuffBJ(u, 'BOwk') then
        set l = GetUnitLoc(u)
        call SetUnitPositionLoc(u, l) //manual update
    endif
    call RemoveLocation(l)
    set l = null
    set u = null
    set i = null
endfunction

//===========================================================================
function InitTrig_WindwalkDropItem takes nothing returns nothing
    set gg_trg_WindwalkDropItem = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_WindwalkDropItem, EVENT_PLAYER_UNIT_DROP_ITEM )
    call TriggerAddAction( gg_trg_WindwalkDropItem, function Trig_WindwalkDropItem_Actions )
endfunction
this works very similar to 1.26 windwalk.
 
My code was buggy because at the moment drop item event is triggered, the item lcoation is not yet updated. so I added TriggerSleepAction but now I have to manually update location as was suggested above:
JASS:
function Trig_WindwalkDropItem_Actions takes nothing returns nothing
    local item i = GetManipulatedItem()
    local unit u = GetManipulatingUnit()
    local location l
    call TriggerSleepAction( 0.00 )
    set l = GetItemLoc(i)
    call SetTerrainPathableBJ( GetItemLoc(i), PATHING_TYPE_PEONHARVESTPATHING, false )
    if UnitHasBuffBJ(u, 'BOwk') then
        set l = GetUnitLoc(u)
        call SetUnitPositionLoc(u, l) //manual update
    endif
    call RemoveLocation(l)
    set l = null
    set u = null
    set i = null
endfunction

//===========================================================================
function InitTrig_WindwalkDropItem takes nothing returns nothing
    set gg_trg_WindwalkDropItem = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_WindwalkDropItem, EVENT_PLAYER_UNIT_DROP_ITEM )
    call TriggerAddAction( gg_trg_WindwalkDropItem, function Trig_WindwalkDropItem_Actions )
endfunction
this works very similar to 1.26 windwalk.
Just note couple of things, this part is going to leak:

Code:
set l = GetItemLoc(i) // You create the 1st location here
...
if UnitHasBuffBJ(u, 'BOwk') then
    set l = GetUnitLoc(u) // 2nd location created, OVERWRITING the first reference
endif
call RemoveLocation(l) // Only the SECOND location is removed. The first one is now a permanent leak.

This got me going towards the idea that you can actually do this by grabbing the coordinates from the Order itself. Since a drop is technically a Target Order, the engine stores the click coordinates (x,y) - Basically, if you stick to the xy approach, you avoid removing the locations manually.

Regarding the SleepAction, this is known to be buggy and imprecise, can drift for 0.2 depending on lag, I would suggest a next frame timer approach I previously explained here. Basically, creating a 0.00 timer; but by relying on coordinates, we can skip this part too, hopefully.
 
Last edited:
Just note couple of things, this part is going to leak:

Code:
set l = GetItemLoc(i) // You create the 1st location here
...
if UnitHasBuffBJ(u, 'BOwk') then
    set l = GetUnitLoc(u) // 2nd location created, OVERWRITING the first reference
endif
call RemoveLocation(l) // Only the SECOND location is removed. The first one is now a permanent leak.

This got me going towards the idea that you can actually do this by grabbing the coordinates from the Order itself. Since a drop is technically a Target Order, the engine stores the click coordinates (x,y) - Basically, if you stick to the xy approach, you avoid removing the locations manually.

Regarding the SleepAction, this is known to be buggy and imprecise, can drift for 0.2 depending on lag, I would suggest a next frame timer approach I previously explained here. Basically, creating a 0.00 timer; but by relying on coordinates, we can skip this part too, hopefully.
Yeah, you are right about locations. In my defence I barely did any mapmaking in the last years.
My rule of thumb is TriggerSleepAction is safe as long as the wait time is zero, or if I don't care hw much time really passes.
 
No need to be defensive xd I only mentioned the leaks because the first thing i do when looking at triggers/code is to hunt for leaks and stress-test edge cases, so I tend to always point them out.

But honestly, the leak isn't even the biggest issue here. The real problem is that TriggerSleepAction(0.00) is essentially a 'coin flip' for logic.

Even at zero, the engine pauses that thread and puts it at the ‘back of the line.’ If the player is spamming orders or there's a heavy combat frame, that 'zero' wait can turn into 200ms of delay.


In a high-precision system like a 'Windwalk Jump,' 200ms is the difference between a clean gap-close and a unit that feels like it's lagging or glitching.

And I believe by getting the coordinates you are turning your system from reactive to proactive, cuz you are basically getting the data from the intent(order), rather than waiting for the result.

Do update me on this if you manage to bring it to your expectations
 
All done as it seems. Works perfectly. I removed manual snap, because after testing I realised I forgot that in 1.26 snap was not happening the moment you drop an item. It was happening after you move or pick an item etc. And this is happening automatically after my code sets pathing.

There are triggers for item drop, item pick and item die. Other more exotic cases are not needed in my map but can be easily added with ClearItemPathing and SetTerrainPathable functions. Not tested thoroughly
 

Attachments

All done as it seems. Works perfectly. I removed manual snap, because after testing I realised I forgot that in 1.26 snap was not happening the moment you drop an item. It was happening after you move or pick an item etc. And this is happening automatically after my code sets pathing.

There are triggers for item drop, item pick and item die. Other more exotic cases are not needed in my map but can be easily added with ClearItemPathing and SetTerrainPathable functions. Not tested thoroughly
Now this is clean and really well thought out.

Bulletproof version: clear the saved data when the timer expires.
Code:
call FlushChildHashtable(udg_hash, h)
call DestroyTimer(GetExpiredTimer())

Right now, the timer gets destroyed, but the item and unit data stay saved in those hash slots forever. It's not a major issue for a quick test, but on a longer gaming session, those abandoned entries will pile up and eventually cause a leak.

I know there's the old 'never destroy timers' school of thought to avoid handle-id recycling bugs, but in modern patches, a dead timer is just a memory leak. As long as you're destroying it properly at the end of the callback and nulling the reference, your ram memory will be thankful

(Also, you still have a ghost local location l sitting at the top of that function from your previous drafts i suppose)

Yet again, good job!
 
Last edited:
Now this is clean and really well thought out.

Bulletproof version: clear the saved data when the timer expires.
Code:
call FlushChildHashtable(udg_hash, h)
call DestroyTimer(GetExpiredTimer())

Right now, the timer gets destroyed, but the item and unit data stay saved in those hash slots forever. It's not a major issue for a quick test, but on a longer gaming session, those abandoned entries will pile up and eventually cause a leak.

I know there's the old 'never destroy timers' school of thought to avoid handle-id recycling bugs, but in modern patches, a dead timer is just a memory leak. As long as you're destroying it properly at the end of the callback and nulling the reference, your ram memory will be thankful

(Also, you still have a ghost local location l sitting at the top of that function from your previous drafts i suppose)

Yet again, good job!
Thanks for proofreading. Developing with 2 kids is hard :D. Replacing the map in previous post.
 
Jw, but, does anyone know if this system could somehow be used to fix the problem described here to make a ground unit that only collides with certain other ground units (such as units of the same type)?

If not, sorry for polluting the thread 🙇
 
Jw, but, does anyone know if this system could somehow be used to fix the problem described here to make a ground unit that only collides with certain other ground units (such as units of the same type)?

If not, sorry for polluting the thread 🙇
Well, you can try periodic timer that will modify pathing at location of certain units, caching previous location and clearing pathing in it. Amphibious pathing is something you can expiriment with. But it might end up affecting perfomance. Also there would be a problem of unit colliding with itself (I mean you are not supposed to make ground under a unit unwalkable for this unit)
 
Back
Top