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