From 8abe065b7445c0c411535bda611f3e8b65a1efcc Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Fri, 13 Mar 2026 12:51:55 +0000 Subject: [PATCH 1/3] init --- game/neo/scripts/HudLayout.res | 10 +++ src/game/client/CMakeLists.txt | 2 + src/game/client/neo/c_neo_player.h | 3 +- .../neo/ui/neo_hud_walking_indicator.cpp | 81 +++++++++++++++++++ .../client/neo/ui/neo_hud_walking_indicator.h | 34 ++++++++ src/game/server/neo/neo_player.cpp | 32 ++++++++ src/game/server/neo/neo_player.h | 3 + src/game/shared/baseplayer_shared.cpp | 23 ++---- src/game/shared/neo/neo_gamerules.h | 1 + src/game/shared/neo/neo_player_shared.cpp | 36 +++++++++ 10 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 src/game/client/neo/ui/neo_hud_walking_indicator.cpp create mode 100644 src/game/client/neo/ui/neo_hud_walking_indicator.h diff --git a/game/neo/scripts/HudLayout.res b/game/neo/scripts/HudLayout.res index c5f1072b6..941ed7247 100644 --- a/game/neo/scripts/HudLayout.res +++ b/game/neo/scripts/HudLayout.res @@ -908,6 +908,16 @@ "sprint_text_color" "255 255 255 100" "sprint_color" "255 255 255 150" } + neo_walking_indicator + { + "fieldName" "neo_walking_indicator" + "xpos" "208" + "ypos" "464" + "wide" "24" + "tall" "24" + "visible" "1" + "enabled" "1" + } RoundResult { "fieldName" "RoundResult" diff --git a/src/game/client/CMakeLists.txt b/src/game/client/CMakeLists.txt index da9041233..b161820e9 100644 --- a/src/game/client/CMakeLists.txt +++ b/src/game/client/CMakeLists.txt @@ -1610,6 +1610,8 @@ target_sources_grouped( neo/ui/neo_hud_round_state.h neo/ui/neo_hud_startup_sequence.cpp neo/ui/neo_hud_startup_sequence.h + neo/ui/neo_hud_walking_indicator.cpp + neo/ui/neo_hud_walking_indicator.h neo/ui/neo_hud_worldpos_marker.cpp neo/ui/neo_hud_worldpos_marker.h neo/ui/neo_hud_worldpos_marker_generic.cpp diff --git a/src/game/client/neo/c_neo_player.h b/src/game/client/neo/c_neo_player.h index 283a199c4..ee5375a05 100644 --- a/src/game/client/neo/c_neo_player.h +++ b/src/game/client/neo/c_neo_player.h @@ -188,7 +188,8 @@ class C_NEO_Player : public C_HL2MP_Player #ifdef GLOWS_ENABLE void UpdateGlowEffects(int iNewTeam); #endif // GLOWS_ENABLE - + bool ShouldPlayerMakeFootsteps(float speed = -1.f); + float SpeedFractionToSoundThreshold(float speed = -1.f); private: char m_sNameWithTakeoverContextProcessingBuffer[MAX_PLAYER_NAME_LENGTH]; diff --git a/src/game/client/neo/ui/neo_hud_walking_indicator.cpp b/src/game/client/neo/ui/neo_hud_walking_indicator.cpp new file mode 100644 index 000000000..043bc6562 --- /dev/null +++ b/src/game/client/neo/ui/neo_hud_walking_indicator.cpp @@ -0,0 +1,81 @@ +#include "cbase.h" +#include "neo_hud_walking_indicator.h" + +#include "iclientmode.h" +#include + +#include "c_neo_player.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DECLARE_NAMED_HUDELEMENT(CNEOHud_WalkingIndicator, neo_walking_indicator); + +NEO_HUD_ELEMENT_DECLARE_FREQ_CVAR(WalkingIndicator, 0.1) + +CNEOHud_WalkingIndicator::CNEOHud_WalkingIndicator(const char *pElementName, vgui::Panel *parent) + : CHudElement(pElementName), Panel(parent, pElementName) +{ + SetAutoDelete(true); + m_iHideHudElementNumber = NEO_HUD_ELEMENT_WALKING_INDICATOR; + + if (parent) { + SetParent(parent); + } + else + { + SetParent(g_pClientMode->GetViewport()); + } + + m_hWalkingIndicatorTexture = vgui::surface()->CreateNewTextureID(); + Assert(m_hWalkingIndicatorTexture > 0); + vgui::surface()->DrawSetTextureFile(m_hWalkingIndicatorTexture, "vgui/hud/player/walkingIndicator", 1, false); + + SetVisible(true); +} + +void CNEOHud_WalkingIndicator::ApplySchemeSettings(vgui::IScheme* pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBounds(xpos, ypos-tall, wide, tall); + SetFgColor(COLOR_TRANSPARENT); + SetBgColor(COLOR_TRANSPARENT); +} + +void CNEOHud_WalkingIndicator::UpdateStateForNeoHudElementDraw() +{ +} + +void CNEOHud_WalkingIndicator::DrawNeoHudElement() +{ + if (!ShouldDraw()) + return; + + C_NEO_Player* pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer(); + if (!pLocalPlayer) + return; + + C_NEO_Player* pTargetPlayer = pLocalPlayer->IsObserver() && (pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pLocalPlayer->GetObserverMode() == OBS_MODE_CHASE) ? static_cast(pLocalPlayer->GetObserverTarget()) : pLocalPlayer; + if (!pTargetPlayer) + return; + + if (!pTargetPlayer->IsAlive() || !pTargetPlayer->IsWalking())// && !pTargetPlayer->IsInAim()) player is also silent if aiming and not abusing the threshold, but its much harder to make noise when aiming, better for the indicator to strictly appear when walk is pressed + return; + + const float fractionTowardsMakingSound = Max(0.f, Min(1.f, pTargetPlayer->SpeedFractionToSoundThreshold())); + + vgui::surface()->DrawSetTexture(m_hWalkingIndicatorTexture); + vgui::surface()->DrawSetColor(COLOR_WHITE); + const float ICON_WIDTH = 1 / 2.f; + const float ICON_HEIGHT = 1 / 2.f; + vgui::surface()->DrawTexturedSubRect(0, 0, wide, tall, 0, ICON_WIDTH, ICON_HEIGHT, 2 * ICON_HEIGHT); + vgui::surface()->DrawSetColor(255, 255 - (255 * fractionTowardsMakingSound), 255 - (255 * fractionTowardsMakingSound), 255); + vgui::surface()->DrawTexturedSubRect(0, tall * (1 - fractionTowardsMakingSound), wide, tall, 0, ICON_HEIGHT * (1 - fractionTowardsMakingSound), ICON_WIDTH, ICON_HEIGHT); +} + +void CNEOHud_WalkingIndicator::Paint() +{ + BaseClass::Paint(); + PaintNeoElement(); +} \ No newline at end of file diff --git a/src/game/client/neo/ui/neo_hud_walking_indicator.h b/src/game/client/neo/ui/neo_hud_walking_indicator.h new file mode 100644 index 000000000..5c4054aa3 --- /dev/null +++ b/src/game/client/neo/ui/neo_hud_walking_indicator.h @@ -0,0 +1,34 @@ +#pragma once + +#include "neo_hud_childelement.h" +#include "hudelement.h" +#include + +class CNEOHud_WalkingIndicator : public CNEOHud_ChildElement, public CHudElement, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE(CNEOHud_WalkingIndicator, Panel); + +public: + CNEOHud_WalkingIndicator(const char *pElementName, vgui::Panel *parent = NULL); + void ApplySchemeSettings(vgui::IScheme* pScheme) override; + + virtual void Paint() override; + +protected: + virtual void UpdateStateForNeoHudElementDraw() override; + virtual void DrawNeoHudElement() override; + virtual ConVar* GetUpdateFrequencyConVar() const override; + +private: + vgui::HTexture m_hWalkingIndicatorTexture = 0; + + CPanelAnimationVarAliasType(int, xpos, "xpos", "r203", "proportional_xpos"); + CPanelAnimationVarAliasType(int, ypos, "ypos", "446", "proportional_ypos"); + CPanelAnimationVarAliasType(int, wide, "wide", "203", "proportional_xpos"); + CPanelAnimationVarAliasType(int, tall, "tall", "32", "proportional_ypos"); + CPanelAnimationVarAliasType(int, visible, "visible", "1", "int"); + CPanelAnimationVarAliasType(int, enabled, "enabled", "1", "int"); + +private: + CNEOHud_WalkingIndicator(const CNEOHud_WalkingIndicator&other); +}; \ No newline at end of file diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index cf2660fef..3ff3e4df4 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -172,6 +172,8 @@ ConVar sv_neo_warmup_godmode("sv_neo_warmup_godmode", "0", FCVAR_REPLICATED, "If ConVar bot_class("bot_class", "-1", 0, "Force all bots to spawn with the specified class number, or -1 to disable.", true, NEO_CLASS_RANDOM, true, NEO_CLASS_LOADOUTABLE_COUNT-1); static void BotChangeClassFn(const CCommand& args); ConCommand bot_changeclass("bot_changeclass", BotChangeClassFn, "Force all bots to switch to the specified class number."); +static void BotChangeSkinFn(const CCommand& args); +ConCommand bot_changeskin("bot_changeskin", BotChangeSkinFn, "Force all bots to switch to the specified skin number."); // Bot Cloak Detection Thresholds // Base detection chance ratio (0.0 - 1.0) for bots to notice a cloaked target based on difficulty @@ -4315,3 +4317,33 @@ static void BotChangeClassFn(const CCommand& args) player->RequestSetClass(botClass); } } + +static void BotChangeSkinFn(const CCommand& args) +{ + constexpr int minValue = NEO_SKIN_FIRST; + constexpr int maxValue = NEO_SKIN__ENUM_COUNT - 1; + + const auto nag = [&args]() { + Msg("Format: %s \n", args.Arg(0), minValue, maxValue); + }; + + if (args.ArgC() != 2) + { + nag(); + return; + } + + const int botSkin = V_atoi(args.Arg(1)); + if (botSkin < minValue || botSkin > maxValue) + { + nag(); + return; + } + + for (int i = 1; i <= gpGlobals->maxClients; ++i) + { + auto* player = assert_cast(UTIL_PlayerByIndex(i)); + if (player && player->IsBot() && player->GetTeamNumber() >= FIRST_GAME_TEAM) + player->RequestSetSkin(botSkin); + } +} diff --git a/src/game/server/neo/neo_player.h b/src/game/server/neo/neo_player.h index 6e9a15f7b..9cfab5f8e 100644 --- a/src/game/server/neo/neo_player.h +++ b/src/game/server/neo/neo_player.h @@ -238,6 +238,9 @@ class CNEO_Player : public CHL2MP_Player void BecomeJuggernaut(); void SpawnJuggernautPostDeath(); + bool ShouldPlayerMakeFootsteps(float speed = -1.f); + float SpeedFractionToSoundThreshold(float speed = -1.f); + private: bool m_bAllowGibbing; diff --git a/src/game/shared/baseplayer_shared.cpp b/src/game/shared/baseplayer_shared.cpp index 23e4d887b..daff64bef 100644 --- a/src/game/shared/baseplayer_shared.cpp +++ b/src/game/shared/baseplayer_shared.cpp @@ -693,29 +693,18 @@ void CBasePlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOri } #ifdef NEO - // Changing movement direction, looking around, wall-running accelerate the player. Threshold should be lower than regular speed, but higher than walk/aim speed - constexpr float SILENT_THRESHOLD_GRACE = 0.7f; -#endif //NEO + if (neoPlayer->ShouldPlayerMakeFootsteps(speed)) + { + return; + } +#endif // NEO + // play the sound // 65% volume if ducking if ( GetFlags() & FL_DUCKING ) { fvol *= 0.65; -#ifdef NEO - if ((neoPlayer->IsInAim() || neoPlayer->IsWalking()) && speed <= (neoPlayer->GetCrouchSpeed() * SILENT_THRESHOLD_GRACE)) - { - return; - } -#endif // NEO - } -#ifdef NEO - - else if ((neoPlayer->IsInAim() || neoPlayer->IsWalking()) && speed <= (neoPlayer->GetNormSpeed() * SILENT_THRESHOLD_GRACE)) - { - return; } - -#endif PlayStepSound( feet, psurface, fvol, false ); } diff --git a/src/game/shared/neo/neo_gamerules.h b/src/game/shared/neo/neo_gamerules.h index c8cbfd2d3..8fc80ae95 100644 --- a/src/game/shared/neo/neo_gamerules.h +++ b/src/game/shared/neo/neo_gamerules.h @@ -158,6 +158,7 @@ enum NeoHudElements : NEO_HUD_BITS_UNDERLYING_TYPE { NEO_HUD_ELEMENT_SCOREBOARD = (static_cast(1) << 14), NEO_HUD_ELEMENT_PLAYER_PING = (static_cast(1) << 15), NEO_HUD_ELEMENT_WORLDPOS_MARKER_ENT = (static_cast(1) << 16), + NEO_HUD_ELEMENT_WALKING_INDICATOR = (static_cast(1) << 17), }; class CNEORules : public CHL2MPRules, public CGameEventListener diff --git a/src/game/shared/neo/neo_player_shared.cpp b/src/game/shared/neo/neo_player_shared.cpp index fb8cdde29..fd25e2e89 100644 --- a/src/game/shared/neo/neo_player_shared.cpp +++ b/src/game/shared/neo/neo_player_shared.cpp @@ -403,3 +403,39 @@ void CNEO_Player::CheckAimButtons() Weapon_SetZoom(false); } } + +// Changing movement direction, looking around, wall-running accelerate the player. Threshold should be lower than regular speed, but higher than walk/aim speed +constexpr float SILENT_THRESHOLD_GRACE = 0.7f; +bool CNEO_Player::ShouldPlayerMakeFootsteps(float speed) // Could simply always get speed from the player, but didnt want to risk changing the behavior of UpdateStepSound in baseplayer_shared +{ + if (speed < 0) + { + speed = GetAbsVelocity().Length(); + } + + if ( GetFlags() & FL_DUCKING && (IsInAim() || IsWalking()) && speed <= (GetCrouchSpeed() * SILENT_THRESHOLD_GRACE) ) + { + return false; + } + else if ((IsInAim() || IsWalking()) && speed <= (GetNormSpeed() * SILENT_THRESHOLD_GRACE)) + { + return false; + } + + return true; +} + +float CNEO_Player::SpeedFractionToSoundThreshold(float speed) +{ + if (speed < 0) + { + speed = GetAbsVelocity().Length(); + } + + if (IsInAim() || IsWalking()) + { + const float difference = ((GetFlags() & FL_DUCKING ? GetCrouchSpeed() : GetNormSpeed()) * SILENT_THRESHOLD_GRACE) - GetPlayerMaxSpeed(); + return (speed - GetPlayerMaxSpeed()) / difference; + } + return 1.f; +} \ No newline at end of file From 6b3019387dbc5f83115ab2feb2d0c0b60d59c5c7 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Fri, 13 Mar 2026 13:24:41 +0000 Subject: [PATCH 2/3] fix, dont overlap the icons --- game/neo/scripts/HudLayout.res | 2 +- src/game/client/neo/ui/neo_hud_walking_indicator.cpp | 6 ++++-- src/game/shared/baseplayer_shared.cpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/game/neo/scripts/HudLayout.res b/game/neo/scripts/HudLayout.res index 941ed7247..7e29e25a3 100644 --- a/game/neo/scripts/HudLayout.res +++ b/game/neo/scripts/HudLayout.res @@ -912,7 +912,7 @@ { "fieldName" "neo_walking_indicator" "xpos" "208" - "ypos" "464" + "ypos" "470" "wide" "24" "tall" "24" "visible" "1" diff --git a/src/game/client/neo/ui/neo_hud_walking_indicator.cpp b/src/game/client/neo/ui/neo_hud_walking_indicator.cpp index 043bc6562..787ac769d 100644 --- a/src/game/client/neo/ui/neo_hud_walking_indicator.cpp +++ b/src/game/client/neo/ui/neo_hud_walking_indicator.cpp @@ -69,9 +69,11 @@ void CNEOHud_WalkingIndicator::DrawNeoHudElement() vgui::surface()->DrawSetColor(COLOR_WHITE); const float ICON_WIDTH = 1 / 2.f; const float ICON_HEIGHT = 1 / 2.f; - vgui::surface()->DrawTexturedSubRect(0, 0, wide, tall, 0, ICON_WIDTH, ICON_HEIGHT, 2 * ICON_HEIGHT); + vgui::surface()->DrawTexturedSubRect(0, 0, wide, tall * (1 - fractionTowardsMakingSound), + 0, ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT + (ICON_HEIGHT * (1 - fractionTowardsMakingSound))); vgui::surface()->DrawSetColor(255, 255 - (255 * fractionTowardsMakingSound), 255 - (255 * fractionTowardsMakingSound), 255); - vgui::surface()->DrawTexturedSubRect(0, tall * (1 - fractionTowardsMakingSound), wide, tall, 0, ICON_HEIGHT * (1 - fractionTowardsMakingSound), ICON_WIDTH, ICON_HEIGHT); + vgui::surface()->DrawTexturedSubRect(0, tall * (1 - fractionTowardsMakingSound), wide, tall, + 0, ICON_HEIGHT * (1 - fractionTowardsMakingSound), ICON_WIDTH, ICON_HEIGHT); } void CNEOHud_WalkingIndicator::Paint() diff --git a/src/game/shared/baseplayer_shared.cpp b/src/game/shared/baseplayer_shared.cpp index daff64bef..07cd1c77b 100644 --- a/src/game/shared/baseplayer_shared.cpp +++ b/src/game/shared/baseplayer_shared.cpp @@ -693,7 +693,7 @@ void CBasePlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOri } #ifdef NEO - if (neoPlayer->ShouldPlayerMakeFootsteps(speed)) + if (!neoPlayer->ShouldPlayerMakeFootsteps(speed)) { return; } From b89129a34d14e62474c4db40855159f857f56289 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Fri, 13 Mar 2026 14:48:54 +0000 Subject: [PATCH 3/3] drawn image dimensions constant --- .../client/neo/ui/neo_hud_walking_indicator.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/game/client/neo/ui/neo_hud_walking_indicator.cpp b/src/game/client/neo/ui/neo_hud_walking_indicator.cpp index 787ac769d..0652f9169 100644 --- a/src/game/client/neo/ui/neo_hud_walking_indicator.cpp +++ b/src/game/client/neo/ui/neo_hud_walking_indicator.cpp @@ -63,17 +63,19 @@ void CNEOHud_WalkingIndicator::DrawNeoHudElement() if (!pTargetPlayer->IsAlive() || !pTargetPlayer->IsWalking())// && !pTargetPlayer->IsInAim()) player is also silent if aiming and not abusing the threshold, but its much harder to make noise when aiming, better for the indicator to strictly appear when walk is pressed return; - const float fractionTowardsMakingSound = Max(0.f, Min(1.f, pTargetPlayer->SpeedFractionToSoundThreshold())); + // we don't want the subrectangle taken from the texture to vary in size when speed changes but the split between the top and bottom image remains in the same spot since texture can be larger than the area its drawn to + const int pictureSplitDistanceInPixels = tall * (1 - Max(0.f, Min(1.f, pTargetPlayer->SpeedFractionToSoundThreshold()))); + const float pictureSplitDistanceFraction = (float)pictureSplitDistanceInPixels / tall; vgui::surface()->DrawSetTexture(m_hWalkingIndicatorTexture); vgui::surface()->DrawSetColor(COLOR_WHITE); const float ICON_WIDTH = 1 / 2.f; const float ICON_HEIGHT = 1 / 2.f; - vgui::surface()->DrawTexturedSubRect(0, 0, wide, tall * (1 - fractionTowardsMakingSound), - 0, ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT + (ICON_HEIGHT * (1 - fractionTowardsMakingSound))); - vgui::surface()->DrawSetColor(255, 255 - (255 * fractionTowardsMakingSound), 255 - (255 * fractionTowardsMakingSound), 255); - vgui::surface()->DrawTexturedSubRect(0, tall * (1 - fractionTowardsMakingSound), wide, tall, - 0, ICON_HEIGHT * (1 - fractionTowardsMakingSound), ICON_WIDTH, ICON_HEIGHT); + vgui::surface()->DrawTexturedSubRect(0, 0, wide, pictureSplitDistanceInPixels, + 0, ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT + (ICON_HEIGHT * pictureSplitDistanceFraction)); + vgui::surface()->DrawSetColor(255, 255 * pictureSplitDistanceFraction, 255 * pictureSplitDistanceFraction, 255); + vgui::surface()->DrawTexturedSubRect(0, pictureSplitDistanceInPixels, wide, tall, + 0, ICON_HEIGHT * pictureSplitDistanceFraction, ICON_WIDTH, ICON_HEIGHT); } void CNEOHud_WalkingIndicator::Paint()