1 /* 2 ** g_level.h 3 ** 4 **--------------------------------------------------------------------------- 5 ** Copyright 1998-2006 Randy Heit 6 ** All rights reserved. 7 ** 8 ** Redistribution and use in source and binary forms, with or without 9 ** modification, are permitted provided that the following conditions 10 ** are met: 11 ** 12 ** 1. Redistributions of source code must retain the above copyright 13 ** notice, this list of conditions and the following disclaimer. 14 ** 2. Redistributions in binary form must reproduce the above copyright 15 ** notice, this list of conditions and the following disclaimer in the 16 ** documentation and/or other materials provided with the distribution. 17 ** 3. The name of the author may not be used to endorse or promote products 18 ** derived from this software without specific prior written permission. 19 ** 20 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 **--------------------------------------------------------------------------- 31 ** 32 */ 33 34 #ifndef __G_LEVEL_H__ 35 #define __G_LEVEL_H__ 36 37 #include "doomtype.h" 38 #include "doomdef.h" 39 #include "sc_man.h" 40 #include "s_sound.h" 41 #include "textures/textures.h" 42 43 struct level_info_t; 44 struct cluster_info_t; 45 class FScanner; 46 47 #if defined(_MSC_VER) 48 #pragma data_seg(".yreg$u") 49 #pragma data_seg() 50 51 #define MSVC_YSEG __declspec(allocate(".yreg$u")) 52 #define GCC_YSEG 53 #else 54 #define MSVC_YSEG 55 #define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used)) 56 #endif 57 58 struct FIntermissionDescriptor; 59 struct FIntermissionAction; 60 61 struct FMapInfoParser 62 { 63 enum EFormatType 64 { 65 FMT_Unknown, 66 FMT_Old, 67 FMT_New 68 }; 69 70 FScanner sc; 71 int format_type; 72 bool HexenHack; 73 74 FMapInfoParser(int format = FMT_Unknown) 75 { 76 format_type = format; 77 HexenHack = false; 78 } 79 80 bool ParseLookupName(FString &dest); 81 void ParseMusic(FString &name, int &order); 82 //void ParseLumpOrTextureName(char *name); 83 void ParseLumpOrTextureName(FString &name); 84 85 void ParseCluster(); 86 void ParseNextMap(FString &mapname); 87 level_info_t *ParseMapHeader(level_info_t &defaultinfo); 88 void ParseMapDefinition(level_info_t &leveldef); 89 void ParseGameInfo(); 90 void ParseEpisodeInfo (); 91 void ParseSkill (); 92 void ParseMapInfo (int lump, level_info_t &gamedefaults, level_info_t &defaultinfo); 93 94 void ParseOpenBrace(); 95 bool ParseCloseBrace(); 96 bool CheckAssign(); 97 void ParseAssign(); 98 void MustParseAssign(); 99 void ParseComma(); 100 bool CheckNumber(); 101 bool CheckFloat(); 102 void SkipToNext(); 103 void CheckEndOfFile(const char *block); 104 105 void ParseIntermissionAction(FIntermissionDescriptor *Desc); 106 void ParseIntermission(); 107 void ParseDoomEdNums(); 108 void ParseSpawnNums(); 109 void ParseConversationIDs(); 110 void ParseAMColors(bool); 111 FName CheckEndSequence(); 112 FName ParseEndGame(); 113 }; 114 115 #define DEFINE_MAP_OPTION(name, old) \ 116 static void MapOptHandler_##name(FMapInfoParser &parse, level_info_t *info); \ 117 static FMapOptInfo MapOpt_##name = \ 118 { #name, MapOptHandler_##name, old }; \ 119 MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \ 120 static void MapOptHandler_##name(FMapInfoParser &parse, level_info_t *info) 121 122 123 struct FMapOptInfo 124 { 125 const char *name; 126 void (*handler) (FMapInfoParser &parse, level_info_t *levelinfo); 127 bool old; 128 }; 129 130 enum ELevelFlags 131 { 132 LEVEL_NOINTERMISSION = 0x00000001, 133 LEVEL_NOINVENTORYBAR = 0x00000002, // This effects Doom only, since it's the only one without a standard inventory bar. 134 LEVEL_DOUBLESKY = 0x00000004, 135 LEVEL_HASFADETABLE = 0x00000008, // Level uses Hexen's fadetable mapinfo to get fog 136 137 LEVEL_MAP07SPECIAL = 0x00000010, 138 LEVEL_BRUISERSPECIAL = 0x00000020, 139 LEVEL_CYBORGSPECIAL = 0x00000040, 140 LEVEL_SPIDERSPECIAL = 0x00000080, 141 142 LEVEL_SPECLOWERFLOOR = 0x00000100, 143 LEVEL_SPECOPENDOOR = 0x00000200, 144 LEVEL_SPECLOWERFLOORTOHIGHEST=0x00000300, 145 LEVEL_SPECACTIONSMASK = 0x00000300, 146 147 LEVEL_MONSTERSTELEFRAG = 0x00000400, 148 LEVEL_ACTOWNSPECIAL = 0x00000800, 149 LEVEL_SNDSEQTOTALCTRL = 0x00001000, 150 LEVEL_FORCENOSKYSTRETCH = 0x00002000, 151 152 LEVEL_CROUCH_NO = 0x00004000, 153 LEVEL_JUMP_NO = 0x00008000, 154 LEVEL_FREELOOK_NO = 0x00010000, 155 LEVEL_FREELOOK_YES = 0x00020000, 156 157 // The absence of both of the following bits means that this level does not 158 // use falling damage (though damage can be forced with dmflags,. 159 LEVEL_FALLDMG_ZD = 0x00040000, // Level uses ZDoom's falling damage 160 LEVEL_FALLDMG_HX = 0x00080000, // Level uses Hexen's falling damage 161 162 LEVEL_HEADSPECIAL = 0x00100000, // Heretic episode 1/4 163 LEVEL_MINOTAURSPECIAL = 0x00200000, // Heretic episode 2/5 164 LEVEL_SORCERER2SPECIAL = 0x00400000, // Heretic episode 3 165 LEVEL_SPECKILLMONSTERS = 0x00800000, 166 167 LEVEL_STARTLIGHTNING = 0x01000000, // Automatically start lightning 168 LEVEL_FILTERSTARTS = 0x02000000, // Apply mapthing filtering to player starts 169 LEVEL_LOOKUPLEVELNAME = 0x04000000, // Level name is the name of a language string 170 LEVEL_USEPLAYERSTARTZ = 0x08000000, // Use the Z position of player starts 171 172 LEVEL_SWAPSKIES = 0x10000000, // Used by lightning 173 LEVEL_NOALLIES = 0x20000000, // i.e. Inside Strife's front base 174 LEVEL_CHANGEMAPCHEAT = 0x40000000, // Don't display cluster messages 175 LEVEL_VISITED = 0x80000000, // Used for intermission map 176 177 // The flags QWORD is now split into 2 DWORDs 178 LEVEL2_RANDOMPLAYERSTARTS = 0x00000001, // Select single player starts randomnly (no voodoo dolls) 179 LEVEL2_ALLMAP = 0x00000002, // The player picked up a map on this level 180 181 LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed 182 LEVEL2_LAXACTIVATIONMAPINFO = 0x00000008, // LEVEL_LAXMONSTERACTIVATION is not a default. 183 184 LEVEL2_MISSILESACTIVATEIMPACT=0x00000010, // Missiles are the activators of SPAC_IMPACT events, not their shooters 185 LEVEL2_FROZEN = 0x00000020, // Game is frozen by a TimeFreezer 186 187 LEVEL2_KEEPFULLINVENTORY = 0x00000040, // doesn't reduce the amount of inventory items to 1 188 189 LEVEL2_PRERAISEWEAPON = 0x00000080, // players should spawn with their weapons fully raised (but not when respawning it multiplayer) 190 LEVEL2_MONSTERFALLINGDAMAGE = 0x00000100, 191 LEVEL2_CLIPMIDTEX = 0x00000200, 192 LEVEL2_WRAPMIDTEX = 0x00000400, 193 194 LEVEL2_CHECKSWITCHRANGE = 0x00000800, 195 196 LEVEL2_PAUSE_MUSIC_IN_MENUS = 0x00001000, 197 LEVEL2_TOTALINFIGHTING = 0x00002000, 198 LEVEL2_NOINFIGHTING = 0x00004000, 199 200 LEVEL2_NOMONSTERS = 0x00008000, 201 LEVEL2_INFINITE_FLIGHT = 0x00010000, 202 203 LEVEL2_ALLOWRESPAWN = 0x00020000, 204 205 LEVEL2_FORCETEAMPLAYON = 0x00040000, 206 LEVEL2_FORCETEAMPLAYOFF = 0x00080000, 207 208 LEVEL2_CONV_SINGLE_UNFREEZE = 0x00100000, 209 LEVEL2_RAILINGHACK = 0x00200000, // but UDMF requires them to be separate to have more control 210 LEVEL2_DUMMYSWITCHES = 0x00400000, 211 LEVEL2_HEXENHACK = 0x00800000, // Level was defined in a Hexen style MAPINFO 212 213 LEVEL2_SMOOTHLIGHTING = 0x01000000, // Level uses the smooth lighting feature. 214 LEVEL2_POLYGRIND = 0x02000000, // Polyobjects grind corpses to gibs. 215 LEVEL2_RESETINVENTORY = 0x04000000, // Resets player inventory when starting this level (unless in a hub) 216 LEVEL2_RESETHEALTH = 0x08000000, // Resets player health when starting this level (unless in a hub) 217 218 LEVEL2_NOSTATISTICS = 0x10000000, // This level should not have statistics collected 219 LEVEL2_ENDGAME = 0x20000000, // This is an epilogue level that cannot be quit. 220 LEVEL2_NOAUTOSAVEHINT = 0x40000000, // tell the game that an autosave for this level does not need to be kept 221 LEVEL2_FORGETSTATE = 0x80000000, // forget this map's state in a hub 222 223 // More flags! 224 LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled 225 }; 226 227 228 struct acsdefered_t; 229 230 struct FSpecialAction 231 { 232 FName Type; // this is initialized before the actors... 233 BYTE Action; 234 int Args[5]; // must allow 16 bit tags for 666 & 667! 235 }; 236 237 class FCompressedMemFile; 238 class DScroller; 239 240 class FScanner; 241 struct level_info_t; 242 243 struct FOptionalMapinfoData 244 { 245 FOptionalMapinfoData *Next; 246 FName identifier; FOptionalMapinfoDataFOptionalMapinfoData247 FOptionalMapinfoData() { Next = NULL; identifier = NAME_None; } ~FOptionalMapinfoDataFOptionalMapinfoData248 virtual ~FOptionalMapinfoData() {} 249 virtual FOptionalMapinfoData *Clone() const = 0; 250 }; 251 252 struct FOptionalMapinfoDataPtr 253 { 254 FOptionalMapinfoData *Ptr; 255 throwFOptionalMapinfoDataPtr256 FOptionalMapinfoDataPtr() throw() : Ptr(NULL) {} ~FOptionalMapinfoDataPtrFOptionalMapinfoDataPtr257 ~FOptionalMapinfoDataPtr() { if (Ptr!=NULL) delete Ptr; } throwFOptionalMapinfoDataPtr258 FOptionalMapinfoDataPtr(const FOptionalMapinfoDataPtr &p) throw() : Ptr(p.Ptr->Clone()) {} throwFOptionalMapinfoDataPtr259 FOptionalMapinfoDataPtr &operator= (FOptionalMapinfoDataPtr &p) throw() { Ptr = p.Ptr->Clone(); return *this; } 260 }; 261 262 typedef TMap<FName, FOptionalMapinfoDataPtr> FOptData; 263 typedef TMap<int, FName> FMusicMap; 264 265 enum EMapType 266 { 267 MAPTYPE_UNKNOWN = 0, 268 MAPTYPE_DOOM, 269 MAPTYPE_HEXEN, 270 MAPTYPE_BUILD, 271 MAPTYPE_UDMF // This does not distinguish between namespaces. 272 }; 273 274 struct level_info_t 275 { 276 int levelnum; 277 278 FString MapName; 279 FString NextMap; 280 FString NextSecretMap; 281 FString PName; 282 FString SkyPic1; 283 FString SkyPic2; 284 FString FadeTable; 285 FString F1Pic; 286 FString BorderTexture; 287 FString MapBackground; 288 289 int cluster; 290 int partime; 291 int sucktime; 292 DWORD flags; 293 DWORD flags2; 294 DWORD flags3; 295 296 FString Music; 297 FString LevelName; 298 SBYTE WallVertLight, WallHorizLight; 299 int musicorder; 300 FCompressedMemFile *snapshot; 301 DWORD snapshotVer; 302 struct acsdefered_t *defered; 303 float skyspeed1; 304 float skyspeed2; 305 DWORD fadeto; 306 DWORD outsidefog; 307 int cdtrack; 308 unsigned int cdid; 309 float gravity; 310 float aircontrol; 311 int WarpTrans; 312 int airsupply; 313 DWORD compatflags, compatflags2; 314 DWORD compatmask, compatmask2; 315 FString Translator; // for converting Doom-format linedef and sector types. 316 int DefaultEnvironment; // Default sound environment for the map. 317 FName Intermission; 318 FName deathsequence; 319 FName slideshow; 320 321 // Redirection: If any player is carrying the specified item, then 322 // you go to the RedirectMap instead of this one. 323 FName RedirectType; 324 FString RedirectMapName; 325 326 FString EnterPic; 327 FString ExitPic; 328 FString InterMusic; 329 int intermusicorder; 330 331 FString SoundInfo; 332 FString SndSeq; 333 334 float teamdamage; 335 336 FOptData optdata; 337 FMusicMap MusicMap; 338 339 TArray<FSpecialAction> specialactions; 340 341 TArray<FSoundID> PrecacheSounds; 342 TArray<FString> PrecacheTextures; 343 level_info_tlevel_info_t344 level_info_t() 345 { 346 Reset(); 347 } ~level_info_tlevel_info_t348 ~level_info_t() 349 { 350 ClearSnapshot(); 351 ClearDefered(); 352 } 353 void Reset(); 354 bool isValid(); 355 FString LookupLevelName (); 356 void ClearSnapshot(); 357 void ClearDefered(); 358 level_info_t *CheckLevelRedirect (); 359 360 template<class T> 361 T *GetOptData(FName id, bool create = true) 362 { 363 FOptionalMapinfoDataPtr *pdat = optdata.CheckKey(id); 364 365 if (pdat != NULL) 366 { 367 return static_cast<T*>(pdat->Ptr); 368 } 369 else if (create) 370 { 371 T *newobj = new T; 372 optdata[id].Ptr = newobj; 373 return newobj; 374 } 375 else return NULL; 376 } 377 }; 378 379 // [RH] These get zeroed every tic and are updated by thinkers. 380 struct FSectorScrollValues 381 { 382 fixed_t ScrollX, ScrollY; 383 }; 384 385 struct FLevelLocals 386 { 387 void Tick (); 388 void AddScroller (DScroller *, int secnum); 389 390 int time; // time in the hub 391 int maptime; // time in the map 392 int totaltime; // time in the game 393 int starttime; 394 int partime; 395 int sucktime; 396 397 level_info_t *info; 398 int cluster; 399 int clusterflags; 400 int levelnum; 401 int lumpnum; 402 FString LevelName; 403 FString MapName; // the lump name (E1M1, MAP01, etc) 404 FString NextMap; // go here when using the regular exit 405 FString NextSecretMap; // map to go to when used secret exit 406 EMapType maptype; 407 408 DWORD flags; 409 DWORD flags2; 410 DWORD flags3; 411 412 DWORD fadeto; // The color the palette fades to (usually black) 413 DWORD outsidefog; // The fog for sectors with sky ceilings 414 415 FString Music; 416 int musicorder; 417 int cdtrack; 418 unsigned int cdid; 419 FTextureID skytexture1; 420 FTextureID skytexture2; 421 422 float skyspeed1; // Scrolling speed of sky textures, in pixels per ms 423 float skyspeed2; 424 425 int total_secrets; 426 int found_secrets; 427 428 int total_items; 429 int found_items; 430 431 int total_monsters; 432 int killed_monsters; 433 434 float gravity; 435 fixed_t aircontrol; 436 fixed_t airfriction; 437 int airsupply; 438 int DefaultEnvironment; // Default sound environment. 439 440 TObjPtr<class ASkyViewpoint> DefaultSkybox; 441 442 FSectorScrollValues *Scrolls; // NULL if no DScrollers in this level 443 444 SBYTE WallVertLight; // Light diffs for vert/horiz walls 445 SBYTE WallHorizLight; 446 447 bool FromSnapshot; // The current map was restored from a snapshot 448 449 float teamdamage; 450 451 bool IsJumpingAllowed() const; 452 bool IsCrouchingAllowed() const; 453 bool IsFreelookAllowed() const; 454 }; 455 456 457 struct cluster_info_t 458 { 459 int cluster; 460 FString FinaleFlat; 461 FString ExitText; 462 FString EnterText; 463 FString MessageMusic; 464 int musicorder; 465 int flags; 466 int cdtrack; 467 FString ClusterName; 468 unsigned int cdid; 469 470 void Reset(); 471 472 }; 473 474 // Cluster flags 475 #define CLUSTER_HUB 0x00000001 // Cluster uses hub behavior 476 #define CLUSTER_EXITTEXTINLUMP 0x00000002 // Exit text is the name of a lump 477 #define CLUSTER_ENTERTEXTINLUMP 0x00000004 // Enter text is the name of a lump 478 #define CLUSTER_FINALEPIC 0x00000008 // Finale "flat" is actually a full-sized image 479 #define CLUSTER_LOOKUPEXITTEXT 0x00000010 // Exit text is the name of a language string 480 #define CLUSTER_LOOKUPENTERTEXT 0x00000020 // Enter text is the name of a language string 481 #define CLUSTER_LOOKUPNAME 0x00000040 // Name is the name of a language string 482 #define CLUSTER_LOOKUPCLUSTERNAME 0x00000080 // Cluster name is the name of a language string 483 484 extern FLevelLocals level; 485 486 extern TArray<level_info_t> wadlevelinfos; 487 extern TArray<cluster_info_t> wadclusterinfos; 488 489 extern bool savegamerestore; 490 491 // mapname will be changed if it is a valid warptrans 492 bool CheckWarpTransMap (FString &mapname, bool substitute); 493 494 void G_InitNew (const char *mapname, bool bTitleLevel); 495 496 // Can be called by the startup code or M_Responder. 497 // A normal game starts at map 1, 498 // but a warp test can start elsewhere 499 void G_DeferedInitNew (const char *mapname, int skill = -1); 500 struct FGameStartup; 501 void G_DeferedInitNew (FGameStartup *gs); 502 503 void G_ExitLevel (int position, bool keepFacing); 504 void G_SecretExitLevel (int position); 505 const char *G_GetExitMap(); 506 const char *G_GetSecretExitMap(); 507 508 enum 509 { 510 CHANGELEVEL_KEEPFACING = 1, 511 CHANGELEVEL_RESETINVENTORY = 2, 512 CHANGELEVEL_NOMONSTERS = 4, 513 CHANGELEVEL_CHANGESKILL = 8, 514 CHANGELEVEL_NOINTERMISSION = 16, 515 CHANGELEVEL_RESETHEALTH = 32, 516 CHANGELEVEL_PRERAISEWEAPON = 64, 517 }; 518 519 void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill=-1); 520 521 void G_StartTravel (); 522 void G_FinishTravel (); 523 524 void G_DoLoadLevel (int position, bool autosave); 525 526 void G_InitLevelLocals (void); 527 528 void G_AirControlChanged (); 529 530 cluster_info_t *FindClusterInfo (int cluster); 531 level_info_t *FindLevelInfo (const char *mapname, bool allowdefault=true); 532 level_info_t *FindLevelByNum (int num); 533 level_info_t *CheckLevelRedirect (level_info_t *info); 534 535 FString CalcMapName (int episode, int level); 536 537 void G_ParseMapInfo (FString basemapinfo); 538 539 void G_ClearSnapshots (void); 540 void P_RemoveDefereds (); 541 void G_SnapshotLevel (void); 542 void G_UnSnapshotLevel (bool keepPlayers); 543 struct PNGHandle; 544 void G_ReadSnapshots (PNGHandle *png); 545 void G_WriteSnapshots (FILE *file); 546 void G_ClearHubInfo(); 547 548 enum ESkillProperty 549 { 550 SKILLP_AmmoFactor, 551 SKILLP_DropAmmoFactor, 552 SKILLP_DamageFactor, 553 SKILLP_FastMonsters, 554 SKILLP_Respawn, 555 SKILLP_RespawnLimit, 556 SKILLP_Aggressiveness, 557 SKILLP_DisableCheats, 558 SKILLP_AutoUseHealth, 559 SKILLP_SpawnFilter, 560 SKILLP_EasyBossBrain, 561 SKILLP_ACSReturn, 562 SKILLP_MonsterHealth, 563 SKILLP_FriendlyHealth, 564 SKILLP_NoPain, 565 SKILLP_ArmorFactor, 566 SKILLP_HealthFactor, 567 SKILLP_EasyKey, 568 SKILLP_SlowMonsters, 569 SKILLP_Infight, 570 }; 571 int G_SkillProperty(ESkillProperty prop); 572 const char * G_SkillName(); 573 574 typedef TMap<FName, FString> SkillMenuNames; 575 576 typedef TMap<FName, FName> SkillActorReplacement; 577 578 struct FSkillInfo 579 { 580 FName Name; 581 fixed_t AmmoFactor, DoubleAmmoFactor, DropAmmoFactor; 582 fixed_t DamageFactor; 583 bool FastMonsters; 584 bool SlowMonsters; 585 bool DisableCheats; 586 bool AutoUseHealth; 587 588 bool EasyBossBrain; 589 bool EasyKey; 590 int RespawnCounter; 591 int RespawnLimit; 592 fixed_t Aggressiveness; 593 int SpawnFilter; 594 int ACSReturn; 595 FString MenuName; 596 FString PicName; 597 SkillMenuNames MenuNamesForPlayerClass; 598 bool MustConfirm; 599 FString MustConfirmText; 600 char Shortcut; 601 FString TextColor; 602 SkillActorReplacement Replace; 603 SkillActorReplacement Replaced; 604 fixed_t MonsterHealth; 605 fixed_t FriendlyHealth; 606 bool NoPain; 607 int Infighting; 608 fixed_t ArmorFactor; 609 fixed_t HealthFactor; 610 FSkillInfoFSkillInfo611 FSkillInfo() {} FSkillInfoFSkillInfo612 FSkillInfo(const FSkillInfo &other) 613 { 614 operator=(other); 615 } 616 FSkillInfo &operator=(const FSkillInfo &other); 617 int GetTextColor() const; 618 619 void SetReplacement(FName a, FName b); 620 FName GetReplacement(FName a); 621 void SetReplacedBy(FName b, FName a); 622 FName GetReplacedBy(FName b); 623 }; 624 625 extern TArray<FSkillInfo> AllSkills; 626 extern int DefaultSkill; 627 628 struct FEpisode 629 { 630 FString mEpisodeName; 631 FString mEpisodeMap; 632 FString mPicName; 633 char mShortcut; 634 bool mNoSkill; 635 }; 636 637 extern TArray<FEpisode> AllEpisodes; 638 639 640 #endif //__G_LEVEL_H__ 641