Tutorial; Sharing State Between Scopes - HWRM/KarosGraveyard GitHub Wiki
The problem
There is no way to share any variables or state with other Lua scopes which may be running alongside the current one (outside sharing single numbers using UI_SetElementCustomData
/ UI_GetElementCustomData
).
For exmaple, a script running the mission rules will be running during gametime, and so will ship customcode scripts, but they have no way to communicate with eachother!.
This is because they run in different 'scopes', meaning their Lua environments are seperate:
-- my_level_script.lua
SOME_GLOBAL = 10;
-- elsewhere.lua
print(SOME_GLOBAL); -- nil
In order to overcome this, we can write to the UI in a very specific way and keep a string representation of a lua table there!
Implementation
-- scripts/state_scope.lua
tbl_util = {};
function tbl_util:merge(tbl_a, tbl_b, merger)
merger = merger or function (a, b)
if (type(a) == "table" and type(b) == "table") then
return %self:merge(a, b);
else
return (b or a);
end
end
if (tbl_a == nil and tbl_b ~= nil) then
return tbl_b;
elseif (tbl_a ~= nil and tbl_b == nil) then
return tbl_a;
elseif (tbl_b == nil and tbl_b == nil) then
return {};
end
local out = {};
-- basic copy
for k, v in tbl_a do
out[k] = v;
end
for k, v in tbl_b do
if (out[k] == nil) then
out[k] = v;
else
out[k] = merger(out[k], v);
end
end
return out;
end
function makeStateHandle(screen_name, dropdown_host_el)
screen_name = screen_name or "DefaultStateScreen";
dropdown_host_el = dropdown_host_el or "host_dropdown";
if (UI_GetElementCustomData(screen_name, dropdown_host_el) ~= 1) then
UI_AddDropDownListboxItem(screen_name, dropdown_host_el, "_", "", 0, "{}");
UI_SetElementCustomData(screen_name, dropdown_host_el, 1); -- 1 = init
end
return function(new_state, overwrite)
UI_SelectDropDownListboxItemIndex(%screen_name, %dropdown_host_el, 0);
local current_state = dostring("return " .. (UI_GetDropdownListBoxSelectedCustomDataString(%screen_name, %dropdown_host_el) or "{}"));
if (new_state) then
UI_ClearDropDownListbox(%screen_name, %dropdown_host_el);
if (overwrite) then
current_state = new_state;
else
current_state = tbl_util:merge(current_state, new_state);
end
local asStr = function (v, tblParser)
if (type(v) == "table") then
local out = "{";
for k, v in v do
local i = tostring(k);
if (type(k) == "number") then
i = "[" .. k .. "]";
end
out = out .. i .. "=" .. tblParser(v, tblParser) .. ",";
end
out = out .. "}";
return out;
elseif (type(v) == "string") then
return "\"" .. v .. "\"";
else
return tostring(v);
end
end
local state_str = asStr(current_state, asStr);
UI_AddDropDownListboxItem(%screen_name, %dropdown_host_el, "_", "", 0, state_str);
end
return current_state;
end
end
The host screen:
-- ui/newui/defaultstatescreen.lua
DefaultStateScreen = {
size = { 0, 0, 0, 0 }
;
{
type = "DropDownListBox",
name = "host_dropdown",
ListBox = {
type = "ListBox",
ItemToClone = {
type = "TextListBoxItem"
},
}
}
};
You also need to register the screen in uisettings.lua
(which is a stock file, make sure to paste what's in here first and just add a new entry):
{
name = "DefaultStateScreen",
filename = "data:\\ui\\newui\\defaultstatescreen.lua",
activated = 0
},
Usage
-- some scope, perhaps:
-- leveldata/campaign/my_campaign/my_mission.lua
dofilepath("data:scripts/state_scope.lua");
local state = makeStateHandle();
state({ a = "foo", b = 10, c = { d = 1 }});
-- some other scope, i.e:
-- ships/hgn_mothership/hgn_mothership.lua
dofilepath("data:scripts/state_scope.lua");
local state = makeStateHandle();
-- if this code runs after the code in the above script (in terms of gametime), we can access the state we wrote:
local s = state();
print(s.a); -- 'foo'
print(s.b); -- '10'
print(tostring(s.c)); -- table: <table address>
print(s.c.d); -- '1'
You can get and set the state from any runtime scope (any scope with access to UI_
calls).
Remember that global state can get messy quickly since many parts of your code may potentially modify it. Additionally, we are writing and reading from this 'superglobal' table asynchronously, meaning someone may edit the table in-between us setting and reading!