- Joined
- Sep 26, 2009
- Messages
- 9,534
Are you frustrated with your waits taking too long to finish, finishing too early or generally being random in how long they take? This resource is the silver bullet solution to all of these problems: your GUI waits will now wait the exact length of time - no earlier and no later than expected.
It overwrites the
The original idea behind PreciseWait is credited towards @DoctorGester. I was inspired to overwrite the actual GUI function after I saw an implementation from @MindWorX, who had shown it working in TypeScript.
This is completely GUI-friendly, so GUI triggers can just use Wait as they normally would.
Edit 8 July 2019: For reasons related to compatibility, it is also necessary to override the default scripts which normally sleep the thread (ie. TriggerSleepAction and SyncSelections). It is safe to do either one from an ExecuteFunc thread, so I've updated the script. Regular sleep now defaults to the same PolledWait behavior.
Edit 10 July 2019 - Added compatibility to force-enable Wait functions even when using Lua Fast Triggers. Bear in mind the function "EnableWaits" is actually found within the Fast Triggers script and not this one.
Edit 7 April 2022 - Updated requirement to new version of TimerUtils, opened up the API to allow the user to call the original TriggerSleepAction if they wish.
Edit 16 April 2022 - Updated requirement to use Hook.
Edit 18 April 2022 - Updated to require Hook 3.4.
Edit 22 April 2022 - No longer requires TimerUtils.
Edit 5 May 2022 - Now works correctly with the newer version of Hook. Thanks @Jampion !
Edit 6 June 2022 - Updated to use Hook 5.0, add support for "WaitIndex" which Global Variable Remapper turns into returning the value of "coroutine.running()" in order to ensure the index is unique to that set of triggeractions. The cleanup is tricky with this without setting values to nil, so I recommend using GUI Repair Kit to make sure your arrays (tables) get cleaned up when the thread execution has ended.
Edit 7 July 2022 - Fixed the issue created when switching to coroutine.wrap, as I had mistakenly thought the function would create a new thread each time (it doesn't, so I've reverted it). Also fixed the issue where the return value of the hooked triggeraction was forgotten.
5 Oct 2022 - Now uses OnLibraryInit and treats AddHook as an optional requirement.
17 Oct 2022 - Now uses the Require functionality added to Global Initialization instead of OnLibraryInit.
Example usage:
It overwrites the
PolledWait and TriggerSleepAction
natives, making "Wait - Game Time" and "Wait" in GUI simply do just that.The original idea behind PreciseWait is credited towards @DoctorGester. I was inspired to overwrite the actual GUI function after I saw an implementation from @MindWorX, who had shown it working in TypeScript.
This is completely GUI-friendly, so GUI triggers can just use Wait as they normally would.
Edit 8 July 2019: For reasons related to compatibility, it is also necessary to override the default scripts which normally sleep the thread (ie. TriggerSleepAction and SyncSelections). It is safe to do either one from an ExecuteFunc thread, so I've updated the script. Regular sleep now defaults to the same PolledWait behavior.
Edit 10 July 2019 - Added compatibility to force-enable Wait functions even when using Lua Fast Triggers. Bear in mind the function "EnableWaits" is actually found within the Fast Triggers script and not this one.
Edit 7 April 2022 - Updated requirement to new version of TimerUtils, opened up the API to allow the user to call the original TriggerSleepAction if they wish.
Edit 16 April 2022 - Updated requirement to use Hook.
Edit 18 April 2022 - Updated to require Hook 3.4.
Edit 22 April 2022 - No longer requires TimerUtils.
Edit 5 May 2022 - Now works correctly with the newer version of Hook. Thanks @Jampion !
Edit 6 June 2022 - Updated to use Hook 5.0, add support for "WaitIndex" which Global Variable Remapper turns into returning the value of "coroutine.running()" in order to ensure the index is unique to that set of triggeractions. The cleanup is tricky with this without setting values to nil, so I recommend using GUI Repair Kit to make sure your arrays (tables) get cleaned up when the thread execution has ended.
Edit 7 July 2022 - Fixed the issue created when switching to coroutine.wrap, as I had mistakenly thought the function would create a new thread each time (it doesn't, so I've reverted it). Also fixed the issue where the return value of the hooked triggeraction was forgotten.
5 Oct 2022 - Now uses OnLibraryInit and treats AddHook as an optional requirement.
17 Oct 2022 - Now uses the Require functionality added to Global Initialization instead of OnLibraryInit.
Lua:
OnInit("PreciseWait", function(require) --https://www.hiveworkshop.com/threads/total-initialization.317099/
local hook = require.optionally "AddHook" --https://www.hiveworkshop.com/threads/hook.339153
local remap = require.optionally "GlobalRemap" --https://www.hiveworkshop.com/threads/global-variable-remapper-the-future-of-gui.339308/
--Precise Wait v1.5.0.1
--This changes the default functionality of TriggerAddAction, PolledWait
--and (because they don't work with manual coroutines) TriggerSleepAction and SyncSelections.
local _ACTION_PRIORITY = 1 --Specify the hook priority for hooking TriggerAddAction (higher numbers run earlier in the sequence).
local _WAIT_PRIORITY = -2 --The hook priority for TriggerSleepAction/PolledWait
local function wait(duration)
local thread = coroutine.running()
if thread then
local t = CreateTimer()
TimerStart(t, duration, false, function()
DestroyTimer(t)
coroutine.resume(thread)
end)
coroutine.yield(thread)
end
end
if remap then
--This enables GUI to access WaitIndex as a "local" index for their arrays, which allows
--the simplest fully-instanciable data attachment in WarCraft 3's GUI history. However,
--using it as an array index will cause memory leaks over time, unless you also install
--Lua-Infused GUI: https://www.hiveworkshop.com/threads/lua-infused-gui-automatic-group-location-rect-and-force-leak-prevention.317084/
remap("udg_WaitIndex", coroutine.running)
end
if not hook then
hook = function(varName, userFunc)
local old = rawget(_G, varName)
rawset(_G, varName, userFunc)
return old
end
end
hook("PolledWait", wait, _WAIT_PRIORITY)
hook("TriggerSleepAction", wait, _WAIT_PRIORITY)
local oldSync
oldSync = hook("SyncSelections",
function()
local thread = coroutine.running()
if thread then
function SyncSelectionsHelper() --this function gets re-declared each time, so calling it via ExecuteFunc will still reference the correct thread.
oldSync()
coroutine.resume(thread)
end
ExecuteFunc("SyncSelectionsHelper")
coroutine.yield(thread)
end
end)
local oldAdd
oldAdd = hook("TriggerAddAction", function(trig, func)
--Return a function that will actually be added as the triggeraction, which itself wraps the actual function in a coroutine.
return oldAdd(trig, function() coroutine.resume(coroutine.create(func)) end)
end, _ACTION_PRIORITY)
end)
Example usage:
-
NormalTrigger
-
Events
-
Conditions
-
Actions
-
Wait - 3.00 seconds of game time (waits exactly 3 seconds)
-
Wait - 0.03 seconds of game time (waits exactly 0.03 seconds - previously not possible to have waits be this short)
-
-
Last edited: