Xonotic
accuracy.qc
Go to the documentation of this file.
1 #include "accuracy.qh"
2 
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>
13 
14 int accuracy_byte(float n, float d)
15 {
16  if (n <= 0) return 0;
17  if (n > d) return 255;
18  return 1 + rint(n * 100.0 / d);
19 }
20 
21 bool accuracy_send(entity this, entity to, int sf)
22 {
23  WriteHeader(MSG_ENTITY, ENT_CLIENT_ACCURACY);
24 
25  entity a = this.owner;
26  if (IS_SPEC(a)) a = a.enemy;
27  a = CS(a).accuracy;
28 
29  if (to != a.owner)
30  if (!autocvar_sv_accuracy_data_share && !CS_CVAR(a.owner).cvar_cl_accuracy_data_share)
31  sf = 0;
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...
36  int f = 1;
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;
40  }
41  return true;
42 }
43 
44 // init/free
46 {
47  entity a = CS(e).accuracy = new_pure(accuracy);
48  a.owner = e;
49  a.drawonlytoclient = e;
50  Net_LinkEntity(a, false, 0, accuracy_send);
51 }
52 
54 {
55  delete(CS(e).accuracy);
56 }
57 
59 {
60  entity a = CS(e).accuracy;
61  if (!a) return;
62 
63  for (int i = 0; i < REGISTRY_MAX(Weapons); i++)
64  {
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;
70  }
71 }
72 
73 // force a resend of a player's accuracy stats
75 {
76  CS(e).accuracy.SendFlags = 0xFFFFFF;
77 }
78 
79 // update accuracy stats
80 //.float hit_time;
81 .float fired_time;
82 
83 void accuracy_add(entity this, Weapon w, float fired, float hit)
84 {
85  if (IS_INDEPENDENT_PLAYER(this)) return;
86  entity a = CS(this).accuracy;
87  if (!a) return;
88  if (!hit && !fired) return;
89  if (w == WEP_Null) return;
90  int wepid = w.m_id;
91  wepid -= WEP_FIRST;
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;
95 
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;
99  }
100 
101  if (fired && a.fired_time != time) { // only run this once per frame
102  a.accuracy_cnt_fired[wepid] += 1;
103  a.fired_time = time;
104  }
105 
106  if (b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change
107  int sf = 1 << (wepid % 24);
108  a.SendFlags |= sf;
109  FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; });
110 }
111 
112 bool accuracy_isgooddamage(entity attacker, entity targ)
113 {
114  int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ);
115 
116  if (warmup_stage || game_stopped) return false;
117 
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;
123 
124  if (mutator_check == MUT_ACCADD_INVALID) return true;
125 
126  if (mutator_check != MUT_ACCADD_VALID) return false;
127  if (!IS_CLIENT(targ) || !IS_CLIENT(attacker)) return false;
128 
129  return true;
130 }
131 
133 {
134  return !warmup_stage && IS_CLIENT(attacker);
135 }
136 
137 REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share");
138 REPLICATE(cvar_cl_accuracy_data_receive, bool, "cl_accuracy_data_receive");
const int WEP_FIRST
Definition: all.qh:304
#define IS_CLIENT(v)
Definition: utils.qh:13
#define IS_INDEPENDENT_PLAYER(e)
Definition: client.qh:314
entity() spawn
ClientState CS(Client this)
Definition: state.qh:47
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
#define CS_CVAR(this)
Definition: state.qh:51
bool warmup_stage
Definition: main.qh:103
entity to
Definition: self.qh:96
#define WEP_LAST
Definition: all.qh:305
int accuracy_byte(float n, float d)
Definition: accuracy.qc:14
entity owner
Definition: main.qh:73
bool accuracy_send(entity this, entity to, int sf)
Definition: accuracy.qc:21
void accuracy_free(entity e)
Definition: accuracy.qc:53
#define IS_SPEC(v)
Definition: utils.qh:10
entity accuracy
Definition: accuracy.qh:26
void accuracy_reset(entity e)
Definition: accuracy.qc:58
void accuracy_init(entity e)
Definition: accuracy.qc:45
float autocvar_sv_accuracy_data_share
Weapon Accuracy stats.
Definition: accuracy.qh:21
#define REGISTRY_MAX(id)
Definition: registry.qh:17
#define SAME_TEAM(a, b)
Definition: teams.qh:239
#define IS_DEAD(s)
Definition: utils.qh:26
void accuracy_add(entity this, Weapon w, float fired, float hit)
Definition: accuracy.qc:83
float fired_time
Definition: accuracy.qc:81
bool accuracy_isgooddamage(entity attacker, entity targ)
Definition: accuracy.qc:112
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition: weapon.qh:41
float time
Definition: csprogsdefs.qc:16
int m_id
Definition: weapon.qh:42
bool accuracy_canbegooddamage(entity attacker)
Definition: accuracy.qc:132
void accuracy_resend(entity e)
Definition: accuracy.qc:74
REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share")