Daemon666 / CSB C4 Timer Public

Live HUD countdown after the bomb is planted, colour-coded, with a per-CT defusable / too-late notice.

v1.0.0 154 descărcări GPL-3.0 Ultima actualizare Aug 23, 2026
csb_c4_timer.sma v1.0.0 187 linii · 4.5 KB Brut
1 /*
2 * CSB C4 Timer
3 * Copyright (C) 2026 counter-strike-boost.com
4 *
5 * Once the bomb is planted this shows a live HUD countdown of the exact seconds
6 * left, based on mp_c4timer and the moment of the plant. The colour ramps from
7 * green to red as the fuse burns down, and each CT is told whether they can
8 * still reach and defuse it in time given their defuse kit.
9 *
10 * Inspired by the classic "C4 Timer" plugins for AMX Mod X. This is an
11 * independent GPL re-implementation; no original code is reused.
12 *
13 * This program is free software: you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the Free
15 * Software Foundation, either version 3 of the License, or (at your option)
16 * any later version. It is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details: <https://www.gnu.org/licenses/>.
20 */
21
22 #include <amxmodx>
23 #include <cstrike>
24
25 new const PLUGIN[] = "CSB C4 Timer"
26 new const VERSION[] = "1.0.0"
27 new const AUTHOR[] = "counter-strike-boost.com"
28
29 /* stock defuse timings without / with a defuse kit */
30 #define DEFUSE_NOKIT 10.0
31 #define DEFUSE_KIT 5.0
32
33 new g_pEnabled, g_pBeep
34 new g_pC4Timer
35
36 new bool:g_bPlanted = false
37 new Float:g_flExplodeAt
38 new g_iSync
39
40 public plugin_init()
41 {
42 register_plugin(PLUGIN, VERSION, AUTHOR)
43
44 g_pEnabled = register_cvar("csb_c4timer_enabled", "1")
45 g_pBeep = register_cvar("csb_c4timer_beep", "1")
46
47 g_pC4Timer = get_cvar_pointer("mp_c4timer")
48
49 g_iSync = CreateHudSyncObj()
50
51 register_logevent("evPlanted", 3, "2=Planted_The_Bomb")
52 register_logevent("evDefused", 3, "2=Defused_The_Bomb")
53
54 /* new round resets everything (HLTV new-round message) */
55 register_event("HLTV", "evNewRound", "a", "1=0", "2=0")
56 register_event("TextMsg", "evRestart", "a", "2=#Game_will_restart_in")
57 }
58
59 public evPlanted()
60 {
61 if (!get_pcvar_num(g_pEnabled))
62 return
63
64 new Float:c4timer = g_pC4Timer ? get_pcvar_float(g_pC4Timer) : 45.0
65
66 if (c4timer <= 0.0)
67 c4timer = 45.0
68
69 g_bPlanted = true
70 g_flExplodeAt = get_gametime() + c4timer
71
72 set_task(0.2, "taskTick", 0, _, _, "b")
73 }
74
75 public evDefused()
76 {
77 stopTimer()
78 }
79
80 public evNewRound()
81 {
82 stopTimer()
83 }
84
85 public evRestart()
86 {
87 stopTimer()
88 }
89
90 stopTimer()
91 {
92 if (!g_bPlanted)
93 return
94
95 g_bPlanted = false
96 remove_task(0)
97 ClearSyncHud(0, g_iSync)
98 }
99
100 public taskTick()
101 {
102 if (!g_bPlanted)
103 {
104 remove_task(0)
105 return
106 }
107
108 new Float:remain = g_flExplodeAt - get_gametime()
109
110 if (remain <= 0.0)
111 {
112 stopTimer()
113 return
114 }
115
116 new r, g, b
117 fuseColor(remain, r, g, b)
118
119 /* the bomb is a shared threat - draw it to everyone, then add the
120 personal defuse note only for living CTs */
121 new players[32], num, pid
122 get_players(players, num, "ch")
123
124 new bars[16]
125 beepBars(remain, bars, charsmax(bars))
126
127 for (new i = 0; i < num; i++)
128 {
129 pid = players[i]
130
131 new note[64]
132 note[0] = 0
133
134 if (is_user_alive(pid) && cs_get_user_team(pid) == CS_TEAM_CT)
135 defuseNote(pid, remain, note, charsmax(note))
136
137 set_hudmessage(r, g, b, 0.02, 0.20, 0, 0.0, 0.3, 0.0, 0.0, -1)
138
139 if (note[0])
140 ShowSyncHudMsg(pid, g_iSync, "C4: %.1f %s^n%s", remain, bars, note)
141 else
142 ShowSyncHudMsg(pid, g_iSync, "C4: %.1f %s", remain, bars)
143 }
144 }
145
146 fuseColor(Float:remain, &r, &g, &b)
147 {
148 if (remain <= 5.0) { r = 255; g = 0; b = 0; }
149 else if (remain <= 15.0) { r = 255; g = 140; b = 0; }
150 else { r = 0; g = 220; b = 0; }
151 }
152
153 beepBars(Float:remain, out[], len)
154 {
155 if (!get_pcvar_num(g_pBeep))
156 {
157 out[0] = 0
158 return
159 }
160
161 /* more filled bars the closer the explosion is */
162 new filled
163
164 if (remain <= 5.0) filled = 5
165 else if (remain <= 10.0) filled = 4
166 else if (remain <= 20.0) filled = 3
167 else if (remain <= 30.0) filled = 2
168 else filled = 1
169
170 new p = 0
171
172 for (new i = 0; i < 5 && p < len - 1; i++)
173 out[p++] = (i < filled) ? '|' : '.'
174
175 out[p] = 0
176 }
177
178 defuseNote(id, Float:remain, out[], len)
179 {
180 new Float:need = cs_get_user_defuse(id) ? DEFUSE_KIT : DEFUSE_NOKIT
181
182 if (remain >= need)
183 formatex(out, len, "Defusable (%.0fs needed)", need)
184 else
185 formatex(out, len, "TOO LATE to defuse")
186 }
187