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 //**************************************************************************
28 #include "psim/p_levelinfo.h"
29 #include "server/server.h"
30 #include "server/sv_local.h"
31 #include "decorate/vc_decorate.h"
33 #include "filesys/files.h"
34 #include "render/r_public.h" /* R_HasNamedSkybox */
37 static int cli_NoMapinfoPlrClasses
= 0;
38 static int cli_MapperIsIdiot
= 0;
39 int cli_NoZMapinfo
= 0; // need to be extern for files.cpp
41 /*static*/ bool cliRegister_mapinfo_args
=
42 VParsedArgs::RegisterFlagSet("-nomapinfoplayerclasses", "ignore player classes from MAPINFO", &cli_NoMapinfoPlrClasses
) &&
43 VParsedArgs::RegisterFlagSet("-nozmapinfo", "do not load ZMAPINFO", &cli_NoZMapinfo
) &&
44 VParsedArgs::RegisterFlagSet("-mapper-is-idiot", "!", &cli_MapperIsIdiot
);
47 static VCvarB
gm_default_pain_limit("gm_default_pain_limit", true, "Limit Pain Elemental skull spawning by default?", CVAR_Archive
);
50 // ////////////////////////////////////////////////////////////////////////// //
52 struct SCParseModeSaver
{
57 SCParseModeSaver (VScriptParser
*asc
) : sc(asc
) {
58 oldCMode
= sc
->IsCMode();
59 oldEscape
= sc
->IsEscape();
64 ~SCParseModeSaver () {
65 sc
->SetCMode(oldCMode
);
66 sc
->SetEscape(oldEscape
);
69 //SCParseModeSaver (const SCParseModeSaver &);
70 //void operator = (const SCParseModeSaver &) const;
71 VV_DISABLE_COPY(SCParseModeSaver
)
95 // ////////////////////////////////////////////////////////////////////////// //
96 struct MapInfoCommand
{
98 void (*handler
) (VScriptParser
*sc
, bool newFormat
, VMapInfo
*info
, bool &HexenMode
);
103 static MapInfoCommand
*mclist
= nullptr;
104 static TMap
<VStr
, MapInfoCommand
*> mcmap
; // key is lowercase name
105 static bool hasCustomDamageFactors
= false;
108 WSK_WAS_SKY1
= 1u<<0,
109 WSK_WAS_SKY2
= 1u<<1,
111 static unsigned wasSky1Sky2
= 0u; // bit0: was sky1; bit1: was sky2
114 #define MAPINFOCMD(name_) \
115 class MapInfoCommandImpl##name_ { \
117 /*static*/ MapInfoCommand mci; \
118 MapInfoCommandImpl##name_ (const char *aname) { \
120 mci.handler = &Handler; \
121 mci.next = nullptr; \
125 MapInfoCommand *last = mclist; \
126 while (last->next) last = last->next; \
130 static void Handler (VScriptParser *sc, bool newFormat, VMapInfo *info, bool &HexenMode); \
132 /*MapInfoCommand MapInfoCommandImpl##name_ mci;*/ \
133 MapInfoCommandImpl##name_ mpiprzintrnlz_mici_##name_(#name_); \
134 void MapInfoCommandImpl##name_::Handler (VScriptParser *sc, bool newFormat, VMapInfo *info, bool &HexenMode)
137 //==========================================================================
141 //==========================================================================
142 static bool checkEndBracket (VScriptParser
*sc
) {
143 if (sc
->Check("}")) return true;
145 sc
->Message("Some mapper cannot into proper mapinfo (missing '}')");
152 //==========================================================================
156 //==========================================================================
157 static bool ExpectBool (const char *optname
, VScriptParser
*sc
) {
158 if (sc
->Check("true") || sc
->Check("on") || sc
->Check("tan")) return true;
159 if (sc
->CheckNumber()) return !!sc
->Number
;
160 sc
->Error(va("boolean value expected for option '%s'", optname
));
165 // ////////////////////////////////////////////////////////////////////////// //
166 VName
P_TranslateMap (int map
);
167 // `sc->SourceLump` must be set
168 static void ParseMapInfo (VScriptParser
*sc
);
169 // `sc->SourceLump` must be set
170 static void ParseUMapinfo (VScriptParser
*sc
);
173 // ////////////////////////////////////////////////////////////////////////// //
174 static char miWarningBuf
[16384];
176 VVA_OKUNUSED
__attribute__((format(printf
, 2, 3))) static void miWarning (VScriptParser
*sc
, const char *fmt
, ...) {
178 static char miWarningBuf
[16384];
179 va_start(argptr
, fmt
);
180 vsnprintf(miWarningBuf
, sizeof(miWarningBuf
), fmt
, argptr
);
183 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: %s", *sc
->GetLoc().toStringNoCol(), miWarningBuf
);
185 GCon
->Logf(NAME_Warning
, "MAPINFO: %s", miWarningBuf
);
190 VVA_OKUNUSED
__attribute__((format(printf
, 2, 3))) static void miWarning (const VTextLocation
&loc
, const char *fmt
, ...) {
192 va_start(argptr
, fmt
);
193 vsnprintf(miWarningBuf
, sizeof(miWarningBuf
), fmt
, argptr
);
195 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: %s", *loc
.toStringNoCol(), miWarningBuf
);
199 // ////////////////////////////////////////////////////////////////////////// //
200 static VMapInfo DefaultMap
;
201 static TArray
<VMapInfo
> MapInfo
;
202 static TArray
<FMapSongInfo
> MapSongList
;
203 static VClusterDef DefaultClusterDef
;
204 static TArray
<VClusterDef
> ClusterDefs
;
205 static TArray
<VEpisodeDef
> EpisodeDefs
;
206 static TArray
<VSkillDef
> SkillDefs
;
208 static bool mapinfoParsed
= false;
209 static TArray
<ParTimeInfo
> partimes
; // not a hashmap, so i can use `ICmp`
211 static TArray
<VName
> MapInfoPlayerClasses
;
213 static TMap
<int, SpawnEdFixup
> SpawnNumFixups
; // keyed by num
214 static TMap
<int, SpawnEdFixup
> DoomEdNumFixups
; // keyed by num
217 //==========================================================================
221 //==========================================================================
222 VStr
VMapInfo::GetName () const {
223 return (Flags
&VLevelInfo::LIF_LookupName
? GLanguage
[*Name
] : Name
);
227 //==========================================================================
231 //==========================================================================
232 void VMapInfo::dump (const char *msg
) const {
233 if (msg
&& msg
[0]) GCon
->Logf(NAME_Debug
, "==== mapinfo: %s ===", msg
); else GCon
->Log(NAME_Debug
, "==== mapinfo ===");
234 GCon
->Logf(NAME_Debug
, " LumpName: \"%s\"", *VStr(LumpName
).quote());
235 GCon
->Logf(NAME_Debug
, " Name: \"%s\"", *Name
.quote());
236 GCon
->Logf(NAME_Debug
, " LevelNum: %d", LevelNum
);
237 GCon
->Logf(NAME_Debug
, " Cluster: %d", Cluster
);
238 GCon
->Logf(NAME_Debug
, " WarpTrans: %d", WarpTrans
);
239 GCon
->Logf(NAME_Debug
, " NextMap: \"%s\"", *VStr(NextMap
).quote());
240 GCon
->Logf(NAME_Debug
, " SecretMap: \"%s\"", *VStr(SecretMap
).quote());
241 GCon
->Logf(NAME_Debug
, " SongLump: \"%s\"", *VStr(SongLump
).quote());
242 GCon
->Logf(NAME_Debug
, " Sky1Texture: %d", Sky1Texture
);
243 GCon
->Logf(NAME_Debug
, " Sky2Texture: %d", Sky2Texture
);
244 GCon
->Logf(NAME_Debug
, " Sky1ScrollDelta: %g", Sky1ScrollDelta
);
245 GCon
->Logf(NAME_Debug
, " Sky2ScrollDelta: %g", Sky2ScrollDelta
);
246 GCon
->Logf(NAME_Debug
, " SkyBox: \"%s\"", *VStr(SkyBox
).quote());
247 GCon
->Logf(NAME_Debug
, " FadeTable: \"%s\"", *VStr(FadeTable
).quote());
248 GCon
->Logf(NAME_Debug
, " Fade: 0x%08x", Fade
);
249 GCon
->Logf(NAME_Debug
, " OutsideFog: 0x%08x", OutsideFog
);
250 GCon
->Logf(NAME_Debug
, " Gravity: %g", Gravity
);
251 GCon
->Logf(NAME_Debug
, " AirControl: %g", AirControl
);
252 GCon
->Logf(NAME_Debug
, " Flags: 0x%08x", Flags
);
253 GCon
->Logf(NAME_Debug
, " Flags2: 0x%08x", Flags2
);
254 GCon
->Logf(NAME_Debug
, " EnterTitlePatch: \"%s\"", *VStr(EnterTitlePatch
).quote());
255 GCon
->Logf(NAME_Debug
, " ExitTitlePatch: \"%s\"", *VStr(ExitTitlePatch
).quote());
256 GCon
->Logf(NAME_Debug
, " ParTime: %d", ParTime
);
257 GCon
->Logf(NAME_Debug
, " SuckTime: %d", SuckTime
);
258 GCon
->Logf(NAME_Debug
, " HorizWallShade: %d", HorizWallShade
);
259 GCon
->Logf(NAME_Debug
, " VertWallShade: %d", VertWallShade
);
260 GCon
->Logf(NAME_Debug
, " Infighting: %d", Infighting
);
261 GCon
->Logf(NAME_Debug
, " RedirectType: \"%s\"", *VStr(RedirectType
).quote());
262 GCon
->Logf(NAME_Debug
, " RedirectMap: \"%s\"", *VStr(RedirectMap
).quote());
263 GCon
->Logf(NAME_Debug
, " ExitPic: \"%s\"", *VStr(ExitPic
).quote());
264 GCon
->Logf(NAME_Debug
, " EnterPic: \"%s\"", *VStr(EnterPic
).quote());
265 GCon
->Logf(NAME_Debug
, " InterMusic: \"%s\"", *VStr(InterMusic
).quote());
266 GCon
->Logf(NAME_Debug
, " ExitText: \"%s\"", *VStr(ExitText
).quote());
267 GCon
->Logf(NAME_Debug
, " SecretExitText: \"%s\"", *VStr(SecretExitText
).quote());
268 GCon
->Logf(NAME_Debug
, " InterBackdrop: \"%s\"", *VStr(InterBackdrop
).quote());
269 for (auto &&sac
: SpecialActions
) {
270 GCon
->Log(NAME_Debug
, " --- special action ---");
271 GCon
->Logf(NAME_Debug
, " TypeName: \"%s\"", *VStr(sac
.TypeName
).quote());
272 GCon
->Logf(NAME_Debug
, " Special: %d (%d,%d,%d,%d,%d)", sac
.Special
, sac
.Args
[0], sac
.Args
[1], sac
.Args
[2], sac
.Args
[3], sac
.Args
[4]);
277 //==========================================================================
279 // P_SetupMapinfoPlayerClasses
281 //==========================================================================
282 void P_SetupMapinfoPlayerClasses () {
283 VClass
*PPClass
= VClass::FindClass("PlayerPawn");
285 GCon
->Logf(NAME_Warning
, "Can't find PlayerPawn class");
288 if (!flForcePlayerClass
.isEmpty()) {
289 VClass
*Class
= VClass::FindClassNoCase(*flForcePlayerClass
);
291 GCon
->Logf(NAME_Warning
, "Mode-ForcePlayerClass: No such class `%s`", *flForcePlayerClass
);
292 } else if (!Class
->IsChildOf(PPClass
)) {
293 GCon
->Logf(NAME_Warning
, "Mode-ForcePlayerClass: '%s' is not a player pawn class", *flForcePlayerClass
);
295 GGameInfo
->PlayerClasses
.Clear();
296 GGameInfo
->PlayerClasses
.Append(Class
);
297 GCon
->Logf(NAME_Init
, "mode-forced player class '%s'", *flForcePlayerClass
);
301 if (cli_NoMapinfoPlrClasses
> 0) return;
302 if (MapInfoPlayerClasses
.length() == 0) return;
303 GCon
->Logf(NAME_Init
, "setting up %d player class%s from mapinfo", MapInfoPlayerClasses
.length(), (MapInfoPlayerClasses
.length() != 1 ? "es" : ""));
304 GGameInfo
->PlayerClasses
.Clear();
305 for (int f
= 0; f
< MapInfoPlayerClasses
.length(); ++f
) {
306 VClass
*Class
= VClass::FindClassNoCase(*MapInfoPlayerClasses
[f
]);
308 GCon
->Logf(NAME_Warning
, "No player class '%s'", *MapInfoPlayerClasses
[f
]);
311 if (!Class
->IsChildOf(PPClass
)) {
312 GCon
->Logf(NAME_Warning
, "'%s' is not a player pawn class", *MapInfoPlayerClasses
[f
]);
315 GGameInfo
->PlayerClasses
.Append(Class
);
317 if (GGameInfo
->PlayerClasses
.length() == 0) Sys_Error("no valid classes found in MAPINFO playerclass replacement");
321 //==========================================================================
325 //==========================================================================
326 static void appendNumFixup (TMap
<int, SpawnEdFixup
> &arr
, VStr className
, int num
, int flags
=0, int special
=0, int arg1
=0, int arg2
=0, int arg3
=0, int arg4
=0, int arg5
=0) {
327 SpawnEdFixup
*fxp
= arr
.get(num
);
329 fxp
->ClassName
= className
;
331 fxp
->special
= special
;
342 fx
.special
= special
;
348 fx
.ClassName
= className
;
353 //==========================================================================
357 //==========================================================================
358 static void processNumFixups (const char *errname
, bool ismobj
, TMap
<int, SpawnEdFixup
> &fixups
) {
359 //GCon->Logf(NAME_Debug, "fixing '%s' (%d)", errname, fixups.count());
362 while (f
< list
.length()) {
363 mobjinfo_t
&nfo
= list
[f
];
364 SpawnEdFixup
*fxp
= fixups
.get(nfo
.DoomEdNum
);
366 SpawnEdFixup fix
= *fxp
;
367 VStr cname
= fxp
->ClassName
;
368 //GCon->Logf(NAME_Debug, " MAPINFO: class '%s' for %s got doomed num %d (got %d)", *cname, errname, fxp->num, nfo.DoomEdNum);
369 fixups
.del(nfo
.DoomEdNum
);
371 if (cname.length() == 0 || cname.ICmp("none") == 0) {
379 if (cname
.length() == 0 || cname
.ICmp("none") == 0) {
382 cls
= VClass::FindClassNoCase(*cname
);
383 if (!cls
) GCon
->Logf(NAME_Warning
, "MAPINFO: class '%s' for %s %d not found", *cname
, errname
, nfo
.DoomEdNum
);
386 nfo
.GameFilter
= GAME_Any
;
387 nfo
.flags
= fix
.flags
;
388 nfo
.special
= fix
.special
;
389 nfo
.args
[0] = fix
.args
[0];
390 nfo
.args
[1] = fix
.args
[1];
391 nfo
.args
[2] = fix
.args
[2];
392 nfo
.args
[3] = fix
.args
[3];
393 nfo
.args
[4] = fix
.args
[4];
398 //GCon->Logf(NAME_Debug, " appending '%s' (%d)", errname, fixups.count());
400 for (auto it
= fixups
.first(); it
; ++it
) {
401 SpawnEdFixup
*fxp
= &it
.getValue();
402 if (fxp
->num
<= 0) continue;
403 VStr cname
= fxp
->ClassName
;
404 //GCon->Logf(NAME_Debug, " MAPINFO0: class '%s' for %s got doomed num %d", *cname, errname, fxp->num);
407 if (cname
.length() == 0 || cname
.ICmp("none") == 0) {
410 cls
= VClass::FindClassNoCase(*cname
);
411 if (!cls
) GCon
->Logf(NAME_Warning
, "MAPINFO: class '%s' for %s %d not found", *cname
, errname
, fxp
->num
);
413 //GCon->Logf(NAME_Debug, " MAPINFO1: class '%s' for %s got doomed num %d", *cname, errname, fxp->num);
416 VClass::RemoveMObjId(fxp
->num
, GGameInfo
->GameFilterFlag
);
418 VClass::RemoveScriptId(fxp
->num
, GGameInfo
->GameFilterFlag
);
421 mobjinfo_t
*nfo
= (ismobj
? VClass::AllocMObjId(fxp
->num
, GGameInfo
->GameFilterFlag
, cls
) : VClass::AllocScriptId(fxp
->num
, GGameInfo
->GameFilterFlag
, cls
));
424 nfo
->flags
= fxp
->flags
;
425 nfo
->special
= fxp
->special
;
426 nfo
->args
[0] = fxp
->args
[0];
427 nfo
->args
[1] = fxp
->args
[1];
428 nfo
->args
[2] = fxp
->args
[2];
429 nfo
->args
[3] = fxp
->args
[3];
430 nfo
->args
[4] = fxp
->args
[4];
438 //==========================================================================
442 // returns -1 if not found
444 //==========================================================================
445 static int findSavedPar (VName map
) {
446 if (map
== NAME_None
) return -1;
447 for (int f
= partimes
.length()-1; f
>= 0; --f
) {
448 if (VStr::ICmp(*partimes
[f
].MapName
, *map
) == 0) return partimes
[f
].par
;
454 //==========================================================================
458 //==========================================================================
459 static int loadSkyTexture (VScriptParser
*sc
, VName name
, bool silent
=false) {
460 static TMapNC
<VName
, int> forceList
;
462 if (name
== NAME_None
) return GTextureManager
.DefaultTexture
;
464 VName loname
= VName(*name
, VName::AddLower
);
465 auto tidp
= forceList
.get(loname
);
466 if (tidp
) return *tidp
;
468 //int Tex = GTextureManager.NumForName(sc->Name8, TEXTYPE_Wall, false);
469 //info->Sky1Texture = GTextureManager.NumForName(sc->Name8, TEXTYPE_Wall, false);
470 int Tex
= GTextureManager
.CheckNumForName(name
, TEXTYPE_SkyMap
, true);
471 if (Tex
> 0) return Tex
;
473 Tex
= GTextureManager
.CheckNumForName(name
, TEXTYPE_Wall
, true);
475 forceList
.put(loname
, Tex
);
479 Tex
= GTextureManager
.CheckNumForName(name
, TEXTYPE_WallPatch
, false);
481 int LumpNum
= W_CheckNumForTextureFileName(*name
);
483 Tex
= GTextureManager
.FindTextureByLumpNum(LumpNum
);
485 VTexture
*T
= VTexture::CreateTexture(TEXTYPE_WallPatch
, LumpNum
);
486 if (!T
) T
= VTexture::CreateTexture(TEXTYPE_Any
, LumpNum
);
488 Tex
= GTextureManager
.AddTexture(T
);
493 LumpNum
= W_CheckNumForName(name
, WADNS_Patches
);
494 if (LumpNum
< 0) LumpNum
= W_CheckNumForTextureFileName(*name
);
496 Tex
= GTextureManager
.AddTexture(VTexture::CreateTexture(TEXTYPE_WallPatch
, LumpNum
));
498 // DooM:Complete has some patches in "sprites/"
499 LumpNum
= W_CheckNumForName(name
, WADNS_Sprites
);
500 if (LumpNum
>= 0) Tex
= GTextureManager
.AddTexture(VTexture::CreateTexture(TEXTYPE_Any
, LumpNum
));
505 if (Tex
< 0) Tex
= GTextureManager
.CheckNumForName(name
, TEXTYPE_Any
, false);
507 if (Tex
< 0) Tex
= GTextureManager
.AddPatch(name
, TEXTYPE_WallPatch
, true);
510 if (silent
) return -1;
511 miWarning(sc
, "sky '%s' not found; replaced with 'sky1'", *name
);
512 Tex
= GTextureManager
.CheckNumForName("sky1", TEXTYPE_SkyMap
, true);
513 if (Tex
< 0) Tex
= GTextureManager
.CheckNumForName("sky1", TEXTYPE_Wall
, true);
514 forceList
.put(loname
, Tex
);
516 //return GTextureManager.DefaultTexture;
519 forceList
.put(loname
, Tex
);
520 miWarning(sc
, "force-loaded sky '%s'", *name
);
525 //==========================================================================
529 //==========================================================================
530 static void LoadMapInfoLump (int Lump
, bool doFixups
=true) {
531 GCon
->Logf(NAME_Init
, "mapinfo file: '%s'", *W_FullLumpName(Lump
));
532 VScriptParser
*sc
= VScriptParser::NewWithLump(Lump
);
533 if (!sc
) Sys_Error("cannot load lump #%d: '%s'", Lump
, *W_FullLumpName(Lump
));
536 processNumFixups("DoomEdNum", true, DoomEdNumFixups
);
537 processNumFixups("SpawnNum", false, SpawnNumFixups
);
542 //==========================================================================
544 // LoadAllMapInfoLumpsInFile
546 // do this scanning fuckery, because some idiotic tools
547 // loves duplicate lumps
549 //==========================================================================
550 static void LoadAllMapInfoLumpsInFile (int miLump
, int zmiLump
, int vmiLump
) {
551 if (miLump
< 0 && zmiLump
< 0) return;
554 milname
= VName("vmapinfo", VName::Add
);
556 } else if (zmiLump
>= 0) {
557 milname
= VName("zmapinfo", VName::Add
);
559 vassert(miLump
>= 0);
561 milname
= NAME_mapinfo
;
563 vassert(zmiLump
>= 0);
564 int currFile
= W_LumpFile(zmiLump
);
565 bool wasLoaded
= false;
566 for (; zmiLump
>= 0; zmiLump
= W_IterateNS(zmiLump
, WADNS_Global
)) {
567 if (W_LumpFile(zmiLump
) != currFile
) break;
568 if (W_LumpName(zmiLump
) == milname
) {
570 LoadMapInfoLump(zmiLump
, false); // no fixups yet
573 // do fixups if somethig was loaded
575 processNumFixups("DoomEdNum", true, DoomEdNumFixups
);
576 processNumFixups("SpawnNum", false, SpawnNumFixups
);
581 //==========================================================================
585 //==========================================================================
586 static void LoadUmapinfoLump (int lump
) {
587 if (lump
< 0) return;
588 GCon
->Logf(NAME_Init
, "umapinfo file: '%s'", *W_FullLumpName(lump
));
589 VScriptParser
*sc
= VScriptParser::NewWithLump(lump
);
590 if (!sc
) Sys_Error("cannot load lump #%d: '%s'", lump
, *W_FullLumpName(lump
));
595 //==========================================================================
599 //==========================================================================
600 void InitMapInfo () {
601 // use "zmapinfo" if it is present?
602 bool zmapinfoAllowed
= (cli_NoZMapinfo
>= 0);
603 if (!zmapinfoAllowed
) GCon
->Logf(NAME_Init
, "zmapinfo parsing disabled by user");
605 int lastMapinfoFile
= -1; // haven't seen yet
606 int lastMapinfoLump
= -1; // haven't seen yet
607 int lastVMapinfoLump
= -1; // haven't seen yet
608 int lastZMapinfoLump
= -1; // haven't seen yet
609 int lastUmapinfoLump
= -1; // haven't seen yet
610 bool doSkipFile
= false;
611 VName nameVMI
= VName("vmapinfo", VName::Add
);
612 VName nameZMI
= VName("zmapinfo", VName::Add
);
613 VName nameUMI
= VName("umapinfo", VName::Add
);
615 TArray
<int> fileKeyconfLump
;
617 for (int Lump
= W_IterateNS(-1, WADNS_Global
); Lump
>= 0; Lump
= W_IterateNS(Lump
, WADNS_Global
)) {
618 int currFile
= W_LumpFile(Lump
);
620 if (currFile
== lastMapinfoFile
) continue;
621 doSkipFile
= false; // just in case
623 // if we hit another file, load last seen [z]mapinfo lump
624 if (currFile
!= lastMapinfoFile
) {
625 //GCon->Logf(NAME_Debug, "*** new archive at '%s' ***", *W_FullLumpName(Lump));
626 LoadAllMapInfoLumpsInFile(lastMapinfoLump
, lastZMapinfoLump
, lastVMapinfoLump
);
627 if (lastMapinfoLump
< 0 && lastZMapinfoLump
< 0 && lastVMapinfoLump
< 0 && lastUmapinfoLump
>= 0) {
628 LoadUmapinfoLump(lastUmapinfoLump
);
630 // reset/update remembered lump indices
631 lastMapinfoFile
= currFile
;
632 lastMapinfoLump
= lastVMapinfoLump
= lastZMapinfoLump
= lastUmapinfoLump
= -1; // haven't seen yet
633 // load keyconfs from previous files
634 for (auto &&klmp
: fileKeyconfLump
) VCommand::LoadKeyconfLump(klmp
);
635 fileKeyconfLump
.resetNoDtor();
637 doSkipFile
= !W_IsWadPK3File(currFile
);
638 if (doSkipFile
) continue;
641 if (W_LumpName(Lump
) == NAME_keyconf
) fileKeyconfLump
.append(Lump
);
642 // remember last seen [z]mapinfo lump
643 if (lastMapinfoLump
< 0 && W_LumpName(Lump
) == NAME_mapinfo
) lastMapinfoLump
= Lump
;
644 if (zmapinfoAllowed
&& lastZMapinfoLump
< 0 && W_LumpName(Lump
) == nameZMI
) lastZMapinfoLump
= Lump
;
645 if (lastVMapinfoLump
< 0 && W_LumpName(Lump
) == nameVMI
) lastVMapinfoLump
= Lump
;
646 if (lastUmapinfoLump
< 0 && W_LumpName(Lump
) == nameUMI
) lastUmapinfoLump
= Lump
;
649 // load last seen mapinfos
650 LoadAllMapInfoLumpsInFile(lastMapinfoLump
, lastZMapinfoLump
, lastVMapinfoLump
);
651 if (lastMapinfoLump
< 0 && lastZMapinfoLump
< 0 && lastVMapinfoLump
< 0 && lastUmapinfoLump
>= 0) {
652 LoadUmapinfoLump(lastUmapinfoLump
);
655 mapinfoParsed
= true;
657 // load latest keyconfs
658 for (auto &&klmp
: fileKeyconfLump
) VCommand::LoadKeyconfLump(klmp
);
659 fileKeyconfLump
.resetNoDtor();
661 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
662 if (VStr(MapInfo
[i
].NextMap
).StartsWith("&wt@")) {
663 MapInfo
[i
].NextMap
= P_TranslateMap(VStr::atoi(*MapInfo
[i
].NextMap
+4));
665 if (VStr(MapInfo
[i
].SecretMap
).StartsWith("&wt@")) {
666 MapInfo
[i
].SecretMap
= P_TranslateMap(VStr::atoi(*MapInfo
[i
].SecretMap
+4));
670 for (int i
= 0; i
< EpisodeDefs
.length(); ++i
) {
671 if (VStr(EpisodeDefs
[i
].Name
).StartsWith("&wt@")) {
672 EpisodeDefs
[i
].Name
= P_TranslateMap(VStr::atoi(*EpisodeDefs
[i
].Name
+4));
674 if (VStr(EpisodeDefs
[i
].TeaserName
).StartsWith("&wt@")) {
675 EpisodeDefs
[i
].TeaserName
= P_TranslateMap(VStr::atoi(*EpisodeDefs
[i
].TeaserName
+4));
679 // set up default map info returned for maps that have not defined in MAPINFO
680 memset((void *)&DefaultMap
, 0, sizeof(DefaultMap
));
681 DefaultMap
.Name
= "Unnamed";
682 DefaultMap
.Sky1Texture
= loadSkyTexture(nullptr, "sky1"); //GTextureManager.CheckNumForName("sky1", TEXTYPE_Wall, true, true);
683 DefaultMap
.Sky2Texture
= DefaultMap
.Sky1Texture
;
684 DefaultMap
.FadeTable
= NAME_colormap
;
685 DefaultMap
.HorizWallShade
= -8;
686 DefaultMap
.VertWallShade
= 8;
687 //GCon->Logf(NAME_Debug, "*** DEFAULT MAP: Sky1Texture=%d", DefaultMap.Sky1Texture);
689 // we don't need it anymore
692 if (hasCustomDamageFactors
) SV_ReplaceCustomDamageFactors();
696 //==========================================================================
700 //==========================================================================
701 static void SetMapDefaults (VMapInfo
&Info
) {
702 Info
.LumpName
= NAME_None
;
707 Info
.NextMap
= NAME_None
;
708 Info
.SecretMap
= NAME_None
;
709 Info
.SongLump
= NAME_None
;
710 //Info.Sky1Texture = GTextureManager.DefaultTexture;
711 //Info.Sky2Texture = GTextureManager.DefaultTexture;
712 Info
.Sky1Texture
= loadSkyTexture(nullptr, "sky1"); //GTextureManager.CheckNumForName("sky1", TEXTYPE_Wall, true, true);
713 Info
.Sky2Texture
= Info
.Sky1Texture
;
714 //Info.Sky2Texture = GTextureManager.DefaultTexture;
715 Info
.Sky1ScrollDelta
= 0;
716 Info
.Sky2ScrollDelta
= 0;
717 Info
.SkyBox
= NAME_None
;
718 Info
.FadeTable
= NAME_colormap
;
724 Info
.Flags2
= (gm_default_pain_limit
? VLevelInfo::LIF2_CompatLimitPain
: 0);
725 Info
.EnterTitlePatch
= NAME_None
;
726 Info
.ExitTitlePatch
= NAME_None
;
729 Info
.HorizWallShade
= -8;
730 Info
.VertWallShade
= 8;
732 Info
.SpecialActions
.Clear();
733 Info
.RedirectType
= NAME_None
;
734 Info
.RedirectMap
= NAME_None
;
735 Info
.ExitPic
= NAME_None
;
736 Info
.EnterPic
= NAME_None
;
737 Info
.InterMusic
= NAME_None
;
738 Info
.ExitText
= VStr::EmptyString
;
739 Info
.SecretExitText
= VStr::EmptyString
;
740 Info
.InterBackdrop
= NAME_None
;
742 if (GGameInfo
->Flags
&VGameInfo::GIF_DefaultLaxMonsterActivation
) {
743 Info
.Flags2
|= VLevelInfo::LIF2_LaxMonsterActivation
;
748 //==========================================================================
752 //==========================================================================
753 static VName
ParseNextMapName (VScriptParser
*sc
, bool HexenMode
) {
754 if (sc
->CheckNumber()) {
755 if (HexenMode
) return va("&wt@%02d", sc
->Number
);
756 return va("map%02d", sc
->Number
);
758 if (sc
->Check("endbunny")) return "EndGameBunny";
759 if (sc
->Check("endcast")) return "EndGameCast";
760 if (sc
->Check("enddemon")) return "EndGameDemon";
761 if (sc
->Check("endchess")) return "EndGameChess";
762 if (sc
->Check("endunderwater")) return "EndGameUnderwater";
763 if (sc
->Check("endbuystrife")) return "EndGameBuyStrife";
764 if (sc
->Check("endpic")) {
767 return va("EndGameCustomPic%s", *sc
->Name8
);
770 if (sc
->String
.ToLower().StartsWith("endgame")) {
771 switch (sc
->String
[7]) {
772 case '1': return "EndGamePic1";
773 case '2': return "EndGamePic2";
774 case '3': return "EndGameBunny";
775 case 'c': case 'C': return "EndGameCast";
776 case 'w': case 'W': return "EndGameUnderwater";
777 case 's': case 'S': return "EndGameStrife";
778 default: return "EndGamePic3";
781 return VName(*sc
->String
, VName::AddLower8
);
785 //==========================================================================
789 //==========================================================================
790 static void DoCompatFlag (VScriptParser
*sc
, VMapInfo
*info
, int Flag
, bool newFormat
) {
794 if (sc
->CheckNumber()) Set
= sc
->Number
;
795 } else if (sc
->CheckNumber()) {
800 info
->Flags2
|= Flag
;
802 info
->Flags2
&= ~Flag
;
808 //==========================================================================
810 // skipUnimplementedCommand
812 //==========================================================================
813 static void skipUnimplementedCommand (VScriptParser
*sc
, bool wantArg
) {
814 VStr cmd
= sc
->String
;
815 if (sc
->Check("=")) {
816 miWarning(sc
, "Unimplemented command '%s'", *cmd
);
818 while (sc
->Check(",")) {
819 if (sc
->Check("}")) { sc
->UnGet(); break; }
820 if (sc
->AtEnd()) break;
823 } else if (wantArg
) {
824 miWarning(sc
, "Unimplemented old command '%s'", *cmd
);
827 miWarning(sc
, "Unimplemented flag '%s'", *cmd
);
832 // ////////////////////////////////////////////////////////////////////////// //
833 MAPINFOCMD(levelnum
) {
835 if (newFormat
) sc
->Expect("=");
837 info
->LevelNum
= sc
->Number
;
841 // ////////////////////////////////////////////////////////////////////////// //
845 if (newFormat
) sc
->Expect("=");
850 // ////////////////////////////////////////////////////////////////////////// //
851 MAPINFOCMD(cluster
) {
853 if (newFormat
) sc
->Expect("=");
855 info
->Cluster
= sc
->Number
;
856 if (P_GetClusterDef(info
->Cluster
) == &DefaultClusterDef
) {
857 // add empty cluster def if it doesn't exist yet
858 VClusterDef
&C
= ClusterDefs
.Alloc();
859 C
.Cluster
= info
->Cluster
;
861 C
.EnterText
= VStr();
865 if (HexenMode
) C
.Flags
|= CLUSTERF_Hub
;
869 // ////////////////////////////////////////////////////////////////////////// //
870 MAPINFOCMD(warptrans
) {
872 if (newFormat
) sc
->Expect("=");
874 info
->WarpTrans
= sc
->Number
;
877 // ////////////////////////////////////////////////////////////////////////// //
880 if (newFormat
) sc
->Expect("=");
881 info
->NextMap
= ParseNextMapName(sc
, HexenMode
);
882 // hack for "complete"
883 if (sc
->Check("{")) {
884 info
->NextMap
= "endgamec";
885 sc
->SkipBracketed(true); // bracket eaten
886 } else if (newFormat
&& sc
->Check(",")) {
888 // check for more commas?
892 // ////////////////////////////////////////////////////////////////////////// //
895 if (newFormat
) sc
->Expect("=");
896 info
->SecretMap
= ParseNextMapName(sc
, HexenMode
);
899 MAPINFOCMD(secretnext
) {
901 if (newFormat
) sc
->Expect("=");
902 info
->SecretMap
= ParseNextMapName(sc
, HexenMode
);
905 // ////////////////////////////////////////////////////////////////////////// //
908 wasSky1Sky2
|= WSK_WAS_SKY1
;
909 const VTextLocation loc
= sc
->GetLoc();
910 if (newFormat
) sc
->Expect("=");
912 //info->Sky1Texture = GTextureManager.NumForName(sc->Name, TEXTYPE_Wall, false);
913 VName skbname
= R_HasNamedSkybox(sc
->String
);
914 if (skbname
!= NAME_None
) {
915 //k8: ok, this may be done to support sourceports that cannot into skyboxes
916 miWarning(loc
, "sky1 '%s' is actually a skybox (this is mostly harmless)", *sc
->String
);
917 info
->SkyBox
= skbname
;
918 info
->Sky1Texture
= GTextureManager
.DefaultTexture
;
919 info
->Sky2Texture
= GTextureManager
.DefaultTexture
;
920 info
->Sky1ScrollDelta
= 0;
921 info
->Sky2ScrollDelta
= 0;
922 //GCon->Logf(NAME_Init, "using gz skybox '%s'", *skbname);
923 if (!sc
->IsAtEol()) {
925 sc
->ExpectFloatWithSign();
926 if (HexenMode
) sc
->Float
/= 256.0f
;
927 if (sc
->Float
!= 0) miWarning(loc
, "ignoring sky scroll for skybox '%s' (this is mostly harmless)", *skbname
);
930 info
->SkyBox
= NAME_None
;
931 info
->Sky1Texture
= loadSkyTexture(sc
, sc
->Name
);
932 info
->Sky1ScrollDelta
= 0;
934 if (!sc
->IsAtEol()) {
936 sc
->ExpectFloatWithSign();
937 if (HexenMode
) sc
->Float
/= 256.0f
;
938 info
->Sky1ScrollDelta
= sc
->Float
*35.0f
;
941 if (!sc
->IsAtEol()) {
943 sc
->ExpectFloatWithSign();
944 if (HexenMode
) sc
->Float
/= 256.0f
;
945 info
->Sky1ScrollDelta
= sc
->Float
*35.0f
;
951 // ////////////////////////////////////////////////////////////////////////// //
954 wasSky1Sky2
|= WSK_WAS_SKY2
;
955 if (newFormat
) sc
->Expect("=");
957 //info->Sky2Texture = GTextureManager.NumForName(sc->Name8, TEXTYPE_Wall, false);
958 //info->SkyBox = NAME_None; //k8:required or not???
959 info
->Sky2Texture
= loadSkyTexture(sc
, sc
->Name
);
960 info
->Sky2ScrollDelta
= 0;
962 if (!sc
->IsAtEol()) {
964 sc
->ExpectFloatWithSign();
965 if (HexenMode
) sc
->Float
/= 256.0f
;
966 info
->Sky1ScrollDelta
= sc
->Float
*35.0f
;
969 if (!sc
->IsAtEol()) {
971 sc
->ExpectFloatWithSign();
972 if (HexenMode
) sc
->Float
/= 256.0f
;
973 info
->Sky2ScrollDelta
= sc
->Float
*35.0f
;
978 // ////////////////////////////////////////////////////////////////////////// //
981 const VTextLocation loc
= sc
->GetLoc();
982 if (newFormat
) sc
->Expect("=");
984 info
->Sky1ScrollDelta
= 0;
985 info
->Sky2ScrollDelta
= 0;
986 VName skbname
= R_HasNamedSkybox(sc
->String
);
987 if (skbname
!= NAME_None
) {
988 info
->SkyBox
= skbname
;
989 info
->Sky1Texture
= GTextureManager
.DefaultTexture
;
990 info
->Sky2Texture
= GTextureManager
.DefaultTexture
;
992 if (cli_MapperIsIdiot
> 0) {
993 miWarning(loc
, "skybox '%s' not found (mapper is idiot)!", *sc
->String
);
995 sc
->Error(va("skybox '%s' not found (this mapinfo is broken)", *sc
->String
));
997 info
->SkyBox
= NAME_None
;
998 info
->Sky1Texture
= loadSkyTexture(sc
, VName(*sc
->String
, VName::AddLower8
));
999 info
->Sky2Texture
= info
->Sky1Texture
;
1003 // ////////////////////////////////////////////////////////////////////////// //
1004 MAPINFOCMD(skyrotate
) {
1007 miWarning(sc
, "\"skyrotate\" command is not supported yet");
1008 if (newFormat
) sc
->Expect("=");
1009 sc
->ExpectFloatWithSign();
1010 if (sc
->Check(",")) sc
->ExpectFloatWithSign();
1011 if (sc
->Check(",")) sc
->ExpectFloatWithSign();
1014 // ////////////////////////////////////////////////////////////////////////// //
1015 MAPINFOCMD(doublesky
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_DoubleSky
; }
1016 MAPINFOCMD(lightning
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_Lightning
; }
1017 MAPINFOCMD(forcenoskystretch
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_ForceNoSkyStretch
; }
1018 MAPINFOCMD(skystretch
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_ForceNoSkyStretch
; }
1019 MAPINFOCMD(map07special
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_Map07Special
; }
1020 MAPINFOCMD(baronspecial
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_BaronSpecial
; }
1021 MAPINFOCMD(cyberdemonspecial
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_CyberDemonSpecial
; }
1022 MAPINFOCMD(spidermastermindspecial
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_SpiderMastermindSpecial
; }
1023 MAPINFOCMD(minotaurspecial
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_MinotaurSpecial
; }
1024 MAPINFOCMD(dsparilspecial
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_DSparilSpecial
; }
1025 MAPINFOCMD(ironlichspecial
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_IronLichSpecial
; }
1026 MAPINFOCMD(specialaction_exitlevel
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~(VLevelInfo::LIF_SpecialActionOpenDoor
|VLevelInfo::LIF_SpecialActionLowerFloor
); }
1027 MAPINFOCMD(specialaction_opendoor
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_SpecialActionLowerFloor
; info
->Flags
|= VLevelInfo::LIF_SpecialActionOpenDoor
; }
1028 MAPINFOCMD(specialaction_lowerfloor
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_SpecialActionLowerFloor
; info
->Flags
&= ~VLevelInfo::LIF_SpecialActionOpenDoor
; }
1029 MAPINFOCMD(specialaction_killmonsters
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_SpecialActionKillMonsters
; }
1030 MAPINFOCMD(specialaction_blazedoor
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; } //FIXME
1031 MAPINFOCMD(intermission
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_NoIntermission
; }
1032 MAPINFOCMD(nointermission
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_NoIntermission
; }
1033 MAPINFOCMD(nosoundclipping
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; /* ignored */ }
1034 MAPINFOCMD(noinventorybar
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; /* ignored */ }
1035 MAPINFOCMD(allowmonstertelefrags
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_AllowMonsterTelefrags
; }
1036 MAPINFOCMD(noallies
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_NoAllies
; }
1037 MAPINFOCMD(fallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~(VLevelInfo::LIF_OldFallingDamage
|VLevelInfo::LIF_StrifeFallingDamage
); info
->Flags
|= VLevelInfo::LIF_FallingDamage
; }
1038 MAPINFOCMD(oldfallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~(VLevelInfo::LIF_FallingDamage
|VLevelInfo::LIF_StrifeFallingDamage
); info
->Flags
|= VLevelInfo::LIF_OldFallingDamage
; }
1039 MAPINFOCMD(forcefallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~(VLevelInfo::LIF_FallingDamage
|VLevelInfo::LIF_StrifeFallingDamage
); info
->Flags
|= VLevelInfo::LIF_OldFallingDamage
; }
1040 MAPINFOCMD(strifefallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~(VLevelInfo::LIF_OldFallingDamage
|VLevelInfo::LIF_FallingDamage
); info
->Flags
|= VLevelInfo::LIF_StrifeFallingDamage
; }
1041 MAPINFOCMD(nofallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~(VLevelInfo::LIF_OldFallingDamage
|VLevelInfo::LIF_StrifeFallingDamage
|VLevelInfo::LIF_FallingDamage
); }
1042 MAPINFOCMD(monsterfallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_MonsterFallingDamage
; }
1043 MAPINFOCMD(nomonsterfallingdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_MonsterFallingDamage
; }
1044 MAPINFOCMD(deathslideshow
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_DeathSlideShow
; }
1045 MAPINFOCMD(allowfreelook
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_NoFreelook
; }
1046 MAPINFOCMD(nofreelook
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_NoFreelook
; }
1047 MAPINFOCMD(allowjump
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_NoJump
; }
1048 MAPINFOCMD(allowcrouch
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
&= ~VLevelInfo::LIF2_NoCrouch
; }
1049 MAPINFOCMD(nojump
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_NoJump
; }
1050 MAPINFOCMD(nocrouch
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
|= VLevelInfo::LIF2_NoCrouch
; }
1051 MAPINFOCMD(resethealth
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
|= VLevelInfo::LIF2_ResetHealth
; }
1052 MAPINFOCMD(resetinventory
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
|= VLevelInfo::LIF2_ResetInventory
; }
1053 MAPINFOCMD(resetitems
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
|= VLevelInfo::LIF2_ResetItems
; }
1054 MAPINFOCMD(noautosequences
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_NoAutoSndSeq
; }
1055 MAPINFOCMD(activateowndeathspecials
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_ActivateOwnSpecial
; }
1056 MAPINFOCMD(killeractivatesdeathspecials
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_ActivateOwnSpecial
; }
1057 MAPINFOCMD(missilesactivateimpactlines
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_MissilesActivateImpact
; }
1058 MAPINFOCMD(missileshootersactivetimpactlines
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_MissilesActivateImpact
; }
1059 MAPINFOCMD(filterstarts
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_FilterStarts
; }
1060 MAPINFOCMD(infiniteflightpowerup
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_InfiniteFlightPowerup
; }
1061 MAPINFOCMD(noinfiniteflightpowerup
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
&= ~VLevelInfo::LIF_InfiniteFlightPowerup
; }
1062 MAPINFOCMD(clipmidtextures
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_ClipMidTex
; /*sc->Message("ClipMidTextures is ignored");*/ }
1063 MAPINFOCMD(wrapmidtextures
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_WrapMidTex
; sc
->Message("WrapMidTextures is ignored"); }
1064 MAPINFOCMD(keepfullinventory
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags
|= VLevelInfo::LIF_KeepFullInventory
; }
1065 MAPINFOCMD(additive_scrollers
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatBoomScroll
, newFormat
); }
1066 MAPINFOCMD(checkswitchrange
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
|= VLevelInfo::LIF2_CheckSwitchRange
; info
->Flags2
&= ~VLevelInfo::LIF2_NoCheckSwitchRange
; }
1067 MAPINFOCMD(nocheckswitchrange
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Flags2
&= ~VLevelInfo::LIF2_CheckSwitchRange
; info
->Flags2
|= VLevelInfo::LIF2_NoCheckSwitchRange
; }
1068 MAPINFOCMD(compat_shorttex
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatShortTex
, newFormat
); }
1069 MAPINFOCMD(compat_stairs
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatStairs
, newFormat
); }
1070 MAPINFOCMD(compat_limitpain
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatLimitPain
, newFormat
); }
1071 MAPINFOCMD(compat_nopassover
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatNoPassOver
, newFormat
); }
1072 MAPINFOCMD(compat_notossdrops
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatNoTossDrops
, newFormat
); }
1073 MAPINFOCMD(compat_useblocking
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatUseBlocking
, newFormat
); }
1074 MAPINFOCMD(compat_nodoorlight
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatNoDoorLight
, newFormat
); }
1075 MAPINFOCMD(compat_ravenscroll
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatRavenScroll
, newFormat
); }
1076 MAPINFOCMD(compat_soundtarget
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatSoundTarget
, newFormat
); }
1077 MAPINFOCMD(compat_dehhealth
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatDehHealth
, newFormat
); }
1078 MAPINFOCMD(compat_trace
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatTrace
, newFormat
); }
1079 MAPINFOCMD(compat_dropoff
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatDropOff
, newFormat
); }
1080 MAPINFOCMD(compat_boomscroll
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatBoomScroll
, newFormat
); }
1081 MAPINFOCMD(compat_invisibility
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, VLevelInfo::LIF2_CompatInvisibility
, newFormat
); }
1082 MAPINFOCMD(compat_sectorsounds
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, 0, newFormat
); }
1083 MAPINFOCMD(compat_crossdropoff
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; DoCompatFlag(sc
, info
, 0, newFormat
); }
1085 // ////////////////////////////////////////////////////////////////////////// //
1086 MAPINFOCMD(noinfighting
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Infighting
= -1; }
1087 MAPINFOCMD(normalinfighting
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Infighting
= 0; }
1088 MAPINFOCMD(totalinfighting
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->Infighting
= 1; }
1090 // ////////////////////////////////////////////////////////////////////////// //
1091 MAPINFOCMD(fadetable
) {
1093 if (newFormat
) sc
->Expect("=");
1095 info
->FadeTable
= sc
->Name8
;
1098 // ////////////////////////////////////////////////////////////////////////// //
1101 if (newFormat
) sc
->Expect("=");
1103 info
->Fade
= M_ParseColor(*sc
->String
);
1106 // ////////////////////////////////////////////////////////////////////////// //
1107 MAPINFOCMD(outsidefog
) {
1109 if (newFormat
) sc
->Expect("=");
1111 info
->OutsideFog
= M_ParseColor(*sc
->String
);
1114 // ////////////////////////////////////////////////////////////////////////// //
1117 if (newFormat
) sc
->Expect("=");
1118 //sc->ExpectName8();
1119 //info->SongLump = sc->Name8;
1121 info
->SongLump
= sc
->Name
;
1122 const char *nn
= *sc
->Name
;
1125 if (nn
[0] && GLanguage
.HasTranslation(nn
)) {
1126 info
->SongLump
= VName(*GLanguage
[nn
], VName::AddLower
);
1131 // ////////////////////////////////////////////////////////////////////////// //
1132 MAPINFOCMD(cdtrack
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*info->CDTrack = sc->Number;*/ }
1133 MAPINFOCMD(cd_start_track
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*cd_NonLevelTracks[CD_STARTTRACK] = sc->Number;*/ }
1134 MAPINFOCMD(cd_end1_track
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*cd_NonLevelTracks[CD_END1TRACK] = sc->Number;*/ }
1135 MAPINFOCMD(cd_end2_track
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*cd_NonLevelTracks[CD_END2TRACK] = sc->Number;*/ }
1136 MAPINFOCMD(cd_end3_track
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*cd_NonLevelTracks[CD_END3TRACK] = sc->Number;*/ }
1137 MAPINFOCMD(cd_intermission_track
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*cd_NonLevelTracks[CD_INTERTRACK] = sc->Number;*/ }
1138 MAPINFOCMD(cd_title_track
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; if (newFormat
) sc
->Expect("="); sc
->ExpectNumber(); /*cd_NonLevelTracks[CD_TITLETRACK] = sc->Number;*/ }
1139 MAPINFOCMD(cdid
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1141 // ////////////////////////////////////////////////////////////////////////// //
1142 MAPINFOCMD(gravity
) {
1143 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1144 if (newFormat
) sc
->Expect("=");
1146 if (sc
->Float
<= 0.0 || sc
->Float
> 100000.0f
) {
1147 sc
->Messagef("ignored invalid gravity value %g", sc
->Float
);
1149 info
->Gravity
= sc
->Float
;
1153 // ////////////////////////////////////////////////////////////////////////// //
1154 MAPINFOCMD(aircontrol
) {
1155 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1156 if (newFormat
) sc
->Expect("=");
1158 if (sc
->Float
< 0.0 || sc
->Float
> 10.0f
) {
1159 sc
->Messagef("ignored invalid air control value %g", sc
->Float
);
1161 info
->AirControl
= sc
->Float
;
1165 // ////////////////////////////////////////////////////////////////////////// //
1166 MAPINFOCMD(titlepatch
) {
1167 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1168 //FIXME: quoted string is a textual level name
1169 if (newFormat
) sc
->Expect("=");
1170 sc
->ExpectName8Def(NAME_None
);
1171 info
->EnterTitlePatch
= info
->ExitTitlePatch
= sc
->Name8
;
1174 // ////////////////////////////////////////////////////////////////////////// //
1176 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1177 if (newFormat
) sc
->Expect("=");
1179 info
->ParTime
= sc
->Number
;
1182 // ////////////////////////////////////////////////////////////////////////// //
1183 MAPINFOCMD(sucktime
) {
1184 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1185 if (newFormat
) sc
->Expect("=");
1187 info
->SuckTime
= sc
->Number
;
1190 // ////////////////////////////////////////////////////////////////////////// //
1191 MAPINFOCMD(vertwallshade
) {
1192 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1193 if (newFormat
) sc
->Expect("=");
1195 info
->VertWallShade
= midval(-128, sc
->Number
, 127);
1198 // ////////////////////////////////////////////////////////////////////////// //
1199 MAPINFOCMD(horizwallshade
) {
1200 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1201 if (newFormat
) sc
->Expect("=");
1203 info
->HorizWallShade
= midval(-128, sc
->Number
, 127);
1206 // ////////////////////////////////////////////////////////////////////////// //
1207 MAPINFOCMD(specialaction
) {
1208 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1209 if (newFormat
) sc
->Expect("=");
1210 VMapSpecialAction
&A
= info
->SpecialActions
.Alloc();
1211 //sc->SetCMode(true);
1213 A
.TypeName
= *sc
->String
.ToLower();
1216 A
.Special
= FindScriptLineSpecialByName(sc
->String
);
1217 if (!A
.Special
) miWarning(sc
, "Unknown action special '%s'", *sc
->String
);
1218 memset(A
.Args
, 0, sizeof(A
.Args
));
1219 for (int i
= 0; i
< 5 && sc
->Check(","); ++i
) {
1221 A
.Args
[i
] = sc
->Number
;
1225 // ////////////////////////////////////////////////////////////////////////// //
1226 MAPINFOCMD(redirect
) {
1227 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1228 if (newFormat
) sc
->Expect("=");
1230 info
->RedirectType
= *sc
->String
.ToLower();
1231 info
->RedirectMap
= ParseNextMapName(sc
, HexenMode
);
1234 // ////////////////////////////////////////////////////////////////////////// //
1235 MAPINFOCMD(strictmonsteractivation
) {
1236 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1237 info
->Flags2
&= ~VLevelInfo::LIF2_LaxMonsterActivation
;
1238 info
->Flags2
|= VLevelInfo::LIF2_HaveMonsterActivation
;
1241 // ////////////////////////////////////////////////////////////////////////// //
1242 MAPINFOCMD(laxmonsteractivation
) {
1243 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1244 info
->Flags2
|= VLevelInfo::LIF2_LaxMonsterActivation
;
1245 info
->Flags2
|= VLevelInfo::LIF2_HaveMonsterActivation
;
1248 // ////////////////////////////////////////////////////////////////////////// //
1249 MAPINFOCMD(interpic
) {
1250 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1251 if (newFormat
) sc
->Expect("=");
1252 //sc->ExpectName8();
1254 info
->ExitPic
= *sc
->String
.ToLower();
1257 // ////////////////////////////////////////////////////////////////////////// //
1258 MAPINFOCMD(enterpic
) {
1259 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1260 if (newFormat
) sc
->Expect("=");
1261 //sc->ExpectName8();
1263 info
->EnterPic
= *sc
->String
.ToLower();
1266 // ////////////////////////////////////////////////////////////////////////// //
1267 MAPINFOCMD(exitpic
) {
1268 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1269 if (newFormat
) sc
->Expect("=");
1270 //sc->ExpectName8();
1272 info
->ExitPic
= *sc
->String
.ToLower();
1275 // ////////////////////////////////////////////////////////////////////////// //
1276 MAPINFOCMD(intermusic
) {
1277 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1278 if (newFormat
) sc
->Expect("=");
1280 info
->InterMusic
= *sc
->String
.ToLower();
1283 // ////////////////////////////////////////////////////////////////////////// //
1284 MAPINFOCMD(background
) {
1285 (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
;
1286 sc
->Message("'background' mapinfo command is not supported");
1287 if (newFormat
) sc
->Expect("=");
1288 //sc->ExpectName8();
1292 // ////////////////////////////////////////////////////////////////////////// //
1293 MAPINFOCMD(airsupply
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1294 MAPINFOCMD(sndseq
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1295 MAPINFOCMD(sndinfo
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1296 MAPINFOCMD(soundinfo
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1297 MAPINFOCMD(bordertexture
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1298 MAPINFOCMD(f1
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1299 MAPINFOCMD(teamdamage
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1300 MAPINFOCMD(fogdensity
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1301 MAPINFOCMD(outsidefogdensity
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1302 MAPINFOCMD(skyfog
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1303 MAPINFOCMD(translator
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; sc
->MessageErr(va("*** map '%s' contains translator lump, it may not work!", *info
->LumpName
)); skipUnimplementedCommand(sc
, true); }
1304 MAPINFOCMD(lightmode
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; skipUnimplementedCommand(sc
, true); }
1305 MAPINFOCMD(smoothlighting
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->FakeContrast
= 1; }
1306 MAPINFOCMD(evenlighting
) { (void)HexenMode
; (void)info
; (void)newFormat
; (void)sc
; info
->FakeContrast
= 2; }
1309 //==========================================================================
1311 // FixSkyTexturesHack
1313 // another zdoom hack: check for "sky_maplump" sky texture
1315 //==========================================================================
1316 static void FixOneSkyTextureHack (VScriptParser
*sc
, VMapInfo
*info
, int skynum
, vint32
&tx
) {
1319 //GCon->Logf(NAME_Debug, "map '%s': sky1 '%s'", *info->LumpName, *GTextureManager.GetTextureName(tx));
1320 VName skn
= VName(*(VStr(*GTextureManager
.GetTextureName(tx
))+"_"+(*info
->LumpName
)), VName::AddLower
);
1321 if (VStr::length(*skn
) <= 8) {
1322 //GCon->Logf(NAME_Debug, "map '%s': trying sky1 '%s'", *info->LumpName, *skn);
1323 int tt
= loadSkyTexture(sc
, skn
, true);
1325 GCon
->Logf(NAME_Debug
, "map '%s': sky%d '%s' replaced with '%s'", *info
->LumpName
, skynum
, *GTextureManager
.GetTextureName(tx
), *GTextureManager
.GetTextureName(tt
));
1331 skn
= VName(*(VStr("sky_")+(*info
->LumpName
)), VName::AddLower
);
1332 if (VStr::length(*skn
) <= 8) {
1333 //GCon->Logf(NAME_Debug, "map '%s': trying sky1 '%s'", *info->LumpName, *skn);
1334 int tt
= loadSkyTexture(sc
, skn
, true);
1336 GCon
->Logf(NAME_Debug
, "map '%s': sky%d '%s' replaced with '%s'", *info
->LumpName
, skynum
, *GTextureManager
.GetTextureName(tx
), *GTextureManager
.GetTextureName(tt
));
1344 //==========================================================================
1346 // FixSkyTexturesHack
1348 // another zdoom hack: check for "sky_maplump" sky texture
1350 //==========================================================================
1351 static void FixSkyTexturesHack (VScriptParser
*sc
, VMapInfo
*info
) {
1352 FixOneSkyTextureHack(sc
, info
, 1, info
->Sky1Texture
);
1353 //FixOneSkyTextureHack(sc, info, 2, info->Sky2Texture);
1355 // if we have "lightning", but no "sky2", make "sky2" equal to "sky1" (otherwise the sky may flicker)
1356 // actually, always do this, because why not?
1357 if (/*(info->Flags&VLevelInfo::LIF_Lightning) != 0 &&*/ (wasSky1Sky2
&WSK_WAS_SKY2
) == 0) {
1358 // has lighting, but no second sky; force second sky to be the same as the first one
1359 // (but only if the first one is not a skybox)
1360 if (info
->SkyBox
== NAME_None
) {
1361 info
->Sky2Texture
= info
->Sky1Texture
;
1362 info
->Sky2ScrollDelta
= info
->Sky1ScrollDelta
;
1368 //==========================================================================
1372 //==========================================================================
1373 static void ParseMapCommon (VScriptParser
*sc
, VMapInfo
*info
, bool &HexenMode
, const VMapInfo
&Default
) {
1374 // build command map, if it is not built yet
1375 if (mcmap
.length() == 0 && mclist
) {
1376 for (MapInfoCommand
*mcp
= mclist
; mcp
; mcp
= mcp
->next
) {
1377 VStr cn
= VStr(mcp
->cmd
).toLowerCase().xstrip();
1378 if (cn
.isEmpty()) Sys_Error("internal engine error: unnamed mapinfo command handler!");
1379 if (mcmap
.put(cn
, mcp
)) Sys_Error("internal engine error: duplicate mapinfo command handler for '%s'!", mcp
->cmd
);
1383 // if we have "lightning", but no "sky2", make "sky2" equal to "sky1" (otherwise the sky may flicker)
1384 wasSky1Sky2
= 0u; // clear "was skyN" flag
1386 const bool newFormat
= sc
->Check("{");
1387 // clear some more info for the new format (GZDoom compatibility)
1389 info
->Cluster
= Default
.Cluster
;
1390 info
->NextMap
= Default
.NextMap
;
1391 info
->SecretMap
= Default
.SecretMap
;
1392 info
->Sky1Texture
= Default
.Sky1Texture
;
1393 info
->Sky2Texture
= Default
.Sky2Texture
;
1394 info
->Sky1ScrollDelta
= Default
.Sky1ScrollDelta
;
1395 info
->Sky2ScrollDelta
= Default
.Sky2ScrollDelta
;
1396 info
->SkyBox
= Default
.SkyBox
;
1397 info
->ExitPic
= Default
.ExitPic
;
1398 info
->EnterPic
= Default
.EnterPic
;
1399 info
->InterMusic
= Default
.InterMusic
;
1402 //if (newFormat) sc->SetCMode(true);
1403 // process optional tokens
1405 //sc->GetString(); sc->UnGet(); GCon->Logf(NAME_Debug, "%s: %s", *sc->GetLoc().toStringNoCol(), *sc->String);
1406 if (!sc
->GetString()) break;
1407 auto mpp
= mcmap
.get(sc
->String
.toLowerCase());
1409 (*(*mpp
)->handler
)(sc
, newFormat
, info
, HexenMode
);
1411 //GCon->Logf(NAME_Debug, "%s: NOT FOUND cmd='%s' (new=%d)", *sc->GetLoc().toStringNoCol(), *sc->String, (int)newFormat);
1413 if (!newFormat
) break;
1414 if (sc
->Check("}")) break;
1415 if (!sc
->GetString()) break;
1416 //sc->Message(va("*** unknown mapinfo command '%s', skipping", *sc->String));
1417 skipUnimplementedCommand(sc
, false); // don't force args, but skip them
1418 if (sc
->Check("}")) break;
1422 if (sc->CheckStartsWith("compat_")) {
1423 GCon->Logf(NAME_Warning, "%s: mapdef '%s' is not supported yet", *sc->GetLoc().toStringNoCol(), *sc->String);
1428 // these are stubs for now
1429 //} else if (sc->Check("noinventorybar")) { skipUnimplementedCommand(sc, false);
1430 //} else if (sc->Check("allowcrouch")) { skipUnimplementedCommand(sc, false);
1431 //} else if (sc->Check("pausemusicinmenus")) { skipUnimplementedCommand(sc, false);
1432 //} else if (sc->Check("allowrespawn")) { skipUnimplementedCommand(sc, false);
1433 //} else if (sc->Check("teamplayon")) { skipUnimplementedCommand(sc, false);
1434 //} else if (sc->Check("teamplayoff")) { skipUnimplementedCommand(sc, false);
1435 //} else if (sc->Check("checkswitchrange")) { skipUnimplementedCommand(sc, false);
1436 //} else if (sc->Check("nocheckswitchrange")) { skipUnimplementedCommand(sc, false);
1437 //} else if (sc->Check("unfreezesingleplayerconversations")) { skipUnimplementedCommand(sc, false);
1438 //} else if (sc->Check("smoothlighting")) { skipUnimplementedCommand(sc, false);
1439 //} else if (sc->Check("Grinding_PolyObj")) { skipUnimplementedCommand(sc, false);
1440 //} else if (sc->Check("UsePlayerStartZ")) { skipUnimplementedCommand(sc, false);
1441 //} else if (sc->Check("spawnwithweaponraised")) { skipUnimplementedCommand(sc, false);
1442 //} else if (sc->Check("noautosavehint")) { skipUnimplementedCommand(sc, false);
1443 //} else if (sc->Check("PrecacheTextures")) { skipUnimplementedCommand(sc, false);
1444 //} else if (sc->Check("PrecacheSounds")) { skipUnimplementedCommand(sc, false);
1445 //} else if (sc->Check("PrecacheClasses")) { skipUnimplementedCommand(sc, false);
1446 //} else if (sc->Check("intermissionmusic")) { skipUnimplementedCommand(sc, false);
1448 //if (newFormat) sc->SetCMode(false);
1450 FixSkyTexturesHack(sc
, info
);
1452 // second sky defaults to first sky
1453 if (info
->Sky2Texture
== GTextureManager
.DefaultTexture
) info
->Sky2Texture
= info
->Sky1Texture
;
1454 if (info
->Flags
&VLevelInfo::LIF_DoubleSky
) GTextureManager
.SetFrontSkyLayer(info
->Sky1Texture
);
1458 //==========================================================================
1460 // ParseNameOrLookup
1462 //==========================================================================
1463 static void ParseNameOrLookup (VScriptParser
*sc
, vuint32 lookupFlag
, VStr
*name
, vuint32
*flags
, bool newStyle
) {
1464 if (sc
->Check("lookup")) {
1465 if (sc
->GetString()) {
1466 if (sc
->QuotedString
|| sc
->String
!= ",") sc
->UnGet();
1468 //if (newStyle) sc->Check(",");
1469 *flags
|= lookupFlag
;
1471 if (sc
->String
.length() > 1 && sc
->String
[0] == '$') {
1472 *name
= VStr(*sc
->String
+1).ToLower();
1474 *name
= sc
->String
.ToLower();
1478 if (sc
->String
.Length() > 1 && sc
->String
[0] == '$') {
1479 *flags
|= lookupFlag
;
1480 *name
= VStr(*sc
->String
+1).ToLower();
1482 *flags
&= ~lookupFlag
;
1484 if (lookupFlag
== VLevelInfo::LIF_LookupName
) return;
1486 while (!sc
->AtEnd()) {
1487 if (!sc
->Check(",")) break;
1488 if (sc
->AtEnd()) break;
1490 while (!sc
->QuotedString
) {
1491 if (sc
->String
== "}") { sc
->UnGet(); break; } // stray comma
1492 if (sc
->String
!= ",") { sc
->UnGet(); sc
->Error("comma expected"); break; }
1493 if (sc
->AtEnd()) break;
1497 *name
+= sc
->String
;
1500 while (!sc
->AtEnd()) {
1502 if (sc
->Crossed
) { sc
->UnGet(); break; }
1503 while (!sc
->QuotedString
) {
1504 if (sc
->String
!= ",") { sc
->UnGet(); sc
->Error("comma expected"); break; }
1505 if (sc
->AtEnd()) break;
1509 *name
+= sc
->String
;
1512 //GCon->Logf(NAME_Debug, "COLLECTED: <%s>", **name);
1518 //==========================================================================
1520 // ParseNameOrLookup
1522 //==========================================================================
1523 static void ParseNameOrLookup (VScriptParser
*sc
, vint32 lookupFlag
, VStr
*name
, vint32
*flags
, bool newStyle
) {
1524 vuint32 lf
= (vuint32
)lookupFlag
;
1525 vuint32 flg
= (vuint32
)*flags
;
1526 ParseNameOrLookup(sc
, lf
, name
, &flg
, newStyle
);
1527 *flags
= (vint32
)flg
;
1531 //==========================================================================
1535 //==========================================================================
1536 static VStr
ParseUStringKey (VScriptParser
*sc
) {
1539 return sc
->String
.xstrip();
1543 //==========================================================================
1547 //==========================================================================
1548 static bool ParseUBoolKey (VScriptParser
*sc
) {
1551 VStr ss
= sc
->String
.xstrip();
1552 if (ss
.strEquCI("false") || ss
.strEquCI("0")) return false;
1553 if (ss
.strEquCI("true") || ss
.strEquCI("1")) return true;
1554 sc
->Error("boolean value expected");
1559 //==========================================================================
1563 //==========================================================================
1564 static void ParseMapUMapinfo (VScriptParser
*sc
, VMapInfo
*info
) {
1565 // if we have "lightning", but no "sky2", make "sky2" equal to "sky1" (otherwise the sky may flicker)
1566 wasSky1Sky2
= 0u; // clear "was skyN" flag
1567 bool wasEndGame
= false;
1568 bool wasClearBossAction
= false;
1574 const VTextLocation loc
= sc
->GetLoc();
1576 if (sc
->Check("}")) break;
1577 if (sc
->AtEnd()) break;
1579 if (sc
->Check("levelname")) {
1580 VStr ss
= ParseUStringKey(sc
);
1581 if (ss
.length()) info
->Name
= ss
;
1584 if (sc
->Check("levelpic")) {
1585 VStr ss
= ParseUStringKey(sc
);
1586 info
->EnterTitlePatch
= info
->ExitTitlePatch
= VName(*ss
, VName::AddLower
);
1589 if (sc
->Check("exitpic")) {
1590 VStr ss
= ParseUStringKey(sc
);
1591 info
->ExitPic
= VName(*ss
, VName::AddLower
);
1594 if (sc
->Check("enterpic")) {
1595 VStr ss
= ParseUStringKey(sc
);
1596 info
->EnterPic
= VName(*ss
, VName::AddLower
);
1599 if (sc
->Check("next")) {
1600 VStr ss
= ParseUStringKey(sc
);
1601 if (ss
.length()) info
->NextMap
= VName(*ss
, VName::AddLower
);
1604 if (sc
->Check("nextsecret")) {
1605 VStr ss
= ParseUStringKey(sc
);
1606 if (ss
.length()) info
->SecretMap
= VName(*ss
, VName::AddLower
);
1609 if (sc
->Check("skytexture")) {
1610 VStr ss
= ParseUStringKey(sc
);
1612 wasSky1Sky2
|= WSK_WAS_SKY1
;
1613 VName skbname
= R_HasNamedSkybox(sc
->String
);
1614 if (skbname
!= NAME_None
) {
1615 //k8: ok, this may be done to support sourceports that cannot into skyboxes
1616 miWarning(loc
, "sky1 '%s' is actually a skybox (this is mostly harmless)", *sc
->String
);
1617 info
->SkyBox
= skbname
;
1618 info
->Sky1Texture
= GTextureManager
.DefaultTexture
;
1619 info
->Sky2Texture
= GTextureManager
.DefaultTexture
;
1620 info
->Sky1ScrollDelta
= 0;
1621 info
->Sky2ScrollDelta
= 0;
1623 info
->SkyBox
= NAME_None
;
1624 info
->Sky1Texture
= loadSkyTexture(sc
, VName(*ss
, VName::AddLower
));
1625 info
->Sky1ScrollDelta
= 0;
1630 if (sc
->Check("music")) {
1631 VStr ss
= ParseUStringKey(sc
);
1632 if (ss
.length()) info
->SongLump
= VName(*ss
, VName::AddLower
);
1635 if (sc
->Check("partime")) {
1638 info
->ParTime
= sc
->Number
;
1641 if (sc
->Check("endgame")) {
1642 wasEndGame
= ParseUBoolKey(sc
);
1645 if (sc
->Check("endpic")) {
1646 VStr ss
= ParseUStringKey(sc
);
1648 endType
= VStr(va("EndGameCustomPic%s", *ss
));
1650 endType
= "EndGamePic3"; // arbitrary decision, credits
1654 if (sc
->Check("endbunny")) {
1655 if (ParseUBoolKey(sc
)) endType
= "EndGameBunny";
1658 if (sc
->Check("endcast")) {
1659 if (ParseUBoolKey(sc
)) endType
= "EndGameCast";
1662 if (sc
->Check("bossaction")) {
1663 // clear all special map action flags
1665 VLevelInfo::LIF_Map07Special
|
1666 VLevelInfo::LIF_BaronSpecial
|
1667 VLevelInfo::LIF_CyberDemonSpecial
|
1668 VLevelInfo::LIF_SpiderMastermindSpecial
|
1669 VLevelInfo::LIF_MinotaurSpecial
|
1670 VLevelInfo::LIF_DSparilSpecial
|
1671 VLevelInfo::LIF_IronLichSpecial
|
1672 VLevelInfo::LIF_SpecialActionOpenDoor
|
1673 VLevelInfo::LIF_SpecialActionLowerFloor
|
1674 VLevelInfo::LIF_SpecialActionKillMonsters
);
1675 // can't we fuckin' have a complete specs for ANYTHING, for fuck's sake?!
1676 VStr className
= ParseUStringKey(sc
);
1677 if (className
.strEquCI("clear")) {
1678 info
->SpecialActions
.clear();
1679 wasClearBossAction
= true;
1681 //bossaction = thingtype, linespecial, tag
1684 int special
= sc
->Number
;
1685 //if (special < 0) sc->Error(va("invalid bossaction special %d", special));
1688 int tag
= sc
->Number
;
1689 // allow no 0-tag specials here, unless a level exit
1690 if (className
.length() && special
> 0 && (tag
!= 0 || special
== 11 || special
== 51 || special
== 52 || special
== 124)) {
1691 // add special action
1692 if (info
->SpecialActions
.length() == 0) {
1693 VMapSpecialAction
&aa
= info
->SpecialActions
.Alloc();
1694 aa
.TypeName
= VName("UMapInfoActions");
1695 aa
.Special
= 666999; // this means nothing
1697 VMapSpecialAction
&A
= info
->SpecialActions
.Alloc();
1698 A
.TypeName
= VName(*className
);
1699 A
.Special
= -special
; // it should be translated
1701 A
.Args
[1] = A
.Args
[2] = A
.Args
[3] = A
.Args
[4] = 0;
1702 wasClearBossAction
= false;
1704 miWarning(sc
, "Invalid bossaction special %d (tag %d)", special
, tag
);
1710 if (sc
->Check("episode")) {
1711 VStr ss
= ParseUStringKey(sc
);
1712 if (ss
.strEquCI("clear")) {
1713 EpisodeDefs
.Clear();
1714 ClusterDefs
.Clear(); // clear clusterdefs too
1718 bool checkReplace
= true;
1719 if (sc
->Check(",")) {
1721 epname
= sc
->String
.xstrip();
1722 if (sc
->Check(",")) sc
->ExpectString(); // ignore key
1724 epname
= "Unnamed episode";
1725 checkReplace
= false;
1728 VEpisodeDef
*EDef
= nullptr;
1729 // check for replaced episode
1731 for (int i
= 0; i
< EpisodeDefs
.length(); ++i
) {
1732 if (sc
->Name
== EpisodeDefs
[i
].Name
) {
1733 EDef
= &EpisodeDefs
[i
];
1738 if (!EDef
) EDef
= &EpisodeDefs
.Alloc();
1741 EDef
->Name
= info
->LumpName
;
1742 EDef
->TeaserName
= NAME_None
;
1743 EDef
->Text
= epname
;
1744 EDef
->PicName
= VName(*ss
, VName::AddLower
);
1747 EDef
->MapinfoSourceLump
= info
->MapinfoSourceLump
;
1752 if (sc
->Check("nointermission")) {
1753 if (ParseUBoolKey(sc
)) info
->Flags
|= VLevelInfo::LIF_NoIntermission
; else info
->Flags
&= ~VLevelInfo::LIF_NoIntermission
;
1757 // special hack for intertexts in `VBasePlayer::DoClientIntermission()` allows to use per-map texts
1758 if (sc
->Check("intertext")) {
1759 VStr exitText
= ParseUStringKey(sc
);
1760 while (sc
->Check(",")) {
1763 exitText
+= sc
->String
.xstrip();
1765 exitText
= exitText
.xstrip();
1766 if (exitText
.strEquCI("clear")) exitText
= VStr(" ");
1767 info
->ExitText
= exitText
;
1770 if (sc
->Check("intertextsecret")) {
1771 VStr exitText
= ParseUStringKey(sc
);
1772 while (sc
->Check(",")) {
1775 exitText
+= sc
->String
.xstrip();
1777 exitText
= exitText
.xstrip();
1778 if (exitText
.strEquCI("clear")) exitText
= VStr(" ");
1779 info
->SecretExitText
= exitText
;
1782 if (sc
->Check("interbackdrop")) {
1783 VStr ss
= ParseUStringKey(sc
);
1784 info
->InterBackdrop
= VName(*ss
, VName::AddLower
);
1787 if (sc
->Check("intermusic")) {
1788 VStr ss
= ParseUStringKey(sc
);
1789 info
->InterMusic
= VName(*ss
, VName::AddLower
);
1795 // Specifies the string to prepend to the levelname on the automap.
1796 // If not specified the mapname will be used by default followed by
1797 // a colon and a space character (e.g. "E1M1: ").
1799 // Only print the levelname on the automap.
1801 // i'll ignore that for now, it is better than crashing.
1802 if (sc
->Check("label")) {
1803 miWarning(sc
, "ignored UMAPINFO `label`, because it is not supported yet (this is harmless)");
1804 (void)ParseUStringKey(sc
);
1808 if (sc
->Check("author")) {
1809 miWarning(sc
, "ignored UMAPINFO `author`, because it is not supported yet (this is harmless)");
1810 (void)ParseUStringKey(sc
);
1814 if (sc
->Check("enteranim")) {
1815 miWarning(sc
, "ignored UMAPINFO `enteranim`, because it is not supported yet (this is harmless)");
1816 (void)ParseUStringKey(sc
);
1820 if (sc
->Check("exitanim")) {
1821 miWarning(sc
, "ignored UMAPINFO `exitanim`, because it is not supported yet (this is harmless)");
1822 (void)ParseUStringKey(sc
);
1826 sc
->Error(va("Unknown UMAPINFO map key '%s'", *sc
->String
));
1829 if (wasEndGame
|| endType
.length()) {
1830 if (endType
.length() == 0) endType
= "EndGamePic3"; // arbitrary decision, credits
1831 info
->NextMap
= VName(*endType
);
1834 if (info
->SecretMap
== NAME_None
) info
->SecretMap
= info
->NextMap
;
1836 if (wasClearBossAction
) {
1837 vassert(info
->SpecialActions
.length() == 0);
1839 VMapSpecialAction
&A
= info
->SpecialActions
.Alloc();
1840 A
.TypeName
= VName("UMapInfoDoNothing");
1841 A
.Special
= 666999; // this means nothing
1844 FixSkyTexturesHack(sc
, info
);
1846 // second sky defaults to first sky
1847 if (info
->Sky2Texture
== GTextureManager
.DefaultTexture
) info
->Sky2Texture
= info
->Sky1Texture
;
1848 if (info
->Flags
&VLevelInfo::LIF_DoubleSky
) GTextureManager
.SetFrontSkyLayer(info
->Sky1Texture
);
1852 //==========================================================================
1856 //==========================================================================
1857 static void ParseMap (VScriptParser
*sc
, bool &HexenMode
, const VMapInfo
&Default
, bool umapinfo
=false) {
1858 VMapInfo
*info
= nullptr;
1859 vuint32 savedFlags
= 0;
1862 if (!umapinfo
&& sc
->CheckNumber()) {
1863 // map number, for Hexen compatibility
1865 if (sc
->Number
< 1 || sc
->Number
> 99) sc
->Error("Map number out or range");
1866 MapLumpName
= va("map%02d", sc
->Number
);
1870 VStr nn
= sc
->String
.xstrip();
1871 if (nn
.length() == 0) sc
->Error("empty map name");
1872 MapLumpName
= VName(*sc
->String
, VName::AddLower
);
1875 // check for replaced map info
1876 bool replacement
= false;
1877 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
1878 if (MapLumpName
== MapInfo
[i
].LumpName
) {
1880 //GCon->Logf(NAME_Init, "replaced map '%s' (Sky1Texture=%d; default=%d)", *info->LumpName, info->Sky1Texture, Default.Sky1Texture);
1881 savedFlags
= info
->Flags
;
1886 if (!info
) info
= &MapInfo
.Alloc();
1888 // Copy defaults to current map definition
1889 info
->LumpName
= MapLumpName
;
1891 info
->LevelNum
= Default
.LevelNum
;
1892 info
->Cluster
= Default
.Cluster
;
1894 info
->WarpTrans
= Default
.WarpTrans
;
1896 info
->NextMap
= Default
.NextMap
;
1897 info
->SecretMap
= Default
.SecretMap
;
1898 info
->SongLump
= Default
.SongLump
;
1901 info
->Sky1Texture
= Default
.Sky1Texture
;
1902 info
->Sky2Texture
= Default
.Sky2Texture
;
1903 info
->Sky1ScrollDelta
= Default
.Sky1ScrollDelta
;
1904 info
->Sky2ScrollDelta
= Default
.Sky2ScrollDelta
;
1905 info
->SkyBox
= Default
.SkyBox
;
1907 info
->FadeTable
= Default
.FadeTable
;
1908 info
->Fade
= Default
.Fade
;
1909 info
->OutsideFog
= Default
.OutsideFog
;
1910 info
->Gravity
= Default
.Gravity
;
1911 info
->AirControl
= Default
.AirControl
;
1912 info
->Flags
= Default
.Flags
;
1913 info
->Flags2
= Default
.Flags2
;
1914 info
->EnterTitlePatch
= Default
.EnterTitlePatch
;
1915 info
->ExitTitlePatch
= Default
.ExitTitlePatch
;
1916 info
->ParTime
= Default
.ParTime
;
1917 info
->SuckTime
= Default
.SuckTime
;
1918 info
->HorizWallShade
= Default
.HorizWallShade
;
1919 info
->VertWallShade
= Default
.VertWallShade
;
1920 info
->Infighting
= Default
.Infighting
;
1921 info
->SpecialActions
= Default
.SpecialActions
;
1922 info
->RedirectType
= Default
.RedirectType
;
1923 info
->RedirectMap
= Default
.RedirectMap
;
1924 info
->ExitText
= Default
.ExitText
;
1925 info
->SecretExitText
= Default
.SecretExitText
;
1926 info
->InterBackdrop
= Default
.InterBackdrop
;
1928 info
->ExitPic
= Default
.ExitPic
;
1929 info
->EnterPic
= Default
.EnterPic
;
1930 info
->InterMusic
= Default
.InterMusic
;
1933 // copy "no intermission" flag from default map
1934 if (Default
.Flags
&VLevelInfo::LIF_NoIntermission
) info
->Flags
|= VLevelInfo::LIF_NoIntermission
;
1937 info
->Flags
|= VLevelInfo::LIF_NoIntermission
|
1938 VLevelInfo::LIF_FallingDamage
|
1939 VLevelInfo::LIF_MonsterFallingDamage
|
1940 VLevelInfo::LIF_NoAutoSndSeq
|
1941 VLevelInfo::LIF_ActivateOwnSpecial
|
1942 VLevelInfo::LIF_MissilesActivateImpact
|
1943 VLevelInfo::LIF_InfiniteFlightPowerup
;
1946 // set saved par time
1947 int par
= findSavedPar(MapLumpName
);
1949 //GCon->Logf(NAME_Init, "found dehacked par time for map '%s' (%d)", *MapLumpName, par);
1950 info
->ParTime
= par
;
1954 // map name must follow the number
1955 ParseNameOrLookup(sc
, VLevelInfo::LIF_LookupName
, &info
->Name
, &info
->Flags
, false);
1958 // set song lump name from SNDINFO script
1959 for (int i
= 0; i
< MapSongList
.length(); ++i
) {
1960 if (MapSongList
[i
].MapName
== info
->LumpName
) {
1961 info
->SongLump
= MapSongList
[i
].SongName
;
1965 // set default levelnum for this map
1966 const char *mn
= *MapLumpName
;
1967 if (mn
[0] == 'm' && mn
[1] == 'a' && mn
[2] == 'p' && mn
[5] == 0) {
1968 int num
= VStr::atoi(mn
+3);
1969 if (num
>= 1 && num
<= 99) info
->LevelNum
= num
;
1970 } else if (mn
[0] == 'e' && mn
[1] >= '0' && mn
[1] <= '9' &&
1971 mn
[2] == 'm' && mn
[3] >= '0' && mn
[3] <= '9')
1973 info
->LevelNum
= (mn
[1]-'1')*10+(mn
[3]-'0');
1976 info
->MapinfoSourceLump
= sc
->SourceLump
;
1979 ParseMapCommon(sc
, info
, HexenMode
, Default
);
1981 // copy special actions, they should be explicitly cleared
1982 info
->Flags
= savedFlags
&(
1983 VLevelInfo::LIF_Map07Special
|
1984 VLevelInfo::LIF_BaronSpecial
|
1985 VLevelInfo::LIF_CyberDemonSpecial
|
1986 VLevelInfo::LIF_SpiderMastermindSpecial
|
1987 VLevelInfo::LIF_MinotaurSpecial
|
1988 VLevelInfo::LIF_DSparilSpecial
|
1989 VLevelInfo::LIF_IronLichSpecial
|
1990 VLevelInfo::LIF_SpecialActionOpenDoor
|
1991 VLevelInfo::LIF_SpecialActionLowerFloor
|
1992 VLevelInfo::LIF_SpecialActionKillMonsters
);
1993 ParseMapUMapinfo(sc
, info
);
1996 info
->MapinfoSourceLump
= sc
->SourceLump
;
1998 // avoid duplicate levelnums, later one takes precedance
1999 if (info
->LevelNum
) {
2000 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
2001 if (MapInfo
[i
].LevelNum
== info
->LevelNum
&& &MapInfo
[i
] != info
) {
2002 if (W_IsUserWadLump(MapInfo
[i
].MapinfoSourceLump
)) {
2003 if (MapInfo
[i
].MapinfoSourceLump
!= info
->MapinfoSourceLump
) {
2004 GCon
->Logf(NAME_Warning
, "duplicate levelnum %d for maps '%s' and '%s' ('%s' zeroed)", info
->LevelNum
, *MapInfo
[i
].LumpName
, *info
->LumpName
, *MapInfo
[i
].LumpName
);
2005 GCon
->Logf(NAME_Warning
, " first map is defined in '%s'", *W_FullLumpName(MapInfo
[i
].MapinfoSourceLump
));
2006 GCon
->Logf(NAME_Warning
, " second map is defined in '%s'", *W_FullLumpName(info
->MapinfoSourceLump
));
2008 GCon
->Logf(NAME_Warning
, "duplicate levelnum %d for maps '%s' and '%s' ('%s' zeroed)", info
->LevelNum
, *MapInfo
[i
].LumpName
, *info
->LumpName
, *info
->LumpName
);
2009 GCon
->Logf(NAME_Warning
, " both maps are defined in '%s'", *W_FullLumpName(info
->MapinfoSourceLump
));
2010 // this means that latter episode is fucked -- so fuck it for real
2015 MapInfo
[i
].LevelNum
= 0;
2022 //==========================================================================
2026 //==========================================================================
2027 static void ParseClusterDef (VScriptParser
*sc
) {
2028 VClusterDef
*CDef
= nullptr;
2031 // check for replaced cluster def
2032 for (int i
= 0; i
< ClusterDefs
.length(); ++i
) {
2033 if (sc
->Number
== ClusterDefs
[i
].Cluster
) {
2034 CDef
= &ClusterDefs
[i
];
2038 if (!CDef
) CDef
= &ClusterDefs
.Alloc();
2041 CDef
->Cluster
= sc
->Number
;
2043 CDef
->EnterText
= VStr();
2044 CDef
->ExitText
= VStr();
2045 CDef
->Flat
= NAME_None
;
2046 CDef
->Music
= NAME_None
;
2048 //GCon->Logf(NAME_Debug, "=== NEW CLUSTER %d ===", CDef->Cluster);
2049 bool newFormat
= sc
->Check("{");
2050 //if (newFormat) sc->SetCMode(true);
2051 while (!sc
->AtEnd()) {
2052 //if (sc->GetString()) { GCon->Logf(NAME_Debug, ":%s: CLUSTER(%d): <%s>", *sc->GetLoc().toStringNoCol(), (newFormat ? 1 : 0), *sc->String); sc->UnGet(); }
2053 if (sc
->Check("hub")) {
2054 CDef
->Flags
|= CLUSTERF_Hub
;
2055 } else if (sc
->Check("entertext")) {
2056 if (newFormat
) sc
->Expect("=");
2057 ParseNameOrLookup(sc
, CLUSTERF_LookupEnterText
, &CDef
->EnterText
, &CDef
->Flags
, newFormat
);
2058 //GCon->Logf(NAME_Debug, "::: <%s>", *CDef->EnterText);
2059 } else if (sc
->Check("entertextislump")) {
2060 CDef
->Flags
|= CLUSTERF_EnterTextIsLump
;
2061 } else if (sc
->Check("exittext")) {
2062 if (newFormat
) sc
->Expect("=");
2063 ParseNameOrLookup(sc
, CLUSTERF_LookupExitText
, &CDef
->ExitText
, &CDef
->Flags
, newFormat
);
2064 } else if (sc
->Check("exittextislump")) {
2065 CDef
->Flags
|= CLUSTERF_ExitTextIsLump
;
2066 } else if (sc
->Check("flat")) {
2067 if (newFormat
) sc
->Expect("=");
2069 CDef
->Flat
= sc
->Name8
;
2070 CDef
->Flags
&= ~CLUSTERF_FinalePic
;
2071 } else if (sc
->Check("pic")) {
2072 if (newFormat
) sc
->Expect("=");
2073 //sc->ExpectName8();
2075 CDef
->Flat
= sc
->Name
;
2076 CDef
->Flags
|= CLUSTERF_FinalePic
;
2077 } else if (sc
->Check("music")) {
2078 if (newFormat
) sc
->Expect("=");
2079 //sc->ExpectName8();
2081 CDef
->Music
= sc
->Name
;
2082 } else if (sc
->Check("cdtrack")) {
2083 if (newFormat
) sc
->Expect("=");
2085 //CDef->CDTrack = sc->Number;
2086 } else if (sc
->Check("cdid")) {
2087 if (newFormat
) sc
->Expect("=");
2089 //CDef->CDId = sc->Number;
2090 } else if (sc
->Check("name")) {
2091 const VTextLocation loc
= sc
->GetLoc();
2092 if (newFormat
) sc
->Expect("=");
2093 if (sc
->Check("lookup")) {
2094 if (newFormat
) sc
->Expect(",");
2097 miWarning(loc
, "Unimplemented cluster command 'name'");
2100 if (!sc
->Check("}")) {
2101 const VTextLocation loc
= sc
->GetLoc();
2103 VStr cmd
= sc
->String
;
2104 //fprintf(stderr, "!!!!!!\n");
2105 if (sc
->Check("=")) {
2106 //fprintf(stderr, "******\n");
2107 while (!sc
->AtEnd()) {
2109 if (!sc
->Check(",")) break;
2112 miWarning(loc
, "unknown clusterdef command '%s'", *cmd
);
2115 //sc->Error(va("'}' expected in clusterdef, but got \"%s\"", *sc->String));
2122 //if (newFormat) sc->SetCMode(false);
2124 // make sure text lump names are in lower case
2125 if (CDef
->Flags
&CLUSTERF_EnterTextIsLump
) CDef
->EnterText
= CDef
->EnterText
.ToLower();
2126 if (CDef
->Flags
&CLUSTERF_ExitTextIsLump
) CDef
->ExitText
= CDef
->ExitText
.ToLower();
2130 //==========================================================================
2134 //==========================================================================
2135 static void ParseEpisodeDef (VScriptParser
*sc
) {
2136 VEpisodeDef
*EDef
= nullptr;
2140 // check for replaced episode
2141 for (int i
= 0; i
< EpisodeDefs
.length(); ++i
) {
2142 if (sc
->Name
== EpisodeDefs
[i
].Name
) {
2143 EDef
= &EpisodeDefs
[i
];
2149 EDef
= &EpisodeDefs
.Alloc();
2150 EIdx
= EpisodeDefs
.length()-1;
2153 // check for removal of an episode
2154 if (sc
->Check("remove")) {
2155 EpisodeDefs
.RemoveIndex(EIdx
);
2160 EDef
->Name
= sc
->Name
;
2161 EDef
->TeaserName
= NAME_None
;
2162 EDef
->Text
= VStr();
2163 EDef
->PicName
= NAME_None
;
2166 EDef
->MapinfoSourceLump
= sc
->SourceLump
;
2168 if (sc
->Check("teaser")) {
2170 EDef
->TeaserName
= sc
->Name
;
2173 bool newFormat
= sc
->Check("{");
2174 //if (newFormat) sc->SetCMode(true);
2175 while (!sc
->AtEnd()) {
2176 if (sc
->Check("name")) {
2177 if (newFormat
) sc
->Expect("=");
2178 ParseNameOrLookup(sc
, EPISODEF_LookupText
, &EDef
->Text
, &EDef
->Flags
, newFormat
);
2179 } else if (sc
->Check("picname")) {
2180 if (newFormat
) sc
->Expect("=");
2182 EDef
->PicName
= sc
->Name
;
2183 } else if (sc
->Check("key")) {
2184 if (newFormat
) sc
->Expect("=");
2186 EDef
->Key
= sc
->String
.ToLower();
2187 } else if (sc
->Check("noskillmenu")) {
2188 EDef
->Flags
|= EPISODEF_NoSkillMenu
;
2189 } else if (sc
->Check("optional")) {
2190 EDef
->Flags
|= EPISODEF_Optional
;
2192 if (newFormat
&& !sc
->AtEnd()) sc
->Expect("}");
2196 //if (newFormat) sc->SetCMode(false);
2200 //==========================================================================
2204 //==========================================================================
2205 static void ParseSkillDefOld (VScriptParser
*sc
, VSkillDef
*sdef
) {
2206 while (!sc
->AtEnd()) {
2207 if (sc
->Check("AmmoFactor")) {
2209 sdef
->AmmoFactor
= sc
->Float
;
2210 } else if (sc
->Check("DropAmmoFactor")) {
2212 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill setting 'DropAmmoFactor' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2213 } else if (sc
->Check("DoubleAmmoFactor")) {
2215 sdef
->DoubleAmmoFactor
= sc
->Float
;
2216 } else if (sc
->Check("DamageFactor")) {
2218 sdef
->DamageFactor
= sc
->Float
;
2219 } else if (sc
->Check("FastMonsters")) {
2220 sdef
->Flags
|= SKILLF_FastMonsters
;
2221 } else if (sc
->Check("DisableCheats")) {
2223 //sdef->Flags |= SKILLF_DisableCheats;
2224 } else if (sc
->Check("EasyBossBrain")) {
2225 sdef
->Flags
|= SKILLF_EasyBossBrain
;
2226 } else if (sc
->Check("AutoUseHealth")) {
2227 sdef
->Flags
|= SKILLF_AutoUseHealth
;
2228 } else if (sc
->Check("RespawnTime")) {
2230 sdef
->RespawnTime
= sc
->Float
;
2231 } else if (sc
->Check("RespawnLimit")) {
2233 sdef
->RespawnLimit
= sc
->Number
;
2234 } else if (sc
->Check("NoPain")) {
2235 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'NoPain' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2236 } else if (sc
->Check("Aggressiveness")) {
2237 sc
->ExpectFloatWithSign();
2238 if (sc
->Float
< 0) GCon
->Logf(NAME_Warning
, "%s:MAPINFO: \"Aggressiveness\" should be positive", *sc
->GetLoc().toStringNoCol());
2239 sdef
->Aggressiveness
= 1.0f
-midval(0.0f
, sc
->Float
, 1.0f
);
2240 } else if (sc
->Check("SpawnFilter")) {
2241 if (sc
->CheckNumber()) {
2242 if (sc
->Number
> 0 && sc
->Number
< 31) sdef
->SpawnFilter
= 1<<(sc
->Number
-1);
2244 if (sc
->Check("Baby")) sdef
->SpawnFilter
= 0x01;
2245 else if (sc
->Check("Easy")) sdef
->SpawnFilter
= 0x02;
2246 else if (sc
->Check("Normal")) sdef
->SpawnFilter
= 0x04;
2247 else if (sc
->Check("Hard")) sdef
->SpawnFilter
= 0x08;
2248 else if (sc
->Check("Nightmare")) sdef
->SpawnFilter
= 0x10;
2249 else sc
->ExpectString();
2251 } else if (sc
->Check("ACSReturn")) {
2253 sdef
->AcsReturn
= sc
->Number
;
2254 } else if (sc
->Check("Name")) {
2256 sdef
->MenuName
= sc
->String
;
2257 sdef
->Flags
&= ~SKILLF_MenuNameIsPic
;
2258 } else if (sc
->Check("PlayerClassName")) {
2259 VSkillPlayerClassName
&CN
= sdef
->PlayerClassNames
.Alloc();
2261 CN
.ClassName
= sc
->String
;
2263 CN
.MenuName
= sc
->String
;
2264 } else if (sc
->Check("PicName")) {
2266 sdef
->MenuName
= sc
->String
.ToLower();
2267 sdef
->Flags
|= SKILLF_MenuNameIsPic
;
2268 } else if (sc
->Check("MustConfirm")) {
2269 sdef
->Flags
|= SKILLF_MustConfirm
;
2270 if (sc
->CheckQuotedString()) sdef
->ConfirmationText
= sc
->String
;
2271 } else if (sc
->Check("Key")) {
2273 sdef
->Key
= sc
->String
;
2274 } else if (sc
->Check("TextColor")) {
2276 sdef
->TextColor
= sc
->String
;
2282 if (sdef
->SpawnFilter
== 0) {
2283 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'SpawnFilter' is not set for skill '%s'; assume UV.", *sc
->GetLoc().toStringNoCol(), *sdef
->MenuName
);
2284 sdef
->SpawnFilter
= 8; // UV
2289 //==========================================================================
2293 //==========================================================================
2294 static void ParseSkillDef (VScriptParser
*sc
) {
2295 VSkillDef
*sdef
= nullptr;
2298 // check for replaced skill
2299 for (int i
= 0; i
< SkillDefs
.length(); ++i
) {
2300 if (sc
->String
.ICmp(SkillDefs
[i
].Name
) == 0) {
2301 sdef
= &SkillDefs
[i
];
2302 //GCon->Logf(NAME_Debug, "SKILLDEF:%s: replaced skill #%d '%s' (%s)", *sc->GetLoc().toStringNoCol(), i, *sc->String, *SkillDefs[i].Name);
2307 sdef
= &SkillDefs
.Alloc();
2308 sdef
->Name
= sc
->String
;
2309 //GCon->Logf(NAME_Debug, "SKILLDEF:%s: new skill #%d '%s'", *sc->GetLoc().toStringNoCol(), SkillDefs.length(), *sc->String);
2313 sdef
->AmmoFactor
= 1.0f
;
2314 sdef
->DoubleAmmoFactor
= 2.0f
;
2315 sdef
->DamageFactor
= 1.0f
;
2316 sdef
->RespawnTime
= 0.0f
;
2317 sdef
->RespawnLimit
= 0;
2318 sdef
->Aggressiveness
= 1.0f
;
2319 sdef
->SpawnFilter
= 0;
2320 sdef
->MonsterHealth
= 1.0f
;
2321 sdef
->HealthFactor
= 1.0f
;
2322 sdef
->AcsReturn
= SkillDefs
.length()-1;
2323 sdef
->MenuName
.Clean();
2324 sdef
->PlayerClassNames
.Clear();
2325 sdef
->ConfirmationText
.Clean();
2327 sdef
->TextColor
.Clean();
2329 // if skill definition contains replacements, clear the old ones
2330 // k8: i am not sure if i should keep old replacements here, but why not?
2331 bool sdefClearReplacements
= true;
2333 VClass
*eexCls
= VClass::FindClassNoCase("Actor"); // we'll need it later
2335 if (!sc
->Check("{")) { ParseSkillDefOld(sc
, sdef
); return; }
2336 SCParseModeSaver
msave(sc
);
2338 while (!checkEndBracket(sc
)) {
2339 if (sc
->Check("AmmoFactor")) {
2342 sdef
->AmmoFactor
= sc
->Float
;
2343 } else if (sc
->Check("DoubleAmmoFactor")) {
2346 sdef
->DoubleAmmoFactor
= sc
->Float
;
2347 } else if (sc
->Check("DropAmmoFactor")) {
2350 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill setting 'DropAmmoFactor' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2351 } else if (sc
->Check("DamageFactor")) {
2354 sdef
->DamageFactor
= sc
->Float
;
2355 } else if (sc
->Check("RespawnTime")) {
2358 sdef
->RespawnTime
= sc
->Float
;
2359 } else if (sc
->Check("RespawnLimit")) {
2362 sdef
->RespawnLimit
= sc
->Number
;
2363 } else if (sc
->Check("Aggressiveness")) {
2365 sc
->ExpectFloatWithSign();
2366 if (sc
->Float
< 0) GCon
->Logf(NAME_Warning
, "%s:MAPINFO: \"Aggressiveness\" should be positive", *sc
->GetLoc().toStringNoCol());
2367 sdef
->Aggressiveness
= 1.0f
-midval(0.0f
, sc
->Float
, 1.0f
);
2368 } else if (sc
->Check("SpawnFilter")) {
2370 if (sc
->CheckNumber()) {
2371 if (sc
->Number
> 0 && sc
->Number
< 31) {
2372 sdef
->SpawnFilter
= 1<<(sc
->Number
-1);
2374 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: invalid spawnfilter value %d", *sc
->GetLoc().toStringNoCol(), sc
->Number
);
2377 if (sc
->Check("Baby")) sdef
->SpawnFilter
= 1;
2378 else if (sc
->Check("Easy")) sdef
->SpawnFilter
= 2;
2379 else if (sc
->Check("Normal")) sdef
->SpawnFilter
= 4;
2380 else if (sc
->Check("Hard")) sdef
->SpawnFilter
= 8;
2381 else if (sc
->Check("Nightmare")) sdef
->SpawnFilter
= 16;
2382 else { sc
->ExpectString(); GCon
->Logf(NAME_Warning
, "MAPINFO:%s: unknown spawnfilter '%s'", *sc
->GetLoc().toStringNoCol(), *sc
->String
); }
2384 } else if (sc
->Check("ACSReturn")) {
2387 sdef
->AcsReturn
= sc
->Number
;
2388 } else if (sc
->Check("Key")) {
2391 sdef
->Key
= sc
->String
;
2392 } else if (sc
->Check("MustConfirm")) {
2393 sdef
->Flags
|= SKILLF_MustConfirm
;
2394 if (sc
->Check("=")) {
2396 sdef
->ConfirmationText
= sc
->String
;
2398 } else if (sc
->Check("Name")) {
2401 sdef
->MenuName
= sc
->String
;
2402 sdef
->Flags
&= ~SKILLF_MenuNameIsPic
;
2403 } else if (sc
->Check("PlayerClassName")) {
2405 VSkillPlayerClassName
&CN
= sdef
->PlayerClassNames
.Alloc();
2407 CN
.ClassName
= sc
->String
;
2410 CN
.MenuName
= sc
->String
;
2411 } else if (sc
->Check("PicName")) {
2414 sdef
->MenuName
= sc
->String
.ToLower();
2415 sdef
->Flags
|= SKILLF_MenuNameIsPic
;
2416 } else if (sc
->Check("TextColor")) {
2419 sdef
->TextColor
= sc
->String
;
2420 } else if (sc
->Check("EasyBossBrain")) {
2421 sdef
->Flags
|= SKILLF_EasyBossBrain
;
2422 } else if (sc
->Check("EasyKey")) {
2423 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'EasyKey' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2424 } else if (sc
->Check("FastMonsters")) {
2425 sdef
->Flags
|= SKILLF_FastMonsters
;
2426 } else if (sc
->Check("SlowMonsters")) {
2427 sdef
->Flags
|= SKILLF_SlowMonsters
;
2428 } else if (sc
->Check("SpawnMulti")) {
2429 sdef
->Flags
|= SKILLF_SpawnMulti
;
2430 } else if (sc
->Check("DisableCheats")) {
2432 //sdef->Flags |= SKILLF_DisableCheats;
2433 } else if (sc
->Check("AutoUseHealth")) {
2434 sdef
->Flags
|= SKILLF_AutoUseHealth
;
2435 } else if (sc
->Check("ReplaceActor")) {
2436 //GCon->Logf(NAME_Warning, "MAPINFO:%s: skill param 'ReplaceActor' is not implemented yet.", *sc->GetLoc().toStringNoCol());
2439 VStr oldCN
= sc
->String
;
2442 VStr newCN
= sc
->String
;
2443 if (sdefClearReplacements
) { sdef
->Replacements
.clear(); sdefClearReplacements
= false; }
2444 if (eexCls
&& !oldCN
.isEmpty()) {
2445 VClass
*oldCls
= VClass::FindClassNoCase(*oldCN
);
2446 if (!oldCls
) oldCls
= VClass::FindClassNoCase(*oldCN
.xstrip());
2447 if (!oldCls
|| !oldCls
->IsChildOf(eexCls
)) {
2448 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: source class `%s` in 'ReplaceActor' is invalid.", *sc
->GetLoc().toStringNoCol(), *oldCN
);
2450 // source is ok, check destination
2451 VClass
*newCls
= nullptr;
2452 bool newIsValid
= true;
2453 if (!newCN
.isEmpty() && newCN
.xstrip().isEmpty()) newCN
.clear(); // some morons are using a space as "nothing"
2454 if (!newCN
.isEmpty() && !newCN
.xstrip().strEquCI("none") && !newCN
.xstrip().strEquCI("null")) {
2455 newCls
= VClass::FindClassNoCase(*newCN
);
2456 if (!newCls
) newCls
= VClass::FindClassNoCase(*newCN
.xstrip());
2457 if (!newCls
|| !newCls
->IsChildOf(eexCls
)) {
2458 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: destination class `%s` for source class `%s` in 'ReplaceActor' is invalid.", *sc
->GetLoc().toStringNoCol(), *newCN
, *oldCN
);
2463 VSkillMonsterReplacement
&rp
= sdef
->Replacements
.alloc();
2464 rp
.oldClass
= oldCls
;
2465 rp
.newClass
= newCls
;
2469 } else if (sc
->Check("MonsterHealth")) {
2470 //GCon->Logf(NAME_Warning, "MAPINFO:%s: skill param 'MonsterHealth' is not implemented yet.", *sc->GetLoc().toStringNoCol());
2473 if (sc
->Float
< 0) GCon
->Logf(NAME_Warning
, "%s:MAPINFO: \"MonsterHealth\" should be positive", *sc
->GetLoc().toStringNoCol());
2474 sdef
->MonsterHealth
= sc
->Float
;
2475 } else if (sc
->Check("FriendlyHealth")) {
2476 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'FriendlyHealth' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2479 } else if (sc
->Check("NoPain")) {
2480 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'NoPain' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2481 } else if (sc
->Check("DefaultSkill")) {
2482 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'DefaultSkill' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2483 } else if (sc
->Check("ArmorFactor")) {
2484 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'ArmorFactor' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2487 } else if (sc
->Check("NoInfighting")) {
2488 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'NoInfighting' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2489 } else if (sc
->Check("TotalInfighting")) {
2490 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'TotalInfighting' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2491 } else if (sc
->Check("HealthFactor")) {
2492 //GCon->Logf(NAME_Warning, "MAPINFO:%s: skill param 'HealthFactor' is not implemented yet.", *sc->GetLoc().toStringNoCol());
2495 if (sc
->Float
< 0) GCon
->Logf(NAME_Warning
, "%s:MAPINFO: \"HealthFactor\" should be positive", *sc
->GetLoc().toStringNoCol());
2496 sdef
->HealthFactor
= sc
->Float
;
2497 } else if (sc
->Check("KickbackFactor")) {
2498 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'KickbackFactor' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2501 } else if (sc
->Check("NoMenu")) {
2502 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'NoMenu' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2503 } else if (sc
->Check("PlayerRespawn")) {
2504 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'PlayerRespawn' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2505 } else if (sc
->Check("ReplaceActor")) {
2506 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'ReplaceActor' is not implemented yet.", *sc
->GetLoc().toStringNoCol());
2508 while (!sc
->AtEnd()) {
2510 if (!sc
->Check(",")) break;
2514 sc
->Error(va("unknown MAPINFO skill command '%s'", *sc
->String
));
2519 if (sdef
->SpawnFilter
== 0) {
2520 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: skill param 'SpawnFilter' is not set for skill '%s'; assume UV.", *sc
->GetLoc().toStringNoCol(), *sdef
->MenuName
);
2521 sdef
->SpawnFilter
= 8; // UV
2526 //==========================================================================
2530 //==========================================================================
2531 static void ParseGameInfo (VScriptParser
*sc
) {
2532 //auto cmode = sc->IsCMode();
2533 //sc->SetCMode(true);
2535 //sc->SkipBracketed(true);
2537 if (sc
->AtEnd()) { sc
->Message("'}' not found"); break; }
2538 if (sc
->Check("}")) break;
2539 if (sc
->Check("PlayerClasses")) {
2540 MapInfoPlayerClasses
.clear();
2542 while (!sc
->AtEnd()) {
2544 if (sc
->String
.length()) {
2545 if (!FL_IsIgnoredPlayerClass(sc
->String
)) {
2546 MapInfoPlayerClasses
.append(VName(*sc
->String
));
2548 GCon
->Logf(NAME_Init
, "mapinfo player class '%s' ignored due to mod detector/mode orders", *sc
->String
);
2551 if (!sc
->Check(",")) break;
2553 } else if (sc
->Check("weaponslot")) {
2556 int slot
= sc
->Number
;
2557 if (slot
< 0 || slot
> 10) sc
->Message(va("ignoring gameinfo weaponslot %d", slot
));
2559 clist
.append("CmdSetSlot"); // arg0
2560 clist
.append(VStr(slot
)); // arg1 is number
2561 while (sc
->Check(",")) {
2562 if (!sc
->GetString()) sc
->Error("unexpected gameinfo end in mapinfo");
2563 if (!sc
->String
.isEmpty()) clist
.append(sc
->String
);
2565 // we only have so many slots
2566 if (slot
>= 0 && slot
<= 10) {
2567 GGameInfo
->eventCmdSetSlot(&clist
, false); // as gameinfo
2569 } else if (sc
->Check("ForceKillScripts")) {
2571 bool bval
= ExpectBool("ForceKillScripts", sc
);
2572 //mapInfoGameInfoInitial.bForceKillScripts = bval;
2573 if (bval
) GGameInfo
->Flags
|= VGameInfo::GIF_ForceKillScripts
; else GGameInfo
->Flags
&= ~VGameInfo::GIF_ForceKillScripts
;
2574 } else if (sc
->Check("SkyFlatName")) {
2577 if (sc
->String
.length() == 0 || sc
->String
.length() > 8) {
2578 miWarning(sc
, "invalid sky flat name: '%s'", *sc
->String
);
2580 GTextureManager
.SetSkyFlatName(sc
->String
);
2585 if (!sc
->String
.strEquCI("CanIgnoreZScript")) {
2586 sc
->Message(va("skipped gameinfo command '%s'", *sc
->String
));
2590 while (sc
->Check(",")) {
2591 if (!sc
->GetString()) sc
->Error("unexpected gameinfo end in mapinfo");
2595 //sc->SetCMode(cmode);
2599 //==========================================================================
2603 //==========================================================================
2604 static void ParseDamageType (VScriptParser
*sc
) {
2605 sc
->ExpectString(); // name
2606 if (sc
->String
.strEquCI("Normal")) sc
->String
= "None";
2607 VStr dfname
= sc
->String
;
2610 float factor
= 1.0f
;
2611 bool noarmor
= false;
2612 bool replace
= false;
2613 while (!checkEndBracket(sc
)) {
2614 if (sc
->Check("NoArmor")) {
2618 if (sc
->Check("ReplaceFactor")) {
2622 if (sc
->Check("Factor")) {
2628 //FIXME: implement this!
2629 if (sc
->Check("Obituary")) {
2634 sc
->Error(va("unknown DamageType field '%s'", *sc
->String
));
2637 // add or replace damage type
2638 VDamageFactor
*fc
= nullptr;
2639 for (auto &&df
: CustomDamageFactors
) {
2640 if (dfname
.strEquCI(*df
.DamageType
)) {
2647 fc
= &CustomDamageFactors
.alloc();
2648 memset((void *)fc
, 0, sizeof(VDamageFactor
));
2649 fc
->DamageType
= VName(*dfname
);
2652 fc
->Factor
= factor
;
2653 if (replace
) fc
->Flags
|= VDamageFactor::DF_REPLACE
;
2654 if (noarmor
) fc
->Flags
|= VDamageFactor::DF_NOARMOR
;
2656 hasCustomDamageFactors
= true;
2660 //==========================================================================
2664 // `sc->SourceLump` must be set
2666 //==========================================================================
2667 static void ParseMapInfo (VScriptParser
*sc
) {
2668 const unsigned int MaxStack
= 64;
2669 bool HexenMode
= false;
2670 VScriptParser
*scstack
[MaxStack
];
2671 unsigned int scsp
= 0;
2674 // set up default map info
2676 SetMapDefaults(Default
);
2679 while (!sc
->AtEnd()) {
2680 if (sc
->Check("map")) {
2681 ParseMap(sc
, HexenMode
, Default
);
2682 } else if (sc
->Check("defaultmap")) {
2683 SetMapDefaults(Default
);
2684 ParseMapCommon(sc
, &Default
, HexenMode
, Default
);
2685 } else if (sc
->Check("adddefaultmap")) {
2686 ParseMapCommon(sc
, &Default
, HexenMode
, Default
);
2687 } else if (sc
->Check("clusterdef")) {
2688 ParseClusterDef(sc
);
2689 } else if (sc
->Check("cluster")) {
2690 ParseClusterDef(sc
);
2691 } else if (sc
->Check("episode")) {
2692 ParseEpisodeDef(sc
);
2693 } else if (sc
->Check("clearepisodes")) {
2694 // clear episodes and clusterdefs
2695 EpisodeDefs
.Clear();
2696 ClusterDefs
.Clear();
2697 } else if (sc
->Check("skill")) {
2699 } else if (sc
->Check("clearskills")) {
2701 } else if (sc
->Check("include")) {
2703 //int lmp = W_CheckNumForFileName(sc->String);
2704 int lmp
= VScriptParser::FindIncludeLump(sc
->SourceLump
, sc
->String
);
2706 if (scsp
>= MaxStack
) {
2707 sc
->Error("mapinfo include nesting too deep");
2711 GCon
->Logf(NAME_Init
, "Including '%s'...", *sc
->String
);
2712 scstack
[scsp
++] = sc
;
2713 sc
= VScriptParser::NewWithLump(lmp
);
2715 sc
->Error(va("mapinfo include '%s' not found", *sc
->String
));
2719 } else if (sc
->Check("gameinfo")) {
2721 } else if (sc
->Check("damagetype")) {
2722 ParseDamageType(sc
);
2724 sc->Message("Unimplemented MAPINFO command `DamageType`");
2725 if (!sc->Check("{")) {
2727 sc->SkipBracketed();
2729 sc->SkipBracketed(true); // bracket eaten
2732 } else if (sc
->Check("GameSkyFlatName")) {
2733 if (sc
->Check("=")) {} // just in case
2735 if (sc
->String
.length() == 0 || sc
->String
.length() > 8) {
2736 miWarning(sc
, "invalid sky flat name: '%s'", *sc
->String
);
2738 GTextureManager
.SetSkyFlatName(sc
->String
);
2741 } else if (sc
->Check("intermission")) {
2742 sc
->Message("Unimplemented MAPINFO command `Intermission`");
2743 if (!sc
->Check("{")) {
2745 sc
->SkipBracketed();
2747 sc
->SkipBracketed(true); // bracket eaten
2750 } else if (sc->Check("gamedefaults")) {
2751 GCon->Logf(NAME_Warning, "Unimplemented MAPINFO section gamedefaults");
2752 sc->SkipBracketed();
2753 } else if (sc->Check("automap")) {
2754 GCon->Logf(NAME_Warning, "Unimplemented MAPINFO command Automap");
2755 sc->SkipBracketed();
2756 } else if (sc->Check("automap_overlay")) {
2757 GCon->Logf(NAME_Warning, "Unimplemented MAPINFO command automap_overlay");
2758 sc->SkipBracketed();
2760 } else if (sc
->Check("DoomEdNums")) {
2761 //GCon->Logf(NAME_Debug, "*** <%s> ***", *sc->String);
2762 //auto cmode = sc->IsCMode();
2763 //sc->SetCMode(true);
2766 if (checkEndBracket(sc
)) break;
2768 int num
= sc
->Number
;
2771 VStr clsname
= sc
->String
;
2774 int args
[5] = {0, 0, 0, 0, 0};
2775 if (sc
->Check(",")) {
2776 const VTextLocation loc
= sc
->GetLoc();
2778 if (sc
->Check("noskillflags")) { flags
|= mobjinfo_t::FlagNoSkill
; doit
= sc
->Check(","); }
2780 flags
|= mobjinfo_t::FlagSpecial
;
2782 VStr spcname
= sc
->String
;
2786 if (VStr::convertInt(*spcname
, &arg0
)) {
2789 args
[argn
++] = arg0
;
2791 while (sc
->Check(",")) {
2792 sc
->ExpectNumber(true); // with sign, why not
2793 if (argn
< 5) args
[argn
] = sc
->Number
;
2796 if (argn
> 5) GCon
->Logf(NAME_Warning
, "MAPINFO:%s: too many arguments (%d) to special '%s'", *loc
.toStringNoCol(), argn
, *spcname
);
2797 // find special number
2798 if (special
== 0) special
= FindScriptLineSpecialByName(spcname
);
2800 flags
&= ~mobjinfo_t::FlagSpecial
;
2801 GCon
->Logf(NAME_Warning
, "MAPINFO:%s: special '%s' not found", *loc
.toStringNoCol(), *spcname
);
2803 if (special
== -1) special
= 0; // special special ;-)
2806 //GCon->Logf(NAME_Debug, "MAPINFO: DOOMED: '%s', %d (%d)", *clsname, num, flags);
2807 appendNumFixup(DoomEdNumFixups
, clsname
, num
, flags
, special
, args
[0], args
[1], args
[2], args
[3], args
[4]);
2809 //sc->SetCMode(cmode);
2810 } else if (sc
->Check("SpawnNums")) {
2811 //auto cmode = sc->IsCMode();
2812 //sc->SetCMode(true);
2815 if (checkEndBracket(sc
)) break;
2817 int num
= sc
->Number
;
2820 appendNumFixup(SpawnNumFixups
, VStr(sc
->String
), num
);
2822 //sc->SetCMode(cmode);
2823 } else if (sc
->Check("author")) {
2826 VStr cmdname
= sc
->String
;
2828 if (sc
->Check("{")) {
2829 GCon
->Logf(NAME_Warning
, "Unimplemented MAPINFO command \"%s\"", *cmdname
.quote());
2830 sc
->SkipBracketed(true); // bracket eaten
2831 } else if (!sc
->String
.strEquCI("}")) { // some mappers cannot into mapinfo
2832 sc
->Error(va("Invalid command '%s'", *sc
->String
));
2836 sc
->Message(va("Some mapper cannot into proper mapinfo (stray \"%s\")", *sc
->String
.quote()));
2841 while (scsp
> 0) { delete sc
; sc
= scstack
[--scsp
]; }
2844 if (scsp
== 0) break;
2845 GCon
->Logf(NAME_Init
, "Finished included '%s'", *sc
->GetLoc().GetSourceFile());
2847 sc
= scstack
[--scsp
];
2853 //==========================================================================
2857 // `sc->SourceLump` must be set
2859 //==========================================================================
2860 static void ParseUMapinfo (VScriptParser
*sc
) {
2861 // set up default map info
2863 SetMapDefaults(Default
);
2864 bool HexenMode
= false;
2866 while (!sc
->AtEnd()) {
2867 if (sc
->Check("map")) {
2868 ParseMap(sc
, HexenMode
, Default
, true);
2870 sc
->Error(va("Unknown UMAPINFO key '%s'", *sc
->String
));
2878 //==========================================================================
2882 //==========================================================================
2883 static int QualifyMap (int map
) {
2884 return (map
< 0 || map
>= MapInfo
.length() ? 0 : map
);
2888 //==========================================================================
2892 //==========================================================================
2893 const VMapInfo
&P_GetMapInfo (VName map
) {
2894 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
2895 if (map
== MapInfo
[i
].LumpName
) return MapInfo
[i
];
2901 //==========================================================================
2905 //==========================================================================
2906 VStr
P_GetMapName (int map
) {
2907 return MapInfo
[QualifyMap(map
)].GetName();
2911 //==========================================================================
2913 // P_GetMapInfoIndexByLevelNum
2915 // Returns map info index given a level number
2917 //==========================================================================
2918 int P_GetMapIndexByLevelNum (int map
) {
2919 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
2920 if (MapInfo
[i
].LevelNum
== map
) return i
;
2927 //==========================================================================
2931 //==========================================================================
2932 VName
P_GetMapLumpName (int map
) {
2933 return MapInfo
[QualifyMap(map
)].LumpName
;
2937 //==========================================================================
2941 // Returns the map lump name given a warp map number.
2943 //==========================================================================
2944 VName
P_TranslateMap (int map
) {
2945 for (int i
= MapInfo
.length()-1; i
>= 0; --i
) {
2946 if (MapInfo
[i
].WarpTrans
== map
) return MapInfo
[i
].LumpName
;
2949 return (MapInfo
.length() > 0 ? MapInfo
[0].LumpName
: NAME_None
);
2953 //==========================================================================
2957 // Returns the map lump name given a warp map number.
2959 //==========================================================================
2960 VName
P_TranslateMapEx (int map
) {
2961 for (int i
= MapInfo
.length()-1; i
>= 0; --i
) {
2962 if (MapInfo
[i
].WarpTrans
== map
) return MapInfo
[i
].LumpName
;
2969 //==========================================================================
2971 // P_GetMapLumpNameByLevelNum
2973 // Returns the map lump name given a level number.
2975 //==========================================================================
2976 VName
P_GetMapLumpNameByLevelNum (int map
) {
2977 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
2978 if (MapInfo
[i
].LevelNum
== map
) return MapInfo
[i
].LumpName
;
2980 // not found, use map##
2981 return va("map%02d", map
);
2985 //==========================================================================
2989 //==========================================================================
2990 void P_PutMapSongLump (int map
, VName lumpName
) {
2991 FMapSongInfo
&ms
= MapSongList
.Alloc();
2992 ms
.MapName
= va("map%02d", map
);
2993 ms
.SongName
= lumpName
;
2997 //==========================================================================
3001 //==========================================================================
3002 const VClusterDef
*P_GetClusterDef (int Cluster
) {
3003 for (int i
= 0; i
< ClusterDefs
.length(); ++i
) {
3004 if (Cluster
== ClusterDefs
[i
].Cluster
) return &ClusterDefs
[i
];
3006 return &DefaultClusterDef
;
3010 //==========================================================================
3014 //==========================================================================
3015 int P_GetNumEpisodes () {
3016 return EpisodeDefs
.length();
3020 //==========================================================================
3024 //==========================================================================
3025 int P_GetNumMaps () {
3026 return MapInfo
.length();
3030 //==========================================================================
3034 //==========================================================================
3035 VMapInfo
*P_GetMapInfoPtr (int mapidx
) {
3036 return (mapidx
>= 0 && mapidx
< MapInfo
.length() ? &MapInfo
[mapidx
] : nullptr);
3040 //==========================================================================
3044 //==========================================================================
3045 VEpisodeDef
*P_GetEpisodeDef (int Index
) {
3046 return &EpisodeDefs
[Index
];
3050 //==========================================================================
3054 //==========================================================================
3055 int P_GetNumSkills () {
3056 return SkillDefs
.length();
3060 //==========================================================================
3064 //==========================================================================
3065 const VSkillDef
*P_GetSkillDef (int Index
) {
3066 return &SkillDefs
[Index
];
3070 //==========================================================================
3072 // P_GetMusicLumpNames
3074 //==========================================================================
3075 void P_GetMusicLumpNames (TArray
<FReplacedString
> &List
) {
3076 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
3077 const char *MName
= *MapInfo
[i
].SongLump
;
3078 if (MName
[0] == 'd' && MName
[1] == '_') {
3079 FReplacedString
&R
= List
.Alloc();
3088 //==========================================================================
3090 // P_ReplaceMusicLumpNames
3092 //==========================================================================
3093 void P_ReplaceMusicLumpNames (TArray
<FReplacedString
> &List
) {
3094 for (int i
= 0; i
< List
.length(); ++i
) {
3095 if (List
[i
].Replaced
) {
3096 MapInfo
[List
[i
].Index
].SongLump
= VName(*(VStr("d_")+List
[i
].New
), VName::AddLower8
);
3102 //==========================================================================
3106 //==========================================================================
3107 void P_SetParTime (VName Map
, int Par
) {
3108 if (Map
== NAME_None
|| Par
< 0) return;
3109 if (mapinfoParsed
) {
3110 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
3111 if (MapInfo
[i
].LumpName
== NAME_None
) continue;
3112 if (VStr::ICmp(*MapInfo
[i
].LumpName
, *Map
) == 0) {
3113 MapInfo
[i
].ParTime
= Par
;
3117 GCon
->Logf(NAME_Warning
, "No such map '%s' for par time", *Map
);
3119 ParTimeInfo
&pi
= partimes
.alloc();
3126 //==========================================================================
3130 //==========================================================================
3131 bool IsMapPresent (VName MapName
) {
3132 if (W_CheckNumForName(MapName
) >= 0) return true;
3133 VStr FileName
= va("maps/%s.wad", *MapName
);
3134 if (FL_FileExists(FileName
)) return true;
3139 //==========================================================================
3143 //==========================================================================
3145 for (int i
= 0; i
< MapInfo
.length(); ++i
) {
3146 if (IsMapPresent(MapInfo
[i
].LumpName
)) {
3147 GCon
->Log(VStr(MapInfo
[i
].LumpName
)+" - "+(MapInfo
[i
].Flags
&VLevelInfo::LIF_LookupName
? GLanguage
[*MapInfo
[i
].Name
] : MapInfo
[i
].Name
));
3153 //==========================================================================
3157 //==========================================================================
3158 void ShutdownMapInfo () {
3159 DefaultMap
.Name
.Clean();
3161 MapSongList
.Clear();
3162 ClusterDefs
.Clear();
3163 EpisodeDefs
.Clear();