Counts shots and hits per weapon, keeps per-map and lifetime accuracy in nVault, shown with /acc.
csb_accuracy_tracker.sma v1.0.0
1
/*
2
* CSB Accuracy Tracker
3
* Copyright (C) 2026 counter-strike-boost.com
4
*
5
* Counts shots fired (Ham_Weapon_PrimaryAttack) and bullet hits
6
* (Ham_TakeDamage with DMG_BULLET) per weapon for every player, keeps per-map
7
* and lifetime totals (lifetime persisted in nVault per SteamID) and prints a
8
* console table with /acc. Useful on public servers for spotting who actually
9
* hits what they shoot at.
10
*
11
* Inspired by the weapon accuracy reporting of AMXX StatsX; independent GPL
12
* re-implementation, no original code reused.
13
*
14
* This program is free software: you can redistribute it and/or modify it under
15
* the terms of the GNU General Public License as published by the Free Software
16
* Foundation, either version 3 of the License, or (at your option) any later
17
* version. It is distributed in the hope that it will be useful, but WITHOUT
18
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
* FOR A PARTICULAR PURPOSE. See <https://www.gnu.org/licenses/> for details.
20
*/
21
22
#include <amxmodx>
23
#include <hamsandwich>
24
#include <fakemeta>
25
#include <nvault>
26
27
new const PLUGIN[] = "CSB Accuracy Tracker"
28
new const VERSION[] = "1.0.0"
29
new const AUTHOR[] = "counter-strike-boost.com"
30
31
#if !defined DMG_BULLET
32
#define DMG_BULLET (1<<1)
33
#endif
34
35
#define MAX_WEAPONS 24
36
37
new const g_szWeapons[][] =
38
{
39
"weapon_glock18", "weapon_usp", "weapon_p228", "weapon_deagle",
40
"weapon_fiveseven", "weapon_elite", "weapon_m3", "weapon_xm1014",
41
"weapon_tmp", "weapon_mac10", "weapon_mp5navy", "weapon_ump45",
42
"weapon_p90", "weapon_galil", "weapon_famas", "weapon_ak47",
43
"weapon_m4a1", "weapon_scout", "weapon_sg552", "weapon_aug",
44
"weapon_awp", "weapon_sg550", "weapon_g3sg1", "weapon_m249"
45
}
46
47
new g_iMapShots[33][MAX_WEAPONS]
48
new g_iMapHits[33][MAX_WEAPONS]
49
new g_iLifeShots[33][MAX_WEAPONS]
50
new g_iLifeHits[33][MAX_WEAPONS]
51
52
new g_iLastWeapon[33]
53
new g_iVault
54
55
public plugin_init()
56
{
57
register_plugin(PLUGIN, VERSION, AUTHOR)
58
59
register_cvar("csb_acc_enabled", "1")
60
61
for (new w = 0; w < MAX_WEAPONS; w++)
62
RegisterHam(Ham_Weapon_PrimaryAttack, g_szWeapons[w], "OnFire", 1)
63
64
RegisterHam(Ham_TakeDamage, "player", "OnDamage", 1)
65
66
register_clcmd("say /acc", "cmdAcc")
67
register_clcmd("say_team /acc", "cmdAcc")
68
69
g_iVault = nvault_open("csb_accuracy")
70
}
71
72
public plugin_end()
73
{
74
if (g_iVault != -1)
75
nvault_close(g_iVault)
76
}
77
78
public client_putinserver(id)
79
{
80
for (new w = 0; w < MAX_WEAPONS; w++)
81
{
82
g_iMapShots[id][w] = 0
83
g_iMapHits[id][w] = 0
84
g_iLifeShots[id][w] = 0
85
g_iLifeHits[id][w] = 0
86
}
87
88
loadPlayer(id)
89
}
90
91
public client_disconnect(id)
92
{
93
savePlayer(id)
94
}
95
96
public OnFire(ent)
97
{
98
new id = pev(ent, pev_owner)
99
100
if (id < 1 || id > 32 || !is_user_connected(id))
101
return HAM_IGNORED
102
103
new w = weaponIndex(ent)
104
105
if (w == -1)
106
return HAM_IGNORED
107
108
g_iLastWeapon[id] = w
109
g_iMapShots[id][w]++
110
g_iLifeShots[id][w]++
111
112
return HAM_IGNORED
113
}
114
115
public OnDamage(victim, inflictor, attacker, Float:damage, damagetype)
116
{
117
if (attacker < 1 || attacker > 32 || attacker == victim)
118
return HAM_IGNORED
119
120
if (!(damagetype & DMG_BULLET))
121
return HAM_IGNORED
122
123
if (get_user_team(attacker) == get_user_team(victim))
124
return HAM_IGNORED
125
126
new w = g_iLastWeapon[attacker]
127
128
if (w < 0 || w >= MAX_WEAPONS)
129
return HAM_IGNORED
130
131
g_iMapHits[attacker][w]++
132
g_iLifeHits[attacker][w]++
133
134
return HAM_IGNORED
135
}
136
137
weaponIndex(ent)
138
{
139
static cls[32]
140
pev(ent, pev_classname, cls, charsmax(cls))
141
142
for (new w = 0; w < MAX_WEAPONS; w++)
143
{
144
if (equal(cls, g_szWeapons[w]))
145
return w
146
}
147
148
return -1
149
}
150
151
public cmdAcc(id)
152
{
153
client_print(id, print_console, " ")
154
client_print(id, print_console, "----- CSB Accuracy (this map / lifetime) -----")
155
156
for (new w = 0; w < MAX_WEAPONS; w++)
157
{
158
if (g_iMapShots[id][w] == 0 && g_iLifeShots[id][w] == 0)
159
continue
160
161
new name[24]
162
copy(name, charsmax(name), g_szWeapons[w])
163
replace(name, charsmax(name), "weapon_", "")
164
165
new Float:mAcc = g_iMapShots[id][w] > 0
166
? 100.0 * float(g_iMapHits[id][w]) / float(g_iMapShots[id][w]) : 0.0
167
new Float:lAcc = g_iLifeShots[id][w] > 0
168
? 100.0 * float(g_iLifeHits[id][w]) / float(g_iLifeShots[id][w]) : 0.0
169
170
client_print(id, print_console, "%-12s map %4d/%4d (%5.1f%%) life %5d/%5d (%5.1f%%)",
171
name, g_iMapHits[id][w], g_iMapShots[id][w], mAcc,
172
g_iLifeHits[id][w], g_iLifeShots[id][w], lAcc)
173
}
174
175
client_print(id, print_chat, "[CSB] Accuracy table printed to your console.")
176
return PLUGIN_HANDLED
177
}
178
179
loadPlayer(id)
180
{
181
if (g_iVault == -1)
182
return
183
184
new auth[36], key[44], data[1536]
185
get_user_authid(id, auth, charsmax(auth))
186
187
if (auth[0] == 0 || equal(auth, "STEAM_ID_PENDING"))
188
return
189
190
formatex(key, charsmax(key), "acc_%s", auth)
191
192
if (nvault_get(g_iVault, key, data, charsmax(data)) < 1)
193
return
194
195
/* data: "shots0 hits0 shots1 hits1 ..." */
196
new pos = 0, w = 0
197
new val[16]
198
199
while (w < MAX_WEAPONS)
200
{
201
pos = argparse(data, pos, val, charsmax(val))
202
203
if (pos == -1)
204
break
205
206
g_iLifeShots[id][w] = str_to_num(val)
207
208
pos = argparse(data, pos, val, charsmax(val))
209
210
if (pos == -1)
211
break
212
213
g_iLifeHits[id][w] = str_to_num(val)
214
w++
215
}
216
}
217
218
savePlayer(id)
219
{
220
if (g_iVault == -1)
221
return
222
223
new auth[36], key[44]
224
get_user_authid(id, auth, charsmax(auth))
225
226
if (auth[0] == 0 || equal(auth, "STEAM_ID_PENDING"))
227
return
228
229
formatex(key, charsmax(key), "acc_%s", auth)
230
231
new data[1536], len
232
len = 0
233
234
for (new w = 0; w < MAX_WEAPONS; w++)
235
{
236
len += formatex(data[len], charsmax(data) - len, "%d %d ",
237
g_iLifeShots[id][w], g_iLifeHits[id][w])
238
}
239
240
nvault_set(g_iVault, key, data)
241
}
242









