--[[ Wrapper class to let you autoinject things via monkey patch to all traders, respecting the restock time. How to use: Monkey patch the update function here in your script. ex: TraderAuto = trader_autoinject.update function trader_autoinject.update(npc) TraderAuto(npc) add_custom_crap(npc) -- you define this function ok end Some functions provided below for convenience. Note: If you want to iterate NPC inventory to check for items, fire a time event to allow the items to register on new game. --]] -- find = string.find local function t2c(t) if not t then return nil end local ct = game.CTime() ct:set(t.Y,t.M,t.D,t.h,t.m,t.s,t.ms) return ct end local function c2t(ct) if not ct then return nil end -- printf('%s, %s',ct,type(ct)) local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0 Y, M, D, h, m, s, ms = ct:get(Y, M, D, h, m, s, ms) return { Y=Y, M=M, D=D, h=h, m=m, s=s, ms=ms } end TraderUpdate = trade_manager.update function trade_manager.update(npc, force_refresh) local id = npc:id() if not npc:alive() then return default end local reup_time = trade_manager.get_trade_profile(id, "resupply_time") TraderUpdate(npc, force_refresh) local restock_time = game_difficulties.get_eco_factor("restock") or 24 if force_refresh then restock_time = 0 end if reup_time and game.get_game_time():diffSec(t2c(reup_time)) < (restock_time * 3600) then -- print_dbg("Not time to resupply yet!") return end disable_info("sleep_active") CreateTimeEvent("custom_update"..npc:id(), "custom_resupply"..npc:id(), 0.1, timed_update, npc) end -- Add easier to trace callback function timed_update(npc) update(npc) SendScriptCallback("trader_on_restock",npc) return true end -- monkeypatch me function update(npc) end -- util functions to help with monkey patching function get_faction_goodwill(faction) end COMPANION = 0 -- companions got special trade logic, this is just to catch errors MECHANIC = 1 -- mechanics/techs BARMAN = 2 -- exclusive food suppliers like Spirit MEDIC = 3 -- medics SUPPLIER = 4 -- everyone else that sells crap -- return trader type as int, or nil if error function get_trader_type(npc) local st = db.storage[npc:id()] if not st then return -1 end local trader = false if npc:character_community() == "trader" or npc:clsid() == clsid.script_trader or npc:clsid() == clsid.trader then trader = true end if find(npc:section(),"trader") then trader = true end local cini = st.ini local logic = st.section_logic if not logic and not trader then return -1 end local trade_logic = cini and cini:r_string_ex(logic, "trade") if not trade_logic then return -1 end if find(trade_logic, "companion") then return COMPANION elseif find(trade_logic, "trade_generic_mechanic") then return MECHANIC elseif find(trade_logic, "trade_generic_barman") then return BARMAN elseif find(trade_logic, "trade_generic_medic") then return MEDIC else return SUPPLIER end end -- return supply level of npc, like suppy_1, supply_2, etc -- as_number removes the supply_ prefix and only returns as int function supply_level(npc, as_number) local profile = trade_manager.get_trade_profile(npc:id(), "cfg_ltx") -- printf("Profile is %s", profile) local config = trade_manager.get_trade_cfg(profile) if not config then return end local str = config:r_string_ex("trader", "buy_supplies") if not (str) then return -- no buy_supplies this is normal end local condlist = xr_logic.parse_condlist(npc, "trader", "buy_supplies", str) str = condlist and xr_logic.pick_section_from_condlist(db.actor, npc, condlist) if as_number then local num = str_explode(str, "_") return tonumber(num[2]) else return str end end -- collapse several tables into one table, the way sections work in ltx files -- tables should be in section -> amount format -- precedence goes up to last table, meaning whatever is in the last table will be the last changes applied function merge_tables(tables) local final_table = {} if #tables > 0 then copy_table(final_table, tables[1]) if #tables > 1 then for i=2, #tables do for k,v in pairs(tables[i]) do final_table[k] = v end end end end return final_table end local furniture = { ["esc_m_trader"] = true, ["red_m_lesnik"] = true } local blacklisted_comms = { ["trader"] = true, ["monster"] = true } -- used to get the real community of the NPC by checking spawn id -- author: HarukaSai function get_real_community(npc, default) if furniture[npc:name()] then return "stalker" end local community = character_community(npc) if not blacklisted_comms[community] then return community end local squad_community = get_object_squad(npc):get_squad_community() if not blacklisted_comms[squad_community] then return squad_community else return default end end -- to_spawn should be table of sections to amount -- if check_existing is true, only spawns up to that amount in trader inventory. else arbitrarily spawns function spawn_items(npc, to_spawn, check_existing) local npc_name = npc:name() local alive_or_furniture = xr_conditions.is_alive(db.actor, npc) or furniture[npc_name] if not alive_or_furniture then return end local supply_table = {} copy_table(supply_table, to_spawn) if check_existing then local function itr_inv(temp, item) if supply_table[item:section()] and supply_table[item:section()] > 0 then -- printf("Found 1 of %s", item:section()) supply_table[item:section()] = supply_table[item:section()] - 1 end end npc:iterate_inventory(itr_inv) end for k,v in pairs(supply_table) do -- printf("Creating %s of %s", v, k) for i=1, v do -- printf("Created %s", k) alife_create_item(k, npc) end end end AddScriptCallback("trader_on_restock")