458 lines
12 KiB
Plaintext
458 lines
12 KiB
Plaintext
--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 |