Divergent/mods/Stealth Overhaul/gamedata/scripts/xr_combat_ignore.script

452 lines
14 KiB
Plaintext
Raw Permalink Normal View History

2024-03-17 20:18:03 -04:00
--[[
scheme_type: generic
author: Stohe
modified_by: Alundaio
--]]
fighting_with_actor_npcs = {}
safe_zone_npcs = {}
local ignored_zone = {}
local ignored_zone_list = {
--"bar_mutant_exile_zone",
--"esc_smart_terrain_2_12_sr_no_assault",
--"esc_lager_guard_kill_zone",
"zat_a2_sr_no_assault",
"jup_a6_sr_no_assault",
"jup_b41_sr_no_assault"
}
------------------------------------------
-- Localized Functions
------------------------------------------
local function npc_on_hit_callback(npc,amount,local_direction,who,bone_index)
if (amount > 0) then
db.storage[npc:id()].hitted_by = who and who:id()
end
-- xr_bribe
if who:id() == AC_ID then
xr_bribe.npc_on_hit_callback(character_community(npc))
end
end
local function npc_on_death_callback(npc,who)
safe_zone_npcs[npc:id()] = nil
end
local function squad_on_npc_death(squad,npc)
if (squad:npc_count() == 0) then
safe_zone_npcs[squad.id] = nil
end
end
local function on_game_load()
if (not alife_storage_manager.get_state().enable_warfare_mode) then
for i=1,#ignored_zone_list do
--ignored_zone[#ignored_zone+1] = ignored_zone_list[i] --Test
end
end
end
--------------------------------
-- Callback Register
--------------------------------
function on_game_start()
RegisterScriptCallback("npc_on_hit_callback",npc_on_hit_callback)
RegisterScriptCallback("npc_on_death_callback",npc_on_death_callback)
RegisterScriptCallback("squad_on_npc_death",squad_on_npc_death)
RegisterScriptCallback("on_game_load",on_game_load)
end
math.randomseed(os.time())
local rnd = math.random(180000,480000)
local rnd2 = rnd - 10000
local dist_rnd = math.random(18000,33000)
function is_enemy(obj,enemy,no_memory)
--utils_data.debug_write("eval_is_enemy")
if(device().precache_frame > 1) then
return false -- fix actor dying before game loads from enemies. Also fixes strange crash on game load when in combat sometimes.
end
if (obj:clsid() == clsid.crow) or (enemy:clsid() == clsid.crow) then -- freakin crows!
return false
end
if not (obj:alive()) then
return false
end
if not (enemy:alive()) then
return false
end
if (obj:critically_wounded()) then
return true
end
local flags = {override = false, result = false}
SendScriptCallback("on_enemy_eval",obj,enemy,flags)
if flags.override then return flags.result end
local ene_id = enemy:id()
local id = obj:id()
local st = db.storage[id]
if (DEV_DEBUG) then
if (xrs_debug_tools and xrs_debug_tools.debug_invis and ene_id == AC_ID) then
return false
end
end
-- xr_bribe
if xr_bribe and ene_id == AC_ID then
if xr_bribe.at_peace(character_community(obj),character_community(enemy),enemy:position():distance_to_sqr(obj:position())) then
return false
end
end
local obj_is_stalker = IsStalker(obj)
-- Ignore long unseen/unheard enemies
if (obj_is_stalker) then
if (no_memory ~= true) then
local tg = time_global()
local time_in_memory = tg - obj:memory_time(enemy)
if (time_in_memory < 0) then
--obj:enable_memory_object(enemy,false)
--time_in_memory = time_in_memory + load_var(obj,"mem_time_offset",0)
if (ene_id == 0 and time_in_memory < rnd*(-1) or time_in_memory < rnd2*(-1)) then
return false
end
else
if (ene_id == AC_ID and time_in_memory>rnd or time_in_memory>rnd2) or (enemy:position():distance_to_sqr(obj:position())>dist_rnd and not enemy:see(obj)) then
--obj:enable_memory_object(enemy,false)
--save_var(obj,"mem_time_offset",tg)
return false
end
end
end
end
-- NPC was hit by actor always return true. It's for keep_when_attacked condition
if (ene_id == AC_ID and load_var(obj,"xr_combat_ignore_enabled",true) == false) then
st.enemy_id = ene_id
return true
end
if (obj:has_info("npcx_is_companion") or enemy:has_info("npcx_is_companion")) then
-- If companion is in stealth mode then ignore combat greater than 30m unless enemy is in combat with actor
if (obj:has_info("npcx_beh_substate_stealth") or enemy:has_info("npcx_beh_substate_stealth")) then
if not (fighting_with_actor_npcs[id] or fighting_with_actor_npcs[ene_id]) then
if (enemy:position():distance_to_sqr(obj:position()) < 900) then
return false
end
end
end
else
local is_actor = (ene_id == AC_ID)
-- Ignore enemies during surge, only if npc has a surge job at a smart_terrain and the enemy is greater then 25m (actor 100m) away from the npc's surge job's alife_task position
if (xr_conditions.surge_started) then
local smart = xr_gulag.get_npc_smart(obj)
local npc_info = smart and smart.npc_info and smart.npc_info[id]
if (npc_info) then
local npc_job = npc_info.job
if (npc_job and npc_job.job_type_id == 2) then
local alife_task = npc_job.alife_task
if (alife_task) then
if (is_actor and enemy:position():distance_to_sqr(alife_task:position()) > 10000) then
return false
elseif (enemy:position():distance_to_sqr(alife_task:position()) > 625) then
return false
end
end
end
end
end
local sim = alife()
local is_monster = character_community(enemy) == "zombied" or IsMonster(enemy)
-- Ignore enemies with the safe_zone flag
if (is_actor ~= true) then
-- ignore hostages
if (axr_task_manager.hostages_by_id[ene_id]) then
return true
end
if (obj_is_stalker and not is_monster) then
local se_obj = sim:object(id)
if not (se_obj) then
return false
end
local cid = se_obj.group_id and se_obj.group_id ~= 65535 and se_obj.group_id or id
local tg = time_global()
if (safe_zone_npcs[cid]) then
db.storage[id].heli_enemy_flag = nil
if (tg < safe_zone_npcs[cid] + 8000) then
return false
end
safe_zone_npcs[cid] = nil
else
for i,v in ipairs(ignored_zone) do
if (utils_obj.npc_in_zone(obj, v)) then
safe_zone_npcs[cid] = tg
return false
end
end
end
local ene_squad = get_object_squad(enemy)
local bid = ene_squad and ene_squad.id or ene_id
if (safe_zone_npcs[bid]) then
return false
else
for i,v in ipairs(ignored_zone) do
if (utils_obj.npc_in_zone(enemy, v)) then
safe_zone_npcs[bid] = tg
return false
end
end
end
end
end
--Ignore by distance; mainly unrestricted for all non-story stalker vs. stalker combat
local pos1 = obj:position()
local pos2 = enemy:position()
local comm = character_community(obj)
local ene_comm = character_community(enemy)
if (comm == "army" and ene_comm == "stalker") or (comm == "stalker" and ene_comm == "army") then
-- In Cordon prevent stalker v military combat with distances greater than 60m
if (level.name() == "l01_escape" and pos1:distance_to_sqr(pos2) >= 3600) then
return false
end
end
if (is_actor) then
if (level.get_time_hours() < 4 or level.get_time_hours() > 19 or level.rain_factor() >= 0.4) and (pos1:distance_to_sqr(pos2) > 10000) then
-- limit combat range to 100m during night hours or heavy rain for stalker v actor
return false
end
elseif (is_monster) then
if (math.abs(pos1.y-pos2.y) > 30) then
-- prevent stalker from attack mutants under ground or above ground
return false
end
if (pos1:distance_to_sqr(pos2) > 4900) then
-- ignore monsters 70m or greater
return false
end
else
local dist = pos1:distance_to_sqr(pos2)
if (dist > 10000) then -- 100m max combat range for stalker v stalker
return false
end
if (dist > 5625) and (level.rain_factor() > 0 or level.get_time_hours() < 4 or level.get_time_hours() > 19) then
-- limit combat range to 75m during night hours for stalker v stalker or rain
return false
end
if (dist > 3600) then
-- limit combat range to 60m during heavy rain, surges or if they are story related
if (xr_conditions.surge_started() or level.rain_factor() >= 0.5 or get_object_story_id(id) or get_object_story_id(ene_id)) then
return false
end
local squad = get_object_squad(obj)
local ene_squad = get_object_squad(enemy)
if (squad and get_object_story_id(squad.id) or ene_squad and get_object_story_id(ene_squad.id)) then
return false
end
end
end
end
-- Store Pure enemy (Enemy without overrides)
if (obj:relation(enemy) >= game_object.enemy) then
st.enemy_id = ene_id
end
-- Ignore enemies by overrides
if (ignore_enemy_by_overrides(obj,enemy)) then
return false
end
return true
end
function ignore_enemy_by_overrides(obj,enemy,no_check_job)
if not (enemy) then
return true
end
if (IsStalker(obj)) then
if (enemy:section() == "mar_smart_terrain_doc_dog" or enemy:section() == "mar_smart_terrain_base_dog_doctor") then
return true
end
end
local id = obj:id()
local ene_id = enemy:id()
local st = db.storage[id] and db.storage[id].overrides
if (st) then
-- combat_ignore_cond from custom data logic
local ignore = st and st.combat_ignore and xr_logic.pick_section_from_condlist(enemy, obj, st.combat_ignore.condlist)
if (ignore == "true") then
--obj:enable_memory_object(enemy,false)
return true
-- Ignore enemies because of no_combat_job
elseif (no_check_job ~= true) and (st and st.no_combat_job and xr_logic.pick_section_from_condlist(enemy, obj, st.no_combat_job.condlist) == "true") then
return true
end
end
-- enemy_ignore_cond override from custom data logic
-- if this is true then npc will IGNORE combat with this specific enemy
local ene_st = db.storage[ene_id] and db.storage[ene_id].overrides
if (ene_st) then
ignore = ene_st.enemy_ignore and xr_logic.pick_section_from_condlist(enemy, obj, ene_st.enemy_ignore.condlist)
if (ignore == "true") then
--obj:enable_memory_object(enemy,false)
return true
end
end
return false
end
function npc_in_safe_zone(npc)
if (npc:id() == AC_ID) then
return false
end
local squad = get_object_squad(npc)
if (squad and safe_zone_npcs[squad.id]) or (safe_zone_npcs[id]) then
return true
end
return false
end
----------------------------------------------------------------------------------------------------------------------
class "action_process_enemy"
function action_process_enemy:__init( obj, storage )
--printf("action_process_enemy init: %s", obj:name())
self.object = obj
self.st = storage
end
function action_process_enemy:trader_enemy_callback(obj,enemy)
return false
end
function action_process_enemy:enemy_callback(npc, enemy)
--utils_data.debug_write(strformat("action_process_enemy:enemy_callback"))
local id = npc:id()
local ene_id = enemy:id()
--printf("action_process_enemy enemy_callback: %s, %s", npc:name(), enemy:name())
if (is_enemy(npc,enemy)) then
--printf("!action_process_enemy enemy_callback: enemy detected")
--utils_data.debug_write(strformat("action_process_enemy:enemy_callback is_enemy true"))
-- keep track of actor enemies
if (ene_id == AC_ID and not fighting_with_actor_npcs[id]) then
fighting_with_actor_npcs[id] = true
SendScriptCallback("npc_on_fighting_actor",npc)
end
-- Set smart alarm (unused in coc)
--[[
local sim = alife()
local se_obj = sim:object(id)
if (se_obj and se_obj.m_smart_terrain_id ~= 65535) then
local smart = sim:object(se_obj.m_smart_terrain_id)
if (smart) then
smart:set_alarm()
end
end
--]]
return true
end
db.storage[id].enemy_id = nil
if (ene_id == AC_ID) then
fighting_with_actor_npcs[id] = nil
save_var(npc,"xr_combat_ignore_enabled",nil)
end
--utils_data.debug_write(strformat("action_process_enemy:enemy_callback is_enemy false"))
--printf("-action_process_enemy enemy_callback: no enemy")
return false
end
function action_process_enemy:hit_callback(obj, amount, local_direction, who, bone_index)
if (load_var(obj,"xr_combat_ignore_enabled") == false) then
return
end
if (amount > 0 and who and who:id() == AC_ID) then
local sim = alife()
local se_obj = sim:object(obj:id())
if (se_obj) then
local squad = se_obj.group_id and se_obj.group_id ~= 65535 and sim:object(se_obj.group_id)
if (squad) then
-- disable combat ignore for entire squad if attacked by actor
for k in squad:squad_members() do
local st = db.storage[k.id]
if (st) then
if (st.overrides == nil or st.overrides.combat_ignore_keep_when_attacked ~= true) then
--st.combat_ignore.enabled = false
save_var(st.object,"xr_combat_ignore_enabled",false)
end
end
end
else
local overrides = db.storage[obj:id()] and db.storage[obj:id()].overrides
if not overrides or not overrides.combat_ignore_keep_when_attacked then
--self.st.enabled = false
save_var(obj,"xr_combat_ignore_enabled",false)
end
end
end
end
end
----------------------------------------------------------------------------------------------------------------------
-- binder
----------------------------------------------------------------------------------------------------------------------
function setup_generic_scheme(npc,ini,scheme,section,stype,temp)
local st = xr_logic.assign_storage_and_bind(npc,ini,"combat_ignore",section,temp)
end
function add_to_binder(npc,ini,scheme,section,st,temp)
st.action = action_process_enemy(npc,st)
end
function reset_generic_scheme(npc,scheme,section,stype,st)
local storage = st.combat_ignore
if not (storage) then
printf("xr_combat_ignore: reset_generic_scheme storage is nil! npc=%s scheme=%s section=%s",npc and npc:name(),scheme,section)
return
end
npc:set_enemy_callback(storage.action.enemy_callback,storage.action)
xr_logic.subscribe_action_for_events(npc, storage, storage.action)
--storage.enabled = true
end
function disable_generic_scheme(npc,scheme,stype)
npc:set_enemy_callback()
local st = db.storage[npc:id()][scheme]
if st then
xr_logic.unsubscribe_action_from_events(npc, st, st.action)
end
end