Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
base.qh
Go to the documentation of this file.
1#pragma once
2
3#ifdef CSQC
4 #include <client/main.qh>
5#elifdef MENUQC
6 #include <common/util.qh> // for icon_path_from_menuskin
7#endif
8
9const int CBC_ORDER_FIRST = 1;
10const int CBC_ORDER_LAST = 2;
11const int CBC_ORDER_EXCLUSIVE = 3;
12const int CBC_ORDER_ANY = 4;
13
15
16// Callbacks may be added to zero or more callback chains.
26 ATTRIB(Callback, cbc_func, bool());
27 CONSTRUCTOR(Callback, bool() func)
28 {
30 this.cbc_func = func;
31 }
33
34// Callback chains contain zero or more callbacks.
41 {
43 this.cbc = it;
44 this.cbc_order = order;
45 }
47
51 {
53 this.netname = _name;
54 }
55
56 bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
57 {
58 if (order & CBC_ORDER_FIRST)
59 {
60 if (order & CBC_ORDER_LAST)
61 if (this.cbc_order & CBC_ORDER_ANY)
62 return false;
63 if (this.cbc_order & CBC_ORDER_FIRST)
64 return false;
65 }
66 else if (order & CBC_ORDER_LAST)
67 {
68 if (this.cbc_order & CBC_ORDER_LAST)
69 return false;
70 }
71 entity node = NEW(CallbackNode, cb, order);
72 if (order & CBC_ORDER_FIRST)
73 {
74 node.cbc_next = this.cbc_next;
75 this.cbc_next = node;
76 }
77 else if (order & CBC_ORDER_LAST)
78 {
79 CallbackNode prev = NULL, it = this.cbc_next;
80 while (it)
81 {
82 prev = it;
83 it = it.cbc_next;
84 }
85 if (prev)
86 prev.cbc_next = node;
87 else
88 this.cbc_next = node;
89 }
90 else
91 {
92 // by default we execute last, but before a possible CBC_ORDER_LAST callback
93 CallbackNode prev = NULL, it = this.cbc_next;
94 while (it && !(it.cbc_order & CBC_ORDER_LAST))
95 {
96 prev = it;
97 it = it.cbc_next;
98 }
99 node.cbc_next = it;
100 if (prev)
101 prev.cbc_next = node;
102 else
103 this.cbc_next = node;
104 }
105 this.cbc_order |= order | CBC_ORDER_ANY;
106 return true;
107 }
109 {
110 int n = 0, order = 0;
111 for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next)
112 {
113 if (it.cbc == cb)
114 {
115 // remove it from the chain
116 Callback next = it.cbc_next;
117 if (prev)
118 prev.cbc_next = next;
119 else
120 this.cbc_next = next;
121 ++n;
122 }
123 // it is now something we want to keep
124 order |= it.cbc_order & CBC_ORDER_ANY;
125 }
126 this.cbc_order = order;
127 return n;
128 }
130 {
131 bool r = false;
132 for (Callback it = this.cbc_next; it; it = it.cbc_next)
133 {
135 r |= it.cbc.cbc_func();
136 }
137 return r; // callbacks return an error status, so 0 is default return value
138 }
140
141
142// Limits can be increased as needed
143#define MUTATOR_TYPE_ARRAY(type, limit) noref type MUTATOR_ARGV_##type[limit]
150#undef MUTATOR_TYPE_ARRAY
151
152// TODO: search for and remove any leftovers from when globals were used
153#define M_ARGV(idx, type) MUTATOR_ARGV_##type[idx]
154
155#define _MUTATOR_HANDLE_NOP(type, idx)
156#define _MUTATOR_HANDLE_PARAMS(type, idx) , type in_MUTATOR_ARGV_##type##_##idx
157#define _MUTATOR_HANDLE_PREPARE(type, idx) MUTATOR_ARGV_##type[idx] = in_MUTATOR_ARGV_##type##_##idx;
158#define _MUTATOR_HANDLE_PUSHTMP(type, idx) type tmp_MUTATOR_ARGV_##type##_##idx = MUTATOR_ARGV_##type[idx];
159#define _MUTATOR_HANDLE_PUSHOUT(type, idx) type out_MUTATOR_ARGV_##type##_##idx = MUTATOR_ARGV_##type[idx];
160#define _MUTATOR_HANDLE_POPTMP(type, idx) MUTATOR_ARGV_##type[idx] = tmp_MUTATOR_ARGV_##type##_##idx;
161#define _MUTATOR_HANDLE_POPOUT(type, idx) MUTATOR_ARGV_##type[idx] = out_MUTATOR_ARGV_##type##_##idx;
162
163#define EV_NO_ARGS(i, o)
164
167
168#define MUTATOR_HOOKABLE(name, params) _MUTATOR_HOOKABLE(name, params)
169#define _MUTATOR_HOOKABLE(name, params) \
170 CallbackChain HOOK_##name; \
171 bool __Mutator_Send_##name(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) \
172 { \
173 params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
174 params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
175 bool ret = CallbackChain_Call(HOOK_##name); \
176 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_PUSHOUT) \
177 params(_MUTATOR_HANDLE_POPTMP, _MUTATOR_HANDLE_NOP) \
178 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_POPOUT) \
179 return ret; \
180 } \
181 ACCUMULATE void RegisterHooks() \
182 { \
183 HOOK_##name = NEW(CallbackChain, #name); \
184 }
185
186#define MUTATOR_CALLHOOK(name, ...) _MUTATOR_CALLHOOK(name, __VA_ARGS__)
187#ifdef __STDC__
188 #define _MUTATOR_CALLHOOK(name, ...) APPLY(__Mutator_Send_##name, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
189#else
190 #define _MUTATOR_CALLHOOK(name, ...) APPLY(__Mutator_Send_##name, 0, ##__VA_ARGS__)
191#endif
192
193enum
194{
198};
199
201
202#ifdef MENUQC
203 #include <menu/draw.qh>
204#endif
205
207 ATTRIB(Mutator, m_id, int, 0);
210 ATTRIB(Mutator, m_icon, string);
213 ATTRIB(Mutator, mutatorcheck, bool());
214#ifdef MENUQC
216 {
217 TC(Mutator, this);
218 return SUPER(Mutator).describe(this);
219 }
220 METHOD(Mutator, display, void(Mutator this, void(string name, string icon) returns))
221 {
222 TC(Mutator, this);
223 returns(this.message, icon_path_from_menuskin(this.m_icon));
224 }
225#endif
227
228REGISTRY(Mutators, BITS(7))
229
230REGISTRY_DEFINE_GET(Mutators, NULL)
231bool Mutator_Add(Mutator mut);
232void Mutator_Remove(Mutator mut);
235
236#define _MUTATOR_IS_ENABLED(this) this.mutatorcheck()
237#define MUTATOR_IS_ENABLED(this) _MUTATOR_IS_ENABLED(MUTATOR_##this)
238
239#ifdef GAMEQC
240// Server mutators activate corresponding client mutators for all clients
242
243#ifdef SVQC
244bool Mutator_SendEntity(entity this, entity to, int sf)
245{
248 return true;
249}
250#elifdef CSQC
252{
253 string s = this.netname;
254 WITH(bool, mutator_log, true, FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it)));
255}
256NET_HANDLE(Mutator, bool isNew)
257{
258 make_pure(this);
259 string s = this.netname = ReadString();
260 return = true;
261 if (isNew)
262 {
263 make_pure(this);
264 this.entremove = NET_Mutator_Remove;
265 int added = 0;
266 WITH(bool, mutator_log, true, FOREACH(Mutators, it.registered_id == s,
267 {
268 Mutator_Add(it);
269 ++added;
270 }));
271 if (added > 1)
272 LOG_WARNF("Added more than one mutator for %s", s);
273 }
274}
275#endif
276
277#endif // GAMEQC
278
280{
281 if (mut.m_added)
282 return true; // already added
283
284 mut.m_added = true;
285 mutatorfunc_t func = mut.mutatorfunc;
286 if (!func(MUTATOR_ADDING))
287 {
288 // good
289 if (mutator_log)
290 LOG_TRACEF("Mutator: added %s", mut.m_name);
291 #ifdef SVQC
292 Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
293 #endif
294 return true;
295 }
296 backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
297 if (func(MUTATOR_ROLLING_BACK))
298 // baaaaad
299 error("WARNING: when adding mutator: rolling back failed");
300
301 return false;
302}
303
305{
306 if (!mut.m_added)
307 {
308 backtrace("WARNING: removing not-added mutator\n");
309 return;
310 }
311
312 mut.m_added = false;
313 mutatorfunc_t func = mut.mutatorfunc;
314 if (func(MUTATOR_REMOVING))
315 // baaaaad
316 error("Mutator_Remove: removing mutator failed");
317
318 if (mutator_log)
319 LOG_TRACEF("Mutator: removed %s", mut.m_name);
320#ifdef SVQC
321 Net_UnlinkEntity(mut);
322#endif
323}
324
325#define _REGISTER_MUTATOR(id, dependence, cname) \
326 bool MUTATORFUNC_##id##_hooks(int mode) \
327 { \
328 return = false; \
329 } \
330 bool MUTATORFUNC_##id(int mode) \
331 { \
332 return = false; \
333 bool ret = MUTATORFUNC_##id##_hooks(mode); \
334 if (ret) \
335 return ret; \
336 } \
337 bool MUTATOR_##id##_check() \
338 { \
339 return dependence; \
340 } \
341 REGISTER(Mutators, MUTATOR, id, m_id, NEW(cname)) \
342 { \
343 this.m_name = #id; \
344 this.mutatorfunc = MUTATORFUNC_##id; \
345 this.mutatorcheck = MUTATOR_##id##_check; \
346 } \
347 ACCUMULATE bool MUTATORFUNC_##id(int mode)
348
349#define REGISTER_MUTATOR(...) EVAL(OVERLOAD(REGISTER_MUTATOR, __VA_ARGS__))
350#define REGISTER_MUTATOR_2(id, dependence) _REGISTER_MUTATOR(id, dependence, Mutator)
351#define REGISTER_MUTATOR_3(id, dependence, cname) _REGISTER_MUTATOR(id, dependence, cname)
352
353STATIC_INIT(Mutators)
354{
357 RegisterMutators();
358}
359
361{
362 FOREACH(Mutators, _MUTATOR_IS_ENABLED(it), Mutator_Add(it));
363}
364
365#define MUTATOR_ONADD if (mode == MUTATOR_ADDING)
366#define MUTATOR_ONREMOVE if (mode == MUTATOR_REMOVING)
367#define MUTATOR_ONROLLBACK_OR_REMOVE if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
368
369#define MUTATOR_STATIC() MACRO_BEGIN \
370 MUTATOR_ONADD \
371 { \
372 if (time > 1) /* game loads at time 1 */ \
373 error("This is a gametype and it cannot be added at runtime."); \
374 } \
375 MUTATOR_ONREMOVE \
376 { \
377 LOG_INFO("This is a gametype and it cannot be removed at runtime."); \
378 return -1; \
379 } \
380MACRO_END
381
382#define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
383#define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
384#define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
385
386#define _MUTATOR_CALLBACK(name, func) \
387 Callback CB_##name; \
388 bool func(); \
389 ACCUMULATE void RegisterCallbacks() \
390 { \
391 CB_##name = NEW(Callback, func); \
392 }
393
394#define MUTATOR_HOOKFUNCTION(...) \
395 EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
396#define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
397
398#define MUTATOR_HOOKFUNCTION_2(mut, cb) \
399 MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
400
401#define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \
402 _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \
403 ACCUMULATE bool MUTATORFUNC_##mut##_hooks(int mode) \
404 { \
405 MUTATOR_HOOK(cb, mut##_##cb, order); \
406 } \
407 bool mut##_##cb() \
408 { \
409 return = false; \
410 } \
411 ACCUMULATE bool mut##_##cb()
412
413void _mutPrintFail(string cb, string func)
414{
415 // this is inside a function to avoid expanding it on compilation everytime
416 LOG_INFO("HOOK FAILED: ", cb, ":", func);
417}
418
419#define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN \
420 MUTATOR_ONADD \
421 { \
422 if (!CallbackChain_Add(HOOK_##cb, CB_##func, order)) \
423 { \
424 _mutPrintFail(#cb, #func); \
425 return true; \
426 } \
427 } \
428 MUTATOR_ONROLLBACK_OR_REMOVE \
429 { \
430 CallbackChain_Remove(HOOK_##cb, CB_##func); \
431 } \
432MACRO_END
433
434#include "events.qh"
#define MUTATOR_TYPE_ARRAY(type, limit)
Definition base.qh:143
@ MUTATOR_ADDING
Definition base.qh:196
@ MUTATOR_REMOVING
Definition base.qh:195
@ MUTATOR_ROLLING_BACK
Definition base.qh:197
void Mutator_Remove(Mutator mut)
Definition base.qh:304
bool(int) mutatorfunc_t
Definition base.qh:200
const int CBC_ORDER_FIRST
Definition base.qh:9
bool Mutator_SendEntity(entity this, entity to, int sf)
Definition base.qh:244
bool m_added
Definition base.qh:234
const int CBC_ORDER_LAST
Definition base.qh:10
#define _MUTATOR_IS_ENABLED(this)
Definition base.qh:236
const int CBC_ORDER_ANY
Definition base.qh:12
const int CBC_ORDER_EXCLUSIVE
Definition base.qh:11
bool mutator_log
Definition base.qh:233
void _mutPrintFail(string cb, string func)
Definition base.qh:413
void NET_Mutator_Remove(entity this)
Definition base.qh:251
void RegisterHooks()
Definition base.qh:165
bool CallbackChain_ReturnValue
read-only field of the current return value
Definition base.qh:14
bool Mutator_Add(Mutator mut)
Definition base.qh:279
void RegisterCallbacks()
Definition base.qh:166
#define BITS(n)
Definition bits.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define ReadString
CallbackNode::CallbackNode(Callback it, int order)
Definition base.qh:40
ATTRIB(CallbackNode, cbc, Callback)
int cbc_order
Definition base.qh:49
bool CallbackChain_Call(CallbackChain this)
Definition base.qh:129
bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
Definition base.qh:56
int CallbackChain_Remove(CallbackChain this, Callback cb)
Definition base.qh:108
ATTRIB(CallbackChain, cbc_next, CallbackNode)
CallbackChain(string _name)
Definition base.qh:50
Callback(bool() func)
Definition base.qh:27
ATTRIB(Callback, cbc_func, bool())
a callback function is like this: bool mycallback() { do something return false; }
int m_id
Definition base.qh:207
virtual void describe()
Definition base.qh:215
virtual void display()
Definition base.qh:220
ATTRIB(Mutator, message, string)
vector m_color
Definition base.qh:209
string netname
Definition powerups.qc:20
string message
Definition powerups.qc:19
string m_name
Definition scores.qh:142
string icon_path_from_menuskin(string theIcon)
Returns icon path from the current menu skin (MENU only).
Definition util.qc:239
#define false
Definition csprogsdefs.qh:6
WriteString(chan, ent.netname)
prev
Definition all.qh:53
next
Definition all.qh:75
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define USING(name, T)
Definition _all.inc:72
#define TC(T, sym)
Definition _all.inc:82
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:156
void Net_UnlinkEntity(entity e)
Definition net.qh:196
#define WriteHeader(to, id)
Definition net.qh:265
#define REGISTER_NET_LINKED(id)
Definition net.qh:91
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define LOG_WARNF(...)
Definition log.qh:59
#define LOG_TRACEF(...)
Definition log.qh:75
#define LOG_INFO(...)
Definition log.qh:62
#define backtrace(msg)
Definition log.qh:96
string name
Definition menu.qh:30
#define WITH(type, name, value, block)
Definition misc.qh:37
#define NEW(cname,...)
Definition oo.qh:120
#define SUPER(cname)
Definition oo.qh:236
#define make_pure(e)
direct use is
Definition oo.qh:13
#define CLASS(...)
Definition oo.qh:149
#define ENDCLASS(cname)
Definition oo.qh:286
#define METHOD(cname, name, prototype)
Definition oo.qh:274
#define CONSTRUCTOR(cname,...)
Definition oo.qh:217
#define CONSTRUCT(cname,...)
Definition oo.qh:126
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
string registered_id
Registered item identifier.
Definition registry.qh:94
#define REGISTRY(id, max)
Declares a new registry.
Definition registry.qh:23
#define REGISTRY_DEFINE_GET(id, null)
Definition registry.qh:50
vector
Definition self.qh:96
#define STATIC_INIT_LATE(func)
directly after STATIC_INIT
Definition static.qh:38
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:33