Live HUD countdown after the bomb is planted, colour-coded, with a per-CT defusable / too-late notice.
csb_c4_timer.sma v1.0.0
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









