]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/lib/spawnfunc.qh
Include a couple more used fields in the spawnfunc field list
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / spawnfunc.qh
index e0605c93840599e8f928f359ce1f6b02cccd7016..244665cc5f3c4cf47cf3062e7dd27660701f9244 100644 (file)
@@ -1,5 +1,8 @@
 #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;
@@ -19,57 +22,67 @@ noref bool require_spawnfunc_prefix;
        }
 
        #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;
        noref 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)
+    .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)
@@ -94,12 +107,14 @@ noref bool require_spawnfunc_prefix;
                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) \
@@ -107,6 +122,8 @@ noref bool require_spawnfunc_prefix;
                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) \
@@ -117,24 +134,29 @@ noref bool require_spawnfunc_prefix;
                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, 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) \
@@ -151,6 +173,7 @@ noref bool require_spawnfunc_prefix;
                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) \
@@ -178,6 +201,7 @@ noref bool require_spawnfunc_prefix;
                FIELD_SCALAR(fld, target_random) \
                FIELD_SCALAR(fld, target_range) \
                FIELD_SCALAR(fld, team) \
+               FIELD_SCALAR(fld, trigger_reverse) \
                FIELD_SCALAR(fld, turret_scale_health) \
                FIELD_SCALAR(fld, turret_scale_range) \
                FIELD_SCALAR(fld, turret_scale_respawn) \
@@ -191,14 +215,74 @@ noref bool require_spawnfunc_prefix;
                FIELD_VEC(fld, absmin) \
                FIELD_VEC(fld, angles) \
                FIELD_VEC(fld, avelocity) \
+               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, 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