Divergent/mods/Zone Reality Remade/gamedata/scripts/surge_manager.script

1731 lines
60 KiB
Plaintext

--------------------------------------------------------------------------------
-- Surge manager class ---------------------------------------------------------
-- Made by Peacemaker ----------------------------------------------------------
-- 05.03.07 --------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Edited for AtmosFear 3 ------------------------------------------------------
-- by Cromm Cruac --------------------------------------------------------------
-- 25.06.2011 ------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Edited by Alundaio for Call of Chernobyl
--[[
Tronex
2019/9/13
Modified for Anomaly 1.5
--]]
-------------------
local alife, math_random, pairs, tostring = alife, math.random, pairs, tostring
-------------------
---------------------------------------------------------------------
-- class instance
SurgeManager = nil
function get_surge_manager()
SurgeManager = SurgeManager or CSurgeManager()
return SurgeManager
end
---------------------------------------------------------------------
local a_ini = ini_file("alife.ltx")
local normal_time_factor = a_ini:r_float_ex("alife","time_factor") or 6
local surge_time_factor = 10
local surge_shock_pp_eff = 1
local earthquake_cam_eff = 2
local sleep_cam_eff = 3
local sleep_fade_pp_eff = 4
local prev_sec = -1
local prev_game_sec = -1
local currentPPEfactor = 0.001
local _tmr
local snd_surge1 = sound_object('characters_voice\\scenario\\zaton\\zat_a2_stalker_barmen_surge_phase_1')
local snd_surge2 = sound_object('characters_voice\\scenario\\zaton\\zat_a2_stalker_barmen_surge_phase_2')
local snd_surge3 = sound_object('characters_voice\\scenario\\zaton\\zat_a2_stalker_barmen_after_surge')
local presurge_played = {0, 0, 0, 0, 0, 0}
--[[Amount of time before a surge in in-game seconds (6x real life seconds)
Note that the script updates events only once per real life second]]--
local presurge_time = 360
local function main_loop()
local tg = time_global()
if (_tmr and tg < _tmr) then
return false
end
_tmr = tg + 1000
if not (db.actor and db.actor.afterFirstUpdate) then
return false
end
get_surge_manager():update()
psi_storm_manager.get_psi_storm_manager():update()
if level.get_time_factor() ~= normal_time_factor and not is_started() and not psi_storm_manager.is_started() then
if is_finished() and psi_storm_manager.is_finished() then
level.set_time_factor(normal_time_factor)
end
end
return false
end
local function on_level_changing()
local tm = task_manager.get_task_manager()
local task_info = tm.task_info["faction_base_defense"]
if (task_info and task_info.status ~= "completed" and task_info.status ~= "fail") then
stop_surge()
tm:set_task_cancelled("faction_base_defense")
end
end
local function actor_on_sleep(hours)
get_surge_manager().time_forwarded = true
psi_storm_manager.get_psi_storm_manager().time_forwarded = true
if(is_started() and level_weathers.get_weather_manager().weather_fx) then
level.stop_weather_fx()
end
level_weathers.get_weather_manager():forced_weather_change()
end
function on_game_start()
AddUniqueCall(main_loop)
RegisterScriptCallback("on_level_changing",on_level_changing)
RegisterScriptCallback("actor_on_sleep",actor_on_sleep)
end
local enable_debug = false
function print_dbg(fmt,...)
if enable_debug then
printf("Surge manager | " .. fmt,...)
end
end
local function file_exists(filename)
local file = io.open(filename, "r")
if (file) then
file:close()
return true
end
return false
end
-- ###############################################################################################
-- SURGE MANAGER
-- ###############################################################################################
class "CSurgeManager"
function CSurgeManager:__init()
self.ini = ini_file("misc\\surge_manager.ltx")
self.blowout_sounds = {
begin01 =sound_object("ambient\\blowout\\blowout_begin"),
begin02 =sound_object("ambient\\blowout\\blowout_begin_02"),
impact01 =sound_object("ambient\\blowout\\blowout_impact"),
impact02 =sound_object("ambient\\blowout\\blowout_impact_02"),
wave01 =sound_object("ambient\\blowout\\blowout_wave_01"),
wave02 =sound_object("ambient\\blowout\\blowout_wave_02_short"),
wave03 =sound_object("ambient\\blowout\\blowout_wave_03_short"),
wave04 =sound_object("ambient\\blowout\\blowout_wave_04"),
body_tear =sound_object("anomaly\\anomaly_body_tear_1"),
siren =sound_object("ambient\\blowout\\blowout_siren"),
crow0 =sound_object("ambient\\blowout\\cut\\close"),
crow1 =sound_object("ambient\\blowout\\cut\\closemedium"),
crow2 =sound_object("ambient\\blowout\\cut\\medium"),
crow3 =sound_object("ambient\\blowout\\cut\\prettyfar"),
crow4 =sound_object("ambient\\blowout\\cut\\far"),
crow5 =sound_object("ambient\\blowout\\cut\\veryfar")
}
self.blowout_waves = {}
self.body_tears = {}
end
function CSurgeManager:initialize()
--[[
self.levels_respawn = empty_table(self.levels_respawn)
for level,v in pairs(level_weathers.valid_levels) do
self.levels_respawn[level] = false
end
--]]
--utils_data.debug_write("CSurgeManager:initialize() start")
self.aspectRatio = 1
if(device().width/device().height>(1024/768+0.01)) then
self.aspectRatio = 0.8
end
if not (self._state_loaded) then -- because state_load called too early, we wait till actor_on_load to truly init and we do not want to overwrite loaded values
self.last_surge_time = game.get_game_time()
self.inited_time = game.get_game_time()
self.started = false
self.finished = true
self.game_time_factor=level.get_time_factor()
self.task_given = nil
end
if not (self._delta) then
local freq = ui_options.get("alife/event/emission_frequency")
self._delta = math_random(math.floor(freq/2)*3600, freq*3600)
end
self.hitFactor = 0
self.surge_time = 222
self:init_surge_covers()
local ini = self.ini
self.condlist = ini:r_string_to_condlist("settings","condlist")
self.survive = ini:r_string_to_condlist("settings","survive")
self.surge_message = ""
self.surge_task_sect = ""
--printf("level=%s artefact_respawn_levels=%s",level.name(),m_data.artefact_respawn_levels[level.name()])
--utils_data.debug_write("CSurgeManager:initialize() end")
end
function CSurgeManager:start(manual)
-- manual is used to set inited_time to current in-game date
-- sets inited_time for the surge + prepare values
-- skip surge in special cases
--utils_data.debug_write("CSurgeManager:start()")
if not (self._state_loaded) then
self:initialize()
end
self.game_time_factor = level.get_time_factor()
local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0
Y, M, D, h, m, s, ms = self.last_surge_time:get(Y, M, D, h, m, s, ms)
if (manual or not self.inited_time) then
self.inited_time = game.get_game_time()
else
self.inited_time:set(Y, M, D, h, m, s + self._delta, ms)
end
local flags = {allow = true}
SendScriptCallback("on_before_surge",flags)
local level_name = level.name()
if (level_weathers.valid_levels[level_name] ~= true or (not ui_options.get("alife/event/emission_state")) or flags.allow == false) then
self:skip_surge()
return
end
-- Generators is emission source and Deserted Hospital has nowhere to hide (almost all the map is inside anyway).
if (level_name == "l13_generators" or level_name == "l11_hospital" or has_alife_info("bar_arena_fight")) then
self.skip_message = true
self:skip_surge()
return
end
local diff_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/level.get_time_factor())
if (diff_sec+6 > self.surge_time)then
self.skip_message = true
self:skip_surge()
else
-- heli_combat flag
save_var(db.actor,"heli_enemy_flag",nil)
self.started = true
self.finished = false
self.task_given = nil
self:finalize()
self.blowout_waves = empty_table(self.blowout_waves)
self.objects_to_kill = empty_table(self.objects_to_kill)
self.stages = empty_table(self.stages)
self.body_tears = empty_table(self.body_tears)
self.hitFactor=0
level.set_time_factor(surge_time_factor)
self.zombie_count = 0
SendScriptCallback("actor_on_interaction", "anomalies", nil, "emission_start")
end
end
function CSurgeManager:new_surge_time(reset)
if reset then
self.last_surge_time = game.get_game_time()
end
--utils_data.debug_write("CSurgeManager:new_surge_time()")
local freq = ui_options.get("alife/event/emission_frequency")
self._delta = math_random(math.floor(freq/2)*3600, freq*3600)
local g_time = game.get_game_time()
local psi_manager = psi_storm_manager and psi_storm_manager.get_psi_storm_manager()
local last_psi_storm_time = psi_manager and psi_manager.last_psi_storm_time or game.get_game_time()
local psi_storm_start = psi_manager and math.floor(psi_manager._delta - g_time:diffSec(last_psi_storm_time)) or 0
local psi_storm_end = psi_manager and math.floor(psi_manager._delta+3600 - g_time:diffSec(last_psi_storm_time)) or 0
local surge_start = math.floor(self._delta - g_time:diffSec(self.last_surge_time))
local surge_end = math.floor(self._delta+3600 - g_time:diffSec(self.last_surge_time))
if ((surge_end > psi_storm_start) and (surge_end < psi_storm_end)) then
--1h earlier
self._delta=self._delta-3600
end
if ((surge_start > psi_storm_start) and (surge_start < psi_storm_end)) then
--1h later
self._delta=self._delta+3600
end
end
function CSurgeManager:skip_surge()
if not (self.inited_time) then
self.inited_time = game.get_game_time()
end
if not (self.last_surge_time) then
self.last_surge_time = game.get_game_time()
end
local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0
Y, M, D, h, m, s, ms = self.inited_time:get(Y, M, D, h, m, s, ms)
self.last_surge_time:set(Y, M, D, h, m, s + self.surge_time, ms)
self:new_surge_time(false)
self.started = false
self.finished = true
--[[
self.levels_respawn = empty_table(self.levels_respawn)
for level,v in pairs(level_weathers.valid_levels) do
self.levels_respawn[level] = true
end
--]]
self.surge_message = ""
self.surge_task_sect = ""
self.task_given = false
self.effector_set = false
self.second_message_given = false
self.ui_disabled = false
self.blowout_sound = false
self.wave_sound = false
prev_sec = 0
SendScriptCallback("actor_on_interaction", "anomalies", nil, "emission_end")
if (self.skip_message and ui_options.get("alife/event/emission_state")) then
news_manager.send_tip(db.actor, "st_surge_while_asleep", nil, "recent_surge", nil, nil)
SendScriptCallback("actor_on_interaction", "anomalies", nil, "emissions")
game_statistics.increment_statistic("emissions")
self.skip_message = nil
--release_body_manager.get_release_body_manager():clear(true)
end
-- hide indicators
self:displayIndicators(0)
level.set_time_factor(self.game_time_factor)
end
function CSurgeManager:end_surge(manual)
if (self.started == true) then
game_statistics.increment_statistic("emissions")
self:kill_all_unhided()
SendScriptCallback("actor_on_interaction", "anomalies", nil, "emissions")
SendScriptCallback("actor_on_interaction", "anomalies", nil, "emission_end")
end
self.last_surge_time = game.get_game_time()
self.started = false
self.finished = true
self:new_surge_time(false)
self.surge_message = ""
self.surge_task_sect = ""
self.task_given = nil
presurge_played = {0, 0, 0, 0, 0}
if(self.blowout_sound) then
xr_sound.stop_sound_looped(AC_ID, "blowout_rumble")
end
if(self.wave_sound) then
xr_sound.stop_sound_looped(AC_ID, "blowout_particle_wave_looped")
end
for k,wave in pairs(self.blowout_waves) do
if wave.effect:playing() then
self:kill_wave(k)
end
end
for k,snd in pairs(self.blowout_sounds) do
if snd ~= nil and snd:playing() then
snd:stop()
end
end
if(self.second_message_given) then
xr_sound.stop_sound_looped(AC_ID, "surge_earthquake_sound_looped")
end
if(manual or (self.time_forwarded and level_weathers.get_weather_manager().weather_fx)) then
level.stop_weather_fx()
-- level_weathers.get_weather_manager():select_weather(true)
level_weathers.get_weather_manager():forced_weather_change()
end
self.effector_set = false
self.second_message_given = false
self.ui_disabled = false
self.blowout_sound = false
self.wave_sound = false
prev_sec = 0
self.hitFactor=0
level.remove_pp_effector(surge_shock_pp_eff)
level.remove_cam_effector(earthquake_cam_eff)
-- hide indicators
self:displayIndicators(0)
level.set_time_factor(self.game_time_factor)
for k,v in pairs(db.signal_light) do
v:stop_light()
v:stop()
end
end
function CSurgeManager:pos_in_cover(pos,by_name)
if not (self.covers) then
return
end
local sr
for name,condlist in pairs(self.covers) do
--if (db.actor_inside_zones[name]) then
--return true
--end
sr = db.zone_by_name[name]
if (sr and sr:inside(pos)) then
if by_name then
return name
end
return true
end
end
return false
end
function CSurgeManager:init_surge_covers() -- collect safe zones list, if not done yet
if (self.covers) then
return
end
self.covers = {}
local ini = self.ini
for i = 0, ini:line_count("list")-1 do
temp1, id, temp2 = ini:r_line("list", i, "", "")
self.covers[id] = ini:r_string_to_condlist(id,"condlist") or xr_logic.parse_condlist(nil,"surge_manager","covers","true")
end
end
function CSurgeManager:hit_power(power,hit_type)
local hit_types={"burn", "shock", "chemical_burn", "radiation", "telepatic"}
local suit_factors={10,1,5,33,10}
local drug_factors={1,1,5,33,10}
local artefact_factors={10,5,10,10,10}
local suit_protection = db.actor:get_current_outfit_protection(hit_type)*suit_factors[hit_type+1]
local helmet_protection = 0
local helmet = db.actor:item_in_slot(12)
if helmet ~= nil then
helmet_protection = ini_sys:r_float_ex(helmet:section(),hit_types[hit_type+1].."_protection")*suit_factors[hit_type+1]
end
local g_time
-- DRUG PROTECTIONS CHECK
local expiration = load_var(db.actor,"drug_psy_blockade_expiration")
if (expiration) then
g_time = utils_data.CTimeToSec(game.get_game_time())
if g_time<expiration then
self.drug_telepatic_protection = ini_sys:r_float_ex("drug_psy_blockade","boost_telepat_protection")
else
self.drug_telepatic_protection = nil
end
end
expiration = load_var(db.actor,"drug_radioprotector_expiration")
if (expiration) then
g_time = g_time or utils_data.CTimeToSec(game.get_game_time())
if g_time<expiration then
self.drug_radiation_protection = ini_sys:r_float_ex("drug_radioprotector","boost_radiation_protection")
else
self.drug_radiation_protection = nil
end
end
expiration = load_var(db.actor,"drug_antidot_expiration")
if (expiration) then
g_time = g_time or utils_data.CTimeToSec(game.get_game_time())
if g_time<expiration then
self.drug_chemical_burn_protection = ini_sys:r_float_ex("drug_antidot","boost_chemburn_protection")
else
self.drug_chemical_burn_protection = nil
end
end
local drug_protection = 0
if hit_type == 2 and self.drug_chemical_burn_protection ~= nil then
drug_protection=self.drug_chemical_burn_protection*drug_factors[hit_type+1]
elseif hit_type == 3 and self.drug_radiation_protection ~= nil then
drug_protection=self.drug_radiation_protection*drug_factors[hit_type+1]
elseif hit_type == 4 and self.drug_telepatic_protection ~= nil then
drug_protection=self.drug_telepatic_protection*drug_factors[hit_type+1]
end
protection = (suit_protection + helmet_protection + drug_protection)
if protection > 1 then
protection = 1
end
hit_power=power*(1-protection)
return hit_power
end
function CSurgeManager:start_wave(num,inited_time)
if inited_time==nil then
inited_time=prev_game_sec
end
local actor_pos=db.actor:position()
local effect=particles_object("crommcruac\\blowout_wave_blend")
local inited_pos= vector():set(actor_pos.x, actor_pos.y/2, actor_pos.z+250)
self.blowout_waves[num]={effect=effect,inited_time=inited_time,inited_pos=inited_pos}
self.blowout_waves[num].effect:play_at_pos(inited_pos)
if not(self.wave_sound) then
xr_sound.play_sound_looped(AC_ID, "blowout_particle_wave_looped")
xr_sound.set_volume_sound_looped(AC_ID, "blowout_particle_wave_looped", 0)
self.wave_sound=true
end
end
function CSurgeManager:finalize()
for k,v in pairs(self.blowout_waves) do
if (v.effect and v.effect:playing()) then
v.effect:stop()
end
end
for k,v in pairs(self.body_tears) do
if (v:playing()) then
v:stop()
end
end
end
function CSurgeManager:kill_wave(num)
if self.blowout_waves[num] then
self.blowout_waves[num].effect:stop_deffered()
self.blowout_waves[num]=nil
end
end
function fade(currentTime,startTime,endTime,startValue,endValue)
local totalFadeTime=endTime-startTime
local totalFadeValue=endValue-startValue
local elapsedFadeTime=currentTime-startTime
local currentValue=(totalFadeValue*elapsedFadeTime)/totalFadeTime+startValue
if (totalFadeValue<0) then
if (currentValue>startValue) then
currentValue=startValue
end
if (currentValue<endValue) then
currentValue=endValue
end
else
if (currentValue>endValue) then
currentValue=endValue
end
if (currentValue<startValue) then
currentValue=startValue
end
end
return currentValue
end
function play_sound_radius(sndobj, radius, min, max)
local rnd_pos = db.actor:position()
local rnd_angle = math.random(min, max)
rnd_pos.x = rnd_pos.x + (math.cos(rnd_angle) * radius)
rnd_pos.z = rnd_pos.z + (math.sin(rnd_angle) * radius)
if(sndobj:playing()) then
sndobj:stop()
end
sndobj:play_at_pos(db.actor, rnd_pos)
printf("played sound at x=%s z=%s", rnd_pos.x, rnd_pos.z)
end
----------------------------------
-- Update
----------------------------------
function CSurgeManager:update()
--utils_data.debug_write("CSurgeManager:update()")
if not (self._state_loaded) then
print_dbg('self._state_loaded is not loaded -> initialize')
self:initialize()
return
end
if not (self.first_update) then
print_dbg('first update')
self.first_update = true
local flags = {allow = true}
SendScriptCallback("on_before_surge",flags)
-- end surge if level is not valid
local level_name = level.name()
if (level_name == "l13_generators"
or level_name == "l11_hospital"
or has_alife_info("bar_arena_fight")
or flags.allow == false
or level_weathers.valid_levels[level_name] ~= true)
then
print_dbg('level is not valid')
if (self.started) then
self:end_surge()
-- else
-- self:skip_surge()
end
return
end
local diff_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/level.get_time_factor())
print_dbg('diff_sec 1 = %s',diff_sec)
if (diff_sec > self.surge_time)then
print_dbg('diff_sec > self.surge_time')
self:skip_surge()
return
end
end
SetEvent("surge", "state", self.started)
if (not self.started) then
--print_dbg('not started')
local g_time = game.get_game_time()
if (self.time_forwarded) then
local diff = math.abs(self._delta - g_time:diffSec(self.last_surge_time))
if (diff < 3600) then
self._delta = 3*3600 + g_time:diffSec(self.last_surge_time)
end
self.time_forwarded = false
print_dbg('time forwarded')
end
--Plays sound before a blowout
if(math.abs(self._delta - g_time:diffSec(self.last_surge_time)) < presurge_time) then
if(presurge_played[1] ~= true) then
--self.blowout_sounds["presurge"]:play(db.actor)
play_sound_radius(self.blowout_sounds["crow5"], 1000, 140, 220)
play_sound_radius(self.blowout_sounds["crow4"], 1000, 140, 220)
printf("Played far crows")
presurge_played[1] = true
end
if(math.abs(self._delta - g_time:diffSec(self.last_surge_time)) < (presurge_time - 60) and presurge_played[2] ~= true) then
--self.blowout_sounds["presurge"]:play(db.actor)
play_sound_radius(self.blowout_sounds["crow3"], 1000, 100, 260)
play_sound_radius(self.blowout_sounds["crow2"], 1000, 100, 260)
printf("Played medium crows")
presurge_played[2] = true
end
if(math.abs(self._delta - g_time:diffSec(self.last_surge_time)) < (presurge_time - 120) and presurge_played[3] ~= true) then
--self.blowout_sounds["presurge"]:play(db.actor)
play_sound_radius(self.blowout_sounds["crow1"], 1000, 0, 360)
play_sound_radius(self.blowout_sounds["crow0"], 1000, 0, 360)
printf("Played close crows")
presurge_played[3] = true
end
if(math.abs(self._delta - g_time:diffSec(self.last_surge_time)) < (presurge_time - 180) and presurge_played[4] ~= true) then
--self.blowout_sounds["presurge"]:play(db.actor)
play_sound_radius(self.blowout_sounds["crow2"], 1000, 280, 440)
play_sound_radius(self.blowout_sounds["crow3"], 1000, 280, 440)
printf("Played medium crows")
presurge_played[4] = true
end
if(math.abs(self._delta - g_time:diffSec(self.last_surge_time)) < (presurge_time - 240) and presurge_played[5] ~= true) then
--self.blowout_sounds["presurge"]:play(db.actor)
play_sound_radius(self.blowout_sounds["crow4"], 1000, 320, 400)
play_sound_radius(self.blowout_sounds["crow5"], 1000, 320, 400)
printf("Played far crows")
presurge_played[5] = true
end
end
--DEBUG
--printf("until blowout: %s", math.abs(self._delta - g_time:diffSec(self.last_surge_time)))
-- return if next surge time hasn't been reached yet
if (g_time:diffSec(self.last_surge_time) < self._delta) then
--print_dbg('next surge time hasnt been reached yet')
return
end
if (self.condlist and xr_logic.pick_section_from_condlist(db.actor, nil, self.condlist) == "false") then
return
end
print_dbg('start')
self:start()
return
end
local diff_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/level.get_time_factor())
if (diff_sec >= self.surge_time) then
print_dbg('diff_sec 2 = %s', diff_sec)
if (load_var(db.actor,"surge_immuned",false) ~= true) then -- special surged immuned for tasks
self:end_surge()
return
else
print_dbg('start surge')
self:start(true)
end
end
local diff_game_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/2)
print_dbg('diff_game_sec = %s', diff_game_sec)
------------------------------------update here--------------------------------
--printf("diff_sec = %s",diff_sec)
if (prev_sec ~= diff_sec) then
prev_sec = diff_sec
SetEvent("surge", "time", diff_sec)
if (not ui_options.get("alife/event/emission_state")) then
--printf("Surge stopped because of level!")
print_dbg('stop surge because option')
self:end_surge()
return
end
local rnd_sound= math.random(1,4)
-- blowout begins -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=0 and self.stages['beginning'] ~= true) then
print_dbg('diff_sec>=%s | blowout begins',diff_sec)
if (level.get_time_hours() >= 5 and level.get_time_hours() <= 20) then
level.set_weather_fx("fx_blowout_day")
else
level.set_weather_fx("fx_blowout_night")
end
if (rnd_sound % 2) ~= 0 then
self:play_blowout_sound("begin01")
else
self:play_blowout_sound("begin02")
end
--xr_sound.set_sound_play(AC_ID, "blowout_begin")
self.stages['beginning']=true
--self.debugMessages[2]:SetText("blowout launched at: "..tostring(diff_sec).." rnd: "..tostring(rnd_sound))
end
-- siren warning -----------------------------------------------------------------------------------------------------------------------------------------
local warn = ui_options.get("alife/event/emission_warning")
if (diff_sec>=25) and (self.stages['siren'] ~= true) and (warn == "siren" or warn == "siren_radio" or warn == "radio_siren") then
print_dbg('diff_sec>=%s | siren warning',diff_sec)
self:play_siren_sound()
self.stages['siren']=true
end
-- blowout warning -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=30) then
print_dbg('diff_sec>=%s | blowout warning',diff_sec)
if (self.task_given ~= true) then
if (warn == "siren" or warn == "siren_radio" or warn == "radio_siren") then
if (level.name() == "zaton") then
xr_sound.set_sound_play(AC_ID, "zat_a2_stalker_barmen_surge_phase_1")
elseif (level.name() == "jupiter") then
xr_sound.set_sound_play(AC_ID, "jup_a6_stalker_medik_phase_1")
elseif (level.name() == "l03_agroprom") then
xr_sound.set_sound_play(AC_ID, "kovalsky_surge_phase_1")
elseif (level.name() ~= nil) then
snd_surge1:play(db.actor,0,sound_object.s2d)
end
end
self:launch_rockets()
if (load_var(db.actor,"surge_immuned",false) ~= true) then -- special surged immuned for tasks
printf("opt_blowout_task",ui_options.get("alife/event/emission_task"))
if ui_options.get("alife/event/emission_task") then
self:give_surge_hide_task()
end
end
self.task_given = true
--self.debugMessages[2]:SetText("task given at: "..tostring(diff_sec))
end
end
-- blowout impact -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=47) and (self.stages['impact'] ~= true) then
print_dbg('diff_sec>=%s | blowout impact',diff_sec)
if (rnd_sound % 2) ~= 0 then
--xr_sound.set_sound_play(AC_ID, "blowout_hit_1")
self:play_blowout_sound("impact01")
else
self:play_blowout_sound("impact02")
--xr_sound.set_sound_play(AC_ID, "blowout_hit_2")
end
self.stages['impact']=true
--self.debugMessages[2]:SetText("impact launched at: "..tostring(diff_sec).." rnd: "..tostring(rnd_sound))
end
-- start rumble -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=50) and (self.stages['rumble']==nil) and not(self.blowout_sound) then
print_dbg('diff_sec>=%s | blowout rumble',diff_sec)
xr_sound.play_sound_looped(AC_ID, "blowout_rumble")
xr_sound.set_volume_sound_looped(AC_ID, "blowout_rumble", 0.25)
self.stages['rumble']=true
self.blowout_sound = true
--self.debugMessages[2]:SetText("rumble launched at: "..tostring(diff_sec))
end
-- rumble sound fade in -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=50) and (diff_sec<=75) and (self.stages['rumble']~=nil) and (self.blowout_sound) then
print_dbg('diff_sec>=%s | blowout rumble sound fade in',diff_sec)
xr_sound.set_volume_sound_looped(AC_ID, "blowout_rumble", fade(diff_game_sec,50*5,75*5,0.25,1))
--self.debugMessages[3]:SetText("rumble vol: "..tostring(fade(diff_game_sec,50*5,75*5,0.25,1)))
end
-- 1st earthquake -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=80) and (self.stages['quake20']==nil) then
print_dbg('diff_sec>=%s | 1st earthquake',diff_sec)
--self.debugMessages[2]:SetText("eathquake started at: "..tostring(diff_sec))
level.add_cam_effector("camera_effects\\earthquake_20.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake20']=true
--self.debugMessages[2]:SetText("quake20 at: "..tostring(diff_sec))
end
-- second message -----------------------------------------------------------------------------------------------------------------------------------------
if(diff_sec>=90) and not(self.second_message_given) then
print_dbg('diff_sec>=%s | second message',diff_sec)
if (warn == "siren" or warn =="siren_radio" or warn =="radio_siren") then
if (level.name() == "zaton") then
xr_sound.set_sound_play(AC_ID, "zat_a2_stalker_barmen_surge_phase_2")
elseif(level.name() == "jupiter") then
xr_sound.set_sound_play(AC_ID, "jup_a6_stalker_medik_phase_2")
elseif (level.name() == "l03_agroprom") then
xr_sound.set_sound_play(AC_ID, "kovalsky_surge_phase_2")
elseif (level.name() ~= nil) then
snd_surge2:play(db.actor,0,sound_object.s2d)
end
end
self.second_message_given = true
--self.debugMessages[2]:SetText("second message given at: "..tostring(diff_sec))
end
-- earthquakes -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=100) and (self.stages['quake40']==nil) then
print_dbg('diff_sec>=%s | earthquakes quake40',diff_sec)
xr_sound.play_sound_looped(AC_ID, "surge_earthquake_sound_looped")
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_40.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake40']=true
--self.debugMessages[2]:SetText("quake40 at: "..tostring(diff_sec))
end
if (diff_sec>=102) and (self.stages['quake60']==nil) then
print_dbg('diff_sec>=%s | earthquakes quake60',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_60.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake60']=true
--self.debugMessages[2]:SetText("quake60 at: "..tostring(diff_sec))
end
if (diff_sec>=104) and (self.stages['quake80']==nil) then
print_dbg('diff_sec>=%s | earthquakes quake80',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_80.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake80']=true
--self.debugMessages[2]:SetText("quake80 at: "..tostring(diff_sec))
end
if (diff_sec>=106) and (self.stages['quake100']==nil) then
print_dbg('diff_sec>=%s | earthquakes quake100',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake100']=true
--self.debugMessages[2]:SetText("quake100 at: "..tostring(diff_sec))
end
if (load_var(db.actor,"surge_immuned",false) ~= true) then -- special surged immuned for tasks
-- 1st wave sound -----------------------------------------------------------------------------------------------------------------------------------------
if(diff_sec>=108) and (self.stages['1stwavesnd']==nil) then
print_dbg('diff_sec>=%s | 1st wave sound',diff_sec)
--xr_sound.set_sound_play(AC_ID, "blowout_wave_1")
if rnd_sound==1 then
self:play_blowout_sound("wave01")
elseif rnd_sound==2 then
self:play_blowout_sound("wave02")
elseif rnd_sound==3 then
self:play_blowout_sound("wave03")
elseif rnd_sound==4 then
self:play_blowout_sound("wave04")
end
self.stages['1stwavesnd']=true
--self.debugMessages[2]:SetText("1st wave snd at: "..tostring(diff_sec).." rnd: "..tostring(rnd_sound))
end
-- 1st wave -----------------------------------------------------------------------------------------------------------------------------------------
if(diff_sec>=120) and (self.stages['1stwave']==nil) then
print_dbg('diff_sec>=%s | 1st wave',diff_sec)
self:start_wave(1,diff_game_sec)
self.stages['1stwave']=true
--self.debugMessages[2]:SetText("1st wave at: "..tostring(diff_sec))
end
-- 2nd wave sound -----------------------------------------------------------------------------------------------------------------------------------------
if(diff_sec>=156) and (self.stages['2ndwavesnd']==nil) then
print_dbg('diff_sec>=%s | 2nd wave sound',diff_sec)
--xr_sound.set_sound_play(AC_ID, "blowout_wave_1")
if rnd_sound==1 then
self:play_blowout_sound("wave01")
elseif rnd_sound==2 then
self:play_blowout_sound("wave02")
elseif rnd_sound==3 then
self:play_blowout_sound("wave03")
elseif rnd_sound==4 then
self:play_blowout_sound("wave04")
end
self.stages['2ndwavesnd']=true
--self.debugMessages[2]:SetText("2nd wave snd at: "..tostring(diff_sec))
end
-- 2nd wave -----------------------------------------------------------------------------------------------------------------------------------------
--if(diff_sec>=2) and (self.stages['2ndwave']==nil) then
if(diff_sec>=168) and (self.stages['2ndwave']==nil) then
print_dbg('diff_sec>=%s | 2nd wave',diff_sec)
self:start_wave(2,diff_game_sec)
self.stages['2ndwave']=true
--self.debugMessages[2]:SetText("2nd wave at: "..tostring(diff_sec))
end
end
-- earthquakes fade-----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=200) and (self.stages['quake100']==true) then
print_dbg('diff_sec>=%s | earthquakes fade quake100',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_80.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake100']=false
--self.debugMessages[2]:SetText("quake80 at: "..tostring(diff_sec))
end
if (diff_sec>=202) and (self.stages['quake80']==true) then
print_dbg('diff_sec>=%s | earthquakes fade quake80',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_60.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake80']=false
--self.debugMessages[2]:SetText("quake60 at: "..tostring(diff_sec))
end
if (diff_sec>=206) and (self.stages['quake60']==true) then
print_dbg('diff_sec>=%s | earthquakes fade quake60',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_40.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake60']=false
--self.debugMessages[2]:SetText("quake40 at: "..tostring(diff_sec))
end
if (diff_sec>=209) and (self.stages['quake40']==true) then
print_dbg('diff_sec>=%s | earthquakes fade quake40',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
level.add_cam_effector("camera_effects\\earthquake_20.anm", earthquake_cam_eff, true, "", 0, false)
self.stages['quake40']=false
--self.debugMessages[2]:SetText("quake20 at: "..tostring(diff_sec))
end
if (diff_sec>=214) and (self.stages['quake20']==true) and (self.stages['quakeended']==nil) then
print_dbg('diff_sec>=%s | earthquakes fade quake20',diff_sec)
level.remove_cam_effector(earthquake_cam_eff)
self.stages['quakeended']=true
--self.debugMessages[2]:SetText("eathquake ended at: "..tostring(diff_sec))
end
-- rumble and quake sound fade out -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=200) and (diff_sec<=214) and (self.stages['rumble']~=nil) and (self.blowout_sound) then
print_dbg('diff_sec>=%s | rumble and quake sound fade out',diff_sec)
xr_sound.set_volume_sound_looped(AC_ID, "blowout_rumble", fade(diff_game_sec,200*5,214*5,1,0))
xr_sound.set_volume_sound_looped(AC_ID, "surge_earthquake_sound_looped", fade(diff_game_sec,200*5,214*5,1,0))
--self.debugMessages[3]:SetText("rumble vol: "..tostring(fade(diff_game_sec,200*5,214*5,1,0)))
end
-- rumble and quake sound stop -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=214) and (self.stages['rumble']==true) and (self.blowout_sound) then
print_dbg('diff_sec>=%s | rumble and quake sound stop',diff_sec)
xr_sound.stop_sound_looped(AC_ID, "blowout_rumble")
xr_sound.stop_sound_looped(AC_ID, "surge_earthquake_sound_looped")
--self.debugMessages[2]:SetText("sounds stopped at: "..tostring(diff_sec))
self.stages['rumble']=false
end
-- end message -----------------------------------------------------------------------------------------------------------------------------------------
if (diff_sec>=212) and (self.stages['endmessage']==nil) then
print_dbg('diff_sec>=%s | end message',diff_sec)
if (level) and (warn == "siren" or warn =="siren_radio" or warn =="radio_siren") then
if(level.name()=="zaton") then
xr_sound.set_sound_play(AC_ID, "zat_a2_stalker_barmen_after_surge")
elseif(level.name()=="jupiter") then
xr_sound.set_sound_play(AC_ID, "jup_a6_stalker_medik_after_surge")
elseif (level.name() == "l03_agroprom") then
xr_sound.set_sound_play(AC_ID, "kovalsky_after_surge")
elseif (level.name() ~= nil) then
snd_surge3:play(db.actor,0,sound_object.s2d)
end
end
--self.debugMessages[2]:SetText("end message at: "..tostring(diff_sec))
self.stages['endmessage']=true
end
-- after sound -----------------------------------------------------------------------------------------------------------------------------------------
if(diff_sec>=220) and (self.stages['endsnd']==nil) then
print_dbg('diff_sec>=%s | after sound',diff_sec)
xr_sound.set_sound_play(AC_ID, "blowout_hit_3")
self.stages['endsnd']=true
--self.debugMessages[2]:SetText("ending snd at: "..tostring(diff_sec))
end
end
--------------------------------- update every 2 game sec -----------------------------------
if (prev_game_sec ~= diff_game_sec) then
prev_game_sec = diff_game_sec
if (diff_sec < self.surge_time) then
print_dbg('diff_sec>=%s | effectors',diff_sec)
local actor_pos = db.actor:position()
-- hitFactor changes -----------------------------------------------------------------------------------------------------------------------------------------
--self.debugMessages[3]:SetText("hitFactor: "..tostring(self.hitFactor))
--self.debugMessages[4]:SetText("psy_health="..tostring(db.actor.psy_health))
--if (diff_sec>=8) and (diff_sec<210) and not(self.effector_set) then
if (diff_sec>=108) and (diff_sec<210) and not(self.effector_set) then
level.add_pp_effector("psychic.ppe", surge_shock_pp_eff, true)
level.set_pp_effector_factor(surge_shock_pp_eff, 0.001)
self.effector_set=true
end
--if (diff_sec>=8) and (diff_sec<=14) and (self.effector_set) then
if (diff_sec>=108) and (diff_sec<=114) and (self.effector_set) then
self.hitFactor=fade(diff_game_sec,8*5,14*5,0.001,1)
end
--if (diff_sec>=14) and (diff_sec<=20) and (self.effector_set) then
if (diff_sec>=114) and (diff_sec<=120) and (self.effector_set) then
self.hitFactor=fade(diff_game_sec,14*5,20*5,1,0.3)
end
if (diff_sec>=156) and (diff_sec<=162) and (self.effector_set) then
self.hitFactor=fade(diff_game_sec,156*5,162*5,0.3,1)
end
if (diff_sec>=162) and (diff_sec<=168) and (self.effector_set) then
self.hitFactor=fade(diff_game_sec,162*5,168*5,1,0.3)
end
if (diff_sec>=200) and (diff_sec<=210) and (self.effector_set) then
self.hitFactor=fade(diff_game_sec,200*5,210*5,0.3,0.001)
end
if (diff_sec>=210) and (self.effector_set) then
self.hitFactor=0
level.remove_pp_effector(surge_shock_pp_eff)
self.effector_set=false
end
-- setting effector
local PPEfactor
local hitPower
if not (GetEvent("current_safe_cover")) then
PPEfactor = self.hitFactor
hitPower = self.hitFactor/50
if PPEfactor < 0.001 then PPEfactor = 0.001 end
--self.debugMessages[6]:SetText("outside")
else
PPEfactor = 0.002
hitPower = 0
--self.debugMessages[6]:SetText("in cover")
end
currentPPEfactor = currentPPEfactor+(PPEfactor - currentPPEfactor) * 0.1
if (load_var(db.actor,"surge_immuned",false) ~= true) then
if (self.effector_set) then
level.set_pp_effector_factor(surge_shock_pp_eff, currentPPEfactor)
self:displayIndicators(currentPPEfactor)
if (db.actor:alive() and character_community(db.actor) ~= "actor_monolith") then
local h = hit()
h.type = hit.telepatic
h.power = self:hit_power(hitPower, h.type)
h.impulse = 0.0
h.direction = VEC_Z
h.draftsman = db.actor
if (self.survive and xr_logic.pick_section_from_condlist(db.actor, nil, self.survive) == "true") then
if(db.actor.health<=h.power) then
if (db.actor.health-0.05 > 0.05) then
h.power = 0.05
else
h.power = 0
end
end
end
print_dbg('diff_sec>=%s | hit actor',diff_sec)
db.actor:hit(h)
end
end
end
--self.debugMessages[5]:SetText("PPE: "..tostring(currentPPEfactor))
--self.debugMessages[7]:SetText("hitPower: "..tostring(hitPower))
-- Waves
local wavevol=0
local fate = ui_options.get("alife/event/emission_fate") or "kill_at_wave"
for k,wave in pairs(self.blowout_waves) do
if wave.effect:playing() then
local wave_pos= vector():set(actor_pos.x, actor_pos.y/2, wave.inited_pos.z-(diff_game_sec-wave.inited_time)*3)
wave.effect:move_to(wave_pos,VEC_ZERO)
local wavevoltemp = (250 - math.abs(actor_pos.z-wave_pos.z))/250
if (wavevoltemp > wavevol) then
wavevol = wavevoltemp
end
if wave_pos.z < actor_pos.z - 250 then
wave.effect:stop_deffered()
if (k==2) then
--kill all remaining npcs
if fate == "turn_to_zombie" or fate == "explode" then
print_dbg('diff_sec>=%s | turn_to_zombie',diff_sec)
self:kill_objects_at_pos(-10000,fate)
end
end
end
self:kill_crows_at_pos(wave_pos.z)
if fate == "kill_at_wave" then
if (prev_sec == prev_game_sec/5) then
print_dbg('diff_sec>=%s | kill_objects_at_pos',diff_sec)
self:kill_objects_at_pos(wave_pos.z,fate)
end
end
if (fate == "turn_to_zombie" or fate == "explode") and k==2 then
print_dbg('diff_sec>=%s | turn_to_zombie',diff_sec)
self:kill_objects_at_pos(wave_pos.z, fate)
end
if (wave_pos.z < actor_pos.z) then
if k==1
and not GetEvent("current_safe_cover")
and (character_community(db.actor) ~= "actor_monolith")
and (character_community(db.actor) ~= "actor_zombied")
then
local h = hit()
h.type = hit.telepatic
h.power = self:hit_power(2, h.type)+0.5
h.impulse = 0.0
h.direction = VEC_Z
h.draftsman = db.actor
if (xr_logic.pick_section_from_condlist(db.actor, nil, self.survive)=="true") then
if(db.actor.health<=h.power) then
if (db.actor.health-0.05 > 0.05) then
h.power = 0.05
else
h.power = 0
end
end
end
if (load_var(db.actor,"surge_immuned",false) ~= true) then
print_dbg('diff_sec>=%s | hit actor 2',diff_sec)
db.actor:hit(h)
end
self.stages['1stwavehit']=true;
--self.debugMessages[2]:SetText("first wave hit at: "..tostring(diff_sec)..' with strength: '..h.power)
end
if (k==2 and self.stages['2ndwavehit']==nil) then
if fate=="kill_at_end" then
print_dbg('diff_sec>=%s | kill_at_end',diff_sec)
self:kill_all_unhided()
else
if (load_var(db.actor,"surge_immuned",false) ~= true) then
if not (level_environment.is_actor_immune()) then
print_dbg('diff_sec>=%s | kill_actor_at_pos',diff_sec)
self:kill_actor_at_pos(wave_pos.z)
end
end
end
self.stages['2ndwavehit']=true
--self.debugMessages[2]:SetText("second wave hit at: "..tostring(diff_sec))
end
end
end
end
if (self.wave_sound) then
--self.debugMessages[1]:SetText("wave vol: "..tostring(wavevol))
xr_sound.set_volume_sound_looped(AC_ID, "blowout_particle_wave_looped", wavevol)
end
end
end
end
----------------------------------
-- Effects
----------------------------------
function CSurgeManager:displayIndicators(power)
local hud = get_hud()
if not (hud) then
return
end
local indik
if power>=0.8 then indik=nil
elseif power>=0.6 then indik=nil
elseif power>=0.4 then indik=nil
elseif power>=0.1 then indik=nil
else indik=nil end
local aspectRatio=""
if self.aspectRatio~=1 then
aspectRatio="_16"
end
local indikName="atm_indik_psi_"..tostring(indik)..aspectRatio
local currentIndikName="atm_indik_psi_"..tostring(currentIndik)..aspectRatio
--remove indik if 0
if not indik and currentIndik~=nil then
local hudIndikRem = hud:GetCustomStatic(currentIndikName)
if hudIndikRem then
hud:RemoveCustomStatic(currentIndikName)
end
currentIndik=nil
return
end
-- display different indik
if currentIndik~=indik then
-- first remove existing one
if currentIndik~=nil then
local hudIndik = hud:GetCustomStatic(currentIndikName)
if hudIndik then
hud:RemoveCustomStatic(currentIndikName)
end
end
-- now display new one
if indik then
hud:AddCustomStatic(indikName,true)
currentIndik=indik
end
end
--self.debugMessages[8]:SetText("indik="..currentIndikName)
end
function CSurgeManager:play_blowout_sound(id)
local snd_obj=self.blowout_sounds[id]
if snd_obj ~= nil and snd_obj:playing() then
snd_obj:stop()
end
if snd_obj ~= nil then
snd_obj:play(db.actor)
snd_obj.volume = 1
--self.debugMessages[12]:SetText("playing: "..tostring(id))
end
end
function CSurgeManager:play_siren_sound()
local snd_obj=self.blowout_sounds["siren"]
if (snd_obj == nil) then
return
end
local snd_position
if snd_obj:playing() then
snd_obj:stop()
end
if(level.name()=="zaton") then
snd_position= vector():set(115,8,184)
elseif(level.name()=="jupiter") then
snd_position= vector():set(-50,16,198)
else
snd_position= vector():set(147,15,-187)
end
snd_obj:play_at_pos(db.actor, snd_position)
snd_obj.volume = 1
end
function CSurgeManager:launch_rockets()
for k,v in pairs(db.signal_light) do
if not(v:is_flying()) then
v:launch()
end
end
end
function surge_callback()
level.add_cam_effector("camera_effects\\surge_01.anm", sleep_cam_eff, false, "surge_manager.surge_callback2")
-- level.stop_weather_fx()
-- level.change_game_time(0,0,15)
-- level_weathers.get_weather_manager():forced_weather_change()
end
function surge_callback2()
xr_effects.enable_ui(db.actor, nil)
--[[
level.enable_input()
level.show_indicators()
db.actor:restore_weapon()
]]--
end
----------------------------------
-- NPC fate
----------------------------------
function make_dead_crow(id,powr)
local crow = id and level.object_by_id(id)
if(crow and crow:alive() and powr) then
crow:hit(powr)
end
return true
end
function make_dead(id)
local obj = id and db.storage[id] and db.storage[id].object --level.object_by_id(id)
if (obj and obj:alive()) then
obj:kill(obj)
else
local se_obj = id and alife_object(id)
if (se_obj and se_obj:alive()) then
se_obj:kill()
end
end
return true
end
function CSurgeManager:kill_crows_at_pos(pos,delay) -- hit all crows if their pos is bigger than "pos"
local h = hit()
h.type = hit.fire_wound
h.power = 0.9
h.impulse = 0.0
h.direction = VEC_Z
h.draftsman = db.actor
for k,id in pairs(bind_crow.crow_storage) do
if delay then
CreateTimeEvent("delay_kill_crow",id,math_random(1,3),make_dead_crow,id,h)
else
local crow = level.object_by_id(id)
if (crow and crow:alive() and pos < crow:position().z) then
crow:hit(h)
end
end
end
end
function CSurgeManager:kill_objects_at_pos(surge_pos, fate, delay)
if (load_var(db.actor,"surge_immuned",false) == true) then
return
end
local can_kill = (fate == "kill_at_wave") or (fate == "turn_to_zombie") or (fate == "explode") or (fate == "kill_at_end")
if (not can_kill) then
print_dbg('kill_objects_at_pos | cant kill because fate is [%s]',fate)
return
end
print_dbg("kill_objects_at_pos | pos: %s - fate: %s - delay: %s", surge_pos, fate, delay)
local id, comm, npc_pos, se_npc, npc, squad, squad_id, smart, surge_smart
local board = SIMBOARD
local sim = alife()
for i=1, #db.OnlineStalkers do
id = db.OnlineStalkers[i]
npc = db.storage[id] and db.storage[id].object or level.object_by_id(id)
if npc then
comm = npc:character_community()
npc_pos = npc:position()
if npc_pos
and IsStalker(npc) -- must be stalker
and npc:alive() -- must be alive
and (comm ~= "monolith") -- not monolith (immune)
and (comm ~= "zombied") -- not zombied (immune)
and (not get_object_story_id(id)) -- not story npc
and ((surge_pos == false) or (surge_pos and npc_pos.z > surge_pos)) -- npc is behind emission wave
then
se_npc = sim:object(id)
squad_id = se_npc.group_id
squad = squad_id and sim:object(squad_id)
if se_npc and squad then
smart = board and squad.smart_id and board.smarts[squad.smart_id] and board.smarts[squad.smart_id].smrt
surge_smart = smart and smart.props and (tonumber(smart.props["surge"]) > 0)
if not (surge_smart or self:pos_in_cover(npc_pos)) then -- if stalker is not in a safe smart or not inside
print_dbg('kill_objects_at_pos | kill npc [%s]',se_npc:name())
if (fate == "turn_to_zombie") then
self:turn_to_zombie(se_npc,squad)
elseif (fate == "explode") then
self:explode(se_npc,squad)
elseif (fate == "kill_at_wave") or (fate == "kill_at_end") then
if delay then
CreateTimeEvent("delay_kill",id,math_random(1,3),make_dead,id)
else
npc:kill(npc)
end
end
else
print_dbg('kill_objects_at_pos | cant kill npc [%s] | surge_smart: %s - in_cover: %s',se_npc:name(), surge_smart, self:pos_in_cover(npc_pos,true))
end
end
else
print_dbg('kill_objects_at_pos | cant kill npc [%s] | behind emission wave: %s',npc:name(), ((surge_pos == false) or (surge_pos and npc_pos and npc_pos.z > surge_pos)))
end
end
end
end
function CSurgeManager:kill_actor_at_pos(pos) -- kill player at end
if (load_var(db.actor,"surge_immuned",false) == true) then
return
end
if(db.actor and db.actor:alive() and db.actor:position().z>pos) then
if not (GetEvent("current_safe_cover")) then
xr_effects.disable_ui_only(db.actor, nil)
if (xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, self.survive)~="true") then
local fate = ui_options.get("alife/event/emission_fate") or "kill_at_wave"
self:kill_objects_at_pos(false, fate, true)
db.actor:kill(db.actor)
self.ui_disabled = true
return
else
level.add_cam_effector("camera_effects\\surge_02.anm", sleep_cam_eff, false, "surge_manager.surge_callback")
level.add_pp_effector("surge_fade.ppe", sleep_fade_pp_eff, false)
--db.actor:change_health(-0.05)
self:end_surge(true)
end
end
end
end
function CSurgeManager:kill_all_unhided() -- called only when "kill_at_end" is active, turn to zombie + explode + kill with delay, kill actor
if (load_var(db.actor,"surge_immuned",false) == true) then
return
end
-- delay hit for crows
self:kill_crows_at_pos(false,true)
-- delay kill for online npcs
local fate = ui_options.get("alife/event/emission_fate") or "kill_at_wave"
self:kill_objects_at_pos(false, fate, true)
if (level_environment.is_actor_immune()) then
return
end
-- don't kill actor if he isn't on a valid level
local sim,gg = alife(),game_graph()
local actor_level = sim:level_name(gg:vertex(sim:actor().m_game_vertex_id):level_id())
if (self.indoor_levels and self.indoor_levels[actor_level] or level_weathers.valid_levels[actor_level] ~= true) then
return
end
if (db.actor and db.actor:alive() and not GetEvent("current_safe_cover")) then
--[[
if has_alife_info("anabiotic_in_process") then
local counter_name = "actor_marked_by_zone_cnt"
local cnt_value = load_var(db.actor, counter_name, 0)
save_var(db.actor, counter_name, cnt_value + 1)
end
]]
xr_effects.disable_ui_only(db.actor, nil)
if (xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, self.survive) ~= "true") then
local fate = ui_options.get("alife/event/emission_fate") or "kill_at_wave"
self:kill_objects_at_pos(false, fate, true)
db.actor:kill(db.actor)
return
else
level.add_cam_effector("camera_effects\\surge_02.anm", sleep_cam_eff, false, "surge_manager.surge_callback")
level.add_pp_effector("surge_fade.ppe", sleep_fade_pp_eff, false)
--db.actor.health = db.actor.health-0.05
end
end
end
function CSurgeManager:turn_to_zombie(se_obj,squad)
if not (squad) then
se_obj:kill()
return
end
self.zombie_count = self.zombie_count and self.zombie_count + 1 or 0
if (self.zombie_count > 12) then
se_obj:kill()
return
end
local zombie_type
local section_number = string.sub(se_obj:section_name(), -1)
if section_number=="4" then
zombie_type="sim_default_zombied_4"
elseif section_number=="3" then
zombie_type="sim_default_zombied_3"
elseif section_number=="2" or section_number=="1" then
zombie_type="sim_default_zombied_2"
else
zombie_type="sim_default_zombied_1"
end
squad:remove_npc(se_obj.id, true)
alife_create(zombie_type,se_obj.position,se_obj.m_level_vertex_id,se_obj.m_game_vertex_id)
end
function CSurgeManager:explode(se_obj,squad)
if not (squad) then
se_obj:kill()
return
end
-- play body tear and scary sound
self.body_tears[se_obj.id]=particles_object("anomaly2\\body_tear_0"..math.random(1,2))
self.body_tears[se_obj.id]:play_at_pos(se_obj.position)
local snd_obj = self.blowout_sounds["body_tear"]
if (snd_obj) then
if (snd_obj:playing()) then
snd_obj:stop()
end
snd_obj:play_at_pos(db.actor, se_obj.position)
snd_obj.volume = 1
end
squad:remove_npc(se_obj.id, true)
end
----------------------------------
-- Task
----------------------------------
function CSurgeManager:give_surge_hide_task()
if (self.surge_message ~= "empty") then
local mess = ""
if (self.surge_message == "") then
local time = 0
mess = game.translate_string("hide_from_surge_message")
else
mess = game.translate_string(self.surge_message)
end
end
if (self.surge_task_sect ~= "empty") then
if (self.surge_task_sect == "") then
task_manager.get_task_manager():give_task("hide_from_surge")
else
task_manager.get_task_manager():give_task(self.surge_task_sect)
end
end
end
function get_task_descr()
if GetEvent("current_safe_cover") then
return game.translate_string("hide_from_surge_descr_2_a")
end
return game.translate_string("hide_from_surge_descr_1_a")
end
function get_task_target()
if GetEvent("current_safe_cover") then
return nil
end
return GetEvent("nearest_safe_cover")
end
function set_surge_task(tsk)
get_surge_manager().surge_task_sect = tsk
end
----------------------------------
-- Data management
----------------------------------
function actor_on_save(binder,packet)
if (USE_MARSHAL) then
return
end
-- initialize to set default values if not loaded
local mgr = SurgeManager
if not (mgr._state_loaded) then
mgr:initialize()
mgr._state_loaded = true
end
set_save_marker(packet, "save", false, "SurgeHide")
utils_data.w_stpk(packet,"bool",mgr.finished,"CSurgeManager:finished")
utils_data.w_stpk(packet,"bool",mgr.started,"CSurgeManager:started")
utils_data.w_stpk(packet,"CTime",mgr.last_surge_time,"CSurgeManager:last_surge_time")
if(mgr.started) then
utils_data.w_stpk(packet,"CTime",mgr.inited_time,"CSurgeManager:inited_time")
utils_data.w_stpk(packet,"bool",mgr.task_given,"CSurgeManager:task_given")
utils_data.w_stpk(packet,"bool",mgr.effector_set,"CSurgeManager:effector_set")
utils_data.w_stpk(packet,"bool",mgr.second_message_given,"CSurgeManager:second_message_given")
utils_data.w_stpk(packet,"bool",mgr.ui_disabled,"CSurgeManager:ui_disabled")
utils_data.w_stpk(packet,"bool",mgr.blowout_sound,"CSurgeManager:blowout_sound")
--utils_data.w_stpk(packet,"stringZ",mgr.surge_message,"CSurgeManager:surge_message")
--utils_data.w_stpk(packet,"stringZ",mgr.surge_task_sect,"CSurgeManager:surge_task_sect")
utils_data.w_stpk(packet,"u32",mgr.game_time_factor,"CSurgeManager:game_time_factor")
end
utils_data.w_stpk(packet,"u32",mgr._delta,"CSurgeManager:_delta")
set_save_marker(packet, "save", true, "SurgeHide")
end
function actor_on_load(binder,packet)
--printf("actor on load")
local mgr = get_surge_manager()
if not (mgr._state_loaded) then
mgr:initialize()
mgr._state_loaded = true
end
if (USE_MARSHAL) then
return
end
set_save_marker(packet, "load", false, "SurgeHide")
mgr.finished = packet:r_bool()
mgr.started = packet:r_bool()
mgr.last_surge_time = utils_data.r_CTime(packet,"surge_manager") or game.get_game_time()
if(mgr.started) then
mgr.inited_time = utils_data.r_CTime(packet,"surge_manager") or game.get_game_time()
mgr.task_given = packet:r_bool()
mgr.effector_set = packet:r_bool()
mgr.second_message_given = packet:r_bool()
mgr.ui_disabled = packet:r_bool()
mgr.blowout_sound = packet:r_bool()
--mgr.surge_message = packet:r_stringZ()
--mgr.surge_task_sect = packet:r_stringZ()
mgr.game_time_factor = packet:r_u32()
mgr:finalize()
mgr.blowout_waves = empty_table(mgr.blowout_waves)
mgr.objects_to_kill = empty_table(mgr.objects_to_kill)
mgr.stages = empty_table(mgr.stages)
mgr.body_tears = empty_table(mgr.body_tears)
end
mgr._delta = packet:r_u32()
set_save_marker(packet, "load", true, "SurgeHide")
end
function save_state(m_data)
--utils_data.debug_write("SurgeManager:save_state BEFORE")
m_data.SurgeManager = {}
local mgr = get_surge_manager()
if not (mgr._state_loaded) then
mgr:initialize()
mgr._state_loaded = true
end
m_data.SurgeManager.finished = mgr.finished == nil and true or mgr.finished
m_data.SurgeManager.started = mgr.started == nil and false or mgr.started
m_data.SurgeManager.last_surge_time = mgr.last_surge_time and utils_data.CTime_to_table(mgr.last_surge_time) or game.get_game_time()
if(mgr.started) then
m_data.SurgeManager.inited_time = utils_data.CTime_to_table(mgr.inited_time) or game.get_game_time()
--m_data.SurgeManager.levels_respawn = mgr.levels_respawn
m_data.SurgeManager.task_given = mgr.task_given
m_data.SurgeManager.effector_set = mgr.effector_set
m_data.SurgeManager.second_message_given = mgr.second_message_given
m_data.SurgeManager.ui_disabled = mgr.ui_disabled
m_data.SurgeManager.blowout_sound = mgr.blowout_sound
m_data.SurgeManager.game_time_factor = mgr.game_time_factor
end
m_data.SurgeManager._delta = mgr._delta
--utils_data.debug_write("SurgeManager:save_state AFTER")
end
function load_state(m_data)
if not (m_data.SurgeManager) then
return
end
--utils_data.debug_write("SurgeManager:load_state BEFORE")
local mgr = get_surge_manager()
mgr:initialize()
mgr.finished = m_data.SurgeManager.finished
mgr.started = m_data.SurgeManager.started
mgr.last_surge_time = m_data.SurgeManager.last_surge_time and utils_data.CTime_from_table(m_data.SurgeManager.last_surge_time) or game.get_game_time()
if (mgr.started == true and mgr.finished == false) then
mgr.inited_time = m_data.SurgeManager.inited_time and utils_data.CTime_from_table(m_data.SurgeManager.inited_time) or game.get_game_time()
mgr.task_given = m_data.SurgeManager.task_given or false
mgr.effector_set = m_data.SurgeManager.effector_set or false
mgr.second_message_given = m_data.SurgeManager.second_message_given or false
mgr.ui_disabled = m_data.SurgeManager.ui_disabled or false
mgr.blowout_sound = m_data.SurgeManager.blowout_sound or false
mgr.game_time_factor = m_data.SurgeManager.game_time_factor or level.get_time_factor()
mgr:finalize()
mgr.blowout_waves = empty_table(mgr.blowout_waves)
mgr.objects_to_kill = empty_table(mgr.objects_to_kill)
mgr.stages = empty_table(mgr.stages)
mgr.body_tears = empty_table(mgr.body_tears)
else
mgr.started = false
mgr.finished = true
end
mgr._delta = m_data.SurgeManager._delta
mgr._state_loaded = true
m_data.SurgeManager = nil
--utils_data.debug_write("SurgeManager:load_state BEFORE")
end
----------------------------------
-- Utilities
----------------------------------
function start_surge(p)
get_surge_manager():start(true)
end
function stop_surge()
if(get_surge_manager().started) then
get_surge_manager():end_surge(true)
end
end
function is_started()
return get_surge_manager().started
end
function is_finished()
return not get_surge_manager().started
end
function is_loaded()
return get_surge_manager()._state_loaded == true
end
function actor_in_cover()
return GetEvent("current_safe_cover") and true or false
end
function npc_in_cover(npc)
return get_surge_manager():pos_in_cover(npc:position())
end
function job_in_surge_cover(se_obj,job)
if not (job.alife_task) then
return false
end
return get_surge_manager():pos_in_cover(job.alife_task:position())
end
function set_surge_message(mess)
get_surge_manager().surge_message = mess
end
function is_killing_all()
local mgr = get_surge_manager()
if(mgr.started and mgr.ui_disabled) then
return true
end
return false
end
function sound_started()
return get_surge_manager().started and get_surge_manager().blowout_sound
end