]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Support gametype and not_gametype fields on QL maps
authorbones_was_here <bones_was_here@xonotic.au>
Wed, 20 Mar 2024 18:41:15 +0000 (04:41 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Wed, 17 Apr 2024 14:49:39 +0000 (00:49 +1000)
Documents all Q3A and QL gametypes including the inconsistencies between
entity fields and .arena files.

Includes minor corrections.

qcsrc/server/compat/quake3.qc

index 8df003b39247d96ce7c9c84179f9da9ad332f5c3..39a26540539fb8edc8496ff28b4550f5f3d10f31 100644 (file)
@@ -340,10 +340,14 @@ spawnfunc(target_smallprint)
 .bool notvq3;
 .bool notcpm;
 .string gametype;
+.string not_gametype;
 bool DoesQ3ARemoveThisEntity(entity this)
 {
        // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY)
 
+       if (!this.classname)
+               return true;
+
        // DeFRaG mappers use "notcpm" or "notvq3" to disable an entity in CPM or VQ3 physics
        // Xonotic is usually played with a CPM-based physics so we default to CPM mode
        if(cvar_string("g_mod_physics") == "Q3")
@@ -359,9 +363,11 @@ bool DoesQ3ARemoveThisEntity(entity this)
        if(this.notta)
                return true;
 
-       // FIXME: singleplayer does not use maxclients 1 as that would prevent bots
+       // FIXME: singleplayer does not use maxclients 1 as that would prevent bots,
+       // this is the case in Q3 also, it uses another method to block clients.
+       // Only accessible in VQ3, via the `spmap` command.
        if(this.notsingle)
-               if(maxclients == 1)
+               if(maxclients == 1 && IS_GAMETYPE(DEATHMATCH))
                        return true;
 
        if(this.notteam)
@@ -372,23 +378,62 @@ bool DoesQ3ARemoveThisEntity(entity this)
                if(!teamplay)
                        return true;
 
-       if(this.gametype)
+       if(this.gametype || this.not_gametype)
        {
-               string gametypename;
-               // From ioq3 g_spawn.c: static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester"};
-               gametypename = "ffa";
+               // Q3 checks these with strstr(): case-sensitive, no gametype can be a substring of another,
+               // any separator is allowed (conventions are: spaces, commas, or commas with spaces).
+               // QL's entities.def says they're space delineated.
+
+               // Q3 gametype entity fields: ffa tournament single team ctf oneflag obelisk  harvester    (game/g_spawn.c)
+               // Q3 arena file 'type' key:  ffa tourney                ctf oneflag overload harvester    (ui/ui_gameinfo.c)
+
+               // QL gametype entity fields: ffa duel tdm ca ft rr ctf ad dom har 1f      race ob
+               // QL arena file 'type' key:  ffa duel tdm ca ft rr ctf ad dom har oneflag race
+
+               string gametypename_q3, gametypename_ql;
+
+               // One of these will apply if our gametype has no Q3/QL equivalent
                if(teamplay)
-                       gametypename = "team";
+               {
+                       gametypename_q3 = "team";
+                       gametypename_ql = "tdm";
+               }
+               else
+                       gametypename_q3 = gametypename_ql = "ffa";
+
                if(g_ctf)
-                       gametypename = "ctf";
-               if(g_ctf && ctf_oneflag)
-                       gametypename = "oneflag";
-               if(g_duel)
-                       gametypename = "tournament";
-               if(maxclients == 1)
-                       gametypename = "single";
-               // we do not have the other types (obelisk, harvester)
-               if(strstrofs(this.gametype, gametypename, 0) < 0)
+               {
+                       if (ctf_oneflag)
+                       {
+                               gametypename_q3 = "oneflag";
+                               gametypename_ql = "1f";
+                       }
+                       else
+                               gametypename_q3 = gametypename_ql = "ctf";
+               }
+               else if(g_duel)
+               {
+                       gametypename_q3 = "tournament";
+                       gametypename_ql = "duel";
+               }
+               else if(IS_GAMETYPE(DEATHMATCH) && maxclients == 1)
+                       gametypename_q3 = "single";
+               else if(g_ca)
+                       gametypename_ql = "ql";
+               else if(IS_GAMETYPE(FREEZETAG))
+                       gametypename_ql = "ft";
+               else if(IS_GAMETYPE(DOMINATION))
+                       gametypename_ql = "dom";
+               else if(g_race || g_cts)
+                       gametypename_ql = "race";
+
+               if(this.gametype)
+               if(strstrofs(this.gametype, gametypename_q3, 0) < 0
+               && strstrofs(this.gametype, gametypename_ql, 0) < 0)
+                       return true;
+
+               // Only supported by QL
+               if(strstrofs(this.not_gametype, gametypename_ql, 0) >= 0)
                        return true;
        }