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

[Lua] PauseUnits

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,890
A very simple script in lua that mimics BlzPauseUnitEx, with the ability to get the internal stun counter of a unit. (similar to the VJASS version [vJASS] - PauseUnitEx)
Lua:
--[[
        PauseUnits v1.0
    Lua version by Wrda
    ---------------------------------------------------------------
    -   Mimics BlzPauseUnitEx while providing an actual counter   -
    -   which is able to be manipulated.                          -
    ---------------------------------------------------------------
    API
        PauseUnits.pauseUnit(u:unit, flag:boolean)
            - Self-explanatory. Pausing the unit more than one time will result the counter rise up.
            - Unpausing does the reverse.
        PauseUnits.getUnitPauseCounter(u:unit)
            - Returns the pause counter of the unit.
        PauseUnits.setUnitPauseCounter(u:unit, new:integer)
            - Sets the unit pause counter to the new desired value while calling BlzPauseUnitEx internally.
            - O(n)
        PauseUnits.isUnitPaused(u)
            - Checks if the unit is paused.
]]
do
    PauseUnits = {}
    local paused = setmetatable({}, {__mode = "k",
        __index = function(_, k) return 0 end}) -- default counter as 0 while first time accessing by user or the system.

    ---Pauses the unit, with an internal counter. Calls BlzPauseUnitEx internally.
    ---@param u u
    ---@param flag boolean
    function PauseUnits.pauseUnit(u, flag)
        if not paused[u] then paused[u] = 0 end
        if flag then
            paused[u] = paused[u] + 1
        else
            paused[u] = paused[u] - 1
        end
        BlzPauseUnitEx(u, flag)
    end

    ---Gets the pause counter of the unit.
    ---@param u unit
    ---@return integer
    function PauseUnits.getUnitPauseCounter(u)
        return paused[u]
    end

    ---Sets the pause counter of the unit to the new desired value. Calls BlzPauseUnitEx internally.
    ---O(n)
    ---@param u unit
    ---@param new integer
    function PauseUnits.setUnitPauseCounter(u, new)
        local sign = 0
        local flag = false
        local counter = paused[u]
        if new > counter then
            sign = 1
            flag = true
        elseif new < counter then
            sign = -1
            flag = false
        end
        while new ~= counter do
            counter = counter + sign
            BlzPauseUnitEx(u, flag)
        end
        paused[u] = counter
    end

    ---Checks if the unit is paused.
    ---O(n)
    ---@param u unit
    ---@return boolean
    function PauseUnits.isUnitPaused(u)
        return paused[u] > 0
    end
end
 

Attachments

  • PauseUnit.w3m
    47.1 KB · Views: 7

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,890
According to my tests, the native does use it's own counter internally.
Also I forgot to mention, it DOESN'T pause buildings, it simply has no effect. Only the old pause function works. IsUnitPaused doesn't work with BlzPauseUnitEx.

Furthermore, I've just tested other possibilities such as reincarnation and ressurection, and the results are...interesting, to say the least.
Pausing the unit with the old function works before and after reincarnation and ressurection. Subsequent pauses after these events also work.
Pausing the unit with the new native function works before reincarnation and ressurection. Subsequent pauses after these events don't work.

It seems to me that the variables referencing the unit are just simply vanished when the unit is killed while paused by the new native (thanks blizzard).
 

Attachments

  • BlzPauseUnitEx.w3m
    18.1 KB · Views: 7
According to my tests, the native does use it's own counter internally.
But I see you looping over the BlzPauseUnitEx function, just like MyPad did in his vJass version of this resource?


Anyway, BlzPauseUnitEx doesn't work with resurrected units? What about if a unit is not paused when they die, then you try to use BlzPauseUnitEx after they revive - does it work then? If so, then a workaround would be to use Unit Event and detect that event to make sure to unpause the unit before it is registered as dead.
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,890
But I see you looping over the BlzPauseUnitEx function, just like MyPad did in his vJass version of this resource?
Yeah, but we only do that because we have no way to access the counter at all. I only know that I pause the unit 10 times if I do it manually (without any of these systems). So using our own counter keeps track of those calls. The main use of this is the Get and Set of such a counter.

Anyway, BlzPauseUnitEx doesn't work with resurrected units?
What about if a unit is not paused when they die, then you try to use BlzPauseUnitEx after they revive - does it work then? If so, then a workaround would be to use Unit Event and detect that event to make sure to unpause the unit before it is registered as dead.
Yes, pausing after ressurection or reincarnation works when they haven't been paused before.

Also I jumped in a bit too soon, nor BlzPauseUnitEx nor PauseUnit work if the former is used before dying and then after the mentioned events happen.
Some weird voodoo man.
 
Yeah, but we only do that because we have no way to access the counter at all. I only know that I pause the unit 10 times if I do it manually (without any of these systems). So using our own counter keeps track of those calls. The main use of this is the Get and Set of such a counter.

Yes, pausing after ressurection or reincarnation works when they haven't been paused before.

Also I jumped in a bit too soon, nor BlzPauseUnitEx nor PauseUnit work if the former is used before dying and then after the mentioned events happen.
Some weird voodoo man.
I see, thank you for your research! So in that case, systems like this (and @MyPad 's) could try to use an undefend order event to unpause the unit (as a paused unit cannot normally trigger such an order) as a necessary workaround to fix both natives.
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,890
Been a bit lazy for a while, but here's an update that attempts to fix the problems with the native, regarding ressurect, animate dead and reicarnation
Lua:
--[[
        PauseUnits v1.1
    Lua version by Wrda
    https://www.hiveworkshop.com/threads/pauseunits.340457/


    Requires:
        - Global Init: https://www.hiveworkshop.com/threads/global-initialization.317099/ 
 
    ---------------------------------------------------------------
    -   Mimics BlzPauseUnitEx while providing an actual counter   -
    -   which is able to be manipulated.                          -
    -                                                             -
    -   Known issue from this native that fixed with this script: -
    -       - Pausing after reviving or any other form to come    -
    -   back to life wouldn't work while the unit was paused      -
    -   before the start of reviving and during it.               -
    -                                                             -
    -   Note: While pausing buildings work, buildings that are    -
    -    currently training a queue of units/researches aren't    -
    -    stopped in any shape or form. However, attempting to     -
    -    train/researc further things after a pause doesn't work, -
    -    as expected.                                             -
    ---------------------------------------------------------------
    API
        PauseUnits.pauseUnit(u:unit, flag:boolean)
            - Self-explanatory. Pausing the unit more than one time will result the counter rise up.
            - Unpausing does the reverse.
        PauseUnits.getUnitPauseCounter(u:unit)
            - Returns the pause counter of the unit.
        PauseUnits.setUnitPauseCounter(u:unit, new:integer)
            - Sets the unit pause counter to the new desired value while calling BlzPauseUnitEx internally.
            - O(n)
        PauseUnits.isUnitPaused(u)
            - Checks if the unit is paused.
]]
do
    PauseUnits = {}
    local paused = setmetatable({}, {__mode = "k",
        __index = function(_, k) return 0 end}) -- default counter as 0 while first time accessing by user or the system.
    local CUSTOM_DEFEND = FourCC('A000') -- Your custom defend ability
    
    ---Pauses the unit, with an internal counter. Calls BlzPauseUnitEx internally.
    ---@param u u
    ---@param flag boolean
    function PauseUnits.pauseUnit(u, flag)
        if flag then
            paused[u] = paused[u] + 1
        else
            paused[u] = paused[u] - 1
        end
        BlzPauseUnitEx(u, flag)
    end
    ---Gets the pause counter of the unit.
    ---@param u unit
    ---@return integer
    function PauseUnits.getUnitPauseCounter(u)
        return paused[u]
    end
    ---Sets the pause counter of the unit to the new desired value. Calls BlzPauseUnitEx internally.
    ---O(n)
    ---@param u unit
    ---@param new integer
    function PauseUnits.setUnitPauseCounter(u, new)
        local sign = 0
        local flag = false
        local counter = paused[u]
        if new > counter then
            sign = 1
            flag = true
        elseif new < counter then
            sign = -1
            flag = false
        end
        while new ~= counter do
            counter = counter + sign
            BlzPauseUnitEx(u, flag)
        end
        paused[u] = counter
    end
    ---Checks if the unit is paused.
    ---O(n)
    ---@param u unit
    ---@return boolean
    function PauseUnits.isUnitPaused(u)
        return paused[u] > 0
    end
    
    --actions for events.
    local enterMapActions = function(u)
        local unit = u or GetTriggerUnit()
        UnitAddAbility(unit, CUSTOM_DEFEND)
        UnitMakeAbilityPermanent(unit, true, CUSTOM_DEFEND)
    end
    local deathActions = function()
        PauseUnits.setUnitPauseCounter(GetTriggerUnit(), 0)
    end
    --If unit has become alive again through reicarn, ressurect, animate dead
    local wasUnitRevived = Condition(function() return (GetIssuedOrderId() == 852056) and UnitAlive(GetTriggerUnit()) end)
    local unitRevivedActions = function()
        PauseUnits.setUnitPauseCounter(GetTriggerUnit(), 0)
    end
    OnTrigInit(function()
        for i = 0, GetBJMaxPlayers() - 1 do
            SetPlayerAbilityAvailable(Player(i), CUSTOM_DEFEND, false)
        end
        local area = CreateRegion()
        RegionAddRect(area, bj_mapInitialPlayableArea)
        local g = CreateGroup()
        GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, nil)
        ForGroup(g, function() enterMapActions(GetEnumUnit()) end)
        DestroyGroup(g)
        local enterMap = CreateTrigger() --register on map enter
        TriggerRegisterEnterRegion(enterMap, area, nil)
        TriggerAddAction(enterMap, enterMapActions)
        local death = CreateTrigger() --actual death
        TriggerRegisterAnyUnitEventBJ(death, EVENT_PLAYER_UNIT_DEATH)
        TriggerAddAction(death, deathActions)
        local other = CreateTrigger() --finished reicarn, ressurect, started animate dead
        TriggerRegisterAnyUnitEventBJ(other, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        TriggerAddCondition(other, wasUnitRevived)
        TriggerAddAction(other, unitRevivedActions)
    end)
end
Still, I was unable to fix this completely. It seems to work the majority of the time, but I can't reproduce this error.
 
Top