From f8c5e458c895852817da803985bee21097c908a6 Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Thu, 21 Mar 2024 04:41:15 +1000 Subject: [PATCH] Support gametype and not_gametype fields on QL maps Documents all Q3A and QL gametypes including the inconsistencies between entity fields and .arena files. Includes minor corrections. --- qcsrc/server/compat/quake3.qc | 77 +++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc index 8df003b39..39a265405 100644 --- a/qcsrc/server/compat/quake3.qc +++ b/qcsrc/server/compat/quake3.qc @@ -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; } -- 2.39.2