2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2000-2020 Ashley Rose "LadyHavoc" Hale
4 Copyright (C) 2020 David "Cloudwalk" Knapp
5 Copyright (C) 2020 Kristus <kristustf@gmail.com>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 See the GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the
31 const char *gamedirname1;
32 const char *gamedirname2;
33 const char *gamescreenshotname;
34 const char *gameuserdirname;
35 char com_modname[MAX_OSPATH] = "";
37 gamemode_t com_startupgamemode;
38 gamemode_t com_startupgamegroup;
40 typedef struct gamemode_info_s
42 gamemode_t mode; // this gamemode
43 gamemode_t group; // different games with same group can switch automatically when gamedirs change
44 const char* prog_name; // not null
45 const char* cmdline; // not null
46 const char* gamename; // not null
47 const char* gamenetworkfiltername; // not null
48 const char* gamedirname1; // not null
49 const char* gamedirname2; // null
50 const char* gamescreenshotname; // not nul
51 const char* gameuserdirname; // not null
54 static const gamemode_info_t gamemode_info [GAME_COUNT] =
55 {// game basegame prog_name cmdline gamename gamenetworkfilername basegame modgame screenshot userdir // commandline option
56 { GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
57 { GAME_HIPNOTIC, GAME_NORMAL, "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
58 { GAME_ROGUE, GAME_NORMAL, "rogue", "-rogue", "Darkplaces-Rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
59 { GAME_NEHAHRA, GAME_NORMAL, "nehahra", "-nehahra", "DarkPlaces-Nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
60 { GAME_QUOTH, GAME_NORMAL, "quoth", "-quoth", "Darkplaces-Quoth", "Darkplaces-Quoth", "id1", "quoth", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quoth runs the Quoth mod for playing community maps made for it
61 { GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
62 { GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
63 { GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
64 { GAME_GOODVSBAD2, GAME_GOODVSBAD2, "gvb2", "-goodvsbad2", "GoodVs.Bad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
65 { GAME_TEU, GAME_TEU, "teu", "-teu", "TheEvilUnleashed", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
66 { GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
67 { GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
68 { GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
69 { GAME_TENEBRAE, GAME_NORMAL, "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
70 { GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
71 { GAME_OPENQUARTZ, GAME_NORMAL, "openquartz", "-openquartz", "OpenQuartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
72 { GAME_PRYDON, GAME_NORMAL, "prydon", "-prydon", "PrydonGate", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
73 { GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "Deluxe_Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
74 { GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "The_Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
75 { GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2, "did2", "-did2", "Defeat In Detail 2", "Defeat_In_Detail_2", "data", NULL, "did2_", "did2" }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
76 { GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
77 { GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "Contagion_Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
78 { GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "EDU2_Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
79 { GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "Prophecy", "gamedata", NULL, "phcy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
80 { GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "Blood_Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
81 { GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
82 { GAME_STEELSTORM2, GAME_STEELSTORM2, "steelstorm2", "-steelstorm2", "Steel Storm 2", "Steel_Storm_2", "gamedata", NULL, "ss2", "steelstorm2" }, // COMMANDLINEOPTION: Game: -steelstorm2 runs the game Steel Storm 2
83 { GAME_SSAMMO, GAME_SSAMMO, "steelstorm-ammo", "-steelstormammo", "Steel Storm A.M.M.O.", "Steel_Storm_A.M.M.O.", "gamedata", NULL, "ssammo", "steelstorm-ammo" }, // COMMANDLINEOPTION: Game: -steelstormammo runs the game Steel Storm A.M.M.O.
84 { GAME_STEELSTORMREVENANTS, GAME_STEELSTORMREVENANTS, "steelstorm-revenants", "-steelstormrev", "Steel Storm: Revenants", "Steel_Storm_Revenants", "base", NULL, "ssrev", "steelstorm-revenants" }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants
85 { GAME_TOMESOFMEPHISTOPHELES, GAME_TOMESOFMEPHISTOPHELES, "tomesofmephistopheles","-tomesofmephistopheles", "Tomes of Mephistopheles", "Tomes_of_Mephistopheles", "gamedata", NULL, "tom", "tomesofmephistopheles" }, // COMMANDLINEOPTION: Game: -tomesofmephistopheles runs the game Tomes of Mephistopheles
86 { GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "Strap-on-bomb_Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
87 { GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
88 { GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
89 { GAME_DOOMBRINGER, GAME_DOOMBRINGER, "doombringer", "-doombringer", "DOOMBRINGER", "DOOMBRINGER", "dbdata", NULL, "doombringer", "doombringer" }, // COMMANDLINEOPTION: Game: -doombringer runs the game DOOMBRINGER
92 static void COM_SetGameType(int index);
93 void COM_InitGameType (void)
95 char name [MAX_OSPATH];
100 COM_ToLowerString(FORCEGAME, name, sizeof (name));
102 // check executable filename for keywords, but do it SMARTLY - only check the last path element
103 FS_StripExtension(FS_FileWithoutPath(sys.argv[0]), name, sizeof (name));
104 COM_ToLowerString(name, name, sizeof (name));
106 for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
107 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
110 // check commandline options for keywords
111 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
112 if (COM_CheckParm (gamemode_info[i].cmdline))
115 com_startupgamemode = gamemode_info[index].mode;
116 com_startupgamegroup = gamemode_info[index].group;
117 COM_SetGameType(index);
120 void COM_ChangeGameTypeForGameDirs(void)
124 // this will not not change the gamegroup
125 // first check if a base game (single gamedir) matches
126 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
128 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
134 // now that we have a base game, see if there is a matching derivative game (two gamedirs)
137 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
139 if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
146 // we now have a good guess at which game this is meant to be...
147 if (index >= 0 && gamemode != gamemode_info[index].mode)
148 COM_SetGameType(index);
151 static void COM_SetGameType(int index)
153 static char gamenetworkfilternamebuffer[64];
155 if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
157 gamemode = gamemode_info[index].mode;
158 gamename = gamemode_info[index].gamename;
159 gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
160 gamedirname1 = gamemode_info[index].gamedirname1;
161 gamedirname2 = gamemode_info[index].gamedirname2;
162 gamescreenshotname = gamemode_info[index].gamescreenshotname;
163 gameuserdirname = gamemode_info[index].gameuserdirname;
165 if (gamemode == com_startupgamemode)
167 if((t = COM_CheckParm("-customgamename")) && t + 1 < sys.argc)
168 gamename = gamenetworkfiltername = sys.argv[t+1];
169 if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < sys.argc)
170 gamenetworkfiltername = sys.argv[t+1];
171 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < sys.argc)
172 gamedirname1 = sys.argv[t+1];
173 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < sys.argc)
174 gamedirname2 = *sys.argv[t+1] ? sys.argv[t+1] : NULL;
175 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < sys.argc)
176 gamescreenshotname = sys.argv[t+1];
177 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < sys.argc)
178 gameuserdirname = sys.argv[t+1];
181 if (gamedirname2 && gamedirname2[0])
182 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
184 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
185 for (i = 0;i < fs_numgamedirs;i++)
188 Con_Printf(", with mod gamedirs");
189 Con_Printf(" %s", fs_gamedirs[i]);
193 if (strchr(gamenetworkfiltername, ' '))
196 // if there are spaces in the game's network filter name it would
197 // cause parse errors in getservers in dpmaster, so we need to replace
198 // them with _ characters
199 strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
200 while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
202 gamenetworkfiltername = gamenetworkfilternamebuffer;
205 Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);