#pragma once #include "gstrings.h" #include "cmdlib.h" #include "quotemgr.h" #include "palentry.h" #include "vectors.h" #ifdef GetMessage #undef GetMessage // Windows strikes... #endif enum EMax { MAXSKILLS = 7, MAXMENUGAMEPLAYENTRIES = 7, }; enum EVolFlags { VF_HIDEFROMSP = 1, VF_OPTIONAL = 2, VF_SHAREWARELOCK = 4, // show in shareware but lock access. VF_NOSKILL = 8, }; enum EMapFlags { LEVEL_NOINTERMISSION = 1, LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels. LEVEL_CLEARINVENTORY = 4, LEVEL_CLEARWEAPONS = 8, LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game. }; enum EMapGameFlags { LEVEL_RR_HULKSPAWN = 1, LEVEL_RR_CLEARMOONSHINE = 2, }; // These get filled in by the map definition parsers of the front ends. extern FString gSkillNames[MAXSKILLS]; extern int gDefaultVolume, gDefaultSkill; // Localization capable replacement of the game specific solutions. inline void MakeStringLocalizable(FString "e) { // Only prepend a quote if the string is localizable. if (quote.Len() > 0 && quote[0] != '$' && GStrings[quote]) quote.Insert(0, "$"); } enum { MI_FORCEEOG = 1, MI_USERMAP = 2, }; enum { MAX_MESSAGES = 32 }; class DObject; struct MapRecord; struct CutsceneDef { FString video; FString function; FString soundName; int soundID; // ResID not SoundID! int sound = 0; int framespersec = 0; // only relevant for ANM. bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game. void Create(DObject* runner); bool Create(DObject* runner, MapRecord* map, bool transition); bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); } int GetSound(); }; struct GlobalCutscenes { CutsceneDef Intro; CutsceneDef DefaultMapIntro; CutsceneDef DefaultMapOutro; CutsceneDef SharewareEnd; CutsceneDef LoadingScreen; FString MPSummaryScreen; FString SummaryScreen; }; struct ClusterDef { FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be. CutsceneDef intro; // plays when entering this cluster CutsceneDef outro; // plays when leaving this cluster CutsceneDef gameover; // when defined, plays when the player dies in this cluster FString InterBackground; int index = -1; int flags = 0; // engine and common flags int gameflags = 0; // game specific flags. }; struct VolumeRecord // episodes { FString startmap; FString name; FString subtitle; int index = -1; int flags = 0; int shortcut = 0; }; struct MapRecord { int parTime = 0; int designerTime = 0; FString fileName; FString labelName; FString name; FString music; FString Author; FString NextMap; FString NextSecret; int cdSongId = -1; int musicorder = -1; CutsceneDef intro; CutsceneDef outro; int flags = 0; int gameflags = 0; int levelNumber = -1; int mapindex = -1; // index in the episode. This only for finding the next map in the progression when nothing explicit is defined. int cluster = -1; PalEntry fadeto = 0; int fogdensity = 0; int skyfog = 0; FString BorderTexture; FString InterBackground; TArray PrecacheTextures; FVector4 skyrotatevector; // The rest is only used by Blood FString messages[MAX_MESSAGES]; int8_t fog = -1, weather = -1; // Blood defines these but they aren't used. // game specific stuff int rr_startsound = 0; const char* LabelName() const { if (flags & MI_USERMAP) return GStrings("TXT_USERMAP"); return labelName; } const char *DisplayName() const { if (name.IsEmpty()) return labelName; return GStrings.localize(name); } void SetName(const char *n) { name = n; MakeStringLocalizable(name); } void SetFileName(const char* n) { if (*n == '/' || *n == '\\') n++; fileName = n; FixPathSeperator(fileName); labelName = ExtractFileBase(n); } const char* GetMessage(int num) { if (num < 0 || num>= MAX_MESSAGES) return ""; return GStrings(messages[num]); } void AddMessage(int num, const FString &msg) { messages[num] = msg; } }; struct SummaryInfo { int kills; int maxkills; int secrets; int maxsecrets; int supersecrets; int time; int playercount; bool cheated; bool endofgame; }; extern GlobalCutscenes globalCutscenes; extern MapRecord *currentLevel; bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false); MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByLevelNum(int num); MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable. MapRecord* FindMapByIndex(int clst, int num); MapRecord *FindNextMap(MapRecord *thismap); MapRecord* FindNextSecretMap(MapRecord* thismap); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* AllocateMap(); VolumeRecord* FindVolume(int index); ClusterDef* FindCluster(int index); ClusterDef* AllocateCluster(); VolumeRecord* AllocateVolume(); void SetLevelNum(MapRecord* info, int num); inline VolumeRecord* MustFindVolume(int index) { auto r = FindVolume(index); if (r) return r; r = AllocateVolume(); r->index = index; return r; } inline ClusterDef* MustFindCluster(int index) { auto r = FindCluster(index); if (r) return r; r = AllocateCluster(); r->index = index; return r; } // These should be the only places converting between level numbers and volume/map pairs constexpr inline int makelevelnum(int vol, int map) { return vol * 1000 + map; } enum { RRENDSLOT = 127 };