]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/spawnfunc.qh
Merge branch 'drjaska/2809-splashdmgforcecalcfix' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / spawnfunc.qh
1 #pragma once
2
3 // remove this ifdef when client or menu will actually make use of this stuff
4 #ifdef SVQC
5
6 /** If this global exists, only functions with spawnfunc_ name prefix qualify as spawn functions */
7 noref bool require_spawnfunc_prefix;
8 .bool spawnfunc_checked;
9 /** Not for production use, provides access to a dump of the entity's fields when it is parsed from map data */
10 noref string __fullspawndata;
11 .string fullspawndata;
12
13 // Optional type checking; increases compile time too much to be enabled by default
14 #if 0
15         bool entityfieldassignablefromeditor(int i)
16         {
17                 switch (entityfieldtype(i))
18                 {
19                         case FIELD_STRING:
20                         case FIELD_FLOAT:
21                         case FIELD_VECTOR:
22                                 return true;
23                 }
24                 return false;
25         }
26
27         #define _spawnfunc_checktypes(fld) \
28                 if (s == #fld) \
29                         if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", s);
30 #else
31         #define _spawnfunc_checktypes(fld)
32 #endif
33         #define _spawnfunc_check(fld) \
34                 if (s == #fld) continue;
35
36         noref int __spawnfunc_expecting;
37         noref entity __spawnfunc_expect;
38         noref bool __spawnfunc_unreachable_workaround = true;
39
40     .void(entity) __spawnfunc_constructor;
41     noref IntrusiveList g_spawn_queue;
42
43     #define SPAWNFUNC_INTERNAL_FIELDS(X) \
44         X(string, classname, "spawnfunc") \
45         X(string, target, string_null) \
46         X(string, target2, string_null) \
47         X(string, target3, string_null) \
48         X(string, target4, string_null) \
49         X(string, targetname, string_null) \
50         /**/
51
52     #define X(T, fld, def) .T fld, __spawnfunc_##fld;
53     SPAWNFUNC_INTERNAL_FIELDS(X)
54     #undef X
55
56     void __spawnfunc_defer(entity prototype, void(entity) constructor)
57     {
58         IL_PUSH(g_spawn_queue, prototype);
59         #define X(T, fld, def) { prototype.__spawnfunc_##fld = prototype.fld; prototype.fld = def; }
60         SPAWNFUNC_INTERNAL_FIELDS(X);
61         #undef X
62         prototype.__spawnfunc_constructor = constructor;
63     }
64
65     noref IntrusiveList g_map_entities;
66     #define __spawnfunc_spawn_all() MACRO_BEGIN \
67         g_map_entities = IL_NEW(); \
68         IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
69     MACRO_END
70 #ifdef SVQC
71     void _SV_OnEntityPreSpawnFunction(entity this);
72 #endif
73     void __spawnfunc_spawn(entity prototype)
74     {
75         entity e = new(clone);
76         copyentity_qc(prototype, e);
77         IL_PUSH(g_map_entities, e);
78         #define X(T, fld, def) { e.fld = e.__spawnfunc_##fld; e.__spawnfunc_##fld = def; }
79         SPAWNFUNC_INTERNAL_FIELDS(X);
80         #undef X
81 #ifdef SVQC
82         _SV_OnEntityPreSpawnFunction(e);
83         if (wasfreed(e)) {
84             return;
85         }
86 #endif
87         e.__spawnfunc_constructor(e);
88     }
89
90 // enables/disables warnings if a map entity has a field that doesn't exist
91 // useful for debugging and compatibility testing
92 #define SPAWNFUNC_CHECK_FIELDS 1
93 #if SPAWNFUNC_CHECK_FIELDS
94
95         #define FIELD_SCALAR(fld, n) \
96                 fld(n)
97         #define FIELD_VEC(fld, n) \
98                 fld(n) \
99                 fld(n##_x) \
100                 fld(n##_y) \
101                 fld(n##_z)
102
103         #define FIELDS_NONE(fld)
104         #define FIELDS_ALL(fld) if (false)
105
106         #define FIELDS_COMMON(fld) \
107                 FIELD_SCALAR(fld, classname) \
108                 FIELD_SCALAR(fld, sourceLoc) \
109                 FIELD_SCALAR(fld, spawnfunc_checked) \
110                 FIELD_VEC(fld, origin) \
111                 /**/
112
113         #define FIELDS_UNION(fld) \
114                 FIELD_SCALAR(fld, Version) \
115                 FIELD_SCALAR(fld, ammo_cells) \
116                 FIELD_SCALAR(fld, ammo_nails) \
117                 FIELD_SCALAR(fld, ammo_rockets) \
118                 FIELD_SCALAR(fld, antiwall_flag) \
119                 FIELD_SCALAR(fld, armorvalue) \
120                 FIELD_SCALAR(fld, atten) \
121                 FIELD_SCALAR(fld, bgmscriptdecay) \
122                 FIELD_SCALAR(fld, bgmscriptsustain) \
123                 FIELD_SCALAR(fld, bgmscript) \
124                 FIELD_SCALAR(fld, button0) \
125                 FIELD_SCALAR(fld, chmap) \
126                 FIELD_SCALAR(fld, cnt) \
127                 FIELD_SCALAR(fld, colormap) \
128                 FIELD_SCALAR(fld, count) \
129                 FIELD_SCALAR(fld, curvetarget) \
130                 FIELD_SCALAR(fld, cvarfilter) \
131                 FIELD_SCALAR(fld, debrisdamageforcescale) \
132                 FIELD_SCALAR(fld, debrisfadetime) \
133                 FIELD_SCALAR(fld, debrismovetype) \
134                 FIELD_SCALAR(fld, debrisskin) \
135                 FIELD_SCALAR(fld, debristimejitter) \
136                 FIELD_SCALAR(fld, debristime) \
137                 FIELD_SCALAR(fld, debris) \
138                 FIELD_SCALAR(fld, delay) \
139                 FIELD_SCALAR(fld, dmgtime) \
140                 FIELD_SCALAR(fld, dmg) \
141                 FIELD_SCALAR(fld, dmg_edge) \
142                 FIELD_SCALAR(fld, dmg_force) \
143                 FIELD_SCALAR(fld, dmg_radius) \
144                 FIELD_SCALAR(fld, effects) \
145                 FIELD_SCALAR(fld, falloff) \
146                 FIELD_SCALAR(fld, flags) \
147                 FIELD_SCALAR(fld, fog) \
148                 FIELD_SCALAR(fld, frags) \
149                 FIELD_SCALAR(fld, frame) \
150                 FIELD_SCALAR(fld, gametype) \
151                 FIELD_SCALAR(fld, gametypefilter) \
152                 FIELD_SCALAR(fld, geomtype) \
153                 FIELD_SCALAR(fld, gravity) \
154                 FIELD_SCALAR(fld, health) \
155                 FIELD_SCALAR(fld, height) \
156                 FIELD_SCALAR(fld, impulse) \
157                 FIELD_SCALAR(fld, invincible_finished) \
158                 FIELD_SCALAR(fld, invisibility_finished) \
159                 FIELD_SCALAR(fld, item_pickupsound) \
160                 FIELD_SCALAR(fld, killtarget) \
161                 FIELD_SCALAR(fld, lerpfrac) \
162                 FIELD_SCALAR(fld, light_lev) \
163                 FIELD_SCALAR(fld, lip) \
164                 FIELD_SCALAR(fld, loddistance1) \
165                 FIELD_SCALAR(fld, lodmodel1) \
166                 FIELD_SCALAR(fld, lodmodel2) \
167                 FIELD_SCALAR(fld, ltime) \
168                 FIELD_SCALAR(fld, map) \
169                 FIELD_SCALAR(fld, max_health) \
170                 FIELD_SCALAR(fld, mdl) \
171                 FIELD_SCALAR(fld, message2) \
172                 FIELD_SCALAR(fld, message) \
173                 FIELD_SCALAR(fld, modelindex) \
174                 FIELD_SCALAR(fld, modelscale) \
175                 FIELD_SCALAR(fld, model) \
176                 FIELD_SCALAR(fld, monsterid) \
177                 FIELD_SCALAR(fld, monster_moveflags) \
178                 FIELD_SCALAR(fld, monster_name) \
179                 FIELD_SCALAR(fld, movetype) \
180                 FIELD_SCALAR(fld, move_movetype) \
181                 FIELD_SCALAR(fld, music) \
182                 FIELD_SCALAR(fld, netname) \
183                 FIELD_SCALAR(fld, nextthink) \
184                 FIELD_SCALAR(fld, noalign) \
185                 FIELD_SCALAR(fld, noise1) \
186                 FIELD_SCALAR(fld, noise2) \
187                 FIELD_SCALAR(fld, noise3) \
188                 FIELD_SCALAR(fld, noise) \
189                 FIELD_SCALAR(fld, notcpm) \
190                 FIELD_SCALAR(fld, notfree) \
191                 FIELD_SCALAR(fld, notsingle) \
192                 FIELD_SCALAR(fld, notta) \
193                 FIELD_SCALAR(fld, notteam) \
194                 FIELD_SCALAR(fld, notvq3) \
195                 FIELD_SCALAR(fld, phase) \
196                 FIELD_SCALAR(fld, platmovetype) \
197                 FIELD_SCALAR(fld, race_place) \
198                 FIELD_SCALAR(fld, speed_finished) \
199                 FIELD_SCALAR(fld, strength_finished) \
200                 FIELD_SCALAR(fld, radius) \
201                 FIELD_SCALAR(fld, respawntimestart) \
202                 FIELD_SCALAR(fld, respawntimejitter) \
203                 FIELD_SCALAR(fld, respawntime) \
204                 FIELD_SCALAR(fld, restriction) \
205                 FIELD_SCALAR(fld, scale) \
206                 FIELD_SCALAR(fld, skin) \
207                 FIELD_SCALAR(fld, solid) \
208                 FIELD_SCALAR(fld, sound1) \
209                 FIELD_SCALAR(fld, sounds) \
210                 FIELD_SCALAR(fld, spawnflags) \
211                 FIELD_SCALAR(fld, spawnmob) \
212                 FIELD_SCALAR(fld, speed) \
213                 FIELD_SCALAR(fld, strength) \
214                 FIELD_SCALAR(fld, style) \
215                 FIELD_SCALAR(fld, target2) \
216                 FIELD_SCALAR(fld, target3) \
217                 FIELD_SCALAR(fld, target4) \
218                 FIELD_SCALAR(fld, targetname) \
219                 FIELD_SCALAR(fld, target) \
220                 FIELD_SCALAR(fld, target_random) \
221                 FIELD_SCALAR(fld, target_range) \
222                 FIELD_SCALAR(fld, team) \
223                 FIELD_SCALAR(fld, trigger_reverse) \
224                 FIELD_SCALAR(fld, turret_scale_aim) \
225                 FIELD_SCALAR(fld, turret_scale_ammo) \
226                 FIELD_SCALAR(fld, turret_scale_damage) \
227                 FIELD_SCALAR(fld, turret_scale_health) \
228                 FIELD_SCALAR(fld, turret_scale_range) \
229                 FIELD_SCALAR(fld, turret_scale_refire) \
230                 FIELD_SCALAR(fld, turret_scale_respawn) \
231                 FIELD_SCALAR(fld, volume) \
232                 FIELD_SCALAR(fld, wait) \
233                 FIELD_SCALAR(fld, warpzone_fadeend) \
234                 FIELD_SCALAR(fld, warpzone_fadestart) \
235                 FIELD_SCALAR(fld, weapon) \
236                 FIELD_SCALAR(fld, worldtype) \
237                 FIELD_VEC(fld, absmax) \
238                 FIELD_VEC(fld, absmin) \
239                 FIELD_VEC(fld, angles) \
240                 FIELD_VEC(fld, avelocity) \
241                 FIELD_VEC(fld, beam_color)\
242                 FIELD_VEC(fld, debrisavelocityjitter) \
243                 FIELD_VEC(fld, debrisvelocity) \
244                 FIELD_VEC(fld, debrisvelocityjitter) \
245                 FIELD_VEC(fld, color) \
246                 FIELD_VEC(fld, mangle) \
247                 FIELD_VEC(fld, maxs) \
248                 FIELD_VEC(fld, mins) \
249                 FIELD_VEC(fld, modelscale_vec) \
250                 FIELD_VEC(fld, velocity) \
251                 /**/
252 #endif
253
254 ERASEABLE
255 void _checkWhitelisted(entity this, string id)
256 {
257 #if SPAWNFUNC_CHECK_FIELDS
258         for (int i = 0, n = numentityfields(); i < n; ++i)
259         {
260                 string value = getentityfieldstring(i, this);
261                 string s = entityfieldname(i);
262                 FIELDS_UNION(_spawnfunc_checktypes)
263                 if (value == "") continue;
264                 if (s == "") continue;
265                 FIELDS_COMMON(_spawnfunc_check)
266                 FIELDS_UNION(_spawnfunc_check)
267                 LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), id, s, value);
268         }
269 #endif
270 }
271
272 // this function simply avoids expanding IL_NEW during compilation
273 // for each spawning entity
274 void g_spawn_queue_spawn() { g_spawn_queue = IL_NEW(); }
275
276 noref bool __spawnfunc_first;
277
278 #define spawnfunc(id) \
279         void __spawnfunc_##id(entity this); \
280         ACCUMULATE void spawnfunc_##id(entity this) \
281         { \
282                 if (!__spawnfunc_first) { \
283                         __spawnfunc_first = true; \
284                         static_init_early(); \
285                 } \
286                 bool dospawn = true; \
287                 if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = 0; } \
288                 else if (__spawnfunc_expecting) { \
289                         /* engine call */ \
290                         if (!g_spawn_queue) g_spawn_queue_spawn(); \
291                         __spawnfunc_expecting = 0; \
292                         this = __spawnfunc_expect; \
293                         __spawnfunc_expect = NULL; \
294                         dospawn = false; \
295                 } else { \
296                         /* userland call */ \
297                         assert(this); \
298                 } \
299                 if (!this.sourceLoc) { \
300                         this.sourceLoc = __FILE__":"STR(__LINE__); \
301                 } \
302                 this.classname = #id; \
303                 if (!this.spawnfunc_checked) { \
304                         _checkWhitelisted(this, #id); \
305                         if (__fullspawndata) { \
306                                 /* not supported in old DP */ \
307                                 /* must be read inside the real spawnfunc */ \
308                                 this.fullspawndata = __fullspawndata; \
309                         } \
310                         this.spawnfunc_checked = true; \
311                         if (this) { \
312                                 /* not worldspawn, delay spawn */ \
313                                 /* clear some dangerous fields (TODO: properly support these in the map!) */ \
314                                 this.think = func_null; \
315                                 this.nextthink = 0; \
316                                 __spawnfunc_defer(this, __spawnfunc_##id); \
317                         } else { \
318                                 /* world might not be "worldspawn" */ \
319                                 this.__spawnfunc_constructor = __spawnfunc_##id; \
320                         } \
321                 } \
322                 if (dospawn) { __spawnfunc_##id(this); } \
323                 if (__spawnfunc_unreachable_workaround) return; \
324         } \
325         void __spawnfunc_##id(entity this)
326
327 #endif