]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/spawnfunc.qh
Nades code: don't use booleans as array indexes for m_projectile, optimize spawn_held...
[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 // 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(); }
93
94 noref bool __spawnfunc_first;
95
96 #define spawnfunc(id) \
97         void __spawnfunc_##id(entity this); \
98         ACCUMULATE void spawnfunc_##id(entity this) \
99         { \
100                 if (!__spawnfunc_first) { \
101                         __spawnfunc_first = true; \
102                         static_init_early(); \
103                 } \
104                 bool dospawn = true; \
105                 if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = 0; } \
106                 else if (__spawnfunc_expecting) { \
107                         /* engine call */ \
108                         if (!g_spawn_queue) g_spawn_queue_spawn(); \
109                         __spawnfunc_expecting = 0; \
110                         this = __spawnfunc_expect; \
111                         __spawnfunc_expect = NULL; \
112                         dospawn = false; \
113                 } else { \
114                         /* userland call */ \
115                         assert(this); \
116                 } \
117                 if (!this.sourceLoc) { \
118                         this.sourceLoc = __FILE__":"STR(__LINE__); \
119                 } \
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; \
126                         } \
127                         this.spawnfunc_checked = true; \
128                         if (this) { \
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); \
134                         } else { \
135                                 /* world might not be "worldspawn" */ \
136                                 this.__spawnfunc_constructor = __spawnfunc_##id; \
137                         } \
138                 } \
139                 if (dospawn) { __spawnfunc_##id(this); } \
140                 if (__spawnfunc_unreachable_workaround) return; \
141         } \
142         void __spawnfunc_##id(entity this)
143
144 #endif