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

481 lines
15 KiB
Plaintext
Raw Permalink Normal View History

2024-03-17 20:18:03 -04:00
-- Visual Memory Manager exports
-- by Alundaio
-- called from engine
-- This occurs during the visible check. If value >= visiblity_threshold then object is considered visible
-- warning npc and who can be nil sometimes
local jacket_t = {
["o_medium"] = 2,
["o_sci"] = 2,
["o_light"] = 1.35,
["o_heavy"] = 4.5,
}
local jacketmult = 1
local summ = 0
local renderer = get_console_cmd(0, "renderer")
local is_r1 = (renderer == "renderer_r1")
local correct_game_ver = GAME_VERSION and string.find(GAME_VERSION, "1.5.2")
local campfire_npcs = {}
local muzzle_t = {}
local get_camo_k = 0
local vision_memory = {}
stealth_light_ind = { 0, false }
local ind_t = {}
function get_visible_value(npc,who,time_delta,time_quant,luminocity,velocity_factor,velocity,distance,object_distance,always_visible_distance)
distance = distance <= 0 and 0.00001 or distance
local function obj_is_alive(obj)
return (IsStalker(obj) or IsMonster(obj)) and obj:alive()
end
local npc_fits = npc and obj_is_alive(npc)
local who_fits = who and obj_is_alive(who)
if not (npc_fits and who_fits) then
local def_lum = luminocity <= 0 and 0.0001 or luminocity
local def_step_incr = time_delta / time_quant * def_lum * (1 + velocity_factor * velocity) * (distance - object_distance) / distance
return def_step_incr
end
luminocity = get_luminocity_mult(npc, who, luminocity, object_distance, distance)
local lum_dist = (distance - object_distance / 1.35) / distance
local Luminocity = (33 * luminocity * lum_dist) * stealth_mcm.get_config("luminocity")
local Velocity = (1 + velocity_factor * velocity) * stealth_mcm.get_config("velocity")
local eq_dist = ((distance - object_distance / 1.15) / distance) * stealth_mcm.get_config("distance")
local camo_x = get_camo_mult(velocity, object_distance, who) -- actor only
local weight = get_weight_mult(object_distance, who) -- actor only
local crouch = get_crouch_mult(who)
local danger_mult = get_danger_mult(npc, time_quant)
local muzzle_flash = muzzle_t[who:id()] or 0
local scheme_mult = get_scheme_mult(npc)
local memory_factor = get_memory_val(npc, who)
local nvg_factor = get_nvg_val(npc)
--============== total =============================
local step_incr = Luminocity * Velocity * eq_dist * camo_x * weight * crouch * danger_mult * scheme_mult + muzzle_flash + memory_factor + nvg_factor
-------------------------------------------------------------------------
-- alt icon
stealth_icon_alt(npc, who, step_incr)
-- dbg
stealth_dbg(npc, who, step_incr, Luminocity, luminocity, Velocity, distance, weight, crouch, danger_mult, camo_x, scheme_mult, memory_factor, eq_dist, nvg_factor)
-------------------------------------------------------------------------
return step_incr
end
------- Luminocity
-- main
function lights_lum()
local function andruxa(animegif)
return 0.21 * animegif.x + 0.72 * animegif.y + 0.07 * animegif.z
end
local andsun = andruxa(weather.get_value_vector("sun_color"))
local andhem = andruxa(weather.get_value_vector("hemisphere_color"))
local andlum = math.max(andhem, andsun)
return andlum
end
function get_luminocity_mult(npc, who, luminocity, object_distance, distance)
local michiko_patch = stealth_mcm.get_config("michiko_patch")
local hrs = level.get_time_hours() + level.get_time_minutes() / 60
local who_lum = who:get_luminocity()
-- dx8
local lumin_r1 = luminocity <= 0 and 0.0001 or luminocity
if is_r1 then
if level_weathers.bLevelUnderground then
return lumin_r1 + 0.35
end
return lumin_r1
end
-- colors, time, lum
local lumin_r2 = (who_lum + who_lum^0.5 + lights_lum()) / 3.02
local is_night = (hrs > 21 or hrs < 4)
local lumin_night = clamp((lumin_r2 * 2)^1.6, 0.01, 0.99)
if michiko_patch then
is_night = (hrs > 21 or hrs < 5)
lumin_night = clamp((lumin_r2 * 7)^3, 0.01, 0.99)
end
-- campfire / underground
lumin_r2 = lumin_r2 + (campfire_npcs[who:id()] or 0)
if is_night then
lumin_r2 = lumin_night
end
if level_weathers.bLevelUnderground then
lumin_r2 = lumin_r2 + 0.35
end
-- flashlights
local torch = db.actor:item_in_slot(10)
local flash = db.actor:item_in_slot(9)
local npc_torch = IsStalker(npc) and npc:object("device_torch")
local who_torch = IsStalker(who) and who:object("device_torch")
local who_dist = 20 / object_distance
local npc_dist = 10 / object_distance
local actor_torch_active = (who:id() == db.actor:id()) and ( (torch and torch:torch_enabled()) or (flash and (flash:section() == "device_flashlight") and db.actor:active_detector()) )
local npc_to_npc_torch_active = who_torch and who_torch:attachable_item_enabled()
local npc_torch_active = npc_torch and npc_torch:attachable_item_enabled() and (object_distance <= 25 and distance > 90)
if actor_torch_active or npc_to_npc_torch_active then
lumin_r2 = lumin_r2 + who_dist
elseif npc_torch_active then
lumin_r2 = lumin_r2 + npc_dist
end
return lumin_r2
end
-- campfires
local cf_tmr = 0
function set_campfire_val()
local tg = time_global()
if tg < cf_tmr then return end
cf_tmr = tg + 1000
empty_table(campfire_npcs)
for id, binder in pairs(bind_campfire.campfires_all) do
if (binder and binder.campfire and binder.campfire:is_on()) then
level.iterate_nearest(binder.object:position(), 7, function(who)
if (who and (IsStalker(who) or IsMonster(who)) and who.alive and who:alive()) then
campfire_npcs[who:id()] = 0.4
end
end)
end
end
end
-- icon
function icon_lum()
local act_lum = db.actor:get_luminocity()
local dblum = (act_lum + act_lum^0.5 + lights_lum()) / 3 + (campfire_npcs[AC_ID] or 0)
local luminocity_icon = is_r1 and (act_lum)^0.7 or dblum
return luminocity_icon
end
-- alt icon
function stealth_icon_alt(npc, who, incr)
if who:id() ~= db.actor:id() then return end
local is_enemy_to_ac = game_relations.get_npcs_relation(npc, who) == game_object.enemy
if not (is_enemy_to_ac) then return end
local in_danger = (db.storage[npc:id()] and db.storage[npc:id()].danger_flag) or (npc:best_enemy() and true) or false
local vis_t = npc:visibility_threshold()
ind_t[npc:id()] = ind_t[npc:id()] or {}
ind_t[npc:id()].val = ind_t[npc:id()].val or 0
ind_t[npc:id()].norm = ind_t[npc:id()].norm or false
ind_t[npc:id()].danger = in_danger
if (npc:see(who)) then
ind_t[npc:id()].val = 1
else
incr = normalize(incr, 0, vis_t)
if not (ind_t[npc:id()].norm) then
ind_t[npc:id()].val = normalize(ind_t[npc:id()].val, 0, vis_t)
ind_t[npc:id()].norm = true
end
ind_t[npc:id()].val = (ind_t[npc:id()].val < 1) and ind_t[npc:id()].val + incr or 1
end
local function del_elem(te_id)
ind_t[te_id] = nil
return true
end
ResetTimeEvent("stealth_hud_e" .. npc:id(), "stealth_hud_a" .. npc:id(), 1)
CreateTimeEvent("stealth_hud_e" .. npc:id(), "stealth_hud_a" .. npc:id(), 1, del_elem, npc:id())
end
function update_icon()
local max_id = next(ind_t)
local max_val = ind_t[max_id] and ind_t[max_id].val or 0
for id, t in pairs(ind_t) do
if t.val > max_val then
max_id, max_val = id, t.val
end
end
stealth_light_ind[1] = max_val
stealth_light_ind[2] = ind_t[max_id] and ind_t[max_id].danger or false
end
--------------------------------------------------------------------------------
------- Camo and weight
function actor_on_first_update()
local outfit = db.actor:item_in_slot(7)
if not (outfit) then return end
for i = 1, 13 do
local obj = db.actor:item_in_slot(i)
slot_in_out(obj)
end
end
function slot_in_out(obj)
if not IsOutfit(obj) then return end
local outfit = db.actor:item_in_slot(7)
local kind = outfit and ini_sys:r_string_ex(outfit:section(),"kind")
local get_camo_get_line = outfit and ini_sys:line_exist(outfit:section(), "npc_blindness_koeff")
get_camo_k = get_camo_get_line and ini_sys:r_float(outfit:section(), "npc_blindness_koeff") or 0
jacketmult = kind and jacket_t[kind] or 1
end
function get_camo_mult(velocity, object_distance, who)
local camo_mult = 1
local camo_checks = get_camo_k > 0 and velocity == 0 and object_distance >= 30
if (who:id() ~= AC_ID) or (not camo_checks) then
return camo_mult
end
local camo_dist = 1 - object_distance * 0.01
local camo_mult = get_camo_k / 20 * camo_dist
camo_mult = clamp(camo_mult, 0.02, 1.0)
return camo_mult
end
function get_weight_mult(object_distance, who)
if (who:id() ~= AC_ID) then return 1 end
local actor_weight = db.actor:get_total_weight()
local weight_factor = math.exp(actor_weight / 100)
local dist = 1.1 - 0.02 * object_distance
local weight_mult = weight_factor * jacketmult * dist * stealth_mcm.get_config("weight")
weight_mult = weight_mult > 1 and weight_mult or 1
return weight_mult
end
--------------------------------------------------------------------------------
------- Body state / Danger / Scheme / Memory / Muzzle flash
-- body state
function get_crouch_mult(who)
local crouch_mult = 1
local crouch_state = IsMoveState('mcCrouch')
local accel_state = IsMoveState('mcAccel')
if who:id() == AC_ID then
if crouch_state then
crouch_mult = accel_state and stealth_mcm.get_config("low_crouch") or stealth_mcm.get_config("crouch")
end
elseif IsStalker(who) and who:body_state() == move.crouch then
crouch_mult = stealth_mcm.get_config("crouch")
end
return crouch_mult
end
-- danger
function get_danger_mult(npc, time_quant)
local danger_mult = 1
if IsMonster(npc) then
danger_mult = 0.003 / time_quant
elseif IsStalker(npc) then
danger_mult = time_quant * 2
end
return danger_mult
end
-- scheme
function get_scheme_mult(npc)
local scheme_mult = 1
local st = db.storage[npc:id()]
if st and ((st.active_scheme == "guard") or (st.active_scheme == "sniper")) then
scheme_mult = 0.25
end
return scheme_mult
end
-- memory
function get_memory_val(npc, who)
local tg = time_global()
local memory_val = 0
local mcm_memory = stealth_mcm.get_config("memory")
local who_fits = IsStalker(who) or IsMonster(who)
if not (who_fits) then
return
end
local see = npc:see(who)
local vis_memory_t = vision_memory[npc:id()] and vision_memory[npc:id()][who:id()] or nil
if (see) and (not (vis_memory_t)) then
vision_memory[npc:id()] = {}
vision_memory[npc:id()][who:id()] = true
elseif (not (see)) and (vis_memory_t) then
local mem_time = (tg - npc:memory_time(who)) * 0.001
local mem_time_factor = 7.5 * (1 / mem_time)^2
local vis_threshold = npc:visibility_threshold()
memory_val = mcm_memory * mem_time_factor * vis_threshold -- 35 threshold: 100 sec = 0.025 ; 80 sec = 0.04 ; 60 sec = 0.08 ; 40 sec = 0.17 ; 20 sec = 0.66 ; 10 sec = 2.6
end
memory_val = memory_val > 0.01 and memory_val or 0
return memory_val
end
-- nvg
function get_nvg_val(npc)
local nvg_val = stealth_mcm.get_config("nvg_val")
local hrs = level.get_time_hours() + level.get_time_minutes() / 60
local is_night = (hrs > 21 or hrs < 5)
if not (nvg_val > 0 and is_night and stealth_nvg.stealth_nvg_ps[npc:id()]) then
return 0
end
return nvg_val
end
-- muzzle flash
if correct_game_ver then
AddScriptCallback("npc_on_weapon_fired")
function xr_motivator.motivator_binder:weapon_fired()
SendScriptCallback("npc_on_weapon_fired", self.object, self.object:active_item())
end
end
function actor_on_weapon_fired()
local wpn = db.actor:active_item()
if not (wpn) then return end
if not (IsWeapon(wpn)) or IsMelee(wpn) or wpn:weapon_is_silencer() then return end
muzzle_t[db.actor:id()] = 7
local function wpn_fired_stop(id)
muzzle_t[id] = 0
return true
end
CreateTimeEvent("stealth_wpn_fired_e_" .. db.actor:id(), "stealth_wpn_fired_a_" .. db.actor:id(), 0.2, wpn_fired_stop, db.actor:id())
end
function npc_on_net_spawn(npc)
if not (npc and IsStalker(npc) and npc.alive and npc:alive()) then return end
local binder = npc.binded_object and npc:binded_object()
if not binder then return end
npc:set_callback(callback.weapon_fired, binder.weapon_fired, binder)
end
function npc_on_weapon_fired(npc, wpn)
if not (wpn) then return end
if not (IsWeapon(wpn)) or IsMelee(wpn) or wpn:weapon_is_silencer() then return end
if not (npc and IsStalker(npc) and npc.alive and npc:alive()) then return end
muzzle_t[npc:id()] = 7
local function wpn_fired_stop(id)
muzzle_t[id] = 0
return true
end
CreateTimeEvent("stealth_wpn_fired_e_" .. npc:id(), "stealth_wpn_fired_a_" .. npc:id(), 0.2, wpn_fired_stop, npc:id())
end
--------------------------------------------------------------------------------
------- Debug
function stealth_dbg(npc, who, step_incr, Luminocity, luminocity, Velocity, distance, weight, crouch, danger_mult, camo_x, scheme_mult, memory_factor, eq_dist, nvg_factor)
if not (stealth_mcm.get_config("debugx")) then return end
local wthrs = level_weathers.get_weather_manager():get_curr_weather_preset()
local obj = level.get_target_obj()
local obj_fits = obj and (IsStalker(obj) or IsMonster(obj)) and obj.alive and obj:alive()
if obj_fits then
if (npc and npc:id() == obj:id()) and (who and who:id() == 0) and (not obj:see(db.actor)) then
summ = summ + step_incr
elseif obj:see(db.actor) then
summ = 0
news_manager.send_tip(db.actor, "Spotted", 0, nil, 1000)
end
end
local function r(val)
return round_idp(val, 2)
end
local function pr(var, val, ...)
if var ~= val then
printf(..., var)
end
end
if obj_fits and (npc and npc:id() == obj:id()) and (who and who:id() == 0) then
printf('---------------------------------------------------------------')
printf("Weather is: " .. (wthrs))
printf("1. Luminocity ---------- %s", Luminocity)
printf("1.a) luminocity --------- %s", luminocity)
printf("1.b) lights_lum() ------- %s", lights_lum())
printf("1.c) who:get_lum() ---- %s", who:get_luminocity())
pr(Velocity, 1, ("2. Velocity ------------- %s"))
printf("3. dist_original --------- %s", distance)
pr(weight, 1, ("4. weight&outfit ------- %s"))
pr(crouch, 1, ("5. crouch ------------- %s"))
pr(muzzle_t[who:id()] or 0, 0, ("6.a) shot_mult --------- %s"))
pr(danger_mult, 1, ("6.b) danger ------------ %s"))
pr(camo_x, 1, ("6.c) camo ------------- %s"))
pr(scheme_mult, 1, ("6.d) ai scheme --------- %s"))
pr(get_camo_k, 0, ("6.e) get_camo_k ------ %s"))
pr(jacketmult, 1, ("6.f) jacketmult --------- %s"))
pr(memory_factor, 0, ("7. memory ------------- %s"))
pr(nvg_factor, 0, ("8. NVG ------------- %s"))
printf("8. Step incr --- %s", step_incr)
actor_menu.set_msg(1, strformat("Threshold: %s/%s || (Lumin: %s | Veloc: %s | Dist: %s | Weight: %s | Memory: %s || Total per update: %s)", r(summ), obj:visibility_threshold(), r(Luminocity), r(Velocity), r(eq_dist), r(weight), r(memory_factor), r(step_incr) ))
end
if obj == nil then
summ = 0
end
end
--===================================
function on_game_start()
RegisterScriptCallback("actor_on_update", set_campfire_val)
RegisterScriptCallback("actor_on_update", update_icon)
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
RegisterScriptCallback("actor_item_to_slot", slot_in_out)
RegisterScriptCallback("actor_item_to_ruck", slot_in_out)
RegisterScriptCallback("actor_on_item_drop", slot_in_out)
RegisterScriptCallback("actor_on_weapon_fired", actor_on_weapon_fired)
if correct_game_ver then
RegisterScriptCallback("npc_on_net_spawn", npc_on_net_spawn)
RegisterScriptCallback("npc_on_weapon_fired", npc_on_weapon_fired)
end
end