- WIP level transition refactored to a game independent event system.

This commit is contained in:
Christoph Oelckers 2020-09-03 23:10:28 +02:00
parent 1cf0ca3f75
commit e5e8c02f1d
36 changed files with 536 additions and 368 deletions

View file

@ -509,9 +509,7 @@ void GameInterface::Startup()
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
else
{
gamestate = GS_MENUSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
startmainmenu();
}
}
}
@ -573,6 +571,13 @@ void GameInterface::FreeGameData()
if (BloodINI) delete BloodINI;
}
void GameInterface::FreeLevelData()
{
EndLevel();
::GameInterface::FreeLevelData();
}
ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
{
int top = 0;

View file

@ -79,6 +79,7 @@ struct GameInterface : ::GameInterface
const char* Name() override { return "Blood"; }
void app_init() override;
bool GenerateSavePic() override;
void FreeLevelData() override;
void FreeGameData() override;
FSavegameInfo GetSaveSig() override;
void MenuOpened() override;

View file

@ -99,17 +99,6 @@ enum rendmode_t {
Iter>=0 && (Next=nextspritestat[Iter], 1); Iter=Next
////////// True Room over Room (YAX == rot -17 of "PRO") //////////
#define YAX_ENABLE
//#define YAX_DEBUG
//#define ENGINE_SCREENSHOT_DEBUG
#ifdef YAX_ENABLE
# if !defined NEW_MAP_FORMAT
# define YAX_ENABLE__COMPAT
# endif
#endif
////////// yax defs //////////
#define SECTORFLD(Sect,Fld, Cf) (*((Cf) ? (&sector[Sect].floor##Fld) : (&sector[Sect].ceiling##Fld)))
@ -648,7 +637,6 @@ int32_t videoSetGameMode(char davidoption, int32_t daupscaledxdim, int32_t dau
void videoSetCorrectedAspect();
void videoSetViewableArea(int32_t x1, int32_t y1, int32_t x2, int32_t y2);
void renderSetAspect(int32_t daxrange, int32_t daaspect);
inline void renderFlushPerms(void) {}
void plotpixel(int32_t x, int32_t y, char col);
FCanvasTexture *renderSetTarget(int16_t tilenume);

View file

@ -43,10 +43,18 @@
#include "gamestate.h"
#include "mmulti.h"
#include "gstrings.h"
#include "gamecontrol.h"
#include "mapinfo.h"
CVAR(Bool, sv_cheats, true, CVAR_ARCHIVE|CVAR_SERVERINFO)
CVAR(Bool, cl_blockcheats, false, 0)
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool CheckCheatmode (bool printmsg, bool sponly)
{
if ((sponly && netgame) || gamestate != GS_LEVEL)
@ -76,6 +84,12 @@ bool CheckCheatmode (bool printmsg, bool sponly)
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void genericCheat(int player, uint8_t** stream, bool skip)
{
int cheat = ReadByte(stream);
@ -94,6 +108,11 @@ void genericCheat(int player, uint8_t** stream, bool skip)
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(god)
{
@ -140,6 +159,12 @@ CCMD(allmap)
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(give)
{
static const char* type[] = { "ALL","AMMO","ARMOR","HEALTH","INVENTORY","ITEMS","KEYS","WEAPONS",nullptr };
@ -167,3 +192,250 @@ CCMD(give)
Net_WriteByte(found);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void CompleteLevel(MapRecord* map)
{
gameaction = ga_completed;
g_nextmap = !currentLevel || !(currentLevel->flags & MI_FORCEEOG)? map : nullptr;
g_nextskill = -1; // This does not change the skill
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void changeMap(int player, uint8_t** stream, bool skip)
{
int skill = (int8_t)ReadByte(stream);
auto mapname = ReadStringConst(stream);
if (skip) return;
auto map = FindMapByName(mapname);
if (map || *mapname == 0) // mapname = "" signals end of episode
{
gameaction = ga_completed;
g_nextmap = map;
g_nextskill = skill;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ChangeLevel(MapRecord* map, int skill)
{
Net_WriteByte(DEM_CHANGEMAP);
Net_WriteByte(skill);
Net_WriteString(map->labelName);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void DeferedStartGame(MapRecord* map, int skill)
{
g_nextmap = map;
g_nextskill = skill;
gameaction = ga_newgame;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2)
{
int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order.
if (argv.argc() <= numparm)
{
if (numparm == 2) Printf(PRINT_BOLD, "%s <e> <m>: %s episode 'e' and map 'm'\n", cmdname, t2);
else Printf(PRINT_BOLD, "%s <m>: %s map 'm'\n", cmdname, t2);
return nullptr;
}
// Values are one-based.
int e = numparm == 2 ? atoi(argv[1]) : 0;
int m = atoi(numparm == 2 ? argv[2] : argv[1]);
if (e <= 0 || m <= 0)
{
Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n");
return nullptr;
}
auto map = FindMapByLevelNum(levelnum(e - 1, m - 1));
if (!map)
{
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);
else Printf(PRINT_BOLD, "Level %s not found!\n", argv[1]);
return nullptr;
}
return map;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(levelwarp)
{
if (gamestate != GS_LEVEL)
{
Printf("Use the startgame command when not in a game.\n");
return;
}
#if 0
if (/*!players[consoleplayer].settings_controller &&*/ netgame)
{
Printf("Only setting controllers can change the map.\n");
return;
}
#endif
auto map = levelwarp_common(argv, "levelwarp", "warp to");
if (map)
{
ChangeLevel(map, -1);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(startgame)
{
if (netgame)
{
Printf("Use " TEXTCOLOR_BOLD "levelwarp" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "startgame"
TEXTCOLOR_NORMAL " is for single-player only.\n");
return;
}
auto map = levelwarp_common(argv, "start game", "start new game at");
if (map)
{
DeferedStartGame(map, -1);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(changemap)
{
if (argv.argc() < 2)
{
Printf(PRINT_BOLD, "changemap <mapname>: warp to the given map, identified by its name.\n");
return;
}
if (gamestate != GS_LEVEL)
{
Printf("Use the map command when not in a game.\n");
return;
}
#if 0
if (/*!players[consoleplayer].settings_controller &&*/ netgame)
{
Printf("Only setting controllers can change the map.\n");
return;
}
#endif
FString mapname = argv[1];
FString mapfilename = mapname;
DefaultExtension(mapfilename, ".map");
auto map = FindMapByName(mapname);
if (map == nullptr)
{
// got a user map
Printf(PRINT_BOLD, "%s: Map not defined.\n", mapname.GetChars());
return;
}
if (map->flags & MI_USERMAP)
{
// got a user map
Printf(PRINT_BOLD, "%s: Cannot warp to user maps.\n", mapname.GetChars());
return;
}
ChangeLevel(map, -1);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(map)
{
if (argv.argc() < 2)
{
Printf(PRINT_BOLD, "map <mapname>: start new game at the given map, identified by its name.\n");
return;
}
if (netgame)
{
Printf("Use " TEXTCOLOR_BOLD "changemap" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "map"
TEXTCOLOR_NORMAL " is for single-player only.\n");
return;
}
FString mapname = argv[1];
FString mapfilename = mapname;
DefaultExtension(mapfilename, ".map");
// Check if the map is already defined.
auto map = FindMapByName(mapname);
if (map == nullptr)
{
// got a user map
if (g_gameType & GAMEFLAG_SHAREWARE)
{
Printf(PRINT_BOLD, "Cannot use user maps in shareware.\n");
return;
}
map = SetupUserMap(mapfilename, g_gameType & GAMEFLAG_DUKE? "dethtoll.mid" : nullptr);
}
if (map)
{
DeferedStartGame(map, -1);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(restartmap)
{
if (gamestate != GS_LEVEL || currentLevel == nullptr)
{
Printf("Must be in a game to restart a level.\n");
return;
}
ChangeLevel(currentLevel, -1);
}

View file

@ -7,3 +7,4 @@
EXTERN_CVAR(Bool, sv_cheats)
void genericCheat(int player, uint8_t** stream, bool skip);
void changeMap(int player, uint8_t** stream, bool skip);

View file

@ -1664,6 +1664,7 @@ bool D_CheckNetGame (void)
// First install the global net command handlers
Net_SetCommandHandler(DEM_GENERICCHEAT, genericCheat);
Net_SetCommandHandler(DEM_CHANGEMAP, changeMap);
for (i = 0; i < MAXNETNODES; i++)
{

View file

@ -122,6 +122,11 @@ CCMD(togglefollow)
cycle_t thinktime, actortime, gameupdatetime, drawtime;
gamestate_t gamestate = GS_STARTUP;
gameaction_t gameaction = ga_nothing;
// gameaction state
MapRecord* g_nextmap;
int g_nextskill;
FILE* hashfile;
@ -1182,3 +1187,11 @@ void startmainmenu()
FX_StopAllSounds();
}
void GameInterface::FreeLevelData()
{
// Make sure that there is no more level to toy around with.
initspritelists();
numsectors = numwalls = 0;
currentLevel = nullptr;
}

View file

@ -19,6 +19,12 @@ extern bool AppActive;
extern cycle_t drawtime, actortime, thinktime, gameupdatetime;
extern bool r_NoInterpolate;
struct MapRecord;
struct FSaveGameNode;
extern MapRecord* g_nextmap;
extern int g_nextskill;
extern FSaveGameNode* g_savenode;
extern FMemArena dump; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown.
extern TMap<FName, int32_t> NameToTileIndex;
@ -50,6 +56,9 @@ void CONFIG_ReadCombatMacros();
int GameMain();
void startmainmenu();
void updatePauseStatus();
void DeferedStartGame(MapRecord* map, int skill);
void ChangeLevel(MapRecord* map, int skill);
void CompleteLevel(MapRecord* map);
struct UserConfig
{

View file

@ -21,4 +21,20 @@ enum gamestate_t : int
GS_FORCEWIPEMELT = -4
};
enum gameaction_t : int
{
ga_nothing,
ga_startup, // go back to intro after uninitializing the game state
ga_mainmenu, // go back to main menu after uninitializing the game state
ga_newgame, // start a new game
ga_recordgame, // start a new demo recording (later)
ga_loadgame, // load a savegame and resume play.
ga_loadgameplaydemo, // load a savegame and play a demo.
ga_autoloadgame, // load last autosave and resume play.
ga_savegame, // save the game
ga_autosave, // autosave the game (for triggering a save from within the game.)
ga_completed, // Level was exited.
ga_nextlevel // Actually start the next level.
};
extern gamestate_t gamestate;
extern gameaction_t gameaction;

View file

@ -23,8 +23,6 @@ struct FNewGameStartup
int Episode;
int Level;
int Skill;
int CustomLevel1;
int CustomLevel2;
};
struct FSavegameInfo
@ -56,6 +54,7 @@ struct ReservedSpace
};
enum EMenuSounds : int;
struct MapRecord;
extern cycle_t drawtime, actortime, thinktime, gameupdatetime;
@ -67,6 +66,7 @@ struct GameInterface
virtual void app_init() = 0;
virtual void clearlocalinputstate() {}
virtual void UpdateScreenSize() {}
virtual void FreeLevelData();
virtual void FreeGameData() {}
virtual void PlayHudSound() {}
virtual GameStats getStats() { return {}; }
@ -86,7 +86,6 @@ struct GameInterface
virtual bool SaveGame(FSaveGameNode*) { return true; }
virtual bool LoadGame(FSaveGameNode*) { return true; }
virtual void SerializeGameState(FSerializer& arc) {}
virtual bool CleanupForLoad() { return true; }
virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {}
virtual void QuitToTitle() {}
virtual void SetAmbience(bool on) {}
@ -104,6 +103,9 @@ struct GameInterface
virtual int GetPlayerChecksum(int pnum) { return 0x12345678 + pnum; }
virtual const char *CheckCheatMode() { return nullptr; }
virtual const char* GenericCheat(int player, int cheat) = 0;
virtual void NextLevel(MapRecord* map, int skill) {}
virtual void NewGame(MapRecord* map, int skill) {}
virtual void LevelCompleted(MapRecord* map, int skill) {}
virtual FString statFPS()
{

View file

@ -83,6 +83,7 @@
#include "palette.h"
#include "build.h"
#include "g_input.h"
#include "mapinfo.h"
CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, r_ticstability, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -125,16 +126,75 @@ static void GameTicker()
handleevents();
#if 0
// Todo: Migrate state changes to here instead of doing them ad-hoc
while (gameaction != ga_nothing)
{
switch (gameaction)
auto ga = gameaction;
gameaction = ga_nothing;
switch (ga)
{
case ga_autoloadgame:
// todo: for now just handle the restart case
g_nextmap = currentLevel;
g_nextskill = -1;
FX_StopAllSounds();
FX_SetReverb(0);
gi->NextLevel(currentLevel, -1);
break;
case ga_completed:
FX_StopAllSounds();
FX_SetReverb(0);
if (g_nextmap == currentLevel)
{
// if the same level is restarted, skip any progression stuff like summary screens or cutscenes.
gi->FreeLevelData();
gi->NextLevel(currentLevel, g_nextskill);
}
else
gi->LevelCompleted(g_nextmap, g_nextskill);
break;
case ga_nextlevel:
gi->FreeLevelData();
gi->NextLevel(currentLevel, g_nextskill);
break;
case ga_newgame:
FX_StopAllSounds();
FX_SetReverb(0);
gi->FreeLevelData();
gi->NewGame(g_nextmap, g_nextskill);
break;
case ga_startup:
gi->FreeLevelData();
gamestate = GS_STARTUP;
break;
case ga_mainmenu:
gi->FreeLevelData();
startmainmenu();
break;
case ga_savegame:
// We only need this for multiplayer saves that need to go through the network.
// gi->SaveGame();
break;
case ga_autosave:
M_Autosave();
break;
// for later
// case ga_recordgame, // start a new demo recording (later)
// case ga_loadgameplaydemo, // load a savegame and play a demo.
default:
break;
}
C_AdjustBottom();
}
#endif
// get commands, check consistancy, and build new consistancy check
int buf = (gametic / ticdup) % BACKUPTICS;

View file

@ -35,6 +35,8 @@
#include "mapinfo.h"
#include "raze_music.h"
#include "filesystem.h"
#include "printf.h"
MapRecord mapList[512]; // Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts.
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
@ -127,6 +129,13 @@ MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
return &map;
}
}
if (!fileSystem.FileExists(boardfilename))
{
Printf(TEXTCOLOR_RED "map: file \"%s\" not found.\n", boardfilename);
return nullptr;
}
auto map = AllocateMap();
map->name = "";
map->SetFileName(boardfilename);

View file

@ -61,6 +61,7 @@ struct MapRecord
}
void SetFileName(const char* n)
{
if (*n == '/' || *n == '\\') n++;
fileName = n;
FixPathSeperator(fileName);
labelName = ExtractFileBase(n);

View file

@ -434,12 +434,10 @@ bool M_SetMenu(FName menu, int param, FName caller)
switch (caller.GetIndex())
{
case NAME_Episodemenu:
case NAME_HuntMenu:
case NAME_TargetMenu:
// sent from the episode menu
NewGameStartupInfo.Episode = param;
NewGameStartupInfo.Level = 0;
NewGameStartupInfo.CustomLevel1 = NewGameStartupInfo.CustomLevel2 = -1;
NewGameStartupInfo.Skill = gDefaultSkill;
break;
@ -447,26 +445,6 @@ bool M_SetMenu(FName menu, int param, FName caller)
NewGameStartupInfo.Skill = param;
break;
case NAME_CustomGameMenu:
NewGameStartupInfo.CustomLevel1 = param;
NewGameStartupInfo.CustomLevel2 = -1;
NewGameStartupInfo.Episode = 0; // Set start to E1L1 so that even if the script fails to set the starting level it is set to something valid.
NewGameStartupInfo.Level = 0;
NewGameStartupInfo.Skill = gDefaultSkill;
gi->CustomMenuSelection(param, -1);
break;
case NAME_CustomSubMenu1:
case NAME_CustomSubMenu2:
case NAME_CustomSubMenu3:
case NAME_CustomSubMenu4:
case NAME_CustomSubMenu5:
case NAME_CustomSubMenu6:
case NAME_CustomSubMenu7:
NewGameStartupInfo.CustomLevel2 = param;
gi->CustomMenuSelection(NewGameStartupInfo.CustomLevel1, param);
break;
case NAME_Skillmenu:
NewGameStartupInfo.Skill = param;
break;

View file

@ -53,6 +53,7 @@
#include "serializer.h"
#include "findfile.h"
#include "inputstate.h"
#include "gamestate.h"
FSavegameManager savegameManager;
@ -60,8 +61,7 @@ FSavegameManager savegameManager;
void FSavegameManager::LoadGame(FSaveGameNode* node)
{
inputState.ClearAllInput();
if (gi->CleanupForLoad())
{
gi->FreeLevelData();
if (OpenSaveGameForRead(node->Filename))
{
if (gi->LoadGame(node))
@ -70,7 +70,6 @@ void FSavegameManager::LoadGame(FSaveGameNode* node)
}
}
}
}
void FSavegameManager::SaveGame(FSaveGameNode* node, bool ok4q, bool forceq)
{
@ -576,6 +575,7 @@ static int nextquicksave = -1;
void M_Autosave()
{
if (disableautosave) return;
if (!gi->CanSave()) return;
FString description;
FString file;
@ -602,8 +602,7 @@ void M_Autosave()
CCMD(autosave)
{
if (disableautosave) return;
M_Autosave();
gameaction = ga_autosave;
}
CCMD(rotatingquicksave)

View file

@ -267,7 +267,8 @@ void GameInterface::Startup()
}
else
{
DoTitle([](bool) { startmainmenu(); });
if (!userConfig.nologo) DoTitle([](bool) { startmainmenu(); });
else startmainmenu();
}
}

View file

@ -968,7 +968,7 @@ public:
//
//---------------------------------------------------------------------------
void dobonus_d(bool bonusonly, const CompletionFunc& completion)
void dobonus_d(int bonusonly, const CompletionFunc& completion)
{
JobDesc jobs[20];
int job = 0;
@ -976,7 +976,7 @@ void dobonus_d(bool bonusonly, const CompletionFunc& completion)
FX_StopAllSounds();
Mus_Stop();
if (!bonusonly && numplayers < 2 && ud.eog && ud.from_bonus == 0)
if (bonusonly < 0 && numplayers < 2 && ud.from_bonus == 0)
{
bonussequence_d(volfromlevelnum(currentLevel->levelNumber), jobs, job);
}
@ -985,7 +985,7 @@ void dobonus_d(bool bonusonly, const CompletionFunc& completion)
{
jobs[job++] = { Create<DDukeMultiplayerBonusScreen>(playerswhenstarted) };
}
else if (!bonusonly && ud.multimode <= 1)
else if (bonusonly <= 0 && ud.multimode <= 1)
{
jobs[job++] = { Create<DDukeLevelSummaryScreen>() };
}
@ -1081,68 +1081,4 @@ void PrintLevelName_d(double alpha)
BigText(160, 114, currentLevel->DisplayName(), alpha);
}
// Utility for testing the above screens
CCMD(testscreen)
{
JobDesc jobs[10];
int job = 0;
C_HideConsole();
FX_StopAllSounds();
Mus_Stop();
auto gs = gamestate;
auto completion = [=](bool)
{
if (gs == GS_LEVEL || gs == GS_MENUSCREEN) gamestate = gs;
else gamestate = GS_STARTUP;
};
if (argv.argc() > 1)
{
int screen = strtol(argv[1], nullptr, 0);
switch (screen)
{
case 0:
case 1:
case 2:
case 3:
case 4:
bonussequence_d(screen, jobs, job);
RunScreenJob(jobs, job, completion);
break;
case 5:
e4intro(nullptr);
break;
case 6:
jobs[job++] = { Create<DDukeMultiplayerBonusScreen>(6) };
RunScreenJob(jobs, job, completion);
break;
case 7:
showtwoscreens(nullptr);
break;
case 8:
doorders(nullptr);
break;
case 9:
jobs[job++] = { Create<DDukeLevelSummaryScreen>() };
RunScreenJob(jobs, job, completion);
break;
case 10:
ud.eog = true;
jobs[job++] = { Create<DDukeLevelSummaryScreen>() };
RunScreenJob(jobs, job, completion);
ud.eog = false;
break;
}
}
}
END_DUKE_NS

View file

@ -556,7 +556,7 @@ public:
//
//---------------------------------------------------------------------------
void dobonus_r(bool bonusonly, const CompletionFunc& completion)
void dobonus_r(int bonusonly, const CompletionFunc& completion)
{
JobDesc jobs[20];
int job = 0;
@ -564,7 +564,7 @@ void dobonus_r(bool bonusonly, const CompletionFunc& completion)
FX_StopAllSounds();
Mus_Stop();
if (!bonusonly && !isRRRA() && numplayers < 2 && ud.eog && ud.from_bonus == 0)
if (bonusonly < 0 && !isRRRA() && numplayers < 2 && ud.from_bonus == 0)
{
int vol = volfromlevelnum(currentLevel->levelNumber);
bonussequence_r(vol, jobs, job);
@ -574,7 +574,7 @@ void dobonus_r(bool bonusonly, const CompletionFunc& completion)
{
jobs[job++] = { Create<DRRMultiplayerBonusScreen>(playerswhenstarted) };
}
else if (!bonusonly && ud.multimode <= 1)
else if (bonusonly <= 0 && ud.multimode <= 1)
{
if (isRRRA() && !(currentLevel->flags & MI_USERMAP) && currentLevel->levelNumber < 106) // fixme: The logic here is awful. Shift more control to the map records.
{
@ -584,7 +584,7 @@ void dobonus_r(bool bonusonly, const CompletionFunc& completion)
mysnprintf(fn, 20, "lvl%d.anm", levnum + 1);
static const int framespeed[] = { 20, 20, 7200 }; // wait for one minute on the final frame so that the video doesn't stop before the user notices.
jobs[job++] = { PlayVideo(fn, nullptr, framespeed) };
if (ud.eog && currentLevel->levelNumber > 100)
if (bonusonly < 0 && currentLevel->levelNumber > 100)
{
jobs[job++] = { Create<DRRRAEndOfGame>() };
}

View file

@ -2133,9 +2133,7 @@ static void rrra_specialstats()
ps[screenpeek].MamaEnd--;
if (ps[screenpeek].MamaEnd == 0)
{
ps[screenpeek].gm = MODE_EOL;
ud.eog = 1;
ud.nextLevel = nullptr;
CompleteLevel(nullptr);
}
}

View file

@ -37,90 +37,6 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
BEGIN_DUKE_NS
static void dowarp(MapRecord *map)
{
ud.m_monsters_off = ud.monsters_off = 0;
ud.m_respawn_items = 0;
ud.m_respawn_inventory = 0;
ud.multimode = 1;
if (ps[myconnectindex].gm & MODE_GAME)
{
donewgame(map, ud.m_player_skill);
ps[myconnectindex].gm = MODE_RESTART;
}
else startnewgame(map, ud.m_player_skill);
}
static int ccmd_levelwarp(CCmdFuncPtr parm)
{
if (parm->numparms != 2)
return CCMD_SHOWHELP;
int e = atoi(parm->parms[0]);
int m = atoi(parm->parms[1]);
if (e == 0 || m == 0)
{
Printf(TEXTCOLOR_RED "Invalid level!: E%sL%s\n", parm->parms[0], parm->parms[1]);
return CCMD_OK;
}
auto map = FindMapByLevelNum(levelnum(e - 1, m - 1));
if (!map)
{
Printf(TEXTCOLOR_RED "Level not found!: E%sL%s\n", parm->parms[0], parm->parms[1]);
return CCMD_OK;
}
dowarp(map);
return CCMD_OK;
}
static int ccmd_map(CCmdFuncPtr parm)
{
if (parm->numparms != 1)
{
return CCMD_SHOWHELP;
}
FString mapname = parm->parms[0];
FString mapfilename = mapname;
DefaultExtension(mapfilename, ".map");
if (!fileSystem.FileExists(mapfilename))
{
Printf(TEXTCOLOR_RED "map: file \"%s\" not found.\n", mapname.GetChars());
return CCMD_OK;
}
// Check if the map is already defined.
auto map = FindMapByName(mapname);
if (map == nullptr)
{
// got a user map
if (VOLUMEONE)
{
Printf(TEXTCOLOR_RED "Cannot use user maps in shareware.\n");
return CCMD_OK;
}
if (mapfilename[0] != '/') mapfilename.Insert(0, "/");
map = SetupUserMap(mapfilename, !isRR() ? "dethtoll.mid" : nullptr);
}
if (numplayers > 1)
{
return CCMD_OK;
}
dowarp(map);
return CCMD_OK;
}
static int ccmd_restartmap(CCmdFuncPtr)
{
if (ps[myconnectindex].gm & MODE_GAME && ud.multimode == 1)
ps[myconnectindex].gm = MODE_RESTART;
return CCMD_OK;
}
int getlabelvalue(const char* text);
static int ccmd_spawn(CCmdFuncPtr parm)
@ -131,10 +47,12 @@ static int ccmd_spawn(CCmdFuncPtr parm)
int ang = 0;
int set = 0, idx;
if (numplayers > 1 || !(ps[myconnectindex].gm & MODE_GAME)) {
#if 0 // fixme - route through the network and this limitation becomes irrelevant
if (netgame || numplayers > 1 || !(ps[myconnectindex].gm & MODE_GAME)) {
Printf("spawn: Can't spawn sprites in multiplayer games or demos\n");
return CCMD_OK;
}
#endif
switch (parm->numparms) {
case 7: // x,y,z
@ -252,9 +170,6 @@ static int osdcmd_show_weapon(CCmdFuncPtr parm)
int registerosdcommands(void)
{
C_RegisterFunction("map","map <mapname>: warp to the given map, identified by its name", ccmd_map);
C_RegisterFunction("levelwarp","levelwarp <e> <m>: warp to episode 'e' and map 'm'", ccmd_levelwarp);
C_RegisterFunction("restartmap", "restartmap: restarts the current map", ccmd_restartmap);
C_RegisterFunction("spawn","spawn <picnum> [palnum] [cstat] [ang] [x y z]: spawns a sprite with the given properties",ccmd_spawn);
C_RegisterFunction("warptocoords","warptocoords [x] [y] [z] [ang] (optional) [horiz] (optional): warps the player to the specified coordinates",osdcmd_warptocoords);
C_RegisterFunction("third_person_view", "Switch to third person view", osdcmd_third_person_view);

View file

@ -295,21 +295,16 @@ static bool cheatLevel(cheatseq_t *s)
auto map = FindMapByLevelNum(levelnum(volnume, levnume));
if (map)
{
ud.nextLevel = map;
FX_StopAllSounds();
FX_SetReverb(0);
ps[myconnectindex].gm |= MODE_RESTART;
ChangeLevel(map, -1);
}
return true;
}
static bool cheatSkill(cheatseq_t *s)
{
lastlevel = 0;
ud.m_player_skill = ud.player_skill = s->Args[0] - '1';
ps[myconnectindex].gm |= MODE_RESTART;
FX_StopAllSounds();
FX_SetReverb(0);
ChangeLevel(currentLevel, s->Args[0] - '1');
//FX_StopAllSounds();
//FX_SetReverb(0);
return true;
}

View file

@ -416,10 +416,8 @@ enum EFlamethrowerState
};
enum gamemode_t {
MODE_DEMO = 0x00000002,
MODE_GAME = 0x00000004,
MODE_EOL = 0x00000008,
MODE_RESTART = 0x00000020,
};

View file

@ -273,7 +273,6 @@ void GameInterface::StartGame(FNewGameStartup& gs)
static const short sounds_r[] = { 427, 428, 196, 195, 197 };
if (gs.Skill >=0 && gs.Skill <= 5) skillsound = isRR()? sounds_r[gs.Skill] : sounds_d[gs.Skill];
ud.m_player_skill = gs.Skill + 1;
if (menu_sounds && skillsound >= 0 && SoundEnabled() && !netgame)
{
S_PlaySound(skillsound, CHAN_AUTO, CHANF_UI);
@ -286,16 +285,10 @@ void GameInterface::StartGame(FNewGameStartup& gs)
}
Net_ClearFifo();
}
ud.m_respawn_monsters = (gs.Skill == 3);
ud.m_monsters_off = ud.monsters_off = 0;
ud.m_respawn_items = 0;
ud.m_respawn_inventory = 0;
ud.multimode = 1;
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
if (map)
{
startnewgame(map, ud.m_player_skill);
DeferedStartGame(map, gs.Skill);
}
}

View file

@ -58,7 +58,10 @@ struct GameInterface : public ::GameInterface
void Render() override;
void Ticker() override;
const char* GenericCheat(int player, int cheat) override;
const char* CheckCheatMode();
const char* CheckCheatMode() override;
void NextLevel(MapRecord* map, int skill) override;
void NewGame(MapRecord* map, int skill) override;
void LevelCompleted(MapRecord* map, int skill) override;
};

View file

@ -208,8 +208,8 @@ void drawstatusbar_r(int snum);
void drawoverheadmap(int cposx, int cposy, int czoom, int cang);
void cameratext(int i);
void dobonus(int bonusonly, const CompletionFunc& completion);
void dobonus_d(bool bonusonly, const CompletionFunc& completion);
void dobonus_r(bool bonusonly, const CompletionFunc& completion);
void dobonus_d(int bonusonly, const CompletionFunc& completion);
void dobonus_r(int bonusonly, const CompletionFunc& completion);
void drawoverlays(double smoothratio);
void drawbackground(void);
@ -221,7 +221,7 @@ bool setnextmap(bool checksecretexit);
void prelevel_d(int g);
void prelevel_r(int g);
void e4intro(const CompletionFunc& completion);
void exitlevel();
void exitlevel(MapRecord *next);
int enterlevel(MapRecord* mi, int gm);
void donewgame(MapRecord* map, int sk);
void startnewgame(MapRecord* map, int skill);

View file

@ -41,6 +41,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
#include "prediction.h"
#include "sbar.h"
#include "glbackend/glbackend.h"
#include "gamestate.h"
BEGIN_DUKE_NS
@ -84,7 +85,7 @@ template<class func>
void runbonus(func completion)
{
// MP scoreboard
if (playerswhenstarted > 1 && ps[myconnectindex].gm & MODE_GAME && !ud.coop)
if (playerswhenstarted > 1 && !ud.coop)
{
dobonus(1, completion);
}
@ -127,7 +128,7 @@ void GameInterface::ExitFromMenu()
void FTA(int q, struct player_struct* p)
{
if (q < 0 || !(p->gm & MODE_GAME))
if (q < 0 || gamestate != GS_LEVEL)
return;
if (p->ftq != q || (ud.levelclock - p->ftt > TICRATE && q != QUOTE_DEAD))

View file

@ -36,6 +36,7 @@ source as it is released.
#include "duke3d.h"
#include "gamevar.h"
#include "mapinfo.h"
#include "gamestate.h"
BEGIN_DUKE_NS
@ -692,7 +693,7 @@ int ParseState::parse(void)
insptr++;
ps[g_p].timebeforeexit = *insptr;
ps[g_p].customexitsound = -1;
ud.eog = 1;
ChangeLevel(nullptr, -1);
insptr++;
break;
@ -975,18 +976,9 @@ int ParseState::parse(void)
case concmd_resetplayer:
insptr++;
//AddLog("resetplayer");
if(ud.multimode < 2)
{
#if 0
if( lastsavedpos >= 0 && ud.recstat != 2 )
{
KB_ClearKeyDown(sc_Space);
cmenu(15000);
}
else
#endif
ps[g_p].gm = MODE_RESTART;
gameaction = ga_autoloadgame;
killit_flag = 2;
}
else
@ -1737,8 +1729,7 @@ CCMD(endofgame)
{
ps[0].timebeforeexit = 120;
ps[0].customexitsound = -1;
ud.eog = 1;
ChangeLevel(nullptr, -1);
}
END_DUKE_NS

View file

@ -103,11 +103,6 @@ void GameInterface::Ticker()
}
else r_NoInterpolate = true;
if (ps[myconnectindex].gm & (MODE_EOL | MODE_RESTART))
{
exitlevel();
}
}
//---------------------------------------------------------------------------
@ -138,7 +133,8 @@ void GameInterface::Startup()
}
else
{
fi.ShowLogo([](bool) { startmainmenu(); });
if (!userConfig.nologo) fi.ShowLogo([](bool) { startmainmenu(); });
else startmainmenu();
}
}
@ -160,6 +156,44 @@ void GameInterface::Render()
drawtime.Unclock();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::NextLevel(MapRecord* map, int skill)
{
ud.m_player_skill = skill + 1;
int res = enterlevel(map, 0);
if (!res) gameaction = ga_startup;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::NewGame(MapRecord* map, int skill)
{
// Hmm... What about the other players?
ps[0].last_extra = max_player_health;
resetweapons(0);
resetinventory(0);
NextLevel(map, skill);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::LevelCompleted(MapRecord* map, int skill)
{
exitlevel(map);
}
END_DUKE_NS

View file

@ -420,7 +420,6 @@ void resetprestat(int snum,int g)
randomseed = 17L;
paused = 0;
ud.camerasprite =-1;
ud.eog = 0;
tempwallptr = 0;
camsprite =-1;
earthquaketime = 0;
@ -435,12 +434,7 @@ void resetprestat(int snum,int g)
numinterpolations = 0;
//startofdynamicinterpolations = 0;
if( ( (g&MODE_EOL) != MODE_EOL && numplayers < 2) || (ud.coop != 1 && numplayers > 1) )
{
resetweapons(snum);
resetinventory(snum);
}
else if(p->curr_weapon == HANDREMOTE_WEAPON)
if(p->curr_weapon == HANDREMOTE_WEAPON)
{
p->ammo_amount[HANDBOMB_WEAPON]++;
p->curr_weapon = HANDBOMB_WEAPON;
@ -581,7 +575,7 @@ void resetpspritevars(int g)
which_palookup = 9;
j = connecthead;
i = headspritestat[10]; // 10 == players...
i = headspritestat[STAT_PLAYER];
while (i >= 0)
{
nexti = nextspritestat[i];
@ -613,7 +607,7 @@ void resetpspritevars(int g)
s->xoffset = 0;
s->clipdist = 64;
if ((g & MODE_EOL) != MODE_EOL || ps[j].last_extra == 0)
if (ps[j].last_extra == 0)
{
ps[j].last_extra = max_player_health;
s->extra = max_player_health;
@ -774,7 +768,7 @@ void donewgame(MapRecord* map, int sk)
auto p = &ps[0];
show_shareware = 26 * 34;
ud.nextLevel = map;
//ud.nextLevel = map;
ud.player_skill = sk;
ud.secretlevel = 0;
ud.from_bonus = 0;
@ -782,7 +776,6 @@ void donewgame(MapRecord* map, int sk)
ud.last_level = -1;
p->zoom = 768;
p->gm = 0;
M_ClearMenus();
ResetGameVars();
@ -950,9 +943,6 @@ int enterlevel(MapRecord *mi, int gamemode)
ud.coop = ud.m_coop;
ud.ffire = ud.m_ffire;
if ((gamemode & MODE_DEMO) == 0 && ud.recstat == 2)
ud.recstat = 0;
OnEvent(EVENT_ENTERLEVEL);
// Stop all sounds
@ -970,7 +960,7 @@ int enterlevel(MapRecord *mi, int gamemode)
if (res != 0) return res;
// Try this first so that it can disable the CD player if no tracks are found.
if (isRR() && !(gamemode & MODE_DEMO))
if (isRR())
S_PlayRRMusic();
if (ud.recstat != 2)
@ -978,17 +968,6 @@ int enterlevel(MapRecord *mi, int gamemode)
S_PlayLevelMusic(mi);
}
if (gamemode & (MODE_GAME|MODE_EOL))
{
ps[myconnectindex].gm = MODE_GAME;
}
else if (gamemode & MODE_RESTART)
{
if (ud.recstat == 2)
ps[myconnectindex].gm = MODE_DEMO;
else ps[myconnectindex].gm = MODE_GAME;
}
if (VOLUMEONE && mi->levelNumber == 0 && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
for (int i = connecthead; i >= 0; i = connectpoint2[i])
@ -1027,11 +1006,17 @@ int enterlevel(MapRecord *mi, int gamemode)
void startnewgame(MapRecord* map, int skill)
{
ud.m_player_skill = skill;
ud.m_respawn_monsters = (skill == 4);
ud.m_monsters_off = ud.monsters_off = 0;
ud.m_respawn_items = 0;
ud.m_respawn_inventory = 0;
ud.multimode = 1;
newgame(map, skill, [=](bool)
{
if (enterlevel(map, MODE_GAME))
if (enterlevel(map, 0))
{
ps[myconnectindex].gm = 0;
startmainmenu();
}
else
@ -1077,16 +1062,11 @@ bool setnextmap(bool checksecretexit)
map = FindNextMap(currentLevel);
}
for (int i = connecthead; i >= 0; i = connectpoint2[i])
ps[i].gm = MODE_EOL;
if (map)
{
ud.from_bonus = from_bonus;
ud.nextLevel = map;
return true;
}
ud.eog = true;
CompleteLevel(map);
return false;
}
@ -1096,31 +1076,13 @@ bool setnextmap(bool checksecretexit)
//
//---------------------------------------------------------------------------
int exitlevelend()
void exitlevel(MapRecord *nextlevel)
{
if (numplayers > 1)
ps[myconnectindex].gm = MODE_GAME;
int res = enterlevel(ud.nextLevel, ps[myconnectindex].gm);
ud.nextLevel = nullptr;
return res;
}
void exitlevel(void)
{
bool endofgame = ud.eog || (currentLevel->flags & MI_FORCEEOG) || ud.nextLevel == nullptr;
bool endofgame = nextlevel == nullptr;
STAT_Update(endofgame);
setpal(&ps[myconnectindex]);
if (ps[myconnectindex].gm & MODE_RESTART)
{
// If no level was set, restart the current one.
if (!ud.nextLevel) ud.nextLevel = currentLevel;
}
if (ps[myconnectindex].gm & MODE_EOL)
{
dobonus(0, [=](bool)
dobonus(endofgame? -1 : 0, [=](bool)
{
// Clear potentially loaded per-map ART only after the bonus screens.
@ -1128,30 +1090,29 @@ void exitlevel(void)
gamestate = GS_LEVEL;
if (endofgame)
{
ud.eog = 0;
if (ud.multimode < 2)
{
ps[myconnectindex].gm = 0;
if (!VOLUMEALL)
doorders([](bool) { gamestate = GS_STARTUP; });
else gamestate = GS_STARTUP;
doorders([](bool) { gameaction = ga_startup; });
else gameaction = ga_startup;
return;
}
else
{
ud.nextLevel = FindMapByLevelNum(0);
if (!ud.nextLevel || exitlevelend())
gamestate = GS_STARTUP;
auto nextlevel = FindMapByLevelNum(0);
if (!nextlevel)
{
gameaction = ga_startup;
return;
}
else gameaction = ga_nextlevel;
}
}
else if (exitlevelend())
gamestate = GS_STARTUP;
else
gameaction = ga_nextlevel;
});
}
else if (exitlevelend())
gamestate = GS_STARTUP;
}
END_DUKE_NS

View file

@ -207,7 +207,6 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, player_struct& w,
("last_full_weapon", w.last_full_weapon)
("footprintshade", w.footprintshade)
("boot_amount", w.boot_amount)
("gm", w.gm)
("on_warping_sector", w.on_warping_sector)
("footprintcount", w.footprintcount)
("hbomb_on", w.hbomb_on)
@ -400,7 +399,6 @@ void GameInterface::SerializeGameState(FSerializer& arc)
//("auto_run", ud.auto_run)
("monsters_off", ud.monsters_off)
("last_level", ud.last_level)
("eog", ud.eog)
("coop", ud.coop)
("marker", ud.marker)
("ffire", ud.ffire)
@ -488,7 +486,6 @@ void GameInterface::SerializeGameState(FSerializer& arc)
if (arc.isReading())
{
screenpeek = myconnectindex;
ps[myconnectindex].gm = MODE_GAME;
gamestate = GS_LEVEL;
ud.recstat = 0;

View file

@ -42,6 +42,7 @@ source as it is released.
#include "raze_music.h"
#include "mapinfo.h"
#include "raze_sound.h"
#include "gamestate.h"
BEGIN_DUKE_NS
@ -324,8 +325,7 @@ void GameInterface::UpdateSounds(void)
vec3_t* c;
int32_t ca, cs;
auto& gm = ps[myconnectindex].gm;
if (isRR() && !Mus_IsPlaying() && (gm && gm & MODE_GAME))
if (isRR() && !Mus_IsPlaying() && !paused && gamestate == GS_LEVEL)
S_PlayRRMusic();
S_GetCamera(&c, &ca, &cs);

View file

@ -52,7 +52,7 @@ struct TileInfo
struct user_defs
{
int levelclock;
unsigned char god, cashman, eog;
unsigned char god, cashman;
unsigned char show_help, scrollmode, clipping;
char user_name[MAXPLAYERS][32];
unsigned char showweapons;
@ -76,7 +76,7 @@ struct user_defs
int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off, detail;
int m_ffire, ffire, m_player_skill, multimode;
int player_skill, marker;
MapRecord* nextLevel;
//MapRecord* nextLevel;
};
@ -156,7 +156,6 @@ struct player_struct
short holoduke_on, pycount, frag_ps;
short transporter_hold, last_full_weapon, footprintshade, boot_amount;
unsigned char gm;
unsigned char on_warping_sector, footprintcount;
unsigned char hbomb_on, jumping_toggle, rapid_fire_hold, on_ground;
char name[32];

View file

@ -205,16 +205,14 @@ void GameInterface::StartGame(FNewGameStartup& gs)
ready2send = 0;
MapRecord* map;
if (gs.Episode >= 1)
NextLevel = FindMapByLevelNum(5);
map = FindMapByLevelNum(5);
else
NextLevel = FindMapByLevelNum(1);
map = FindMapByLevelNum(1);
if (!NextLevel) return;
ExitLevel = TRUE;
NewGame = TRUE;
if (!map) return;
CameraTestMode = FALSE;
Skill = gs.Skill;
StopFX();
//InitNewGame();
@ -238,6 +236,7 @@ void GameInterface::StartGame(FNewGameStartup& gs)
}
Net_ClearFifo();
}
DeferedStartGame(map, gs.Skill);
}
FSavegameInfo GameInterface::GetSaveSig()

View file

@ -280,13 +280,6 @@ void GameInterface::DrawBackground(void)
MNU_DrawString(160, 170, "Lo Wang is waiting for other players...", 1, 16, 0);
MNU_DrawString(160, 180, "They are afraid!", 1, 16, 0);
}
// hack alert. This needs to go away.
if (SavegameLoaded || NextLevel)
{
TerminateLevel();
ExitLevel = false;
gamestate = GS_LEVEL;
}
}
//---------------------------------------------------------------------------
@ -663,7 +656,7 @@ void GameInterface::Ticker(void)
StartAmbientSound();
ExitLevel = false;
}
else if (NextLevel)
else if (ShadowWarrior::NextLevel)
{
InitLevel();
InitRunLevel();
@ -731,7 +724,7 @@ void GameInterface::ErrorCleanup()
{
// Make sure we do not leave the game in an unstable state
TerminateLevel();
NextLevel = nullptr;
ShadowWarrior::NextLevel = nullptr;
SavegameLoaded = false;
ExitLevel = false;
FinishAnim = 0;
@ -847,4 +840,10 @@ void GameInterface::FreeGameData()
TerminateLevel();
}
void GameInterface::FreeLevelData()
{
TerminateLevel();
::GameInterface::FreeLevelData();
}
END_SW_NS

View file

@ -2203,6 +2203,7 @@ struct GameInterface : ::GameInterface
const char* Name() override { return "ShadowWarrior"; }
void app_init() override;
void FreeGameData() override;
void FreeLevelData() override;
bool GenerateSavePic() override;
void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) override;
void MenuOpened() override;
@ -2212,7 +2213,6 @@ struct GameInterface : ::GameInterface
void StartGame(FNewGameStartup& gs) override;
FSavegameInfo GetSaveSig() override;
void DrawMenuCaption(const DVector2& origin, const char* text) override;
bool CleanupForLoad() override;
bool LoadGame(FSaveGameNode* sv) override;
bool SaveGame(FSaveGameNode* sv) override;
void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); }

View file

@ -650,13 +650,6 @@ bool GameInterface::SaveGame(FSaveGameNode *sv)
extern SWBOOL SavegameLoaded;
bool GameInterface::CleanupForLoad()
{
TerminateLevel();
StopFX();
return true;
}
bool GameInterface::LoadGame(FSaveGameNode* sv)
{
MFILE_READ fil;
@ -1089,7 +1082,7 @@ bool GameInterface::LoadGame(FSaveGameNode* sv)
}
// this is not a new game
NewGame = FALSE;
ShadowWarrior::NewGame = FALSE;
DoPlayerDivePalette(Player+myconnectindex);