423 lines
12 KiB
Plaintext
423 lines
12 KiB
Plaintext
-- Adapted the debugger code
|
|
function spawn_at_position(section, position_vector, lvid, gvid, is_squad, angle, is_anomaly)
|
|
local se_obj = alife_create(section, position_vector, lvid, gvid)
|
|
|
|
if is_squad then
|
|
se_obj:create_npc(nil, position_vector, lvid, gvid)
|
|
local sim = alife()
|
|
for k in se_obj:squad_members() do
|
|
local se_npc = k.object or k.id and sim:object(k.id)
|
|
if (se_npc) then
|
|
SIMBOARD:setup_squad_and_group(se_npc)
|
|
SendScriptCallback("squad_on_npc_creation",se_obj,se_npc)
|
|
end
|
|
end
|
|
end
|
|
|
|
if angle then
|
|
se_obj.angle = angle
|
|
end
|
|
|
|
if is_anomaly then
|
|
local data = utils_stpk.get_object_data(se_obj)
|
|
if (data) then
|
|
data.object_flags = 31
|
|
data.restrictor_type = 0
|
|
data.shapes = {}
|
|
data.shapes[1] = {}
|
|
data.shapes[1].shtype = 0
|
|
data.shapes[1].offset = VEC_ZERO
|
|
data.shapes[1].radius = 10
|
|
utils_stpk.set_object_data(data,se_obj)
|
|
end
|
|
end
|
|
|
|
return se_obj.id
|
|
end
|
|
|
|
-- TODO: remove second param - it's cancer
|
|
-- Second param is mainly used for loop spawning to not have same sections defined in configs
|
|
function spawn_helper(spawn_config, section_override)
|
|
return spawn_at_position(
|
|
spawn_config.section or section_override,
|
|
spawn_config.vector,
|
|
spawn_config.lvid,
|
|
spawn_config.gvid,
|
|
spawn_config.is_squad,
|
|
spawn_config.angle,
|
|
spawn_config.is_anomaly
|
|
)
|
|
end
|
|
|
|
function is_dark_night ()
|
|
return level.get_time_hours() <= 3 or level.get_time_hours() >= 22
|
|
end
|
|
|
|
function spawn_helicopter(is_strong, pos, lvid, gvid)
|
|
local heli = "new_tasks_addon_heli_" .. (is_strong and "strong" or "weak")
|
|
local se_obj = alife_create(heli, pos, lvid, gvid)
|
|
|
|
local visual = utils_data.cfg_get_string(ini_sys, heli,"visual", se_obj, false)
|
|
local data = utils_stpk.get_heli_data(se_obj)
|
|
if (data) then
|
|
data.visual_name = visual and visual ~= "" and visual or [[dynamics\vehicles\mi2\veh_mi2_01]]
|
|
data.motion_name = [[helicopter\aaa.anm]]
|
|
data.startup_animation = "idle"
|
|
data.skeleton_name = "idle"
|
|
data.engine_sound = [[vehicles\helicopter\helicopter]]
|
|
utils_stpk.set_heli_data(data, se_obj)
|
|
else
|
|
safe_release_manager.release(se_obj)
|
|
end
|
|
|
|
return se_obj.id
|
|
end
|
|
|
|
function immediate_explosion(actor,obj)
|
|
if (obj) then
|
|
obj:explode(0)
|
|
end
|
|
end
|
|
|
|
function noop() end
|
|
|
|
function find_index(tab, val)
|
|
for index, value in ipairs(tab) do
|
|
if value == val then
|
|
return index
|
|
end
|
|
end
|
|
return -1
|
|
end
|
|
|
|
function has_value(tab, val)
|
|
for index, value in ipairs(tab) do
|
|
if value == val then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function shallow_copy(t)
|
|
local res = {}
|
|
for k,v in pairs(t) do
|
|
res[k] = v
|
|
end
|
|
return res
|
|
end
|
|
|
|
-- No invalid range check - expect to receive the right input
|
|
function rand_with_exclusion(min, max, exclude)
|
|
local t = {}
|
|
for i = min, max do
|
|
if i ~= exclude then
|
|
table.insert(t, i)
|
|
end
|
|
end
|
|
|
|
local rand_i = math.random(#t)
|
|
return t[rand_i]
|
|
end
|
|
|
|
function is_strike(shit)
|
|
-- IDK why but for some reason hit.strike comes back as 7 sometimes. Something's really fucked up
|
|
return shit.type == 5
|
|
end
|
|
|
|
function is_axe()
|
|
local item = db.actor:active_item()
|
|
return item and string.find(item:section(), "axe")
|
|
end
|
|
|
|
function is_melee(item)
|
|
return string.find(item:section(), "axe") or string.find(item:section(), "knife")
|
|
end
|
|
|
|
function teleport_visual()
|
|
level.add_pp_effector ("teleport.ppe", 2006, false)
|
|
local snd_obj = sound_object("affects\\tinnitus3a")
|
|
snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, VEC_ZERO, 1.0, 1.0)
|
|
end
|
|
|
|
-- Used to push dead bodies that should disappear below the textures to avoid errors caused by on_death callbacks from other mods
|
|
function hide_through_teleport(id)
|
|
CreateTimeEvent(0,"nta_hide_body_teleport_" .. id,0, function ()
|
|
local obj = level.object_by_id(id)
|
|
local pos = obj:position()
|
|
pos.y = pos.y - 40
|
|
obj:force_set_position(pos, false)
|
|
return true
|
|
end)
|
|
end
|
|
|
|
function spawn_companion_squad(sq_section, vector, lvid, gvid)
|
|
local id = spawn_at_position(sq_section, vector, lvid, gvid, true)
|
|
local squad = alife_object(id)
|
|
|
|
axr_companions.companion_squads[squad.id] = squad
|
|
for k in squad:squad_members() do
|
|
local se_obj = k.object or k.id and sim:object(k.id)
|
|
if (se_obj) then
|
|
se_save_var(se_obj.id,se_obj:name(),"companion",true)
|
|
se_save_var(se_obj.id,se_obj:name(),"companion_cannot_dismiss",true)
|
|
SIMBOARD:setup_squad_and_group(se_obj)
|
|
end
|
|
end
|
|
|
|
-- Add to ignore offline combat simulation list
|
|
sim_offline_combat.task_squads[squad.id] = true
|
|
|
|
return id
|
|
end
|
|
|
|
function squad_alive(id)
|
|
local sim = alife()
|
|
local squad = sim:object(id)
|
|
|
|
local alive = false
|
|
for k in squad:squad_members() do
|
|
local se_npc = k.object or k.id and sim:object(k.id)
|
|
if (se_npc:alive()) then
|
|
alive = true
|
|
end
|
|
end
|
|
return alive
|
|
end
|
|
|
|
-- Excluded Bar
|
|
southern_maps = {
|
|
"k00_marsh",
|
|
"k01_darkscape",
|
|
"k02_trucks_cemetery",
|
|
"l01_escape",
|
|
"l02_garbage",
|
|
"l03_agroprom",
|
|
"l04_darkvalley",
|
|
"l06_rostok",
|
|
"l07_military",
|
|
"l08_yantar",
|
|
"l09_deadcity",
|
|
"y04_pole",
|
|
}
|
|
|
|
nothern_maps = {
|
|
"l10_red_forest",
|
|
"jupiter",
|
|
"pripyat",
|
|
"zaton",
|
|
"l13_generators",
|
|
"l12_stancia_2",
|
|
"l12_stancia",
|
|
"l11_pripyat",
|
|
"l10_radar",
|
|
}
|
|
|
|
sim_stalkers = nil
|
|
function prefetch_sim_stalkers()
|
|
sim_stalkers = {}
|
|
for i = 1, 65534 do
|
|
local se_obj = alife_object(i)
|
|
|
|
if
|
|
se_obj
|
|
and (IsStalker(nil,se_obj:clsid()) and se_obj:alive() and string.find(se_obj:section_name(),"sim_default") and get_object_story_id(id) == nil)
|
|
and (se_obj.group_id ~= 65535 and get_object_story_id(se_obj.group_id) == nil)
|
|
then
|
|
local comm = alife_character_community(se_obj)
|
|
|
|
if not sim_stalkers[comm] then
|
|
sim_stalkers[comm] = {}
|
|
end
|
|
|
|
table.insert(sim_stalkers[comm], se_obj.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
function find_random_stalker(factions, maps)
|
|
local limit = 60
|
|
-- Check 60 random stalkers at most and make it completely random
|
|
for i = 1, limit do
|
|
local faction = factions[math.random(1, #factions)]
|
|
local faction_stalkers = sim_stalkers[faction]
|
|
-- It might happen that there's no stalker of a given faction on the map and the entry for that faction won't be created
|
|
if faction_stalkers then
|
|
local stalker_id = faction_stalkers[math.random(1, #faction_stalkers)]
|
|
local se_obj = alife_object(stalker_id)
|
|
|
|
if se_obj then
|
|
local level = get_object_level_id(se_obj)
|
|
|
|
if
|
|
(not maps or has_value(maps, level))
|
|
and IsStalker(nil,se_obj:clsid()) and se_obj:alive() and alife_character_community(se_obj) == faction
|
|
then
|
|
return {
|
|
id = stalker_id,
|
|
level = level,
|
|
faction = faction,
|
|
name = se_obj:character_name()
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function get_object_level_id(se_obj)
|
|
local gg = game_graph()
|
|
local sim = alife()
|
|
return sim:level_name(gg:vertex(se_obj.m_game_vertex_id):level_id())
|
|
end
|
|
|
|
local earthquake_cam_eff = 3
|
|
function earthquake_screen_effect_gradual(duration_multiplier)
|
|
local phase_duration_multiplier = duration_multiplier or 2
|
|
local effector_names = {
|
|
"camera_effects\\earthquake_20.anm",
|
|
"camera_effects\\earthquake_40.anm",
|
|
"camera_effects\\earthquake_60.anm",
|
|
"camera_effects\\earthquake_80.anm",
|
|
"camera_effects\\earthquake.anm",
|
|
"camera_effects\\earthquake_80.anm",
|
|
"camera_effects\\earthquake_60.anm",
|
|
"camera_effects\\earthquake_40.anm",
|
|
"camera_effects\\earthquake_20.anm"
|
|
}
|
|
|
|
|
|
|
|
for index, name in ipairs(effector_names) do
|
|
CreateTimeEvent(0,"nta_earthquake_screen_" .. index, (index - 1) * phase_duration_multiplier, function ()
|
|
level.remove_cam_effector(earthquake_cam_eff)
|
|
level.add_cam_effector(name, earthquake_cam_eff, true, "", 0, false)
|
|
return true
|
|
end)
|
|
end
|
|
|
|
CreateTimeEvent(0,"nta_earthquake_screen_remove", #effector_names * phase_duration_multiplier, function ()
|
|
level.remove_cam_effector(earthquake_cam_eff)
|
|
return true
|
|
end)
|
|
end
|
|
|
|
function earthquake_screen_effect_strong(duration)
|
|
level.add_cam_effector("camera_effects\\earthquake.anm", earthquake_cam_eff, true, "", 0, false)
|
|
|
|
CreateTimeEvent(0,"nta_earthquake_screen_remove", duration or 6, function ()
|
|
level.remove_cam_effector(earthquake_cam_eff)
|
|
return true
|
|
end)
|
|
end
|
|
|
|
function actor_in_range(vector, dist)
|
|
return db.actor:position():distance_to(vector) < dist
|
|
end
|
|
|
|
function actor_icon()
|
|
return db.actor:character_icon()
|
|
end
|
|
|
|
function actor_name()
|
|
return db.actor:character_name()
|
|
end
|
|
|
|
function obj_position_to_spawn_config_partial(obj)
|
|
return {
|
|
vector = obj:position(),
|
|
lvid = obj:level_vertex_id(),
|
|
gvid = obj:game_vertex_id()
|
|
}
|
|
end
|
|
|
|
levels = {
|
|
swamp = "k00_marsh",
|
|
darkscape = "k01_darkscape",
|
|
truck_cemetery = "k02_trucks_cemetery",
|
|
cordon = "l01_escape",
|
|
garbage = "l02_garbage",
|
|
agroprom = "l03_agroprom",
|
|
dark_valley = "l04_darkvalley",
|
|
wild_territory = "l06_rostok",
|
|
army_warehouses = "l07_military",
|
|
yantar = "l08_yantar",
|
|
dead_city = "l09_deadcity",
|
|
meadow = "y04_pole",
|
|
x16 = 'l08u_brainlab',
|
|
x18 = 'l04u_labx18',
|
|
red_forest = 'l10_red_forest',
|
|
outskirts = 'pripyat'
|
|
}
|
|
-- TODO: Remove separate functions from task files and use this one instead
|
|
function actor_on_level(lvl)
|
|
return level.name() == lvl
|
|
end
|
|
|
|
local function load_defaults()
|
|
local t = {}
|
|
local op = new_tasks_addon_mcm.op
|
|
for _, v in ipairs(op.gr) do
|
|
if v.def ~= nil then
|
|
t[v.id] = v.def
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
mcm_config = load_defaults()
|
|
|
|
local function load_settings()
|
|
mcm_config = load_defaults()
|
|
if ui_mcm then
|
|
for k, v in pairs(mcm_config) do
|
|
mcm_config[k] = ui_mcm.get("new_tasks_addon/" .. k)
|
|
end
|
|
end
|
|
end
|
|
|
|
xr_effects.dispatch_nta_task_details = function(actor, npc, p)
|
|
CreateTimeEvent(0,"nta_task_details",0, function ()
|
|
local task_id = p[1]
|
|
-- Build News
|
|
local news_caption = game.translate_string(task_manager.task_ini:r_string_ex(task_id, "title")) or "error"
|
|
|
|
local news_text = game.translate_string(task_manager.task_ini:r_string_ex(task_id, "nta_task_details")) or ""
|
|
local news_ico = task_manager.task_ini:r_string_ex(task_id, "icon")
|
|
|
|
db.actor:give_talk_message2(news_caption, news_text, news_ico, "iconed_answer_item")
|
|
return true
|
|
end)
|
|
end
|
|
|
|
xr_conditions.check_nta_replayability = function(actor, npc, p)
|
|
local task_id = p[1]
|
|
if not db.actor:has_info(task_id .. '_completed') then
|
|
return true
|
|
else
|
|
return mcm_config["repl_" .. task_id]
|
|
end
|
|
end
|
|
|
|
function check_nta_auto_assignment(task_id)
|
|
return mcm_config["auto_assignment_" .. task_id]
|
|
end
|
|
|
|
function actor_on_first_update()
|
|
prefetch_sim_stalkers()
|
|
end
|
|
|
|
add_console_command("chopper", function()
|
|
se_id = spawn_helicopter(true, vector():set(-178.07975769043,5.3180623054504,-181.78283691406))
|
|
end)
|
|
|
|
function change_level(configs)
|
|
change_level_now(configs.vector, configs.lvid, configs.gvid, configs.angle or vector():set(0, 0, 0))
|
|
end
|
|
|
|
function on_game_start()
|
|
RegisterScriptCallback("on_game_load", load_settings)
|
|
RegisterScriptCallback("on_option_change", load_settings)
|
|
RegisterScriptCallback("actor_on_first_update",actor_on_first_update)
|
|
end |