Divergent/mods/Looting Takes Time/gamedata/scripts/loot_searching.script

342 lines
10 KiB
Plaintext

local corpses = {}
local initial_inv = {}
local cur_npc_id = nil
local sfind = string.find
local snd_t = {
["w_ammo"] = { "ammo_1", "ammo_2", "ammo_3", "ammo_4" },
["w_misc"] = { "parts_1", "parts_2", "parts_3" },
["w_explosive"] = { "grenade_1", "grenade_2" },
["w_melee"] = { "knife_1", "knife_2" },
["wpn"] = { "wpn_1", "wpn_2" },
["o_helmet"] = { "cloth_1" },
["outfit"] = { "cloth_2", "cloth_3" },
["i_arty"] = { "cloth_1", "cloth_2" },
["i_arty_cont"] = { "wpnbig_1", "wpnbig_2" },
["i_arty_junk"] = { "cloth_1", "cloth_2" },
["i_attach"] = { "parts_1", "parts_2", "parts_3" },
["i_drink"] = { "bottle_1", "bottle_2", "bottle_3", "bottle_4" },
["i_medical"] = { "pills_1", "pills_2" },
["i_tool"] = { "take_all" },
["i_device"] = { "parts_1", "parts_2" },
["i_letter"] = { "money_paper" },
["i_part"] = { "parts_1", "parts_2", "parts_3" },
["money_paper"] = { "money_paper" },
["money_coin"] = { "money_coin_1", "money_coin_2" },
["generic"] = { "generic_3", "generic_4", "generic_5", "parts_2" }, -- i_upgrade, i_repair, i_kit, i_food, i_mutant_cooked, i_mutant_raw, i_mutant_part, i_mutant_belt, i_backpack
}
local replace_snd_by_sec = {
["money_paper"] = { "cash", "money" },
["money_coin"] = { "roubles" },
["generic"] = { "joint", "marijuana", "cigarettes", "cigar", "tobacco", "drug_booster", "bandage", "jgut", "stimpack", "rebirth", "tetanus", "glucose", "cocaine", "salicidic_acid", "morphine", "adrenalin" },
}
local replace_snd_by_kind = {
["wpn"] = { "w_rifle", "w_pistol", "w_smg", "w_shotgun", "w_sniper" },
["outfit"] = { "o_heavy", "o_light", "o_medium", "o_sci" },
}
-- mcm
local time_min = loot_searching_mcm.get_config("time_min")
local time_max = loot_searching_mcm.get_config("time_max")
local hud_enable = loot_searching_mcm.get_config("hud_enable")
local sound_enable = loot_searching_mcm.get_config("sound_enable")
local sound_enable_vol = loot_searching_mcm.get_config("sound_enable_vol")
local sound_background_enable = loot_searching_mcm.get_config("sound_bg_enable")
local sound_background_enable_vol = loot_searching_mcm.get_config("sound_bg_enable_vol")
local hud_icon = loot_searching_mcm.get_config("hud_icon")
local debugx = loot_searching_mcm.get_config("debugx")
--------
function pr(...)
if not debugx then return end
printf(...)
end
function GUI_on_show()
CreateTimeEvent("init_inv_ev", "init_inv_ac", 0, save_initial_inv) -- has to be on next update
end
function save_initial_inv()
local inventory = ui_inventory.GUI
if not inventory then
return true
end
local npc = inventory:GetPartner() or nil
local npc_id = npc and npc:id() or nil
if inventory.mode ~= "loot" or not npc_id then
return true
end
if not IsStalker(npc) or IsInvbox(npc) then
return true
end
if not initial_inv[npc_id] then
initial_inv[npc_id] = {}
corpses[npc_id] = {}
local function initial_items(owner, obj)
if not initial_inv[npc_id][obj:id()] then
pr("$ GUI ON SHOW npc item: [ %s ]", obj:id())
initial_inv[npc_id][obj:id()] = true
end
end
npc:iterate_inventory(initial_items, npc)
npc:iterate_ruck(initial_items, npc)
end
cur_npc_id = npc_id -- for hud
return true
end
local basePI = ui_inventory.UIInventory.ParseInventory
function ui_inventory.UIInventory:ParseInventory(npc, all, id_list, ignore_kind)
local inv = basePI(self, npc, all, id_list, ignore_kind)
if not (self:IsInvOwner(npc) and npc:id() ~= AC_ID and self.mode == "loot" and all and id_list) then
return inv
end
local res = dup_table(inv)
-- corpses == (nil = timer for item does not exist, true = timer ticking, false = timer ended)
local npc_id = npc:id()
for item_id, item in pairs(res) do
local item_timer = show_item_time(item_id)
-- if item was in initial spawn and item isn't on timer
if initial_inv[npc_id] and initial_inv[npc_id][item_id] and corpses[npc_id] then
-- create timer if it does not exist
if corpses[npc_id][item_id] == nil then
corpses[npc_id][item_id] = true
pr("~ id_list || Creating Time Event for npc_id: [ %s ] || itm_sec: [ %s ] || itm_id: [ %s ] || itm_tmr: [ %s ]", npc_id, item:section(), item_id, item_timer)
CreateTimeEvent("item_hiding", "item_hiding_" .. npc_id .. "_" .. item_id, item_timer, show_item, npc_id, item_id)
end
-- and pass table without this item if timer didn't exist or still ticking (guess just true works)
if corpses[npc_id][item_id] ~= false then
res[item_id] = nil
end
end
end
return res
end
function show_item_time(id)
local item = level.object_by_id(id)
local sec = item:section()
local weight = SYS_GetParam(2, sec, "inv_weight", 2)
weight = weight > 0.05 and weight or 0.05
local grid = SYS_GetParam(2, sec, "inv_grid_width", 1) * SYS_GetParam(2, sec, "inv_grid_height", 1) -- size of item xd
local time = math.random(time_min, time_max) - weight - grid
time = time > 1 and time or 1
return time
end
function show_item(npc_id, id)
local gui = GetActorMenu()
if corpses[npc_id] and corpses[npc_id][id] then
pr("- Show at npc: [ %s ] item: [ %s ]", npc_id, id)
item_found_snd(id)
corpses[npc_id][id] = false
if actor_menu.get_last_mode() == 4 and gui.npc_id == npc_id then --if loot gui not open or is open to a diff NPC don't refresh.
--Sorting plus fix
if zzz_rax_sortingplus_mcm and zzz_rax_sortingplus_mcm.NPCINV then
ui_inventory.GUI.CC["npc_bag"]:Reset()
end
gui:UpdateInventories()
end
end
return true
end
function item_found_snd(id)
if not sound_enable then return end
local item = level.object_by_id(id)
local sec = item and item:section() or nil
if not sec then return end
local kind = SYS_GetParam(0, sec, "kind", "generic")
-- generalize some kinds
local skip_sfind = false
for new_kind, t in pairs(replace_snd_by_kind) do
for i = 1, #t do
if t[i] == kind then
kind = new_kind
skip_sfind = true
break
end
end
end
-- replace some kinds by section string find
if not skip_sfind then
for new_kind, t in pairs(replace_snd_by_sec) do
for i = 1, #t do
if sfind(sec, t[i]) then
kind = new_kind
break
end
end
end
end
local snd_kind = snd_t[kind] or snd_t["generic"]
local random_kind_snd = snd_kind[math.random(1, #snd_kind)]
local snd_file = "interface\\items\\inv_items_" .. random_kind_snd
local snd = sound_object(snd_file)
if snd then
snd:play_at_pos(db.actor, VEC_ZERO, 0, sound_object.s2d)
snd.volume = snd.volume * sound_enable_vol
else
pr("! sound: [ %s ] not found", snd_file)
end
end
function GUI_on_hide()
local inventory = ui_inventory.GUI
if not inventory then return end
local npc = inventory:GetPartner() or nil
local npc_id = npc and npc:id() or nil
if inventory.mode ~= "loot" then return end
if not npc_id then return end
-- check if val is true (timer still ticking) when we close loot window
for npc_id, t in pairs(corpses) do
for item_id, val in pairs(t) do
if val == true then
pr("! Removing time event for npc: [ %s ] and item: [ %s ]", npc_id, item_id)
RemoveTimeEvent("item_hiding", "item_hiding_" .. npc_id .. "_" .. item_id)
corpses[npc_id][item_id] = nil -- start timer again next time we loot, if item didnt appear and we have closed the window before
end
end
end
cur_npc_id = nil -- for hud
end
-- HUD
function ui_inventory.UIInventory:InitIconX()
self.loot_xml = CScriptXmlInit()
self.loot_xml:ParseFile("ui_xcvb_looting.xml")
if hud_icon == 1 then
self.loot_icon_x = self.loot_xml:InitTextWnd("looting_text", self)
self.loot_icon_x:SetText(game.translate_string("st_xcvb_looting"))
elseif hud_icon == 2 then
self.loot_icon_x = self.loot_xml:InitStatic("poarch_cringe_seq", self)
end
end
local base = ui_inventory.UIInventory.UpdateInfo
function ui_inventory.UIInventory:UpdateInfo(go)
self:LootUpdateX()
base(self, go)
end
local hud_tmr
local snd_tmr = 0
local dots = 0
function ui_inventory.UIInventory:LootUpdateX()
local tg = time_global()
if (hud_tmr and tg < hud_tmr) then return end
hud_tmr = tg + 500
if not self.loot_icon_x then
self:InitIconX()
end
if not cur_npc_id then
self.loot_icon_x:Show(false)
return
end
if not hud_enable then
self.loot_icon_x:Show(false)
return
end
local itms = 0 -- numeric instead of boolean in case of percents or bars someday
for npc_id, t in pairs(corpses) do
if npc_id == cur_npc_id then
for item_id, val in pairs(t) do
if val then
itms = itms + 1
end
end
end
end
if itms <= 0 then
self.loot_icon_x:Show(false)
return
end
local function dots_str()
local str = game.translate_string("st_xcvb_looting")
dots = dots < 5 and dots + 1 or 1
for i = 1, dots do
str = str .. " ."
end
return str
end
-- looting sound
if sound_background_enable and tg > snd_tmr then
local snd_file = "looting\\looting_" .. math.random(1, 12)
local snd = sound_object(snd_file)
if snd then
snd_tmr = tg + snd:length() * 0.5
pr("new sound: %s || delay_by: %s", snd_file, snd:length() * 0.5)
snd:play_at_pos(db.actor, VEC_ZERO, 0, sound_object.s2d)
snd.volume = snd.volume * sound_background_enable_vol
end
end
---------
self.loot_icon_x:Show(true)
if hud_icon == 1 then
self.loot_icon_x:SetText(dots_str())
end
end
----------------------
function server_entity_on_unregister(se_obj, typ)
local id = se_obj.id
corpses[id] = nil
initial_inv[id] = nil
end
function on_option_change()
time_min = loot_searching_mcm.get_config("time_min")
time_max = loot_searching_mcm.get_config("time_max")
hud_enable = loot_searching_mcm.get_config("hud_enable")
sound_enable = loot_searching_mcm.get_config("sound_enable")
sound_enable_vol = loot_searching_mcm.get_config("sound_enable_vol")
sound_background_enable = loot_searching_mcm.get_config("sound_bg_enable")
sound_background_enable_vol = loot_searching_mcm.get_config("sound_bg_enable_vol")
debugx = loot_searching_mcm.get_config("debugx")
end
function on_game_start()
RegisterScriptCallback("GUI_on_show", GUI_on_show)
RegisterScriptCallback("GUI_on_hide", GUI_on_hide)
RegisterScriptCallback("server_entity_on_unregister", server_entity_on_unregister)
RegisterScriptCallback("on_option_change", on_option_change)
end