raze/source/exhumed/src/gameloop.cpp
Christoph Oelckers aabbbcb2ff - reset the network timer after lengthy operations.
This includes loading a level and busy-waiting for a sound to play.
Also block these loops and the sounds they wait for in network games to avoid problems from longer delays here.
The problem seems to be directly inherited from ZDoom which shows the same issue with screen wipes.

Fixes #297
2020-09-02 10:00:07 +02:00

308 lines
7.9 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "compat.h"
#include "common.h"
#include "engine.h"
#include "exhumed.h"
#include "sequence.h"
#include "names.h"
#include "player.h"
#include "ps_input.h"
#include "sound.h"
#include "view.h"
#include "status.h"
#include "version.h"
#include "aistuff.h"
#include "mapinfo.h"
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include <assert.h>
#include "gamecvars.h"
#include "savegamehelp.h"
#include "c_dispatch.h"
#include "raze_sound.h"
#include "gamestate.h"
#include "screenjob.h"
#include "c_console.h"
#include "cheathandler.h"
#include "statistics.h"
#include "g_input.h"
#include "core/menu/menu.h"
#include "d_net.h"
BEGIN_PS_NS
short nBestLevel;
static int32_t nonsharedtimer;
int GameAction=-1;
extern uint8_t nCinemaSeen;
void RunCinemaScene(int num);
void GameMove(void);
void DrawClock();
double calc_smoothratio();
void DoTitle(CompletionFunc completion);
static int FinishLevel(TArray<JobDesc> &jobs)
{
int lnum = currentLevel->levelNumber;
if (lnum > nBestLevel) {
nBestLevel = lnum - 1;
}
StopAllSounds();
bCamera = false;
automapMode = am_off;
STAT_Update(lnum == kMap20);
if (lnum != kMap20)
{
if (EndLevel != 2 && !netgame)
{
// There's really no choice but to enter an active wait loop here to make the sound play out.
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
int nTicks = I_msTime() + 100;
while (nTicks > I_msTime())
{
I_GetEvent();
soundEngine->UpdateSounds(I_GetTime());
}
Net_ClearFifo();
}
}
else nPlayerLives[0] = 0;
DoAfterCinemaScene(lnum, jobs);
return lnum == kMap20? -1 : lnum + 1;
}
static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray<JobDesc> &jobs)
{
if (nLevelNew == 5 && !(nCinemaSeen & 1)) {
nCinemaSeen |= 1;
DoBeforeCinemaScene(5, jobs);
}
menu_DrawTheMap(nLevel, nLevelNew, nLevelBest, jobs);
if (nLevelNew == 11 && !(nCinemaSeen & 2)) {
DoBeforeCinemaScene(11, jobs);
}
}
static void GameDisplay(void)
{
drawtime.Reset();
drawtime.Clock();
if (currentLevel && currentLevel->levelNumber == kMap20)
{
DoEnergyTile();
DrawClock();
}
DrawView(calc_smoothratio());
DrawStatusBar();
if (paused && !M_Active())
{
auto tex = GStrings("TXTB_PAUSED");
int nStringWidth = SmallFont->StringWidth(tex);
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 100, tex, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
}
drawtime.Unclock();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void drawmenubackground()
{
auto nLogoTile = EXHUMED ? kExhumedLogo : kPowerslaveLogo;
int dword_9AB5F = (I_GetBuildTime() / 16) & 3;
twod->ClearScreen();
DrawRel(kSkullHead, 160, 100, 32);
DrawRel(kSkullJaw, 161, 130, 32);
DrawRel(nLogoTile, 160, 40, 32);
// draw the fire urn/lamp thingies
DrawRel(kTile3512 + dword_9AB5F, 50, 150, 32);
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 32);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void CheckProgression()
{
TArray<JobDesc> jobs;
bool startlevel = false;
int mylevelnew = -1;
if (GameAction >= 0)
{
if (GameAction < 1000)
{
// start a new game on the given level
currentLevel = nullptr;
mylevelnew = GameAction;
currentLevel = FindMapByLevelNum(mylevelnew);
GameAction = -1;
InitNewGame();
if (mylevelnew > 0) STAT_StartNewGame("Exhumed", 1);
if (mylevelnew != 0) nBestLevel = mylevelnew - 1;
}
else
{
// A savegame was loaded. Just start the level without any further actions.
GameAction = -1;
gamestate = GS_LEVEL;
return;
}
}
else if (EndLevel)
{
if (currentLevel->levelNumber == 0) startmainmenu();
else mylevelnew = FinishLevel(jobs);
EndLevel = false;
}
if (mylevelnew > -1 && mylevelnew < kMap20)
{
startlevel = true;
// start a new game at the given level
if (!nNetPlayerCount && mylevelnew > 0)
{
showmap(currentLevel? currentLevel->levelNumber : -1, mylevelnew, nBestLevel, jobs);
}
else
jobs.Push({ Create<DScreenJob>() }); // we need something in here even in the multiplayer case.
}
if (jobs.Size() > 0)
{
selectedlevelnew = mylevelnew;
RunScreenJob(jobs.Data(), jobs.Size(), [=](bool)
{
if (!startlevel) gamestate = GS_STARTUP;
else
{
gamestate = GS_LEVEL;
InitLevel(selectedlevelnew);
#if 0
// this would be the place to autosave upon level start
if (!bInDemo && selectedlevelnew > nBestLevel && selectedlevelnew != 0 && selectedlevelnew <= kMap20) {
menu_GameSave(SavePosition);
}
#endif
if (selectedlevelnew > nBestLevel)
{
nBestLevel = selectedlevelnew;
}
if (selectedlevelnew == 11) nCinemaSeen |= 2;
if (mylevelnew != selectedlevelnew) STAT_Cancel();
else STAT_NewLevel(currentLevel->labelName);
}
});
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameLoop()
{
GameTicker();
PlayerInterruptKeys(true);
gi->UpdateSounds();
CheckKeys2();
}
void GameInterface::RunGameFrame()
{
again:
CheckProgression();
switch (gamestate)
{
default:
case GS_STARTUP:
resettiming();
GameAction = -1;
EndLevel = false;
if (userConfig.CommandMap.IsNotEmpty())
{
auto map = FindMapByName(userConfig.CommandMap);
if (map) GameAction = map->levelNumber;
userConfig.CommandMap = "";
goto again;
}
else
{
DoTitle([](bool) { startmainmenu(); });
}
break;
case GS_MENUSCREEN:
case GS_FULLCONSOLE:
drawmenubackground();
break;
case GS_LEVEL:
GameLoop();
GameDisplay();
break;
case GS_INTERMISSION:
case GS_INTRO:
RunScreenJobFrame(); // This handles continuation through its completion callback.
break;
}
}
void GameInterface::ErrorCleanup()
{
// Clear all progression sensitive variables here.
GameAction = -1;
EndLevel = false;
}
END_PS_NS