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