]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/oo.qh
Add a few checks
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / oo.qh
1 #ifndef OO_H
2 #define OO_H
3
4 #include "misc.qh"
5 #include "nil.qh"
6 #include "static.qh"
7
8 #ifdef MENUQC
9         #define NULL (null_entity)
10 #else
11         #define NULL (world)
12 #endif
13
14 .vector origin;
15 .bool pure_data;
16 #define make_pure(e) \
17         do \
18         { \
19                 (e).pure_data = true; \
20         } \
21         while (0)
22 #define make_impure(e) \
23         do \
24         { \
25                 (e).pure_data = false; \
26         } \
27         while (0)
28 #define is_pure(e) ((e).pure_data)
29
30 .string classname;
31 /** Location entity was spawned from in source */
32 .string sourceLocFile;
33 .int sourceLocLine;
34 entity _spawn();
35 entity __spawn(string _classname, string _sourceFile, int _sourceLine, bool pure)
36 {
37         entity this = _spawn();
38         this.classname = _classname;
39         this.sourceLocFile = _sourceFile;
40         this.sourceLocLine = _sourceLine;
41         if (pure) make_pure(this);
42         return this;
43 }
44
45
46 #define entityclass(...) EVAL(OVERLOAD(entityclass, __VA_ARGS__))
47 #define entityclass_1(name) entityclass_2(name, Object)
48 #ifndef QCC_SUPPORT_ENTITYCLASS
49         #define entityclass_2(name, base) typedef entity name
50         #define class(name)
51         #define new(class) __spawn( #class, __FILE__, __LINE__, false)
52 #else
53         #define entityclass_2(name, base) entityclass name : base {}
54         #define class(name) [[class(name)]]
55         #define new(class) ((class) __spawn( #class, __FILE__, __LINE__, false))
56 #endif
57 #define spawn() __spawn("entity", __FILE__, __LINE__, false)
58
59 entity _clearentity_ent;
60 STATIC_INIT(clearentity)
61 {
62         _clearentity_ent = new(clearentity);
63         make_pure(_clearentity_ent);
64 }
65 void clearentity(entity e)
66 {
67 #ifdef CSQC
68                 int n = e.entnum;
69 #endif
70         bool was_pure = is_pure(e);
71         copyentity(_clearentity_ent, e);
72         if (!was_pure) make_impure(e);
73 #ifdef CSQC
74                 e.entnum = n;
75 #endif
76 }
77
78 // Classes have a `spawn##cname(entity)` constructor
79 // The parameter is used across [[accumulate]] functions
80
81 // Macros to hide this implementation detail:
82 #ifdef GMQCC
83         #define NEW(cname, ...) \
84                 OVERLOAD(spawn##cname, new(cname),##__VA_ARGS__)
85
86         #define CONSTRUCT(cname, ...) \
87                 OVERLOAD(spawn##cname, this,##__VA_ARGS__)
88 #else
89         #define NEW_(cname, ...) \
90                 OVERLOAD_(spawn##cname, __VA_ARGS__)
91         #define NEW(cname, ...) \
92                 NEW_(cname, new(cname),##__VA_ARGS__)(new(cname),##__VA_ARGS__)
93
94         #define CONSTRUCT_(cname, ...) \
95                 OVERLOAD_(spawn##cname, __VA_ARGS__)
96         #define CONSTRUCT(cname, ...) \
97                 CONSTRUCT_(cname, this,##__VA_ARGS__)(this,##__VA_ARGS__)
98 #endif
99
100 #define CONSTRUCTOR(cname, ...) \
101         cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) \
102         { \
103                 return = this; \
104         } \
105         [[accumulate]] cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__)
106
107 .string vtblname;
108 .entity vtblbase;
109
110 void RegisterClasses() {}
111 STATIC_INIT(RegisterClasses)
112 {
113         RegisterClasses();
114 }
115
116 #define VTBL(cname, base) \
117         INIT_STATIC(cname); \
118         entity cname##_vtbl; \
119         void cname##_vtbl_init() \
120         { \
121                 cname e = new(vtbl); \
122                 make_pure(e); \
123                 spawn##cname##_static(e); \
124                 e.vtblname = #cname; \
125                 /* Top level objects refer to themselves */ \
126                 e.vtblbase = base##_vtbl ? base##_vtbl : e; \
127                 cname##_vtbl = e; \
128         } \
129         ACCUMULATE_FUNCTION(RegisterClasses, cname##_vtbl_init)
130
131 #define INIT_STATIC(cname) [[accumulate]] void spawn##cname##_static(cname this)
132 #define INIT(cname) [[accumulate]] cname spawn##cname##_1(cname this)
133
134 #define CLASS(cname, base)                  \
135         entityclass(cname, base);               \
136         class(cname).bool instanceOf##cname;   \
137         VTBL(cname, base)                       \
138         INIT_STATIC(cname) \
139         {                    \
140                 if (cname##_vtbl) \
141                 {                 \
142                         copyentity(cname##_vtbl, this); \
143                         return;                         \
144                 }                                   \
145                 spawn##base##_static(this);         \
146                 this.instanceOf##cname = true;      \
147         }                                       \
148         INIT(cname) \
149         {                           \
150                 /* Only statically initialize the current class, it contains everything it inherits */ \
151                 if (cname##_vtbl.vtblname == this.classname) \
152                 { \
153                         spawn##cname##_static(this);    \
154                         this.classname = #cname;        \
155                         this.vtblname = string_null;    \
156                         this.vtblbase = cname##_vtbl;   \
157                 }                                   \
158                 spawn##base##_1(this);              \
159         }
160
161 #define METHOD(cname, name, prototype)      \
162         class(cname).prototype name;           \
163         prototype cname##_##name;               \
164         INIT_STATIC(cname) \
165         { \
166                 this.name = cname##_##name; \
167         } \
168         prototype cname##_##name
169
170 #define ATTRIB(cname, name, type, val)      \
171         class(cname).type name;                \
172         INIT(cname) \
173         { \
174                 noref bool strzone; /* Error on strzone() calls. */ \
175                 this.name = val; \
176         }
177
178 #define ATTRIB_STRZONE(cname, name, type, val)      \
179         class(cname).type name;                \
180         INIT(cname) \
181         { \
182                 if (this.name) \
183                         strunzone(this.name); \
184                 this.name = strzone(val); \
185         }
186
187 #define ATTRIBARRAY(cname, name, type, cnt) \
188         class(cname).type name[cnt];
189
190 #define ENDCLASS(cname) \
191         INIT(cname) \
192         { \
193                 return this; \
194         }
195
196 #define SUPER(cname) (cname##_vtbl.vtblbase)
197
198 #define spawn_static(this)
199 #define spawn_1(this)
200 #define _vtbl NULL
201 CLASS(Object, );
202         METHOD(Object, describe, string(entity this))
203         {
204                 string s = _("No description");
205                 if (cvar("developer"))
206                 {
207                         for (int i = 0, n = numentityfields(); i < n; ++i)
208                         {
209                                 string value = getentityfieldstring(i, this);
210                                 if (value != "") s = sprintf("%s\n%s = %s", s, entityfieldname(i), value);
211                         }
212                 }
213                 return s;
214         }
215         METHOD(Object, display, void(entity this, void(string name, string icon) returns))
216         {
217                 returns(sprintf("entity %i", this), "nopreview_map");
218         }
219 ENDCLASS(Object)
220 #undef spawn_static
221 #undef spawn_1
222 #undef _vtbl
223
224 #endif