├── C4Timer.csproj
├── README.md
├── C4Timer.sln
└── C4Timer.cs
/C4Timer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # C4 Timer
2 | This plugin adds countdown to c4 bomb explosion to your server.
3 |
4 | 
5 |
6 | The countdown can look any way you want it to look.
7 | All you have to do is customize the configuration to your liking.
8 |
9 | # Config
10 | ```
11 | {
12 | "EnableTimer": true,
13 | "EnableProgressBar": true,
14 | "TimerStarting": 45,
15 | "LeftSideTimer": "-[ ",
16 | "RightSideTimer": " ]-",
17 | "EnableColorMessage": true,
18 | "SidesTimerColor": "45:white",
19 | "TimeColor": "20:yellow, 10:red, 5:darkred",
20 | "ProgressBarColor": "20:yellow, 10:red, 5:darkred"
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/C4Timer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.9.34723.18
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "C4Timer", "C4Timer.csproj", "{ABC03A0F-2111-47C4-BEA8-A5589DE99BA2}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {ABC03A0F-2111-47C4-BEA8-A5589DE99BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {ABC03A0F-2111-47C4-BEA8-A5589DE99BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {ABC03A0F-2111-47C4-BEA8-A5589DE99BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {ABC03A0F-2111-47C4-BEA8-A5589DE99BA2}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {4222F3D5-AEDF-4CA5-9400-FE0C8D6E16F4}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/C4Timer.cs:
--------------------------------------------------------------------------------
1 | using CounterStrikeSharp.API;
2 | using CounterStrikeSharp.API.Core;
3 | using CounterStrikeSharp.API.Modules.Memory;
4 | using CounterStrikeSharp.API.Modules.Timers;
5 | using CounterStrikeSharp.API.Modules.Utils;
6 | using Microsoft.Extensions.Logging;
7 | using System.Text.Json.Serialization;
8 | using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
9 |
10 | namespace C4Timer;
11 |
12 | public class C4TimerConfig : BasePluginConfig
13 | {
14 | [JsonPropertyName("EnableTimer")]
15 | public bool EnableTimer { get; set; } = true;
16 |
17 | [JsonPropertyName("EnableProgressBar")]
18 | public bool EnableProgressBar { get; set; } = true;
19 |
20 | [JsonPropertyName("TimerStarting")]
21 | public int TimerStarting { get; set; } = 45;
22 |
23 | [JsonPropertyName("LeftSideTimer")]
24 | public string LeftSideTimer { get; set; } = "-[ ";
25 |
26 | [JsonPropertyName("RightSideTimer")]
27 | public string RightSideTimer { get; set; } = " ]-";
28 |
29 | [JsonPropertyName("EnableColorMessage")]
30 | public bool EnableColorMessage { get; set; } = true;
31 |
32 | [JsonPropertyName("SidesTimerColor")]
33 | public string SidesTimerColor { get; set; } = "45:white";
34 |
35 | [JsonPropertyName("TimeColor")]
36 | public string TimeColor { get; set; } = "20:yellow, 10:red, 5:darkred";
37 |
38 | [JsonPropertyName("ProgressBarColor")]
39 | public string ProgressBarColor { get; set; } = "20:yellow, 10:red, 5:darkred";
40 | }
41 |
42 | public class C4Timer : BasePlugin, IPluginConfig
43 | {
44 | public override string ModuleName => "C4 Timer";
45 | public override string ModuleVersion => "1.6";
46 | public override string ModuleAuthor => "belom0r";
47 |
48 | Dictionary TimeColor = new Dictionary();
49 | Dictionary ProgressBarColor = new Dictionary();
50 | Dictionary SidesTimerColor = new Dictionary();
51 |
52 | private bool PlantedC4 = false;
53 |
54 | private int TimerLength = 0;
55 | private int TimerСountdown = 0;
56 |
57 | private string messageCountdown = "";
58 |
59 | private Timer? CountdownToExplosion;
60 |
61 | public required C4TimerConfig Config { get; set; }
62 |
63 | public void OnConfigParsed(C4TimerConfig config) { Config = config; }
64 |
65 | public override void Load(bool hotReload)
66 | {
67 | RegisterEventHandler(BombPlantedPost); //bPlantedC4 = true
68 | RegisterEventHandler((_, _) => { PlantedC4 = false; return HookResult.Continue; });
69 | RegisterEventHandler((_, _) => { PlantedC4 = false; return HookResult.Continue; });
70 | RegisterEventHandler((_, _) => { PlantedC4 = false; return HookResult.Continue; });
71 |
72 | if (Config.EnableColorMessage)
73 | {
74 | RegisterListener(OnTick);
75 | }
76 |
77 | ColorMsg(Config.TimeColor, TimeColor);
78 | ColorMsg(Config.ProgressBarColor, ProgressBarColor);
79 | ColorMsg(Config.SidesTimerColor, SidesTimerColor);
80 | }
81 |
82 | private HookResult BombPlantedPost(EventBombPlanted @event, GameEventInfo info)
83 | {
84 | var planted = GetPlantedC4();
85 |
86 | if (planted == null)
87 | return HookResult.Continue;
88 |
89 | PlantedC4 = true;
90 |
91 | TimerLength = TimerСountdown = (int)(planted.TimerLength + 1.0f);
92 |
93 | Config.TimerStarting = Math.Clamp(Config.TimerStarting, 0, TimerLength);
94 |
95 | CountdownToExplosion = new Timer(1.0f, CountdownToExplosionC4, TimerFlags.REPEAT);
96 |
97 | Timers.Add(CountdownToExplosion);
98 |
99 | return HookResult.Continue;
100 | }
101 |
102 | public void OnTick()
103 | {
104 | if (string.IsNullOrEmpty(messageCountdown))
105 | return;
106 |
107 | foreach (var Player in GetPlayers())
108 | {
109 | Player.PrintToCenterHtml(messageCountdown);
110 | }
111 | }
112 |
113 | public void CountdownToExplosionC4()
114 | {
115 | TimerСountdown--;
116 |
117 | if ((int)TimerСountdown == 0)
118 | {
119 | messageCountdown = WrapWithColor("C4 bomb exploded !!!", "darkred");
120 | }
121 | else if (TimerСountdown < 0 || !PlantedC4)
122 | {
123 | CountdownToExplosion!.Kill();
124 | Timers.Remove(CountdownToExplosion);
125 | CountdownToExplosion = null;
126 |
127 | TimerLength = 0;
128 | TimerСountdown = 0;
129 |
130 | messageCountdown = "";
131 |
132 | return;
133 | }
134 | else messageCountdown = GenerateCountdownMessage();
135 |
136 | if (!Config.EnableColorMessage)
137 | VirtualFunctions.ClientPrintAll(HudDestination.Center, messageCountdown, 0, 0, 0, 0);
138 | }
139 |
140 | private string GenerateCountdownMessage()
141 | {
142 | if (TimerСountdown > Config.TimerStarting)
143 | return "";
144 |
145 | string timerStyle = GenerateTimerStyle();
146 | string progressBarStyle = GenerateProgressBarStyle();
147 |
148 | return Config.EnableColorMessage
149 | ? $"{timerStyle}{progressBarStyle}"
150 | : ConnectStrings(timerStyle, progressBarStyle);
151 | }
152 |
153 | private string GenerateTimerStyle()
154 | {
155 | if (!Config.EnableTimer)
156 | return "";
157 |
158 | string leftSide = WrapWithColor(Config.LeftSideTimer, SidesTimerColor[TimerСountdown]);
159 | string time = WrapWithColor(TimerСountdown.ToString(), TimeColor[TimerСountdown]);
160 | string rightSide = WrapWithColor(Config.RightSideTimer, SidesTimerColor[TimerСountdown]);
161 |
162 | return $"{leftSide}{time}{rightSide}";
163 | }
164 |
165 | private string GenerateProgressBarStyle()
166 | {
167 | if (!Config.EnableProgressBar)
168 | return "";
169 |
170 | int total = Math.Min(Config.TimerStarting, TimerLength);
171 |
172 | char[] progressBar = new char[total];
173 | for (int i = 0; i < total; i++)
174 | progressBar[i] = i >= TimerСountdown ? '-' : '|';
175 |
176 | string progressBar_txt = WrapWithColor(new string(progressBar), ProgressBarColor[TimerСountdown]);
177 |
178 | return Config.EnableTimer && Config.EnableColorMessage ? "
" + progressBar_txt : progressBar_txt;
179 | }
180 |
181 | private string WrapWithColor(string text, string color)
182 | {
183 | return Config.EnableColorMessage ? $"{text}" : text;
184 | }
185 |
186 | private CPlantedC4? GetPlantedC4()
187 | {
188 | var PlantedC4 = Utilities.FindAllEntitiesByDesignerName("planted_c4");
189 |
190 | if (PlantedC4 == null || !PlantedC4.Any())
191 | return null;
192 |
193 | return PlantedC4.FirstOrDefault();
194 | }
195 |
196 | public string ConnectStrings(string str1, string str2)
197 | {
198 | if (string.IsNullOrEmpty(str2))
199 | return str1;
200 |
201 | return $"{str1}{Environment.NewLine}{str2}";
202 | }
203 |
204 | public void ColorMsg(string msg, Dictionary colorDictionary)
205 | {
206 | colorDictionary.Clear();
207 |
208 | for (int i = 0; i <= Config.TimerStarting; i++)
209 | colorDictionary[i] = "white";
210 |
211 | if (!Config.EnableColorMessage || string.IsNullOrEmpty(msg))
212 | return;
213 |
214 | foreach (var color in msg.Split(',', StringSplitOptions.RemoveEmptyEntries))
215 | {
216 | try
217 | {
218 | var elements = color.Split(':', StringSplitOptions.RemoveEmptyEntries);
219 | if (elements.Length != 2) continue;
220 |
221 | int index = int.Parse(elements[0]);
222 | string colorValue = elements[1];
223 |
224 | for (int i = index; i >= 0; i--)
225 | colorDictionary[i] = colorValue;
226 | }
227 | catch
228 | {
229 | Logger.LogError($"Invalid color format: {color}");
230 | }
231 | }
232 | }
233 |
234 | public List GetPlayers()
235 | {
236 | return Utilities.GetPlayers().Where(player =>
237 | player != null && player.IsValid && player.Connected == PlayerConnectedState.PlayerConnected).ToList();
238 | }
239 | }
--------------------------------------------------------------------------------