1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
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.
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.
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/>.
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "../server/server.h"
29 #include "../filesys/files.h"
31 # include "../client/client.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;
54 default: // just in case
55 case PlrOnlySpawned
: if (gi
->Players
[plridx
]->PlayerFlags
&VBasePlayer::PF_Spawned
) return; break;
62 //==========================================================================
64 // VGameInfo::VGameInfo
66 //==========================================================================
68 VGameInfo::VGameInfo ()
70 , GenericConScript(E_NoInit)
71 , PlayerClasses(E_NoInit)
77 //==========================================================================
79 // VGameInfo::IsWipeAllowed
81 //==========================================================================
82 bool VGameInfo::IsWipeAllowed () {
84 return (NetMode
== NM_Standalone
);
91 //==========================================================================
93 // VGameInfo::IsInWipe
95 //==========================================================================
96 bool VGameInfo::IsInWipe () {
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
);
107 IMPLEMENT_FUNCTION(VGameInfo
, get_isInWipe
) {
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;
122 if (NetMode
== NM_Standalone
) {
123 const bool isBadApple
= ((GLevel
&& GLevel
->IsBadApple()) || (GClLevel
&& GClLevel
->IsBadApple()));
124 if (isBadApple
) return IsInWipe();
127 // should we totally ignore pause flag in server mode?
131 // in single player pause game if in menu or console
133 (NetMode
== NM_Standalone
&& (MN_Active() || (!ignoreOpenConsole
&& C_Active())))
139 IMPLEMENT_FUNCTION(VGameInfo
, get_isPaused
) {
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");
169 VClass
*Class
= VClass::FindClassNoCase(*Args
[1]);
171 GCon
->Logf(NAME_Warning
, "AddPlayerClass: No such class `%s`", *Args
[1]);
175 VClass
*PPClass
= VClass::FindClass("PlayerPawn");
177 GCon
->Logf(NAME_Warning
, "AddPlayerClass: Can't find PlayerPawn class");
181 if (!Class
->IsChildOf(PPClass
)) {
182 GCon
->Logf(NAME_Warning
, "AddPlayerClass: '%s' is not a player pawn class", *Args
[1]);
186 if (FL_IsIgnoredPlayerClass(Args
[1])) {
187 GCon
->Logf(NAME_Init
, "keyconf player class '%s' ignored due to mod detector orders", Class
->GetName());
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 //==========================================================================
210 //==========================================================================
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
) {
243 if (!cc
->IsChildOf(PPClass
)) continue;
244 savedPlayerClasses
.append(cc
);
249 //==========================================================================
251 // COMMAND ForcePlayerClass
253 //==========================================================================
254 COMMAND_WITH_AC(ForcePlayerClass
) {
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!");
265 if (Args
.length() < 2) {
266 GCon
->Logf(NAME_Warning
, "ForcePlayerClass: Player class name missing");
270 VClass
*PPClass
= VClass::FindClass("PlayerPawn");
272 GCon
->Logf(NAME_Warning
, "ForcePlayerClass: Can't find PlayerPawn class");
276 TArray
<VClass
*> clist
;
277 for (int f
= 1; f
< Args
.length(); ++f
) {
279 if (cn
.length() && cn
[0] == '*') cn
.chopLeft(1);
280 VClass
*Class
= VClass::FindClassNoCase(*cn
);
282 GCon
->Logf(NAME_Warning
, "ForcePlayerClass: No such class `%s`", *cn
);
285 if (!Class
->IsChildOf(PPClass
)) {
286 GCon
->Logf(NAME_Warning
, "ForcePlayerClass: '%s' is not a player pawn class", *cn
);
292 if (clist
.length() == 0) {
293 GCon
->Logf(NAME_Warning
, "ForcePlayerClass: no valid player classes were specified.");
297 GGameInfo
->PlayerClasses
.Clear();
298 for (auto &&cc
: clist
) GGameInfo
->PlayerClasses
.Append(cc
);
302 //==========================================================================
304 // COMMAND_AC ForcePlayerClass
306 //==========================================================================
307 COMMAND_AC(ForcePlayerClass
) {
310 VClass
*PPClass
= VClass::FindClass("PlayerPawn");
311 if (!PPClass
) return VStr::EmptyString
;
313 TArray
<VClass
*> clist
;
314 for (auto &&cc
: savedPlayerClasses
) {
316 if (!cc
->IsChildOf(PPClass
)) continue;
320 if (clist
.length() == 0) return VStr::EmptyString
;
323 VStr prefix
= (aidx
< args
.length() ? args
[aidx
] : VStr());
325 if (prefix
.length() && prefix
[0] == '*') {
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
;
335 for (VClass
*cn
: clist
) list
.append(cn
->GetName());
337 return VCommand::AutoCompleteFromListCmd(prefix
, list
);
339 return VStr::EmptyString
;
344 //==========================================================================
346 // COMMAND PrintPlayerClasses
348 //==========================================================================
349 COMMAND(PrintPlayerClasses
) {
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!");
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());