]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/weapons/accuracy.qc
a53adb6246c508875095c8c0d4f29da81825f343
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / accuracy.qc
1 #include "accuracy.qh"
2
3 #include <server/mutators/_mod.qh>
4 #include <server/g_damage.qh>
5 #include <common/constants.qh>
6 #include <common/net_linked.qh>
7 #include <server/player.qh>
8 #include <common/teams.qh>
9 #include <common/util.qh>
10 #include <common/weapons/_all.qh>
11
12 int accuracy_byte(float n, float d)
13 {
14         if (n <= 0) return 0;
15         if (n > d) return 255;
16         return 1 + rint(n * 100.0 / d);
17 }
18
19 bool accuracy_send(entity this, entity to, int sf)
20 {
21         WriteHeader(MSG_ENTITY, ENT_CLIENT_ACCURACY);
22
23         entity a = this.owner;
24         if (IS_SPEC(a)) a = a.enemy;
25         a = CS(a).accuracy;
26
27         if (to != a.owner)
28                 if (!autocvar_sv_accuracy_data_share && !CS(a.owner).cvar_cl_accuracy_data_share)
29                         sf = 0;
30         // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
31         WriteInt24_t(MSG_ENTITY, sf);
32         if (sf == 0) return true;
33         // note: we know that client and server agree about SendFlags...
34         int f = 1;
35         for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
36                 if (sf & f) WriteByte(MSG_ENTITY, accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w]));
37                 f = (f == 0x800000) ? 1 : f * 2;
38         }
39         return true;
40 }
41
42 // init/free
43 void accuracy_init(entity e)
44 {
45         entity a = CS(e).accuracy = new_pure(accuracy);
46         a.owner = e;
47         a.drawonlytoclient = e;
48         Net_LinkEntity(a, false, 0, accuracy_send);
49 }
50
51 void accuracy_free(entity e)
52 {
53         delete(CS(e).accuracy);
54 }
55
56 // force a resend of a player's accuracy stats
57 void accuracy_resend(entity e)
58 {
59         CS(e).accuracy.SendFlags = 0xFFFFFF;
60 }
61
62 // update accuracy stats
63 //.float hit_time;
64 .float fired_time;
65
66 void accuracy_add(entity this, Weapon w, int fired, int hit)
67 {
68         if (IS_INDEPENDENT_PLAYER(this)) return;
69         entity a = CS(this).accuracy;
70         if (!a) return;
71         if (!hit && !fired) return;
72         if (w == WEP_Null) return;
73         int wepid = w.m_id;
74         wepid -= WEP_FIRST;
75         int b = accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid]);
76         if (hit)    a.accuracy_hit  [wepid] += hit;
77         if (fired)  a.accuracy_fired[wepid] += fired;
78
79     if (hit && STAT(HIT_TIME, a) != time) { // only run this once per frame
80         a.accuracy_cnt_hit[wepid] += 1;
81         STAT(HIT_TIME, a) = time;
82     }
83
84     if (fired && a.fired_time != time) { // only run this once per frame
85         a.accuracy_cnt_fired[wepid] += 1;
86         a.fired_time = time;
87     }
88
89         if (b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change
90         int sf = 1 << (wepid % 24);
91         a.SendFlags |= sf;
92         FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; });
93 }
94
95 bool accuracy_isgooddamage(entity attacker, entity targ)
96 {
97         int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ);
98
99         if (warmup_stage) return false;
100         if (game_stopped) return false;
101
102         // damage to dead/frozen players is good only if it happens in the frame they get killed / frozen
103         // so that stats for weapons that shoot multiple projectiles per shot are properly counted
104         if (IS_DEAD(targ) && time > targ.death_time) return false;
105         if (STAT(FROZEN, targ) && time > targ.freeze_time) return false;
106         if (SAME_TEAM(attacker, targ)) return false;
107
108         if (mutator_check == MUT_ACCADD_INVALID) return true;
109
110         if (mutator_check != MUT_ACCADD_VALID) return false;
111         if (!IS_CLIENT(targ) || !IS_CLIENT(attacker)) return false;
112
113         return true;
114 }
115
116 bool accuracy_canbegooddamage(entity attacker)
117 {
118         return !warmup_stage && IS_CLIENT(attacker);
119 }