--[[ ------------------------------------------------------------ -- Survival Mode Remade - Extra items from stash boxes ------------------------------------------------------------ -- MCM-configurable script to give the player a chance to get some extra items whenever they open a stash box. -- by dph-hcl ------------------------------------------------------------ ]]-- -- REPAIR KITS local repair_tier1 = { "glue_a", "glue_b", "glue_e", "gun_oil", "gun_oil_ru", "gun_oil_ru_d", "armor_repair_fa", "sharpening_stones", } local repair_tier2 = { "sharpening_stones", "cleaning_kit_p", "cleaning_kit_s", "cleaning_kit_r5", "cleaning_kit_r7", "sewing_kit_b", "sewing_kit_a", } local repair_tier3 = { "cleaning_kit_u", "toolkit_p", "toolkit_s", "sewing_kit_h", "medium_repair_kit", "helmet_repair_kit", "light_repair_kit", } local repair_tier4 = { "toolkit_r5", "toolkit_r7", "heavy_repair_kit", "exo_repair_kit", } -- BASIC AMMO local basic_ammo = { "ammo_11.43x23_fmj", "ammo_12x70_buck", "ammo_7.62x25_p", "ammo_12x76_zhekan", "ammo_5.45x39_fmj", "ammo_5.45x39_ep", "ammo_5.56x45_fmj", "ammo_5.56x45_ss190", "ammo_9x18_fmj", "ammo_9x19_fmj", "ammo_11.43x23_hydro", "ammo_12x76_dart", "ammo_7.62x25_ps", "ammo_5.45x39_ap", "ammo_5.56x45_ap", "ammo_9x18_ap", "ammo_9x18_pmm", "ammo_9x19_ap", "ammo_9x19_pbp", } -- ADVANCED AMMO local advanced_ammo = { "ammo_357_hp_mag", "ammo_7.62x39_fmj", "ammo_7.62x51_fmj", "ammo_7.62x54_7h1", "ammo_9x39_pab9", "ammo_7.62x39_ap", "ammo_7.62x51_ap", "ammo_7.62x54_7h14", "ammo_7.62x54_ap", "ammo_9x39_ap", } -- EXOTIC AMMO local exotic_ammo = { "ammo_12.7x55_fmj", "ammo_4.6x30_fmj", "ammo_5.7x28_ss195", "ammo_7.92x33_fmj", "ammo_m209", "ammo_vog-25", "ammo_12.7x55_ap", "ammo_5.7x28_ss190", "ammo_50_bmg", "ammo_7.92x33_ap", "ammo_magnum_300", "ammo_pkm_100", "ammo_gauss", } -- USEFUL ITEMS local useful_tier1 = { "device_torch_dummy", "batteries_dead", "batteries_dead", "batteries_dead", "detector_simple", "af_iam", "itm_sleepbag", "wpn_knife2", "wpn_axe", "kit_hunt", "wpn_binoc_inv", "itm_actor_backpack", "leatherman_tool", "itm_sleepbag", "equ_small_pack", "af_aac", } local useful_tier2 = { "device_torch_nv_1", "detector_advanced", "wpn_knife3", "wpn_axe2", "equ_small_military_pack", "itm_tent", } local useful_tier3 = { "device_torch_nv_2", "detector_scientific", "wpn_knife4", "wpn_knife5", "wpn_axe3", "equ_military_pack", } local useful_tier4 = { "device_torch_nv_3", "detector_elite", "af_aam", "equ_tourist_pack", } -- MEDICAL ITEMS local meds_tier1 = { "bandage", "caffeine", "drug_sleepingpills", "drug_coagulant", "yadylin", "medkit", "jgut", "glucose_s", "antirad", "antirad_cystamine", } local meds_tier2 = { "medkit_army", "stimpack", "salicidic_acid", "glucose", "drug_psy_blockade", "antirad_kalium", "medkit_ai1", "akvatab", } local meds_tier3 = { "medkit_scientic", "stimpack_army", "morphine", "drug_radioprotector", "medkit_ai3", "adrenalin", "drug_anabiotic", } local meds_tier4 = { "stimpack_scientic", "survival_kit", "medkit_ai2", } local lootboxes_tier1 = { "lootbox_1", "lootbox_11", "lootbox_2", "lootbox_5", "lootbox_51", } local lootboxes_tier2 = { "lootbox_7", "lootbox_4", "lootbox_41", "lootbox_6", "lootbox_61", "lootbox_71", "lootbox_3", } local lootboxes_tier3 = { "lootbox_8", "lootbox_81", "lootbox_9", } -- table of already looted boxes local boxes = {} -- table of already looted npcs local npcs = {} local pda_msg_status = true local function pda_message(type) local strs = { ["repair"] = "st_smr_loot_found_repair", ["ammo"] = "st_smr_loot_found_ammo", ["meds"] = "st_smr_loot_found_meds", ["useful"] = "st_smr_loot_found_useful", ["mags"] = "st_smr_loot_found_mags", ["lootbox"] = "st_smr_loot_found_lootbox", } if (not smr_loot_mcm.get_config("send_message")) or (not pda_msg_status) then return end db.actor:give_game_news(game.translate_string(strs[type]), "", "ui_inGame2_Polucheni_koordinaty_taynika", 0, 3000) smr_debug.get_log().info("loot/general", "PDA message sent: %s", strs[type]) xr_sound.set_sound_play(AC_ID, "pda_tips") end -- creates an item with a random amount of uses or condition local function create_item_random_uses(i, box) smr_debug.get_log().info("loot/general", "Creating item with random uses: %s", i) local max_uses = ini_sys:r_float_ex(i, "max_uses") local se_item = alife_create_item(i, box) if max_uses then alife_process_item(i, se_item.id, {uses = (math.random(1, max_uses))}) elseif utils_item.is_degradable(nil, se_item.id) then alife_process_item(i, se_item.id, {cond = (math.random(50,100)/100)}) end end local function roll_item_tiered(t, box) local r = math.random(1,100) table.sort(t, function(a, b) return a[1] > b[1] end) for i, n in ipairs(t) do if (r >= n[1]) then local itm = n[2][math.random(#n[2])] smr_debug.get_log().info("loot/rolls", "Rolled succesfully for %s (%s >= %s)", itm, r, n[1]) create_item_random_uses(itm, box) return true end end return false end function try_spawn_lootbox(box) if math.random(1,100) <= smr_zzintegration_mcm.get_config("lootboxes_chance") then pda_message("lootbox") local tbl = { { 90, lootboxes_tier3}, { 50, lootboxes_tier2}, { 0, lootboxes_tier1}, } roll_item_tiered(tbl, box) end end function try_spawn_mag(box) if math.random(1,100) <= smr_zzintegration_mcm.get_config("mags_redux_chance") then smr_debug.get_log().info("integration", "Rolling for magazine") local mags = {} for i=1,3 do local wpn = db.actor:item_in_slot(i) if wpn and magazine_binder.is_supported_weapon(wpn) then mags = magazine_binder.get_mags_for_basetype(magazine_binder.get_weapon_base_type(wpn)) end end if (next(mags) == nil) then return end pda_message("mags") alife_create_item(mags[math.random(#mags)], box) end end -- rolls for a repair kit, and spawns if sucessful function try_spawn_repair(box) if math.random(1,100) <= smr_loot_mcm.get_config("repair_chance") then pda_message("repair") local tbl = { { 90, repair_tier4}, { 50, repair_tier3}, { 0, repair_tier2}, } roll_item_tiered(tbl, box) end end function try_spawn_repair_minor(box) if math.random(1,100) <= smr_loot_mcm.get_config("repair_minor_chance") then pda_message("repair") create_item_random_uses(repair_tier1[math.random(#repair_tier1)], box) end end -- rolls for ammo, and spawns if sucessful function try_spawn_ammo(box) if math.random(1,100) <= smr_loot_mcm.get_config("ammo_chance") then pda_message("ammo") local r = math.random(1,100) if r > 90 then create_ammo(exotic_ammo[math.random(#exotic_ammo)], box) elseif r > 60 then create_ammo(advanced_ammo[math.random(#advanced_ammo)], box) else create_ammo(basic_ammo[math.random(#basic_ammo)], box)end end end -- rolls for an medical item, and spawns if sucessful function try_spawn_meds(box) if math.random(1,100) <= smr_loot_mcm.get_config("meds_chance") then pda_message("meds") local tbl = { { 90, meds_tier4}, { 70, meds_tier3}, { 40, meds_tier2}, { 0, meds_tier1}, } roll_item_tiered(tbl, box) end end -- rolls for a useful item, and spawns if sucessful function try_spawn_useful(box) local f = factor or 1 if math.random(1,100) <= smr_loot_mcm.get_config("useful_chance") then pda_message("useful") local tbl = { { 90, useful_tier4}, { 70, useful_tier3}, { 40, useful_tier2}, { 0, useful_tier1}, } roll_item_tiered(tbl, box) end end -- creates a random amount of ammo in a box function create_ammo(i, box) local se_item if smr_loot_mcm.get_config("ammo_bad_spawn") and (math.random(1,100) <= smr_loot_mcm.get_config("ammo_bad_chance")) then se_item = alife_create_item(i .. "_bad", box) else se_item = alife_create_item(i, box) end local bs = ini_sys:r_float_ex(i, "box_size") or 10 local r = math.ceil(math.random(bs/2, bs*2) * smr_loot_mcm.get_config("ammo_amount")) alife_process_item(i, se_item.id, {ammo = r}) end function try_spawn(box) if smr_zzintegration_mcm.get_config("mags_redux") and magazines then smr_loot.try_spawn_mag(box) end if smr_zzintegration_mcm.get_config("lootboxes") and arti_lootboxes_mcm then smr_loot.try_spawn_lootbox(box) end if smr_loot_mcm.get_config("repair_spawn") then smr_loot.try_spawn_repair(box) end if smr_loot_mcm.get_config("repair_minor_spawn") then smr_loot.try_spawn_repair_minor(box) end if smr_loot_mcm.get_config("ammo_spawn") then smr_loot.try_spawn_ammo(box) end if smr_loot_mcm.get_config("useful_spawn") then smr_loot.try_spawn_useful(box) end if smr_loot_mcm.get_config("meds_spawn") then smr_loot.try_spawn_meds(box) end end -- --- -- ENTRY POINTS -- --- -- callback, used to write box table to save local function save_state(data) if not (data.smr_loot) then data.smr_loot = {} end data.smr_loot.npcs = npcs data.smr_loot.boxes = boxes end -- callback, used to load box table from save local function load_state(data) if not (data.smr_loot) then return end boxes = data.smr_loot.boxes or {} data.smr_loot.boxes = {} npcs = data.smr_loot.npcs or {} data.smr_loot.npcs = {} end -- callback, when opening boxes local function physic_object_on_use_callback(box) local id = box:id() local player_stashes = alife_storage_manager.get_state().player_created_stashes if (id and boxes and boxes[id]) or (player_stashes and player_stashes[id]) then return end if (IsInvbox(box)) then try_spawn(box) boxes[id] = true end end local function npc_on_death_callback(obj, who) local id = obj:id() if id and npcs and npcs[id] then return end if math.random(1,100) <= smr_loot_mcm.get_config("npc_chance") then pda_msg_status = false try_spawn(obj) pda_msg_status = true npcs[id] = true end end -- register callback for box opening local function actor_on_first_update() if smr_amain_mcm.get_config("smr_enabled") then RegisterScriptCallback("physic_object_on_use_callback", physic_object_on_use_callback) if smr_loot_mcm.get_config("npc_spawn") then RegisterScriptCallback("npc_on_death_callback", npc_on_death_callback) end end end -- register callbacks for saving/loading function on_game_start() if not smr_amain_mcm.get_config("smr_enabled") then return end if smr_zzintegration_mcm.get_config("glowsticks") and zz_glowstick_mcm then table.insert(useful_tier1, "device_glowstick_orange") table.insert(useful_tier1, "device_glowstick_red") table.insert(useful_tier1, "device_glowstick_blue") table.insert(useful_tier1, "device_glowstick") end if smr_zzintegration_mcm.get_config("lootboxes") and arti_lootboxes_mcm then table.insert(useful_tier1, "lockpick") table.insert(useful_tier1, "bundle_lockpick") table.insert(useful_tier2, "lockpick_set") table.insert(useful_tier4, "skeleton_key") end if smr_loot_mcm.get_config("repair_parts_spawn") then table.insert(repair_tier1, "ramrod_tool") table.insert(repair_tier1, "rasp_tool") table.insert(repair_tier1, "sewing_thread") table.insert(repair_tier2, "heavy_sewing_thread") end RegisterScriptCallback("save_state", save_state) RegisterScriptCallback("load_state", load_state) RegisterScriptCallback("npc_on_death_callback", npc_on_death_callback) RegisterScriptCallback("actor_on_first_update", actor_on_first_update) end