1875 lines
60 KiB
Plaintext
1875 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
|
||
|
--]]
|
||
|
|
||
|
-- Edited by demonized, 2022
|
||
|
-- Cleanup and reformat
|
||
|
-- NPC Die in Emissions
|
||
|
-- Faster emissions
|
||
|
|
||
|
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 presurge_played = { false, false, false, false, false }
|
||
|
|
||
|
--[[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 = 0
|
||
|
|
||
|
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.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: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 = ""
|
||
|
|
||
|
-- Stages of emission in real seconds, start is 0
|
||
|
-- Siren
|
||
|
self.siren_sec = 25
|
||
|
|
||
|
-- Blowout warning
|
||
|
self.blowout_warning_sec = 30
|
||
|
|
||
|
-- Blowout impact sound
|
||
|
self.blowout_impact_sec = 47
|
||
|
|
||
|
-- Start rumble
|
||
|
self.rumble_sec = 50
|
||
|
|
||
|
-- First earthquake
|
||
|
self.first_earthquake_sec = 80
|
||
|
|
||
|
-- Second warning
|
||
|
self.second_message_sec = 90
|
||
|
|
||
|
-- Earthquakes
|
||
|
self.earthquakes1_sec = 100
|
||
|
self.earthquakes2_sec = self.earthquakes1_sec + 2
|
||
|
self.earthquakes3_sec = self.earthquakes1_sec + 4
|
||
|
self.earthquakes4_sec = self.earthquakes1_sec + 6
|
||
|
|
||
|
-- First wave sound
|
||
|
self.first_wave_sound_sec = 108
|
||
|
|
||
|
-- First wave
|
||
|
self.first_wave_sec = 120
|
||
|
|
||
|
-- Second wave sound
|
||
|
self.second_wave_sound_sec = self.first_wave_sound_sec + 48
|
||
|
|
||
|
-- Second wave
|
||
|
self.second_wave_sec = self.first_wave_sec + 48
|
||
|
|
||
|
-- Earthquakes fade
|
||
|
self.earthquakes1_fade_sec = self.second_wave_sec + 32
|
||
|
self.earthquakes2_fade_sec = self.earthquakes1_fade_sec + 2
|
||
|
self.earthquakes3_fade_sec = self.earthquakes1_fade_sec + 6
|
||
|
self.earthquakes4_fade_sec = self.earthquakes1_fade_sec + 9
|
||
|
self.earthquakes5_fade_sec = self.earthquakes1_fade_sec + 14
|
||
|
|
||
|
-- End message
|
||
|
self.end_message_sec = self.earthquakes5_fade_sec + 5
|
||
|
|
||
|
-- After sound sec
|
||
|
self.after_sound_sec = self.earthquakes5_fade_sec + 3
|
||
|
|
||
|
-- Total surge time based on upper times
|
||
|
self.surge_time = self.end_message_sec - 3
|
||
|
|
||
|
-- Options
|
||
|
-- Turn to zombies right after first wave (false - vanilla behaviour - only after second)
|
||
|
self.zombie_at_first_wave = true
|
||
|
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
|
||
|
|
||
|
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")
|
||
|
or has_alife_info("lttz_dp_active")
|
||
|
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
|
||
|
|
||
|
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.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)
|
||
|
|
||
|
presurge_played = { false, false, false, false, false }
|
||
|
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 = { false, false, false, false, false }
|
||
|
|
||
|
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():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
|
||
|
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()
|
||
|
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 has_alife_info("lttz_dp_active")
|
||
|
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()
|
||
|
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)
|
||
|
|
||
|
--DEBUG
|
||
|
local g_time = game.get_game_time()
|
||
|
-- printf("until blowout: %s", self._delta - g_time:diffSec(self.last_surge_time))
|
||
|
|
||
|
--Plays sound before a blowout
|
||
|
if self._delta - g_time:diffSec(self.last_surge_time) < presurge_time and not presurge_played[1] then
|
||
|
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 self._delta - g_time:diffSec(self.last_surge_time) < presurge_time - 60 and not presurge_played[2] then
|
||
|
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 self._delta - g_time:diffSec(self.last_surge_time) < presurge_time - 120 and not presurge_played[3] then
|
||
|
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 self._delta - g_time:diffSec(self.last_surge_time) < presurge_time - 180 and not presurge_played[4] then
|
||
|
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 self._delta - g_time:diffSec(self.last_surge_time) < presurge_time - 240 and not presurge_played[5] then
|
||
|
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
|
||
|
|
||
|
if not self.started then
|
||
|
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
|
||
|
|
||
|
-- 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
|
||
|
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
|
||
|
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
|
||
|
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 >= self.siren_sec) and (self.stages["siren"] ~= true) and (warn == "siren" 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 >= self.blowout_warning_sec then
|
||
|
print_dbg("diff_sec>=%s | blowout warning", diff_sec)
|
||
|
if self.task_given ~= true then
|
||
|
--[[if (warn=="radio" or warn =="radio_siren") then
|
||
|
local level_name = level.name()
|
||
|
if (level_name == "zaton") then
|
||
|
if not (utils_obj.npc_in_zone(db.actor,"zat_a2_sr_noweap")) then
|
||
|
xr_sound.set_sound_play(AC_ID, "zat_a2_stalker_barmen_surge_phase_1")
|
||
|
end
|
||
|
elseif (level_name == "jupiter") then
|
||
|
if not (utils_obj.npc_in_zone(db.actor,"jup_a6_sr_noweap")) then
|
||
|
xr_sound.set_sound_play(AC_ID, "jup_a6_stalker_medik_phase_1")
|
||
|
end
|
||
|
elseif (level_name == "l03_agroprom") then
|
||
|
xr_sound.set_sound_play(AC_ID, "kovalsky_surge_phase_1")
|
||
|
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 >= self.blowout_impact_sec) and (self.stages["impact"] ~= true) then
|
||
|
print_dbg("diff_sec>=%s | blowout impact", diff_sec)
|
||
|
if (rnd_sound % 2) ~= 0 then
|
||
|
self:play_blowout_sound("impact01")
|
||
|
else
|
||
|
self:play_blowout_sound("impact02")
|
||
|
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 >= self.rumble_sec) 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 >= self.rumble_sec) and (diff_sec <= self.rumble_sec + 25) 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, self.rumble_sec * 5, (self.rumble_sec + 25) * 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 >= self.first_earthquake_sec) 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 >= self.second_message_sec) and not self.second_message_given then
|
||
|
print_dbg("diff_sec>=%s | second message", diff_sec)
|
||
|
--[[if (warn=="radio" or warn=="radio_siren") then
|
||
|
local level_name = level.name()
|
||
|
if(level_name == "zaton" and not utils_obj.npc_in_zone(db.actor,"zat_a2_sr_noweap")) then
|
||
|
xr_sound.set_sound_play(AC_ID, "zat_a2_stalker_barmen_surge_phase_2")
|
||
|
elseif(level_name == "jupiter" and not utils_obj.npc_in_zone(db.actor,"jup_a6_sr_noweap")) 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")
|
||
|
end
|
||
|
end]]
|
||
|
self.second_message_given = true
|
||
|
--self.debugMessages[2]:SetText("second message given at: "..tostring(diff_sec))
|
||
|
end
|
||
|
|
||
|
-- earthquakes
|
||
|
if (diff_sec >= self.earthquakes1_sec) 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 >= self.earthquakes2_sec) 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 >= self.earthquakes3_sec) 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 >= self.earthquakes4_sec) 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 >= self.first_wave_sound_sec) and (self.stages["1stwavesnd"] == nil) then
|
||
|
print_dbg("diff_sec>=%s | 1st wave sound", diff_sec)
|
||
|
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 >= self.first_wave_sec) 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 >= self.second_wave_sound_sec) and (self.stages["2ndwavesnd"] == nil) then
|
||
|
print_dbg("diff_sec>=%s | 2nd wave sound", diff_sec)
|
||
|
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 >= self.second_wave_sec) 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 >= self.earthquakes1_fade_sec) 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 >= self.earthquakes2_fade_sec) 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 >= self.earthquakes3_fade_sec) 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 >= self.earthquakes4_fade_sec) 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 >= self.earthquakes5_fade_sec) 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 >= self.earthquakes1_fade_sec) and (diff_sec <= self.earthquakes5_fade_sec) 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, self.earthquakes1_fade_sec * 5, self.earthquakes5_fade_sec * 5, 1, 0))
|
||
|
xr_sound.set_volume_sound_looped(
|
||
|
AC_ID,
|
||
|
"surge_earthquake_sound_looped",
|
||
|
fade(diff_game_sec, self.earthquakes1_fade_sec * 5, self.earthquakes5_fade_sec * 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 >= self.earthquakes5_fade_sec) 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 >= self.end_message_sec) and (self.stages["endmessage"] == nil) then
|
||
|
print_dbg("diff_sec>=%s | end message", diff_sec)
|
||
|
--[[if (level) and (warn=="radio" or warn=="radio_siren") then
|
||
|
if(level.name()=="zaton" and not utils_obj.npc_in_zone(db.actor,"zat_a2_sr_noweap")) then
|
||
|
xr_sound.set_sound_play(AC_ID, "zat_a2_stalker_barmen_after_surge")
|
||
|
elseif(level.name()=="jupiter"and not utils_obj.npc_in_zone(db.actor,"jup_a6_sr_noweap")) 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")
|
||
|
end
|
||
|
end]]
|
||
|
--self.debugMessages[2]:SetText("end message at: "..tostring(diff_sec))
|
||
|
self.stages["endmessage"] = true
|
||
|
end
|
||
|
|
||
|
-- after sound
|
||
|
if (diff_sec >= self.after_sound_sec) 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 >= self.first_wave_sound_sec) and (diff_sec < self.earthquakes4_fade_sec) 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 >= self.first_wave_sound_sec) and (diff_sec <= self.first_wave_sec) and self.effector_set then
|
||
|
self.hitFactor = fade(diff_sec, self.first_wave_sound_sec, self.first_wave_sec, 0.1, 1)
|
||
|
end
|
||
|
|
||
|
if (diff_sec >= self.first_wave_sec) and (diff_sec <= self.second_wave_sound_sec) and self.effector_set then
|
||
|
self.hitFactor = fade(diff_sec, self.first_wave_sec, self.second_wave_sound_sec, 1, 0.3)
|
||
|
end
|
||
|
|
||
|
if (diff_sec >= self.second_wave_sound_sec) and (diff_sec <= self.second_wave_sec) and self.effector_set then
|
||
|
self.hitFactor = fade(diff_sec, self.second_wave_sound_sec, self.second_wave_sec, 0.3, 1)
|
||
|
end
|
||
|
|
||
|
if (diff_sec >= self.second_wave_sec) and (diff_sec <= self.earthquakes1_fade_sec) and self.effector_set then
|
||
|
self.hitFactor = fade(diff_sec, self.second_wave_sec, self.earthquakes1_fade_sec, 1, 0.3)
|
||
|
end
|
||
|
|
||
|
if (diff_sec >= self.earthquakes1_fade_sec) and (diff_sec <= self.earthquakes4_fade_sec) and self.effector_set then
|
||
|
self.hitFactor = fade(diff_sec, self.earthquakes1_fade_sec, self.earthquakes4_fade_sec, 0.3, 0.001)
|
||
|
end
|
||
|
|
||
|
if (diff_sec >= self.earthquakes4_fade_sec) and self.effector_set then
|
||
|
self.hitFactor = 0
|
||
|
level.remove_pp_effector(surge_shock_pp_eff)
|
||
|
self.effector_set = false
|
||
|
end
|
||
|
|
||
|
print_dbg("sec %s, hit factor %s", diff_sec, self.hitFactor)
|
||
|
|
||
|
-- setting effector
|
||
|
local PPEfactor
|
||
|
local hitPower
|
||
|
if not (GetEvent("current_safe_cover")) then
|
||
|
PPEfactor = self.hitFactor
|
||
|
hitPower = self.hitFactor / 30
|
||
|
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 not level_environment.is_actor_immune() 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 -- Commenting that bit
|
||
|
if 1 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 == "explode" then
|
||
|
print_dbg("diff_sec>=%s | explode", diff_sec)
|
||
|
self:kill_objects_at_pos(wave_pos.z, fate)
|
||
|
end
|
||
|
|
||
|
if (fate == "turn_to_zombie") and (self.zombie_at_first_wave or 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 not level_environment.is_actor_immune() 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 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
|
||
|
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 = "red"
|
||
|
elseif power >= 0.6 then
|
||
|
indik = "orange"
|
||
|
elseif power >= 0.4 then
|
||
|
indik = "yellow"
|
||
|
elseif power >= 0.1 then
|
||
|
indik = "green"
|
||
|
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:set_grace_threshold(npc)
|
||
|
local id = npc:id()
|
||
|
db.storage[id].surge_cover_grace_threshold_time = time_global()
|
||
|
end
|
||
|
|
||
|
function CSurgeManager:check_grace_threshold(npc)
|
||
|
local id = npc:id()
|
||
|
local gt = db.storage[id].surge_cover_grace_threshold_time or 0
|
||
|
return ((time_global() - gt) < 1500)
|
||
|
end
|
||
|
|
||
|
function CSurgeManager:kill_objects_at_pos(surge_pos, fate, delay)
|
||
|
-- if (level_environment.is_actor_immune()) 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
|
||
|
and not (id == AC_ID and level_environment.is_actor_immune()) -- npc is actor and not immune
|
||
|
and not surge_manager_ignore_npc.ignore_npc[npc:section()] -- not important npc (immune)
|
||
|
and not self:pos_in_cover(npc_pos) -- not in cover
|
||
|
and not self:check_grace_threshold(npc)
|
||
|
then
|
||
|
se_npc = sim:object(id)
|
||
|
if se_npc then
|
||
|
if fate == "turn_to_zombie" then
|
||
|
squad_id = se_npc.group_id
|
||
|
squad = squad_id and squad_id ~= 65535 and sim:object(squad_id)
|
||
|
if not self:pos_in_cover(npc_pos) then -- if stalker is not inside
|
||
|
print_dbg("kill_objects_at_pos | make npc zombie [%s]", se_npc:name())
|
||
|
self:turn_to_zombie(se_npc, squad)
|
||
|
else
|
||
|
if self:pos_in_cover(npc_pos) then
|
||
|
self:set_grace_threshold(npc)
|
||
|
print_dbg("cant make npc zombie %s, in cover", se_npc:name())
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
if not self:pos_in_cover(npc_pos) then -- if stalker is not inside
|
||
|
if fate == "explode" then
|
||
|
print_dbg("kill_objects_at_pos | explode [%s]", se_npc:name())
|
||
|
self:explode(se_npc)
|
||
|
elseif fate == "kill_at_wave" or fate == "kill_at_end" then
|
||
|
print_dbg("kill_objects_at_pos | kill npc [%s]", se_npc:name())
|
||
|
if delay then
|
||
|
CreateTimeEvent("delay_kill", id, math_random(1, 3), make_dead, id)
|
||
|
else
|
||
|
npc:kill(npc)
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
if self:pos_in_cover(npc_pos) then
|
||
|
self:set_grace_threshold(npc)
|
||
|
print_dbg("cant kill %s, in cover", se_npc:name())
|
||
|
end
|
||
|
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
|
||
|
end
|
||
|
else
|
||
|
if not IsStalker(npc) then
|
||
|
print_dbg("cant kill %s, must be stalker", npc:name())
|
||
|
end
|
||
|
if not npc:alive() then
|
||
|
print_dbg("cant kill %s, must be alive", npc:name())
|
||
|
end
|
||
|
if not (comm ~= "monolith") then
|
||
|
print_dbg("cant kill %s, is monolith (immune)", npc:name())
|
||
|
end
|
||
|
if not (comm ~= "zombied") then
|
||
|
print_dbg("cant kill %s, is zombied (immune)", npc:name())
|
||
|
end
|
||
|
if not (not get_object_story_id(id)) then
|
||
|
print_dbg("cant kill %s, is story npc", npc:name())
|
||
|
end
|
||
|
if not ((surge_pos == false) or (surge_pos and npc_pos.z > surge_pos)) then
|
||
|
print_dbg(
|
||
|
"cant kill %s, npc is behind emission wave, npc pos %s, wave pos %s",
|
||
|
npc:name(),
|
||
|
npc_pos.z,
|
||
|
surge_pos
|
||
|
)
|
||
|
end
|
||
|
if not not (id == AC_ID and level_environment.is_actor_immune()) then
|
||
|
print_dbg("cant kill %s, npc is actor and immune", npc:id())
|
||
|
end
|
||
|
if not not surge_manager_ignore_npc.ignore_npc[npc:section()] then
|
||
|
print_dbg("cant kill %s, is important, section %s", npc:name(), npc:section())
|
||
|
end
|
||
|
if npc_pos and self:pos_in_cover(npc_pos) then
|
||
|
self:set_grace_threshold(npc)
|
||
|
print_dbg(
|
||
|
"cant kill npc [%s] | in_cover: %s",
|
||
|
npc:name(),
|
||
|
npc_pos and self:pos_in_cover(npc_pos, true)
|
||
|
)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function CSurgeManager:kill_actor_at_pos(pos) -- kill player at end
|
||
|
if level_environment.is_actor_immune() 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"
|
||
|
and not level_environment.is_actor_immune()
|
||
|
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 (level_environment.is_actor_immune()) 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"
|
||
|
and not level_environment.is_actor_immune()
|
||
|
then
|
||
|
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 se_obj then return end
|
||
|
-- 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
|
||
|
|
||
|
local pos = se_obj.position
|
||
|
local lvid = se_obj.m_level_vertex_id
|
||
|
local gvid = se_obj.m_game_vertex_id
|
||
|
|
||
|
if squad then
|
||
|
squad:remove_npc(se_obj.id, true)
|
||
|
else
|
||
|
alife():release(se_obj)
|
||
|
end
|
||
|
|
||
|
alife_create(zombie_type, pos, lvid, gvid)
|
||
|
end
|
||
|
|
||
|
function CSurgeManager:explode(se_obj, squad)
|
||
|
-- 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
|
||
|
|
||
|
-- alife():release(se_obj)
|
||
|
if not squad then
|
||
|
alife():release(se_obj)
|
||
|
return
|
||
|
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
|