--[[ 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) --------------------------------------------------------------------------------------------------------------------- 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) --------------------------------------------------------------------------------------------------------------------- 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