├── 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 | 
2 | 
3 | 
4 | 
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 |
--------------------------------------------------------------------------------