#pragma once
+// remove this ifdef when client or menu will actually make use of this stuff
+#ifdef SVQC
+
/** If this global exists, only functions with spawnfunc_ name prefix qualify as spawn functions */
noref bool require_spawnfunc_prefix;
.bool spawnfunc_checked;
}
#define _spawnfunc_checktypes(fld) \
- if (fieldname == #fld) \
- if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", fieldname);
+ if (s == #fld) \
+ if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", s);
#else
#define _spawnfunc_checktypes(fld)
#endif
#define _spawnfunc_check(fld) \
- if (fieldname == #fld) continue;
+ if (s == #fld) continue;
- noref bool __spawnfunc_expecting;
+ noref int __spawnfunc_expecting;
noref entity __spawnfunc_expect;
- bool __spawnfunc_unreachable_workaround = true;
-
- #define spawnfunc_1(id) spawnfunc_2(id, FIELDS_UNION)
- #define spawnfunc_2(id, whitelist) \
- void __spawnfunc_##id(entity this); \
- [[accumulate]] void spawnfunc_##id(entity this) \
- { \
- if (__spawnfunc_expecting) \
- { \
- /* engine call */ \
- __spawnfunc_expecting = false; \
- this = __spawnfunc_expect; \
- __spawnfunc_expect = NULL; \
- } \
- else \
- { \
- assert(this); \
- } \
- if (!this.sourceLoc) \
- { \
- this.sourceLoc = __FILE__ ":" STR(__LINE__); \
- } \
- if (!this.spawnfunc_checked) \
- { \
- for (int i = 0, n = numentityfields(); i < n; ++i) \
- { \
- string value = getentityfieldstring(i, this); \
- string fieldname = entityfieldname(i); \
- whitelist(_spawnfunc_checktypes) \
- if (value == "") continue; \
- if (fieldname == "") continue; \
- FIELDS_COMMON(_spawnfunc_check) \
- whitelist(_spawnfunc_check) \
- LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), #id, fieldname, value); \
- } \
- this.spawnfunc_checked = true; \
- } \
- __spawnfunc_##id(this); \
- if (__spawnfunc_unreachable_workaround) return; \
- } \
- void __spawnfunc_##id(entity this)
+ noref bool __spawnfunc_unreachable_workaround = true;
+
+ .void(entity) __spawnfunc_constructor;
+ noref IntrusiveList g_spawn_queue;
+
+ #define SPAWNFUNC_INTERNAL_FIELDS(X) \
+ X(string, classname, "spawnfunc") \
+ X(string, target, string_null) \
+ X(string, target2, string_null) \
+ X(string, target3, string_null) \
+ X(string, target4, string_null) \
+ X(string, targetname, string_null) \
+ /**/
+
+ #define X(T, fld, def) .T fld, __spawnfunc_##fld;
+ SPAWNFUNC_INTERNAL_FIELDS(X)
+ #undef X
+
+ void __spawnfunc_defer(entity prototype, void(entity) constructor)
+ {
+ IL_PUSH(g_spawn_queue, prototype);
+ #define X(T, fld, def) { prototype.__spawnfunc_##fld = prototype.fld; prototype.fld = def; }
+ SPAWNFUNC_INTERNAL_FIELDS(X);
+ #undef X
+ prototype.__spawnfunc_constructor = constructor;
+ }
+
+ noref IntrusiveList g_map_entities;
+ #define __spawnfunc_spawn_all() MACRO_BEGIN \
+ g_map_entities = IL_NEW(); \
+ IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
+ MACRO_END
+#ifdef SVQC
+ void _SV_OnEntityPreSpawnFunction(entity this);
+#endif
+ void __spawnfunc_spawn(entity prototype)
+ {
+ entity e = new(clone);
+ copyentity(prototype, e);
+ IL_PUSH(g_map_entities, e);
+ #define X(T, fld, def) { e.fld = e.__spawnfunc_##fld; e.__spawnfunc_##fld = def; }
+ SPAWNFUNC_INTERNAL_FIELDS(X);
+ #undef X
+#ifdef SVQC
+ _SV_OnEntityPreSpawnFunction(e);
+ if (wasfreed(e)) {
+ return;
+ }
+#endif
+ e.__spawnfunc_constructor(e);
+ }
#define FIELD_SCALAR(fld, n) \
fld(n)
FIELD_SCALAR(fld, ammo_cells) \
FIELD_SCALAR(fld, ammo_nails) \
FIELD_SCALAR(fld, ammo_rockets) \
+ FIELD_SCALAR(fld, antiwall_flag) \
FIELD_SCALAR(fld, armorvalue) \
FIELD_SCALAR(fld, atten) \
FIELD_SCALAR(fld, bgmscriptdecay) \
FIELD_SCALAR(fld, bgmscriptsustain) \
FIELD_SCALAR(fld, bgmscript) \
FIELD_SCALAR(fld, button0) \
+ FIELD_SCALAR(fld, chmap) \
FIELD_SCALAR(fld, cnt) \
FIELD_SCALAR(fld, colormap) \
FIELD_SCALAR(fld, count) \
FIELD_SCALAR(fld, cvarfilter) \
FIELD_SCALAR(fld, debrisdamageforcescale) \
FIELD_SCALAR(fld, debrisfadetime) \
+ FIELD_SCALAR(fld, debrismovetype) \
+ FIELD_SCALAR(fld, debrisskin) \
FIELD_SCALAR(fld, debristimejitter) \
FIELD_SCALAR(fld, debristime) \
FIELD_SCALAR(fld, debris) \
FIELD_SCALAR(fld, dmg_force) \
FIELD_SCALAR(fld, dmg_radius) \
FIELD_SCALAR(fld, effects) \
+ FIELD_SCALAR(fld, falloff) \
FIELD_SCALAR(fld, flags) \
FIELD_SCALAR(fld, fog) \
FIELD_SCALAR(fld, frags) \
FIELD_SCALAR(fld, frame) \
+ FIELD_SCALAR(fld, gametype) \
FIELD_SCALAR(fld, gametypefilter) \
FIELD_SCALAR(fld, geomtype) \
FIELD_SCALAR(fld, gravity) \
FIELD_SCALAR(fld, health) \
FIELD_SCALAR(fld, height) \
FIELD_SCALAR(fld, impulse) \
+ FIELD_SCALAR(fld, invincible_finished) \
+ FIELD_SCALAR(fld, item_pickupsound) \
FIELD_SCALAR(fld, killtarget) \
FIELD_SCALAR(fld, lerpfrac) \
FIELD_SCALAR(fld, light_lev) \
FIELD_SCALAR(fld, lip) \
FIELD_SCALAR(fld, loddistance1) \
FIELD_SCALAR(fld, lodmodel1) \
+ FIELD_SCALAR(fld, lodmodel2) \
FIELD_SCALAR(fld, ltime) \
+ FIELD_SCALAR(fld, map) \
+ FIELD_SCALAR(fld, max_health) \
FIELD_SCALAR(fld, mdl) \
FIELD_SCALAR(fld, message2) \
FIELD_SCALAR(fld, message) \
FIELD_SCALAR(fld, noalign) \
FIELD_SCALAR(fld, noise1) \
FIELD_SCALAR(fld, noise2) \
+ FIELD_SCALAR(fld, noise3) \
FIELD_SCALAR(fld, noise) \
FIELD_SCALAR(fld, phase) \
FIELD_SCALAR(fld, platmovetype) \
FIELD_SCALAR(fld, race_place) \
+ FIELD_SCALAR(fld, strength_finished) \
FIELD_SCALAR(fld, radius) \
FIELD_SCALAR(fld, respawntimestart) \
FIELD_SCALAR(fld, respawntimejitter) \
FIELD_SCALAR(fld, sound1) \
FIELD_SCALAR(fld, sounds) \
FIELD_SCALAR(fld, spawnflags) \
+ FIELD_SCALAR(fld, spawnmob) \
FIELD_SCALAR(fld, speed) \
FIELD_SCALAR(fld, strength) \
+ FIELD_SCALAR(fld, style) \
FIELD_SCALAR(fld, target2) \
FIELD_SCALAR(fld, target3) \
FIELD_SCALAR(fld, target4) \
FIELD_SCALAR(fld, target_random) \
FIELD_SCALAR(fld, target_range) \
FIELD_SCALAR(fld, team) \
+ FIELD_SCALAR(fld, trigger_reverse) \
+ FIELD_SCALAR(fld, turret_scale_aim) \
+ FIELD_SCALAR(fld, turret_scale_ammo) \
+ FIELD_SCALAR(fld, turret_scale_damage) \
FIELD_SCALAR(fld, turret_scale_health) \
FIELD_SCALAR(fld, turret_scale_range) \
+ FIELD_SCALAR(fld, turret_scale_refire) \
FIELD_SCALAR(fld, turret_scale_respawn) \
FIELD_SCALAR(fld, volume) \
FIELD_SCALAR(fld, wait) \
FIELD_SCALAR(fld, warpzone_fadeend) \
FIELD_SCALAR(fld, warpzone_fadestart) \
FIELD_SCALAR(fld, weapon) \
+ FIELD_SCALAR(fld, worldtype) \
FIELD_VEC(fld, absmax) \
FIELD_VEC(fld, absmin) \
FIELD_VEC(fld, angles) \
FIELD_VEC(fld, avelocity) \
- FIELD_VEC(fld, maxs) \
+ FIELD_VEC(fld, beam_color)\
+ FIELD_VEC(fld, debrisavelocityjitter) \
+ FIELD_VEC(fld, debrisvelocity) \
+ FIELD_VEC(fld, debrisvelocityjitter) \
+ FIELD_VEC(fld, color) \
+ FIELD_VEC(fld, mangle) \
FIELD_VEC(fld, maxs) \
FIELD_VEC(fld, mins) \
FIELD_VEC(fld, modelscale_vec) \
FIELD_VEC(fld, velocity) \
/**/
- #define spawnfunc(...) EVAL_spawnfunc(OVERLOAD(spawnfunc, __VA_ARGS__))
- #define EVAL_spawnfunc(...) __VA_ARGS__
+ERASEABLE
+void _checkWhitelisted(entity this, string id)
+{
+ for (int i = 0, n = numentityfields(); i < n; ++i)
+ {
+ string value = getentityfieldstring(i, this);
+ string s = entityfieldname(i);
+ FIELDS_UNION(_spawnfunc_checktypes)
+ if (value == "") continue;
+ if (s == "") continue;
+ FIELDS_COMMON(_spawnfunc_check)
+ FIELDS_UNION(_spawnfunc_check)
+ LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), id, s, value);
+ }
+}
+
+noref bool __spawnfunc_first;
+
+#define spawnfunc(id) \
+ void __spawnfunc_##id(entity this); \
+ ACCUMULATE void spawnfunc_##id(entity this) \
+ { \
+ if (!__spawnfunc_first) { \
+ __spawnfunc_first = true; \
+ static_init_early(); \
+ } \
+ bool dospawn = true; \
+ if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = 0; } \
+ else if (__spawnfunc_expecting) { \
+ /* engine call */ \
+ if (!g_spawn_queue) { g_spawn_queue = IL_NEW(); } \
+ __spawnfunc_expecting = 0; \
+ this = __spawnfunc_expect; \
+ __spawnfunc_expect = NULL; \
+ dospawn = false; \
+ } else { \
+ /* userland call */ \
+ assert(this); \
+ } \
+ if (!this.sourceLoc) { \
+ this.sourceLoc = __FILE__":"STR(__LINE__); \
+ } \
+ if (!this.spawnfunc_checked) { \
+ _checkWhitelisted(this, #id); \
+ this.spawnfunc_checked = true; \
+ if (this) { \
+ /* not worldspawn, delay spawn */ \
+ __spawnfunc_defer(this, __spawnfunc_##id); \
+ } else { \
+ /* world might not be "worldspawn" */ \
+ this.__spawnfunc_constructor = __spawnfunc_##id; \
+ } \
+ } \
+ if (dospawn) { __spawnfunc_##id(this); } \
+ if (__spawnfunc_unreachable_workaround) return; \
+ } \
+ void __spawnfunc_##id(entity this)
+
+#endif