Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- ------------------------------------------------------------------------------------------------------------------
- -- Treasure randomizer
- -- by Alundaio
- ------------------------------------------------------------------------------------------------------------------
- This script spawns loot, randomly, inside all INVBOX class objects. Loot is determined on new game start and stored
- within the .scoc as a string. When the cache is opened, the loot is spawned.
- A list of all valid item sections is generated automatically by parsing all of system_ini(). From this list, loot can be obtained. If you do not
- want a certain section from being spawned, put it in 'plugins\treasure_blacklist' 'ignore_sections'.
- A valid item section is determined by several factors:
- 1. It must have can_trade = true
- 2. It must have quest_item = false
- 3. It must not have '_mp' in it's section name
- 4. It must have a valid 'inv_name' value
- 5. 'cost' must be greater than 0
- To debug or to obtain a list of valid item sections, simply enable DEV_DEBUG (-dbg in command line).
- You shall find a 'valid_item_sections.ltx' in your main game directory after you start a new game.
- To debug the actual results, start a new game. Then while in the loadscreen menu ctrl+left-click on the new 'autosave'. You
- will find the <filename>.lua in your savegame folder. Search the file for 'caches'
- --]]
- caches = {}
- local caches_count = 0
- local valid_item_list = nil
- local valid_item_list_array = nil
- local valid_map_spots = {
- ["treasure"] = true,
- ["treasure_player"] = true,
- ["treasure_searched"] = true,
- ["treasure_unique"] = true
- }
- -- Table to put things that you wish only the specific count can ever only spawn
- local always_in_existence = {
- ["af_compass"] = 1,
- ["detector_advanced"] = 4,
- ["detector_elite"] = 2,
- ["detector_scientific"] = 1,
- ["detector_simple"] = 8,
- ["equ_military_pack"] = 3,
- ["equ_tourist_pack"] = 2,
- ["itm_repairkit_tier_1"] = 4,
- ["itm_repairkit_tier_2"] = 4,
- ["itm_repairkit_tier_3"] = 4,
- ["wpn_gauss"] = 1,
- ["wpn_addon_sight_ir"] = 2,
- ["wpn_addon_sight_irvar"] = 1
- }
- ------------------------------------------------------------------------------------------------------------------
- -- PRIVATE
- ------------------------------------------------------------------------------------------------------------------
- local function on_game_load()
- if (caches_count > 0) then
- return
- end
- local ignore_list = {
- ["bar_inv_box"] = true,
- ["bar_inventory_box_2"] = true,
- ["val_recover_item_2_spawn"] = true
- }
- local sim = alife()
- for id, se_obj in alife():objects() do
- if (IsInvbox(nil,se_obj:clsid()) and not ignore_list[se_obj:name()]) then
- caches[id] = false
- caches_count = caches_count + 1
- --elseif (se_obj:spawn_ini() and se_obj:spawn_ini():section_exist("drop_box")) then
- -- box_caches[id] = false
- -- box_caches_count = caches_count + 1
- end
- end
- for i=1, math.floor(caches_count/2) do
- create_random_stash(true,"stash")
- end
- local temp = {}
- for section,count in pairs(always_in_existence) do
- for i=1,count do
- iempty_table(temp)
- temp[1] = section
- create_random_stash(true,"stash",temp)
- end
- end
- end
- local function actor_on_item_take_from_box(box,itm)
- if (caches[box:id()] == true) then
- -- Remove all existing map spots from the cache.
- -- Remember that this will only apply to randomly generated stashes.
- for k, v in pairs(valid_map_spots) do
- if level.map_has_object_spot(box:id(), k) then
- level.map_remove_object_spot(box:id(), k)
- end
- end
- -- Mark the stash as "partially looted".
- local id = alife_object(box:id()).id
- level.map_add_object_spot_ser(id, "treasure_searched", game.translate_string("st_ui_pda_secret_searched"))
- -- Remove the cache from the treasure manager.
- caches[box:id()] = false
- dxr_statistics.increment_statistic("stashes_found")
- news_manager.send_treasure(1)
- end
- -- If no items remain; remove the "partially looted" marker.
- if box:is_inv_box_empty() then
- if level.map_has_object_spot(box:id(), "treasure_searched") then
- level.map_remove_object_spot(box:id(), "treasure_searched")
- end
- end
- end
- local function save_state(data)
- --alun_utils.debug_write("coc_treasure_manager.save_state")
- if (caches_count <= 0) then
- return
- end
- if not (data.coc_treasure_manager) then
- data.coc_treasure_manager = {}
- end
- data.coc_treasure_manager.caches_count = caches_count
- data.coc_treasure_manager.caches = caches
- end
- local function load_state(data)
- if not (data.coc_treasure_manager) then
- return
- end
- caches_count = data.coc_treasure_manager.caches_count or caches_count
- caches = data.coc_treasure_manager.caches or caches
- data.coc_treasure_manager.caches_count = nil
- data.coc_treasure_manager.caches = nil
- end
- local function physic_object_on_use_callback(box,who)
- if (IsInvbox(box)) then
- try_spawn_treasure(box)
- end
- end
- ------------------------------------------------------------------------------------------------------------------
- -- ON GAME START
- ------------------------------------------------------------------------------------------------------------------
- function on_game_start()
- RegisterScriptCallback("on_game_load",on_game_load)
- RegisterScriptCallback("actor_on_item_take_from_box",actor_on_item_take_from_box)
- RegisterScriptCallback("save_state",save_state)
- RegisterScriptCallback("load_state",load_state)
- RegisterScriptCallback("physic_object_on_use_callback",physic_object_on_use_callback)
- end
- -------------------------------------------------------------------------------------------------------------------
- last_secret = nil
- function create_random_stash(no_spot,hint,bonus_items, map_spot, delivery_id)
- map_spot = valid_map_spots[map_spot] and map_spot or "treasure"
- last_secret = nil
- if (caches_count <= 0) and (delivery_id == nil) then
- return
- end
- last_secret = true
- local sim = alife()
- -- create a temporary table to use math.random
- local t = {}
- local size_t = 0
- for id,v in pairs(caches) do
- -- false means box is available
- if (v == false) then
- size_t = size_t + 1
- t[size_t] = id
- end
- end
- local index = size_t > 0 and math.random(size_t)
- if not (index) and (delivery_id == nil) then
- return
- end
- local id = t[index]
- local se_box = id and sim:object(id)
- if not (se_box) and (delivery_id == nil) then
- caches[id] = nil
- caches_count = caches_count - 1
- return
- end
- if not (valid_item_list) then
- valid_item_list,valid_item_list_array = get_valid_item_sections()
- end
- local spawned_item = bonus_items or {}
- local max_weight = math.random(80,130)
- if (dxr_achievements.has_achievement("rag_and_bone")) then
- max_weight = max_weight + 30
- end
- if delivery_id then max_weight = math.random(200,300) end
- local ini = system_ini()
- local function is_consumable(section)
- local v = ini:r_string_ex(section,"class","")
- return v == "S_FOOD" or v == "II_FOOD"
- end
- local function is_outfit(section)
- local v = ini:r_string_ex(section,"class","")
- return v == "EQU_STLK" or v == "E_STLK" or v == "EQU_HLMET" or v == "E_HLMET"
- end
- local function is_weapon(section)
- local v = ini:r_string_ex(section,"class","")
- return string.find(v,"WP_") ~= nil
- end
- local function is_ammo(section)
- local v = ini:r_string_ex(section,"class","")
- return v == "AMMO" or v == "AMMO_S"
- end
- local function is_backpack(section)
- local v = ini:r_string_ex(section,"class","")
- return v == "EQ_BAKPK"
- end
- local function is_toolkit(section)
- return section == "itm_repairkit_tier_1" or section == "itm_repairkit_tier_2" or section == "itm_repairkit_tier_3"
- end
- local function is_artefact(section)
- local v = ini:r_string_ex(section,"class","")
- return v == "SCRPTART"
- end
- local function is_detector(section)
- local v = ini:r_string_ex(section,"class","")
- return v == "DET_ADVA" or v == "DET_ELIT" or v == "DET_SCIE" or v == "DET_SIMP"
- end
- --\\ FOR DEBUG
- local cost_lvls = {500,1000,3000,5000,9000,15000,25000}
- if (DEV_DEBUG_DEV) and not sorted then
- -- iterate most expensive to least expensive by group.
- local tsortc,tsorto,tsortw,tsorta,tsorts,tsort = {},{},{},{},{},{}
- for section,cost in spairs(valid_item_list,function(t,a,b) return t[a] > t[b] end) do
- if is_consumable(section) then table.insert(tsortc, section.." = "..cost)
- elseif is_outfit(section) then table.insert(tsorto, section.." = "..cost)
- elseif is_weapon(section) then table.insert(tsortw, section.." = "..cost)
- elseif is_artefact(section) then table.insert(tsorta, section.." = "..cost)
- elseif is_ammo(section) then table.insert(tsorts, section.." = "..cost)
- else table.insert(tsort, section.." = "..cost)
- end
- end
- local cfg = io.open("valid_item_sort.ltx","w+")
- local function addgap(header, tname)
- local cost
- local lvl = 7
- cfg:write(header.."\n\n")
- for i=1,#tname do
- cost = tonumber(tname[i]:match("=%s(%d+)"))
- local once = nil
- while ((lvl >= 1) and cost < cost_lvls[lvl]) do
- lvl = lvl - 1
- once = true
- end
- if once then cfg:write("// "..cost_lvls[lvl+1].." //\n") end
- cfg:write(tname[i].."\n")
- end
- end
- addgap("### Outfits ###", tsorto)
- addgap("\n### Weapons ###", tsortw)
- addgap("\n### Consumables ###", tsortc)
- addgap("\n### Artifacts ###", tsorta)
- addgap("\n### Ammo ###", tsorts)
- addgap("\n### Other ###", tsort)
- cfg:close()
- sorted = true
- end
- --//
- local has_spawned = false
- local function try_spawn_item(sec,min,max,chance,weight)
- if (max_weight >= weight) then
- local new_max = math.random(min,max)
- for i=1,new_max do
- if ((math.random(1,1000)/1000) <= chance) then
- spawned_item[#spawned_item+1] = sec
- max_weight = max_weight - weight
- has_spawned = true
- end
- end
- end
- end
- local allow_item = true
- utils.shuffle(valid_item_list_array)
- for i,section in ipairs(valid_item_list_array) do
- local cost = valid_item_list[section]
- if not (always_in_existence[section]) then
- has_spawned = false
- local skip = false
- local bDetector = is_detector(section)
- local bToolkit = not bDetector and is_toolkit(section)
- local bArtefact = not bToolkit and is_artefact(section)
- local bWeapon = not bArtefact and is_weapon(section)
- local bOutfit = not bWeapon and is_outfit(section)
- local bBackpack = not bWeapon and not bOutfit and is_backpack(section)
- if (bWeapon or bOutfit or bBackpack or bArtefact) then
- if not (allow_item) then
- skip = true
- end
- end
- if not (skip) then
- if (bBackpack) then
- if (section == "equ_tourist_pack") then
- try_spawn_item(section,1,1,0.03,80)
- elseif (section == "equ_military_pack") then
- try_spawn_item(section,1,1,0.05,80)
- else
- try_spawn_item(section,1,1,0.10,50)
- end
- elseif (bDetector) then
- local v = ini:r_string_ex(section,"class","")
- if (v == "DET_SCIE") then
- try_spawn_item(section,1,1,0.005,80)
- elseif (v == "DET_ELIT") then
- try_spawn_item(section,1,1,0.05,60)
- elseif (v == "DET_ADVA") then
- try_spawn_item(section,1,1,0.10,50)
- else
- try_spawn_item(section,1,1,0.15,40)
- end
- elseif (bToolkit) then
- if (section == "itm_repairkit_tier_3") then
- try_spawn_item(section,1,1,0.04,80)
- elseif (section == "itm_repairkit_tier_2") then
- try_spawn_item(section,1,1,0.08,80)
- else
- try_spawn_item(section,1,1,0.12,50)
- end
- else
- -- decide for each section based on chance, weight and item classification
- if (cost <= cost_lvls[1]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,3,0.50,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.20,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.05,40)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.05,50)
- else
- try_spawn_item(section,1,1,0.40,40)
- end
- elseif (cost <= cost_lvls[2]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,2,0.20,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.10,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.04,40)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.05,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.01,50)
- else
- try_spawn_item(section,1,1,0.30,40)
- end
- elseif (cost <= cost_lvls[3]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,1,0.10,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.05,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.03,40)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.02,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.01,50)
- else
- try_spawn_item(section,1,1,0.15,40)
- end
- elseif (cost <= cost_lvls[4]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,1,0.10,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.03,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.01,60)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.01,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.01,50)
- else
- try_spawn_item(section,1,1,0.10,40)
- end
- elseif (cost <= cost_lvls[5]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,1,0.05,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.01,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.005,80)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.005,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.005,50)
- else
- try_spawn_item(section,1,1,0.05,40)
- end
- elseif (cost <= cost_lvls[6]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,1,0.03,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.01,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.005,80)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.004,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.003,50)
- else
- try_spawn_item(section,1,1,0.03,40)
- end
- elseif (cost <= cost_lvls[7]) then
- if (is_consumable(section)) then
- try_spawn_item(section,1,1,0.01,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.005,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.003,80)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.003,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.001,50)
- else
- try_spawn_item(section,1,1,0.01,40)
- end
- else
- if (is_consumable(section)) then
- try_spawn_item(section,1,1,0.005,40)
- elseif (is_ammo(section)) then
- try_spawn_item(section,1,1,0.003,40)
- elseif (bOutfit) then
- try_spawn_item(section,1,1,0.001,80)
- elseif (bWeapon) then
- try_spawn_item(section,1,1,0.001,60)
- elseif (bArtefact) then
- try_spawn_item(section,1,1,0.001,50)
- else
- try_spawn_item(section,1,1,0.005,40)
- end
- end
- end
- end
- if (has_spawned) and (bWeapon or bOutfit or bBackpack or bArtefact) then
- if (allow_item) then
- allow_item = false
- end
- end
- if (max_weight <= 0) then
- break
- end
- end
- end
- if delivery_id then
- caches[delivery_id] = table.concat(spawned_item,",")
- --printf("cache %s",caches[delivery_id])
- return
- end
- if (#spawned_item > 0) then
- -- create map spot
- local ignore = nil
- for k, v in pairs(valid_map_spots) do
- if (level.map_has_object_spot(id,k) ~= 0) then
- ignore = true
- end
- end
- if (no_spot ~= true and not ignore) then
- level.map_add_object_spot_ser(id, map_spot or "treasure", hint or game.translate_string("st_ui_pda_secret"))
- news_manager.send_treasure(0)
- end
- caches[id] = table.concat(spawned_item,",")
- else
- caches[id] = false
- end
- end
- function try_spawn_treasure(box, delivery_id)
- local id = box:id()
- --printf("try_spawn_treasure [%s]",caches[id])
- if not (caches[id]) and delivery_id and not (caches[delivery_id]) then
- return
- end
- if not (type(caches[id]) == "string") and (delivery_id == nil) then
- return
- end
- local spawned_item
- caches[id] = true
- if delivery_id then
- spawned_item = alun_utils.str_explode(caches[delivery_id],",")
- caches[id] = nil
- caches[delivery_id] = nil
- else
- spawned_item = alun_utils.str_explode(caches[id],",")
- caches[id] = true
- end
- if not (valid_item_list) then
- valid_item_list,valid_item_list_array = get_valid_item_sections()
- end
- local sec,ammos,ct,ammo_type
- local ini = system_ini()
- local sim = alife()
- for i=1,#spawned_item do
- sec = spawned_item[i]
- if (valid_item_list[sec] and sec ~= "" and ini:section_exist(sec)) then
- if (utils.is_ammo(sec)) then
- create_ammo(sec,box:position(),box:level_vertex_id(),box:game_vertex_id(),id,math.random(10,20))
- else
- -- Chernobyl Weapon Pack / Arsenal Overhaul 3
- -- Switches the weapon to a sighted variant if available.
- if (math.random() <= 0.3) then
- local sights = alun_utils.parse_list(ini, sec, "sights")
- if (#sights > 0) then
- sight = sights[math.random(#sights)]
- local variant_section = sec .. "_" .. sight
- if (ini:section_exist(variant_section)) then
- sec = variant_section
- end
- end
- end
- -- since we spawning on parent, we don't want to register object or packetdata will be inaccurate
- local se_obj = sim:create(sec,box:position(),0,0,id)
- if (se_obj) then
- local cls = se_obj:clsid()
- if (IsWeapon(nil,cls) and cls ~= clsid.wpn_knife_s) then
- se_obj.condition = (math.random(70)+30)/100
- local flags = se_obj:get_addon_flags()
- if (math.random() <= 0.3 and se_obj.scope_status == cse_alife_item_weapon.eAddonAttachable) then
- flags:set(cse_alife_item_weapon.eWeaponAddonScope,true)
- end
- if (math.random() <= 0.3 and se_obj.grenade_launcher_status == cse_alife_item_weapon.eAddonAttachable) then
- flags:set(cse_alife_item_weapon.eWeaponAddonGrenadeLauncher,true)
- end
- if (math.random() <= 0.3 and se_obj.silencer_status == cse_alife_item_weapon.eAddonAttachable) then
- flags:set(cse_alife_item_weapon.eWeaponAddonSilencer,true)
- end
- -- Create random ammo type
- ammos = alun_utils.parse_list(ini,sec,"ammo_class")
- ct = ammos and #ammos
- ammo_type = ammos and ct and math.random(0,ct-1) or 0
- se_obj.ammo_type = ammo_type
- end
- end
- end
- else
- printf("coc_treasure_manager.script: invalid section %s",sec)
- end
- end
- end
- function save(pk)
- if (USE_MARSHAL) then
- return
- end
- pk:w_u16(caches_count)
- for id,v in pairs(caches) do
- pk:w_u16(id)
- pk:w_bool(v)
- end
- end
- function load(pk)
- if (USE_MARSHAL) then
- return
- end
- caches_count = pk:r_u16()
- for i=1,caches_count do
- caches[pk:r_u16()] = pk:r_bool()
- end
- end
- function get_valid_item_sections()
- local ltx = ini_file("plugins\\coc_treasure_manager.ltx")
- local t,tt = {},{}
- local ini = system_ini()
- ini:section_for_each(function(section)
- if not (ltx:line_exist("ignore_sections",section)) then
- if (ini:line_exist(section,"cform")) then
- if (ini:r_bool_ex(section,"can_trade",true) == true) then
- if (ini:r_bool_ex(section,"quest_item",false) == false) then
- if not (string.find(section,"mp_")) then
- local name = ini:r_string_ex(section,"inv_name")
- if (name and name ~= "" and name ~= "default") then
- local class = ini:r_string_ex(section,"class")
- if (class == "G_FAKE") then
- else
- -- Chernobyl Weapon Pack / Arsenal Overhaul 3
- -- blacklist everything but base weapon, try_spawn_treasure will handle the randomization of sights
- local parent_section = ini:r_string_ex(section,"parent_section")
- if (parent_section == nil or parent_section == "" or parent_section == section) then
- local cost = ini:r_float_ex(section,"cost") or 0
- if (cost > 0) then
- t[section] = cost
- tt[#tt+1] = section
- end
- end
- end
- end
- end
- end
- end
- end
- end
- end
- )
- -- List of all items in game that are not quest items
- if (DEV_DEBUG_DEV) then
- local cfg = io.open("valid_item_sections.ltx","w+")
- for k,v in pairs(t) do
- cfg:write(k.."\n")
- end
- cfg:close()
- end
- return t,tt
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement