539 lines
17 KiB
Plaintext
539 lines
17 KiB
Plaintext
--[[
|
|
|
|
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
|