1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: mapinfo.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "sv_local.h"
30 
31 // MACROS ------------------------------------------------------------------
32 
33 enum
34 {
35 	CD_STARTTRACK,
36 	CD_END1TRACK,
37 	CD_END2TRACK,
38 	CD_END3TRACK,
39 	CD_INTERTRACK,
40 	CD_TITLETRACK,
41 };
42 
43 // TYPES -------------------------------------------------------------------
44 
45 struct FMapSongInfo
46 {
47 	VName	MapName;
48 	VName	SongName;
49 };
50 
51 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
52 
53 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
54 
55 VName P_TranslateMap(int map);
56 
57 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
58 
59 static void ParseMapInfo(VScriptParser* sc);
60 
61 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
62 
63 // PUBLIC DATA DEFINITIONS -------------------------------------------------
64 
65 // PRIVATE DATA DEFINITIONS ------------------------------------------------
66 
67 static mapInfo_t			DefaultMap;
68 static TArray<mapInfo_t>	MapInfo;
69 static TArray<FMapSongInfo>	MapSongList;
70 static VClusterDef			DefaultClusterDef;
71 static TArray<VClusterDef>	ClusterDefs;
72 static TArray<VEpisodeDef>	EpisodeDefs;
73 static TArray<VSkillDef>	SkillDefs;
74 // Non-level specific song cd track numbers
75 static int					cd_NonLevelTracks[6];
76 
77 // CODE --------------------------------------------------------------------
78 
79 //==========================================================================
80 //
81 // InitMapInfo
82 //
83 //==========================================================================
84 
InitMapInfo()85 void InitMapInfo()
86 {
87 	guard(InitMapInfo);
88 	for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
89 		Lump = W_IterateNS(Lump, WADNS_Global))
90 	{
91 		if (W_LumpName(Lump) == NAME_mapinfo)
92 		{
93 			ParseMapInfo(new VScriptParser(*W_LumpName(Lump),
94 				W_CreateLumpReaderNum(Lump)));
95 		}
96 	}
97 	//	Optionally parse script file.
98 	if (fl_devmode && FL_FileExists("scripts/mapinfo.txt"))
99 	{
100 		ParseMapInfo(new VScriptParser("scripts/mapinfo.txt",
101 			FL_OpenFileRead("scripts/mapinfo.txt")));
102 	}
103 
104 	for (int i = 0; i < MapInfo.Num(); i++)
105 	{
106 		if (VStr(MapInfo[i].NextMap).StartsWith("&wt@"))
107 		{
108 			MapInfo[i].NextMap = P_TranslateMap(atoi(
109 				*MapInfo[i].NextMap + 4));
110 		}
111 		if (VStr(MapInfo[i].SecretMap).StartsWith("&wt@"))
112 		{
113 			MapInfo[i].SecretMap = P_TranslateMap(atoi(
114 				*MapInfo[i].SecretMap + 4));
115 		}
116 	}
117 	for (int i = 0; i < EpisodeDefs.Num(); i++)
118 	{
119 		if (VStr(EpisodeDefs[i].Name).StartsWith("&wt@"))
120 		{
121 			EpisodeDefs[i].Name = P_TranslateMap(atoi(
122 				*EpisodeDefs[i].Name + 4));
123 		}
124 		if (VStr(EpisodeDefs[i].TeaserName).StartsWith("&wt@"))
125 		{
126 			EpisodeDefs[i].TeaserName = P_TranslateMap(atoi(
127 				*EpisodeDefs[i].TeaserName + 4));
128 		}
129 	}
130 
131 	//	Set up default map info returned for maps that have not defined in
132 	// MAPINFO
133 	DefaultMap.Name = "Unnamed";
134 	DefaultMap.Sky1Texture = GTextureManager.CheckNumForName("sky1",
135 		TEXTYPE_Wall, true, true);
136 	DefaultMap.Sky2Texture = DefaultMap.Sky1Texture;
137 	DefaultMap.FadeTable = NAME_colormap;
138 	DefaultMap.HorizWallShade = -8;
139 	DefaultMap.VertWallShade = 8;
140 	unguard;
141 }
142 
143 //==========================================================================
144 //
145 //	SetMapDefaults
146 //
147 //==========================================================================
148 
SetMapDefaults(mapInfo_t & Info)149 static void SetMapDefaults(mapInfo_t& Info)
150 {
151 	guard(SetMapDefaults);
152 	Info.LumpName = NAME_None;
153 	Info.Name = VStr();
154 	Info.LevelNum = 0;
155 	Info.Cluster = 0;
156 	Info.WarpTrans = 0;
157 	Info.NextMap = NAME_None;
158 	Info.SecretMap = NAME_None;
159 	Info.SongLump = NAME_None;
160 	Info.CDTrack = 0;
161 	Info.Sky1Texture = GTextureManager.DefaultTexture;
162 	Info.Sky2Texture = GTextureManager.DefaultTexture;
163 	Info.Sky1ScrollDelta = 0;
164 	Info.Sky2ScrollDelta = 0;
165 	Info.SkyBox = NAME_None;
166 	Info.FadeTable = NAME_colormap;
167 	Info.Fade = 0;
168 	Info.OutsideFog = 0;
169 	Info.Gravity = 0;
170 	Info.AirControl = 0;
171 	Info.Flags = 0;
172 	Info.Flags2 = 0;
173 	Info.TitlePatch = NAME_None;
174 	Info.ParTime = 0;
175 	Info.SuckTime = 0;
176 	Info.HorizWallShade = -8;
177 	Info.VertWallShade = 8;
178 	Info.Infighting = 0;
179 	Info.SpecialActions.Clear();
180 	Info.RedirectType = NAME_None;
181 	Info.RedirectMap = NAME_None;
182 	Info.ExitPic = NAME_None;
183 	Info.EnterPic = NAME_None;
184 	Info.InterMusic = NAME_None;
185 
186 	if (GGameInfo->Flags & VGameInfo::GIF_DefaultLaxMonsterActivation)
187 	{
188 		Info.Flags2 |= MAPINFOF2_LaxMonsterActivation;
189 	}
190 	unguard;
191 }
192 
193 //==========================================================================
194 //
195 //	ParseNextMapName
196 //
197 //==========================================================================
198 
ParseNextMapName(VScriptParser * sc,bool HexenMode)199 static VName ParseNextMapName(VScriptParser* sc, bool HexenMode)
200 {
201 	guard(ParseNextMapName);
202 	if (sc->CheckNumber())
203 	{
204 		if (HexenMode)
205 			return va("&wt@%02d", sc->Number);
206 		else
207 			return va("map%02d", sc->Number);
208 	}
209 	else if (sc->Check("endbunny"))
210 	{
211 		return "EndGameBunny";
212 	}
213 	else if (sc->Check("endcast"))
214 	{
215 		return "EndGameCast";
216 	}
217 	else if (sc->Check("enddemon"))
218 	{
219 		return "EndGameDemon";
220 	}
221 	else if (sc->Check("endchess"))
222 	{
223 		return "EndGameChess";
224 	}
225 	else if (sc->Check("endunderwater"))
226 	{
227 		return "EndGameUnderwater";
228 	}
229 	else if (sc->Check("endbuystrife"))
230 	{
231 		return "EndGameBuyStrife";
232 	}
233 	else if (sc->Check("endpic"))
234 	{
235 		sc->ExpectName8();
236 		return va("EndGameCustomPic%s", *sc->Name8);
237 	}
238 	else
239 	{
240 		sc->ExpectString();
241 		if (sc->String.ToLower().StartsWith("endgame"))
242 		{
243 			switch (sc->String[7])
244 			{
245 			case '1':
246 				return "EndGamePic1";
247 			case '2':
248 				return "EndGamePic2";
249 			case '3':
250 				return "EndGameBunny";
251 			case 'c':
252 			case 'C':
253 				return "EndGameCast";
254 			case 'w':
255 			case 'W':
256 				return "EndGameUnderwater";
257 			case 's':
258 			case 'S':
259 				return "EndGameStrife";
260 			default:
261 				return "EndGamePic3";
262 			}
263 		}
264 		else
265 		{
266 			return VName(*sc->String, VName::AddLower8);
267 		}
268 	}
269 	unguard;
270 }
271 
272 //==========================================================================
273 //
274 //	DoCompatFlag
275 //
276 //==========================================================================
277 
DoCompatFlag(VScriptParser * sc,mapInfo_t * info,int Flag)278 static void DoCompatFlag(VScriptParser* sc, mapInfo_t* info, int Flag)
279 {
280 	guard(DoCompatFlag);
281 	int Set = 1;
282 	if (sc->CheckNumber())
283 	{
284 		Set = sc->Number;
285 	}
286 	if (Set)
287 	{
288 		info->Flags2 |= Flag;
289 	}
290 	else
291 	{
292 		info->Flags2 &= ~Flag;
293 	}
294 	unguard;
295 }
296 
297 //==========================================================================
298 //
299 //	ParseMapCommon
300 //
301 //==========================================================================
302 
ParseMapCommon(VScriptParser * sc,mapInfo_t * info,bool & HexenMode)303 static void ParseMapCommon(VScriptParser* sc, mapInfo_t* info, bool& HexenMode)
304 {
305 	guard(ParseMapCommon);
306 	// Process optional tokens
307 	while (1)
308 	{
309 		if (sc->Check("levelnum"))
310 		{
311 			sc->ExpectNumber();
312 			info->LevelNum = sc->Number;
313 		}
314 		else if (sc->Check("cluster"))
315 		{
316 			sc->ExpectNumber();
317 			info->Cluster = sc->Number;
318 			if (P_GetClusterDef(info->Cluster) == &DefaultClusterDef)
319 			{
320 				//	Add empty cluster def if it doesn't exist yet.
321 				VClusterDef& C = ClusterDefs.Alloc();
322 				C.Cluster = info->Cluster;
323 				C.Flags = 0;
324 				C.EnterText = VStr();
325 				C.ExitText = VStr();
326 				C.Flat = NAME_None;
327 				C.Music = NAME_None;
328 				C.CDTrack = 0;
329 				C.CDId = 0;
330 				if (HexenMode)
331 				{
332 					C.Flags |= CLUSTERF_Hub;
333 				}
334 			}
335 		}
336 		else if (sc->Check("warptrans"))
337 		{
338 			sc->ExpectNumber();
339 			info->WarpTrans = sc->Number;
340 		}
341 		else if (sc->Check("next"))
342 		{
343 			info->NextMap = ParseNextMapName(sc, HexenMode);
344 		}
345 		else if (sc->Check("secret") || sc->Check("secretnext"))
346 		{
347 			info->SecretMap = ParseNextMapName(sc, HexenMode);
348 		}
349 		else if (sc->Check("sky1"))
350 		{
351 			sc->ExpectName8();
352 			info->Sky1Texture = GTextureManager.NumForName(
353 				sc->Name8, TEXTYPE_Wall, true, false);
354 			sc->ExpectFloat();
355 			if (HexenMode)
356 			{
357 				sc->Float /= 256.0;
358 			}
359 			info->Sky1ScrollDelta = sc->Float * 35.0;
360 		}
361 		else if (sc->Check("sky2"))
362 		{
363 			sc->ExpectName8();
364 			info->Sky2Texture = GTextureManager.NumForName(
365 				sc->Name8, TEXTYPE_Wall, true, false);
366 			sc->ExpectFloat();
367 			if (HexenMode)
368 			{
369 				sc->Float /= 256.0;
370 			}
371 			info->Sky2ScrollDelta = sc->Float * 35.0;
372 		}
373 		else if (sc->Check("skybox"))
374 		{
375 			sc->ExpectString();
376 			info->SkyBox = *sc->String;
377 		}
378 		else if (sc->Check("doublesky"))
379 		{
380 			info->Flags |= MAPINFOF_DoubleSky;
381 		}
382 		else if (sc->Check("lightning"))
383 		{
384 			info->Flags |= MAPINFOF_Lightning;
385 		}
386 		else if (sc->Check("forcenoskystretch"))
387 		{
388 			info->Flags |= MAPINFOF_ForceNoSkyStretch;
389 		}
390 		else if (sc->Check("fadetable"))
391 		{
392 			sc->ExpectName8();
393 			info->FadeTable = sc->Name8;
394 		}
395 		else if (sc->Check("fade"))
396 		{
397 			sc->ExpectString();
398 			info->Fade = M_ParseColour(sc->String);
399 		}
400 		else if (sc->Check("outsidefog"))
401 		{
402 			sc->ExpectString();
403 			info->OutsideFog = M_ParseColour(sc->String);
404 		}
405 		else if (sc->Check("music"))
406 		{
407 			sc->ExpectName8();
408 			info->SongLump = sc->Name8;
409 		}
410 		else if (sc->Check("cdtrack"))
411 		{
412 			sc->ExpectNumber();
413 			info->CDTrack = sc->Number;
414 		}
415 		else if (sc->Check("gravity"))
416 		{
417 			sc->ExpectNumber();
418 			info->Gravity = (float)sc->Number;
419 		}
420 		else if (sc->Check("aircontrol"))
421 		{
422 			sc->ExpectFloat();
423 			info->AirControl = sc->Float;
424 		}
425 		else if (sc->Check("map07special"))
426 		{
427 			info->Flags |= MAPINFOF_Map07Special;
428 		}
429 		else if (sc->Check("baronspecial"))
430 		{
431 			info->Flags |= MAPINFOF_BaronSpecial;
432 		}
433 		else if (sc->Check("cyberdemonspecial"))
434 		{
435 			info->Flags |= MAPINFOF_CyberDemonSpecial;
436 		}
437 		else if (sc->Check("spidermastermindspecial"))
438 		{
439 			info->Flags |= MAPINFOF_SpiderMastermindSpecial;
440 		}
441 		else if (sc->Check("minotaurspecial"))
442 		{
443 			info->Flags |= MAPINFOF_MinotaurSpecial;
444 		}
445 		else if (sc->Check("dsparilspecial"))
446 		{
447 			info->Flags |= MAPINFOF_DSparilSpecial;
448 		}
449 		else if (sc->Check("ironlichspecial"))
450 		{
451 			info->Flags |= MAPINFOF_IronLichSpecial;
452 		}
453 		else if (sc->Check("specialaction_exitlevel"))
454 		{
455 			info->Flags &= ~(MAPINFOF_SpecialActionOpenDoor |
456 				MAPINFOF_SpecialActionLowerFloor);
457 		}
458 		else if (sc->Check("specialaction_opendoor"))
459 		{
460 			info->Flags &= ~MAPINFOF_SpecialActionLowerFloor;
461 			info->Flags |= MAPINFOF_SpecialActionOpenDoor;
462 		}
463 		else if (sc->Check("specialaction_lowerfloor"))
464 		{
465 			info->Flags |= MAPINFOF_SpecialActionLowerFloor;
466 			info->Flags &= ~MAPINFOF_SpecialActionOpenDoor;
467 		}
468 		else if (sc->Check("specialaction_killmonsters"))
469 		{
470 			info->Flags |= MAPINFOF_SpecialActionKillMonsters;
471 		}
472 		else if (sc->Check("intermission"))
473 		{
474 			info->Flags &= ~MAPINFOF_NoIntermission;
475 		}
476 		else if (sc->Check("nointermission"))
477 		{
478 			info->Flags |= MAPINFOF_NoIntermission;
479 		}
480 		else if (sc->Check("titlepatch"))
481 		{
482 			sc->ExpectName8();
483 			info->TitlePatch = sc->Name8;
484 		}
485 		else if (sc->Check("par"))
486 		{
487 			sc->ExpectNumber();
488 			info->ParTime = sc->Number;
489 		}
490 		else if (sc->Check("sucktime"))
491 		{
492 			sc->ExpectNumber();
493 			info->SuckTime = sc->Number;
494 		}
495 		else if (sc->Check("nosoundclipping"))
496 		{
497 			//	Ignored
498 		}
499 		else if (sc->Check("allowmonstertelefrags"))
500 		{
501 			info->Flags |= MAPINFOF_AllowMonsterTelefrags;
502 		}
503 		else if (sc->Check("noallies"))
504 		{
505 			info->Flags |= MAPINFOF_NoAllies;
506 		}
507 		else if (sc->Check("fallingdamage"))
508 		{
509 			info->Flags &= ~(MAPINFOF_OldFallingDamage |
510 				MAPINFOF_StrifeFallingDamage);
511 			info->Flags |= MAPINFOF_FallingDamage;
512 		}
513 		else if (sc->Check("oldfallingdamage") ||
514 			sc->Check("forcefallingdamage"))
515 		{
516 			info->Flags &= ~(MAPINFOF_FallingDamage |
517 				MAPINFOF_StrifeFallingDamage);
518 			info->Flags |= MAPINFOF_OldFallingDamage;
519 		}
520 		else if (sc->Check("strifefallingdamage"))
521 		{
522 			info->Flags &= ~(MAPINFOF_OldFallingDamage |
523 				MAPINFOF_FallingDamage);
524 			info->Flags |= MAPINFOF_StrifeFallingDamage;
525 		}
526 		else if (sc->Check("nofallingdamage"))
527 		{
528 			info->Flags &= ~(MAPINFOF_OldFallingDamage |
529 				MAPINFOF_StrifeFallingDamage | MAPINFOF_FallingDamage);
530 		}
531 		else if (sc->Check("monsterfallingdamage"))
532 		{
533 			info->Flags |= MAPINFOF_MonsterFallingDamage;
534 		}
535 		else if (sc->Check("nomonsterfallingdamage"))
536 		{
537 			info->Flags &= ~MAPINFOF_MonsterFallingDamage;
538 		}
539 		else if (sc->Check("deathslideshow"))
540 		{
541 			info->Flags |= MAPINFOF_DeathSlideShow;
542 		}
543 		else if (sc->Check("allowfreelook"))
544 		{
545 			info->Flags &= ~MAPINFOF_NoFreelook;
546 		}
547 		else if (sc->Check("nofreelook"))
548 		{
549 			info->Flags |= MAPINFOF_NoFreelook;
550 		}
551 		else if (sc->Check("allowjump"))
552 		{
553 			info->Flags &= ~MAPINFOF_NoJump;
554 		}
555 		else if (sc->Check("nojump"))
556 		{
557 			info->Flags |= MAPINFOF_NoJump;
558 		}
559 		else if (sc->Check("noautosequences"))
560 		{
561 			info->Flags |= MAPINFOF_NoAutoSndSeq;
562 		}
563 		else if (sc->Check("activateowndeathspecials"))
564 		{
565 			info->Flags |= MAPINFOF_ActivateOwnSpecial;
566 		}
567 		else if (sc->Check("killeractivatesdeathspecials"))
568 		{
569 			info->Flags &= ~MAPINFOF_ActivateOwnSpecial;
570 		}
571 		else if (sc->Check("missilesactivateimpactlines"))
572 		{
573 			info->Flags |= MAPINFOF_MissilesActivateImpact;
574 		}
575 		else if (sc->Check("missileshootersactivetimpactlines"))
576 		{
577 			info->Flags &= ~MAPINFOF_MissilesActivateImpact;
578 		}
579 		else if (sc->Check("filterstarts"))
580 		{
581 			info->Flags |= MAPINFOF_FilterStarts;
582 		}
583 		else if (sc->Check("infiniteflightpowerup"))
584 		{
585 			info->Flags |= MAPINFOF_InfiniteFlightPowerup;
586 		}
587 		else if (sc->Check("noinfiniteflightpowerup"))
588 		{
589 			info->Flags &= ~MAPINFOF_InfiniteFlightPowerup;
590 		}
591 		else if (sc->Check("clipmidtextures"))
592 		{
593 			info->Flags |= MAPINFOF_ClipMidTex;
594 		}
595 		else if (sc->Check("wrapmidtextures"))
596 		{
597 			info->Flags |= MAPINFOF_WrapMidTex;
598 		}
599 		else if (sc->Check("keepfullinventory"))
600 		{
601 			info->Flags |= MAPINFOF_KeepFullInventory;
602 		}
603 		else if (sc->Check("compat_shorttex"))
604 		{
605 			DoCompatFlag(sc, info, MAPINFOF2_CompatShortTex);
606 		}
607 		else if (sc->Check("compat_stairs"))
608 		{
609 			DoCompatFlag(sc, info, MAPINFOF2_CompatStairs);
610 		}
611 		else if (sc->Check("compat_limitpain"))
612 		{
613 			DoCompatFlag(sc, info, MAPINFOF2_CompatLimitPain);
614 		}
615 		else if (sc->Check("compat_nopassover"))
616 		{
617 			DoCompatFlag(sc, info, MAPINFOF2_CompatNoPassOver);
618 		}
619 		else if (sc->Check("compat_notossdrops"))
620 		{
621 			DoCompatFlag(sc, info, MAPINFOF2_CompatNoTossDrops);
622 		}
623 		else if (sc->Check("compat_useblocking"))
624 		{
625 			DoCompatFlag(sc, info, MAPINFOF2_CompatUseBlocking);
626 		}
627 		else if (sc->Check("compat_nodoorlight"))
628 		{
629 			DoCompatFlag(sc, info, MAPINFOF2_CompatNoDoorLight);
630 		}
631 		else if (sc->Check("compat_ravenscroll"))
632 		{
633 			DoCompatFlag(sc, info, MAPINFOF2_CompatRavenScroll);
634 		}
635 		else if (sc->Check("compat_soundtarget"))
636 		{
637 			DoCompatFlag(sc, info, MAPINFOF2_CompatSoundTarget);
638 		}
639 		else if (sc->Check("compat_dehhealth"))
640 		{
641 			DoCompatFlag(sc, info, MAPINFOF2_CompatDehHealth);
642 		}
643 		else if (sc->Check("compat_trace"))
644 		{
645 			DoCompatFlag(sc, info, MAPINFOF2_CompatTrace);
646 		}
647 		else if (sc->Check("compat_dropoff"))
648 		{
649 			DoCompatFlag(sc, info, MAPINFOF2_CompatDropOff);
650 		}
651 		else if (sc->Check("compat_boomscroll") ||
652 			sc->Check("additive_scrollers"))
653 		{
654 			DoCompatFlag(sc, info, MAPINFOF2_CompatBoomScroll);
655 		}
656 		else if (sc->Check("compat_invisibility"))
657 		{
658 			DoCompatFlag(sc, info, MAPINFOF2_CompatInvisibility);
659 		}
660 		else if (sc->Check("evenlighting"))
661 		{
662 			info->HorizWallShade = 0;
663 			info->VertWallShade = 0;
664 		}
665 		else if (sc->Check("vertwallshade"))
666 		{
667 			sc->ExpectNumber();
668 			info->VertWallShade = MID(-128, sc->Number, 127);
669 		}
670 		else if (sc->Check("horizwallshade"))
671 		{
672 			sc->ExpectNumber();
673 			info->HorizWallShade = MID(-128, sc->Number, 127);
674 		}
675 		else if (sc->Check("noinfighting"))
676 		{
677 			info->Infighting = -1;
678 		}
679 		else if (sc->Check("normalinfighting"))
680 		{
681 			info->Infighting = 0;
682 		}
683 		else if (sc->Check("totalinfighting"))
684 		{
685 			info->Infighting = 1;
686 		}
687 		else if (sc->Check("specialaction"))
688 		{
689 			VMapSpecialAction& A = info->SpecialActions.Alloc();
690 			sc->SetCMode(true);
691 			sc->ExpectString();
692 			A.TypeName = *sc->String.ToLower();
693 			sc->Expect(",");
694 			sc->ExpectString();
695 			A.Special = 0;
696 			for (int i = 0; i < LineSpecialInfos.Num(); i++)
697 			{
698 				if (!LineSpecialInfos[i].Name.ICmp(sc->String))
699 				{
700 					A.Special = LineSpecialInfos[i].Number;
701 					break;
702 				}
703 			}
704 			if (!A.Special)
705 			{
706 				GCon->Logf("Unknown action special %s", *sc->String);
707 			}
708 			memset(A.Args, 0, sizeof(A.Args));
709 			for (int i = 0; i < 5 && sc->Check(","); i++)
710 			{
711 				sc->ExpectNumber();
712 				A.Args[i] = sc->Number;
713 			}
714 			sc->SetCMode(false);
715 		}
716 		else if (sc->Check("redirect"))
717 		{
718 			sc->ExpectString();
719 			info->RedirectType = *sc->String.ToLower();
720 			info->RedirectMap = ParseNextMapName(sc, HexenMode);
721 		}
722 		else if (sc->Check("strictmonsteractivation"))
723 		{
724 			info->Flags2 &= ~MAPINFOF2_LaxMonsterActivation;
725 			info->Flags2 |= MAPINFOF2_HaveMonsterActivation;
726 		}
727 		else if (sc->Check("laxmonsteractivation"))
728 		{
729 			info->Flags2 |= MAPINFOF2_LaxMonsterActivation;
730 			info->Flags2 |= MAPINFOF2_HaveMonsterActivation;
731 		}
732 		else if (sc->Check("interpic") || sc->Check("exitpic"))
733 		{
734 			sc->ExpectName8();
735 			info->ExitPic = *sc->String.ToLower();
736 		}
737 		else if (sc->Check("enterpic"))
738 		{
739 			sc->ExpectName8();
740 			info->EnterPic = *sc->String.ToLower();
741 		}
742 		else if (sc->Check("intermusic"))
743 		{
744 			sc->ExpectString();
745 			info->InterMusic = *sc->String.ToLower();
746 		}
747 		else if (sc->Check("cd_start_track"))
748 		{
749 			sc->ExpectNumber();
750 			cd_NonLevelTracks[CD_STARTTRACK] = sc->Number;
751 		}
752 		else if (sc->Check("cd_end1_track"))
753 		{
754 			sc->ExpectNumber();
755 			cd_NonLevelTracks[CD_END1TRACK] = sc->Number;
756 		}
757 		else if (sc->Check("cd_end2_track"))
758 		{
759 			sc->ExpectNumber();
760 			cd_NonLevelTracks[CD_END2TRACK] = sc->Number;
761 		}
762 		else if (sc->Check("cd_end3_track"))
763 		{
764 			sc->ExpectNumber();
765 			cd_NonLevelTracks[CD_END3TRACK] = sc->Number;
766 		}
767 		else if (sc->Check("cd_intermission_track"))
768 		{
769 			sc->ExpectNumber();
770 			cd_NonLevelTracks[CD_INTERTRACK] = sc->Number;
771 		}
772 		else if (sc->Check("cd_title_track"))
773 		{
774 			sc->ExpectNumber();
775 			cd_NonLevelTracks[CD_TITLETRACK] = sc->Number;
776 		}
777 		//	These are stubs for now.
778 		else if (sc->Check("cdid"))
779 		{
780 			GCon->Logf("Unimplemented MAPINFO comand cdid");
781 			sc->ExpectString();
782 		}
783 		else if (sc->Check("noinventorybar"))
784 		{
785 			GCon->Logf("Unimplemented MAPINFO comand noinventorybar");
786 		}
787 		else if (sc->Check("airsupply"))
788 		{
789 			GCon->Logf("Unimplemented MAPINFO comand airsupply");
790 			sc->ExpectNumber();
791 		}
792 		else if (sc->Check("sndseq"))
793 		{
794 			GCon->Logf("Unimplemented MAPINFO comand sndseq");
795 			sc->ExpectName8();
796 		}
797 		else if (sc->Check("sndinfo"))
798 		{
799 			GCon->Logf("Unimplemented MAPINFO comand sndinfo");
800 			sc->ExpectName8();
801 		}
802 		else if (sc->Check("soundinfo"))
803 		{
804 			GCon->Logf("Unimplemented MAPINFO comand soundinfo");
805 			sc->ExpectName8();
806 		}
807 		else if (sc->Check("allowcrouch"))
808 		{
809 			GCon->Logf("Unimplemented MAPINFO comand allowcrouch");
810 		}
811 		else if (sc->Check("nocrouch"))
812 		{
813 			GCon->Logf("Unimplemented MAPINFO comand nocrouch");
814 		}
815 		else if (sc->Check("pausemusicinmenus"))
816 		{
817 			GCon->Logf("Unimplemented MAPINFO comand pausemusicinmenus");
818 		}
819 		else if (sc->Check("bordertexture"))
820 		{
821 			GCon->Logf("Unimplemented MAPINFO comand bordertexture");
822 			sc->ExpectName8();
823 		}
824 		else if (sc->Check("f1"))
825 		{
826 			GCon->Logf("Unimplemented MAPINFO comand f1");
827 			sc->ExpectString();
828 		}
829 		else if (sc->Check("allowrespawn"))
830 		{
831 			GCon->Logf("Unimplemented MAPINFO comand allowrespawn");
832 		}
833 		else if (sc->Check("teamdamage"))
834 		{
835 			GCon->Logf("Unimplemented MAPINFO comand teamdamage");
836 			sc->ExpectFloat();
837 		}
838 		else if (sc->Check("fogdensity"))
839 		{
840 			GCon->Logf("Unimplemented MAPINFO comand fogdensity");
841 			sc->ExpectNumber();
842 		}
843 		else if (sc->Check("outsidefogdensity"))
844 		{
845 			GCon->Logf("Unimplemented MAPINFO comand outsidefogdensity");
846 			sc->ExpectNumber();
847 		}
848 		else if (sc->Check("skyfog"))
849 		{
850 			GCon->Logf("Unimplemented MAPINFO comand skyfog");
851 			sc->ExpectNumber();
852 		}
853 		else if (sc->Check("teamplayon"))
854 		{
855 			GCon->Logf("Unimplemented MAPINFO comand teamplayon");
856 		}
857 		else if (sc->Check("teamplayoff"))
858 		{
859 			GCon->Logf("Unimplemented MAPINFO comand teamplayoff");
860 		}
861 		else if (sc->Check("checkswitchrange"))
862 		{
863 			GCon->Logf("Unimplemented MAPINFO comand checkswitchrange");
864 		}
865 		else if (sc->Check("nocheckswitchrange"))
866 		{
867 			GCon->Logf("Unimplemented MAPINFO comand nocheckswitchrange");
868 		}
869 		else if (sc->Check("translator"))
870 		{
871 			GCon->Logf("Unimplemented MAPINFO comand translator");
872 			sc->ExpectString();
873 		}
874 		else if (sc->Check("unfreezesingleplayerconversations"))
875 		{
876 			GCon->Logf("Unimplemented MAPINFO comand unfreezesingleplayerconversations");
877 		}
878 		else
879 		{
880 			break;
881 		}
882 	}
883 
884 	//	Second sky defaults to first sky
885 	if (info->Sky2Texture == GTextureManager.DefaultTexture)
886 	{
887 		info->Sky2Texture = info->Sky1Texture;
888 	}
889 
890 	if (info->Flags & MAPINFOF_DoubleSky)
891 	{
892 		GTextureManager.SetFrontSkyLayer(info->Sky1Texture);
893 	}
894 	unguard;
895 }
896 
897 //==========================================================================
898 //
899 //	ParseMap
900 //
901 //==========================================================================
902 
ParseMap(VScriptParser * sc,bool & HexenMode,mapInfo_t & Default)903 static void ParseMap(VScriptParser* sc, bool& HexenMode, mapInfo_t& Default)
904 {
905 	guard(ParseMap);
906 	mapInfo_t* info = NULL;
907 	VName MapLumpName;
908 	if (sc->CheckNumber())
909 	{
910 		//	Map number, for Hexen compatibility
911 		HexenMode = true;
912 		if (sc->Number < 1 || sc->Number > 99)
913 		{
914 			sc->Error("Map number out or range");
915 		}
916 		MapLumpName = va("map%02d", sc->Number);
917 	}
918 	else
919 	{
920 		//	Map name
921 		sc->ExpectName8();
922 		MapLumpName = sc->Name8;
923 	}
924 
925 	//	Check for replaced map info.
926 	for (int i = 0; i < MapInfo.Num(); i++)
927 	{
928 		if (MapLumpName == MapInfo[i].LumpName)
929 		{
930 			info = &MapInfo[i];
931 			break;
932 		}
933 	}
934 	if (!info)
935 	{
936 		info = &MapInfo.Alloc();
937 	}
938 
939 	// Copy defaults to current map definition
940 	info->LumpName = MapLumpName;
941 	info->LevelNum = Default.LevelNum;
942 	info->Cluster = Default.Cluster;
943 	info->WarpTrans = Default.WarpTrans;
944 	info->NextMap = Default.NextMap;
945 	info->SecretMap = Default.SecretMap;
946 	info->SongLump = Default.SongLump;
947 	info->CDTrack = Default.CDTrack;
948 	info->Sky1Texture = Default.Sky1Texture;
949 	info->Sky2Texture = Default.Sky2Texture;
950 	info->Sky1ScrollDelta = Default.Sky1ScrollDelta;
951 	info->Sky2ScrollDelta = Default.Sky2ScrollDelta;
952 	info->SkyBox = Default.SkyBox;
953 	info->FadeTable = Default.FadeTable;
954 	info->Fade = Default.Fade;
955 	info->OutsideFog = Default.OutsideFog;
956 	info->Gravity = Default.Gravity;
957 	info->AirControl = Default.AirControl;
958 	info->Flags = Default.Flags;
959 	info->Flags2 = Default.Flags2;
960 	info->TitlePatch = Default.TitlePatch;
961 	info->ParTime = Default.ParTime;
962 	info->SuckTime = Default.SuckTime;
963 	info->HorizWallShade = Default.HorizWallShade;
964 	info->VertWallShade = Default.VertWallShade;
965 	info->Infighting = Default.Infighting;
966 	info->SpecialActions = Default.SpecialActions;
967 	info->RedirectType = Default.RedirectType;
968 	info->RedirectMap = Default.RedirectMap;
969 	info->ExitPic = Default.ExitPic;
970 	info->EnterPic = Default.EnterPic;
971 	info->InterMusic = Default.InterMusic;
972 
973 	if (HexenMode)
974 	{
975 		info->Flags |= MAPINFOF_NoIntermission |
976 			MAPINFOF_FallingDamage |
977 			MAPINFOF_MonsterFallingDamage |
978 			MAPINFOF_NoAutoSndSeq |
979 			MAPINFOF_ActivateOwnSpecial |
980 			MAPINFOF_MissilesActivateImpact |
981 			MAPINFOF_InfiniteFlightPowerup;
982 	}
983 
984 	// Map name must follow the number
985 	if (sc->Check("lookup"))
986 	{
987 		info->Flags |= MAPINFOF_LookupName;
988 		sc->ExpectString();
989 		info->Name = sc->String.ToLower();
990 	}
991 	else
992 	{
993 		info->Flags &= ~MAPINFOF_LookupName;
994 		sc->ExpectString();
995 		info->Name = sc->String;
996 	}
997 
998 	//	Set song lump name from SNDINFO script
999 	for (int i = 0; i < MapSongList.Num(); i++)
1000 	{
1001 		if (MapSongList[i].MapName == info->LumpName)
1002 		{
1003 			info->SongLump = MapSongList[i].SongName;
1004 		}
1005 	}
1006 
1007 	//	Set default levelnum for this map.
1008 	const char* mn = *MapLumpName;
1009 	if (mn[0] == 'm' && mn[1] == 'a' && mn[2] == 'p' && mn[5] == 0)
1010 	{
1011 		int num = atoi(mn + 3);
1012 		if  (num >= 1 && num <= 99)
1013 			info->LevelNum = num;
1014 	}
1015 	else if (mn[0] == 'e' && mn[1] >= '0' && mn[1] <= '9' &&
1016 		mn[2] == 'm' && mn[3] >= '0' && mn[3] <= '9')
1017 	{
1018 		info->LevelNum = (mn[1] - '1') * 10 + (mn[3] - '0');
1019 	}
1020 
1021 	ParseMapCommon(sc, info, HexenMode);
1022 
1023 	//	Avoid duplicate levelnums, later one takes precedance.
1024 	for (int i = 0; i < MapInfo.Num(); i++)
1025 	{
1026 		if (MapInfo[i].LevelNum == info->LevelNum &&
1027 			&MapInfo[i] != info)
1028 		{
1029 			MapInfo[i].LevelNum = 0;
1030 		}
1031 	}
1032 	unguard;
1033 }
1034 
1035 //==========================================================================
1036 //
1037 //	ParseClusterDef
1038 //
1039 //==========================================================================
1040 
ParseClusterDef(VScriptParser * sc)1041 static void ParseClusterDef(VScriptParser* sc)
1042 {
1043 	guard(ParseClusterDef);
1044 	VClusterDef* CDef = NULL;
1045 	sc->ExpectNumber();
1046 
1047 	//	Check for replaced cluster def.
1048 	for (int i = 0; i < ClusterDefs.Num(); i++)
1049 	{
1050 		if (sc->Number == ClusterDefs[i].Cluster)
1051 		{
1052 			CDef = &ClusterDefs[i];
1053 			break;
1054 		}
1055 	}
1056 	if (!CDef)
1057 	{
1058 		CDef = &ClusterDefs.Alloc();
1059 	}
1060 
1061 	//	Set defaults.
1062 	CDef->Cluster = sc->Number;
1063 	CDef->Flags = 0;
1064 	CDef->EnterText = VStr();
1065 	CDef->ExitText = VStr();
1066 	CDef->Flat = NAME_None;
1067 	CDef->Music = NAME_None;
1068 	CDef->CDTrack = 0;
1069 	CDef->CDId = 0;
1070 
1071 	while (1)
1072 	{
1073 		if (sc->Check("hub"))
1074 		{
1075 			CDef->Flags |= CLUSTERF_Hub;
1076 		}
1077 		else if (sc->Check("entertext"))
1078 		{
1079 			if (sc->Check("lookup"))
1080 			{
1081 				CDef->Flags |= CLUSTERF_LookupEnterText;
1082 				sc->ExpectString();
1083 				CDef->EnterText = sc->String.ToLower();
1084 			}
1085 			else
1086 			{
1087 				CDef->Flags &= ~CLUSTERF_LookupEnterText;
1088 				sc->ExpectString();
1089 				CDef->EnterText = sc->String;
1090 			}
1091 		}
1092 		else if (sc->Check("entertextislump"))
1093 		{
1094 			CDef->Flags |= CLUSTERF_EnterTextIsLump;
1095 		}
1096 		else if (sc->Check("exittext"))
1097 		{
1098 			if (sc->Check("lookup"))
1099 			{
1100 				CDef->Flags |= CLUSTERF_LookupExitText;
1101 				sc->ExpectString();
1102 				CDef->ExitText = sc->String.ToLower();
1103 			}
1104 			else
1105 			{
1106 				CDef->Flags &= ~CLUSTERF_LookupExitText;
1107 				sc->ExpectString();
1108 				CDef->ExitText = sc->String;
1109 			}
1110 		}
1111 		else if (sc->Check("exittextislump"))
1112 		{
1113 			CDef->Flags |= CLUSTERF_ExitTextIsLump;
1114 		}
1115 		else if (sc->Check("flat"))
1116 		{
1117 			sc->ExpectName8();
1118 			CDef->Flat = sc->Name8;
1119 			CDef->Flags &= ~CLUSTERF_FinalePic;
1120 		}
1121 		else if (sc->Check("pic"))
1122 		{
1123 			sc->ExpectName8();
1124 			CDef->Flat = sc->Name8;
1125 			CDef->Flags |= CLUSTERF_FinalePic;
1126 		}
1127 		else if (sc->Check("music"))
1128 		{
1129 			sc->ExpectName8();
1130 			CDef->Music = sc->Name8;
1131 		}
1132 		else if (sc->Check("cdtrack"))
1133 		{
1134 			sc->ExpectNumber();
1135 			CDef->CDTrack = sc->Number;
1136 		}
1137 		else if (sc->Check("cdid"))
1138 		{
1139 			sc->ExpectNumber();
1140 			CDef->CDId = sc->Number;
1141 		}
1142 		else if (sc->Check("name"))
1143 		{
1144 			sc->Check("lookup");
1145 			sc->ExpectString();
1146 			GCon->Logf("Unimplemented MAPINFO cluster comand name");
1147 		}
1148 		else
1149 		{
1150 			break;
1151 		}
1152 	}
1153 
1154 	//	Make sure text lump names are in lower case.
1155 	if (CDef->Flags & CLUSTERF_EnterTextIsLump)
1156 	{
1157 		CDef->EnterText = CDef->EnterText.ToLower();
1158 	}
1159 	if (CDef->Flags & CLUSTERF_ExitTextIsLump)
1160 	{
1161 		CDef->ExitText = CDef->ExitText.ToLower();
1162 	}
1163 	unguard;
1164 }
1165 
1166 //==========================================================================
1167 //
1168 //	ParseEpisodeDef
1169 //
1170 //==========================================================================
1171 
ParseEpisodeDef(VScriptParser * sc)1172 static void ParseEpisodeDef(VScriptParser* sc)
1173 {
1174 	guard(ParseEpisodeDef);
1175 	VEpisodeDef* EDef = NULL;
1176 	int EIdx = 0;
1177 	sc->ExpectName8();
1178 
1179 	//	Check for replaced episode.
1180 	for (int i = 0; i < EpisodeDefs.Num(); i++)
1181 	{
1182 		if (sc->Name8 == EpisodeDefs[i].Name)
1183 		{
1184 			EDef = &EpisodeDefs[i];
1185 			EIdx = i;
1186 			break;
1187 		}
1188 	}
1189 	if (!EDef)
1190 	{
1191 		EDef = &EpisodeDefs.Alloc();
1192 		EIdx = EpisodeDefs.Num() - 1;
1193 	}
1194 
1195 	//	Check for removal of an episode.
1196 	if (sc->Check("remove"))
1197 	{
1198 		EpisodeDefs.RemoveIndex(EIdx);
1199 		return;
1200 	}
1201 
1202 	//	Set defaults.
1203 	EDef->Name = sc->Name8;
1204 	EDef->TeaserName = NAME_None;
1205 	EDef->Text = VStr();
1206 	EDef->PicName = NAME_None;
1207 	EDef->Flags = 0;
1208 	EDef->Key = VStr();
1209 
1210 	if (sc->Check("teaser"))
1211 	{
1212 		sc->ExpectName8();
1213 		EDef->TeaserName = sc->Name8;
1214 	}
1215 
1216 	while (1)
1217 	{
1218 		if (sc->Check("name"))
1219 		{
1220 			if (sc->Check("lookup"))
1221 			{
1222 				EDef->Flags |= EPISODEF_LookupText;
1223 				sc->ExpectString();
1224 				EDef->Text = sc->String.ToLower();
1225 			}
1226 			else
1227 			{
1228 				EDef->Flags &= ~EPISODEF_LookupText;
1229 				sc->ExpectString();
1230 				EDef->Text = sc->String;
1231 			}
1232 		}
1233 		else if (sc->Check("picname"))
1234 		{
1235 			sc->ExpectName8();
1236 			EDef->PicName = sc->Name8;
1237 		}
1238 		else if (sc->Check("key"))
1239 		{
1240 			sc->ExpectString();
1241 			EDef->Key = sc->String.ToLower();
1242 		}
1243 		else if (sc->Check("noskillmenu"))
1244 		{
1245 			EDef->Flags |= EPISODEF_NoSkillMenu;
1246 		}
1247 		else if (sc->Check("optional"))
1248 		{
1249 			EDef->Flags |= EPISODEF_Optional;
1250 		}
1251 		else
1252 		{
1253 			break;
1254 		}
1255 	}
1256 	unguard;
1257 }
1258 
1259 //==========================================================================
1260 //
1261 //	ParseSkillDef
1262 //
1263 //==========================================================================
1264 
ParseSkillDef(VScriptParser * sc)1265 static void ParseSkillDef(VScriptParser* sc)
1266 {
1267 	guard(ParseSkillDef);
1268 	VSkillDef* SDef = NULL;
1269 	sc->ExpectString();
1270 
1271 	//	Check for replaced skill.
1272 	for (int i = 0; i < SkillDefs.Num(); i++)
1273 	{
1274 		if (!sc->String.ICmp(SkillDefs[i].Name))
1275 		{
1276 			SDef = &SkillDefs[i];
1277 			break;
1278 		}
1279 	}
1280 	if (!SDef)
1281 	{
1282 		SDef = &SkillDefs.Alloc();
1283 		SDef->Name = sc->String;
1284 	}
1285 
1286 	//	Set defaults.
1287 	SDef->AmmoFactor = 1.0;
1288 	SDef->DoubleAmmoFactor = 2.0;
1289 	SDef->DamageFactor = 1.0;
1290 	SDef->RespawnTime = 0.0;
1291 	SDef->RespawnLimit = 0;
1292 	SDef->Aggressiveness = 1.0;
1293 	SDef->SpawnFilter = 0;
1294 	SDef->AcsReturn = SkillDefs.Num() - 1;
1295 	SDef->MenuName.Clean();
1296 	SDef->PlayerClassNames.Clear();
1297 	SDef->ConfirmationText.Clean();
1298 	SDef->Key.Clean();
1299 	SDef->TextColour.Clean();
1300 	SDef->Flags = 0;
1301 
1302 	while (1)
1303 	{
1304 		if (sc->Check("AmmoFactor"))
1305 		{
1306 			sc->ExpectFloat();
1307 			SDef->AmmoFactor = sc->Float;
1308 		}
1309 		else if (sc->Check("DoubleAmmoFactor"))
1310 		{
1311 			sc->ExpectFloat();
1312 			SDef->DoubleAmmoFactor = sc->Float;
1313 		}
1314 		else if (sc->Check("DamageFactor"))
1315 		{
1316 			sc->ExpectFloat();
1317 			SDef->DamageFactor = sc->Float;
1318 		}
1319 		else if (sc->Check("FastMonsters"))
1320 		{
1321 			SDef->Flags |= SKILLF_FastMonsters;
1322 		}
1323 		else if (sc->Check("DisableCheats"))
1324 		{
1325 			SDef->Flags |= SKILLF_DisableCheats;
1326 		}
1327 		else if (sc->Check("EasyBossBrain"))
1328 		{
1329 			SDef->Flags |= SKILLF_EasyBossBrain;
1330 		}
1331 		else if (sc->Check("AutoUseHealth"))
1332 		{
1333 			SDef->Flags |= SKILLF_AutoUseHealth;
1334 		}
1335 		else if (sc->Check("RespawnTime"))
1336 		{
1337 			sc->ExpectFloat();
1338 			SDef->RespawnTime = sc->Float;
1339 		}
1340 		else if (sc->Check("RespawnLimit"))
1341 		{
1342 			sc->ExpectNumber();
1343 			SDef->RespawnLimit = sc->Number;
1344 		}
1345 		else if (sc->Check("Aggressiveness"))
1346 		{
1347 			sc->ExpectFloat();
1348 			SDef->Aggressiveness = 1.0 - MID(0.0, sc->Float, 1.0);
1349 		}
1350 		else if (sc->Check("SpawnFilter"))
1351 		{
1352 			if (sc->CheckNumber())
1353 			{
1354 				if (sc->Number > 0)
1355 				{
1356 					SDef->SpawnFilter = 1 << (sc->Number - 1);
1357 				}
1358 			}
1359 			else
1360 			{
1361 				if (sc->Check("Baby"))
1362 				{
1363 					SDef->SpawnFilter = 1;
1364 				}
1365 				else if (sc->Check("Easy"))
1366 				{
1367 					SDef->SpawnFilter = 2;
1368 				}
1369 				else if (sc->Check("Normal"))
1370 				{
1371 					SDef->SpawnFilter = 4;
1372 				}
1373 				else if (sc->Check("Hard"))
1374 				{
1375 					SDef->SpawnFilter = 8;
1376 				}
1377 				else if (sc->Check("Nightmare"))
1378 				{
1379 					SDef->SpawnFilter = 16;
1380 				}
1381 				else
1382 				{
1383 					sc->ExpectString();
1384 				}
1385 			}
1386 		}
1387 		else if (sc->Check("ACSReturn"))
1388 		{
1389 			sc->ExpectNumber();
1390 			SDef->AcsReturn = sc->Number;
1391 		}
1392 		else if (sc->Check("Name"))
1393 		{
1394 			sc->ExpectString();
1395 			SDef->MenuName = sc->String;
1396 			SDef->Flags &= ~SKILLF_MenuNameIsPic;
1397 		}
1398 		else if (sc->Check("PlayerClassName"))
1399 		{
1400 			VSkillPlayerClassName& CN = SDef->PlayerClassNames.Alloc();
1401 			sc->ExpectString();
1402 			CN.ClassName = sc->String;
1403 			sc->ExpectString();
1404 			CN.MenuName = sc->String;
1405 		}
1406 		else if (sc->Check("PicName"))
1407 		{
1408 			sc->ExpectString();
1409 			SDef->MenuName = sc->String.ToLower();
1410 			SDef->Flags |= SKILLF_MenuNameIsPic;
1411 		}
1412 		else if (sc->Check("MustConfirm"))
1413 		{
1414 			SDef->Flags |= SKILLF_MustConfirm;
1415 			if (sc->CheckQuotedString())
1416 			{
1417 				SDef->ConfirmationText = sc->String;
1418 			}
1419 		}
1420 		else if (sc->Check("Key"))
1421 		{
1422 			sc->ExpectString();
1423 			SDef->Key = sc->String;
1424 		}
1425 		else if (sc->Check("TextColor"))
1426 		{
1427 			sc->ExpectString();
1428 			SDef->TextColour = sc->String;
1429 		}
1430 		else
1431 		{
1432 			break;
1433 		}
1434 	}
1435 	unguard;
1436 }
1437 
1438 //==========================================================================
1439 //
1440 //	ParseMapInfo
1441 //
1442 //==========================================================================
1443 
ParseMapInfo(VScriptParser * sc)1444 static void ParseMapInfo(VScriptParser* sc)
1445 {
1446 	guard(ParseMapInfo);
1447 	bool HexenMode = false;
1448 
1449 	//	Set up default map info.
1450 	mapInfo_t Default;
1451 	SetMapDefaults(Default);
1452 
1453 	while (!sc->AtEnd())
1454 	{
1455 		if (sc->Check("map"))
1456 		{
1457 			ParseMap(sc, HexenMode, Default);
1458 		}
1459 		else if (sc->Check("defaultmap"))
1460 		{
1461 			SetMapDefaults(Default);
1462 			ParseMapCommon(sc, &Default, HexenMode);
1463 		}
1464 		else if (sc->Check("adddefaultmap"))
1465 		{
1466 			ParseMapCommon(sc, &Default, HexenMode);
1467 		}
1468 		else if (sc->Check("clusterdef"))
1469 		{
1470 			ParseClusterDef(sc);
1471 		}
1472 		else if (sc->Check("episode"))
1473 		{
1474 			ParseEpisodeDef(sc);
1475 		}
1476 		else if (sc->Check("clearepisodes"))
1477 		{
1478 			EpisodeDefs.Clear();
1479 		}
1480 		else if (sc->Check("skill"))
1481 		{
1482 			ParseSkillDef(sc);
1483 		}
1484 		else if (sc->Check("clearskills"))
1485 		{
1486 			SkillDefs.Clear();
1487 		}
1488 		else
1489 		{
1490 			sc->Error(va("Invalid command %s", *sc->String));
1491 		}
1492 	}
1493 	delete sc;
1494 	sc = NULL;
1495 	unguard;
1496 }
1497 
1498 //==========================================================================
1499 //
1500 // QualifyMap
1501 //
1502 //==========================================================================
1503 
QualifyMap(int map)1504 static int QualifyMap(int map)
1505 {
1506 	return (map < 0 || map >= MapInfo.Num()) ? 0 : map;
1507 }
1508 
1509 //==========================================================================
1510 //
1511 //	P_GetMapInfo
1512 //
1513 //==========================================================================
1514 
P_GetMapInfo(VName map)1515 const mapInfo_t& P_GetMapInfo(VName map)
1516 {
1517 	guard(P_GetMapInfo);
1518 	for (int i = 0; i < MapInfo.Num(); i++)
1519 	{
1520 		if (map == MapInfo[i].LumpName)
1521 		{
1522 			return MapInfo[i];
1523 		}
1524 	}
1525 	return DefaultMap;
1526 	unguard;
1527 }
1528 
1529 //==========================================================================
1530 //
1531 //	P_GetMapName
1532 //
1533 //==========================================================================
1534 
P_GetMapName(int map)1535 const char* P_GetMapName(int map)
1536 {
1537 	return *MapInfo[QualifyMap(map)].GetName();
1538 }
1539 
1540 //==========================================================================
1541 //
1542 //	P_GetMapLumpName
1543 //
1544 //==========================================================================
1545 
P_GetMapLumpName(int map)1546 VName P_GetMapLumpName(int map)
1547 {
1548 	return MapInfo[QualifyMap(map)].LumpName;
1549 }
1550 
1551 //==========================================================================
1552 //
1553 // P_TranslateMap
1554 //
1555 // Returns the actual map number given a warp map number.
1556 //
1557 //==========================================================================
1558 
P_TranslateMap(int map)1559 VName P_TranslateMap(int map)
1560 {
1561 	guard(P_TranslateMap);
1562 	for (int i = MapInfo.Num() - 1; i >= 0; i--)
1563 	{
1564 		if (MapInfo[i].WarpTrans == map)
1565 		{
1566 			return MapInfo[i].LumpName;
1567 		}
1568 	}
1569 	// Not found
1570 	return MapInfo[0].LumpName;
1571 	unguard;
1572 }
1573 
1574 //==========================================================================
1575 //
1576 //	P_GetMapNameByLevelNum
1577 //
1578 //	Returns the actual map name given a level number.
1579 //
1580 //==========================================================================
1581 
P_GetMapNameByLevelNum(int map)1582 VName P_GetMapNameByLevelNum(int map)
1583 {
1584 	guard(P_GetMapNameByLevelNum);
1585 	for (int i = 0; i < MapInfo.Num(); i++)
1586 	{
1587 		if (MapInfo[i].LevelNum == map)
1588 		{
1589 			return MapInfo[i].LumpName;
1590 		}
1591 	}
1592 	// Not found, use map##
1593 	return va("map%02d", map);
1594 	unguard;
1595 }
1596 
1597 //==========================================================================
1598 //
1599 // P_PutMapSongLump
1600 //
1601 //==========================================================================
1602 
P_PutMapSongLump(int map,VName lumpName)1603 void P_PutMapSongLump(int map, VName lumpName)
1604 {
1605 	guard(P_PutMapSongLump);
1606 	FMapSongInfo& ms = MapSongList.Alloc();
1607 	ms.MapName = va("map%02d", map);
1608 	ms.SongName = lumpName;
1609 	unguard;
1610 }
1611 
1612 //==========================================================================
1613 //
1614 //	P_GetClusterDef
1615 //
1616 //==========================================================================
1617 
P_GetClusterDef(int Cluster)1618 const VClusterDef* P_GetClusterDef(int Cluster)
1619 {
1620 	guard(P_GetClusterDef);
1621 	for (int i = 0; i < ClusterDefs.Num(); i++)
1622 	{
1623 		if (Cluster == ClusterDefs[i].Cluster)
1624 		{
1625 			return &ClusterDefs[i];
1626 		}
1627 	}
1628 	return &DefaultClusterDef;
1629 	unguard;
1630 }
1631 
1632 //==========================================================================
1633 //
1634 //	P_GetNumEpisodes
1635 //
1636 //==========================================================================
1637 
P_GetNumEpisodes()1638 int P_GetNumEpisodes()
1639 {
1640 	return EpisodeDefs.Num();
1641 }
1642 
1643 //==========================================================================
1644 //
1645 //	P_GetEpisodeDef
1646 //
1647 //==========================================================================
1648 
P_GetEpisodeDef(int Index)1649 VEpisodeDef* P_GetEpisodeDef(int Index)
1650 {
1651 	return &EpisodeDefs[Index];
1652 }
1653 
1654 //==========================================================================
1655 //
1656 //	P_GetNumSkills
1657 //
1658 //==========================================================================
1659 
P_GetNumSkills()1660 int P_GetNumSkills()
1661 {
1662 	return SkillDefs.Num();
1663 }
1664 
1665 //==========================================================================
1666 //
1667 //	P_GetSkillDef
1668 //
1669 //==========================================================================
1670 
P_GetSkillDef(int Index)1671 const VSkillDef* P_GetSkillDef(int Index)
1672 {
1673 	return &SkillDefs[Index];
1674 }
1675 
1676 //==========================================================================
1677 //
1678 //	P_GetMusicLumpNames
1679 //
1680 //==========================================================================
1681 
P_GetMusicLumpNames(TArray<FReplacedString> & List)1682 void P_GetMusicLumpNames(TArray<FReplacedString>& List)
1683 {
1684 	guard(P_GetMusicLumpNames);
1685 	for (int i = 0; i < MapInfo.Num(); i++)
1686 	{
1687 		const char* MName = *MapInfo[i].SongLump;
1688 		if (MName[0] == 'd' && MName[1] == '_')
1689 		{
1690 			FReplacedString& R = List.Alloc();
1691 			R.Index = i;
1692 			R.Replaced = false;
1693 			R.Old = MName + 2;
1694 		}
1695 	}
1696 	unguard;
1697 }
1698 
1699 //==========================================================================
1700 //
1701 //	P_ReplaceMusicLumpNames
1702 //
1703 //==========================================================================
1704 
P_ReplaceMusicLumpNames(TArray<FReplacedString> & List)1705 void P_ReplaceMusicLumpNames(TArray<FReplacedString>& List)
1706 {
1707 	guard(P_ReplaceMusicLumpNames);
1708 	for (int i = 0; i < List.Num(); i++)
1709 	{
1710 		if (List[i].Replaced)
1711 		{
1712 			MapInfo[List[i].Index].SongLump = VName(*(VStr("d_") +
1713 				List[i].New), VName::AddLower8);
1714 		}
1715 	}
1716 	unguard;
1717 }
1718 
1719 //==========================================================================
1720 //
1721 //	P_SetParTime
1722 //
1723 //==========================================================================
1724 
P_SetParTime(VName Map,int Par)1725 void P_SetParTime(VName Map, int Par)
1726 {
1727 	guard(P_SetParTime);
1728 	for (int i = 0; i < MapInfo.Num(); i++)
1729 	{
1730 		if (MapInfo[i].LumpName == Map)
1731 		{
1732 			MapInfo[i].ParTime = Par;
1733 			return;
1734 		}
1735 	}
1736 	GCon->Logf("WARNING! No such map %s", *Map);
1737 	unguard;
1738 }
1739 
1740 //==========================================================================
1741 //
1742 //	IsMapPresent
1743 //
1744 //==========================================================================
1745 
IsMapPresent(VName MapName)1746 bool IsMapPresent(VName MapName)
1747 {
1748 	guard(IsMapPresent);
1749 	if (W_CheckNumForName(MapName) >= 0)
1750 	{
1751 		return true;
1752 	}
1753 	VStr FileName = va("maps/%s.wad", *MapName);
1754 	if (FL_FileExists(FileName))
1755 	{
1756 		return true;
1757 	}
1758 	return false;
1759 	unguard;
1760 }
1761 
1762 //==========================================================================
1763 //
1764 // P_GetCDStartTrack
1765 //
1766 //==========================================================================
1767 
P_GetCDStartTrack()1768 int P_GetCDStartTrack()
1769 {
1770 	return cd_NonLevelTracks[CD_STARTTRACK];
1771 }
1772 
1773 //==========================================================================
1774 //
1775 // P_GetCDEnd1Track
1776 //
1777 //==========================================================================
1778 
P_GetCDEnd1Track()1779 int P_GetCDEnd1Track()
1780 {
1781 	return cd_NonLevelTracks[CD_END1TRACK];
1782 }
1783 
1784 //==========================================================================
1785 //
1786 // P_GetCDEnd2Track
1787 //
1788 //==========================================================================
1789 
P_GetCDEnd2Track()1790 int P_GetCDEnd2Track()
1791 {
1792 	return cd_NonLevelTracks[CD_END2TRACK];
1793 }
1794 
1795 //==========================================================================
1796 //
1797 // P_GetCDEnd3Track
1798 //
1799 //==========================================================================
1800 
P_GetCDEnd3Track()1801 int P_GetCDEnd3Track()
1802 {
1803 	return cd_NonLevelTracks[CD_END3TRACK];
1804 }
1805 
1806 //==========================================================================
1807 //
1808 // P_GetCDIntermissionTrack
1809 //
1810 //==========================================================================
1811 
P_GetCDIntermissionTrack()1812 int P_GetCDIntermissionTrack()
1813 {
1814 	return cd_NonLevelTracks[CD_INTERTRACK];
1815 }
1816 
1817 //==========================================================================
1818 //
1819 // P_GetCDTitleTrack
1820 //
1821 //==========================================================================
1822 
P_GetCDTitleTrack()1823 int P_GetCDTitleTrack()
1824 {
1825 	return cd_NonLevelTracks[CD_TITLETRACK];
1826 }
1827 
1828 //==========================================================================
1829 //
1830 //	COMMAND MapList
1831 //
1832 //==========================================================================
1833 
COMMAND(MapList)1834 COMMAND(MapList)
1835 {
1836 	guard(COMMAND MapList);
1837 	for (int i = 0; i < MapInfo.Num(); i++)
1838 	{
1839 		if (IsMapPresent(MapInfo[i].LumpName))
1840 		{
1841 			GCon->Log(VStr(MapInfo[i].LumpName) + " - " +
1842 				((MapInfo[i].Flags & MAPINFOF_LookupName) ?
1843 				GLanguage[*MapInfo[i].Name] : MapInfo[i].Name));
1844 		}
1845 	}
1846 	unguard;
1847 }
1848 
1849 //==========================================================================
1850 //
1851 //	ShutdownMapInfo
1852 //
1853 //==========================================================================
1854 
ShutdownMapInfo()1855 void ShutdownMapInfo()
1856 {
1857 	guard(ShutdownMapInfo);
1858 	DefaultMap.Name.Clean();
1859 	MapInfo.Clear();
1860 	MapSongList.Clear();
1861 	ClusterDefs.Clear();
1862 	EpisodeDefs.Clear();
1863 	SkillDefs.Clear();
1864 	unguard;
1865 }
1866