Divergent/mods/Groks Body Health System Redux/gamedata/scripts/itms_manager.script

1528 lines
43 KiB
Plaintext

--[[
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