--[[ itms_manager by Alundaio Copyright (C) 2012 Alundaio This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License Modified by Tronex: 2018/7/18 - Added support for new usable items (maps,craft) 2018/7/21 - Added the ability to gather/separate muti-use items 2018/7/26 - Added the ability to disassemble misc items 2018/7/26 - Rewrote a lot of function with better codes 2018/7/31 - Added new items, battery consumption for geiger counter and gps tracker 2018/8/2 - Drag artefacts on containers to put them in, container tool is no longer used 2019/2/16 - Organized the items in tables for global use 2019/9/19 - Bolt count manager 2020/3/18 - Cleaning --]] -- local uses = obj:get_remaining_uses() -- local max_uses = obj:get_max_uses() -- obj:set_remaining_uses(num) -- Prepare ini files ini_manager = ini_file("items\\settings\\itms_manager.ltx") ini_container = ini_file("items\\settings\\arty_container.ltx") ini_parts = ini_file("items\\settings\\parts.ltx") ini_craft = ini_file("items\\settings\\craft.ltx") ini_death = ini_file("items\\settings\\death_generic.ltx") ini_reward = ini_file("items\\settings\\item_rewards.ltx") local n = 0 local result,id,value = "","","" local string_find = string.find local string_gsub = string.gsub -- Collect item sections of main tools itms_arty_container = utils_data.collect_section(ini_container,"containers",true) or {} local empty_syringe_items = {} n = ini_manager:line_count("empty_syringe_items") for i=0,n-1 do result, id, value = ini_manager:r_line_ex("empty_syringe_items",i,"","") if ini_sys:section_exist(id) then empty_syringe_items[id] = true end end item_rewards = {} ini_reward:section_for_each(function(section) if (not item_rewards[section]) then item_rewards[section] = {} end n = ini_reward:line_count(section) for i=0,n-1 do result, id, value = ini_reward:r_line_ex(section,i,"","") if ini_sys:section_exist(id) then item_rewards[section][id] = ini_sys:r_float_ex(id,"tier") or 1 end end end) item_combine = {} n = ini_craft:line_count("item_combination") for i=0,n-1 do result, id, value = ini_craft:r_line_ex("item_combination",i,"","") if id and value then local str = str_explode(id,":") if str[1] and str[2] and ini_sys:section_exist(str[1]) and ini_sys:section_exist(str[2]) and ini_sys:section_exist(value) then if (not item_combine[str[1]]) then item_combine[str[1]] = {} end item_combine[str[1]][str[2]] = value else printe("!ERROR item_combination | wrong section names") end end end ------------------------------- -- CALLBACKS ------------------------------- function on_game_start() RegisterScriptCallback("actor_on_item_before_use",actor_on_item_before_use) RegisterScriptCallback("actor_on_first_update",actor_on_first_update) RegisterScriptCallback("actor_on_item_drop",actor_on_item_drop) RegisterScriptCallback("actor_on_item_use",actor_on_item_use) RegisterScriptCallback("actor_on_item_take",actor_on_item_take) RegisterScriptCallback("ActorMenu_on_item_drag_drop",ActorMenu_on_item_drag_drop) RegisterScriptCallback("ActorMenu_on_item_focus_receive",ActorMenu_on_item_focus_receive) RegisterScriptCallback("save_state",save_state) end local old_booster = {} local boost_min = 0.00005 local booster_string = { ["pwr"] = "ui_inv_power", ["psy"] = "ui_inv_outfit_telepatic_protection", ["bld"] = "ui_inv_bleeding", ["hp"] = "ui_inv_health", ["rad"] = "ui_inv_radiation", } function actor_on_item_before_use(obj,flags) local sec = obj:section() -- no need to check for tools if IsItem("tool",sec) then --printf("- actor_on_item_before_use [%s] | ignore utility item", sec) flags.ret_value = true return end local curr_booster = {} local time_g = time_global() local str = "" local pass = true -- read important boosts local period = ini_sys:r_float_ex(sec, "boost_time") or 0 local rad_boost = ini_sys:r_float_ex(sec, "boost_radiation_restore") or 0 local psy_boost = ini_sys:r_float_ex(sec, "boost_telepat_protection") or 0 local bld_boost = ini_sys:r_float_ex(sec, "boost_bleeding_restore") or 0 local hp_boost = ini_sys:r_float_ex(sec, "boost_health_restore") or 0 local pwr_boost = ini_sys:r_float_ex(sec, "boost_power_restore") or 0 -- if an item is required local require_tool = ini_sys:r_string_ex(sec, "required_tool") local obj_tool = require_tool and ini_sys:section_exist(require_tool) and db.actor:object(require_tool) -- store boosts in a table curr_booster["section"] = sec curr_booster["period"] = (period > 0) and (time_g + period*1000) or time_g curr_booster["pwr"] = (pwr_boost > boost_min) and pwr_boost or 0 curr_booster["psy"] = (psy_boost > boost_min) and psy_boost or 0 curr_booster["bld"] = (bld_boost > boost_min) and bld_boost or 0 curr_booster["hp"] = (hp_boost > boost_min) and hp_boost or 0 curr_booster["rad"] = (rad_boost > boost_min) and rad_boost or 0 -- A required tool is missing -> no eat if require_tool and (not obj_tool) then --printf("~ actor_on_item_before_use [%s] | require_tool [%s] is missing", sec, require_tool) str = strformat(game.translate_string("st_itm_manager_missing_requirements"), ui_item.get_sec_name(require_tool)) pass = false -- older booster is still active -- elseif old_booster["period"] and (old_booster["period"] > time_g) then --printf("~ on_before_item_use [%s] | older booster [%s] is still active", sec, old_booster['section']) -- local weaker_effect -- local stronger_effect -- for k,v in pairs(curr_booster) do -- if (k ~= "section") and (k ~= "period") then -- local v2 = old_booster[k] -- if v2 and (v > boost_min) and (v2 > boost_min) then -- if (v <= v2) then -- weaker_effect = k -- else -- stronger_effect = k -- end -- elseif (v2 == 0) and (v > boost_min) then -- stronger_effect = k -- end -- end -- end -- older booster has some stronger effect, while new one doesn't have any stronger effect -- if weaker_effect and (not stronger_effect) then --printf("~ on_before_item_use [%s] | older booster [%s] has stronger effect: %s", sec, old_booster['section'], weaker_effect) -- local boost_str = game.translate_string(booster_string[weaker_effect]) -- str = strformat(game.translate_string("st_itm_manager_greater_effect"),boost_str) -- pass = false -- end end -- to eat or not to eat if pass then --printf("- on_before_item_use [%s] | pass = true", sec) flags.ret_value = true copy_table(old_booster, curr_booster) if obj_tool then utils_item.discharge(obj_tool) end else --printf("! actor_on_item_before_use [%s] | pass = false", sec) flags.ret_value = false --alife_release(obj) --alife_create_item(sec,db.actor) utils_xml.hide_menu() actor_menu.set_msg(1, str,3) end end function actor_on_first_update() -- Delete base pda local sim = alife() local obj, se_obj --[[ for i=1,65534 do se_obj = sim:object(i) if se_obj and (se_obj:section_name() == "device_pda") then sim:release(se_obj,true) end end --]] -- Delete animation items on actor for sec,_ in pairs(GetItemList("release")) do obj = db.actor:object(sec) if obj then alife_release(obj) end end -- Spawn bolts local m_data = alife_storage_manager.get_state() local bolt_first = m_data.bolt_first if bolt_first then alife_create_item(bolt_first, db.actor) m_data.bolt_first = nil end local bolt_slot = m_data.bolt_slot if bolt_slot then alife_create_item(bolt_slot, db.actor) end local bolts = m_data.bolts if bolts then for sec,cnt in pairs(bolts) do for i=1,cnt do alife_create_item(sec, db.actor) end end end -- Damage equipment if (not has_alife_info("start_equippment_handled")) and (not IsTestMode()) then CreateTimeEvent(0,"delay_new_game_autosave",4,new_game_equippment) end -- Bolt manager CreateTimeEvent("cycle","bolt_manager",60,bolt_manager) end function actor_on_item_drop(obj) if not (obj) then return end if (db.actor:has_info("actor_made_wish_for_riches")) then db.actor:transfer_item(obj,db.actor) elseif IsWeapon(obj) then se_save_var(obj:id(), nil, "strapped_item", obj:condition()) end end function actor_on_item_use(obj) if (db.actor:has_info("actor_made_wish_for_riches")) then return end local sec = obj:section() -- Strelok Notes if (sec == "mlr_strelok_item_01") then txr_routes.open_route("val","x18") return -- Deployable mgun elseif (sec == "itm_deployable_mgun") then use_deployable_mgun(obj) return -- Watch elseif (sec == "hand_watch") then use_watch(obj) return -- Chocolate Bar elseif (sec == "chocolate") then alife_create_item("chocolate_p", db.actor) return -- Bolts pack elseif (sec == "bolts_pack") then local bolts = {} local n = math.random(20,30) for i=1,n do local actor = db.actor if math.random(1,100) > 50 then bolts[#bolts + 1] = "bolt" else bolts[#bolts + 1] = "bolt_bullet" end end utils_item.delay_event(bolts, nil, "bolts_pack", false, 5) return -- give empty syringe on using some medical items elseif empty_syringe_items[sec] then alife_create_item("e_syringe", db.actor) -- weather radar elseif (sec == "device_weather_radar") then ui_debug_weather.activate() return end -- don't discharge items with no removal if IsItem("multiuse_r",sec) then local uses = obj:get_remaining_uses() local max_uses = obj:get_max_uses() if uses and max_uses and (uses < max_uses) then obj:set_remaining_uses(uses + 1) end end end local str_itm_taken = game.translate_string("st_item_taken") function actor_on_item_take(obj) local sec = obj:section() -- Play sound effect for item taking play_item_sound(obj) if IsWeapon(obj) and se_load_var(item_id, nil, "strapped_item") then se_save_var(obj:id(), nil, "strapped_item", nil) -- Explosive barrels elseif (sec == "explosive_mobiltank") or (sec == "explosive_tank") then local se_obj = alife_object(obj:id()) local fuel = tostring(math.random(6,8)) if se_obj then alife_release(se_obj) alife_create_item("explo_jerrycan_fuel", db.actor, {uses = 2}) end -- Story special elseif (sec == "main_story_1_quest_case") and (not has_alife_info("agr_u_bloodsucker_on_case")) then db.actor:give_info_portion("agr_u_bloodsucker_on_case") xr_effects.create_squad(nil,nil,{"agr_u_bloodsucker_3_squad","agr_u_bloodsucker"}) end -- Show notification --local str = strformat(str_itm_taken, ui_item.get_sec_name(sec)) --actor_menu.set_msg(2, str) end function dropdrop_ArtyContainer(obj_1, obj_2, sec_1, sec_2) -- Put artefact in container local cont = sec_2 local arty = sec_1 if ini_sys:section_exist(arty .. "_" .. cont) then local cond = obj_1 and obj_1:condition() actor_effects.play_item_fx("container_tool_" .. cont .. "_dummy") alife_create_item(arty .. "_" .. cont, db.actor, { cond = cond } ) alife_release(obj_1) alife_release(obj_2) end end function dropdrop_Basic_Combination(obj_1, obj_2, sec_1, sec_2) -- Combine basic items local sec_new = item_combine[sec_1][sec_2] actor_effects.play_item_fx("item_combination") alife_create_item(sec_new, db.actor) alife_release(obj_1) alife_release(obj_2) end function ActorMenu_on_item_drag_drop(obj_1, obj_2, slot_from, slot_to) -- Check capability if not (slot_from == EDDListType.iActorBag and slot_to == EDDListType.iActorBag) then return end local sec_1 = obj_1:section() local sec_2 = obj_2:section() if itms_arty_container[sec_2] then if (ini_sys:r_string_ex(sec_1,"class") == "ARTEFACT") or (ini_sys:r_string_ex(sec_1,"class") == "SCRPTART") then dropdrop_ArtyContainer(obj_1, obj_2, sec_1, sec_2) end elseif item_combine[sec_1] and item_combine[sec_1][sec_2] then dropdrop_Basic_Combination(obj_1, obj_2, sec_1, sec_2) end end local focus_last_sec local focus_tbl = {} local focus_upgr = {} function ActorMenu_on_item_focus_receive(obj) -- highlight compatible items --[[ local parent = obj:parent() if not (parent and parent:id() == AC_ID) then return end --]] local sec_focus = obj:section() if (focus_last_sec ~= sec_focus) then local id = obj:id() focus_last_sec = sec_focus empty_table(focus_tbl) local parent_sec = ini_sys:r_string_ex(sec_focus,"parent_section") or sec_focus -- For weapons if IsWeapon(obj) then -- Ammo local ammo = utils_item.get_ammo(sec_focus, id) for i=1,#ammo do focus_tbl[#focus_tbl + 1] = ammo[i] end -- Scopes local scopes = parse_list(ini_sys, parent_sec, "scopes") for i=1,#scopes do focus_tbl[#focus_tbl + 1] = scopes[i] end local scope = utils_item.get_wpn_param(obj, sec_focus, "scopes_sect") if scope and (obj:weapon_scope_status() == 2) then focus_tbl[#focus_tbl + 1] = scope end -- Silencer local sil = utils_item.get_wpn_param(obj, sec_focus, "silencer_name") if sil and (obj:weapon_silencer_status() == 2) then focus_tbl[#focus_tbl + 1] = sil end -- Grenade Launcher local gl = utils_item.get_wpn_param(obj, sec_focus, "grenade_launcher_name") if gl and (obj:weapon_grenadelauncher_status() == 2) then focus_tbl[#focus_tbl + 1] = gl end end -- Parts local parts_str = ini_parts:r_string_ex("con_parts_list",parent_sec) local parts = parts_str and (parts_str ~= "") and str_explode(parts_str,",") if parts then for i=1,#parts do focus_tbl[#focus_tbl + 1] = parts[i] end end -- Repair kits local repair_type = ini_sys:r_string_ex(parent_sec,"repair_type") if repair_type and repair_type ~= "" then for kit,v in pairs(GetItemList("repair")) do if v[repair_type] then focus_tbl[#focus_tbl + 1] = kit end end --[[ for kit,v in pairs(GetItemList("workshop")) do if v[repair_type] then focus_tbl[#focus_tbl + 1] = kit end end --]] end -- Upgrade parts local upgr_str = ini_sys:r_string_ex(parent_sec,"upgrades") if upgr_str and upgr_str ~= "" then if (not focus_upgr[parent_sec]) then local upgr = parse_list(ini_sys,parent_sec,"upgrades") focus_upgr[parent_sec] = {} for i=1,#upgr do extract_upgr_tools(focus_upgr[parent_sec], upgr[i]) end end for tool,_ in pairs(focus_upgr[parent_sec]) do --printf("- upgrade part for [%s] -> [%s]", parent_sec, tool) focus_tbl[#focus_tbl + 1] = tool end end end local inventory = GetActorMenu() if not ((#focus_tbl > 0) or (inventory and inventory:IsShown())) then return end for i=1,#focus_tbl do inventory:highlight_section_in_slot(focus_tbl[i],EDDListType.iActorBag) inventory:highlight_section_in_slot(focus_tbl[i],EDDListType.iPartnerTradeBag) inventory:highlight_section_in_slot(focus_tbl[i],EDDListType.iDeadBodyBag) inventory:highlight_section_in_slot(focus_tbl[i],EDDListType.iActorTrade) inventory:highlight_section_in_slot(focus_tbl[i],EDDListType.iPartnerTrade) end end function save_state(m_data) --// NOTE: bolts aren't saved in alife, so this is a temp solution local bolts = {} local function itr(obj) local sec = obj:section() if (sec == "bolt") or (sec == "bolt_bullet") then if (not bolts[sec]) then bolts[sec] = 0 end bolts[sec] = bolts[sec] + 1 end return false end db.actor:inventory_for_each(itr) m_data.bolts = bolts m_data.bolt_slot = db.actor:item_in_slot(6) and db.actor:item_in_slot(6):section() or nil end ------------------------------- -- ITEM OPTIONS (MENU) ------------------------------- function menu_open(itm) -- return "open" name local p = itm:parent() if not (p and p:id() == AC_ID) then return end if itms_arty_container[itm:section()] then return end -- default containers return game.translate_string("st_item_open") end function menu_unpack(itm) -- return "unpack" name local p = itm:parent() if not (p and p:id() == AC_ID) then return end return game.translate_string("st_item_unpack") end function menu_play(itm) -- return "Play" name local p = itm:parent() if not (p and p:id() == AC_ID) then return end return game.translate_string("st_item_play") end function menu_place(obj) -- return "Place" name return game.translate_string("st_item_place") end ------------------------------- -- ITEM OPTIONS (FUNCTOR) ------------------------------- function use_package(obj) local sec = obj:section() local content = parse_list(ini_manager, "package_content", sec) if #content > 0 then utils_item.delay_event(content, {obj:id()}, "package_content", false, 5) end end function use_package_random(obj) local sec = obj:section() local content = ini_manager:r_string_ex("package_content",sec) if not content then return end local t = str_explode(content,",") local pick = {} for i=1,#t do if (#pick < 6) and (math.random(100) < 50) then pick[#pick+1] = t[i] end end pick = #pick > 1 and pick or {t[1],t[2],t[3],t[4]} utils_item.delay_event(pick, {obj:id()}, "package_content", true, 5) end function use_deployable_mgun(obj) local pos = vector():set(device().cam_pos) pos:add(device().cam_dir:mul(3)) alife_create("deployable_mgun",pos,level.vertex_id(pos),db.actor:game_vertex_id()) end function use_guitar(obj) local n = math.random(28) local snd = sound_object("music\\guitar_" .. tostring(n)) if (not snd) then return end local period = snd:length() snd:play_no_feedback(db.actor, 0, 0, db.actor:position(), 1.0, 1.0) actor_effects.play_continuous_effect(period) end function use_harmonica(obj) local n = math.random(5) local snd = sound_object("music\\harmonica_" .. tostring(n)) if (not snd) then return end local period = snd:length() snd:play_no_feedback(db.actor, 0, 0, db.actor:position(), 1.0, 1.0) actor_effects.play_continuous_effect(period) end function use_arty_container(obj) local break_con local break_arty local sec = obj:section() if (string.find(sec, "(lead.-_box)",3)) then break_con = "lead_box" break_arty = sec:gsub("_lead_box", "") elseif (string.find(sec, "(af.-_iam)",3)) then break_con = "af_iam" break_arty = sec:gsub("_af_iam", "") elseif (string.find(sec, "(af.-_aac)",3)) then break_con = "af_aac" break_arty = sec:gsub("_af_aac", "") elseif (string.find(sec, "(af.-_aam)",3)) then break_con = "af_aam" break_arty = sec:gsub("_af_aam", "") end if break_con and break_arty and ini_sys:section_exist(break_con) and ini_sys:section_exist(break_arty) then local cond = obj:condition() _G.ARTY_FROM_CONT = true -- Hack to prevent player from exploting Artefacts Containers (gaining rank by recieving artefacts) actor_effects.play_item_fx(break_con .. "_dummy") alife_create_item(break_con, db.actor) alife_create_item(break_arty, db.actor, { cond = cond } ) alife_release(obj) end end function use_watch(obj) local Y, M, D, h, mint, sec, ms = game.get_game_time():get() local pharse = game.translate_string("st_dyn_news_day_part_am") local mints = tostring(mint) if (h > 12) then h = h - 12 pharse = game.translate_string("st_dyn_news_day_part_pm") elseif (h == 12) then pharse = game.translate_string("st_dyn_news_day_part_pm") elseif (h == 0) then h = 12 end if (mint < 10) then mints = "0" .. tostring(mint) end utils_xml.hide_menu() actor_menu.set_msg(1, tostring(h) .. ":" .. mints .. " " .. pharse , 3) end function use_place(obj) local p = obj:parent() if not (p and p:id() == AC_ID) then return end local section = obj:section() alife_release_id(obj:id()) local pos = db.actor:position() pos:add(device().cam_dir:mul(1.2)) pos.y = db.actor:position().y + 1 local lvid = db.actor:level_vertex_id() local gvid = db.actor:game_vertex_id() local se_obj = alife_create(section,pos,lvid,gvid) local rot = device().cam_dir:getH() se_obj.angle = vector():set(0,rot,0) end ------------------------------- -- OTHERS ------------------------------- function actor_on_trade(obj,sell_bye,money) -- bind_stalker on_trade end function actor_item_take(obj) -- bind_stalker on_item_take end function npc_on_item_take_from_box(npc,box,item) end function new_game_equippment() -- Damage equipment local function damage_items(actor,itm) local sec = itm:section() if (IsWeapon(itm) and (sec ~= "wpn_binoc_inv")) or IsOutfit(itm) or IsHeadgear(itm) or IsItem("device",sec) then itm:set_condition(math.random(75,85)/100) end end db.actor:iterate_inventory(damage_items,db.actor) give_info("start_equippment_handled") -- Override autosave exec_console_cmd("save " .. user_name() .. " - autosave") printf("- Autosaved new game") -- Override ammo type if required local start_wpn_tbl = alife_storage_manager.get_state().start_wpn_ammo if start_wpn_tbl then for id, ammo_sec in pairs(start_wpn_tbl) do local wpn = level.object_by_id(id) if wpn then local ammo_list = utils_item.get_ammo(wpn:section(), wpn:id()) local ammo_type for i=1,#ammo_list do if (ammo_list[i] == ammo_sec) then ammo_type = i-1 break end end if ammo_type then local wpn_ammo_mag_size = ini_sys:r_u32(wpn:section(), "ammo_mag_size") if wpn_ammo_mag_size then wpn:unload_magazine() wpn:set_ammo_type(ammo_type) wpn:set_ammo_elapsed(wpn_ammo_mag_size ) printdbg("- New game weapon | [%s] - ammo type used: %s", wpn:section(), ammo_type) end end end end end return true end function bolt_manager() -- limit bolt count in actor inventory ResetTimeEvent("cycle","bolt_manager",60) local sim = alife() local bolt_max_num = ini_manager:r_float_ex("settings","bolt_max_num") or 99 local cnt = 0 local id, sec, se_obj local function itr(temp, obj) sec = obj:section() if (sec == "bolt") or (sec == "bolt_bullet") then cnt = cnt + 1 if (cnt > bolt_max_num) then local se_obj = alife_object(obj:id()) if se_obj then alife_release(se_obj) end end end end db.actor:iterate_ruck(itr, nil) return false end function give_item_reward(num_of_items) num_of_items = num_of_items or 1 local prior = { ["health"] = 2, ["rad"] = 2, ["drink"] = 2, ["food"] = 2, ["ammo"] = 2, ["battery"] = 0, } local tot_power,tot_devices = 0,0 local ammo_suitable = {} local ammo_avail = {} local function itr(obj) local sec = obj:section() -- Evaluate medkits if item_rewards["items_health"][sec] then prior["health"] = prior["health"] - 1 end -- Evaluate anti-rads if item_rewards["items_rad"][sec] then prior["rad"] = prior["rad"] - 1 end -- Evaluate drink if item_rewards["items_drink"][sec] then prior["drink"] = prior["drink"] - 1 end -- Evaluate food if item_rewards["items_food"][sec] then prior["food"] = prior["food"] - 1 end -- Evaluate devices power if IsItem("device",sec) then tot_devices = tot_devices + 1 tot_power = tot_power + (obj:condition() * 100) end -- Evaluate weapons and ammo if (sec ~= "wpn_binoc") and IsWeapon(obj) and (not IsMelee(obj)) then local ammo = utils_item.get_ammo(obj:section(), obj:id()) if ammo and #ammo > 1 then for i=1,#ammo do local sec_ammo = ammo[i] if item_rewards["items_ammo"][sec_ammo] then ammo_suitable[sec_ammo] = true end end end end if IsItem("ammo",sec) then ammo_avail[sec] = { cnt = obj:ammo_get_count() , box = IsItem("ammo",sec) } --printf("ammo_avail[%s] = { cnt = %s | box = %s", sec, ammo_avail[sec].cnt, ammo_avail[sec].box) end return false end db.actor:inventory_for_each(itr) -- Total power of devices is less than %50 -> need battery if (tot_power < (50 * tot_devices)) then prior["battery"] = 1 end -- No enough ammo found for existing weapons -> need ammo for sec,_ in pairs(ammo_suitable) do local ammo = ammo_avail[sec] if ammo and (ammo.cnt >= ammo.box) then prior["ammo"] = prior["ammo"] - 1 end end --[[ for k,p in pairs(prior) do printf("- Prior[%s] = %s",k,p) end --]] -- Give actor items for i=1,num_of_items do -- Search from higher to lower priority local picker = {} local pick_prior local functor = function(t,a,b) return t[a] > t[b] end for k,p in spairs(prior,functor) do if (p > 0) then if (not pick_prior) or (pick_prior == p) then pick_prior = p picker[#picker + 1] = k end end end if pick_prior and (#picker > 0) then -- Pick random type of this priority local item_reward local k = picker[math.random(#picker)] -- Pick random item if k == "health" then item_reward = random_key_table(item_rewards["items_health"]) elseif k == "rad" then item_reward = random_key_table(item_rewards["items_rad"]) elseif k == "drink" then item_reward = random_key_table(item_rewards["items_drink"]) elseif k == "food" then item_reward = random_key_table(item_rewards["items_food"]) elseif k == "battery" then item_reward = item_device.device_battery elseif k == "ammo" then item_reward = random_key_table(ammo_suitable) end if item_reward then -- Reduce priority prior[k] = prior[k] - 1 local amount = 1 -- Give items local box_size = IsItem("ammo",item_reward) local max_uses = IsItem("multiuse",item_reward) local uses = max_uses and (max_uses >= 2) and 2 or 1 if uses then amount = math.random(1,uses) end alife_create_item(item_reward, db.actor, {uses = amount , ammo = box_size}) -- Send news news_manager.relocate_item(db.actor, "in", item_reward, amount) end break end end end function send_itm_msg(sec) local str = strformat(game.translate_string("st_item_taken"), ui_item.get_sec_name(sec)) actor_menu.set_msg(2, str) end local upg_gr = { ["first"] = 1, ["secon"] = 2, ["third"] = 3, ["fourt"] = 4, ["fifth"] = 5, } local upg_ind = { ["a"] = 1, ["b"] = 2, ["c"] = 3, ["d"] = 4, ["e"] = 5, ["f"] = 6, } function extract_upgr_tools(t1, current_grp) local elements = parse_list(ini_sys, current_grp, "elements") -- Gather groups and indexes for i=1,#elements do -- search in upgrade group elements for k,v in pairs(upg_gr) do if string_find(elements[i],k) then -- if we found a legit element local indx = elements[i]:sub(9,9) -- get the index local upg_idx = indx and upg_ind[indx] if upg_idx then local prop = ini_sys:r_string_ex(elements[i],"property") if prop then local pr = str_explode(prop,",") local tool = utils_item.get_upgrade_prop_tool(pr[1]) local num = ((upg_idx <= 2) and 1) or ((upg_idx <= 4) and 2) or 3 local sec_tool = tool and tool:gsub("%^d", tostring(num)) or nil if sec_tool then t1[sec_tool] = true else local sec = ini_sys:r_string_ex(elements[i],"section") printdbg("! extract_upgr | can't generate tool for upgrade [%s], property: %s", sec, pr[1]) end end end end end end -- Repeat for i=1,#elements do local next_grp = ini_sys:r_string_ex(elements[i],"effects") if next_grp and (next_grp ~= "") then extract_upgr_tools(t1,next_grp) end end end ------------------------------- -- MULTI-USE ITEMS ------------------------------- function relocate_item_to_actor(actor, npc, section, amount) if (not actor) then return end amount = amount or 1 local npc_inv = npc and (not IsItem("anim",section)) and utils_item.collect_amount(npc, section, 1) or {} local sim = alife() local cnt = amount local max_uses = IsItem("multiuse",section) or 1 while cnt > 0 do -- Create or transfer object from npc local id if not is_empty(npc_inv) then local id_pick = random_key_table(npc_inv) local obj = id_pick and level.object_by_id(id_pick) if obj then npc:transfer_item(obj, actor) npc_inv[id_pick] = nil id = id_pick end else local se_obj = alife_create_item(section, db.actor) id = se_obj and se_obj.id -- Register PDA if found if npc and id and item_device.device_npc_pda[section] then ui_pda_npc_tab.register_pda(npc, section, id) end end -- Set remaining uses if needed if id then cnt = cnt - max_uses if cnt < 0 then local uses = (max_uses - (-cnt)) process_item(section , id, {uses = uses} ) end else printe("!ERROR: relocate_item_to_actor | object for section [%s] is bugged!", section) break end end local box_size = IsItem("ammo",section) if box_size then amount = amount * box_size end news_manager.relocate_item(actor, "in", section, amount) end function relocate_item_from_actor(actor, npc, section, amount) if (not actor) then return end if (npc == nil) then --printe("!ERROR: Couldn't relocate_item_from_actor | no npc found!") end amount = amount or 1 local cnt = amount local max_uses = IsItem("multiuse",section) local keep_itr = true local function itr(temp, obj) --printf("~relocate_item_from_actor | checked [%s]", obj:section()) if keep_itr and (obj and obj:section() == section) then --printf("-relocate_item_from_actor | found needed section [%s]", section) local uses = max_uses and obj:get_remaining_uses() or 1 cnt = cnt - uses if (cnt >= 0) then if npc then actor:transfer_item(obj, npc) else alife_release_id(obj:id()) end if (cnt == 0) then keep_itr = false end else local remain_1 = -cnt local remain_2 = max_uses - remain_1 process_item(section, obj:id(), {uses = remain_1}) if npc then alife_create_item(section, npc, {uses = remain_2}) end keep_itr = false end end end actor:iterate_inventory(itr, nil) if cnt > 0 then printe("! ERROR: Couldn't relocate_item_from_actor | not enough item [%s] recolated! need %s more", section, cnt) end local box_size = IsItem("ammo",section) if box_size then amount = amount * box_size end news_manager.relocate_item(actor, "out", section, amount) end ------------------------------- -- Sound Effects ------------------------------- local time_snd_prev = 0 local snd_on_take = { ["coin"] = {"interface\\items\\inv_items_money_coin_2"}, ["bolt"] = {"interface\\items\\inv_items_ammo_1"}, ["paper"] = {"interface\\items\\inv_items_money_paper"}, ["bottle"] = {"interface\\items\\inv_items_bottle_",1,2}, ["pills"] = {"interface\\items\\inv_items_pills_2"}, ["part"] = {"interface\\items\\inv_items_parts_",1,2}, ["outfit"] = {"interface\\items\\inv_items_cloth_",1,3}, ["ammo"] = {"interface\\items\\inv_items_ammo_",4,7}, ["grenade"] = {"interface\\items\\inv_items_grenade_",1,2}, ["knife"] = {"interface\\items\\inv_items_knife_",1,2}, ["weapon"] = {"interface\\items\\inv_items_wpn_",1,2}, ["other"] = {"interface\\items\\inv_items_generic_",2,5}, } function play_item_sound(item, vol) if (not item) then printe("!ERROR itms_manager | play_item_sound | no object recieved!") end local snd_type = SYS_GetParam(0,item:section(),"snd_on_take") if not (snd_type and snd_type ~= "") then return end local snd = snd_on_take[snd_type] if (not snd) then return end local snd_obj if (#snd == 1) then snd_obj = sound_object(snd[1]) else snd_obj = sound_object(snd[1] .. tostring(math.random(snd[2],snd[3]))) end local time_g = time_global() if snd_obj and (time_g > time_snd_prev + 25) then snd_obj:play(db.actor,0,sound_object.s2d) snd_obj.volume = vol or 1 end time_snd_prev = time_g end ------------------------------- -- Item Processor ------------------------------- local c_instance function get_item_processor() if c_instance == nil then c_instance = ItemProcessor() end return c_instance end function process_item(...) if c_instance == nil then c_instance = ItemProcessor() end return c_instance:Process_Item(...) end function create_item(...) if c_instance == nil then c_instance = ItemProcessor() end return c_instance:Create_Item(...) end class "ItemProcessor" function ItemProcessor:__init() self.Remove = {} self.Cond = {} self.Uses = {} self.Ammo = {} self.Debug = false -- true to debug self.Cycles = 10 end function ItemProcessor:update() -- Process Condition for id,con in pairs(self.Cond) do local obj = level.object_by_id(id) if obj then if self.Debug then printf("* ItemProcessor | processing condition | id: %s - con: %s", id, con) end obj:set_condition(con) local new_con = obj:condition() if (new_con < con + 0.02) and (new_con > con - 0.02) then -- range check self.Cond[id] = nil if self.Debug then printf("# ItemProcessor | processing condition done | id: %s - con: %s", id, new_con) end else self:Remove_Process(id,"!","can't set condition!") end elseif alife_object(id) then self:Remove_Process(id,"~","server object exists, no game object yet") else self:Remove_Process(id,"!","no game object!") end end -- Process Uses for id,uses in pairs(self.Uses) do local obj = level.object_by_id(id) if obj then if self.Debug then printf("* ItemProcessor | processing uses | id: %s - uses: %s", id, uses) end obj:set_remaining_uses(uses) if (obj:get_remaining_uses() == uses) then self.Uses[id] = nil if self.Debug then printf("# ItemProcessor | processing uses done | id: %s - con: %s", id, uses) end else self:Remove_Process(id,"!","can't set uses!") end elseif alife_object(id) then self:Remove_Process(id,"~","server object exists, no game object yet") else self:Remove_Process(id,"!","no game object!") end end -- Process Ammo for id,ammo in pairs(self.Ammo) do local obj = level.object_by_id(id) if obj then if self.Debug then printf("* ItemProcessor | processing ammo | id: %s - ammo: %s", id, ammo) end obj:ammo_set_count(ammo) if (obj:ammo_get_count() == ammo) then -- range self.Ammo[id] = nil if self.Debug then printf("# ItemProcessor | processing ammo done | id: %s - con: %s", id, new_con) end else self:Remove_Process(id,"!","can't set ammo!") end elseif alife_object(id) then self:Remove_Process(id,"~","server object exists, no game object yet") else self:Remove_Process(id,"!","no game object!") end end end function ItemProcessor:Create_Item(section, owner, t) t = t or {} if section then local uses section, uses = self:Extract_Uses(section) if (ini_sys:section_exist(section)) then -- Spawn object local se_itm if owner then -- Collect spawn data local pos, lvi, gvi, pid, spawn_typ if (type(owner) == "table") then pos, lvi, gvi, pid = owner[1], owner[2], owner[3], owner[4] if pid then spawn_typ = "on custom object" else spawn_typ = "in world" end elseif (type(owner.id) == "function") then pos, lvi, gvi, pid = owner:position(), owner:level_vertex_id(), owner:game_vertex_id(), owner:id() spawn_typ = "on game object" elseif owner.id then pos, lvi, gvi, pid = owner.position, owner.m_level_vertex_id, owner.m_game_vertex_id, owner.id spawn_typ = "on server object" end -- Validate if not (pos and lvi and gvi) then callstack() printe("! ItemProcessor | Missing spawn properties for [%s] | Spawn type: %s | pos: %s, lvi: %s, gvi: %s", section, spawn_typ, pos , lvi , gvi) return nil end if self.Debug then local obj_p = pid and alife_object(pid) local name_p = obj_p and (obj_p.id == AC_ID and "actor" or obj_p:name()) or "" spawn_typ = spawn_typ .. (obj_p and (" (".. name_p .. ")") or "") end -- Ammo need unique process to spawn multi-objects, return result from here if t.ammo and t.ammo > 0 and IsItem("ammo",section) then -- Replace damaged items with old if _NO_DAMAGED_AMMO allows it if _NO_DAMAGED_AMMO and string_find(section,"verybad") then local new_section = string_gsub(section,"verybad","bad") if ini_sys:section_exist(new_section) then section = new_section end end local num_in_box = ini_sys:r_u32(section, "box_size") local num = t.ammo local se_tbl = {} local sim = alife() local p_id = pid or 65535 -- because xray while (num > num_in_box) do se_tbl[#se_tbl+1] = sim:create_ammo(section, pos, lvi, gvi, p_id, num_in_box) alife_record( se_tbl[#se_tbl] , true ) num = num - num_in_box if self.Debug then local se_ammo = se_tbl[#se_tbl] printf("/ alife_create [%s] (%s) x%s ammo %s", section, se_ammo and se_ammo.id, num_in_box, spawn_typ) end end se_tbl[#se_tbl+1] = sim:create_ammo(section, pos, lvi, gvi, p_id, num) alife_record( se_tbl[#se_tbl] , true ) if self.Debug then local se_ammo = se_tbl[#se_tbl] printf("/ alife_create [%s] (%s) x%s ammo %s", section, se_ammo and se_ammo.id, num, spawn_typ) end return se_tbl -- Other items else if pid then se_itm = alife():create(section, pos, lvi, gvi, pid) else se_itm = alife():create(section, pos, lvi, gvi) end if self.Debug then printf("/ alife_create [%s] (%s) on %s", section, se_itm and se_itm.id, spawn_typ) end end else printe("! ItemProcessor | Missing spawn owner for [%s]!", section) end -- Process if se_itm then alife_record(se_itm,true) -- Multiuse items if IsItem("multiuse",section) then uses = uses or t.uses -- Send to process if uses then self.Uses[se_itm.id] = uses --self:Process_Item(section, se_itm.id, { uses = uses }) end -- Degradable items elseif utils_item.is_degradable(nil, section) then local cond = t.cond -- Parts if t.cond_cr and t.cond_ct and IsItem(cond_ct,section) then cond = (#t.cond_cr > 2) and self:Random_Choice(t.cond_cr) or self:Random_Condition(t.cond_cr) -- others elseif t.cond_r then cond = (#t.cond_r > 2) and self:Random_Choice(t.cond_r) or self:Random_Condition(t.cond_r) end -- Send to process if cond then self.Cond[se_itm.id] = cond --self:Process_Item(section, se_itm.id, { cond = cond }) end end return se_itm else printe("!ERROR [%s] is not spawned by ItemProcessor",section) end else printe("! ItemProcessor | section [%s] doesn't exist!", section) end else printf("~ ItemProcessor | nothing is passed to process!") end return nil end function ItemProcessor:Process_Item(section, id, t) if t then if t.uses then self.Uses[id] = t.uses elseif t.cond then self.Cond[id] = t.cond elseif t.ammo then self.Ammo[id] = t.ammo elseif self.Debug then --printf("* ItemProcessor | no process done for [%s] (%s)", section, id) end end end function ItemProcessor:Remove_Process(id, mark, str) mark = mark or "~" self.Remove[id] = self.Remove[id] and (self.Remove[id] + 1) or 0 if (self.Remove[id] > self.Cycles) then self.Cond[id] = nil self.Uses[id] = nil self.Ammo[id] = nil self.Remove[id] = nil if self.Debug then local obj = level.object_by_id(id) local p = obj and obj:parent() printf("%s ItemProcessor | %s: (%s) [%s] - owner: (%s) [%s]", mark, str, id, obj and obj:section(), p and p:id() or "-", p and p:section()) end end end function ItemProcessor:Random_Choice(arg) if arg and (#arg > 0) then local r = math.random(1, #arg) return arg[r] end end function ItemProcessor:Random_Condition(arg) if arg and (#arg > 0) then return (math.random(arg[1], arg[2])/100) end end function ItemProcessor:Extract_Uses(sec_d) local _, __, sec_u, uses = string_find(sec_d,"(.*)__(%d)") if sec_u and uses and tonumber(uses) and ini_sys:section_exist(sec_u) then return sec_u, tonumber(uses) end return sec_d end -------------------------------------------------------------------------------- ------------------------------- -- DEBUG ------------------------------- function generate_boosters_list() local config = ini_file_ex("booster_stats.ltx",true) ini_sys:section_for_each(function(section) local cls = ini_sys:r_string_ex(section,"class") if (cls == "II_FOOD") and (ini_sys:r_float_ex(section,"boost_time") > 0) then local str = "" str = str .. ("cost:" .. ini_sys:r_float_ex(section,"cost") .. "|") str = str .. (ini_sys:r_float_ex(section,"max_uses") and ("uses:" .. ini_sys:r_float_ex(section,"max_uses") .. "|") or ("uses:1|")) str = str .. ("boost_time:" .. ini_sys:r_float_ex(section,"boost_time") .. "|") if ini_sys:r_float_ex(section,"boost_max_weight") ~= 0 then str = str .. ("boost_max_weight:" .. ini_sys:r_float_ex(section,"boost_max_weight") .. "|") end if ini_sys:r_float_ex(section,"boost_health_restore") ~= 0 then str = str .. ("boost_health_restore:" .. ini_sys:r_float_ex(section,"boost_health_restore") .. "|") end if ini_sys:r_float_ex(section,"boost_power_restore") ~= 0 then str = str .. ("boost_power_restore:" .. ini_sys:r_float_ex(section,"boost_power_restore") .. "|") end if ini_sys:r_float_ex(section,"boost_radiation_restore") ~= 0 then str = str .. ("boost_radiation_restore:" .. ini_sys:r_float_ex(section,"boost_radiation_restore") .. "|") end if ini_sys:r_float_ex(section,"boost_bleeding_restore") ~= 0 then str = str .. ("boost_bleeding_restore:" .. ini_sys:r_float_ex(section,"boost_bleeding_restore") .. "|") end if ini_sys:r_float_ex(section,"boost_radiation_protection") ~= 0 then str = str .. ("boost_radiation_protection:" .. ini_sys:r_float_ex(section,"boost_radiation_protection") .. "|") end if ini_sys:r_float_ex(section,"boost_telepat_protection") ~= 0 then str = str .. ("boost_telepat_protection:" .. ini_sys:r_float_ex(section,"boost_telepat_protection") .. "|") end if ini_sys:r_float_ex(section,"boost_chemburn_protection") ~= 0 then str = str .. ("boost_chemburn_protection:" .. ini_sys:r_float_ex(section,"boost_chemburn_protection") .. "|") end if ini_sys:r_float_ex(section,"boost_burn_immunity") ~= 0 then str = str .. ("boost_burn_immunity:" .. ini_sys:r_float_ex(section,"boost_burn_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_shock_immunity") ~= 0 then str = str .. ("boost_shock_immunity:" .. ini_sys:r_float_ex(section,"boost_shock_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_radiation_immunity") ~= 0 then str = str .. ("boost_radiation_immunity:" .. ini_sys:r_float_ex(section,"boost_radiation_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_telepat_immunity") ~= 0 then str = str .. ("boost_telepat_immunity:" .. ini_sys:r_float_ex(section,"boost_telepat_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_chemburn_immunity") ~= 0 then str = str .. ("boost_chemburn_immunity:" .. ini_sys:r_float_ex(section,"boost_chemburn_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_strike_immunity") ~= 0 then str = str .. ("boost_strike_immunity:" .. ini_sys:r_float_ex(section,"boost_strike_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_wound_immunity") ~= 0 then str = str .. ("boost_wound_immunity:" .. ini_sys:r_float_ex(section,"boost_wound_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_explosion_immunity") ~= 0 then str = str .. ("boost_explosion_immunity:" .. ini_sys:r_float_ex(section,"boost_explosion_immunity") .. "|") end if ini_sys:r_float_ex(section,"boost_fire_wound_immunity") ~= 0 then str = str .. ("boost_fire_wound_immunity:" .. ini_sys:r_float_ex(section,"boost_fire_wound_immunity") .. "|") end config:w_value("temp", section, str) end end) config:save() end