]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/oo.qh
Weapons: network balance changes
[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_REFERENCE(cname, name) \
162         cname##_##name
163
164 #define STATIC_METHOD(cname, name, prototype) \
165         prototype METHOD_REFERENCE(cname, name)
166
167 #define METHOD(cname, name, prototype) \
168         STATIC_METHOD(cname, name, prototype); \
169         class(cname) .prototype name; \
170         INIT_STATIC(cname) \
171         { \
172                 this.name = METHOD_REFERENCE(cname, name); \
173         } \
174         STATIC_METHOD(cname, name, prototype)
175
176 #define ATTRIB(cname, name, type, val)      \
177         class(cname).type name;                \
178         INIT(cname) \
179         { \
180                 noref bool strzone; /* Error on strzone() calls. */ \
181                 this.name = val; \
182         }
183
184 #define ATTRIB_STRZONE(cname, name, type, val)      \
185         class(cname).type name;                \
186         INIT(cname) \
187         { \
188                 if (this.name) \
189                         strunzone(this.name); \
190                 this.name = strzone(val); \
191         }
192
193 #define ATTRIBARRAY(cname, name, type, cnt) \
194         class(cname).type name[cnt];
195
196 #define ENDCLASS(cname) \
197         INIT(cname) \
198         { \
199                 return this; \
200         }
201
202 #define SUPER(cname) (cname##_vtbl.vtblbase)
203
204 #define spawn_static(this)
205 #define spawn_1(this)
206 #define _vtbl NULL
207 CLASS(Object, );
208         METHOD(Object, describe, string(entity this))
209         {
210                 string s = _("No description");
211                 if (cvar("developer"))
212                 {
213                         for (int i = 0, n = numentityfields(); i < n; ++i)
214                         {
215                                 string value = getentityfieldstring(i, this);
216                                 if (value != "") s = sprintf("%s\n%s = %s", s, entityfieldname(i), value);
217                         }
218                 }
219                 return s;
220         }
221         METHOD(Object, display, void(entity this, void(string name, string icon) returns))
222         {
223                 returns(sprintf("entity %i", this), "nopreview_map");
224         }
225 ENDCLASS(Object)
226 #undef spawn_static
227 #undef spawn_1
228 #undef _vtbl
229
230 #endif