- Joined
- Nov 7, 2014
- Messages
- 571
warning: I've only tested this on version 1.31, it might not work on later versions (Reforge).
my-preload-file.pld:
loading the Lua script:
Running the watch script
Introduction
When the game loads a map, at some point, it also loads and runs the map script. After the script is loaded, any further changes to it are not reflected until the map is restarted. This is very inconvenient because map restarts are not instantaneous. Many scripting languages, including Lua, have built-in functions for loading new scripts. One such function is the 'load' function (the 'loadfile' function is not available), it loads a string (chunk) and returns a function which, when called, executes the Lua code inside. Script reloading is simply loading a changed script and executing it.Loading a Lua script from a file
We can use TriggerHappy's method for reading strings from a file. It relies on thePreloader
and BlzGetAbilityTooltip
native functions.my-preload-file.pld:
JASS:
function PreloadFiles takes nothing returns nothing
call BlzSetAbilityTooltip ('Agyv', "<put-Lua-script-here-but-make-sure-it-is-valid-Jass-string>", 0)
endfunction
loading the Lua script:
Lua:
-- the absolute path to the preload file is: 'c:\users\<user-name>\documents\warcraft iii\custommapdata\my-preload-file.pld'
-- documents = My Documents
--
local preload_file = 'my-preload-file.pld'
Preloader(preload_file)
local text = BlzGetAbilityTooltip(FourCC('Agyv'), 0)
Detecting script changes
See the 'watch-wc3-script-for-changes.lua' in the attached zip file. I've used Lua (and a tiny C program) for this, but there are better ways to do it (some OS API that notifies us when the file has changed, or not using Lua =)).
Running the watch script
I use a '.bat' script similar to this to start the watch script:
Code:
@echo off
set watch_script="<path-to>\watch-wc3-script-for-changes.lua"
set script_file="<path-to-the-script-file-we-want-to>\reload.lua"
set preload_file="c:\users\<user-name>\documents\warcraft iii\custommapdata\my-preload-file.pld"
lua %watch_script% %script_file% %preload_file%
Reloading the script in-game
Lua:
local sprintf = string.format
local function writefln(fmt, ...)
print(sprintf(fmt, ...))
end
local intcc = FourCC
local function reload_script()
local script_file = 'reload.lua'
local preload_file = 'my-preload-file.pld'
Preloader(preload_file)
local text = BlzGetAbilityTooltip(intcc('Agyv'), 0)
local fn
local err
local ok
fn, err = load(text, script_file, 't', _ENV)
if err ~= nil then
writefln('syntax error: %s', err)
else
clear_all()
ok, err = pcall(fn)
if not ok then
writefln('error: %s', err)
end
end
end
...
-- I use the Esc key to reload the script
local t = CreateTrigger()
TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
TriggerAddAction(t, reload_script)
...
If you are using the World Editor for writing Lua watch out for those '%' signs.
Undoing side effects
One good thing of restarting the map after a script change is that it gives us a "clean slate". Imagine we have the following script:
Lua:
CreateUnit(Player(0), FourCC('Hpal'), 0.0, 0.0, 270.0)
If we reload it, we would get a Paladin on the map. If we then change it to:
Lua:
CreateUnit(Player(0), FourCC('Ofar'), 0.0, 0.0, 270.0)
And reload it, we would get a Far Seer on the map, but the Paladin would still be there as well.
A simple but annoying way around this is to wrap the native CreateUnit (in this case) with something like this:
Lua:
local native_CreateUnit = CreateUnit
local clear_CreateUnit
do
local xs = {}
_ENV.CreateUnit = function(a1, a2, a3, a4, a5)
local x = native_CreateUnit(a1, a2, a3, a4, a5)
xs[#xs+1] = x
return x
end
clear_CreateUnit = function()
for a = #xs, 1, -1 do
RemoveUnit(xs[a])
xs[a] = nil
end
end
end
local function clear_all()
clear_CreateUnit()
end
local function reload_script()
...
clear_all()
ok, err = pcall(fn)
...
We call the clear_all/clear_CreateUnit function, which effectively removes our side effects, before running the new version of the script.
I don't like Lua, can I reload Jass instead?
Yes, see Jhcr by Lep.
I use a '.bat' script similar to this to start the watch script:
Code:
@echo off
set watch_script="<path-to>\watch-wc3-script-for-changes.lua"
set script_file="<path-to-the-script-file-we-want-to>\reload.lua"
set preload_file="c:\users\<user-name>\documents\warcraft iii\custommapdata\my-preload-file.pld"
lua %watch_script% %script_file% %preload_file%
Reloading the script in-game
Lua:
local sprintf = string.format
local function writefln(fmt, ...)
print(sprintf(fmt, ...))
end
local intcc = FourCC
local function reload_script()
local script_file = 'reload.lua'
local preload_file = 'my-preload-file.pld'
Preloader(preload_file)
local text = BlzGetAbilityTooltip(intcc('Agyv'), 0)
local fn
local err
local ok
fn, err = load(text, script_file, 't', _ENV)
if err ~= nil then
writefln('syntax error: %s', err)
else
clear_all()
ok, err = pcall(fn)
if not ok then
writefln('error: %s', err)
end
end
end
...
-- I use the Esc key to reload the script
local t = CreateTrigger()
TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
TriggerAddAction(t, reload_script)
...
If you are using the World Editor for writing Lua watch out for those '%' signs.
Undoing side effects
One good thing of restarting the map after a script change is that it gives us a "clean slate". Imagine we have the following script:
Lua:
CreateUnit(Player(0), FourCC('Hpal'), 0.0, 0.0, 270.0)
Lua:
CreateUnit(Player(0), FourCC('Ofar'), 0.0, 0.0, 270.0)
A simple but annoying way around this is to wrap the native CreateUnit (in this case) with something like this:
Lua:
local native_CreateUnit = CreateUnit
local clear_CreateUnit
do
local xs = {}
_ENV.CreateUnit = function(a1, a2, a3, a4, a5)
local x = native_CreateUnit(a1, a2, a3, a4, a5)
xs[#xs+1] = x
return x
end
clear_CreateUnit = function()
for a = #xs, 1, -1 do
RemoveUnit(xs[a])
xs[a] = nil
end
end
end
local function clear_all()
clear_CreateUnit()
end
local function reload_script()
...
clear_all()
ok, err = pcall(fn)
...
We call the clear_all/clear_CreateUnit function, which effectively removes our side effects, before running the new version of the script.