From a9cc26679a701fbba64e2906e81d9a748339e3e6 Mon Sep 17 00:00:00 2001 From: MotoLegacy Date: Thu, 28 Nov 2024 12:35:16 -0800 Subject: [PATCH] CLIENT: Add Hold Breath to Steady Rifle --- source/client/defs/custom.qc | 2 + source/client/hud.qc | 43 ++++++++++++++ source/client/main.qc | 105 ++++++++++++++++++++++++++++++++--- 3 files changed, 141 insertions(+), 9 deletions(-) diff --git a/source/client/defs/custom.qc b/source/client/defs/custom.qc index f03dee8..4e2612c 100644 --- a/source/client/defs/custom.qc +++ b/source/client/defs/custom.qc @@ -102,6 +102,8 @@ float active_achievement; float current_achievement_page; float achievement_pages; +float sniper_hold_breath; + float K_LEFTDOWN, K_RIGHTDOWN, K_BACKDOWN, K_FORWARDDOWN; diff --git a/source/client/hud.qc b/source/client/hud.qc index 8faf5f7..ec453d5 100644 --- a/source/client/hud.qc +++ b/source/client/hud.qc @@ -1299,6 +1299,49 @@ void() Draw_Crosshair = drawfill('0 0 0', [g_width/2 - g_height/2, g_height, 0], '0 0 0', 1, 0); drawpic([(g_width/2 - g_height/2),0,0], "gfx/hud/scope_nb.tga", [g_height, g_height, 1], [1,1,1], 1); drawfill([(g_width/2 + g_height/2),0,0], [g_width, g_height, 0], '0 0 0', 1, 0); + + // Draw "Hold Breath" text if we aren't already doing so and we don't have Deadshot Daiquiri. + if (sniper_hold_breath != true && !(getstatf(STAT_PERKS) & P_DEAD)) { + // Grab the bind for Sprint/Hold Breath + string breath_button = ""; + string space_width = ""; + + float argc = tokenize(findkeysforcommandex("impulse 23")); + for (int i = 0; i < argc; i++) { + breath_button = strtoupper(argv(i)); + float bind_is_gamepad = Key_IsControllerGlyph(breath_button); + + if (bind_is_gamepad && last_input_was_gamepad) + break; + else if (!bind_is_gamepad && !last_input_was_gamepad) + break; + } + + if (breath_button == "") + breath_button = "UNBOUND"; + + string breath_button_string = " "; + + // If this is a gamepad button, the space we want to reserve + // in the string should be a fixed width. + if (!Key_IsControllerGlyph(breath_button)) { + breath_button_string = breath_button; + } + + string breath_string = strcat("Hold ", breath_button_string, " to steady"); + float print_width = getTextWidth(breath_string, 12); + float x = (g_width - print_width)/2; + Draw_String([x, 65, 0], breath_string, [12, 12, 0], [1, 1, 1], 1, 0); + + // Draw highlighted button + float button_width = x + getTextWidth("Hold ", 12); + + if (Key_IsControllerGlyph(breath_button)) + Key_DrawControllerGlyph([button_width - 5, 62], breath_button, [18, 18]); + else + Draw_String([button_width, 65, 0], breath_button, [12, 12, 0], [1, 1, 0], 1, 0); + } + return; } diff --git a/source/client/main.qc b/source/client/main.qc index 50fac49..bc98ec6 100644 --- a/source/client/main.qc +++ b/source/client/main.qc @@ -609,22 +609,102 @@ float(float a) angledelta = } //MOVEME -float delta_pitch, delta_yaw; +vector current_sway; +float current_intensity; +float target_intensity; vector sniper_sway; -// Sways the camera while scoped in. -void() Camera_SniperSway = +float sniper_breath_starttime; +float sniper_breath_endtime; +float sniper_breath_counter; +float sniper_breath_exhaustion_starttime; +float sniper_breath_exhaustion_endtime; +float sniper_breath_delay; +float sniper_hold_breath; + +#define SNIPER_SWAY_INTENSITY_FOCUSED 0.05 +#define SNIPER_SWAY_INTENSITY_NORMAL 0.5 +#define SNIPER_SWAY_INTENSITY_EXHAUSTION 2 + +void(float inhale) Sniper_HoldBreath = { + if (sniper_breath_delay > time) + return; + if (getstatf(STAT_WEAPONZOOM) != 2 || (getstatf(STAT_PERKS) & P_DEAD)) { - sniper_sway = '0 0 0'; return; } - delta_pitch = (cos(cltime/0.7) + cos(cltime) + sin(cltime/1.1)) * 0.5; - delta_yaw = (sin(cltime/0.4) + cos(cltime/0.56) + sin(cltime)) * 0.5; - - sniper_sway[0] = angledelta(delta_pitch); - sniper_sway[1] = angledelta(delta_yaw); + if (inhale) { + if (sniper_hold_breath == true) + return; + sniper_breath_starttime = time; + sniper_breath_endtime = time + 4; + sniper_hold_breath = true; + localsound("sounds/player/inhale.wav", 0, 1); + } else { + if (sniper_hold_breath == false) + return; + sniper_breath_endtime = sniper_breath_starttime = 0; + sniper_hold_breath = false; + sniper_breath_delay = time + 1; + localsound("sounds/player/exhale.wav", 0, 1); + } }; + +void() Camera_SniperSway = +{ + // Determine target intensity based on state + target_intensity = SNIPER_SWAY_INTENSITY_NORMAL; + + if (getstatf(STAT_WEAPONZOOM) != 2 || (getstatf(STAT_PERKS) & P_DEAD)) { + current_intensity = 0; + current_sway = '0 0 0'; + target_intensity = 0; + sniper_breath_counter = 0; + } else { + // We are focused. + if (sniper_breath_endtime > time) { + target_intensity = SNIPER_SWAY_INTENSITY_FOCUSED; + + float breath_duration = floor(time - sniper_breath_starttime); + + // Play heartbeat sfx at every second interval. + if (breath_duration != sniper_breath_counter) { + sniper_breath_counter = breath_duration; + localsound("sounds/player/heart.wav", 0, 5); + } + } + // Time has expired and we're out of breath, begin exhaustion. + else if (sniper_breath_endtime < time && sniper_hold_breath == true) { + Sniper_HoldBreath(false); + sniper_breath_exhaustion_starttime = time; + sniper_breath_exhaustion_endtime = time + 4; + sniper_breath_delay = time + 4; + } + + // Slowly decrease in intensity from fully exhausted to normal. + if (sniper_breath_exhaustion_endtime > time) { + float progress = (time - sniper_breath_exhaustion_starttime) / (sniper_breath_exhaustion_endtime - sniper_breath_exhaustion_starttime); + target_intensity = SNIPER_SWAY_INTENSITY_EXHAUSTION + progress * (SNIPER_SWAY_INTENSITY_NORMAL - SNIPER_SWAY_INTENSITY_EXHAUSTION); + } + } + + // Smoothly interpolate intensity towards target + current_intensity += (target_intensity - current_intensity) * (clframetime * 5); // Adjust 0.1 for smoother/faster transitions + + // Calculate sway deltas based on cltime and intensity + float sway_pitch = (cos(cltime / 0.7) + cos(cltime) + sin(cltime / 1.1)); + float sway_yaw = (sin(cltime / 0.4) + cos(cltime / 0.56) + sin(cltime)); + + // Update current sway using the smoothly interpolated intensity + current_sway[0] = sway_pitch * current_intensity; + current_sway[1] = sway_yaw * current_intensity; + + // Apply the smooth sway to sniper_sway + sniper_sway[0] = angledelta(current_sway[0]); + sniper_sway[1] = angledelta(current_sway[1]); +}; + float gamepad_enabled; // CALLED EVERY CLIENT RENDER FRAME @@ -782,6 +862,13 @@ void(float scanx, float setval) Input_Movecheck = tokenize(findkeysforcommand("+back")); if (scanx == stof(argv(0))) K_BACKDOWN = setval; + + float argc = tokenize(findkeysforcommand("impulse 23")); + for (int i = 0; i < argc; i++) { + if (scanx == stof(argv(i))) { + Sniper_HoldBreath(setval); + } + } } noref float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent =