libvwad: updated -- vwadwrite: free file buffers on close (otherwise archive creation...
[k8vavoom.git] / source / psim / p_gameinfo.cpp
blob76f5079a044e7befd22847a25398457d48001fd6
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "../server/server.h"
28 #include "../menu.h"
29 #include "../filesys/files.h"
30 #ifdef CLIENT
31 # include "../client/client.h"
32 #endif
33 #include "p_player.h"
36 IMPLEMENT_CLASS(V, GameInfo)
38 VGameInfo *GGameInfo = nullptr;
40 static TArray<VClass *> savedPlayerClasses;
41 static bool playerClassesSaved = false;
44 //==========================================================================
46 // VGameInfo::PlayersIter::nextByType
48 //==========================================================================
49 void VGameInfo::PlayersIter::nextByType () noexcept {
50 for (++plridx; plridx < MAXPLAYERS; ++plridx) {
51 if (!gi->Players[plridx]) continue;
52 switch (type) {
53 case PlrAll: return;
54 default: // just in case
55 case PlrOnlySpawned: if (gi->Players[plridx]->PlayerFlags&VBasePlayer::PF_Spawned) return; break;
58 plridx = MAXPLAYERS;
62 //==========================================================================
64 // VGameInfo::VGameInfo
66 //==========================================================================
68 VGameInfo::VGameInfo ()
69 : AcsHelper(E_NoInit)
70 , GenericConScript(E_NoInit)
71 , PlayerClasses(E_NoInit)
77 //==========================================================================
79 // VGameInfo::IsWipeAllowed
81 //==========================================================================
82 bool VGameInfo::IsWipeAllowed () {
83 #ifdef CLIENT
84 return (NetMode == NM_Standalone);
85 #else
86 return false;
87 #endif
91 //==========================================================================
93 // VGameInfo::IsInWipe
95 //==========================================================================
96 bool VGameInfo::IsInWipe () {
97 #ifdef CLIENT
98 // in single player pause game if in menu or console
99 if (GLevel && serverStartRenderFramesTic > 0 && GLevel->TicTime < serverStartRenderFramesTic) return false;
100 return (clWipeTimer >= 0.0f);
101 #else
102 return false;
103 #endif
107 IMPLEMENT_FUNCTION(VGameInfo, get_isInWipe) {
108 vobjGetParamSelf();
109 RET_BOOL(Self ? Self->IsInWipe() : false);
113 //==========================================================================
115 // VGameInfo::IsPaused
117 //==========================================================================
118 bool VGameInfo::IsPaused (bool ignoreOpenConsole) {
119 if (NetMode <= NM_TitleMap) return false;
120 #ifdef CLIENT
121 // BadApple.wad hack
122 if (NetMode == NM_Standalone) {
123 const bool isBadApple = ((GLevel && GLevel->IsBadApple()) || (GClLevel && GClLevel->IsBadApple()));
124 if (isBadApple) return IsInWipe();
126 #endif
127 // should we totally ignore pause flag in server mode?
128 return
129 !!(Flags&GIF_Paused)
130 #ifdef CLIENT
131 // in single player pause game if in menu or console
132 || IsInWipe() ||
133 (NetMode == NM_Standalone && (MN_Active() || (!ignoreOpenConsole && C_Active())))
134 #endif
139 IMPLEMENT_FUNCTION(VGameInfo, get_isPaused) {
140 vobjGetParamSelf();
141 RET_BOOL(Self ? Self->IsPaused() : false);
145 //==========================================================================
147 // COMMAND ClearPlayerClasses
149 //==========================================================================
150 COMMAND(ClearPlayerClasses) {
151 if (!ParsingKeyConf) return;
152 GGameInfo->PlayerClasses.Clear();
156 //==========================================================================
158 // COMMAND AddPlayerClass
160 //==========================================================================
161 COMMAND(AddPlayerClass) {
162 if (!ParsingKeyConf) return;
164 if (Args.length() < 2) {
165 GCon->Logf(NAME_Warning, "AddPlayerClass: Player class name missing");
166 return;
169 VClass *Class = VClass::FindClassNoCase(*Args[1]);
170 if (!Class) {
171 GCon->Logf(NAME_Warning, "AddPlayerClass: No such class `%s`", *Args[1]);
172 return;
175 VClass *PPClass = VClass::FindClass("PlayerPawn");
176 if (!PPClass) {
177 GCon->Logf(NAME_Warning, "AddPlayerClass: Can't find PlayerPawn class");
178 return;
181 if (!Class->IsChildOf(PPClass)) {
182 GCon->Logf(NAME_Warning, "AddPlayerClass: '%s' is not a player pawn class", *Args[1]);
183 return;
186 if (FL_IsIgnoredPlayerClass(Args[1])) {
187 GCon->Logf(NAME_Init, "keyconf player class '%s' ignored due to mod detector orders", Class->GetName());
188 return;
191 GGameInfo->PlayerClasses.Append(Class);
195 //==========================================================================
197 // COMMAND WeaponSection
199 //==========================================================================
200 COMMAND(WeaponSection) {
201 if (!ParsingKeyConf) return;
202 GGameInfo->eventCmdWeaponSection(Args.length() > 1 ? Args[1] : "");
206 //==========================================================================
208 // COMMAND SetSlot
210 //==========================================================================
211 COMMAND(SetSlot) {
212 if (!ParsingKeyConf) return;
213 GGameInfo->eventCmdSetSlot(&Args, true); // as keyconf
217 //==========================================================================
219 // COMMAND AddSlotDefault
221 //==========================================================================
222 COMMAND(AddSlotDefault) {
223 if (!ParsingKeyConf) return;
224 GGameInfo->eventCmdAddSlotDefault(&Args, true); // as keyconf
228 //==========================================================================
230 // COMMAND ForcePlayerClass
232 //==========================================================================
233 static void savePlayerClasses () {
234 if (playerClassesSaved) return;
235 playerClassesSaved = true;
236 vassert(savedPlayerClasses.length() == 0);
238 VClass *PPClass = VClass::FindClass("PlayerPawn");
239 if (!PPClass) return;
241 for (VClass *cc : GGameInfo->PlayerClasses) {
242 if (!cc) continue;
243 if (!cc->IsChildOf(PPClass)) continue;
244 savedPlayerClasses.append(cc);
249 //==========================================================================
251 // COMMAND ForcePlayerClass
253 //==========================================================================
254 COMMAND_WITH_AC(ForcePlayerClass) {
255 savePlayerClasses();
257 if (GGameInfo->NetMode > NM_TitleMap) {
258 CMD_FORWARD_TO_SERVER();
259 if (GGameInfo->NetMode >= NM_Client) {
260 GCon->Logf(NAME_Error, "Cannot force player class on client!");
261 return;
265 if (Args.length() < 2) {
266 GCon->Logf(NAME_Warning, "ForcePlayerClass: Player class name missing");
267 return;
270 VClass *PPClass = VClass::FindClass("PlayerPawn");
271 if (!PPClass) {
272 GCon->Logf(NAME_Warning, "ForcePlayerClass: Can't find PlayerPawn class");
273 return;
276 TArray<VClass *> clist;
277 for (int f = 1; f < Args.length(); ++f) {
278 VStr cn = Args[f];
279 if (cn.length() && cn[0] == '*') cn.chopLeft(1);
280 VClass *Class = VClass::FindClassNoCase(*cn);
281 if (!Class) {
282 GCon->Logf(NAME_Warning, "ForcePlayerClass: No such class `%s`", *cn);
283 continue;
285 if (!Class->IsChildOf(PPClass)) {
286 GCon->Logf(NAME_Warning, "ForcePlayerClass: '%s' is not a player pawn class", *cn);
287 continue;
289 clist.append(Class);
292 if (clist.length() == 0) {
293 GCon->Logf(NAME_Warning, "ForcePlayerClass: no valid player classes were specified.");
294 return;
297 GGameInfo->PlayerClasses.Clear();
298 for (auto &&cc : clist) GGameInfo->PlayerClasses.Append(cc);
302 //==========================================================================
304 // COMMAND_AC ForcePlayerClass
306 //==========================================================================
307 COMMAND_AC(ForcePlayerClass) {
308 savePlayerClasses();
310 VClass *PPClass = VClass::FindClass("PlayerPawn");
311 if (!PPClass) return VStr::EmptyString;
313 TArray<VClass *> clist;
314 for (auto &&cc : savedPlayerClasses) {
315 if (!cc) continue;
316 if (!cc->IsChildOf(PPClass)) continue;
317 clist.append(cc);
320 if (clist.length() == 0) return VStr::EmptyString;
322 TArray<VStr> list;
323 VStr prefix = (aidx < args.length() ? args[aidx] : VStr());
324 if (aidx == 1) {
325 if (prefix.length() && prefix[0] == '*') {
326 VStr pfx = prefix;
327 pfx.chopLeft(1);
328 VClass::ForEachChildOf("PlayerPawn", [&list, &pfx](VClass *cls) {
329 if (pfx.length() == 0 || VStr::startsWithCI(cls->GetName(), *pfx)) {
330 list.append(VStr("*")+cls->GetName());
332 return FERes::FOREACH_NEXT;
334 } else {
335 for (VClass *cn : clist) list.append(cn->GetName());
337 return VCommand::AutoCompleteFromListCmd(prefix, list);
338 } else {
339 return VStr::EmptyString;
344 //==========================================================================
346 // COMMAND PrintPlayerClasses
348 //==========================================================================
349 COMMAND(PrintPlayerClasses) {
350 savePlayerClasses();
352 if (GGameInfo->NetMode > NM_TitleMap) {
353 CMD_FORWARD_TO_SERVER();
354 if (GGameInfo->NetMode >= NM_Client) {
355 GCon->Logf(NAME_Error, "Cannot list player classes on client!");
356 return;
360 GCon->Logf("=== %d known player class%s ===", GGameInfo->PlayerClasses.length(), (GGameInfo->PlayerClasses.length() != 1 ? "es" : ""));
361 for (auto &&cc : savedPlayerClasses) GCon->Logf(" %s (%s)", cc->GetName(), *cc->Loc.toStringNoCol());