Divergent/mods/Selfkill/gamedata/scripts/selfkill.script

458 lines
12 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
--Used demonized's code
under_suicide = false
local wct = weapon_cover_tilt
local dte = demonized_time_events
local keybind = DIK_keys.DIK_T --change this to your NV Toggle Keybind if you don't use MCM
-- Params
local max_deg = 100 -- Max deg of gun Y rotation
local max_coeff = max_deg / 75 -- Max value of coeff, 75 is WCT default
local coeff = 0 -- Progress of suicide animation in terms of 0 to max_coeff
local normalized_coeff = 0 -- Normalized coeff from 0 to 1
local up_increment = 0.0005 * max_coeff -- Increment of coeff --0.00018
local down_increment = 0.00027 * max_coeff -- Decrement of coeff
local detector_hidden = false
local default_smoothing = 11.5
local smoothed_values = {}
local forced = false
local function get_value(t)
return type(t) == "table" and dup_table(t) or t
end
local function array_keys(t, sorted, sort_func)
local res = {}
local res_count = 1
for k, v in pairs(t) do
res[res_count] = k
res_count = res_count + 1
end
if sorted then
if sort_func then
table.sort(res, sort_func)
else
table.sort(res)
end
end
return res
end
local get_safe_sound_object = xr_sound.get_safe_sound_object
local function bisect_left(a, x, lo, hi)
local lo = lo or 1
local hi = hi or #a
if lo < 0 then
return
end
while lo < hi do
local mid = math.floor((lo + hi) * 0.5)
if a[mid] < x then
lo = mid+1
else
hi = mid
end
end
return lo
end
local function lookup(t, key, tkeys)
if is_empty(t) then return 0 end
if not tkeys then
local tkeys = array_keys(t, true)
end
local tkeys_len = #tkeys
if key <= tkeys[1] then return get_value(t[tkeys[1]]) end
if key >= tkeys[tkeys_len] then return get_value(t[tkeys[tkeys_len]]) end
local where = bisect_left(tkeys, key)
local lo = tkeys[where-1] or tkeys[where]
local hi = tkeys[where]
if lo == hi then return get_value(t[lo]) end
local delta = (key - lo) / (hi - lo)
if type(t[lo]) ~= "table" then
local res = delta * t[hi] + (1 - delta) * t[lo]
return res
else
local res = {}
for i = 1, #t[lo] do
res[i] = delta * t[hi][i] + (1 - delta) * t[lo][i]
end
return res
end
end
local offset_luts = {
pistol = {
[0] = {0, 0, 0},
[max_deg] = {-0.05, -0.17, -0.12},
},
pistol_silenced = {
[0] = {0, 0, 0},
[max_deg] = {-0.05, -0.21, -0.12},
},
rifle = {
[0] = {0, 0, 0},
[max_deg] = {-0.05, -0.23, -0.12},
},
rifle_silenced = {
[0] = {0, 0, 0},
[max_deg] = {-0.05, -0.29, -0.12},
},
}
local offset_luts_keys = (function()
local t = {}
for k, v in pairs(offset_luts) do
t[k] = array_keys(v, true)
end
return t
end)()
local function ema(key, value, def, steps, delta)
local steps = steps or default_smoothing
local delta = delta or steps
local smoothing_alpha = 2.0 / (steps + 1)
smoothed_values[key] = smoothed_values[key] and smoothed_values[key] + math.min(smoothing_alpha * (delta / steps), 1) * (value - smoothed_values[key]) or def or value
--printf("EMA fired, key %s, target %s, current %s, going %s", key, value, smoothed_values[key], (value > smoothed_values[key] and "up" or "down"))
return smoothed_values[key]
end
local function reset_ema()
smoothed_values["controller_coeff_roll"] = nil
smoothed_values["controller_coeff_yaw"] = nil
for i = 1, 3 do
smoothed_values["offsets"..i] = nil
end
end
function IsFirearm(wpn)
return IsWeapon(wpn) and not (IsMelee(wpn) or IsItem("fake_ammo_wpn", wpn:section()) or wct.IsBinoc(wpn:section()))
end
local function lerp(a, b, f)
if a and b and f then
return a + f * (b - a)
else
return a or b or 0
end
end
function set_suicide()
if not under_suicide then
local actor = db.actor
local active_item = actor:active_item()
if active_item and IsFirearm(active_item) then
actor_menu.set_msg(1, game.translate_string("st_suicide_start"),4)
RegisterScriptCallback("actor_on_update", actor_on_update)
under_suicide = true
end
end
end
function set_forced_suicide()
if not under_suicide then
actor_menu.set_msg(1, game.translate_string("st_suicide_start"),4)
RegisterScriptCallback("actor_on_update", actor_on_update)
under_suicide = true
forced = true
local actor = db.actor
local active_item = actor:active_item()
if not (active_item and IsFirearm(active_item)) then
local slot_check = {2, 3, 1, 5, 4, 6, 7, 8, 9, 10, 11, 12, 13}
for i, v in ipairs(slot_check) do
local item = actor:item_in_slot(v)
if item and IsFirearm(item) then
actor:activate_slot(v)
return
end
end
actor:iterate_inventory(function(owner, item)
if item and IsFirearm(item) then
actor:make_item_active(item)
return
end
end)
end
end
end
function unset_suicide()
if under_suicide and (not forced) then
actor_menu.set_msg(1, game.translate_string("st_suicide_next_time"),4)
under_suicide = false
end
end
function update_suicide_state()
local actor = db.actor
local delta = device().time_delta
local up_increment = up_increment
if forced then
up_increment = up_increment / 2
end
local diff = (under_suicide and up_increment or -down_increment) * delta
local c = clamp(coeff + diff, 0, max_coeff)
coeff = ema("controller_coeff", c, c, 1)
normalized_coeff = normalize(coeff, 0, max_coeff)
if coeff > 0 then
wct.set_force_disabled()
local current_deg = max_deg * normalized_coeff
local det_active = actor:active_detector()
if det_active and not detector_hidden then
detector_hidden = true
det_active:switch_state(2)
end
if axr_main.weapon_is_zoomed then
if (get_console():get_bool("wpn_aim_toggle")) then
level.press_action(bind_to_dik(key_bindings.kWPN_ZOOM))
else
level.release_action(bind_to_dik(key_bindings.kWPN_ZOOM))
end
end
local wpn = actor:active_item()
if not wpn then
return
end
if not IsFirearm(wpn) then
return
end
local sec = wpn:section()
local hud_sec = SYS_GetParam(0, sec, "hud")
if not hud_sec then
return
end
wct.add_to_weapon_table(sec)
local roll = ema("controller_coeff_roll", math.random(-10, 10), 0, 11, delta)
local yaw = ema("controller_coeff_yaw", math.random(-5, 5), 0, 11, delta)
wct.set_roll(roll)
wct.set_yaw(yaw)
local is_pistol = SYS_GetParam(0, sec, "kind", "") == "w_pistol" and not wct.not_pistol_sec[sec]
local is_silenced = wct.isSilencedWeapon(wpn)
local lut_key
if is_silenced then
lut_key = is_pistol and "pistol_silenced" or "rifle_silenced"
else
lut_key = is_pistol and "pistol" or "rifle"
end
local offset_lut = offset_luts[lut_key]
local offset_lut_keys = offset_luts_keys[lut_key]
local offsets = lookup(offset_lut, current_deg, offset_lut_keys)
for i = 1, 3 do
-- if weapon_cover_tilt_positions
-- and weapon_cover_tilt_positions.weapon_offsets
-- and weapon_cover_tilt_positions.weapon_offsets[sec] then
-- local t = {"x", "y", "z"}
-- local o = weapon_cover_tilt_positions.weapon_offsets[sec][t[i]] or 0
-- if t[i] == "y" then
-- if current_deg < 90 then
-- o = lerp(0, o, coeff)
-- else
-- local d = o * 90 / 75
-- o = lerp(d, 0, normalize(current_deg, 90, 180))
-- end
-- -- offsets[i] = offsets[i] - o * 0.5
-- end
-- end
offsets[i] = ema("offsets"..i, offsets[i], 0, 15, delta)
end
wct.set_custom_offsets(offsets[1], offsets[2], offsets[3])
local new_pos, new_ori = wct.calculate_new_position(wpn, sec, coeff, delta, 10)
wct.enable_tilt(sec, wpn)
wct.set_weapon_position(sec, new_pos, new_ori)
if coeff == max_coeff then
if not forced then
actor_menu.set_msg(1, game.translate_string("st_suicide_shoot"),4)
end
if forced then
reset()
local snd = (function()
local def = "weapons\\ak74\\ak74_shoot"
local snds = is_silenced and {"snd_silncer_shot_actor", "snd_silncer_shot", "snd_shoot_actor", "snd_shoot"} or {"snd_shoot_actor", "snd_shoot"}
for i, v in ipairs(snds) do
local s = SYS_GetParam(0, sec, v)
if s then
if ini_sys:section_exist(s) then
s = SYS_GetParam(0, s, "snd_1_layer", s)
end
s = get_safe_sound_object(s)
if s then
return s
end
end
end
return get_safe_sound_object(def)
end)()
if not arszi_psy.fate_of_player_zombfication then
if snd then
snd:play(actor, 0, sound_object.s2d)
snd.volume = 1
snd.frequency = 1
end
actor_menu.set_msg(1, game.translate_string("st_psy_death_scene"), 4)
actor:set_health_ex(0)
else
local item,slot = db.actor:active_item(),db.actor:active_slot()
if item and (slot == 2 or slot == 3) then
db.actor:drop_item(item)
end
arszi_psy.psy_table.actor_psy_health = 1.0
--trace_this("GOODWILL BEFORE: "..relation_registry.community_goodwill("stalker", AC_ID))
db.actor:set_character_community("actor_zombied", 0, 0)
arszi_psy.psy_table.actor_zombied = true
--game_relations.set_factions_community_num("actor_zombied", "stalker", -5000)
--trace_this("GOODWILL AFTER: "..relation_registry.community_goodwill("stalker", AC_ID))
actor_menu.set_msg(1, game.translate_string("st_psy_zombification_scene"), 4)
forced = false
unset_suicide()
end
level.enable_input()
end
else
suicide_countdown = 0
end
else
reset()
end
end
function arszi_psy.manage_zombification()
--Original community will be restored on load game always. Temp solution.
if (arszi_psy.psy_table.actor_zombied and db.actor:character_community() ~= "actor_zombied") then
db.actor:set_character_community("actor_zombied", 0, 0)
end
--Add visual effects
if (arszi_psy.psy_table.actor_zombied) then
--Todo why is alcohol not working!?
level.add_pp_effector("radiation.ppe", c_id_ppe_radiation, true)
level.add_pp_effector("alcohol.ppe", c_id_ppe_alcohol, true)
end
if (arszi_psy.psy_table.actor_psy_health <= 0) then
--Death or Zombification
if (arszi_psy.psy_table.current_stage ~= c_stage_3) then
--show_message_news("ENTER STAGE 6")
arszi_psy.remove_all_psy_ppe_effects()
--level.add_pp_effector("black_infinite.ppe", c_id_ppe_black_infinite, true)
set_forced_suicide()
arszi_psy.psy_table.psy_death_scene = true
level.disable_input()
arszi_psy.psy_table.current_stage = c_stage_3
end
end
end
function actor_on_update()
try(function()
update_suicide_state()
end)
end
function reset()
wct.set_force_disabled(false)
wct.remove_roll_yaw()
wct.set_custom_offsets()
reset_ema()
UnregisterScriptCallback("actor_on_update", actor_on_update)
if actor_sound and actor_sound:playing() then
actor_sound:stop()
end
actor_sound = nil
if psy_sound and psy_sound:playing() then
psy_sound:stop()
end
psy_sound = nil
detector_hidden = false
under_suicide = false
coeff = 0
normalized_coeff = 0
end
function actor_on_weapon_before_fire(flags)
local actor = db.actor
if coeff == max_coeff then
flags.ret_value = false
reset()
actor_menu.set_msg(1, game.translate_string("st_suicide_goodbye"),4)
local snd = (function()
local wpn = actor:active_item()
if not wpn then
return
end
local sec = wpn:section()
local hud_sec = SYS_GetParam(0, sec, "hud")
if not hud_sec then
return
end
local def = "weapons\\ak74\\ak74_shoot"
local snds = is_silenced and {"snd_silncer_shot_actor", "snd_silncer_shot", "snd_shoot_actor", "snd_shoot"} or {"snd_shoot_actor", "snd_shoot"}
for i, v in ipairs(snds) do
local s = SYS_GetParam(0, sec, v)
if s then
if ini_sys:section_exist(s) then
s = SYS_GetParam(0, s, "snd_1_layer", s)
end
s = get_safe_sound_object(s)
if s then
return s
end
end
end
return get_safe_sound_object(def)
end)()
if snd then
snd:play(actor, 0, sound_object.s2d)
snd.volume = 1
snd.frequency = 1
end
actor:set_health_ex(0)
end
end
function on_key_press(key)
if (key == keybind) then
if (not under_suicide) then
set_suicide()
else
unset_suicide()
end
end
end
function on_option_change()
keybind = selfkill_mcm.get_config("keybind_mcm")
end
function on_game_start()
assert(weapon_cover_tilt and weapon_cover_tilt.VERSION and weapon_cover_tilt.VERSION >= 9, "Suicide mod ERROR: Weapon Cover Tilt UPDATE 9 or upper is required for Suicide mod")
RegisterScriptCallback("actor_on_weapon_before_fire", actor_on_weapon_before_fire)
wct.add_callback("actor_on_weapon_before_tilt", block_cover_tilt)
RegisterScriptCallback("on_key_press", on_key_press)
RegisterScriptCallback("on_option_change", on_option_change)
on_option_change()
end