local DIALOG_ID = {} local sfind = string.find local fetch_items = {} local fetch_rank_tier = {} local patch_general = { ["army"] = { ["freedom_patch"] = 0, ["bandit_patch"] = 0, ["stalker_patch"] = 0, ["csky_patch"] = 0, ["monolith_patch"] = 0, }, ["bandit"] = { ["army_patch"] = 0, ["dolg_patch"] = 0, ["stalker_patch"] = 0, ["csky_patch"] = 0, ["ecolog_patch"] = 0, }, ["csky"] = { ["army_patch"] = 0, ["bandit_patch"] = 0, ["renegade_patch"] = 0, }, ["dolg"] = { ["freedom_patch"] = 0, ["bandit_patch"] = 0, ["monolith_patch"] = 0, }, ["freedom"] = { ["dolg_patch"] = 0, ["army_patch"] = 0, ["monolith_patch"] = 0, }, ["killer"] = { ["freedom_patch"] = 0, ["bandit_patch"] = 0, ["dolg_patch"] = 0, ["army_patch"] = 0, ["monolith_patch"] = 0, }, ["monolith"] = { ["army_patch"] = 0, ["freedom_patch"] = 0, ["bandit_patch"] = 0, ["stalker_patch"] = 0, ["csky_patch"] = 0, ["dolg_patch"] = 0, }, ["stalker"] = { ["bandit_patch"] = 0, }, ["greh"] = { ["army_patch"] = 0, ["freedom_patch"] = 0, ["bandit_patch"] = 0, ["stalker_patch"] = 0, ["csky_patch"] = 0, ["dolg_patch"] = 0, }, ["renegade"] = { ["army_patch"] = 0, ["dolg_patch"] = 0, ["stalker_patch"] = 0, ["csky_patch"] = 0, ["ecolog_patch"] = 0, }, } local faction_lookup = { -- List of factions used in patch tasks "stalker", "dolg", "freedom", "csky", "ecolog", "killer", "army", "bandit", "monolith" } if smr_amain_mcm.get_config("smr_enabled") then faction_lookup = smr_pop.get_faction_spawn_table() end ---------------------------< Utility >--------------------------- function postpone_fetch_for_next_frame(task_id, section, amount) if not (task_id and section) then return true end task_id = string.sub(task_id,1,-7) or "" -- because it ends with fetch amount = amount or 1 local extra = "" if (ini_sys:r_string_ex(section,"kind") == "i_arty") then extra = " " .. game.translate_string("st_ui_artefact") end local clr = utils_xml.get_color("pda_white") --"%c[255,238,153,26]" local news_caption = game.translate_string("ui_inv_needs") .. ":" --game.translate_string(task_manager.task_ini:r_string_ex(task_id, "title")) or "error" local news_ico = task_manager.task_ini:r_string_ex(task_id, "icon") or "ui_inGame2_D_Sisshik" local news_text = ui_item.get_sec_name(section) .. extra .. clr .. " (x" .. amount .. ")" db.actor:give_talk_message2(news_caption, news_text, news_ico, "iconed_answer_item") return true end xr_effects.fetch_reward_and_remove = function(actor, npc, p) -- Remove fetch items and give money reward: -- Description: Removes fetch task items and gives actor money reward based on item value -- Usage: fetch_reward_and_remove( p[1]:p[2] ) -- Parameters: -- p[1] (type: string) Var name given to fetch task items -- p[2] (type: float) Multiplier for cost of items to apply to reward value (optional; default = 1.0) -- Return value (type: none): none, removes fetch items and gives actor reward -- Get fetch items local sec = load_var( db.actor, p[1] ) if not (sec) then return end local sim = alife() -- Extract artefact if its in container if string.find(sec,"af_") and (not db.actor:object(sec)) then local break_con local break_arty local id_combo local search_cont = { "lead_box" , "af_aam", "af_aac", "af_iam" } for i=1,#search_cont do if db.actor:object(sec .. "_" .. search_cont[i]) then break_arty = sec break_con = search_cont[i] id_combo = db.actor:object(sec .. "_" .. search_cont[i]):id() break end end if id_combo and break_con and break_arty and ini_sys:section_exist(break_con) then printdbg("/ Artefact container found, artefact [%s] - contairer [%s]", break_arty, break_con) alife_create_item(break_arty, db.actor) alife_create_item(break_con, db.actor) alife_release_id(id_combo) -- play effect level.add_cam_effector('camera_effects\\item_use.anm', 8053, false,'') xr_effects.play_inv_aac_open() end end local function timer() -- delay for 1 sec, to register alife changes -- Gather task items and cost local count = load_var( db.actor, (p[1] .. "_count") ) or 1 local mult = tonumber(p[2]) or 1 local max_use = IsItem("multiuse",sec) or 1 local cost = ini_sys:r_float_ex(sec,"cost") * (1 / max_use) local last_obj local collected_itms = {} local total_cost, remain = 0,0 local cur_count = count local use_con = (max_use == 1) and (not IsItem("device",sec)) and (not IsItem("battery", sec)) and true or false local function itr(temp, obj) if (obj:section() == sec) then local cnt = (max_use > 1) and obj:get_remaining_uses() or 1 local con = use_con and obj:condition() or 1 collected_itms[obj:id()] = cnt total_cost = total_cost + (cost * cnt * (con * con) * mult) cur_count = cur_count - cnt last_obj = obj end if (cur_count <= 0) then if (cur_count < 0) then remain = math.abs(cur_count) end return true end --return false end db.actor:iterate_inventory(itr, nil) -- Check availability if is_empty(collected_itms) then printe("! ERROR: fetch_reward_and_remove | no fetch item found!") return true end if (total_cost < 50) then total_cost = 50 printf("~ Warning: fetch_reward_and_remove | total_cost is below 50") end -- Reward value for artefacts depends on progression difficulty if IsArtefact(last_obj) then local eco = game_difficulties.get_eco_factor("type") or 0.5 local factor = (eco == 3 and 0.4) or (eco == 2 and 0.5) or 0.6 total_cost = total_cost * factor end -- local delta = math.floor(total_cost * 0.1) local min_reward = (total_cost - delta) local max_reward = (total_cost + delta) xr_effects.reward_random_money( actor, npc, { min_reward , max_reward } ) -- Give task items to npc local trade_npc = false --get_speaker() for k,v in pairs(collected_itms) do if trade_npc then local obj = level.object_by_id(k) if obj then db.actor:transfer_item(obj, trade_npc) end else alife_release_id(k) end end if (remain > 0) and IsItem("multiuse",sec) then alife_create_item(sec, db.actor, {uses = remain}) local last_obj_uses = last_obj:get_remaining_uses() if last_obj_uses and (last_obj_uses > remain) then local uses = (last_obj_uses - remain) alife_process_item(last_obj:section(), last_obj:id(), {uses = uses}) end end news_manager.relocate_item(db.actor, "out", sec, count) return true end CreateTimeEvent(0,"delay_reward", 1, timer) end xr_effects.remove_fetch_item = function(actor,npc,p) local section = load_var(db.actor,p[1]) if (section and db.actor:object(section)) then local amt = p[2] or load_var(db.actor,p[1].."_count") or 1 local trade_npc = get_speaker() if (trade_npc) then local function transfer_object_item(itm) if (itm:section() == section and amt > 0) then db.actor:transfer_item(itm, trade_npc) amt = amt - 1 end if (amt <= 0) then return true end return false end db.actor:inventory_for_each(transfer_object_item) else xr_effects.remove_item(actor, npc, {section,amt}) end end end xr_effects.remove_artefact_item = function(actor,npc,p) local section = load_var(db.actor,p[1]) if (not section) then printe("!ERROR xr_effects.remove_artefact_item | no var found for %s", p[1]) return end local cont local obj = db.actor:object(section) if (not obj) then local search_cont = { "lead_box" , "af_aam", "af_aac", "af_iam" } for i=1,#search_cont do if db.actor:object(section .. "_" .. search_cont[i]) then obj = db.actor:object(section .. "_" .. search_cont[i]) cont = search_cont[i] break end end end if (not obj) then return end local trade_npc = get_speaker() if (trade_npc) then db.actor:transfer_item(obj, trade_npc) else alife_release(obj) end if cont then alife_create_item(cont, db.actor) -- play effect level.add_cam_effector('camera_effects\\item_use.anm', 8053, false,'') xr_effects.play_inv_aac_open() end end ---------------------------< Effects >--------------------------- local function get_suitable_item(tbl,rank) local temp = {} for sec,tier in pairs(tbl) do if (not rank) or (rank and fetch_rank_tier[rank] and fetch_rank_tier[rank][tier]) then temp[#temp + 1] = sec --printf("- Fetch items | found proper item [%s] tier (%s) for rank (%s)", sec, tier, rank) end end return (#temp > 0) and temp[math.random(#temp)] end xr_effects.setup_fetch_task = function(actor,npc,p) -- setup_supplies_fetch_task -- Pick a random item from the list: -- param 1 - var name -- param 2 - items type -- param 2 - min count -- param 3 - max count local npc = get_speaker(true) local id = npc and npc:id() local sec = DIALOG_ID[id] and DIALOG_ID[id][p[1]] and DIALOG_ID[id][p[1]].sec local cnt = DIALOG_ID[id] and DIALOG_ID[id][p[1]] and DIALOG_ID[id][p[1]].cnt if (sec and ini_sys:section_exist(sec) and cnt) then dialogs._FETCH_TEXT = ui_item.get_sec_name(sec) save_var( db.actor, p[1], sec ) save_var( db.actor, (p[1] .. "_count"), cnt ) else local npc_comm = npc and character_community(npc) or "stalker" local npc_rank = ranks.get_obj_rank_name(npc) or "experienced" local itms = {} -- non-key table -- expand supplies list to include drinks and smoke if the task giver is not monolith if (p[2] == "supplies") and (npc_comm ~= "monolith") then itms = fetch_items["supplies"] for sec,tier in pairs(fetch_items["drink"]) do itms[sec] = tier end -- Gather patches of NPC's enemy factions elseif (p[2] == "patch_general") then local patches = {} for i=1,#faction_lookup do if game_relations.is_factions_enemies(faction_lookup[i], npc_comm) then local comm if (faction_lookup[i] == "killer") then comm = "merc" elseif (faction_lookup[i] == "dolg") then comm = "duty" else comm = faction_lookup[i] end local patch = comm .. "_patch" patches[patch] = 0 end end if is_empty(patches) then if smr_amain_mcm.get_config("smr_enabled") then local t = smr_pop.get_faction_spawn_table() local patch = t[math.random(#t)] .. "_patch" patches[patch] = 0 else patches = patch_general[npc_comm] end end itms = patches -- Gather suitable repair kits elseif (p[2] == "repair") then -- Get NPC's weapon local obj_wep = npc:best_weapon() local sec_wep = obj_wep and obj_wep:section() -- Gather proper repair kits local repair_kits = { ["sharpening_stones"] = 0, ["sewing_kit_b"] = 0, } if sec_wep then local wep_type = ini_sys:r_string_ex(sec_wep,"repair_type") or "" for sec,tier in pairs(fetch_items["repair"]) do local kit_type = parse_list(ini_sys,sec,"repair_only",true) if kit_type[wep_type] then repair_kits[sec] = tier end end end itms = repair_kits -- Gather suitable weapons elseif (p[2] == "weapons") then local f = faction_expansions.faction[npc_comm] local npc_preference = f and f["weapon"] or "" -- Collects weapons suitable for npc rank local wpn_by_rank = {} if fetch_items["weapons_" .. npc_rank] then local tbl = fetch_items["weapons_" .. npc_rank] for sec,tier in pairs(tbl) do wpn_by_rank[sec] = tier end end -- Collects weapons suitable for npc faction local wpn_by_ref = {} if fetch_items["weapons_" .. npc_preference] then local tbl = fetch_items["weapons_" .. npc_preference] for sec,tier in pairs(tbl) do wpn_by_ref[sec] = tier end end -- collect suitable weapons local wpns = {} for sec,tier in pairs(wpn_by_rank) do if wpn_by_ref[sec] then wpns[sec] = tier end end for sec,tier in pairs(wpn_by_ref) do if wpn_by_rank[sec] then wpns[sec] = tier end end if is_not_empty(wpns) then itms = wpns else itms = fetch_items["weapons"] end else itms = fetch_items[p[2]] end if (not itms) then printe("! ERROR: %s | fetch list [%s] is invalid", p[1], p[2]) end local min_count = (p[3] and tonumber( p[3] ) or 1) local max_count = (p[4] and tonumber( p[4] ) or min_count) sec = get_suitable_item(itms, npc_rank) or random_key_table(itms) cnt = math.random( min_count, max_count ) -- Save chosen fetch item: if (not DIALOG_ID[id]) then DIALOG_ID[id] = {} end if (not DIALOG_ID[id][p[1]]) then DIALOG_ID[id][p[1]] = {} end DIALOG_ID[id][p[1]].sec = sec DIALOG_ID[id][p[1]].cnt = cnt dialogs._FETCH_TEXT = ui_item.get_sec_name(sec) save_var( db.actor, p[1], sec ) save_var( db.actor, (p[1] .. "_count"), cnt ) end CreateTimeEvent(0,"setup_fetch_task", 0, postpone_fetch_for_next_frame, p[1], sec, cnt) end xr_effects.setup_supplies_fetch_task_lostzone_patch = function(actor,npc,p) local npc = get_speaker(true) local id = npc and npc:id() local sec = DIALOG_ID[id] and DIALOG_ID[id][p[1]] and DIALOG_ID[id][p[1]].sec local cnt = DIALOG_ID[id] and DIALOG_ID[id][p[1]] and DIALOG_ID[id][p[1]].cnt if (sec and cnt and ini_sys:section_exist(sec)) then dialogs._FETCH_TEXT = ui_item.get_sec_name(sec) save_var( db.actor, p[1], sec ) save_var( db.actor, (p[1] .. "_count"), cnt ) else local comm = npc and character_community(npc) or "stalker" local itms = patch_general[comm] or patch_general["stalker"] -- SMR if smr_amain_mcm.get_config("smr_enabled") then local itms = {} for i=1,#faction_lookup do if game_relations.is_factions_enemies(faction_lookup[i], comm) then if (faction_lookup[i] == "killer") then comm = "merc" elseif (faction_lookup[i] == "dolg") then comm = "duty" else comm = faction_lookup[i] end local patch = comm .. "_patch" itms[patch] = 0 end end if is_empty(itms) then local t = smr_pop.get_faction_spawn_table() local patch = t[math.random(#t)] .. "_patch" itms[patch] = 0 end end sec = random_key_table(itms) cnt = math.random(p[2] and tonumber(p[2]) or 1,p[3] and tonumber(p[3]) or 1) -- Save chosen fetch item: if (not DIALOG_ID[id]) then DIALOG_ID[id] = {} end if (not DIALOG_ID[id][p[1]]) then DIALOG_ID[id][p[1]] = {} end DIALOG_ID[id][p[1]].sec = sec DIALOG_ID[id][p[1]].cnt = cnt dialogs._FETCH_TEXT = ui_item.get_sec_name(sec) save_var( db.actor, p[1], sec ) save_var( db.actor, (p[1] .. "_count"), cnt ) end CreateTimeEvent(0,"setup_fetch_task", 0, postpone_fetch_for_next_frame, p[1], sec, cnt) end xr_effects.setup_generic_fetch_task = function(actor,npc,p) -- param1 - variable name -- param2 - count -- param3+ - sections if (p[1] and p[2] and p[3]) then local npc = get_speaker(true) local id = npc and npc:id() local sec = DIALOG_ID[id] and DIALOG_ID[id][p[1]] and DIALOG_ID[id][p[1]].sec local cnt = DIALOG_ID[id] and DIALOG_ID[id][p[1]] and DIALOG_ID[id][p[1]].cnt if (sec and cnt and ini_sys:section_exist(sec)) then dialogs._FETCH_TEXT = ui_item.get_sec_name(sec) save_var( db.actor, p[1], sec ) if (cnt > 1) then save_var( db.actor, (p[1] .. "_count"), cnt ) end else sec = #p > 3 and p[math.random(3,#p)] or p[3] if (sec and ini_sys:section_exist(sec)) then cnt = tonumber(p[2]) or 1 -- Save chosen fetch item: if (not DIALOG_ID[id]) then DIALOG_ID[id] = {} end if (not DIALOG_ID[id][p[1]]) then DIALOG_ID[id][p[1]] = {} end DIALOG_ID[id][p[1]].sec = sec DIALOG_ID[id][p[1]].cnt = cnt dialogs._FETCH_TEXT = ui_item.get_sec_name(sec) save_var( db.actor, p[1], sec ) if (cnt > 1) then save_var( db.actor, (p[1] .. "_count"), cnt ) end else printe("!ERROR: xr_effects:setup_generic_fetch_task - invalid section %s",sec) end end CreateTimeEvent(0,"setup_fetch_task", 0, postpone_fetch_for_next_frame, p[1], sec, cnt) end end ---------------------------< Target functor >--------------------------- task_functor.general_fetch_task = function(task_id,field,p,tsk) if (field == "title") then local fetch = db.actor and ui_item.get_sec_name(load_var(db.actor,task_id.."_fetch","")) local count = fetch and load_var(db.actor,task_id.."_fetch_count") or 1 if (count > 1) then return strformat((game.translate_string(p) or ""),fetch .. " x" .. tostring(count)) end return strformat(game.translate_string(p) or "",fetch) elseif (field == "descr") then if (tsk.stage == 1) then return game.translate_string("st_return_for_reward") end local fetch = db.actor and ui_item.get_sec_name(load_var(db.actor,task_id.."_fetch","")) local count = fetch and load_var(db.actor,task_id.."_fetch_count") or 1 if (count > 1) then return strformat((game.translate_string(p) or ""),fetch .. " x" .. tostring(count)) end return strformat(game.translate_string(p) or "",fetch) elseif (field == "target") then if (tsk.stage == 1) then local id = db.actor and load_var(db.actor,task_id.."_target_id") or tsk.task_giver_id if (id) then return id end local story_id = string.sub(task_id,1,string.find(task_id,"_task")-1) local se_obj = get_story_se_object(story_id) if (se_obj) then return se_obj.id end end end end task_functor.general_warfare_fetch_task = function(task_id,field,p,tsk) -- xQd, add the name and location of the warfare trader to the task description along with the fetch item local id = tsk.task_giver_id local se_obj = id and alife_object(id) if not (se_obj) then --printf("is_task_giver_valid se_obj nil id=%s %s",id,tsk.id) return "" else local name = se_obj:character_name() local lvl = --[[game.translate_string(se_obj.community) or]] game.translate_string(alife():level_name(game_graph():vertex(se_obj.m_game_vertex_id):level_id())) if (field == "title") then local fetch = db.actor and ui_item.get_sec_name(load_var(db.actor,task_id.."_fetch","")) local count = fetch and load_var(db.actor,task_id.."_fetch_count") or 1 if (count > 1) then return strformat((game.translate_string(p) or ""),fetch .. " x" .. tostring(count)) end return strformat(game.translate_string(p) or "",fetch) elseif (field == "descr") then if (tsk.stage == 1) then return game.translate_string("st_return_for_reward") end local fetch = db.actor and ui_item.get_sec_name(load_var(db.actor,task_id.."_fetch","")) local count = fetch and load_var(db.actor,task_id.."_fetch_count") or 1 if (count > 1) then return strformat((game.translate_string(p) or ""),name,lvl,fetch .. " x" .. tostring(count)) end return strformat(game.translate_string(p) or "",name,lvl,fetch) end end end ---------------------------< Status functor >--------------------------- task_status_functor.actor_has_fetch_item = function(tsk,task_id) local actor = db.actor local section = actor and load_var(actor,task_id.."_fetch") if (not section) then return end local item = section and actor:object(section) if (item ~= nil) then if (task_id == "esc_2_12_stalker_nimble_task_1" or task_id == "jup_b220_trapper_task_3") then if (item:condition() >= 0.9) then tsk.stage = 1 else tsk.stage = 0 end else local count = load_var(actor,task_id.."_fetch_count") if (count and count > 1) then local cnt = utils_item.get_amount(db.actor, section, 1) if (cnt >= count) then tsk.stage = 1 else tsk.stage = 0 end return end tsk.stage = 1 -- should never happen but in such case let player finish quest for free end elseif section and sfind(section,"af_") then if actor:object(section .. "_af_aam") or actor:object(section .. "_af_iam") or actor:object(section .. "_af_aac") or actor:object(section .. "_lead_box") then tsk.stage = 1 else tsk.stage = 0 end else tsk.stage = 0 end end task_status_functor.actor_has_fetch_weapon_warfare = function(tsk,task_id) -- xQd, added this for warfare fetch weapon tasks if not (db.actor) then return end local section = db.actor and load_var(db.actor,task_id.."_fetch") local item = section and db.actor:object(section) if (item ~= nil) then if (item:condition() >= 0.60) then tsk.stage = 1 else tsk.stage = 0 end else tsk.stage = 0 end end ---------------------------< Callbacks >--------------------------- local function save_state(m_data) m_data.tasks_fetch_ids = DIALOG_ID end local function load_state(m_data) DIALOG_ID = m_data.tasks_fetch_ids or {} end local function on_before_level_changing() empty_table(DIALOG_ID) alife_storage_manager.get_state().tasks_fetch_ids = nil printdbg("~ tasks_fetch | cleaned fetch list for npcs") end function on_game_start() local ini_fetch = ini_file("items\\settings\\fetch_list.ltx") local n = 0 local result, id, value = "","","" local list = {} n = ini_fetch:line_count("fetch_list") for i=0,n-1 do result, id, value = ini_fetch:r_line_ex("fetch_list",i,"","") list[#list + 1] = id end n = ini_fetch:line_count("fetch_tiers") for i=0,n-1 do result, id, value = ini_fetch:r_line_ex("fetch_tiers",i,"","") if id and value then fetch_rank_tier[id] = {} fetch_rank_tier[id][0] = true local nums = str_explode(value,",") for j=1,#nums do local m = tonumber(nums[j]) or 0 fetch_rank_tier[id][m] = true end end end for k=1,#list do local category = list[k] fetch_items[category] = {} n = ini_fetch:line_count(category) for i=0,n-1 do result, id, value = ini_fetch:r_line_ex(category,i,"","") if id and ini_sys:section_exist(id) then fetch_items[category][id] = (value == "true" and 0) or ini_sys:r_float_ex(id,"tier") or 0 else printe("! WARNING: fetch_list.ltx | wrong section name [%s]", id) end end end RegisterScriptCallback("save_state",save_state) RegisterScriptCallback("load_state",load_state) RegisterScriptCallback("on_before_level_changing",on_before_level_changing) end