Divergent/mods/Quick Action Wheel/gamedata/scripts/custom_functor_autoinject.s...

539 lines
17 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
--[[
Custom Dynamic Functors, written by demonized
Allows to add/remove item functors dynamically from script
Can override ltx-defined functors and can remove the override to return back to ltx functor
To use in your script look at example below
---------------------------------------------------------------------------------------------------------------------
local function name_condition_function(obj, bag, mode)
if obj then
return true
end
end
local function name_function(obj, bag, mode)
return "st_my_functor_string_in_xml"
end
local function action_condition_function(obj, bag, mode)
if obj then
return true
end
end
local function action_function(obj, bag, mode)
alife_create_item(obj:section(), db.actor)
end
local add_functor = custom_functor_autoinject.add_functor
add_functor("my_name_of_functor", name_condition_function, name_function, action_condition_function, action_function, override_bags<true, false>)
---------------------------------------------------------------------------------------------------------------------
to add your functor you use add_functor function which requires these arguments in order:
name: string, your name of your functor, can be any string.
If the name of functor already exists this function will overwrite functions for it
name_condition_function: function, the condition at which you will get your right-click option for item
name_function: function, the name itself, must return string ID defined in XML files for your option.
if set to return nil, the option won't appear
action_condition_function: function, the condition at which the action will be performed, usually its the same as name_condition_function
you can put nil into argument to use the same function as name_condition_function
action_function: function, the action itself to perform
override_bags: boolean. If its true, then you can override bags and modes to check for condition, otherwise it will use defaults (mode == "inventory" and bag == "actor_bag" or bag == "actor_equ" or bag == "actor_belt")
functions themselves accept obj, bag and mode arguments
obj: current object you right-clicked
bag: current bag, list of possible bags: {"actor_equ","actor_belt","actor_bag","actor_trade_bag","actor_trade","npc_bag","npc_trade","npc_trade_bag"}
mode: current mode, list of possible modes: {"inventory" , "loot" , "trade" , "repair"}
bag and mode is not enabled unless you set override_bags flag to true
removal of functor is done by calling this
--------------------------------------------------------------------------------------
local remove_functor = custom_functor_autoinject.remove_functor
remove_functor("my_name_of_functor")
--------------------------------------------------------------------------------------
you can also override existing functors defined in item's ltx by using this
---------------------------------------------------------------------------------------------------------------------
local override_functor = custom_functor_autoinject.override_functor
override_functor(slot, name_condition_function, name_function, action_condition_function, action_function, override_bags<true, false>)
---------------------------------------------------------------------------------------------------------------------
arguments are same, except you have to provide the slot (first argument) for your override. The slots are 1-10
for example if item's ltx have use1_functor and use1_action_functor then
if you want to override it you have to provide slot 1
be aware that if your name_condition_function or action_condition_function may return false
then old functor will be fired
if you don't want that behaviour, you can define generic "return true" function for those
and check condition for firing in name_function and action_function
removal of override is done by calling this
--------------------------------------------------------------------------------------
local remove_override = custom_functor_autoinject.remove_override
remove_override(slot)
--------------------------------------------------------------------------------------
--]]
local function func_index(t,a,b)
return (t[a].index) < (t[b].index)
end
local function func_index_reverse(t,a,b)
return (t[a].index) > (t[b].index)
end
local function func_value(t, a, b)
return t[a] < t[b]
end
local spairs = spairs
local string_find = string.find
local string_gsub = string.gsub
local table_remove = table.remove
local tonumber = tonumber
local unpack = unpack
--Recursive print of tables similar to PHP print_r function
local function print_r(t)
local print_r_cache={}
local function sub_print_r(t,indent)
if (print_r_cache[tostring(t)]) then
printf(indent.."*"..tostring(t))
else
print_r_cache[tostring(t)]=true
if (type(t)=="table") then
for pos,val in pairs(t) do
if (type(val)=="table") then
printf(indent.."["..pos.."] => "..tostring(t).." {")
sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
printf(indent..string.rep(" ",string.len(pos)+6).."}")
else
printf(indent.."["..pos.."] => "..tostring(val))
end
end
else
printf(indent..tostring(t))
end
end
end
sub_print_r(t," ")
end
local function print_table(table, subs)
local sub
if subs ~= nil then
sub = subs
else
sub = ""
end
for k,v in pairs(table) do
if type(v) == "table" then
print_table(v, sub.."["..k.."]----->")
elseif type(v) == "function" then
printf(sub.."%s = function",k)
elseif type(v) == "userdata" then
if (v.x) then
printf(sub.."%s = %s",k,utils_data.vector_to_string(v))
else
printf(sub.."%s = userdata", k)
end
elseif type(v) == "boolean" then
if v == true then
if(type(k)~="userdata") then
printf(sub.."%s = true",k)
else
printf(sub.."userdata = true")
end
else
if(type(k)~="userdata") then
printf(sub.."%s = false", k)
else
printf(sub.."userdata = false")
end
end
else
if v ~= nil then
printf(sub.."%s = %s", k,v)
else
printf(sub.."%s = nil", k,v)
end
end
end
end
-- Removing element from table and shifting down both key and value
-- Modes: 0 - index, 1 - value, 2 - key, 3 - key-index
local function table_remove_shift(t, val, mode)
local removed = false
local res
if mode == 0 then
for k, v in spairs(t, func_index) do
if removed then
t[k - 1] = v
if t[k - 1].index then
t[k - 1].index = t[k - 1].index - 1
end
if t[k - 1].properties_index then
t[k - 1].properties_index = t[k - 1].properties_index - 1
end
t[k] = nil
elseif v.index and v.index == val then
res = t[k]
t[k] = nil
removed = true
end
end
elseif mode == 1 then
for k, v in spairs(t, func_value) do
if removed then
t[k - 1] = v - 1
t[k] = nil
elseif v == val then
res = t[k]
t[k] = nil
removed = true
end
end
elseif mode == 2 then
for k, v in spairs(t, func_value) do
if removed then
t[k] = v - 1
elseif v == val then
res = t[k]
t[k] = nil
removed = true
end
end
elseif mode == 3 then
for k, v in spairs(t, func_index) do
if removed then
if t[k].index then
t[k].index = t[k].index - 1
end
elseif v.index and v.index == val then
res = t[k]
t[k] = nil
removed = true
end
end
end
return res
end
local ui_inventory_init = ui_inventory.UIInventory.__init
ui_inventory.UIInventory.__init = function(self)
ui_inventory_init(self)
self.custom_functor = {}
self.custom_functor_names = {}
end
local NameCustom = ui_inventory.UIInventory.Name_Custom
function ui_inventory.UIInventory:Name_Custom(obj, bag, temp, i)
obj = self:CheckItem(obj,"Name_Custom " .. i)
if self.custom_functor[i] and self.custom_functor[i].cond_name(obj, bag, self.mode) then
return self.custom_functor[i].func_name(obj, bag, self.mode)
else
return NameCustom(self, obj, bag, temp, i)
end
end
local ActionCustom = ui_inventory.UIInventory.Action_Custom
function ui_inventory.UIInventory:Action_Custom(obj, bag, temp, i)
obj = self:CheckItem(obj,"Action_Custom " .. i)
if self.custom_functor[i] and self.custom_functor[i].cond_action(obj, bag, self.mode) then
return self.custom_functor[i].func_action(obj, bag, self.mode)
else
return ActionCustom(self, obj, bag, temp, i)
end
end
ui_inventory.UIInventory.get_max_custom_functor = function(self)
local max = 0
local max_custom = {}
local max_index = 0
for k, v in pairs(self.properties) do
if string_find(k, "custom_.*") then
if v.index > max_index then
max_index = v.index
end
local x = tonumber(string_gsub(k, "custom_(.*)", "%1"), nil)
if x > max then
max = x
max_custom = v
end
end
end
return max, max_custom, max_index
end
local modes = {
["inventory"] = true,
["loot"] = true,
["trade"] = true,
["repair"] = true
}
local bags = {
["actor_equ"] = true,
["actor_belt"] = true,
["actor_bag"] = true,
["actor_trade_bag"] = true,
["actor_trade"] = true,
["npc_bag"] = true,
["npc_trade"] = true,
["npc_trade_bag"] = true
}
ui_inventory.UIInventory.Mode_Custom_Functor = function(self, obj, bag, temp, i)
return modes[self.mode]
end
ui_inventory.UIInventory.Cont_Custom_Functor = function(self, obj, bag, temp, i)
return bags[bag]
end
ui_inventory.UIInventory.add_custom_functor = function(self, name, cond_name, func_name, cond_action, func_action, override_bags)
local custom_functor_slot
if self.custom_functor_names[name] then
custom_functor_slot = self.custom_functor_names[name]
self.custom_functor[custom_functor_slot].cond_name = cond_name
self.custom_functor[custom_functor_slot].func_name = func_name
self.custom_functor[custom_functor_slot].cond_action = cond_action
self.custom_functor[custom_functor_slot].func_action = func_action
self.properties["custom_" .. custom_functor_slot].mode_func[1] = override_bags and "Mode_Custom_Functor" or "Mode_Custom"
self.properties["custom_" .. custom_functor_slot].cont_func[1] = override_bags and "Cont_Custom_Functor" or "Cont_Custom"
else
local max, max_custom, max_index = self:get_max_custom_functor()
local custom_num = max + 1
local properties_num = max_index + 1
for k, v in spairs(self.properties, func_index_reverse) do
if v.index > max_index then
printf("%s, %s", k, v.index)
v.index = v.index + 1
else
printf("%s, %s, max custom_functor reached", k, v.index)
break
end
end
self.properties["custom_" .. custom_num] = {
index = properties_num,
name_func = {"Name_Custom", max_custom["name_func"][2] + 1},
mode_func = {override_bags and "Mode_Custom_Functor" or "Mode_Custom", max_custom["mode_func"][2] + 1},
cont_func = {override_bags and "Cont_Custom_Functor" or "Cont_Custom", max_custom["cont_func"][2] + 1},
precondition1 = {"Name_Custom", max_custom["precondition1"][2] + 1},
action = {"Action_Custom", max_custom["action"][2] + 1}
}
custom_functor_slot = custom_num
self.custom_functor[custom_functor_slot] = {
index = custom_functor_slot,
properties_index = properties_num,
name = name,
cond_name = cond_name,
func_name = func_name,
cond_action = cond_action,
func_action = func_action
}
self.custom_functor_names[name] = custom_functor_slot
end
end
ui_inventory.UIInventory.remove_custom_functor = function(self, name)
if not self.custom_functor_names[name] then return end
local index = table_remove_shift(self.custom_functor_names, self.custom_functor_names[name], 2)
if not index then return end
local custom_functor = table_remove_shift(self.custom_functor, index, 0)
if not custom_functor then return end
local removed = false
for k, v in spairs(self.properties, func_index) do
if string_find(k, "custom_.*") then
if removed then
local x = tonumber(string_gsub(k, "custom_(.*)", "%1"), nil) - 1
self.properties["custom_" .. x] = self.properties[k]
self.properties["custom_" .. x].index = self.properties["custom_" .. x].index - 1
self.properties["custom_" .. x].name_func[2] = self.properties["custom_" .. x].name_func[2] - 1
self.properties["custom_" .. x].mode_func[2] = self.properties["custom_" .. x].mode_func[2] - 1
self.properties["custom_" .. x].cont_func[2] = self.properties["custom_" .. x].cont_func[2] - 1
self.properties["custom_" .. x].precondition1[2] = self.properties["custom_" .. x].precondition1[2] - 1
self.properties["custom_" .. x].action[2] = self.properties["custom_" .. x].action[2] - 1
self.properties[k] = nil
elseif v.index == custom_functor.properties_index then
self.properties[k] = nil
removed = true
end
elseif removed then
v.index = v.index - 1
end
end
end
ui_inventory.UIInventory.override_functor = function(self, slot, cond_name, func_name, cond_action, func_action, override_bags)
self.custom_functor[slot] = {
cond_name = cond_name,
func_name = func_name,
cond_action = cond_action,
func_action = func_action
}
self.properties["custom_" .. slot].mode_func[1] = override_bags and "Mode_Custom_Functor" or "Mode_Custom"
self.properties["custom_" .. slot].cont_func[1] = override_bags and "Cont_Custom_Functor" or "Cont_Custom"
end
ui_inventory.UIInventory.remove_override = function(self, slot)
self.custom_functor[slot] = nil
self.properties["custom_" .. slot].mode_func[1] = "Mode_Custom"
self.properties["custom_" .. slot].cont_func[1] = "Cont_Custom"
end
ui_inventory.UIInventory.get_functor_at_slot = function(self, slot)
return self.custom_functor[slot]
end
ui_inventory.UIInventory.get_functor_by_name = function(self, name)
if self.custom_functor_names[name] then
return self.custom_functor[self.custom_functor_names[name]], self.custom_functor_names[name]
end
end
-- Adding
local first_update_pending = true
local functor_queue = {}
local function add_to_queue(type, ...)
functor_queue[#functor_queue + 1] = {
type = type,
data = {...}
}
end
local function process_queue()
if not ui_inventory.GUI then
ui_inventory.GUI = ui_inventory.UIInventory()
end
printf("Custom functors, processing queue")
for i, v in ipairs(functor_queue) do
printf("Custom functors, type %s", v.type)
ui_inventory.GUI[v.type](ui_inventory.GUI, unpack(v.data))
end
functor_queue = {}
first_update_pending = false
end
local function actor_on_first_update()
process_queue()
end
function add_functor(name, cond_name, func_name, cond_action, func_action, override_bags)
if not name then return end
local cond_action = cond_action or cond_name
if first_update_pending then
add_to_queue("add_custom_functor", name, cond_name, func_name, cond_action, func_action, override_bags)
return
end
ui_inventory.GUI:add_custom_functor(name, cond_name, func_name, cond_action, func_action, override_bags)
end
function remove_functor(name)
if not name then return end
if first_update_pending then
add_to_queue("remove_custom_functor", name)
return
end
ui_inventory.GUI:remove_custom_functor(name)
end
function override_functor(slot, cond_name, func_name, cond_action, func_action, override_bags)
if slot < 1 or slot > 10 then
printf("functor slot is not valid, min 1, max 10, your slot: %s", slot)
return
end
local cond_action = cond_action or cond_name
if first_update_pending then
add_to_queue("override_functor", slot, cond_name, func_name, cond_action, func_action, override_bags)
return
end
ui_inventory.GUI:override_functor(slot, cond_name, func_name, cond_action, func_action, override_bags)
end
function remove_override(slot)
if slot < 1 or slot > 10 then
printf("functor slot is not valid min 1, max 10, your slot: %s", slot)
return
end
if first_update_pending then
add_to_queue("remove_override", slot)
return
end
ui_inventory.GUI:remove_override(slot)
end
function get_functor_at_slot(slot)
if first_update_pending then
add_to_queue("get_functor_at_slot", slot)
return
end
return ui_inventory.GUI:get_functor_at_slot(slot)
end
function get_functor_by_name(name)
if first_update_pending then
add_to_queue("get_functor_by_name", name)
return
end
return ui_inventory.GUI:get_functor_by_name(name)
end
function print_properties()
if not ui_inventory.GUI then
ui_inventory.GUI = ui_inventory.UIInventory()
end
for k, v in spairs(ui_inventory.GUI.properties, func_index) do
printf("[" .. k .. "] => ")
print_r(v)
end
end
function print_custom_functor()
if not ui_inventory.GUI then
ui_inventory.GUI = ui_inventory.UIInventory()
end
print_r(ui_inventory.GUI.custom_functor)
end
function print_custom_functor_names()
if not ui_inventory.GUI then
ui_inventory.GUI = ui_inventory.UIInventory()
end
print_r(ui_inventory.GUI.custom_functor_names)
end
function on_game_start()
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
end