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#endif
6#ifndef SVQC
7#include <common/util.qh> // for icon_path_from_menuskin
8#endif
9
10const int CBC_ORDER_FIRST = 1;
11const int CBC_ORDER_LAST = 2;
12const int CBC_ORDER_EXCLUSIVE = 3;
13const int CBC_ORDER_ANY = 4;
14
15bool CallbackChain_ReturnValue; // read-only field of the current return value
16
29 ATTRIB(Callback, cbc_func, bool());
30 CONSTRUCTOR(Callback, bool() func) {
32 this.cbc_func = func;
33 }
35
36
46 this.cbc = it;
47 this.cbc_order = order;
48 }
50
53 CONSTRUCTOR(CallbackChain, string _name) {
55 this.netname = _name;
56 }
57
58 bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
59 {
60 if (order & CBC_ORDER_FIRST) {
61 if (order & CBC_ORDER_LAST)
62 if (this.cbc_order & CBC_ORDER_ANY)
63 return false;
64 if (this.cbc_order & CBC_ORDER_FIRST)
65 return false;
66 } else if (order & CBC_ORDER_LAST) {
67 if (this.cbc_order & CBC_ORDER_LAST)
68 return false;
69 }
70 entity node = NEW(CallbackNode, cb, order);
71 if (order & CBC_ORDER_FIRST) {
72 node.cbc_next = this.cbc_next;
73 this.cbc_next = node;
74 } else if (order & CBC_ORDER_LAST) {
75 CallbackNode prev = NULL, it = this.cbc_next;
76 while (it) { prev = it, it = it.cbc_next; }
77 if (prev) prev.cbc_next = node;
78 else this.cbc_next = node;
79 } else {
80 // by default we execute last, but before a possible CBC_ORDER_LAST callback
81 CallbackNode prev = NULL, it = this.cbc_next;
82 while (it && !(it.cbc_order & CBC_ORDER_LAST)) { prev = it, it = it.cbc_next; }
83 node.cbc_next = it;
84 if (prev) prev.cbc_next = node;
85 else this.cbc_next = node;
86 }
87 this.cbc_order |= (order | CBC_ORDER_ANY);
88 return true;
89 }
91 {
92 int n = 0, order = 0;
93 for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
94 if (it.cbc == cb) {
95 // remove it from the chain
96 Callback next = it.cbc_next;
97 if (prev) prev.cbc_next = next;
98 else this.cbc_next = next;
99 ++n;
100 }
101 // it is now something we want to keep
102 order |= (it.cbc_order & CBC_ORDER_ANY);
103 }
104 this.cbc_order = order;
105 return n;
106 }
108 {
109 bool r = false;
110 for (Callback it = this.cbc_next; it; it = it.cbc_next) {
112 r |= it.cbc.cbc_func();
113 }
114 return r; // callbacks return an error status, so 0 is default return value
115 }
117
118#define _MUTATOR_HANDLE_NOP(type, id)
119#define _MUTATOR_HANDLE_PARAMS(type, id) , type in_##id
120#define _MUTATOR_HANDLE_PREPARE(type, id) id = in_##id;
121#define _MUTATOR_HANDLE_PUSHTMP(type, id) TC(type, id); type tmp_##id = id;
122#define _MUTATOR_HANDLE_PUSHOUT(type, id) type out_##id = id;
123#define _MUTATOR_HANDLE_POPTMP(type, id) id = tmp_##id;
124#define _MUTATOR_HANDLE_POPOUT(type, id) id = out_##id;
125
128
129#define MUTATOR_HOOKABLE(id, params) _MUTATOR_HOOKABLE(id, params)
130#define _MUTATOR_HOOKABLE(id, params) \
131 CallbackChain HOOK_##id; \
132 bool __Mutator_Send_##id(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
133 params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
134 params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
135 bool ret = CallbackChain_Call(HOOK_##id); \
136 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_PUSHOUT) \
137 params(_MUTATOR_HANDLE_POPTMP, _MUTATOR_HANDLE_NOP) \
138 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_POPOUT) \
139 return ret; \
140 } \
141 ACCUMULATE void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
142
143#define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
144#ifdef __STDC__
145 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
146#else
147 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
148#endif
149
150enum {
154};
155
157
158#ifdef MENUQC
159#include <menu/draw.qh>
160#endif
161
163 ATTRIB(Mutator, m_id, int, 0);
166 ATTRIB(Mutator, m_icon, string);
169 ATTRIB(Mutator, mutatorcheck, bool());
170#ifdef MENUQC
172 {
173 TC(Mutator, this);
174 return SUPER(Mutator).describe(this);
175 }
176 METHOD(Mutator, display, void(Mutator this, void(string name, string icon) returns))
177 {
178 TC(Mutator, this);
179 returns(this.message, icon_path_from_menuskin(this.m_icon));
180 }
181#endif
183
184REGISTRY(Mutators, BITS(7))
185
186REGISTRY_DEFINE_GET(Mutators, NULL)
187bool Mutator_Add(Mutator mut);
188void Mutator_Remove(Mutator mut);
191
192#define _MUTATOR_IS_ENABLED(this) this.mutatorcheck()
193#define MUTATOR_IS_ENABLED(this) _MUTATOR_IS_ENABLED(MUTATOR_##this)
194
195#ifdef GAMEQC
198
199#ifdef SVQC
200bool Mutator_SendEntity(entity this, entity to, int sf)
201{
202 int chan = MSG_ENTITY;
203 WriteHeader(chan, Mutator);
204 WriteString(chan, this.registered_id);
205 return true;
206}
207#endif
208
209#ifdef CSQC
211{
212 string s = this.netname;
213 WITH(bool, mutator_log, true, {
214 FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
215 });
216}
217NET_HANDLE(Mutator, bool isNew)
218{
219 make_pure(this);
220 string s = this.netname = ReadString();
221 return = true;
222 if (isNew)
223 {
224 make_pure(this);
225 this.entremove = NET_Mutator_Remove;
226 int added = 0;
227 WITH(bool, mutator_log, true, {
228 FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
229 });
230 if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
231 }
232}
233#endif
234
235#endif
236
238{
239 if(mut.m_added)
240 return true; // already added
241
242 mut.m_added = true;
243 mutatorfunc_t func = mut.mutatorfunc;
244 if (!func(MUTATOR_ADDING)) {
245 // good
246 if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name);
247#ifdef SVQC
248 Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
249#endif
250 return true;
251 }
252 backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
253 if (func(MUTATOR_ROLLING_BACK)) {
254 // baaaaad
255 error("WARNING: when adding mutator: rolling back failed");
256 }
257 return false;
258}
259
261{
262 if(!mut.m_added)
263 {
264 backtrace("WARNING: removing not-added mutator\n");
265 return;
266 }
267
268 mut.m_added = false;
269 mutatorfunc_t func = mut.mutatorfunc;
270 if (func(MUTATOR_REMOVING)) {
271 // baaaaad
272 error("Mutator_Remove: removing mutator failed");
273 }
274 if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
275#ifdef SVQC
276 Net_UnlinkEntity(mut);
277#endif
278}
279
280#define _REGISTER_MUTATOR(id, dependence, cname) \
281 bool MUTATORFUNC_##id##_hooks(int mode) { return = false; } \
282 bool MUTATORFUNC_##id(int mode) { \
283 return = false; \
284 bool ret = MUTATORFUNC_##id##_hooks(mode); if (ret) return ret; \
285 } \
286 bool MUTATOR_##id##_check() { return dependence; } \
287 REGISTER(Mutators, MUTATOR, id, m_id, NEW(cname)) \
288 { \
289 this.m_name = #id; \
290 this.mutatorfunc = MUTATORFUNC_##id; \
291 this.mutatorcheck = MUTATOR_##id##_check; \
292 } \
293 ACCUMULATE bool MUTATORFUNC_##id(int mode)
294
295#define REGISTER_MUTATOR(...) EVAL(OVERLOAD(REGISTER_MUTATOR, __VA_ARGS__))
296#define REGISTER_MUTATOR_2(id, dependence) _REGISTER_MUTATOR(id, dependence, Mutator)
297#define REGISTER_MUTATOR_3(id, dependence, cname) _REGISTER_MUTATOR(id, dependence, cname)
298
299STATIC_INIT(Mutators) {
302 RegisterMutators();
303}
304
306 FOREACH(Mutators, _MUTATOR_IS_ENABLED(it), Mutator_Add(it));
307}
308
309#define MUTATOR_ONADD if (mode == MUTATOR_ADDING)
310#define MUTATOR_ONREMOVE if (mode == MUTATOR_REMOVING)
311#define MUTATOR_ONROLLBACK_OR_REMOVE if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
312
313#define MUTATOR_STATIC() MACRO_BEGIN \
314 MUTATOR_ONADD { \
315 /* game loads at time 1 */ \
316 if (time > 1) { \
317 error("This is a gametype and it cannot be added at runtime."); \
318 } \
319 } \
320 MUTATOR_ONREMOVE { \
321 LOG_INFO("This is a gametype and it cannot be removed at runtime."); \
322 return -1; \
323 } \
324MACRO_END
325
326#define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
327#define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
328#define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
329
330#define _MUTATOR_CALLBACK(name, func) \
331 Callback CB_##name; \
332 bool func(); \
333 ACCUMULATE void RegisterCallbacks() { CB_##name = NEW(Callback, func); }
334
335#define MUTATOR_HOOKFUNCTION(...) \
336 EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
337#define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
338
339#define MUTATOR_HOOKFUNCTION_2(mut, cb) \
340 MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
341
342#define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \
343 _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \
344 ACCUMULATE bool MUTATORFUNC_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \
345 bool mut##_##cb() { return = false; } \
346 ACCUMULATE bool mut##_##cb()
347
348void _mutPrintFail(string cb, string func)
349{
350 // this is inside a function to avoid expanding it on compilation everytime
351 LOG_INFO("HOOK FAILED: ", cb, ":", func);
352}
353
354#define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN \
355 MUTATOR_ONADD { \
356 if (!CallbackChain_Add(HOOK_##cb, CB_##func, order)) { \
357 _mutPrintFail(#cb, #func); \
358 return true; \
359 } \
360 } \
361 MUTATOR_ONROLLBACK_OR_REMOVE { \
362 CallbackChain_Remove(HOOK_##cb, CB_##func); \
363 } \
364MACRO_END
365
366#include "events.qh"
@ MUTATOR_ADDING
Definition base.qh:152
@ MUTATOR_REMOVING
Definition base.qh:151
@ MUTATOR_ROLLING_BACK
Definition base.qh:153
void Mutator_Remove(Mutator mut)
Definition base.qh:260
const int CBC_ORDER_FIRST
Definition base.qh:10
bool Mutator_SendEntity(entity this, entity to, int sf)
server mutators activate corresponding client mutators for all clients
Definition base.qh:200
bool m_added
Definition base.qh:190
const int CBC_ORDER_LAST
Definition base.qh:11
#define _MUTATOR_IS_ENABLED(this)
Definition base.qh:192
const int CBC_ORDER_ANY
Definition base.qh:13
const int CBC_ORDER_EXCLUSIVE
Definition base.qh:12
bool mutator_log
Definition base.qh:189
void _mutPrintFail(string cb, string func)
Definition base.qh:348
void NET_Mutator_Remove(entity this)
Definition base.qh:210
bool(int) mutatorfunc_t
Definition base.qh:156
void RegisterHooks()
Definition base.qh:126
bool CallbackChain_ReturnValue
Definition base.qh:15
bool Mutator_Add(Mutator mut)
Definition base.qh:237
void RegisterCallbacks()
Definition base.qh:127
#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:44
ATTRIB(CallbackNode, cbc, Callback)
Callback chains contain zero or more callbacks.
Definition base.qh:39
int cbc_order
Definition base.qh:52
bool CallbackChain_Call(CallbackChain this)
Definition base.qh:107
bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
Definition base.qh:58
int CallbackChain_Remove(CallbackChain this, Callback cb)
Definition base.qh:90
ATTRIB(CallbackChain, cbc_next, CallbackNode)
CallbackChain(string _name)
Definition base.qh:53
Callbacks may be added to zero or more callback chains.
Definition base.qh:20
Callback(bool() func)
Definition base.qh:30
ATTRIB(Callback, cbc_func, bool())
a callback function is like this: bool mycallback() { do something return false; }
int m_id
Definition base.qh:163
virtual void describe()
Definition base.qh:171
virtual void display()
Definition base.qh:176
ATTRIB(Mutator, message, string)
vector m_color
Definition base.qh:165
string netname
Definition powerups.qc:20
string message
Definition powerups.qc:19
string m_name
Definition scores.qh:142
#define false
Definition csprogsdefs.qh:6
prev
Definition all.qh:71
next
Definition all.qh:93
#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:115
void Net_UnlinkEntity(entity e)
Definition net.qh:151
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
#define LOG_WARNF(...)
Definition log.qh:62
#define LOG_TRACEF(...)
Definition log.qh:77
#define LOG_INFO(...)
Definition log.qh:65
#define backtrace(msg)
Definition log.qh:99
string name
Definition menu.qh:30
void WriteString(string data, float dest, float desto)
#define WITH(type, name, value, block)
Definition misc.qh:37
#define NEW(cname,...)
Definition oo.qh:117
#define SUPER(cname)
Definition oo.qh:231
#define make_pure(e)
direct use is
Definition oo.qh:13
#define CLASS(...)
Definition oo.qh:145
#define ENDCLASS(cname)
Definition oo.qh:281
#define METHOD(cname, name, prototype)
Definition oo.qh:269
#define CONSTRUCTOR(cname,...)
Definition oo.qh:213
#define CONSTRUCT(cname,...)
Definition oo.qh:123
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
string registered_id
registered item identifier
Definition registry.qh:60
#define REGISTRY(id, max)
Declare a new registry.
Definition registry.qh:26
#define REGISTRY_DEFINE_GET(id, null)
Definition registry.qh:40
vector
Definition self.qh:92
#define STATIC_INIT_LATE(func)
directly after STATIC_INIT
Definition static.qh:37
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:32