3 #include <common/constants.qh>
4 #include <common/net_linked.qh>
5 #include <common/teams.qh>
6 #include <common/util.qh>
7 #include <common/weapons/_all.qh>
8 #include <server/client.qh>
9 #include <server/damage.qh>
10 #include <server/mutators/_mod.qh>
11 #include <server/player.qh>
12 #include <server/world.qh>
14 int accuracy_byte(float n, float d)
17 if (n > d) return 255;
18 return 1 + rint(n * 100.0 / d);
21 bool accuracy_send(entity this, entity to, int sf)
23 WriteHeader(MSG_ENTITY, ENT_CLIENT_ACCURACY);
25 entity a = this.owner;
26 if (IS_SPEC(a)) a = a.enemy;
30 if (!autocvar_sv_accuracy_data_share && !CS_CVAR(a.owner).cvar_cl_accuracy_data_share)
32 // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
33 WriteInt24_t(MSG_ENTITY, sf);
34 if (sf == 0) return true;
35 // note: we know that client and server agree about SendFlags...
37 for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
38 if (sf & f) WriteByte(MSG_ENTITY, accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w]));
39 f = (f == 0x800000) ? 1 : f * 2;
45 void accuracy_init(entity e)
47 entity a = CS(e).accuracy = new_pure(accuracy);
49 a.drawonlytoclient = e;
50 Net_LinkEntity(a, false, 0, accuracy_send);
53 void accuracy_free(entity e)
55 delete(CS(e).accuracy);
58 void accuracy_reset(entity e)
60 entity a = CS(e).accuracy;
63 for (int i = 0; i < REGISTRY_MAX(Weapons); i++)
65 a.accuracy_frags[i] = 0;
66 a.accuracy_hit[i] = 0;
67 a.accuracy_fired[i] = 0;
68 a.accuracy_cnt_hit[i] = 0;
69 a.accuracy_cnt_fired[i] = 0;
73 // force a resend of a player's accuracy stats
74 void accuracy_resend(entity e)
76 CS(e).accuracy.SendFlags = 0xFFFFFF;
79 // update accuracy stats
83 void accuracy_add(entity this, Weapon w, float fired, float hit)
85 if (IS_INDEPENDENT_PLAYER(this)) return;
86 entity a = CS(this).accuracy;
88 if (!hit && !fired) return;
89 if (w == WEP_Null) return;
92 int b = accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid]);
93 if (hit) a.accuracy_hit [wepid] += hit;
94 if (fired) a.accuracy_fired[wepid] += fired;
96 if (hit && STAT(HIT_TIME, a) != time) { // only run this once per frame
97 a.accuracy_cnt_hit[wepid] += 1;
98 STAT(HIT_TIME, a) = time;
101 if (fired && a.fired_time != time) { // only run this once per frame
102 a.accuracy_cnt_fired[wepid] += 1;
106 if (b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change
107 int sf = 1 << (wepid % 24);
109 FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; });
112 bool accuracy_isgooddamage(entity attacker, entity targ)
114 int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ);
116 if (warmup_stage || game_stopped) return false;
118 // damage to dead/frozen players is good only if it happens in the frame they get killed / frozen
119 // so that stats for weapons that shoot multiple projectiles per shot are properly counted
120 if (IS_DEAD(targ) && time > targ.death_time) return false;
121 if (STAT(FROZEN, targ) && time > targ.freeze_time) return false;
122 if (SAME_TEAM(attacker, targ)) return false;
124 if (mutator_check == MUT_ACCADD_INVALID) return true;
126 if (mutator_check != MUT_ACCADD_VALID) return false;
127 if (!IS_CLIENT(targ) || !IS_CLIENT(attacker)) return false;
132 bool accuracy_canbegooddamage(entity attacker)
134 return !warmup_stage && IS_CLIENT(attacker);
137 REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share");
138 REPLICATE(cvar_cl_accuracy_data_receive, bool, "cl_accuracy_data_receive");