2019-12-09 23:01:45 +00:00
# pragma once
# include "gstrings.h"
2019-12-10 16:35:28 +00:00
# include "cmdlib.h"
2019-12-10 23:57:53 +00:00
# include "quotemgr.h"
2021-05-01 20:52:28 +00:00
# include "palentry.h"
# include "vectors.h"
2021-05-21 23:34:00 +00:00
# include "screenjob.h"
2022-09-28 03:33:54 +00:00
# include "maptypes.h"
2023-09-30 21:06:27 +00:00
# include "d_net.h"
2021-05-21 23:34:00 +00:00
2019-12-10 23:57:53 +00:00
# ifdef GetMessage
# undef GetMessage // Windows strikes...
# endif
2019-12-09 23:01:45 +00:00
2020-10-04 16:31:48 +00:00
enum EMax
{
MAXSKILLS = 7 ,
MAXMENUGAMEPLAYENTRIES = 7 ,
} ;
2020-10-06 23:31:41 +00:00
enum EVolFlags
{
2021-05-01 20:52:28 +00:00
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 ,
2021-05-02 08:55:22 +00:00
LEVEL_FORCENOEOG = 16 , // RR E1L7 needs this to override its boss's death ending the game.
2021-05-01 20:52:28 +00:00
} ;
enum EMapGameFlags
{
LEVEL_RR_HULKSPAWN = 1 ,
LEVEL_RR_CLEARMOONSHINE = 2 ,
2021-05-02 13:54:19 +00:00
LEVEL_EX_COUNTDOWN = 4 ,
LEVEL_EX_TRAINING = 8 ,
LEVEL_EX_ALTSOUND = 16 ,
LEVEL_EX_MULTI = 32 ,
2021-05-02 15:35:25 +00:00
LEVEL_SW_SPAWNMINES = 64 ,
LEVEL_SW_BOSSMETER_SERPENT = 128 ,
LEVEL_SW_BOSSMETER_SUMO = 256 ,
LEVEL_SW_BOSSMETER_ZILLA = 512 ,
LEVEL_SW_DEATHEXIT_SERPENT = 1024 ,
LEVEL_SW_DEATHEXIT_SUMO = 2048 ,
LEVEL_SW_DEATHEXIT_ZILLA = 4096 ,
2021-07-25 22:58:00 +00:00
LEVEL_SW_DEATHEXIT_SERPENT_NEXT = 8192 ,
2021-05-02 15:35:25 +00:00
2021-07-25 22:58:00 +00:00
LEVEL_WT_BOSSSPAWN = 16384 ,
2021-05-29 11:12:50 +00:00
2021-08-13 20:25:13 +00:00
LEVEL_BOSSONLYCUTSCENE = 32768 ,
2020-10-06 23:31:41 +00:00
} ;
2020-10-04 16:31:48 +00:00
// These get filled in by the map definition parsers of the front ends.
extern FString gSkillNames [ MAXSKILLS ] ;
extern int gDefaultVolume , gDefaultSkill ;
2019-12-09 23:01:45 +00:00
// Localization capable replacement of the game specific solutions.
2019-12-10 21:22:59 +00:00
enum
{
MI_FORCEEOG = 1 ,
2020-07-07 11:19:09 +00:00
MI_USERMAP = 2 ,
2019-12-10 21:22:59 +00:00
} ;
2020-08-03 18:12:33 +00:00
enum {
MAX_MESSAGES = 32
} ;
2021-04-25 23:45:16 +00:00
class DObject ;
2021-04-27 22:51:28 +00:00
struct MapRecord ;
2021-04-25 23:45:16 +00:00
struct GlobalCutscenes
{
CutsceneDef Intro ;
CutsceneDef DefaultMapIntro ;
CutsceneDef DefaultMapOutro ;
2021-05-02 10:22:40 +00:00
CutsceneDef DefaultGameover ;
2021-04-27 22:51:28 +00:00
CutsceneDef SharewareEnd ;
2021-04-27 23:12:07 +00:00
CutsceneDef LoadingScreen ;
2021-04-25 23:45:16 +00:00
FString MPSummaryScreen ;
FString SummaryScreen ;
2021-05-16 08:43:47 +00:00
FString StatusBarClass ;
2021-04-25 23:45:16 +00:00
} ;
2021-05-01 20:52:28 +00:00
struct ClusterDef
2021-04-25 23:45:16 +00:00
{
2021-05-01 20:52:28 +00:00
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
} ;
struct VolumeRecord // episodes
{
FString startmap ;
2021-04-25 23:45:16 +00:00
FString name ;
FString subtitle ;
2021-05-01 20:52:28 +00:00
int index = - 1 ;
int flags = 0 ;
int shortcut = 0 ;
2021-04-25 23:45:16 +00:00
} ;
2019-12-09 23:01:45 +00:00
struct MapRecord
{
2020-07-01 18:31:29 +00:00
int parTime = 0 ;
int designerTime = 0 ;
2019-12-09 23:01:45 +00:00
FString fileName ;
2019-12-10 16:35:28 +00:00
FString labelName ;
2019-12-09 23:01:45 +00:00
FString name ;
FString music ;
2021-05-01 20:52:28 +00:00
FString Author ;
FString NextMap ;
FString NextSecret ;
int cdSongId = - 1 ;
int musicorder = - 1 ;
2021-04-25 23:45:16 +00:00
CutsceneDef intro ;
CutsceneDef outro ;
2019-12-11 22:41:05 +00:00
int flags = 0 ;
2021-05-01 20:52:28 +00:00
int gameflags = 0 ;
2020-07-01 18:31:29 +00:00
int levelNumber = - 1 ;
2021-04-27 18:04:11 +00:00
int cluster = - 1 ;
2019-12-09 23:01:45 +00:00
2021-05-01 20:52:28 +00:00
PalEntry fadeto = 0 ;
int fogdensity = 0 ;
int skyfog = 0 ;
FString BorderTexture ;
FString InterBackground ;
TArray < FString > PrecacheTextures ;
FVector4 skyrotatevector ;
2019-12-09 23:01:45 +00:00
// The rest is only used by Blood
2020-08-03 18:12:33 +00:00
FString messages [ MAX_MESSAGES ] ;
2019-12-10 23:57:53 +00:00
int8_t fog = - 1 , weather = - 1 ; // Blood defines these but they aren't used.
2021-05-01 20:52:28 +00:00
// game specific stuff
int rr_startsound = 0 ;
2021-05-02 12:01:10 +00:00
int rr_mamaspawn = 15 ;
2023-03-26 10:57:17 +00:00
DAngle ex_ramses_horiz = maphoriz ( - 11 ) ;
2021-05-02 13:54:19 +00:00
int ex_ramses_cdtrack = - 1 ; // this is not music, it is the actual dialogue!
FString ex_ramses_pup ;
FString ex_ramses_text ;
2021-12-30 09:30:21 +00:00
2020-07-07 11:19:09 +00:00
const char * LabelName ( ) const
{
2021-12-26 11:09:06 +00:00
if ( flags & MI_USERMAP ) return GStrings ( " MNU_USERMAP " ) ;
2020-07-07 11:19:09 +00:00
return labelName ;
}
const char * DisplayName ( ) const
2019-12-09 23:01:45 +00:00
{
2019-12-10 16:35:28 +00:00
if ( name . IsEmpty ( ) ) return labelName ;
2019-12-09 23:01:45 +00:00
return GStrings . localize ( name ) ;
}
void SetName ( const char * n )
{
name = n ;
2021-10-19 19:09:49 +00:00
name . StripRight ( ) ;
name = FStringTable : : MakeMacro ( name ) ;
2019-12-09 23:01:45 +00:00
}
2019-12-10 16:35:28 +00:00
void SetFileName ( const char * n )
{
2020-09-03 21:10:28 +00:00
if ( * n = = ' / ' | | * n = = ' \\ ' ) n + + ;
2019-12-10 16:35:28 +00:00
fileName = n ;
2020-03-01 06:28:40 +00:00
FixPathSeperator ( fileName ) ;
2019-12-10 16:35:28 +00:00
labelName = ExtractFileBase ( n ) ;
}
2019-12-10 23:57:53 +00:00
const char * GetMessage ( int num )
{
2020-08-03 18:12:33 +00:00
if ( num < 0 | | num > = MAX_MESSAGES ) return " " ;
return GStrings ( messages [ num ] ) ;
}
2021-12-30 09:30:21 +00:00
2020-08-03 18:12:33 +00:00
void AddMessage ( int num , const FString & msg )
{
messages [ num ] = msg ;
2019-12-10 23:57:53 +00:00
}
2019-12-09 23:01:45 +00:00
} ;
2023-09-30 21:06:27 +00:00
struct StatRecord
{
int max ;
int got ;
int player [ MAXPLAYERS ] ;
void addTotal ( int amount = 1 )
{
max + = amount ;
if ( amount < 0 & & max < 0 ) max = 0 ;
}
void add ( int playerno , int amount = 1 )
{
got + = amount ;
if ( amount < 0 & & got < 0 ) got = 0 ;
if ( playerno > = 0 & & playerno < MAXPLAYERS )
{
player [ playerno ] + = amount ;
if ( amount < 0 & & player [ playerno ] < 0 ) player [ playerno ] = 0 ;
}
}
void clear ( )
{
memset ( this , 0 , sizeof ( * this ) ) ;
}
} ;
2021-04-26 00:00:40 +00:00
struct SummaryInfo
{
int kills ;
int maxkills ;
int secrets ;
int maxsecrets ;
int supersecrets ;
int time ;
2022-10-25 15:09:45 +00:00
int totaltime ;
2021-04-26 19:13:11 +00:00
int playercount ;
2021-04-26 00:00:40 +00:00
bool cheated ;
bool endofgame ;
} ;
2023-09-30 21:06:27 +00:00
struct MapLocals
{
StatRecord kills , secrets , superSecrets ;
void fillSummary ( SummaryInfo & sum )
{
sum . kills = kills . got ;
sum . maxkills = kills . max ;
sum . secrets = secrets . got ;
sum . maxsecrets = std : : max ( secrets . got , secrets . max ) ; // If we found more than there are, increase the total. Blood's secret maintenance is too broken to get right.
sum . supersecrets = superSecrets . got ;
// todo: centralize the remaining info as well.
}
void clearStats ( )
{
kills . clear ( ) ;
secrets . clear ( ) ;
superSecrets . clear ( ) ;
}
void setKills ( int num )
{
kills . clear ( ) ;
kills . max = num ;
}
void setSecrets ( int num , int supernum = 0 )
{
secrets . clear ( ) ;
secrets . max = num ;
superSecrets . clear ( ) ;
superSecrets . max = supernum ;
}
void addKillCount ( int amount = 1 )
{
kills . addTotal ( amount ) ;
}
2023-10-01 08:07:23 +00:00
void addSecretCount ( int amount = 1 )
{
secrets . addTotal ( amount ) ;
}
2023-09-30 21:06:27 +00:00
void addKill ( int playerno , int amount = 1 )
{
kills . add ( playerno , amount ) ;
}
void addSecret ( int playerno , int amount = 1 )
{
secrets . add ( playerno , amount ) ;
}
void addSuperSecret ( int playerno , int amount = 1 )
{
superSecrets . add ( playerno , amount ) ;
}
2023-10-01 08:33:07 +00:00
void addFrags ( int playerno , int amount )
{
// todo
}
2023-09-30 21:06:27 +00:00
} ;
2021-04-25 23:45:16 +00:00
extern GlobalCutscenes globalCutscenes ;
2023-09-30 21:06:27 +00:00
extern MapRecord * currentLevel ; // level that is currently played.
extern MapLocals Level ;
2020-06-23 22:40:22 +00:00
2021-09-05 18:35:10 +00:00
void SetMusicReplacement ( const char * mapname , const char * music ) ;
void ReplaceMusics ( bool namehack = false ) ;
2020-07-07 11:19:09 +00:00
bool SetMusicForMap ( const char * mapname , const char * music , bool namehack = false ) ;
2020-06-23 22:40:22 +00:00
2020-07-07 11:19:09 +00:00
MapRecord * FindMapByName ( const char * nm ) ;
MapRecord * FindMapByLevelNum ( int num ) ;
2021-05-02 07:08:57 +00:00
MapRecord * FindMapByIndexOnly ( int clst , int num ) ; // this is for map setup where fallbacks are undesirable.
MapRecord * FindMapByIndex ( int clst , int num ) ;
2020-07-07 11:19:09 +00:00
MapRecord * FindNextMap ( MapRecord * thismap ) ;
2021-05-02 07:08:57 +00:00
MapRecord * FindNextSecretMap ( MapRecord * thismap ) ;
2020-07-07 18:27:21 +00:00
MapRecord * SetupUserMap ( const char * boardfilename , const char * defaultmusic = nullptr ) ;
MapRecord * AllocateMap ( ) ;
2019-12-10 21:22:59 +00:00
2021-05-01 22:35:56 +00:00
VolumeRecord * FindVolume ( int index ) ;
ClusterDef * FindCluster ( int index ) ;
ClusterDef * AllocateCluster ( ) ;
VolumeRecord * AllocateVolume ( ) ;
2021-05-01 20:52:28 +00:00
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 ;
}
2020-08-03 18:11:30 +00:00
// These should be the only places converting between level numbers and volume/map pairs
2021-05-01 20:52:28 +00:00
constexpr inline int makelevelnum ( int vol , int map )
2020-08-03 18:11:30 +00:00
{
return vol * 1000 + map ;
}
2019-12-10 21:22:59 +00:00
enum
{
RRENDSLOT = 127
2020-08-03 18:11:30 +00:00
} ;