Daemon666 / CSB Accuracy Tracker Herkese açık

Counts shots and hits per weapon, keeps per-map and lifetime accuracy in nVault, shown with /acc.

v1.0.0 137 indirme GPL-3.0 Son güncelleme Aug 23, 2026
csb_accuracy_tracker.sma v1.0.0 242 satır · 5.9 KB Ham
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