Divergent/mods/Barrier Defense Task Emissi.../gamedata/scripts/tasks_defense.script

588 lines
19 KiB
Plaintext

fix_broken_emissions = false
--[[ If your emissions are broken from the vanilla bug, follow these steps:
1. Edit the first line of this file from false to true like so:
fix_broken_emissions = true
2. Load your save with this mod active
3. Make a separate save
4. Edit true back to false at the top of this script
--]]
-- =======================================================================================
-- Created by tdef
-- Last modification: 2023/06/23
-- Modified by Catspaw: fix emissions no longer occurring after failing barrier task
-- Defend the barrier from monolith/zombies invasion
-- =======================================================================================
local ti2ta = utils_data.CTime_to_table
local ta2ti = utils_data.CTime_from_table
local send_tip = dynamic_news_helper.send_tip
local MONO_SQUADS = 6 --10
local ZOMB_SQUADS = 8 --15
local STARTING_SQUADS = 3 --5
local CHAT_DEL = 5 -- seconds
local DEBUG = false
function printl(frmt, ...)
if DEBUG then printf(strformat('!tasks_defense: ' .. frmt, ...)) end
end
-------------------------
--2d distance between 2 vectors
--a,b : vectors, either object.position or vector():set(x,y,z)
-------------------------
local function dist2d_sqr( a, b )
return (b.x-a.x)^2 + (b.z-a.z)^2
end
-------------------------
--2d distance between 2 objects
--a,b : id of objects
-------------------------
local function dist2d_id_sqr(a,b)
local sim = alife()
local ao = sim:object(a)
local bo = sim:object(b)
-- printf("ao %s bo %s",ao,bo)
-- printf("ao.pos %s bo.pos %s",ao.position,bo.position)
if ao and bo then
return dist2d_sqr(ao.position,bo.position)
else
return nil
end
end
if DEBUG then
add_console_command('start_barrier_monolith', function() db.actor:give_info_portion("barrier_defense_monolith_announced") end)
add_console_command('start_barrier_zombie', function() db.actor:give_info_portion("barrier_defense_zombie_announced") end)
end
--============================================< Callbacks >============================================--
local MDATA
function save_state(mdata)
mdata.defense_task_data = MDATA
end
function load_state(mdata)
if mdata.defense_task_data and not fix_broken_emissions then
printf('loading mdata')
MDATA = mdata.defense_task_data
--utils_data.print_table(MDATA)
else
printf('new mdata')
MDATA = {}
MDATA.barrier_defense_monolith = {}
MDATA.barrier_defense_zombie = {}
MDATA.allow_surge = true
end
end
function f_chatter(opponent, stage)
printl('f_chatter(%s, %s)',opponent,stage)
local name, icon = get_random_stalker_chatter()
if name and icon then
send_tip(game.translate_string("barrier_defense_"..opponent.."_chatter_"..math.random(1,7)),name,nil,nil, icon, nil, 'npc')
else
printf('error: get_random_stalker_chatter returned nil')
end
if stage < 3 then
stage = stage + 1
CreateTimeEvent(math.random(999),"chatter",CHAT_DEL,f_chatter,opponent,stage)
end
return true
end
function f_expire(x)
xr_effects.barrier_defense_news(nil,nil,{'end',x})
return true
end
function actor_on_first_update()
-- needs to be delayed because it runs before loading actually finishes so players dont see first message
local delay = 20 -- seconds
printl('actor_on_first_update')
if barrier_defense_available('barrier_defense_monolith') then
-- if DEBUG then db.actor:give_info_portion("TRIGGERED_M") end
CreateTimeEvent(0,"give_info",delay,function()
db.actor:give_info_portion("barrier_defense_monolith_announced")
send_tip(game.translate_string("barrier_defense_monolith_announce"),game.translate_string("mil_freedom_leader_name"),nil,nil, 'ui_inGame2_lukash', nil, 'npc')
MDATA.barrier_defense_monolith.last_msg_time = ti2ta(game.get_game_time())
CreateTimeEvent(math.random(999),"chatter",CHAT_DEL,f_chatter,'monolith',0)
return true
end)
elseif barrier_defense_available('barrier_defense_zombie') then
-- if DEBUG then db.actor:give_info_portion("TRIGGERED_Z") end
CreateTimeEvent(0,"give_info",delay,function()
db.actor:give_info_portion("barrier_defense_zombie_announced")
send_tip(game.translate_string("barrier_defense_zombie_announce"),game.translate_string("mil_freedom_leader_name"),nil,nil, 'ui_inGame2_lukash', nil, 'npc')
MDATA.barrier_defense_zombie.last_msg_time = ti2ta(game.get_game_time())
CreateTimeEvent(math.random(999),"chatter",CHAT_DEL,f_chatter,'zombie',0)
return true
end)
end
if db.actor:has_info('barrier_defense_monolith_announced') then
local last1 = MDATA.barrier_defense_monolith.last_msg_time and ta2ti(MDATA.barrier_defense_monolith.last_msg_time)
local delta1 = last1 and game.get_game_time():diffSec(last1) / (3600 * 24) or 100
if delta1 > 1 then
db.actor:disable_info_portion('barrier_defense_monolith_announced')
CreateTimeEvent(math.random(999),"chatter",delay,f_expire,'monolith')
end
end
if db.actor:has_info('barrier_defense_zombie_announced') then
local last2 = MDATA.barrier_defense_zombie.last_msg_time and ta2ti(MDATA.barrier_defense_zombie.last_msg_time)
local delta2 = last2 and game.get_game_time():diffSec(last2) / (3600 * 24) or 100
if delta2 > 1 then
db.actor:disable_info_portion('barrier_defense_zombie_announced')
CreateTimeEvent(math.random(999),"chatter",delay,f_expire,'zombie')
end
end
end
function skip_surge(t)
if MDATA and MDATA.allow_surge == false then
t.allow = false
end
end
function on_game_start()
RegisterScriptCallback("actor_on_first_update",actor_on_first_update)
RegisterScriptCallback("save_state",save_state)
RegisterScriptCallback("load_state",load_state)
RegisterScriptCallback("on_before_surge",skip_surge)
RegisterScriptCallback("on_before_psi_storm",skip_surge)
if DEBUG then RegisterScriptCallback("npc_on_update",npc_on_update) end
end
function npc_on_update(npc)
local be = npc:best_enemy()
if be and be:id() == 0 then
npc:kill(npc)
end
end
--============================================< Logic >============================================--
local RAND_S
function get_random_stalker_chatter()
if not RAND_S then
RAND_S = {}
for i=1,65534 do
local se_obj = alife_object(i)
if
se_obj and
IsStalker(nil, se_obj:clsid()) and
se_obj:alive() and
not game_relations.is_factions_enemies(se_obj:community(),"freedom") and
string.find(se_obj:name(),'default') and
not (se_obj:community() == 'bandit') and
not (se_obj:community() == 'trader')
then
table.insert(RAND_S,{game.translate_string(se_obj:character_name()),se_obj:character_icon()})
if #RAND_S > 10 then break end
end
end
end
if #RAND_S < 1 then
return nil, nil
end
local i = math.random(#RAND_S)
local x = table.remove(RAND_S, i)
return x[1],x[2]
end
function get_closest_freedom()
local mind = 9999999
local ret = nil
for i,v in ipairs(db.OnlineStalkers) do
local se_obj = alife_object(v)
if
se_obj and
IsStalker(nil, se_obj:clsid()) and
se_obj:alive() and
se_obj:community() == 'freedom' and
string.find(se_obj:name(),'default')
then
local dist = dist2d_id_sqr(0,v)
if dist < mind then
mind = dist
ret = v
end
end
end
if ret then
local se_obj = alife_object(ret)
return game.translate_string(se_obj:character_name()),se_obj:character_icon()
end
return nil, nil
end
function mid_news(str)
local name, icon = get_closest_freedom()
if name and icon then
send_tip(game.translate_string("barrier_defense_"..str.."_midchatter"),name,nil,nil, icon, nil, 'npc')
else
printf('get_closest_freedom returned nil')
end
end
function barrier_defense_available(tid)
printl('barrier_defense_available(%s)',tid)
-- dont crash
if not db.actor then return false end
-- dont trigger if already had message
if db.actor:has_info('barrier_defense_monolith_announced') then return false end
if db.actor:has_info('barrier_defense_zombie_announced') then return false end
-- dont trigger if quest already started
if axr_task_manager.ongoing_tasks['barrier_defense_monolith'] then return false end
if axr_task_manager.ongoing_tasks['barrier_defense_zombie'] then return false end
-- printl('barrier_defense_available(%s) AAA',tid)
-- dont trigger if hostile to freedom
-- should check vs default_comm in gameplay_disguise but what if disguise is disabled? it it nil? or what?
if game_relations.is_factions_enemies(character_community(db.actor),"freedom") then return false end
if not MDATA then return false end
-- printl('barrier_defense_available(%s) BBB',tid)
-- dont trigger if game started less than a week ago
if game.get_game_time():diffSec(level.get_start_time()) < 3600 * 24 * 7 and not DEBUG then return false end
-- printl('barrier_defense_available(%s) CCC',tid)
-- dont trigger if less than a week passed since last defense, either joined or skipped
local last1 = MDATA.barrier_defense_monolith.last_msg_time and ta2ti(MDATA.barrier_defense_monolith.last_msg_time)
-- printl('barrier_defense_available(%s) CCC1',tid)
local last2 = MDATA.barrier_defense_zombie.last_msg_time and ta2ti(MDATA.barrier_defense_zombie.last_msg_time)
-- printl('barrier_defense_available(%s) CCC2',tid)
local delta1 = last1 and game.get_game_time():diffSec(last1) / (3600 * 24) or 100
-- printl('barrier_defense_available(%s) CCC3',tid)
local delta2 = last2 and game.get_game_time():diffSec(last2) / (3600 * 24) or 100
-- printl('barrier_defense_available(%s) CCC4',tid)
local delta = math.min(delta1, delta2)
-- printf('delta - %s', delta)
if delta < 7 then
printl('not enough time since last mission')
return false
end
-- printl('barrier_defense_available(%s) DDD',tid)
-- dont trigger if last check was done less than a day ago to prevent changing level until triggered
-- printf('MDATA[tid].last_check_time -> %s',MDATA[tid].last_check_time)
if MDATA[tid].last_check_time then
-- printf('checking MDATA[tid].last_check_time')
local lct = ta2ti(MDATA[tid].last_check_time)
local delta = game.get_game_time():diffSec(lct) / (3600 * 24)
-- printf('delta2 - %s', delta2)
if delta < 1 then
printf('not enough time since last check')
return false
end
end
MDATA[tid].last_check_time = ti2ta(game.get_game_time())
-- printl('barrier_defense_available(%s) EEE',tid)
-- local function pret(x)
-- -- printf('chance: %s',x)
-- return x
-- end
local chance = DEBUG and -1 or ((tid == 'barrier_defense_monolith') and 0.75 or 0.5)
-- local ret = pret(math.random()) > chance
local ret = math.random() > chance
-- printf('ret %s', ret)
return ret
end
function barrier_defense_monolith_start(a,b) -- dialog
-- printf('barrier_defense_monolith_start')
local gid = a:id()> 0 and a:id() or b:id()
MDATA.barrier_defense_monolith.giver = gid
MDATA.barrier_defense_monolith.defenders = {}
MDATA.barrier_defense_monolith.attackers = {}
MDATA.barrier_defense_monolith.squads_left = MONO_SQUADS
local sq
sq = SIMBOARD:create_squad(SIMBOARD.smarts_by_names['mil_smart_terrain_7_8'],"barrier_defense_squad")
MDATA.barrier_defense_monolith.defenders[sq.id] = true
-- printf('def: %s',sq.id)
sq = SIMBOARD:create_squad(SIMBOARD.smarts_by_names['mil_smart_terrain_7_8'],"barrier_defense_squad")
MDATA.barrier_defense_monolith.defenders[sq.id] = true
-- printf('def: %s',sq.id)
-- printf('init done, start task')
MDATA.allow_surge = false
task_manager.get_task_manager():give_task('barrier_defense_monolith')
end
function barrier_defense_zombie_start(a,b) -- dialog
-- printf('barrier_defense_zombie_start')
local gid = a:id()> 0 and a:id() or b:id()
MDATA.barrier_defense_zombie.giver = gid
MDATA.barrier_defense_zombie.defenders = {}
MDATA.barrier_defense_zombie.attackers = {}
MDATA.barrier_defense_zombie.squads_left = ZOMB_SQUADS
local sq
sq = SIMBOARD:create_squad(SIMBOARD.smarts_by_names['mil_smart_terrain_7_8'],"barrier_defense_squad")
MDATA.barrier_defense_zombie.defenders[sq.id] = true
-- printf('def: %s',sq.id)
local spawn_l = 'barrier_spawn_'..math.random(1,9)
sq = SIMBOARD:create_squad_at_named_location(spawn_l,'barrier_zombie_squad')
local spawn_l = 'barrier_spawn_'..math.random(1,9)
sq = SIMBOARD:create_squad_at_named_location(spawn_l,'barrier_zombie_squad')
-- printf('init done, start task')
MDATA.allow_surge = false
task_manager.get_task_manager():give_task('barrier_defense_zombie')
end
function barrier_defense_monolith_complete(a,b)
MDATA.allow_surge = true
task_manager.get_task_manager():set_task_completed('barrier_defense_monolith')
xr_effects.barrier_defense_clean_flags(nil,nil,{'monolith'})
local r = {}
r.joint = 1
local w1, w2 = db.actor:item_in_slot(2), db.actor:item_in_slot(3)
if w1 then
local s = ini_sys:r_string_ex(w1:section(), "ammo_class")
if (s) then
local ammos = str_explode(s,",")
r[ammos[1]] = math.random(4,6)
end
end
if w2 then
local s = ini_sys:r_string_ex(w2:section(), "ammo_class")
if (s) then
local ammos = str_explode(s,",")
r[ammos[1]] = math.random(4,6)
end
end
local sim = alife()
for k,v in pairs(r) do
news_manager.relocate_item(db.actor, 'in', k, v)
for i=1,v do
alife_create_item(k, db.actor)
end
end
local amt = 40000
news_manager.relocate_money(db.actor, 'in', amt)
db.actor:give_money(amt)
end
function barrier_defense_zombie_complete(a,b)
MDATA.allow_surge = true
task_manager.get_task_manager():set_task_completed('barrier_defense_zombie')
xr_effects.barrier_defense_clean_flags(nil,nil,{'zombie'})
local r = {}
r.vodka = 1
r.drug_psy_blockade = 2
local w1, w2 = db.actor:item_in_slot(2), db.actor:item_in_slot(3)
if w1 then
local s = ini_sys:r_string_ex(w1:section(), "ammo_class")
if (s) then
local ammos = str_explode(s,",")
r[ammos[1]] = math.random(2,4)
end
end
if w2 then
local s = ini_sys:r_string_ex(w2:section(), "ammo_class")
if (s) then
local ammos = str_explode(s,",")
r[ammos[1]] = math.random(2,4)
end
end
local sim = alife()
for k,v in pairs(r) do
news_manager.relocate_item(db.actor, 'in', k, v)
for i=1,v do
alife_create_item(k, db.actor)
end
end
local amt = 25000
news_manager.relocate_money(db.actor, 'in', amt)
db.actor:give_money(amt)
end
function task_functor.barrier_defense_target_functor(task_id)
if db.actor and MDATA[task_id] and MDATA[task_id].giver and (db.actor:has_info('barrier_defense_monolith_finished') or db.actor:has_info('barrier_defense_zombie_finished')) then
return MDATA[task_id].giver
end
local dist = 999999
local sm = SIMBOARD.smarts_by_names['mil_smart_terrain_3_8']
local ret = sm.id
if MDATA[task_id] and MDATA[task_id].attackers then
for k,v in pairs(MDATA[task_id].attackers) do
if alife_object(k) then
local newd = dist2d_id_sqr(0,k)
if newd < dist then
ret = k
dist = newd
end
end
end
end
return ret
end
--============================================< Conditions >============================================--
function xr_conditions.all_attackers_dead(a,b,c)
-- printf('all_attackers_dead start')
local tid = c and c[1]
if not tid then
printf('all_attackers_dead: called without arguments')
return
end
if not MDATA[tid] then return false end
for k,v in pairs(MDATA[tid].attackers) do
local so = alife_object(k)
if so then
-- printf('all_attackers_dead end false')
return false
end
end
-- printf('all_attackers_dead end true')
return true
end
function xr_conditions.distance_to_barrier(a,b,c)
-- mil_smart_terrain_3_8
-- printf('distance_to_barrier %s, %s', c[1], c[2])
if not (c and c[1] and c[2] and tonumber(c[2])) then
printf('distance_to_barrier: missing or malformed arguments, must be ("le" or "ge":number)')
return
end
local sm = SIMBOARD.smarts_by_names['mil_smart_terrain_3_8']
local dist = dist2d_id_sqr(0,sm.id)
local d = tonumber(c[2]) ^ 2
if c[1] == 'le' then return dist <= d end
if c[1] == 'ge' then return dist >= d end
printf('distance_to_barrier: unknown comparison requested, got %s expected "ge" or "le"', c[1])
end
--============================================< Effects >============================================--
function xr_effects.barrier_defense_first_spawn(a,b,c)
-- barrier_monolith_squad
-- barrier_spawn
-- barrier_defense_monolith
-- printf('barrier_defense_monolith_first_spawn')
if not (c and c[1] and (c[1]=='zombie' or c[1]=='monolith')) then
printf('barrier_defense_first_spawn: missing or unexpected arguments, must be "zombie" or "monolith"')
return
end
local squad_s = 'barrier_'..c[1]..'_squad'
for i=1,STARTING_SQUADS do
local spawn_l = 'barrier_spawn_'..math.random(1,9)
local sq = SIMBOARD:create_squad_at_named_location(spawn_l,squad_s)
-- sq.rush_to_target = true
MDATA['barrier_defense_'..c[1]].attackers[sq.id] = true
end
-- printf('barrier_defense_monolith_first_spawn end')
end
function xr_effects.on_barrier_defense_squad_death(a,b,c)
local tid = c[1]
if c[1] == 'barrier_defense_freedom' then
sq = SIMBOARD:create_squad(SIMBOARD.smarts_by_names['mil_smart_terrain_4_7'],"barrier_defense_squad")
if sq and sq:commander_id() and alife_object(sq:commander_id()) then
local se_obj = alife_object(sq:commander_id())
local name, icon = game.translate_string(se_obj:character_name()), se_obj:character_icon()
send_tip(game.translate_string('barrier_defense_reinforcements_'..math.random(1,4)),name,nil,nil, icon, nil, 'npc')
else
printf('squad commander still doesnt exist?')
end
-- MDATA.barrier_defense_zombie.defenders[sq.id] = true
else
local squad_s = tid == "barrier_defense_monolith" and 'barrier_monolith_squad' or 'barrier_zombie_squad'
-- printf('on_death_sq triggered')
local count = MDATA[tid].squads_left
if count > 0 then
if DEBUG then printf('left %s', count) end
local spawn = 'barrier_spawn_'..math.random(1,9)
local rpg = (count < 3 and tid == "barrier_defense_monolith") and '_rpg' or ''
local sq = SIMBOARD:create_squad_at_named_location(spawn, squad_s..rpg)
MDATA[tid].attackers[sq.id] = true
if count == 2 and tid=='barrier_defense_monolith' then
mid_news('monolith')
end
if count == 5 and tid=='barrier_defense_zombie' then
mid_news('zombie')
end
MDATA[tid].squads_left = count - 1
end
end
end
function xr_effects.barrier_defense_clean_flags(a,b,c)
-- barrier_defense_
db.actor:disable_info_portion('barrier_defense_'..c[1]..'_first_spawn')
db.actor:disable_info_portion('barrier_defense_'..c[1]..'_finished')
db.actor:disable_info_portion('barrier_defense_'..c[1]..'_announced')
end
function xr_effects.barrier_defense_news(a,b,c)
if c and c[1] and c[2] and (c[2] == 'monolith' or c[2] == 'zombie') then
local x = c[1]
if x == 'start' then
send_tip(game.translate_string('barrier_defense_'..c[2]..'_start_announce'),game.translate_string("mil_freedom_leader_name"),nil,nil, 'ui_inGame2_lukash', nil, 'npc')
return
end
if x == 'end' then
MDATA.allow_surge = true -- Added by Catspaw to fix broken emissions on barrier task fail
send_tip(game.translate_string('barrier_defense_'..c[2]..'_end_announce'),game.translate_string("mil_freedom_leader_name"),nil,nil, 'ui_inGame2_lukash', nil, 'npc')
return
end
if x == 'escape' then
MDATA.allow_surge = true -- Added by Catspaw to fix broken emissions on barrier task fail
send_tip(game.translate_string('barrier_defense_'..c[2]..'_run_announce'),game.translate_string("mil_freedom_leader_name"),nil,nil, 'ui_inGame2_lukash', nil, 'npc')
return
end
printf('barrier_defense_news: first argument must be "start", "end" or "escape"')
else
printf('barrier_defense_news: missing or wrong arguments, must be ([start,end,escape]:[zombie,monolith])')
end
end