Divergent/mods/Zone Customization Project/gamedata/scripts/tasks_fetch.script

748 lines
22 KiB
Plaintext

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