├── EN ├── CounterStrikeSharp.API.dll ├── ShowDamage.csproj └── ShowDamage.cs ├── RU ├── CounterStrikeSharp.API.dll ├── ShowDamage.csproj └── ShowDamage.cs └── README.md /EN/CounterStrikeSharp.API.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ABKAM2023/CS2ShowDamage/HEAD/EN/CounterStrikeSharp.API.dll -------------------------------------------------------------------------------- /RU/CounterStrikeSharp.API.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ABKAM2023/CS2ShowDamage/HEAD/RU/CounterStrikeSharp.API.dll -------------------------------------------------------------------------------- /EN/ShowDamage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | AnyCPU;x86 6 | true 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /RU/ShowDamage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | AnyCPU;x86 6 | true 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub Repo stars](https://img.shields.io/github/stars/ABKAM2023/CS2-ShowDamage?style=for-the-badge) 2 | ![GitHub issues](https://img.shields.io/github/issues/ABKAM2023/CS2-ShowDamage?style=for-the-badge) 3 | ![GitHub contributors](https://img.shields.io/github/contributors/ABKAM2023/CS2-ShowDamage?style=for-the-badge) 4 | ![GitHub all releases](https://img.shields.io/github/downloads/ABKAM2023/CS2-ShowDamage/total?style=for-the-badge) 5 | 6 | # CS2-ShowDamage 7 | # EN 8 | - **ShowDamage** - displays information about inflicted damage on the screen. 9 | - **Video demonstration - https://www.youtube.com/watch?v=AeJCixkRzyU&ab_channel=ABKAM** 10 | 11 | # Installation 12 | 1. Install [Metamod:Source](https://www.sourcemm.net/downloads.php/?branch=master) and [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp). 13 | 2. Download ShowDamage 14 | 3. Unzip the archive and upload it to your game server 15 | 16 | # Main Config (Config.yml) 17 | ``` 18 | grenadeDamageMessage: "Total grenade damage: {0}❤" 19 | damageMessage: "Damage: {0}♥, Remaining HP: {1}❤, Hit: {2}" 20 | showDamageEnabledMessage: '[ {Green}ShowDamage{White} ] Damage display {Green}enabled{White}.' 21 | showDamageDisabledMessage: '[ {Red}ShowDamage{White} ] Damage display {Red}disabled{White}.' 22 | ``` 23 | 24 | # Commands 25 | - `!damage` turn off/on damage display 26 | 27 | 28 | # RU 29 | - **ShowDamage** - отображает на экране информацию о нанесенных повреждениях. 30 | - **Видео-демонстрация - https://youtu.be/BiAjXuetyT8** 31 | 32 | # Установка 33 | 1. Установите [Metamod:Source](https://www.sourcemm.net/downloads.php/?branch=master) и [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp). 34 | 2. Скачайте ShowDamage 35 | 3. Распакуйте архив и загрузите его на игровой сервер 36 | 37 | # Основной конфиг (Config.yml) 38 | ``` 39 | grenadeDamageMessage: "Общий урон от гранаты: {0}" 40 | damageMessage: "Урон: {0}♥, Остаток HP: {1}❤, Попадание: {2}" 41 | showDamageEnabledMessage: '[ {Green}ShowDamage{White} ] Отображение урона {Green}включено{White}.' 42 | showDamageDisabledMessage: '[ {Red}ShowDamage{White} ] Отображение урона {Red}отключено{White}.' 43 | ``` 44 | 45 | # Команды 46 | - `!damage` выключить/включить показ урона 47 | -------------------------------------------------------------------------------- /EN/ShowDamage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Collections.Generic; 5 | using CounterStrikeSharp.API; 6 | using CounterStrikeSharp.API.Core; 7 | using Newtonsoft.Json; 8 | using YamlDotNet.Serialization; 9 | using YamlDotNet.Serialization.NamingConventions; 10 | using CounterStrikeSharp.API.Core.Attributes.Registration; 11 | using CounterStrikeSharp.API.Modules.Events; 12 | using CounterStrikeSharp.API.Modules.Commands; 13 | using CounterStrikeSharp.API.Modules.Utils; 14 | using CSTimers = CounterStrikeSharp.API.Modules.Timers; 15 | public class ShowDamagePlugin : BasePlugin 16 | { 17 | public override string ModuleName => "ShowDamage by ABKAM"; 18 | public override string ModuleVersion => "1.0"; 19 | private Dictionary playerMessages = new Dictionary(); 20 | private Dictionary playerMessageTimers = new Dictionary(); 21 | private Dictionary grenadeDamage = new Dictionary(); 22 | private Dictionary playerTeams = new Dictionary(); 23 | private Dictionary showDamageEnabled = new Dictionary(); 24 | private string showDamageFilePath; 25 | private Config config; 26 | private string configFilePath; 27 | private void LoadShowDamageConfig() 28 | { 29 | if (File.Exists(showDamageFilePath)) 30 | { 31 | string json = File.ReadAllText(showDamageFilePath); 32 | showDamageEnabled = JsonConvert.DeserializeObject>(json) ?? new Dictionary(); 33 | } 34 | else 35 | { 36 | showDamageEnabled = new Dictionary(); 37 | } 38 | } 39 | private void SaveShowDamageConfig() 40 | { 41 | string json = JsonConvert.SerializeObject(showDamageEnabled); 42 | File.WriteAllText(showDamageFilePath, json); 43 | } 44 | private void LoadConfig() 45 | { 46 | if (File.Exists(configFilePath)) 47 | { 48 | var deserializer = new DeserializerBuilder() 49 | .WithNamingConvention(CamelCaseNamingConvention.Instance) 50 | .Build(); 51 | string yml = File.ReadAllText(configFilePath); 52 | config = deserializer.Deserialize(yml) ?? new Config(); 53 | } 54 | else 55 | { 56 | config = new Config(); 57 | SaveConfig(); 58 | } 59 | } 60 | private void SaveConfig() 61 | { 62 | var serializer = new SerializerBuilder() 63 | .WithNamingConvention(CamelCaseNamingConvention.Instance) 64 | .Build(); 65 | string yml = serializer.Serialize(config); 66 | File.WriteAllText(configFilePath, yml); 67 | } 68 | public override void Load(bool hotReload) 69 | { 70 | base.Load(hotReload); 71 | showDamageFilePath = Path.Combine(ModuleDirectory, "commandsave.json"); 72 | configFilePath = Path.Combine(ModuleDirectory, "Config.yml"); 73 | LoadShowDamageConfig(); 74 | LoadConfig(); 75 | RegisterEventHandler(OnPlayerHurt); 76 | RegisterListener(OnTick); 77 | RegisterEventHandler(OnPlayerTeam); 78 | } 79 | [ConsoleCommand("damage", "Toggle damage display")] 80 | public void ToggleShowDamageCommand(CCSPlayerController player, CommandInfo command) 81 | { 82 | ToggleShowDamage(player); 83 | } 84 | private void ToggleShowDamage(CCSPlayerController player) 85 | { 86 | string playerSteamId = player.SteamID.ToString(); 87 | 88 | if (showDamageEnabled.ContainsKey(playerSteamId)) 89 | { 90 | showDamageEnabled.Remove(playerSteamId); 91 | string enabledMessage = ReplaceColorPlaceholders(config.ShowDamageEnabledMessage); 92 | player.PrintToChat(enabledMessage); 93 | } 94 | else 95 | { 96 | showDamageEnabled[playerSteamId] = true; 97 | string disabledMessage = ReplaceColorPlaceholders(config.ShowDamageDisabledMessage); 98 | player.PrintToChat(disabledMessage); 99 | } 100 | 101 | SaveShowDamageConfig(); 102 | } 103 | 104 | private HookResult OnPlayerTeam(EventPlayerTeam eventInfo, GameEventInfo gameEventInfo) 105 | { 106 | if (eventInfo == null) return HookResult.Continue; 107 | 108 | var player = eventInfo.Userid; 109 | var newTeam = (byte)eventInfo.Team; 110 | 111 | playerTeams[player] = newTeam; 112 | 113 | return HookResult.Continue; 114 | } 115 | private HookResult OnPlayerHurt(EventPlayerHurt eventInfo, GameEventInfo gameEventInfo) 116 | { 117 | if (eventInfo == null) return HookResult.Continue; 118 | 119 | var attacker = eventInfo.Attacker; 120 | var victim = eventInfo.Userid; 121 | 122 | if (attacker != null && victim != null && playerTeams.TryGetValue(attacker, out var attackerTeam) && playerTeams.TryGetValue(victim, out var victimTeam)) 123 | { 124 | if (attackerTeam != victimTeam) 125 | { 126 | var remainingHP = eventInfo.Health; 127 | var damageHP = eventInfo.DmgHealth; 128 | var hitgroup = eventInfo.Hitgroup; 129 | 130 | string attackerSteamId = attacker.SteamID.ToString(); 131 | if (!showDamageEnabled.ContainsKey(attackerSteamId)) 132 | { 133 | if (eventInfo.Weapon == "hegrenade") 134 | { 135 | if (!grenadeDamage.ContainsKey(attacker)) 136 | { 137 | grenadeDamage[attacker] = 0; 138 | } 139 | grenadeDamage[attacker] += damageHP; 140 | 141 | ShowTotalGrenadeDamage(attacker); 142 | } 143 | else 144 | { 145 | var message = string.Format(config.DamageMessage, damageHP, remainingHP, HitGroupToString(hitgroup)); 146 | UpdateCenterMessage(attacker, message, 2); 147 | } 148 | } 149 | } 150 | } 151 | 152 | return HookResult.Continue; 153 | } 154 | private void ShowTotalGrenadeDamage(CCSPlayerController attacker) 155 | { 156 | if (grenadeDamage.TryGetValue(attacker, out var totalDamage)) 157 | { 158 | var message = string.Format(config.GrenadeDamageMessage, totalDamage); 159 | 160 | playerMessages[attacker] = message; 161 | 162 | if (playerMessageTimers.TryGetValue(attacker, out var existingTimer)) 163 | { 164 | existingTimer.Kill(); 165 | } 166 | 167 | var messageTimer = AddTimer(2.0f, () => 168 | { 169 | playerMessages.Remove(attacker); 170 | }); 171 | 172 | playerMessageTimers[attacker] = messageTimer; 173 | } 174 | } 175 | private void UpdateCenterMessage(CCSPlayerController player, string message, float durationInSeconds) 176 | { 177 | playerMessages[player] = message; 178 | 179 | if (playerMessageTimers.TryGetValue(player, out var existingTimer)) 180 | { 181 | existingTimer.Kill(); 182 | } 183 | 184 | var messageTimer = AddTimer(durationInSeconds, () => 185 | { 186 | playerMessages.Remove(player); 187 | }); 188 | 189 | playerMessageTimers[player] = messageTimer; 190 | } 191 | private void OnTick() 192 | { 193 | foreach (var kvp in playerMessages) 194 | { 195 | var player = kvp.Key; 196 | var message = kvp.Value; 197 | 198 | if (player != null && player.IsValid) 199 | { 200 | player.PrintToCenterHtml(message); 201 | } 202 | } 203 | } 204 | private string HitGroupToString(int hitGroup) 205 | { 206 | switch (hitGroup) 207 | { 208 | case 1: 209 | return "Head"; 210 | case 2: 211 | return "Chest"; 212 | case 3: 213 | return "Stomach"; 214 | case 4: 215 | return "Left Arm"; 216 | case 5: 217 | return "Right Arm"; 218 | case 6: 219 | return "Left Leg"; 220 | case 7: 221 | return "Right Leg"; 222 | case 10: 223 | return "Gear"; 224 | default: 225 | return "Unknown"; 226 | } 227 | } 228 | private string ReplaceColorPlaceholders(string message) 229 | { 230 | if (message.Contains('{')) 231 | { 232 | string modifiedValue = message; 233 | foreach (FieldInfo field in typeof(ChatColors).GetFields()) 234 | { 235 | string pattern = $"{{{field.Name}}}"; 236 | if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) 237 | { 238 | modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null).ToString(), StringComparison.OrdinalIgnoreCase); 239 | } 240 | } 241 | return modifiedValue; 242 | } 243 | 244 | return message; 245 | } 246 | public class Config 247 | { 248 | public string GrenadeDamageMessage { get; set; } = "Total grenade damage: {0}❤"; 249 | public string DamageMessage { get; set; } = "Damage: {0}♥, Remaining HP: {1}❤, Hit: {2}"; 250 | public string ShowDamageEnabledMessage { get; set; } = "[ {Green}ShowDamage{White} ] Damage display {Green}enabled{White}."; 251 | public string ShowDamageDisabledMessage { get; set; } = "[ {Red}ShowDamage{White} ] Damage display {Red}disabled{White}."; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /RU/ShowDamage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Collections.Generic; 5 | using CounterStrikeSharp.API; 6 | using CounterStrikeSharp.API.Core; 7 | using Newtonsoft.Json; 8 | using YamlDotNet.Serialization; 9 | using YamlDotNet.Serialization.NamingConventions; 10 | using CounterStrikeSharp.API.Core.Attributes.Registration; 11 | using CounterStrikeSharp.API.Modules.Events; 12 | using CounterStrikeSharp.API.Modules.Commands; 13 | using CounterStrikeSharp.API.Modules.Utils; 14 | using CSTimers = CounterStrikeSharp.API.Modules.Timers; 15 | public class ShowDamagePlugin : BasePlugin 16 | { 17 | public override string ModuleName => "ShowDamage by ABKAM"; 18 | public override string ModuleVersion => "1.0.1"; 19 | private Dictionary playerMessages = new Dictionary(); 20 | private Dictionary playerMessageTimers = new Dictionary(); 21 | private Dictionary grenadeDamage = new Dictionary(); 22 | private Dictionary playerTeams = new Dictionary(); 23 | private Dictionary showDamageEnabled = new Dictionary(); 24 | private string showDamageFilePath; 25 | private Config config; 26 | private string configFilePath; 27 | private void LoadShowDamageConfig() 28 | { 29 | if (File.Exists(showDamageFilePath)) 30 | { 31 | string json = File.ReadAllText(showDamageFilePath); 32 | showDamageEnabled = JsonConvert.DeserializeObject>(json) ?? new Dictionary(); 33 | } 34 | else 35 | { 36 | showDamageEnabled = new Dictionary(); 37 | } 38 | } 39 | private void SaveShowDamageConfig() 40 | { 41 | string json = JsonConvert.SerializeObject(showDamageEnabled); 42 | File.WriteAllText(showDamageFilePath, json); 43 | } 44 | private void LoadConfig() 45 | { 46 | if (File.Exists(configFilePath)) 47 | { 48 | var deserializer = new DeserializerBuilder() 49 | .WithNamingConvention(CamelCaseNamingConvention.Instance) 50 | .Build(); 51 | string yml = File.ReadAllText(configFilePath); 52 | config = deserializer.Deserialize(yml) ?? new Config(); 53 | } 54 | else 55 | { 56 | config = new Config(); 57 | SaveConfig(); 58 | } 59 | } 60 | private void SaveConfig() 61 | { 62 | var serializer = new SerializerBuilder() 63 | .WithNamingConvention(CamelCaseNamingConvention.Instance) 64 | .Build(); 65 | string yml = serializer.Serialize(config); 66 | File.WriteAllText(configFilePath, yml); 67 | } 68 | public override void Load(bool hotReload) 69 | { 70 | base.Load(hotReload); 71 | showDamageFilePath = Path.Combine(ModuleDirectory, "commandsave.json"); 72 | configFilePath = Path.Combine(ModuleDirectory, "Config.yml"); 73 | LoadShowDamageConfig(); 74 | LoadConfig(); 75 | RegisterEventHandler(OnPlayerHurt); 76 | RegisterListener(OnTick); 77 | RegisterEventHandler(OnPlayerTeam); 78 | } 79 | [ConsoleCommand("damage", "Переключить отображение урона")] 80 | public void ToggleShowDamageCommand(CCSPlayerController player, CommandInfo command) 81 | { 82 | ToggleShowDamage(player); 83 | } 84 | private void ToggleShowDamage(CCSPlayerController player) 85 | { 86 | string playerSteamId = player.SteamID.ToString(); 87 | 88 | if (showDamageEnabled.ContainsKey(playerSteamId)) 89 | { 90 | showDamageEnabled.Remove(playerSteamId); 91 | string enabledMessage = ReplaceColorPlaceholders(config.ShowDamageEnabledMessage); 92 | player.PrintToChat(enabledMessage); 93 | } 94 | else 95 | { 96 | showDamageEnabled[playerSteamId] = true; 97 | string disabledMessage = ReplaceColorPlaceholders(config.ShowDamageDisabledMessage); 98 | player.PrintToChat(disabledMessage); 99 | } 100 | 101 | SaveShowDamageConfig(); 102 | } 103 | private HookResult OnPlayerTeam(EventPlayerTeam eventInfo, GameEventInfo gameEventInfo) 104 | { 105 | if (eventInfo == null) return HookResult.Continue; 106 | 107 | var player = eventInfo.Userid; 108 | var newTeam = (byte)eventInfo.Team; 109 | 110 | playerTeams[player] = newTeam; 111 | 112 | return HookResult.Continue; 113 | } 114 | private HookResult OnPlayerHurt(EventPlayerHurt eventInfo, GameEventInfo gameEventInfo) 115 | { 116 | if (eventInfo == null) return HookResult.Continue; 117 | 118 | var attacker = eventInfo.Attacker; 119 | var victim = eventInfo.Userid; 120 | 121 | if (attacker != null && victim != null && playerTeams.TryGetValue(attacker, out var attackerTeam) && playerTeams.TryGetValue(victim, out var victimTeam)) 122 | { 123 | if (attackerTeam != victimTeam) 124 | { 125 | var remainingHP = eventInfo.Health; 126 | var damageHP = eventInfo.DmgHealth; 127 | var hitgroup = eventInfo.Hitgroup; 128 | 129 | string attackerSteamId = attacker.SteamID.ToString(); 130 | if (!showDamageEnabled.ContainsKey(attackerSteamId)) 131 | { 132 | if (eventInfo.Weapon == "hegrenade") 133 | { 134 | if (!grenadeDamage.ContainsKey(attacker)) 135 | { 136 | grenadeDamage[attacker] = 0; 137 | } 138 | grenadeDamage[attacker] += damageHP; 139 | 140 | ShowTotalGrenadeDamage(attacker); 141 | } 142 | else 143 | { 144 | var message = string.Format(config.DamageMessage, damageHP, remainingHP, HitGroupToString(hitgroup)); 145 | UpdateCenterMessage(attacker, message, 2); 146 | } 147 | } 148 | } 149 | } 150 | 151 | return HookResult.Continue; 152 | } 153 | private void ShowTotalGrenadeDamage(CCSPlayerController attacker) 154 | { 155 | if (grenadeDamage.TryGetValue(attacker, out var totalDamage)) 156 | { 157 | var message = string.Format(config.GrenadeDamageMessage, totalDamage); 158 | 159 | playerMessages[attacker] = message; 160 | 161 | if (playerMessageTimers.TryGetValue(attacker, out var existingTimer)) 162 | { 163 | existingTimer.Kill(); 164 | } 165 | 166 | var messageTimer = AddTimer(2.0f, () => 167 | { 168 | playerMessages.Remove(attacker); 169 | }); 170 | 171 | playerMessageTimers[attacker] = messageTimer; 172 | } 173 | } 174 | private void UpdateCenterMessage(CCSPlayerController player, string message, float durationInSeconds) 175 | { 176 | playerMessages[player] = message; 177 | 178 | if (playerMessageTimers.TryGetValue(player, out var existingTimer)) 179 | { 180 | existingTimer.Kill(); 181 | } 182 | 183 | var messageTimer = AddTimer(durationInSeconds, () => 184 | { 185 | playerMessages.Remove(player); 186 | }); 187 | 188 | playerMessageTimers[player] = messageTimer; 189 | } 190 | private void OnTick() 191 | { 192 | foreach (var kvp in playerMessages) 193 | { 194 | var player = kvp.Key; 195 | var message = kvp.Value; 196 | 197 | if (player != null && player.IsValid) 198 | { 199 | player.PrintToCenterHtml(message); 200 | } 201 | } 202 | } 203 | private string HitGroupToString(int hitGroup) 204 | { 205 | switch (hitGroup) 206 | { 207 | case 1: 208 | return "Голова"; 209 | case 2: 210 | return "Грудь"; 211 | case 3: 212 | return "Живот"; 213 | case 4: 214 | return "Левая рука"; 215 | case 5: 216 | return "Правая рука"; 217 | case 6: 218 | return "Левая нога"; 219 | case 7: 220 | return "Правая нога"; 221 | case 10: 222 | return "Экипировка"; 223 | default: 224 | return "Неизвестно"; 225 | } 226 | } 227 | private string ReplaceColorPlaceholders(string message) 228 | { 229 | if (message.Contains('{')) 230 | { 231 | string modifiedValue = message; 232 | foreach (FieldInfo field in typeof(ChatColors).GetFields()) 233 | { 234 | string pattern = $"{{{field.Name}}}"; 235 | if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) 236 | { 237 | modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null).ToString(), StringComparison.OrdinalIgnoreCase); 238 | } 239 | } 240 | return modifiedValue; 241 | } 242 | 243 | return message; 244 | } 245 | public class Config 246 | { 247 | public string GrenadeDamageMessage { get; set; } = "Общий урон от гранаты: {0}❤<"; 248 | public string DamageMessage { get; set; } = "Урон: {0}♥, Остаток HP: {1}❤, Попадание: {2}"; 249 | public string ShowDamageEnabledMessage { get; set; } = "[ {Green}ShowDamage{White} ] Отображение урона {Green}включено{White}."; 250 | public string ShowDamageDisabledMessage { get; set; } = "[ {Red}ShowDamage{White} ] Отображение урона {Red}отключено{White}."; 251 | } 252 | 253 | } 254 | --------------------------------------------------------------------------------