------------------------------------------------------------------------------ ---------------------------------- Arms ------------------------------------- ------------------------------------------------------------------------------ local aim_anm = {"earthquake_1","earthquake_2"} local mcm_arms_penalty = zzz_player_injuries_mcm.get_config("arm_opt") local mcm_arms_min_hp = zzz_player_injuries_mcm.get_config("arm_pen_min_hp") local mcm_arms_cam_pwr = zzz_player_injuries_mcm.get_config("arm_anm_pwr") function shaking_hands() if not mcm_arms_penalty then return end local activeslot = db.actor:active_slot() if not (activeslot == 2 or activeslot == 3 or activeslot == 1) then return end local item_in_slot = db.actor:item_in_slot(activeslot) if not (item_in_slot) then return end local cam_power = 0 local weight = item_in_slot:weight() local body_state = get_body_state() local body_state_mult = (body_state == "crouch" and 0.75) or (body_state == "low_crouch" and 0.5) or 1 local health, timedhp, maxhp = zzz_player_injuries.health, zzz_player_injuries.timedhp, zzz_player_injuries.maxhp local max_left_arm, max_right_arm = maxhp.rightarm, maxhp.leftarm local left_arm, right_arm = health.rightarm + timedhp.rightarm, health.leftarm + timedhp.leftarm local arms_ratio = (left_arm + right_arm) / (max_left_arm + max_right_arm) if arms_ratio >= (mcm_arms_min_hp / 100) then return end -- lower penalty if both arms almost equally damaged (except when both have low hp) if (left_arm / right_arm) > 0.6 and (right_arm / left_arm) > 0.6 then cam_power = 0.08 * weight / arms_ratio else cam_power = 0.12 * weight / arms_ratio end cam_power = cam_power * body_state_mult cam_power = clamp(cam_power, 0, 3) cam_power = cam_power * mcm_arms_cam_pwr if item_in_slot:weapon_is_scope() then cam_power = cam_power * 0.2 end -- play anm level.add_cam_effector("camera_effects\\" .. aim_anm[math.random(#aim_anm)] .. ".anm", 9921, true, "", 0, true, cam_power) end function actor_on_weapon_zoom_in() shaking_hands() end function actor_on_weapon_zoom_out() level.remove_cam_effector(9921) end ------------------------------------------------------------------------------ ---------------------------------- Legs -------------------------------------- ------------------------------------------------------------------------------ local rightleg_anm = {"med1","med4"} local leftleg_anm = {"med2","med3"} local bothlegs_anm = {"strong1","strong2","strong4"} local limp_flag local mcm_legs_penalty = zzz_player_injuries_mcm.get_config("leg_opt") local mcm_legs_min_hp = zzz_player_injuries_mcm.get_config("leg_pen_min_hp") local mcm_legs_anm_pwr = zzz_player_injuries_mcm.get_config("leg_anm_pwr") local mcm_legs_dmg_min_hp = zzz_player_injuries_mcm.get_config("leg_dmg_min_hp") local mcm_legs_new_voice = zzz_player_injuries_mcm.get_config("new_voice_sounds") local legs_speed_tmr = 0 function limp_speed_slow() if not mcm_legs_penalty then return end local tg = time_global() if legs_speed_tmr > tg then return end legs_speed_tmr = tg + 2000 local health, timedhp, maxhp = zzz_player_injuries.health, zzz_player_injuries.timedhp, zzz_player_injuries.maxhp local legs_summ = health.leftleg + timedhp.leftleg + health.rightleg + timedhp.rightleg local max_legs_summ = maxhp.leftleg + maxhp.rightleg local legs_ratio = legs_summ / max_legs_summ if legs_ratio >= (mcm_legs_min_hp / 100) then speed.remove_speed("bhs_speed_run") return end local val = normalize(legs_summ, -1, max_legs_summ) speed.add_speed("bhs_speed_run", val, false, true) end local legs_limp_tmr = 0 function legs_limp_set() if not mcm_legs_penalty then return end local tg = time_global() if legs_limp_tmr > tg then return end legs_limp_tmr = tg + 2000 local health, timedhp, maxhp = zzz_player_injuries.health, zzz_player_injuries.timedhp, zzz_player_injuries.maxhp local right_leg, left_leg = health.leftleg + timedhp.leftleg, health.rightleg + timedhp.rightleg -- right is left local legs_summ = right_leg + left_leg local max_right_leg, max_left_leg = maxhp.leftleg, maxhp.rightleg local max_legs_summ = max_right_leg + max_left_leg local legs_ratio = legs_summ / max_legs_summ if legs_ratio >= (mcm_legs_min_hp / 100) then limp_flag = nil return end if (right_leg == left_leg) and (legs_summ < max_legs_summ) then limp_flag = "both" elseif (left_leg < max_left_leg) and (left_leg < right_leg) then limp_flag = "left" elseif (right_leg < max_right_leg) and (right_leg < left_leg) then limp_flag = "right" end end local legs_anm_tmr = 0 function legs_limp_animation() if not mcm_legs_penalty then return end if not limp_flag then return end local tg = time_global() -- sprint is tearing animations apart, the only way is to increase delay for it if legs_anm_tmr > tg then return end local add_time = IsMoveState("mcSprint") and 1000 or 500 legs_anm_tmr = tg + add_time local health, timedhp, maxhp = zzz_player_injuries.health, zzz_player_injuries.timedhp, zzz_player_injuries.maxhp local right_leg, left_leg = health.leftleg + timedhp.leftleg, health.rightleg + timedhp.rightleg local max_right_leg, max_left_leg = maxhp.leftleg, maxhp.rightleg if limp_flag == "right" then local val = normalize(right_leg, max_right_leg, 0) * mcm_legs_anm_pwr * 3 level.add_cam_effector("camera_effects\\switch\\" .. rightleg_anm[math.random(#rightleg_anm)] .. ".anm", 99251, false, "", 0, false, val) elseif limp_flag == "left" then local val = normalize(left_leg, max_left_leg, 0) * mcm_legs_anm_pwr * 3 level.add_cam_effector("camera_effects\\switch\\" .. leftleg_anm[math.random(#leftleg_anm)] .. ".anm", 99261, false, "", 0, false, val) elseif limp_flag == "both" then local val = normalize( left_leg + right_leg, max_left_leg + max_right_leg, 0 ) * mcm_legs_anm_pwr * 3 level.add_cam_effector("camera_effects\\switch\\" .. bothlegs_anm[math.random(#bothlegs_anm)] .. ".anm", 99271, false, "", 0, false, val) end end local legs_dmg_tmr = 0 function legs_limp_damage() if not mcm_legs_penalty then return end if not IsMoveState("mcSprint") then return end local tg = time_global() if legs_dmg_tmr > tg then return end legs_dmg_tmr = tg + 2500 local health, timedhp, maxhp = zzz_player_injuries.health, zzz_player_injuries.timedhp, zzz_player_injuries.maxhp local right_leg, left_leg = health.leftleg + timedhp.leftleg, health.rightleg + timedhp.rightleg local max_right_leg, max_left_leg = maxhp.leftleg, maxhp.rightleg local right_leg_pwr = (right_leg / max_right_leg) < (mcm_legs_dmg_min_hp / 100) and 0.04 or 0 local left_leg_pwr = (left_leg / max_left_leg) < (mcm_legs_dmg_min_hp / 100) and 0.04 or 0 -- return if legs hp higher than mcm setting local hit_pwr = (right_leg_pwr + left_leg_pwr) / 2 if hit_pwr <= 0 then return end -- return if chance didn't proc local legs_ratio = (right_leg + left_leg) / (max_right_leg + max_left_leg) local chance = (1 - legs_ratio) * 0.5 if chance < math.random() then return end local file if mcm_legs_new_voice then local helmet = db.actor:item_in_slot(12) or db.actor:get_current_outfit() file = "bhs\\" .. (helmet and "m_" or "") .. "pain_" .. math.random(1, 13) else file = "actor\\pain_" .. math.random(1, 8) end file_to_say = sound_object(file) file_to_say:play(db.actor, 0, sound_object.s2d) level.add_pp_effector("blur_bhs.ppe", math.random(655808, 655820), false) db.actor:set_health_ex(db.actor.health - hit_pwr) end function actor_on_footstep() legs_limp_animation() legs_limp_damage() end ------------------------------------------------------------------------------ --------------------------------- Torso -------------------------------------- ------------------------------------------------------------------------------ local mcm_torso_penalty = zzz_player_injuries_mcm.get_config("torso_opt") local torso_tmr = 0 function torso_penalty() if not mcm_torso_penalty then return end local tg = time_global() if torso_tmr > tg then return end torso_tmr = tg + 1000 local torso = zzz_player_injuries.health.torso + zzz_player_injuries.timedhp.torso local max_torso = zzz_player_injuries.maxhp.torso local torso_ratio = torso / max_torso local cap_health = normalize(torso_ratio, -2, 1) if db.actor.health > cap_health then db.actor.health = cap_health end end ------------------------------------------------------------------------------ --------------------------------- Head -------------------------------------- ------------------------------------------------------------------------------ -- zzz_player_injuries.hit_on_update starts it local mcm_head_penalty = zzz_player_injuries_mcm.get_config("head_opt") local mcm_head_min_dmg = zzz_player_injuries_mcm.get_config("head_pen_min_hp") local mcm_head_anm_pwr = zzz_player_injuries_mcm.get_config("head_anm_pwr") head_tbl = {} function head_concussion_start(damage) if not mcm_head_penalty then return end if damage < mcm_head_min_dmg then return end -- new hit during stage 2, override if head_tbl.stage and head_tbl.stage == 2 then empty_table(head_tbl) end -- stage 1 if (not head_tbl.stage) then head_tbl.stage = 1 head_tbl.damage = damage local eff_pwr = (head_tbl.damage / zzz_player_injuries.maxhp.head) * mcm_head_anm_pwr level.add_pp_effector("concussion_bhs.ppe", 99123, false) level.set_pp_effector_factor(99123, (eff_pwr)) head_snd_off() CreateTimeEvent("bhs_conc_e", "bhs_conc_a", 10, head_concussion_mid) end end function head_concussion_mid() -- set stage 2 head_tbl.stage = 2 head_snd_on() local snd = sound_object("bhs\\tinnitusx") snd:play_no_feedback(db.actor, sound_object.s2d, 0, VEC_ZERO, 1.0, 1.0) snd.volume = 2 return true end local head_tmr = 0 function head_concussion_update() -- stage 2 if not (head_tbl.stage and head_tbl.stage == 2) then return end local tg = time_global() if head_tmr > tg then return end head_tmr = tg + 15000 -- reduce passive damage over time (5 damage effect needs 2.5 mins) head_tbl.damage = head_tbl.damage - 0.5 -- remove effect if "damage" drops below zero if head_tbl.damage <= 0 then empty_table(head_tbl) exec_console_cmd("cam_inert 0") return end -- remove effect if head was healed to full (timedhp on head does not count) local head, max_head = zzz_player_injuries.health.head, zzz_player_injuries.maxhp.head if head >= max_head then empty_table(head_tbl) exec_console_cmd("cam_inert 0") return end -- play ppe and anm local eff_pwr = (head_tbl.damage / zzz_player_injuries.maxhp.head) * mcm_head_anm_pwr if eff_pwr > math.random() then level.add_cam_effector("camera_effects\\wake_up.anm", 96872, false, "", 0, true, eff_pwr) end level.add_pp_effector("concussion_post_bhs.ppe", 99133, false) level.set_pp_effector_factor(99133, eff_pwr) -- cam_inert xd exec_console_cmd("cam_inert " .. eff_pwr) end function head_snd_off() _G.amb_vol = math.max(get_console_cmd(2, "snd_volume_eff"), _G.amb_vol or 0) exec_console_cmd("snd_volume_eff 0.01") head_tbl.vol = _G.amb_vol end function head_snd_on() exec_console_cmd("snd_volume_eff ".. (head_tbl.vol or tostring(_G.amb_vol))) _G.amb_vol = 0 head_tbl.vol = 0 end function actor_on_first_update() -- for reloading during stage 1 if head_tbl.stage and head_tbl.stage == 1 then head_tbl.stage = 2 head_snd_on() end exec_console_cmd("cam_inert 0") end ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ function get_body_state() if not IsMoveState("mcCrouch") then return "stand" end return IsMoveState("mcAccel") and "low_crouch" or "crouch" end function actor_on_update() limp_speed_slow() legs_limp_set() torso_penalty() head_concussion_update() end function on_option_change() mcm_arms_penalty = zzz_player_injuries_mcm.get_config("arm_opt") mcm_arms_min_hp = zzz_player_injuries_mcm.get_config("arm_pen_min_hp") mcm_arms_cam_pwr = zzz_player_injuries_mcm.get_config("arm_anm_pwr") mcm_legs_penalty = zzz_player_injuries_mcm.get_config("leg_opt") mcm_legs_min_hp = zzz_player_injuries_mcm.get_config("leg_pen_min_hp") mcm_legs_anm_pwr = zzz_player_injuries_mcm.get_config("leg_anm_pwr") mcm_legs_dmg_min_hp = zzz_player_injuries_mcm.get_config("leg_dmg_min_hp") mcm_legs_new_voice = zzz_player_injuries_mcm.get_config("new_voice_sounds") mcm_torso_penalty = zzz_player_injuries_mcm.get_config("torso_opt") mcm_head_penalty = zzz_player_injuries_mcm.get_config("head_opt") mcm_head_min_dmg = zzz_player_injuries_mcm.get_config("head_pen_min_hp") mcm_head_anm_pwr = zzz_player_injuries_mcm.get_config("head_anm_pwr") end function save_state(m_data) m_data.bhs_head_tbl = head_tbl end function load_state(m_data) head_tbl = m_data.bhs_head_tbl or {} end function on_game_start() 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_footstep", actor_on_footstep) RegisterScriptCallback("actor_on_first_update", actor_on_first_update) RegisterScriptCallback("actor_on_update", actor_on_update) RegisterScriptCallback("on_option_change", on_option_change) RegisterScriptCallback("save_state", save_state) RegisterScriptCallback("load_state", load_state) end