]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/weapons/accuracy.qc
Merge branch 'master' into z411/bai-server
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / weapons / accuracy.qc
1 #include "accuracy.qh"
2
3 #include <common/constants.qh>
4 #include <common/net_linked.qh>
5 #include <common/teams.qh>
6 #include <common/gamemodes/gamemode/duel/duel.qh>
7 #include <common/util.qh>
8 #include <common/weapons/_all.qh>
9 #include <server/client.qh>
10 #include <server/damage.qh>
11 #include <server/mutators/_mod.qh>
12 #include <server/player.qh>
13 #include <server/world.qh>
14
15 int accuracy_byte(float n, float d)
16 {
17         if (n <= 0) return 0;
18         if (n > d) return 255;
19         return 1 + rint(n * 100.0 / d);
20 }
21
22 bool accuracy_send(entity this, entity to, int sf)
23 {
24         WriteHeader(MSG_ENTITY, ENT_CLIENT_ACCURACY);
25
26         entity a = this.owner;
27         if (IS_SPEC(a)) a = a.enemy;
28         a = CS(a).accuracy;
29         
30         // z411 send entity number
31         if(g_duel)
32                 WriteByte(MSG_ENTITY, etof(a.owner));
33         else
34                 WriteByte(MSG_ENTITY, 0);
35
36         if (to != a.owner)
37                 if (!autocvar_sv_accuracy_data_share && !CS_CVAR(a.owner).cvar_cl_accuracy_data_share)
38                         sf = 0;
39         // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
40         WriteInt24_t(MSG_ENTITY, sf);
41         if (sf == 0) return true;
42         
43         // note: we know that client and server agree about SendFlags...
44         int f = 1;
45         for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
46                 if (sf & f) {
47                         if(g_duel) {
48                                 WriteByte(MSG_ENTITY, a.accuracy_frags[w]);
49                                 WriteShort(MSG_ENTITY, a.accuracy_hit[w]);
50                                 WriteShort(MSG_ENTITY, a.accuracy_cnt_hit[w]);
51                                 WriteShort(MSG_ENTITY, a.accuracy_cnt_fired[w]);
52                         } else {
53                                 WriteByte(MSG_ENTITY, accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w]));
54                         }
55                 }
56                 f = (f == 0x800000) ? 1 : f * 2;
57         }
58         return true;
59 }
60
61 // init/free
62 void accuracy_init(entity e)
63 {
64         entity a = CS(e).accuracy = new_pure(accuracy);
65         e.roundaccuracy = new_pure(accuracy);
66         a.owner = e;
67         if(!g_duel) // z411
68                 a.drawonlytoclient = e;
69         Net_LinkEntity(a, false, 0, accuracy_send);
70 }
71
72 void accuracy_free(entity e)
73 {
74         delete(CS(e).accuracy);
75         delete(e.roundaccuracy);
76 }
77
78 void accuracy_reset(entity e)
79 {
80         entity a = CS(e).accuracy;
81         if (!a) return;
82
83         for (int i = 0; i < REGISTRY_MAX(Weapons); ++i)
84         {
85                 a.accuracy_frags[i] = 0;
86                 a.accuracy_hit[i] = 0;
87                 a.accuracy_fired[i] = 0;
88                 a.accuracy_cnt_hit[i] = 0;
89                 a.accuracy_cnt_fired[i] = 0;
90         }
91 }
92
93 // force a resend of a player's accuracy stats
94 void accuracy_resend(entity e)
95 {
96         CS(e).accuracy.SendFlags = 0xFFFFFF;
97 }
98
99 // update accuracy stats
100 //.float hit_time;
101 .float fired_time;
102
103 void roundaccuracy_clear(entity this)
104 {
105         if (IS_INDEPENDENT_PLAYER(this)) return;
106         entity ra = this.roundaccuracy;
107         
108         for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
109                 ra.accuracy_frags[w] = 0;
110                 ra.accuracy_hit[w] = 0;
111                 ra.accuracy_fired[w] = 0;
112                 ra.accuracy_cnt_hit[w] = 0;
113                 ra.accuracy_cnt_fired[w] = 0;
114         }
115 }
116
117 void accuracy_add(entity this, Weapon w, float fired, float hit)
118 {
119         if (IS_INDEPENDENT_PLAYER(this)) return;
120         entity a = CS(this).accuracy;
121         entity ra = this.roundaccuracy;
122         if (!a) return;
123         if (!hit && !fired) return;
124         if (w == WEP_Null) return;
125         int wepid = w.m_id;
126         wepid -= WEP_FIRST;
127         int b = accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid]);
128         if (hit) {
129                 a.accuracy_hit[wepid] += hit;
130                 ra.accuracy_hit[wepid] += hit;
131         }
132         if (fired) {
133                 a.accuracy_fired[wepid] += fired;
134                 ra.accuracy_fired[wepid] += fired;
135         }
136
137     if (hit && STAT(HIT_TIME, a) != time) { // only run this once per frame
138         a.accuracy_cnt_hit[wepid] += 1;
139                 ra.accuracy_cnt_hit[wepid] += 1;
140         STAT(HIT_TIME, a) = time;
141     }
142
143     if (fired && a.fired_time != time) { // only run this once per frame
144         a.accuracy_cnt_fired[wepid] += 1;
145                 ra.accuracy_cnt_fired[wepid] += 1;
146         a.fired_time = time;
147     }
148
149         if (!g_duel && b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change
150         int sf = 1 << (wepid % 24);
151         a.SendFlags |= sf;
152         
153         if(!g_duel)
154                 FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; });
155 }
156
157 bool accuracy_isgooddamage(entity attacker, entity targ)
158 {
159         int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ);
160
161         if (warmup_stage || game_stopped) return false;
162
163         // damage to dead/frozen players is good only if it happens in the frame they get killed / frozen
164         // so that stats for weapons that shoot multiple projectiles per shot are properly counted
165         if (IS_DEAD(targ) && time > targ.death_time) return false;
166         if (STAT(FROZEN, targ) && time > targ.freeze_time) return false;
167         if (SAME_TEAM(attacker, targ)) return false;
168
169         if (mutator_check == MUT_ACCADD_INVALID) return true;
170
171         if (mutator_check != MUT_ACCADD_VALID) return false;
172         if (!IS_CLIENT(targ) || !IS_CLIENT(attacker)) return false;
173
174         return true;
175 }
176
177 bool accuracy_canbegooddamage(entity attacker)
178 {
179         return !warmup_stage && IS_CLIENT(attacker);
180 }
181
182 REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share");
183 REPLICATE(cvar_cl_accuracy_data_receive, bool, "cl_accuracy_data_receive");