238 lines
7.0 KiB
Plaintext
238 lines
7.0 KiB
Plaintext
--[[
|
|
=====================================================================
|
|
Addon : Neutral Zone
|
|
Link : https://www.moddb.com/mods/stalker-anomaly/addons/neutral-zone
|
|
Author : party_50
|
|
Date : 07.02.2024
|
|
Last Edit : 09.02.2024
|
|
=====================================================================
|
|
--]]
|
|
|
|
|
|
local NEUTRAL_ZONES = {}
|
|
|
|
local warning_sent = {}
|
|
local crime_timer = {}
|
|
local warning_timer = 0
|
|
|
|
|
|
function on_option_change()
|
|
NEUTRAL_ZONES = {}
|
|
if z_neutral_zone_mcm.get_config("yantar_bunker") then
|
|
table.insert(NEUTRAL_ZONES, {
|
|
level_name = "l08_yantar",
|
|
smart_name = "yan_smart_terrain_6_4",
|
|
radius = 100,
|
|
faction = "ecolog",
|
|
cooldown = 8,
|
|
warn = true,
|
|
warn_sender = "yan_stalker_sakharov",
|
|
warn_text = "st_yan_sakharov_neutral_zone_warning"
|
|
})
|
|
end
|
|
if z_neutral_zone_mcm.get_config("jupiter_bunker") then
|
|
table.insert(NEUTRAL_ZONES, {
|
|
level_name = "jupiter",
|
|
smart_name = "jup_b41",
|
|
radius = 50,
|
|
faction = "ecolog",
|
|
cooldown = 8
|
|
})
|
|
end
|
|
if z_neutral_zone_mcm.get_config("rostok") then
|
|
table.insert(NEUTRAL_ZONES, {
|
|
level_name = "l05_bar",
|
|
smart_name = "bar_dolg_bunker",
|
|
radius = 160,
|
|
faction = "dolg",
|
|
cooldown = 8,
|
|
warn = true,
|
|
warn_sender = "bar_duty_security_squad_leader",
|
|
warn_text = "st_bar_duty_neutral_zone_warning"
|
|
})
|
|
end
|
|
if z_neutral_zone_mcm.get_config("yanov") then
|
|
table.insert(NEUTRAL_ZONES, {
|
|
level_name = "jupiter",
|
|
smart_name = "jup_a6",
|
|
radius = 50,
|
|
faction = "freedom",
|
|
cooldown = 8
|
|
})
|
|
end
|
|
end
|
|
|
|
function are_npc_enemies_to_faction(npc1, npc2, faction)
|
|
return game_relations.is_factions_enemies(character_community(npc1), faction)
|
|
or game_relations.is_factions_enemies(character_community(npc2), faction)
|
|
end
|
|
|
|
function are_npc_within_neutral_zone(npc1, npc2, zone)
|
|
local smart = SIMBOARD.smarts_by_names[zone.smart_name]
|
|
local npc1_dist = smart.position:distance_to(npc1:position());
|
|
local npc2_dist = smart.position:distance_to(npc2:position());
|
|
|
|
return npc1_dist < zone.radius or npc2_dist < zone.radius
|
|
end
|
|
|
|
function npc_on_before_hit(npc, shit, bone_id, flags)
|
|
if shit.draftsman:id() ~= db.actor:id() then
|
|
return
|
|
end
|
|
|
|
if character_community(npc) == "arena_enemy" then
|
|
return
|
|
end
|
|
|
|
for _, zone in ipairs(NEUTRAL_ZONES) do
|
|
if level.name() ~= zone.level_name then
|
|
goto continue
|
|
end
|
|
if are_npc_enemies_to_faction(npc, shit.draftsman, zone.faction) then
|
|
goto continue
|
|
end
|
|
|
|
if are_npc_within_neutral_zone(npc, shit.draftsman, zone) then
|
|
crime_timer[zone.smart_name] = game.get_game_time()
|
|
end
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
function on_enemy_eval(obj, enemy, flags)
|
|
if character_community(obj) == "arena_enemy" then
|
|
return
|
|
end
|
|
|
|
for _, zone in ipairs(NEUTRAL_ZONES) do
|
|
if level.name() ~= zone.level_name then
|
|
goto continue
|
|
end
|
|
if are_npc_enemies_to_faction(obj, enemy, zone.faction) then
|
|
goto continue
|
|
end
|
|
|
|
if enemy:id() == db.actor:id() and crime_timer[zone.smart_name] then
|
|
if game.get_game_time():diffSec(crime_timer[zone.smart_name]) < 60 * 60 * zone.cooldown then
|
|
if are_npc_within_neutral_zone(obj, enemy, zone) then
|
|
crime_timer[zone.smart_name] = game.get_game_time()
|
|
end
|
|
goto continue
|
|
else
|
|
crime_timer[zone.smart_name] = nil
|
|
end
|
|
end
|
|
|
|
if are_npc_within_neutral_zone(obj, enemy, zone) then
|
|
flags.override = true
|
|
flags.result = false
|
|
end
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
gameplay_disguise.expose_actor = function(npc, comm, is_enem)
|
|
gameplay_disguise.set_msg("st_disguse_actor_exposed",gameplay_disguise.default_comm)
|
|
gameplay_disguise.set_comm(gameplay_disguise.default_comm)
|
|
|
|
-- News
|
|
if npc and (not gameplay_disguise.inside_disguise_zone()) then
|
|
local str = ""
|
|
local flags = {}
|
|
on_enemy_eval(npc, db.actor, flags)
|
|
if is_enem and flags.result ~= false then
|
|
str = "st_news_disguse_enemy_expose_"
|
|
else
|
|
str = "st_news_disguse_natural_expose_"
|
|
if (comm ~= gameplay_disguise.default_comm) then
|
|
if (ui_options.get("alife/general/dynamic_relations") == true)
|
|
and (not game_relations.is_faction_unaffected(comm))
|
|
and (not game_relations.is_faction_unaffected(gameplay_disguise.default_comm))
|
|
and (not game_relations.is_faction_pair_unaffected(comm, gameplay_disguise.default_comm))
|
|
then
|
|
game_relations.change_faction_relations(comm, gameplay_disguise.default_comm, gameplay_disguise.relation_hit)
|
|
end
|
|
game_relations.change_factions_community_num(comm, gameplay_disguise.default_comm, gameplay_disguise.goodwill_hit)
|
|
game_statistics.increment_reputation(gameplay_disguise.reputation_hit)
|
|
end
|
|
end
|
|
local msg = game.translate_string(str .. tostring(math.random(1,4)))
|
|
local comm_name = ", " .. game.translate_string("st_dyn_news_comm_" .. comm .. "_6")
|
|
local se = strformat("%s%s", npc:character_name(), string.find(comm_name,"_6") and comm_name or "")
|
|
dynamic_news_helper.send_tip(msg,se,2,nil,npc:character_icon(),nil,"npc")
|
|
end
|
|
|
|
-- long-term memory
|
|
for k,v in pairs(gameplay_disguise.npcs_memory) do
|
|
if v then
|
|
v.memo = v.memo * gameplay_disguise.memory_multi
|
|
end
|
|
end
|
|
|
|
gameplay_disguise.is_disguised = nil
|
|
end
|
|
|
|
function send_warning(sender, text)
|
|
local se_npc = get_story_se_object(sender)
|
|
if se_npc and se_npc:alive() then
|
|
local msg = game.translate_string(text)
|
|
dynamic_news_helper.send_tip(msg, se_npc:character_name(), 0, 10, se_npc:character_icon(), "news", "npc")
|
|
end
|
|
end
|
|
|
|
function actor_on_update()
|
|
local tg = time_global()
|
|
if warning_timer > tg then
|
|
return
|
|
end
|
|
warning_timer = tg + 1000
|
|
|
|
-- Following code is executed once per second
|
|
|
|
for _, zone in ipairs(NEUTRAL_ZONES) do
|
|
if level.name() ~= zone.level_name then
|
|
goto continue
|
|
end
|
|
if warning_sent[zone.smart_name] or not zone.warn then
|
|
goto continue
|
|
end
|
|
|
|
local smart = SIMBOARD.smarts_by_names[zone.smart_name]
|
|
local dist = smart.position:distance_to(db.actor:position())
|
|
|
|
if dist > zone.radius and dist < zone.radius + 50 then
|
|
local actor_community = character_community(db.actor)
|
|
if not game_relations.is_factions_enemies(actor_community, zone.faction) and actor_community ~= "actor_" .. zone.faction then
|
|
send_warning(zone.warn_sender, zone.warn_text)
|
|
end
|
|
warning_sent[zone.smart_name] = true
|
|
elseif dist < zone.radius then
|
|
warning_sent[zone.smart_name] = true
|
|
end
|
|
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
function save_state(m_data)
|
|
m_data.neutral_zone_warning_sent = warning_sent
|
|
m_data.neutral_zone_crime_timer = crime_timer
|
|
end
|
|
|
|
function load_state(m_data)
|
|
warning_sent = type(m_data.neutral_zone_warning_sent) == "table" and m_data.neutral_zone_warning_sent or {}
|
|
crime_timer = type(m_data.neutral_zone_crime_timer) == "table" and m_data.neutral_zone_crime_timer or {}
|
|
end
|
|
|
|
function on_game_start()
|
|
on_option_change()
|
|
RegisterScriptCallback("on_option_change", on_option_change)
|
|
RegisterScriptCallback("npc_on_before_hit", npc_on_before_hit)
|
|
RegisterScriptCallback("on_enemy_eval", on_enemy_eval)
|
|
RegisterScriptCallback("actor_on_update", actor_on_update)
|
|
RegisterScriptCallback("save_state", save_state)
|
|
RegisterScriptCallback("load_state", load_state)
|
|
end
|