Xonotic
status_effects.qh
Go to the documentation of this file.
1 #pragma once
2 
3 #ifdef GAMEQC
5 
6 REGISTER_MUTATOR(status_effects, true);
7 #endif
8 
9 #include "all.qh"
10 
11 #ifdef GAMEQC
12 
13 .StatusEffects statuseffects;
15 .StatusEffects statuseffects_store;
16 
17 REGISTER_NET_LINKED(ENT_CLIENT_STATUSEFFECTS)
18 
19 const int StatusEffects_groups_minor = 8; // must be a multiple of 8 (one byte) to optimize bandwidth usage
20 const int StatusEffects_groups_major = 4; // must be >= ceil(REGISTRY_COUNT(StatusEffect) / StatusEffects_groups_minor)
21 #endif
22 
23 // no need to perform these checks on both server and client
24 #ifdef CSQC
26 {
27  if (StatusEffects_groups_minor / 8 != floor(StatusEffects_groups_minor / 8))
28  error("StatusEffects_groups_minor is not a multiple of 8.");
29  int min_major_value = ceil(REGISTRY_COUNT(StatusEffect) / StatusEffects_groups_minor);
30  if (StatusEffects_groups_major < min_major_value)
31  error(sprintf("StatusEffects_groups_major can not be < %d.", min_major_value));
32 }
33 #endif
34 
35 #ifdef SVQC
36 #define G_MAJOR(id) (floor((id) / StatusEffects_groups_minor))
37 #define G_MINOR(id) ((id) % StatusEffects_groups_minor)
38 #endif
39 
40 #ifdef CSQC
41 StatusEffects g_statuseffects;
42 void StatusEffects_entremove(entity this)
43 {
44  if(g_statuseffects == this)
45  g_statuseffects = NULL;
46 }
47 
48 NET_HANDLE(ENT_CLIENT_STATUSEFFECTS, bool isnew)
49 {
50  make_pure(this);
51  g_statuseffects = this;
52  this.entremove = StatusEffects_entremove;
53  const int majorBits = Readbits(StatusEffects_groups_major);
54  for (int i = 0; i < StatusEffects_groups_major; ++i) {
55  if (!(majorBits & BIT(i))) {
56  continue;
57  }
58  const int minorBits = Readbits(StatusEffects_groups_minor);
59  for (int j = 0; j < StatusEffects_groups_minor; ++j) {
60  if (!(minorBits & BIT(j))) {
61  continue;
62  }
63  const StatusEffects it = REGISTRY_GET(StatusEffect, StatusEffects_groups_minor * i + j);
64  this.statuseffect_time[it.m_id] = ReadFloat();
65  this.statuseffect_flags[it.m_id] = ReadByte();
66  }
67  }
68  return true;
69 }
70 #endif
71 
72 #ifdef SVQC
73 int SEFminorBitsArr[StatusEffects_groups_major];
74 void StatusEffects_Write(StatusEffects data, StatusEffects store)
75 {
76  if (!data) {
77  WriteShort(MSG_ENTITY, 0);
78  return;
79  }
80  TC(StatusEffects, data);
81 
82  for (int i = 0; i < StatusEffects_groups_major; ++i)
83  SEFminorBitsArr[i] = 0;
84 
85  int majorBits = 0;
86  FOREACH(StatusEffect, true, {
87  .float fld = statuseffect_time[it.m_id];
88  .int flg = statuseffect_flags[it.m_id];
89  const bool changed = (store.(fld) != data.(fld) || store.(flg) != data.(flg));
90  store.(fld) = data.(fld);
91  store.(flg) = data.(flg);
92  if (changed) {
93  int maj = G_MAJOR(it.m_id);
94  majorBits = BITSET(majorBits, BIT(maj), true);
95  SEFminorBitsArr[maj] = BITSET(SEFminorBitsArr[maj], BIT(G_MINOR(it.m_id)), true);
96  }
97  });
98 
99  Writebits(MSG_ENTITY, majorBits, StatusEffects_groups_major);
100  for (int i = 0; i < StatusEffects_groups_major; ++i)
101  {
102  if (!(majorBits & BIT(i)))
103  continue;
104 
105  const int minorBits = SEFminorBitsArr[i];
106  Writebits(MSG_ENTITY, minorBits, StatusEffects_groups_minor);
107  for (int j = 0; j < StatusEffects_groups_minor; ++j)
108  {
109  if (!(minorBits & BIT(j)))
110  continue;
111 
112  const entity it = REGISTRY_GET(StatusEffect, StatusEffects_groups_minor * i + j);
113  WriteFloat(MSG_ENTITY, data.statuseffect_time[it.m_id]);
114  WriteByte(MSG_ENTITY, data.statuseffect_flags[it.m_id]);
115  }
116  }
117 }
118 #endif
119 
120 #undef G_MAJOR
121 #undef G_MINOR
122 
123 #ifdef SVQC
124 bool StatusEffects_Send(StatusEffects this, Client to, int sf)
125 {
126  TC(StatusEffects, this);
127  WriteHeader(MSG_ENTITY, ENT_CLIENT_STATUSEFFECTS);
128  StatusEffects_Write(this, to.statuseffects_store);
129  return true;
130 }
131 
132 bool StatusEffects_customize(entity this, entity client)
133 {
134  // sends to spectators too!
135  return (client.statuseffects == this);
136 }
137 
138 void StatusEffects_new(entity this)
139 {
141  this.statuseffects = eff;
142  eff.owner = this;
143  if(this.statuseffects_store)
144  {
145  setcefc(eff, StatusEffects_customize);
146  Net_LinkEntity(eff, false, 0, StatusEffects_Send);
147  }
148 }
149 void StatusEffects_delete(entity e) { delete(e.statuseffects); e.statuseffects = NULL; }
150 // may be called on non-player entities, should be harmless!
151 void StatusEffects_update(entity e) { e.statuseffects.SendFlags = 0xFFFFFF; }
152 
153 // this clears the storage entity instead of the statuseffects object, useful for map resets and such
154 void StatusEffects_clearall(entity store)
155 {
156  if(!store)
157  return; // safety net
158  // NOTE: you will need to perform StatusEffects_update after this to update the storage entity
159  // (unless store is the storage entity)
160  FOREACH(StatusEffect, true, {
161  store.statuseffect_time[it.m_id] = 0;
162  store.statuseffect_flags[it.m_id] = 0;
163  });
164 }
165 
166 void StatusEffectsStorage_attach(entity e) { e.statuseffects_store = NEW(StatusEffects); e.statuseffects_store.drawonlytoclient = e; }
167 void StatusEffectsStorage_delete(entity e) { delete(e.statuseffects_store); e.statuseffects_store = NULL; }
168 
169 // called when an entity is deleted with delete() / remove()
170 // or when a player disconnects
171 void ONREMOVE(entity this)
172 {
173  // remove statuseffects object attached to 'this'
174  if(this.statuseffects && this.statuseffects.owner == this)
175  StatusEffects_delete(this);
176 }
177 #endif
178 
179 #ifdef GAMEQC
180 bool StatusEffects_active(StatusEffects this, entity actor);
181 
182 // runs every SV_StartFrame on the server
183 // called by HUD_Powerups_add on the client
184 void StatusEffects_tick(entity actor);
185 
186 // accesses the status effect timer, returns 0 if the entity has no statuseffects object
187 // pass g_statuseffects as the actor on client side
188 // pass the entity with a .statuseffects on server side
189 float StatusEffects_gettime(StatusEffects this, entity actor);
190 #endif
191 #ifdef SVQC
192 // call when applying the effect to an entity
193 void StatusEffects_apply(StatusEffects this, entity actor, float eff_time, int eff_flags);
194 
195 // copies all the status effect fields to the specified storage entity
196 // does not perform an update
197 void StatusEffects_copy(StatusEffects this, entity store, float time_offset);
198 
199 // call when removing the effect
200 void StatusEffects_remove(StatusEffects this, entity actor, int removal_type);
201 
202 void StatusEffects_removeall(entity actor, int removal_type);
203 #endif
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
#define NEW(cname,...)
Definition: oo.qh:105
#define REGISTER_MUTATOR(id, dependence)
Definition: base.qh:263
int m_id
Definition: all.qh:31
entity() spawn
#define REGISTRY_GET(id, i)
Definition: registry.qh:43
#define NET_HANDLE(id, param)
Definition: net.qh:12
#define STATIC_INIT(func)
during worldspawn
Definition: static.qh:32
entity to
Definition: self.qh:96
#define REGISTRY_COUNT(id)
Definition: registry.qh:18
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition: bits.qh:8
#define NULL
Definition: post.qh:17
#define TC(T, sym)
Definition: _all.inc:82
Definition: client.qh:88
#define make_pure(e)
Definition: oo.qh:12
#define BITSET(var, mask, flag)
Definition: bits.qh:11
void ONREMOVE(entity this)
#define FOREACH(list, cond, body)
Definition: iter.qh:19