3 // remove this ifdef when client or menu will actually make use of this stuff
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;
12 .string fullspawndata;
14 // Optional type checking; increases compile time too much to be enabled by default
16 bool entityfieldassignablefromeditor(int i)
18 switch (entityfieldtype(i))
28 #define _spawnfunc_checktypes(fld) \
30 if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", s);
32 #define _spawnfunc_checktypes(fld)
34 #define _spawnfunc_check(fld) \
35 if (s == #fld) continue;
37 noref int __spawnfunc_expecting;
38 noref entity __spawnfunc_expect;
39 noref bool __spawnfunc_unreachable_workaround = true;
41 .void(entity) __spawnfunc_constructor;
42 noref IntrusiveList g_spawn_queue;
44 #define SPAWNFUNC_INTERNAL_FIELDS(X) \
45 X(string, classname, "spawnfunc") \
46 X(string, target, string_null) \
47 X(string, target2, string_null) \
48 X(string, target3, string_null) \
49 X(string, target4, string_null) \
50 X(string, targetname, string_null) \
53 #define X(T, fld, def) .T fld, __spawnfunc_##fld;
54 SPAWNFUNC_INTERNAL_FIELDS(X)
57 void __spawnfunc_defer(entity prototype, void(entity) constructor)
59 IL_PUSH(g_spawn_queue, prototype);
60 #define X(T, fld, def) { prototype.__spawnfunc_##fld = prototype.fld; prototype.fld = def; }
61 SPAWNFUNC_INTERNAL_FIELDS(X);
63 prototype.__spawnfunc_constructor = constructor;
66 noref IntrusiveList g_map_entities;
67 #define __spawnfunc_spawn_all() MACRO_BEGIN \
68 g_map_entities = IL_NEW(); \
69 IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
72 void _SV_OnEntityPreSpawnFunction(entity this);
74 void __spawnfunc_spawn(entity prototype)
76 entity e = new(clone);
77 copyentity(prototype, e);
78 IL_PUSH(g_map_entities, e);
79 #define X(T, fld, def) { e.fld = e.__spawnfunc_##fld; e.__spawnfunc_##fld = def; }
80 SPAWNFUNC_INTERNAL_FIELDS(X);
83 _SV_OnEntityPreSpawnFunction(e);
88 e.__spawnfunc_constructor(e);
91 #define FIELD_SCALAR(fld, n) \
93 #define FIELD_VEC(fld, n) \
99 #define FIELDS_NONE(fld)
100 #define FIELDS_ALL(fld) if (false)
102 #define FIELDS_COMMON(fld) \
103 FIELD_SCALAR(fld, classname) \
104 FIELD_SCALAR(fld, sourceLoc) \
105 FIELD_SCALAR(fld, spawnfunc_checked) \
106 FIELD_VEC(fld, origin) \
109 #define FIELDS_UNION(fld) \
110 FIELD_SCALAR(fld, Version) \
111 FIELD_SCALAR(fld, ammo_cells) \
112 FIELD_SCALAR(fld, ammo_nails) \
113 FIELD_SCALAR(fld, ammo_rockets) \
114 FIELD_SCALAR(fld, antiwall_flag) \
115 FIELD_SCALAR(fld, armorvalue) \
116 FIELD_SCALAR(fld, atten) \
117 FIELD_SCALAR(fld, bgmscriptdecay) \
118 FIELD_SCALAR(fld, bgmscriptsustain) \
119 FIELD_SCALAR(fld, bgmscript) \
120 FIELD_SCALAR(fld, button0) \
121 FIELD_SCALAR(fld, chmap) \
122 FIELD_SCALAR(fld, cnt) \
123 FIELD_SCALAR(fld, colormap) \
124 FIELD_SCALAR(fld, count) \
125 FIELD_SCALAR(fld, curvetarget) \
126 FIELD_SCALAR(fld, cvarfilter) \
127 FIELD_SCALAR(fld, debrisdamageforcescale) \
128 FIELD_SCALAR(fld, debrisfadetime) \
129 FIELD_SCALAR(fld, debrismovetype) \
130 FIELD_SCALAR(fld, debrisskin) \
131 FIELD_SCALAR(fld, debristimejitter) \
132 FIELD_SCALAR(fld, debristime) \
133 FIELD_SCALAR(fld, debris) \
134 FIELD_SCALAR(fld, delay) \
135 FIELD_SCALAR(fld, dmgtime) \
136 FIELD_SCALAR(fld, dmg) \
137 FIELD_SCALAR(fld, dmg_edge) \
138 FIELD_SCALAR(fld, dmg_force) \
139 FIELD_SCALAR(fld, dmg_radius) \
140 FIELD_SCALAR(fld, effects) \
141 FIELD_SCALAR(fld, falloff) \
142 FIELD_SCALAR(fld, flags) \
143 FIELD_SCALAR(fld, fog) \
144 FIELD_SCALAR(fld, frags) \
145 FIELD_SCALAR(fld, frame) \
146 FIELD_SCALAR(fld, gametype) \
147 FIELD_SCALAR(fld, gametypefilter) \
148 FIELD_SCALAR(fld, geomtype) \
149 FIELD_SCALAR(fld, gravity) \
150 FIELD_SCALAR(fld, health) \
151 FIELD_SCALAR(fld, height) \
152 FIELD_SCALAR(fld, impulse) \
153 FIELD_SCALAR(fld, invincible_finished) \
154 FIELD_SCALAR(fld, item_pickupsound) \
155 FIELD_SCALAR(fld, killtarget) \
156 FIELD_SCALAR(fld, lerpfrac) \
157 FIELD_SCALAR(fld, light_lev) \
158 FIELD_SCALAR(fld, lip) \
159 FIELD_SCALAR(fld, loddistance1) \
160 FIELD_SCALAR(fld, lodmodel1) \
161 FIELD_SCALAR(fld, lodmodel2) \
162 FIELD_SCALAR(fld, ltime) \
163 FIELD_SCALAR(fld, map) \
164 FIELD_SCALAR(fld, max_health) \
165 FIELD_SCALAR(fld, mdl) \
166 FIELD_SCALAR(fld, message2) \
167 FIELD_SCALAR(fld, message) \
168 FIELD_SCALAR(fld, modelindex) \
169 FIELD_SCALAR(fld, modelscale) \
170 FIELD_SCALAR(fld, model) \
171 FIELD_SCALAR(fld, monsterid) \
172 FIELD_SCALAR(fld, monster_moveflags) \
173 FIELD_SCALAR(fld, monster_name) \
174 FIELD_SCALAR(fld, movetype) \
175 FIELD_SCALAR(fld, move_movetype) \
176 FIELD_SCALAR(fld, netname) \
177 FIELD_SCALAR(fld, nextthink) \
178 FIELD_SCALAR(fld, noalign) \
179 FIELD_SCALAR(fld, noise1) \
180 FIELD_SCALAR(fld, noise2) \
181 FIELD_SCALAR(fld, noise3) \
182 FIELD_SCALAR(fld, noise) \
183 FIELD_SCALAR(fld, notcpm) \
184 FIELD_SCALAR(fld, notfree) \
185 FIELD_SCALAR(fld, notta) \
186 FIELD_SCALAR(fld, notteam) \
187 FIELD_SCALAR(fld, notvq3) \
188 FIELD_SCALAR(fld, phase) \
189 FIELD_SCALAR(fld, platmovetype) \
190 FIELD_SCALAR(fld, race_place) \
191 FIELD_SCALAR(fld, strength_finished) \
192 FIELD_SCALAR(fld, radius) \
193 FIELD_SCALAR(fld, respawntimestart) \
194 FIELD_SCALAR(fld, respawntimejitter) \
195 FIELD_SCALAR(fld, respawntime) \
196 FIELD_SCALAR(fld, restriction) \
197 FIELD_SCALAR(fld, scale) \
198 FIELD_SCALAR(fld, skin) \
199 FIELD_SCALAR(fld, solid) \
200 FIELD_SCALAR(fld, sound1) \
201 FIELD_SCALAR(fld, sounds) \
202 FIELD_SCALAR(fld, spawnflags) \
203 FIELD_SCALAR(fld, spawnmob) \
204 FIELD_SCALAR(fld, speed) \
205 FIELD_SCALAR(fld, strength) \
206 FIELD_SCALAR(fld, style) \
207 FIELD_SCALAR(fld, target2) \
208 FIELD_SCALAR(fld, target3) \
209 FIELD_SCALAR(fld, target4) \
210 FIELD_SCALAR(fld, targetname) \
211 FIELD_SCALAR(fld, target) \
212 FIELD_SCALAR(fld, target_random) \
213 FIELD_SCALAR(fld, target_range) \
214 FIELD_SCALAR(fld, team) \
215 FIELD_SCALAR(fld, trigger_reverse) \
216 FIELD_SCALAR(fld, turret_scale_aim) \
217 FIELD_SCALAR(fld, turret_scale_ammo) \
218 FIELD_SCALAR(fld, turret_scale_damage) \
219 FIELD_SCALAR(fld, turret_scale_health) \
220 FIELD_SCALAR(fld, turret_scale_range) \
221 FIELD_SCALAR(fld, turret_scale_refire) \
222 FIELD_SCALAR(fld, turret_scale_respawn) \
223 FIELD_SCALAR(fld, volume) \
224 FIELD_SCALAR(fld, wait) \
225 FIELD_SCALAR(fld, warpzone_fadeend) \
226 FIELD_SCALAR(fld, warpzone_fadestart) \
227 FIELD_SCALAR(fld, weapon) \
228 FIELD_SCALAR(fld, worldtype) \
229 FIELD_VEC(fld, absmax) \
230 FIELD_VEC(fld, absmin) \
231 FIELD_VEC(fld, angles) \
232 FIELD_VEC(fld, avelocity) \
233 FIELD_VEC(fld, beam_color)\
234 FIELD_VEC(fld, debrisavelocityjitter) \
235 FIELD_VEC(fld, debrisvelocity) \
236 FIELD_VEC(fld, debrisvelocityjitter) \
237 FIELD_VEC(fld, color) \
238 FIELD_VEC(fld, mangle) \
239 FIELD_VEC(fld, maxs) \
240 FIELD_VEC(fld, mins) \
241 FIELD_VEC(fld, modelscale_vec) \
242 FIELD_VEC(fld, velocity) \
246 void _checkWhitelisted(entity this, string id)
248 for (int i = 0, n = numentityfields(); i < n; ++i)
250 string value = getentityfieldstring(i, this);
251 string s = entityfieldname(i);
252 FIELDS_UNION(_spawnfunc_checktypes)
253 if (value == "") continue;
254 if (s == "") continue;
255 FIELDS_COMMON(_spawnfunc_check)
256 FIELDS_UNION(_spawnfunc_check)
257 LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), id, s, value);
261 noref bool __spawnfunc_first;
263 #define spawnfunc(id) \
264 void __spawnfunc_##id(entity this); \
265 ACCUMULATE void spawnfunc_##id(entity this) \
267 if (!__spawnfunc_first) { \
268 __spawnfunc_first = true; \
269 static_init_early(); \
271 bool dospawn = true; \
272 if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = 0; } \
273 else if (__spawnfunc_expecting) { \
275 if (!g_spawn_queue) { g_spawn_queue = IL_NEW(); } \
276 __spawnfunc_expecting = 0; \
277 this = __spawnfunc_expect; \
278 __spawnfunc_expect = NULL; \
281 /* userland call */ \
284 if (!this.sourceLoc) { \
285 this.sourceLoc = __FILE__":"STR(__LINE__); \
287 if (!this.spawnfunc_checked) { \
288 _checkWhitelisted(this, #id); \
289 if (__fullspawndata) { \
290 /* not supported in old DP */ \
291 /* must be read inside the real spawnfunc */ \
292 this.fullspawndata = __fullspawndata; \
294 this.spawnfunc_checked = true; \
296 /* not worldspawn, delay spawn */ \
297 __spawnfunc_defer(this, __spawnfunc_##id); \
299 /* world might not be "worldspawn" */ \
300 this.__spawnfunc_constructor = __spawnfunc_##id; \
303 if (dospawn) { __spawnfunc_##id(this); } \
304 if (__spawnfunc_unreachable_workaround) return; \
306 void __spawnfunc_##id(entity this)