]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/oo.qh
Merge branch 'master' into Mario/csqc_muzzleflash
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / oo.qh
1 #pragma once
2
3 #include "misc.qh"
4 #include "nil.qh"
5 #include "static.qh"
6
7 .vector origin;
8
9 .bool pure_data;
10 #define is_pure(e) ((e).pure_data)
11 /** @deprecated use new_pure or NEW(class) */
12 #define make_pure(e) MACRO_BEGIN \
13         (e).pure_data = true; \
14 MACRO_END
15 #define make_impure(e) MACRO_BEGIN \
16         (e).pure_data = false; \
17 MACRO_END
18
19 .string classname;
20 /** Location entity was spawned from in source */
21 .string sourceLoc;
22 entity _spawn();
23
24 #ifndef SPAWN_PURE
25 #define SPAWN_PURE 0
26 #endif
27
28 // pure entities: need no .origin
29 #if SPAWN_PURE
30 entity spawn_pure() = #600;
31 #else
32 #define spawn_pure() _spawn()
33 #endif
34
35 entity __spawn(string _classname, string _sourceLoc, bool pure)
36 {
37         entity this = pure ? spawn_pure() : _spawn();
38         this.classname = _classname;
39         this.sourceLoc = _sourceLoc;
40         if (pure) {
41                 make_pure(this);
42         }
43         return this;
44 }
45
46
47 #define entityclass(...) EVAL_entityclass(OVERLOAD_(entityclass, __VA_ARGS__))
48 #define EVAL_entityclass(...) __VA_ARGS__
49 #define entityclass_1(name) entityclass_2(name, Object)
50 #ifndef QCC_SUPPORT_ENTITYCLASS
51         #define entityclass_2(name, base) USING(name, entity)
52         #define classfield(name)
53         #define _new(class, pure) __spawn(#class, __FILE__ ":" STR(__LINE__), pure)
54 #else
55         #define entityclass_2(name, base) entityclass name : base {}
56         #define classfield(name) [[class(name)]]
57         #define _new(class, pure) ((class) __spawn(#class, __FILE__ ":" STR(__LINE__), pure))
58 #endif
59 /** entities you care about seeing (.origin works) */
60 #define new(class) _new(class, false)
61 /** purely logical entities (.origin doesn't work) */
62 #define new_pure(class) _new(class, true)
63 #define spawn() __spawn("entity", __FILE__ ":" STR(__LINE__), false)
64
65 ACCUMULATE void ONREMOVE(entity this) {}
66
67 #ifndef SVQC
68         #define delete_fn builtin_remove
69 #endif
70
71 .void(entity this) dtor;
72 #define delete(this) MACRO_BEGIN \
73     entity _this = (this); \
74     void(entity) _dtor = _this.dtor; \
75     ONREMOVE(this); \
76     if (_dtor) _dtor(_this); else delete_fn(_this); \
77     /* this = NULL; */  \
78 MACRO_END
79
80 entity _clearentity_ent;
81 STATIC_INIT(clearentity)
82 {
83         _clearentity_ent = new_pure(clearentity);
84 }
85 void clearentity(entity e)
86 {
87 #ifdef CSQC
88                 int n = e.entnum;
89 #endif
90         bool was_pure = is_pure(e);
91         copyentity(_clearentity_ent, e);
92         if (!was_pure) make_impure(e);
93 #ifdef CSQC
94                 e.entnum = n;
95 #endif
96 }
97
98 // Classes have a `spawn##cname(entity)` constructor
99 // The parameter is used across ACCUMULATE functions
100
101 .bool transmute;
102
103 // Macros to hide this implementation detail:
104 #ifdef __STDC__
105         #define NEW(cname, ...) \
106                 OVERLOAD_(spawn##cname, new_pure(cname) P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
107
108     #define _TRANSMUTE(cname, this, ...) \
109         OVERLOAD_(spawn##cname, this P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
110
111         #define CONSTRUCT(cname, ...) \
112                 OVERLOAD_(spawn##cname, this P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
113 #else
114         #define NEW(cname, ...) \
115                 OVERLOAD(spawn##cname, new_pure(cname),##__VA_ARGS__)
116
117     #define _TRANSMUTE(cname, this, ...) \
118         OVERLOAD(spawn##cname, this,##__VA_ARGS__)
119
120         #define CONSTRUCT(cname, ...) \
121                 OVERLOAD(spawn##cname, this,##__VA_ARGS__)
122 #endif
123
124 #define TRANSMUTE(cname, this, ...) MACRO_BEGIN \
125     entity _e = (this); \
126     if (_e.vtblbase != cname##_vtbl) { \
127         _e.transmute = true; \
128         _e.classname = #cname; \
129         _TRANSMUTE(cname, _e, __VA_ARGS__); \
130     } \
131     MACRO_END
132
133 #define CLASS(...) EVAL_CLASS(OVERLOAD__(CLASS, __VA_ARGS__))
134 #define EVAL_CLASS(...) __VA_ARGS__
135
136 #define ATTRIB(...) EVAL_ATTRIB(OVERLOAD_(ATTRIB, __VA_ARGS__))
137 #define EVAL_ATTRIB(...) __VA_ARGS__
138
139 #ifdef QCC_SUPPORT_CLASS
140
141 #warning "QCC_SUPPORT_CLASS not implemented"
142
143 #define CLASS_1(name)                               CLASS_2(name, entity)
144 #define CLASS_2(name, base)                         class name : base {
145
146 #define INIT(class)                                     void class::class()
147 #define CONSTRUCTOR(class, ...)                         void class::class(__VA_ARGS__)
148 #define DESTRUCTOR(class)                               class::~class()
149
150 #define SUPER(class)                                    super
151
152 #define ATTRIB_3(class, name, T)                        T name
153 #define ATTRIB_4(class, name, T, val)                   ATTRIB_3(class, name, T) = val
154 #define STATIC_ATTRIB(class, name, T, val)              static T name = val
155
156 #define ATTRIB_STRZONE(class, name, T, val)             T name = val
157 #define STATIC_ATTRIB_STRZONE(class, name, T, val)      static T name = val
158
159 #define ATTRIBARRAY(class, name, T, val)                T name[val]
160
161 #define METHOD(class, name, prototype)                  virtual void class::name()
162 #define STATIC_METHOD(class, name, prototype)           static void class::name()
163
164 #define ENDCLASS(class)                             };
165
166 #else
167
168 #define CLASS_1(cname) CLASS_2(cname, )
169 #define CLASS_2(cname, base)                                                                       \
170         entityclass(cname, base);                                                                      \
171         classfield(cname).bool instanceOf##cname;                                                      \
172         DEBUG_STUFF(cname)                                                                             \
173         VTBL(cname, base)                                                                              \
174         _INIT_STATIC(cname)                                                                            \
175         {                                                                                              \
176                 if (cname##_vtbl && !this.transmute)                                                       \
177                 {                                                                                          \
178                         copyentity(cname##_vtbl, this);                                                        \
179                         return;                                                                                \
180                 }                                                                                          \
181                 spawn##base##_static(this);                                                                \
182                 this.instanceOf##cname = true;                                                             \
183         }                                                                                              \
184         INIT(cname)                                                                                    \
185         {                                                                                              \
186                 /* Only statically initialize the current class, it contains everything it inherits */     \
187                 if (cname##_vtbl.vtblname == this.classname)                                               \
188                 {                                                                                          \
189                         spawn##cname##_static(this);                                                           \
190                         this.transmute = false;                                                                \
191                         this.classname = #cname;                                                               \
192                         this.vtblname = string_null;                                                           \
193                         this.vtblbase = cname##_vtbl;                                                          \
194                 }                                                                                          \
195                 spawn##base##_1(this);                                                                     \
196         }
197
198 #define INIT(cname)                                                                                \
199         ACCUMULATE cname spawn##cname##_1(cname this)
200
201 #define CONSTRUCTOR(cname, ...)                                                                    \
202         cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__)                                          \
203         {                                                                                              \
204                 return = this;                                                                             \
205         }                                                                                              \
206         ACCUMULATE cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__)
207
208 #define DESTRUCTOR(cname)                                                                          \
209         STATIC_METHOD(cname, dtorimpl, void(cname this));                                              \
210     METHOD(cname, dtor, void(cname this))                                                          \
211     {                                                                                              \
212         METHOD_REFERENCE(cname, dtorimpl)(this);                                                   \
213         this.instanceOf##cname = false;                                                            \
214         entity super = SUPER(cname);                                                               \
215         if (super != cname##_vtbl) super.dtor(this);                                               \
216     }                                                                                              \
217         STATIC_METHOD(cname, dtorimpl, void(cname this))
218
219 #define SUPER(cname) (cname##_vtbl.vtblbase)
220
221 #define ATTRIB_3(cname, name, type) classfield(cname) .type name
222 #define ATTRIB_4(cname, name, type, val)                                                           \
223         ATTRIB_3(cname, name, type);                                                                   \
224         INIT(cname)                                                                                    \
225         {                                                                                              \
226                 noref bool strzone; /* Error on strzone() calls. */                                        \
227                 this.name = val;                                                                           \
228         }                                                                                              \
229         ATTRIB_3(cname, name, type)
230
231 #define STATIC_ATTRIB(cname, name, type, val)                                                      \
232         type cname##_##name;                                                                           \
233         _INIT_STATIC(cname)                                                                            \
234         {                                                                                              \
235                 noref bool strzone; /* Error on strzone() calls. */                                        \
236                 cname##_##name = val;                                                                      \
237         }
238
239 // cleanup potentially zoned strings from base classes
240 #define ATTRIB_STRZONE(cname, name, type, val)                                                     \
241         classfield(cname).type name;                                                                   \
242         INIT(cname)                                                                                    \
243         {                                                                                              \
244                 strcpy(this.name, val);                                                                    \
245         }
246
247 #define STATIC_ATTRIB_STRZONE(cname, name, type, val)                                              \
248         type cname##_##name;                                                                           \
249         _INIT_STATIC(cname)                                                                            \
250         {                                                                                              \
251                 strcpy(cname##_##name, val);                                                               \
252         }
253
254 #define ATTRIBARRAY(cname, name, type, cnt)                                                        \
255         classfield(cname) .type name[cnt]
256
257 #define METHOD(cname, name, prototype)                                                             \
258         STATIC_METHOD(cname, name, prototype);                                                         \
259         classfield(cname) .prototype name;                                                             \
260         _INIT_STATIC(cname)                                                                            \
261         {                                                                                              \
262                 this.name = METHOD_REFERENCE(cname, name);                                                 \
263         }                                                                                              \
264         STATIC_METHOD(cname, name, prototype)
265
266 #define STATIC_METHOD(cname, name, prototype)                                                      \
267         prototype METHOD_REFERENCE(cname, name)
268
269 #define ENDCLASS(cname)                                                                            \
270         INIT(cname)                                                                                    \
271         {                                                                                              \
272                 return this;                                                                               \
273         }
274
275 // impl
276
277 .string vtblname;
278 .entity vtblbase;
279
280 void RegisterClasses() {}
281 STATIC_INIT(RegisterClasses)
282 {
283         RegisterClasses();
284 }
285
286 #define VTBL(cname, base) \
287         _INIT_STATIC(cname); \
288         entity cname##_vtbl; \
289         void cname##_vtbl_init() \
290         { \
291                 cname e = new_pure(vtbl); \
292                 spawn##cname##_static(e); \
293                 e.vtblname = #cname; \
294                 /* Top level objects refer to themselves */ \
295                 e.vtblbase = base##_vtbl ? base##_vtbl : e; \
296                 cname##_vtbl = e; \
297         } \
298         ACCUMULATE_FUNCTION(RegisterClasses, cname##_vtbl_init)
299
300 #define _INIT_STATIC(cname) ACCUMULATE void spawn##cname##_static(cname this)
301
302 #if NDEBUG
303         #define DEBUG_STUFF(cname)
304 #else
305         #define DEBUG_STUFF(cname) \
306                 ERASEABLE bool is_##cname(entity e) { return e.instanceOf##cname; } \
307                 ERASEABLE void isnt_##cname(entity e) { eprint(e); }
308 #endif
309
310 #define METHOD_REFERENCE(cname, name) \
311         cname##_##name
312
313 #endif
314
315 #define spawn_static(this)
316 #define spawn_1(this)
317 #define _vtbl NULL
318 CLASS(Object)
319     DESTRUCTOR(Object) { builtin_remove(this); }
320     #define remove(this) delete(this)
321         METHOD(Object, describe, string(Object this))
322         {
323                 TC(Object, this);
324                 string s = _("No description");
325                 if (cvar("developer") > 0)
326                 {
327                         for (int i = 0, n = numentityfields(); i < n; ++i)
328                         {
329                                 string value = getentityfieldstring(i, this);
330                                 if (value != "") s = sprintf("%s\n%s = %s", s, entityfieldname(i), value);
331                         }
332                 }
333                 return s;
334         }
335         METHOD(Object, display, void(Object this, void(string name, string icon) returns))
336         {
337                 TC(Object, this);
338                 returns(sprintf("entity %i", this), "nopreview_map");
339         }
340 ENDCLASS(Object)
341 #undef spawn_static
342 #undef spawn_1
343 #undef _vtbl