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;
11 .string fullspawndata;
13 // Optional type checking; increases compile time too much to be enabled by default
15 bool entityfieldassignablefromeditor(int i)
17 switch (entityfieldtype(i))
27 #define _spawnfunc_checktypes(fld) \
29 if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", s);
31 #define _spawnfunc_checktypes(fld)
33 #define _spawnfunc_check(fld) \
34 if (s == #fld) continue;
36 noref int __spawnfunc_expecting;
37 noref entity __spawnfunc_expect;
38 noref bool __spawnfunc_unreachable_workaround = true;
40 .void(entity) __spawnfunc_constructor;
41 noref IntrusiveList g_spawn_queue;
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) \
52 #define X(T, fld, def) .T fld, __spawnfunc_##fld;
53 SPAWNFUNC_INTERNAL_FIELDS(X)
56 void __spawnfunc_defer(entity prototype, void(entity) constructor)
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);
62 prototype.__spawnfunc_constructor = constructor;
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)); \
71 void _SV_OnEntityPreSpawnFunction(entity this);
73 void __spawnfunc_spawn(entity prototype)
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);
82 _SV_OnEntityPreSpawnFunction(e);
87 e.__spawnfunc_constructor(e);
90 // this function simply avoids expanding IL_NEW during compilation
91 // for each spawning entity
92 void g_spawn_queue_spawn() { g_spawn_queue = IL_NEW(); }
94 noref bool __spawnfunc_first;
96 #define spawnfunc(id) \
97 void __spawnfunc_##id(entity this); \
98 ACCUMULATE void spawnfunc_##id(entity this) \
100 if (!__spawnfunc_first) { \
101 __spawnfunc_first = true; \
102 static_init_early(); \
104 bool dospawn = true; \
105 if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = 0; } \
106 else if (__spawnfunc_expecting) { \
108 if (!g_spawn_queue) g_spawn_queue_spawn(); \
109 __spawnfunc_expecting = 0; \
110 this = __spawnfunc_expect; \
111 __spawnfunc_expect = NULL; \
114 /* userland call */ \
117 if (!this.sourceLoc) { \
118 this.sourceLoc = __FILE__":"STR(__LINE__); \
120 this.classname = #id; \
121 if (!this.spawnfunc_checked) { \
122 if (__fullspawndata) { \
123 /* not supported in old DP */ \
124 /* must be read inside the real spawnfunc */ \
125 this.fullspawndata = __fullspawndata; \
127 this.spawnfunc_checked = true; \
129 /* not worldspawn, delay spawn */ \
130 /* clear some dangerous fields (TODO: properly support these in the map!) */ \
131 this.think = func_null; \
132 this.nextthink = 0; \
133 __spawnfunc_defer(this, __spawnfunc_##id); \
135 /* world might not be "worldspawn" */ \
136 this.__spawnfunc_constructor = __spawnfunc_##id; \
139 if (dospawn) { __spawnfunc_##id(this); } \
140 if (__spawnfunc_unreachable_workaround) return; \
142 void __spawnfunc_##id(entity this)