1055 lines
33 KiB
Plaintext
1055 lines
33 KiB
Plaintext
-- Weapon cover tilt script
|
|
-- Makes weapon go up when near obstacle to emulate weapon dimensions
|
|
-- Written by demonized
|
|
|
|
-- Current version
|
|
VERSION = 14
|
|
|
|
--Protected function call to prevent crashes to desktop
|
|
--Prints error in console if occured, otherwise proceed normally
|
|
--Use for test only, slower than usual
|
|
local try = try or function(func, ...)
|
|
local status, error_or_result = pcall(func, ...)
|
|
if not status then
|
|
printf(error_or_result)
|
|
return false, status, error_or_result
|
|
else
|
|
return error_or_result, status
|
|
end
|
|
end
|
|
|
|
local dte = demonized_time_events
|
|
|
|
local normalize = normalize
|
|
local clamp = clamp
|
|
|
|
local abs = math.abs
|
|
local min = math.min
|
|
local max = math.max
|
|
local sqrt = math.sqrt
|
|
|
|
local actor_weapon_lowered = game.actor_weapon_lowered
|
|
|
|
local get_target_dist = level.get_target_dist
|
|
local get_target_obj = level.get_target_obj
|
|
|
|
--EMA smoothing for changing values, frame independent
|
|
local default_smoothing = 11.5
|
|
local smoothed_values = {}
|
|
|
|
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] + 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
|
|
|
|
-- Linear inter/extrapolation
|
|
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
|
|
|
|
local intercepts = {
|
|
["actor_on_weapon_before_tilt"] = {}, -- Params: weapon_object, flags
|
|
["actor_on_weapon_tilt_start"] = {}, -- Params: weapon_object
|
|
["actor_on_weapon_tilt_end"] = {}, -- Params: weapon_object
|
|
["actor_on_weapon_tilting"] = {}, -- Params: weapon_object, coeff
|
|
["actor_on_weapon_tilting_back"] = {}, -- Params: weapon_object, coeff
|
|
}
|
|
|
|
function add_intercept(name)
|
|
if intercepts[name] then return end
|
|
intercepts[name] = {}
|
|
end
|
|
|
|
function add_callback(name, func)
|
|
if not (name and func and intercepts[name]) then return end
|
|
intercepts[name][func] = true
|
|
end
|
|
|
|
function remove_callback(name, func)
|
|
if not (name and func and intercepts[name]) then return end
|
|
intercepts[name][func] = nil
|
|
end
|
|
|
|
function callback(name, ...)
|
|
if not (name and intercepts[name]) then return end
|
|
for func, _ in pairs(intercepts[name]) do
|
|
func(...)
|
|
end
|
|
end
|
|
|
|
-- MCM
|
|
-- Load the defaults
|
|
local function load_defaults()
|
|
local t = {}
|
|
local op = weapon_cover_tilt_mcm.op
|
|
for i, v in ipairs(op.gr) do
|
|
if v.def ~= nil then
|
|
t[v.id] = v.def
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
local settings = load_defaults()
|
|
|
|
local function load_settings()
|
|
settings = load_defaults()
|
|
if ui_mcm then
|
|
for k, v in pairs(settings) do
|
|
settings[k] = ui_mcm.get("weapon_cover_tilt/" .. k)
|
|
end
|
|
end
|
|
end
|
|
|
|
function get_setting(key)
|
|
return settings[key]
|
|
end
|
|
|
|
function debug_enabled()
|
|
return DEV_DEBUG or DEV_DEBUG_DEV
|
|
end
|
|
|
|
local parameters = {
|
|
-- Type: 0 = string | 1 = number | 2 = 3d vector | 3 = 4d vector |
|
|
["hands_position"] = { name = "Hands Position", typ = 2, def = {0,0,0}, indx = 1, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 0, hud = true },
|
|
["hands_orientation"] = { name = "Hands Orientation", typ = 2, def = {0,0,0}, indx = 2, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 0, hud = true },
|
|
["aim_hud_offset_pos"] = { name = "Aim Position", typ = 2, def = {0,0,0}, indx = 3, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 1, hud = true },
|
|
["aim_hud_offset_rot"] = { name = "Aim Orientation", typ = 2, def = {0,0,0}, indx = 4, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 1, hud = true },
|
|
["gl_hud_offset_pos"] = { name = "GL Position", typ = 2, def = {0,0,0}, indx = 5, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 2, hud = true },
|
|
["gl_hud_offset_rot"] = { name = "GL Orientation", typ = 2, def = {0,0,0}, indx = 6, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 2, hud = true },
|
|
["aim_hud_offset_alt_pos"] = { name = "Alt Position", typ = 2, def = {0,0,0}, indx = 7, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 3, hud = true },
|
|
["aim_hud_offset_alt_rot"] = { name = "Alt Orientation", typ = 2, def = {0,0,0}, indx = 8, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 3, hud = true },
|
|
["lowered_hud_offset_pos"] = { name = "Lowered Position", typ = 2, def = {0,0,0}, indx = 9, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 4, hud = true },
|
|
["lowered_hud_offset_rot"] = { name = "Lowered Orientation", typ = 2, def = {0,0,0}, indx = 10, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 4, hud = true },
|
|
["fire_point"] = { name = "Fire Point", typ = 2, def = {0,0,0}, indx = 11, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 10, hud = true, no_16x9 = true },
|
|
["fire_point2"] = { name = "Fire Point 2", typ = 2, def = {0,0,0}, indx = 12, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 11, hud = true, no_16x9 = true },
|
|
["fire_direction"] = { name = "Fire Direction", typ = 2, def = {0,0,1}, indx = 13, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 10, hud = true, no_16x9 = true },
|
|
["shell_point"] = { name = "Shell Point", typ = 2, def = {0,0,0}, indx = 14, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 11, hud = true, no_16x9 = true },
|
|
["custom_ui_pos"] = { name = "UI Position", typ = 2, def = {0,0,0}, indx = 15, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 20 }, --idxb 20 is reserved for device ui
|
|
["custom_ui_rot"] = { name = "UI Orientation", typ = 2, def = {0,0,0}, indx = 16, min = -180, max = 180, step = 1, idxa = 1, idxb = 20 },
|
|
["item_position"] = { name = "Item Position", typ = 2, def = {0,0,0}, indx = 17, min = -180, max = 180, step = 0.0001, idxa = 0, idxb = 12, hud = true, no_16x9 = true },
|
|
["item_orientation"] = { name = "Item Orientation", typ = 2, def = {0,0,0}, indx = 18, min = -180, max = 180, step = 0.0001, idxa = 1, idxb = 12, hud = true, no_16x9 = true },
|
|
["scope_zoom_factor"] = { name = "Zoom Factor", typ = 1, def = 0, indx = 19, min = 0, max = 120, step = 0.1 },
|
|
["gl_zoom_factor"] = { name = "GL Zoom Factor", typ = 1, def = 0, indx = 20, min = 0, max = 120, step = 0.1 },
|
|
["scope_zoom_factor_alt"] = { name = "Alt Zoom Factor", typ = 1, def = 0, indx = 21, min = 0, max = 120, step = 0.1 },
|
|
}
|
|
|
|
function reset_wpn_hud(sec)
|
|
local hud_sec = SYS_GetParam(0, sec, "hud")
|
|
if not hud_sec then return end
|
|
|
|
hud_adjust.enabled(true)
|
|
for k, v in pairs(parameters) do
|
|
if v.typ == 1 then
|
|
local p = SYS_GetParam(2, hud_sec, k, v.def)
|
|
if p then
|
|
hud_adjust.set_value(k, p)
|
|
end
|
|
elseif v.typ == 2 then
|
|
local function res(str)
|
|
local str = str or ""
|
|
local p = SYS_GetParam(0, hud_sec, v.no_16x9 and k or (k .. str))
|
|
|
|
if not p then
|
|
p = table.concat(v.def, ",")
|
|
end
|
|
|
|
if p then
|
|
p = str_explode(p, ",")
|
|
for i, d in ipairs(v.def) do
|
|
p[i] = p[i] and tonumber(p[i]) or d
|
|
end
|
|
hud_adjust.set_vector(v.idxa, v.idxb, p[1] or 0, p[2] or 0, p[3] or 0)
|
|
end
|
|
end
|
|
res(utils_xml.is_widescreen() and "_16x9")
|
|
end
|
|
end
|
|
hud_adjust.enabled(false)
|
|
end
|
|
|
|
function IsBinoc(sec)
|
|
return SYS_GetParam(0, sec, "ammo_class", "") == "ammo_binoc"
|
|
or SYS_GetParam(0, sec, "class", "") == "WP_BINOC"
|
|
end
|
|
|
|
function IsThrowable(wpn)
|
|
return IsBolt(wpn) or SYS_GetParam(0, wpn:section(), "class", "") == "II_BOLT"
|
|
end
|
|
|
|
local scoped_weapon_zoomed = false
|
|
function isScopedWeapon(wpn)
|
|
if not wpn then return false end
|
|
|
|
local has_scopes_sect = SYS_GetParam(0, wpn:section(), "scopes_sect", "") ~= ""
|
|
local has_texture = SYS_GetParam(0, wpn:section(), "scope_texture", "") ~= ""
|
|
|
|
local cobj = wpn:cast_Weapon()
|
|
|
|
-- If old scopes system
|
|
if has_scopes_sect then
|
|
if cobj and cobj:IsScopeAttached() or wpn:weapon_is_scope() then
|
|
return true
|
|
end
|
|
else
|
|
-- If there is fixed scope or no scope defined, but also if there is a scope texture, it will use the texture for zoomin and considered a scoped weapon
|
|
if (wpn:weapon_scope_status() == 1 or wpn:weapon_scope_status() == 0) and has_texture then
|
|
return true
|
|
end
|
|
|
|
-- Usual check
|
|
if (cobj and cobj:IsScopeAttached() or wpn:weapon_is_scope()) and has_texture then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function isSilencedWeapon(wpn)
|
|
if not wpn then return false end
|
|
|
|
local cobj = wpn:cast_Weapon()
|
|
return wpn:weapon_silencer_status() == 2
|
|
and (cobj and cobj:IsSilencerAttached() or wpn:weapon_is_silencer() or utils_item.addon_attached(wpn, "sl"))
|
|
end
|
|
|
|
local random_funcs = demonized_randomizing_functions
|
|
local wpn_positions = weapon_cover_tilt_positions or {}
|
|
local wpn_radii = weapon_cover_tilt_gun_trigger_radii and weapon_cover_tilt_gun_trigger_radii.weapon_trigger_radii or {}
|
|
|
|
function getBestMatch(sec)
|
|
local best_match, best_match_l = nil, 0
|
|
local function getMatch(k, s1, s2)
|
|
local a, b = s1:find(s2)
|
|
if a and b then
|
|
local l = b - a + 1
|
|
if l > best_match_l then
|
|
best_match = k
|
|
best_match_l = l
|
|
end
|
|
end
|
|
end
|
|
for k, v in pairs(wpn_radii) do
|
|
getMatch(k, sec, k)
|
|
-- getMatch(k, k, sec)
|
|
end
|
|
return best_match
|
|
end
|
|
|
|
-- Test of is material of static geometry is shootable, engine edit required
|
|
--[[
|
|
|
|
Material List
|
|
flBreakable = (1ul << 0ul),
|
|
flBounceable = (1ul << 2ul),
|
|
flSkidmark = (1ul << 3ul),
|
|
flBloodmark = (1ul << 4ul),
|
|
flClimable = (1ul << 5ul),
|
|
flPassable = (1ul << 7ul),
|
|
flDynamic = (1ul << 8ul),
|
|
flLiquid = (1ul << 9ul),
|
|
flSuppressShadows = (1ul << 10ul),
|
|
flSuppressWallmarks = (1ul << 11ul),
|
|
flActorObstacle = (1ul << 12ul),
|
|
flNoRicoshet = (1ul << 13ul),
|
|
|
|
flInjurious = (1ul << 28ul),
|
|
flShootable = (1ul << 29ul),
|
|
flTransparent = (1ul << 30ul),
|
|
flSlowDown = (1ul << 31ul)
|
|
|
|
// material exports
|
|
.def_readonly("material_name", &script_rq_result::pMaterialName)
|
|
.def_readonly("material_flags", &script_rq_result::pMaterialFlags)
|
|
.def_readonly("material_phfriction", &script_rq_result::fPHFriction)
|
|
.def_readonly("material_phdamping", &script_rq_result::fPHDamping)
|
|
.def_readonly("material_phspring", &script_rq_result::fPHSpring)
|
|
.def_readonly("material_phbounce_start_velocity", &script_rq_result::fPHBounceStartVelocity)
|
|
.def_readonly("material_phbouncing", &script_rq_result::fPHBouncing)
|
|
.def_readonly("material_flotation_factor", &script_rq_result::fFlotationFactor)
|
|
.def_readonly("material_shoot_factor", &script_rq_result::fShootFactor)
|
|
.def_readonly("material_shoot_factor_mp", &script_rq_result::fShootFactorMP)
|
|
.def_readonly("material_bounce_damage_factor", &script_rq_result::fBounceDamageFactor)
|
|
.def_readonly("material_injurious_speed", &script_rq_result::fInjuriousSpeed)
|
|
.def_readonly("material_vis_transparency_factor", &script_rq_result::fVisTransparencyFactor)
|
|
.def_readonly("material_snd_occlusion_factor", &script_rq_result::fSndOcclusionFactor)
|
|
.def_readonly("material_density_factor", &script_rq_result::fDensityFactor)
|
|
|
|
--]]
|
|
local function lshift(x, by)
|
|
return x * 2 ^ by
|
|
end
|
|
|
|
local function test(x, mask)
|
|
return bit_and(x, mask) == mask
|
|
end
|
|
|
|
local flags_test = {
|
|
-- {"flBreakable", lshift(1, 0)},
|
|
-- {"flBounceable", lshift(1, 2)},
|
|
-- {"flSkidmark", lshift(1, 3)},
|
|
-- {"flBloodmark", lshift(1, 4)},
|
|
-- {"flClimable", lshift(1, 5)},
|
|
-- {"flPassable", lshift(1, 7)},
|
|
-- {"flDynamic", lshift(1, 8)},
|
|
-- {"flLiquid", lshift(1, 9)},
|
|
-- {"flSuppressShadows", lshift(1, 10)},
|
|
-- {"flSuppressWallmarks", lshift(1, 11)},
|
|
-- {"flActorObstacle", lshift(1, 12)},
|
|
-- {"flNoRicoshet", lshift(1, 13)},
|
|
|
|
-- {"flInjurious", lshift(1, 28)},
|
|
{"flShootable", lshift(1, 29)},
|
|
-- {"flTransparent", lshift(1, 30)},
|
|
-- {"flSlowDown", lshift(1, 31)},
|
|
}
|
|
|
|
function isShootableMaterial(max_dist)
|
|
local max_dist = max_dist or 10
|
|
local ray = ray_pick()
|
|
ray:set_flags(3)
|
|
ray:set_range(max_dist)
|
|
ray:set_position(device().cam_pos)
|
|
ray:set_direction(device().cam_dir)
|
|
ray:set_ignore_object(db.actor)
|
|
|
|
local res = ray:query()
|
|
|
|
-- Return false if failed query
|
|
if not res then
|
|
return false
|
|
end
|
|
|
|
-- Return false if game object
|
|
if ray:get_object() then
|
|
return false
|
|
end
|
|
|
|
local result = ray:get_result()
|
|
|
|
-- Return false if engine doesnt have material exports or unknown material
|
|
if not (
|
|
result
|
|
and result.material_name
|
|
and result.material_flags
|
|
and result.material_shoot_factor
|
|
)
|
|
then
|
|
return false
|
|
end
|
|
|
|
local flags = result.material_flags
|
|
local name = result.material_name
|
|
local shoot_factor = result.material_shoot_factor
|
|
|
|
-- Test material flags
|
|
local flags_test_result = {}
|
|
for i, v in ipairs(flags_test) do
|
|
if test(flags, v[2]) then
|
|
flags_test_result[v[1]] = true
|
|
end
|
|
-- printf("%s, %s test, %s, %s, %s", name, v[1], flags, v[2], test(flags, v[2]))
|
|
end
|
|
|
|
-- printf("%s, material_shoot_factor %s", name, shoot_factor)
|
|
|
|
-- If name contains "bush" or material is shootable and low shoot_factor - true
|
|
if string.find(name, "bush") then
|
|
return true
|
|
end
|
|
|
|
if string.find(name, "water") then
|
|
return false
|
|
end
|
|
|
|
if false
|
|
-- or (flags_test_result.flShootable and shoot_factor <= 0.01)
|
|
then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Sections of weapons that have kind "pistol" but they arent pistols
|
|
not_pistol_sec = {
|
|
wpn_svt40_short = true,
|
|
wpn_avt40_short = true,
|
|
wpn_aek919k = true,
|
|
}
|
|
|
|
-- Adjust gun radii by kind if cant find section in radii table
|
|
wpn_kind_adjustment_table = {
|
|
w_pistol = -0.3,
|
|
w_smg = -0.12,
|
|
w_sniper = 0.12,
|
|
w_shotgun = -0.05,
|
|
w_rifle = 0,
|
|
}
|
|
|
|
local weapon_table = {}
|
|
function add_to_weapon_table(sec, force)
|
|
if weapon_table[sec] and not force then return end
|
|
weapon_table[sec] = {
|
|
hands_position = (function()
|
|
local hud_sec = SYS_GetParam(0, sec, "hud")
|
|
local c = str_explode(utils_xml.is_widescreen() and SYS_GetParam(0, hud_sec, "hands_position_16x9") or SYS_GetParam(0, hud_sec, "hands_position") or "0,0,0", ",")
|
|
return {
|
|
[0] = tonumber(c[1]) or 0,
|
|
[1] = tonumber(c[2]) or 0,
|
|
[2] = tonumber(c[3]) or 0,
|
|
}
|
|
end)(),
|
|
hands_orientation = (function()
|
|
local hud_sec = SYS_GetParam(0, sec, "hud")
|
|
local c = str_explode(utils_xml.is_widescreen() and SYS_GetParam(0, hud_sec, "hands_orientation_16x9") or SYS_GetParam(0, hud_sec, "hands_orientation") or "0,0,0", ",")
|
|
return {
|
|
[0] = tonumber(c[1]) or 0,
|
|
[1] = tonumber(c[2]) or 0,
|
|
[2] = tonumber(c[3]) or 0,
|
|
}
|
|
end)(),
|
|
inv_weight = SYS_GetParam(2, sec, "inv_weight", 0),
|
|
zoom_rotate_time = SYS_GetParam(2, sec, "zoom_rotate_time", 0.25),
|
|
}
|
|
end
|
|
|
|
local target_pos = {
|
|
hands_position_pistol = {
|
|
[0] = -0.050912,
|
|
[1] = -0.646908,
|
|
[2] = 0.280633,
|
|
},
|
|
hands_position_rifle = {
|
|
[0] = 0.055224,
|
|
[1] = -0.6,
|
|
[2] = 0.202598,
|
|
}
|
|
}
|
|
|
|
local custom_offsets = {
|
|
[0] = 0,
|
|
[1] = 0,
|
|
[2] = 0,
|
|
}
|
|
|
|
function set_custom_offsets(x, y, z)
|
|
custom_offsets[0] = x or 0
|
|
custom_offsets[1] = y or 0
|
|
custom_offsets[2] = z or 0
|
|
end
|
|
|
|
local tilt_enabled = false
|
|
local tilt_key_pressed = false
|
|
local firepos_state = 0
|
|
local yaw
|
|
local roll
|
|
local max_deg = 75
|
|
local grace_threshold = 75
|
|
local grace_time = 0
|
|
|
|
function set_yaw(deg)
|
|
yaw = deg
|
|
end
|
|
|
|
function set_roll(deg)
|
|
roll = deg
|
|
end
|
|
|
|
function is_tilt_enabled()
|
|
return tilt_enabled
|
|
end
|
|
|
|
function is_tilt_key_pressed()
|
|
return tilt_key_pressed
|
|
end
|
|
|
|
function randomize_roll_yaw()
|
|
if not yaw then
|
|
local max_yaw = settings.yaw_variation
|
|
yaw = random_float(-max_yaw, max_yaw)
|
|
end
|
|
|
|
if not roll then
|
|
local max_roll = settings.roll_variation
|
|
roll = random_float(-max_roll, max_roll * 0.33)
|
|
end
|
|
end
|
|
|
|
function remove_roll_yaw()
|
|
yaw = nil
|
|
roll = nil
|
|
end
|
|
|
|
function enable_tilt(sec, wpn)
|
|
if scoped_weapon_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
|
|
|
|
if not tilt_enabled then
|
|
-- Dirty AF hack to reset position (unused)
|
|
-- local wpn_hud = ui_debug_wpn_hud.WpnHudEditor(nil, sec)
|
|
-- if wpn_hud then
|
|
-- wpn_hud:Reset(true)
|
|
-- wpn_hud:Close()
|
|
-- end
|
|
reset_wpn_hud(sec)
|
|
|
|
hud_adjust.enabled(true)
|
|
set_weapon_position(sec, weapon_table[sec].hands_position, weapon_table[sec].hands_orientation)
|
|
|
|
if debug_enabled() then
|
|
exec_console_cmd("g_firepos 1")
|
|
end
|
|
|
|
tilt_enabled = true
|
|
callback("actor_on_weapon_tilt_start", wpn)
|
|
end
|
|
end
|
|
|
|
function reset(sec)
|
|
if tilt_enabled then
|
|
if sec and weapon_table[sec] then reset_wpn_hud(sec) end
|
|
hud_adjust.enabled(false)
|
|
|
|
if debug_enabled() then
|
|
exec_console_cmd("g_firepos " .. (firepos_state or 0))
|
|
end
|
|
|
|
smoothed_values.hands_position_x = nil
|
|
smoothed_values.hands_position_y = nil
|
|
smoothed_values.hands_position_z = nil
|
|
smoothed_values.hands_orientation_x = nil
|
|
smoothed_values.hands_orientation_y = nil
|
|
smoothed_values.hands_orientation_z = nil
|
|
smoothed_values.coeff = nil
|
|
tilt_enabled = false
|
|
tilt_key_pressed = false
|
|
grace_time = 0
|
|
callback("actor_on_weapon_tilt_end", wpn)
|
|
end
|
|
end
|
|
|
|
-- Get steps depending on inv_weight, heavier guns have less movement speed
|
|
function get_weapon_weight(wpn, sec)
|
|
local cobj = wpn and wpn:cast_Weapon()
|
|
local weight = clamp(cobj and cobj:Weight() or (weapon_table[sec] and weapon_table[sec].inv_weight or 0), 0, 20)
|
|
return weight
|
|
end
|
|
|
|
function get_weapon_steps(wpn, sec)
|
|
return settings.animation_speed * (1 + get_weapon_weight(wpn, sec) * settings.animation_weight_coeff * 0.07)
|
|
end
|
|
|
|
function set_weapon_position(sec, new_pos, new_ori)
|
|
-- printf("old_pos for %s: %s, %s, %s", sec, weapon_table[sec].hands_position[0], weapon_table[sec].hands_position[1], weapon_table[sec].hands_position[2])
|
|
-- printf("new_pos for %s: %s, %s, %s", sec, new_pos[0], new_pos[1], new_pos[2])
|
|
-- printf("old_ori for %s: %s, %s, %s", sec, weapon_table[sec].hands_orientation[0], weapon_table[sec].hands_orientation[1], weapon_table[sec].hands_orientation[2])
|
|
-- printf("new_pri for %s: %s, %s, %s", sec, new_ori[0], new_ori[1], new_ori[2])
|
|
|
|
hud_adjust.set_vector(0, 0, new_pos[0] or 0, new_pos[1] or 0, new_pos[2] or 0)
|
|
hud_adjust.set_vector(1, 0, new_ori[0] or 0, new_ori[1] or 0, new_ori[2] or 0)
|
|
-- hud_adjust.set_vector(parameters[parent].idxa, parameters[parent].idxb, value_1, value_2, value_3)
|
|
end
|
|
|
|
function soft_reset(wpn, sec, delta)
|
|
if not weapon_table[sec] then return reset(sec) end
|
|
local delta = delta or device().time_delta
|
|
|
|
if tilt_enabled then
|
|
-- k is modifier for ema steps to have more inertia in the beginning of movement
|
|
local k = max(1, 1.5 - (1 - normalize(smoothed_values.hands_orientation_y or max_deg, weapon_table[sec].hands_orientation[1], max_deg)))
|
|
|
|
-- Increase speed of return to position when closer to it for zoomed weapons
|
|
local is_scoped = isScopedWeapon(wpn)
|
|
local scope_magnitude = 0.105
|
|
k = k * (is_scoped and normalize(smoothed_values.hands_orientation_y, weapon_table[sec].hands_orientation[1], max_deg) ^ scope_magnitude or 1)
|
|
|
|
-- printf("cur %s, min %s, max %s, %s", smoothed_values.hands_orientation_y, weapon_table[sec].hands_orientation[1], max_deg, k)
|
|
local steps = get_weapon_steps(wpn, sec) * k
|
|
|
|
-- Smooth the coeff
|
|
local coeff_smoothing = steps * 0.5
|
|
local coeff = ema("coeff", 0, 0, coeff_smoothing, delta)
|
|
|
|
-- Remove random roll and yaw if close to end
|
|
if coeff < 0.5 then
|
|
remove_roll_yaw()
|
|
end
|
|
|
|
local new_pos = {
|
|
[0] = ema("hands_position_x", lerp(weapon_table[sec].hands_position[0], smoothed_values.hands_position_x, coeff), weapon_table[sec].hands_position[0], steps, delta),
|
|
[1] = ema("hands_position_y", lerp(weapon_table[sec].hands_position[1], smoothed_values.hands_position_y, coeff), weapon_table[sec].hands_position[1], steps, delta),
|
|
[2] = ema("hands_position_z", lerp(weapon_table[sec].hands_position[2], smoothed_values.hands_position_z, coeff), weapon_table[sec].hands_position[2], steps, delta),
|
|
}
|
|
|
|
local new_ori = {
|
|
[0] = ema("hands_orientation_x", lerp(weapon_table[sec].hands_orientation[0], smoothed_values.hands_orientation_x, coeff), weapon_table[sec].hands_orientation[0], steps, delta),
|
|
[1] = ema("hands_orientation_y", lerp(weapon_table[sec].hands_orientation[1], smoothed_values.hands_orientation_y, coeff), weapon_table[sec].hands_orientation[1], steps, delta),
|
|
[2] = ema("hands_orientation_z", lerp(weapon_table[sec].hands_orientation[2], smoothed_values.hands_orientation_z, coeff), weapon_table[sec].hands_orientation[2], steps, delta),
|
|
}
|
|
|
|
local animation_progress = normalize(new_ori[1], weapon_table[sec].hands_orientation[1], max_deg)
|
|
callback("actor_on_weapon_tilting_back", wpn, animation_progress)
|
|
|
|
set_weapon_position(sec, new_pos, new_ori)
|
|
|
|
if scoped_weapon_zoomed then
|
|
|
|
-- Calculate remaining time for scope aiming
|
|
local ms = delta / 1000
|
|
local t = 0
|
|
local threshold = 0.0007
|
|
smoothed_values.t = animation_progress
|
|
|
|
while t < weapon_table[sec].zoom_rotate_time and smoothed_values.t > threshold do
|
|
local k = smoothed_values.t ^ scope_magnitude
|
|
local steps = get_weapon_steps(wpn, sec) * k
|
|
local old = smoothed_values.t
|
|
smoothed_values.t = ema("t", 0, 0, steps, delta)
|
|
t = t + ms
|
|
end
|
|
|
|
local res = smoothed_values.t
|
|
smoothed_values.t = nil
|
|
|
|
if res > threshold 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
|
|
end
|
|
|
|
for i = 0, 2, 1 do
|
|
if abs(new_pos[i] - weapon_table[sec].hands_position[i]) > 0.055
|
|
or abs(new_ori[i] - weapon_table[sec].hands_orientation[i]) > 0.055
|
|
then
|
|
return
|
|
end
|
|
end
|
|
|
|
-- printf("soft reset complete")
|
|
-- printf("animation_progress %s", animation_progress)
|
|
reset(sec)
|
|
end
|
|
end
|
|
|
|
function calculate_new_position(wpn, sec, coeff, delta, steps)
|
|
-- Calculate new position
|
|
local position_coeff_y = -0.445
|
|
local position_coeff_z = 0.565
|
|
local delta = delta or device().time_delta
|
|
|
|
-- Get steps depending on inv_weight, heavier guns have less movement speed
|
|
-- k is modifier for ema steps to have more inertia in the beginning of movement
|
|
if not steps then
|
|
local k = max(1, 1.5 - sqrt(normalize(smoothed_values.hands_orientation_y or weapon_table[sec].hands_orientation[1], weapon_table[sec].hands_orientation[1], max_deg)))
|
|
steps = get_weapon_steps(wpn, sec) * k
|
|
end
|
|
|
|
-- Smooth the coeff
|
|
local coeff_smoothing = steps * 0.5
|
|
coeff = ema("coeff", coeff, 0, coeff_smoothing, delta)
|
|
|
|
local is_pistol = SYS_GetParam(0, sec, "kind", "") == "w_pistol" and not not_pistol_sec[sec]
|
|
local new_pos
|
|
if wpn_positions.weapon_positions[sec] then
|
|
local p = wpn_positions.weapon_positions[sec]
|
|
new_pos = {
|
|
[0] = lerp(weapon_table[sec].hands_position[0], p.x or weapon_table[sec].hands_position[0], coeff),
|
|
[1] = lerp(weapon_table[sec].hands_position[1], p.y or weapon_table[sec].hands_position[1], coeff),
|
|
[2] = lerp(weapon_table[sec].hands_position[2], p.z or weapon_table[sec].hands_position[2], coeff ^ 3.5),
|
|
}
|
|
else
|
|
-- Check for scopes on weapons
|
|
if not wpn_positions.weapon_offsets[sec] then
|
|
local parent = SYS_GetParam(0, sec, "parent_section", sec)
|
|
local scopes = str_explode(SYS_GetParam(0, parent, "scopes", ""), ",")
|
|
for k, v in pairs(scopes) do
|
|
if (parent .. "_" .. v) == sec then
|
|
wpn_positions.weapon_offsets[sec] = wpn_positions.weapon_offsets[parent]
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Add table with 0 offsets if no weapon offset found
|
|
if not wpn_positions.weapon_offsets[sec] then
|
|
wpn_positions.weapon_offsets[sec] = {
|
|
x = 0,
|
|
y = 0,
|
|
z = 0
|
|
}
|
|
end
|
|
end
|
|
|
|
local o = {
|
|
[0] = wpn_positions.weapon_offsets[sec].x or 0,
|
|
[1] = wpn_positions.weapon_offsets[sec].y or 0,
|
|
[2] = wpn_positions.weapon_offsets[sec].z or 0,
|
|
}
|
|
|
|
-- Interpolate between offseted position and desirable closer to obstacle, looks better, mostly
|
|
-- Different calculations for pistols
|
|
if is_pistol then
|
|
local target_pos = {
|
|
[0] = weapon_table[sec].hands_position[0] + settings.offset_x + custom_offsets[0],
|
|
[1] = target_pos.hands_position_pistol[1] + settings.offset_y + custom_offsets[1],
|
|
[2] = target_pos.hands_position_pistol[2] + settings.offset_z + custom_offsets[2],
|
|
}
|
|
|
|
-- local a = {
|
|
-- [0] = weapon_table[sec].hands_position[0] + o[0] * coeff,
|
|
-- [1] = weapon_table[sec].hands_position[1] + (o[1] + position_coeff_y) * coeff,
|
|
-- [2] = weapon_table[sec].hands_position[2] + (o[2] + position_coeff_z) * coeff >= 1 and coeff or coeff ^ 2,
|
|
-- }
|
|
|
|
local b = {
|
|
[0] = lerp(weapon_table[sec].hands_position[0], (o[0] + target_pos[0]), coeff),
|
|
[1] = lerp(weapon_table[sec].hands_position[1], (o[1] + target_pos[1]), coeff),
|
|
[2] = lerp(weapon_table[sec].hands_position[2], (o[2] + target_pos[2]), coeff >= 1 and coeff or coeff ^ 2),
|
|
}
|
|
|
|
-- local c = {
|
|
-- [0] = lerp(b[0], b[0], coeff),
|
|
-- [1] = lerp(b[1], b[1], coeff),
|
|
-- [2] = lerp(b[2], b[2], coeff ^ 2),
|
|
-- }
|
|
|
|
new_pos = b
|
|
else
|
|
local target_pos = {
|
|
[0] = weapon_table[sec].hands_position[0] + settings.offset_x + custom_offsets[0],
|
|
[1] = target_pos.hands_position_rifle[1] + settings.offset_y + custom_offsets[1],
|
|
[2] = target_pos.hands_position_rifle[2] + settings.offset_z + custom_offsets[2],
|
|
}
|
|
|
|
local a = {
|
|
[0] = weapon_table[sec].hands_position[0] + o[0] * coeff,
|
|
[1] = weapon_table[sec].hands_position[1] + (o[1] + position_coeff_y) * coeff,
|
|
[2] = weapon_table[sec].hands_position[2] + (o[2] + position_coeff_z) * (coeff >= 1 and coeff or coeff ^ 3.5),
|
|
}
|
|
|
|
local b = {
|
|
[0] = lerp(weapon_table[sec].hands_position[0], (o[0] + target_pos[0]), coeff),
|
|
[1] = lerp(weapon_table[sec].hands_position[1], (o[1] + target_pos[1]), coeff),
|
|
[2] = lerp(weapon_table[sec].hands_position[2], (o[2] + target_pos[2]), coeff >= 1 and coeff or coeff ^ 3.5),
|
|
}
|
|
|
|
local c = {
|
|
[0] = lerp(a[0], b[0], coeff),
|
|
[1] = lerp(a[1], b[1], coeff),
|
|
[2] = lerp(a[2], b[2], coeff >= 1 and coeff or coeff ^ 3.5),
|
|
}
|
|
|
|
new_pos = c
|
|
end
|
|
end
|
|
|
|
-- Smooth new position
|
|
new_pos = {
|
|
[0] = ema("hands_position_x", new_pos[0], weapon_table[sec].hands_position[0], steps, delta),
|
|
[1] = ema("hands_position_y", new_pos[1], weapon_table[sec].hands_position[1], steps, delta),
|
|
[2] = ema("hands_position_z", new_pos[2], weapon_table[sec].hands_position[2], steps, delta),
|
|
}
|
|
|
|
-- Randomize roll and yaw
|
|
randomize_roll_yaw()
|
|
local new_ori = {
|
|
[0] = ema("hands_orientation_x", weapon_table[sec].hands_orientation[0] + yaw * coeff, weapon_table[sec].hands_orientation[0], steps, delta),
|
|
[1] = ema("hands_orientation_y", weapon_table[sec].hands_orientation[1] + max_deg * coeff, weapon_table[sec].hands_orientation[1], steps, delta),
|
|
[2] = ema("hands_orientation_z", weapon_table[sec].hands_orientation[2] + roll * coeff, weapon_table[sec].hands_orientation[2], steps, delta),
|
|
}
|
|
return new_pos, new_ori
|
|
end
|
|
|
|
local force_disabled = false
|
|
function set_force_disabled(v)
|
|
force_disabled = v or v == nil
|
|
end
|
|
|
|
local reset_on_show_done = false
|
|
function actor_on_update(binder, delta)
|
|
if force_disabled then return end
|
|
|
|
local actor = db.actor
|
|
|
|
-- Cancel if PDA is active
|
|
if actor:active_slot() == 8 or actor:active_slot() == 14 then
|
|
return reset()
|
|
end
|
|
|
|
local wpn = actor:active_item()
|
|
|
|
-- printf("%s", delta)
|
|
|
|
-- Reset if no wpn
|
|
if not wpn then
|
|
return reset()
|
|
end
|
|
|
|
local sec = wpn:section()
|
|
local hud_sec = SYS_GetParam(0, sec, "hud")
|
|
|
|
-- Reset when no hud sec
|
|
if not hud_sec then
|
|
return reset(sec)
|
|
end
|
|
|
|
-- Reset if melee weapon or binocs
|
|
if IsMelee(wpn) or IsItem("fake_ammo_wpn", sec) or IsBinoc(sec) then
|
|
return reset(sec)
|
|
end
|
|
|
|
-- Reset if throwable weapon (grenade, bolt)
|
|
if IsBolt(wpn) or IsGrenade(wpn) or IsThrowable(wpn) then
|
|
return reset(sec)
|
|
end
|
|
|
|
-- Add to weapon table
|
|
if not weapon_table[sec] then
|
|
add_to_weapon_table(sec)
|
|
end
|
|
|
|
-- Reset HUD once on weapon raise
|
|
local state = wpn:get_state()
|
|
if state == 1 then
|
|
if not reset_on_show_done then
|
|
-- printf("reset on show for %s", sec)
|
|
reset_on_show_done = true
|
|
reset(sec)
|
|
end
|
|
else
|
|
reset_on_show_done = false
|
|
end
|
|
|
|
-- Reset if callback function set enabled to false
|
|
local flags = {
|
|
enabled = true
|
|
}
|
|
callback("actor_on_weapon_before_tilt", wpn, flags)
|
|
if not flags.enabled then
|
|
return reset(sec)
|
|
end
|
|
|
|
-- Soft reset if weapon is lowered
|
|
if actor_weapon_lowered() then
|
|
return soft_reset(wpn, sec, delta)
|
|
end
|
|
|
|
-- Cancel if detector is active
|
|
if actor:active_detector() then
|
|
return soft_reset(wpn, sec, delta)
|
|
end
|
|
|
|
-- Soft reset if hiding weapon or reloading
|
|
if state == 2 or state == 7 then
|
|
return soft_reset(wpn, sec, delta)
|
|
end
|
|
|
|
local new_pos, new_ori
|
|
if tilt_key_pressed then
|
|
new_pos, new_ori = calculate_new_position(wpn, sec, 1, delta)
|
|
else
|
|
-- Get max dist based on weapon
|
|
-- Check for scopes on weapons
|
|
if not wpn_radii[sec] then
|
|
local parent = SYS_GetParam(0, sec, "parent_section", sec)
|
|
local scopes = str_explode(SYS_GetParam(0, parent, "scopes", ""), ",")
|
|
for k, v in pairs(scopes) do
|
|
if (parent .. "_" .. v) == sec then
|
|
wpn_radii[sec] = wpn_radii[parent]
|
|
break
|
|
end
|
|
end
|
|
|
|
if not wpn_radii[sec] then
|
|
local best_match = getBestMatch(sec)
|
|
if best_match then
|
|
wpn_radii[sec] = wpn_radii[best_match]
|
|
end
|
|
end
|
|
|
|
-- Adjust by weapon kind
|
|
if not wpn_radii[sec] then
|
|
local wpn_kind = SYS_GetParam(0, sec, "kind", "")
|
|
local wpn_kind_adjustment = wpn_kind_adjustment_table[wpn_kind] or 0
|
|
wpn_radii[sec] = wpn_kind_adjustment
|
|
end
|
|
|
|
if not wpn_radii[sec] then
|
|
wpn_radii[sec] = 0
|
|
end
|
|
end
|
|
|
|
local max_dist = settings.trigger_radius
|
|
|
|
-- Adjust distance
|
|
local dist_adjustment = (
|
|
-- Adjust distance based on wpn_radii table
|
|
(wpn_radii[sec] or 0)
|
|
|
|
-- Adjust distance based on silenced weapon
|
|
+ (settings.consider_silencer and isSilencedWeapon(wpn) and 0.12 or 0)
|
|
)
|
|
-- Magnify the adjustment
|
|
* settings.trigger_radius_magnitude
|
|
|
|
-- Adjust the distance
|
|
max_dist = max_dist + dist_adjustment
|
|
|
|
-- Get target dist
|
|
local dist = max(0, get_target_dist() - 0.5)
|
|
-- printf("target_dist %s", dist)
|
|
|
|
-- Soft reset if distance is more than max_dist
|
|
if dist > max_dist then
|
|
return soft_reset(wpn, sec, delta)
|
|
end
|
|
|
|
-- Soft reset if target is enemy stalker or monster
|
|
local target_obj = get_target_obj()
|
|
if target_obj and (IsMonster(target_obj) or (IsStalker(target_obj) and xr_combat_ignore.is_enemy(target_obj, actor, true))) then
|
|
return soft_reset(wpn, sec, delta)
|
|
end
|
|
|
|
-- Soft reset is material is shootable, engine edit required
|
|
if isShootableMaterial() then
|
|
return soft_reset(wpn, sec, delta)
|
|
end
|
|
|
|
-- Don't raise before grace time expired
|
|
-- if grace_time < grace_threshold then
|
|
-- grace_time = grace_time + device().time_delta
|
|
-- return
|
|
-- end
|
|
|
|
-- Apply non linear coefficient
|
|
local coeff = random_funcs.CircularEaseOutPowered(1 - dist / max_dist, 0.65)
|
|
|
|
-- Calculate new position
|
|
new_pos, new_ori = calculate_new_position(wpn, sec, coeff, delta)
|
|
end
|
|
|
|
-- Adjust hud
|
|
enable_tilt(sec, wpn)
|
|
local animation_progress = normalize(new_ori[1], weapon_table[sec].hands_orientation[1], max_deg)
|
|
if animation_progress > 0 then
|
|
callback("actor_on_weapon_tilting", wpn, animation_progress)
|
|
end
|
|
set_weapon_position(sec, new_pos, new_ori)
|
|
|
|
end
|
|
|
|
function actor_on_weapon_zoom_in(obj)
|
|
scoped_weapon_zoomed = isScopedWeapon(obj)
|
|
end
|
|
|
|
function actor_on_weapon_zoom_out(obj)
|
|
scoped_weapon_zoomed = false
|
|
end
|
|
|
|
function on_key_press(dik)
|
|
if dik == settings.manual_tilt_key_bind then
|
|
tilt_key_pressed = not tilt_key_pressed
|
|
else
|
|
local bind = dik_to_bind(dik)
|
|
local kb = key_bindings
|
|
|
|
if bind == kb.kWPN_ZOOM
|
|
or bind == kb.kWPN_FIRE
|
|
then
|
|
-- Postpone on next tick, needed to not fire on raised state
|
|
dte.CreateTimeEvent("tilt_key_pressed_postpone", 0, 0, function()
|
|
tilt_key_pressed = false
|
|
return true
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
function actor_on_weapon_before_fire(flags)
|
|
if tilt_key_pressed then
|
|
tilt_key_pressed = false
|
|
flags.ret_value = false
|
|
end
|
|
end
|
|
|
|
function reset_settings()
|
|
load_settings()
|
|
|
|
if settings.enable_manual_tilt then
|
|
RegisterScriptCallback("on_key_press", on_key_press)
|
|
else
|
|
tilt_key_pressed = false
|
|
UnregisterScriptCallback("on_key_press", on_key_press)
|
|
end
|
|
|
|
if settings.enabled then
|
|
RegisterScriptCallback("actor_on_update", actor_on_update)
|
|
else
|
|
local sec
|
|
if db.actor then
|
|
local wpn = db.actor:active_item()
|
|
sec = wpn and wpn:section()
|
|
end
|
|
reset(sec)
|
|
UnregisterScriptCallback("actor_on_update", actor_on_update)
|
|
end
|
|
end
|
|
|
|
function actor_on_first_update()
|
|
-- firepos_state = debug_enabled() and get_console_cmd(0, "g_firepos") or 0
|
|
firepos_state = 0
|
|
reset_settings()
|
|
end
|
|
|
|
-- Reset without parameter, for callbacks
|
|
function reset_func()
|
|
reset()
|
|
end
|
|
|
|
function on_game_start()
|
|
RegisterScriptCallback("on_option_change", reset_settings)
|
|
RegisterScriptCallback("actor_on_before_death", reset_func)
|
|
RegisterScriptCallback("actor_on_net_destroy", reset_func)
|
|
RegisterScriptCallback("on_before_level_changing", reset_func)
|
|
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
|
|
RegisterScriptCallback("actor_on_update", actor_on_update)
|
|
RegisterScriptCallback("actor_on_weapon_zoom_in", actor_on_weapon_zoom_in)
|
|
RegisterScriptCallback("actor_on_weapon_zoom_out", actor_on_weapon_zoom_out)
|
|
RegisterScriptCallback("actor_on_weapon_before_fire", actor_on_weapon_before_fire)
|
|
RegisterScriptCallback("on_key_press", on_key_press)
|
|
end
|
|
|
|
-- Patches
|
|
-- Safemode when weapon is raised manually
|
|
actor_is_safemode = xr_conditions.actor_is_safemode
|
|
xr_conditions.actor_is_safemode = function(actor, npc)
|
|
return actor_is_safemode(actor, npc) or (tilt_enabled and tilt_key_pressed)
|
|
end
|
|
|
|
function muzzle_pos()
|
|
local item = db.actor:active_item()
|
|
if not item then return end
|
|
local pos = utils_obj.safe_bone_pos(item, "muzzle")
|
|
return game.world2ui(pos, true)
|
|
end
|
|
|