2020-08-29 22:55:49 +00:00
|
|
|
/*
|
|
|
|
** mainloop.cpp
|
|
|
|
** Implements the main game loop
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 2020 Christoph Oelckers
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2020-09-13 17:38:31 +00:00
|
|
|
// For TryRunTics the following applies:
|
2020-08-29 22:55:49 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// $Id:$
|
|
|
|
//
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
|
|
// Copyright 1999-2016 Randy Heit
|
|
|
|
// Copyright 2002-2020 Christoph Oelckers
|
|
|
|
//
|
|
|
|
// This source is available for distribution and/or modification
|
|
|
|
// only under the terms of the DOOM Source Code License as
|
|
|
|
// published by id Software. All rights reserved.
|
|
|
|
//
|
|
|
|
// The source is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
|
|
// for more details.
|
|
|
|
//
|
|
|
|
// $Log:$
|
|
|
|
//
|
|
|
|
// DESCRIPTION:
|
|
|
|
// DOOM Network game communication and protocol,
|
|
|
|
// all OS independent parts.
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "i_time.h"
|
|
|
|
#include "d_net.h"
|
|
|
|
#include "gamecontrol.h"
|
|
|
|
#include "c_console.h"
|
2020-10-04 16:31:48 +00:00
|
|
|
#include "razemenu.h"
|
2020-08-29 22:55:49 +00:00
|
|
|
#include "i_system.h"
|
|
|
|
#include "raze_sound.h"
|
|
|
|
#include "raze_music.h"
|
|
|
|
#include "vm.h"
|
2020-08-30 07:32:34 +00:00
|
|
|
#include "gamestate.h"
|
2021-05-21 23:34:00 +00:00
|
|
|
#include "screenjob_.h"
|
2020-08-30 07:32:34 +00:00
|
|
|
#include "c_console.h"
|
|
|
|
#include "uiinput.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "palette.h"
|
2020-08-30 10:02:32 +00:00
|
|
|
#include "build.h"
|
2020-08-30 20:52:20 +00:00
|
|
|
#include "g_input.h"
|
2020-09-03 21:10:28 +00:00
|
|
|
#include "mapinfo.h"
|
2020-09-08 16:28:41 +00:00
|
|
|
#include "automap.h"
|
|
|
|
#include "statusbar.h"
|
2020-10-04 16:31:48 +00:00
|
|
|
#include "gamestruct.h"
|
|
|
|
#include "savegamehelp.h"
|
2020-10-13 19:38:24 +00:00
|
|
|
#include "v_draw.h"
|
2021-04-11 06:40:18 +00:00
|
|
|
#include "gamehud.h"
|
2022-04-25 15:26:17 +00:00
|
|
|
#include "wipe.h"
|
2022-10-02 18:33:18 +00:00
|
|
|
#include "i_interface.h"
|
2022-12-07 16:10:27 +00:00
|
|
|
#include "texinfo.h"
|
2022-12-08 11:41:40 +00:00
|
|
|
#include "texturemanager.h"
|
2020-08-29 22:55:49 +00:00
|
|
|
|
2020-08-30 07:32:34 +00:00
|
|
|
CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
2020-08-29 22:55:49 +00:00
|
|
|
CVAR(Bool, r_ticstability, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
2020-09-27 09:58:54 +00:00
|
|
|
EXTERN_CVAR(Bool, cl_capfps)
|
2020-09-10 20:45:42 +00:00
|
|
|
CVAR(Bool, cl_resumesavegame, true, CVAR_ARCHIVE)
|
2020-08-29 22:55:49 +00:00
|
|
|
|
2020-08-30 07:32:34 +00:00
|
|
|
ticcmd_t playercmds[MAXPLAYERS];
|
|
|
|
|
2020-08-29 22:55:49 +00:00
|
|
|
static uint64_t stabilityticduration = 0;
|
|
|
|
static uint64_t stabilitystarttime = 0;
|
2023-02-04 21:37:37 +00:00
|
|
|
static double inputScale;
|
2020-08-29 22:55:49 +00:00
|
|
|
|
|
|
|
bool r_NoInterpolate;
|
|
|
|
int entertic;
|
|
|
|
int oldentertics;
|
|
|
|
int gametic;
|
2022-04-25 15:26:17 +00:00
|
|
|
int nextwipe = wipe_None;
|
2020-08-29 22:55:49 +00:00
|
|
|
|
2021-05-11 22:31:49 +00:00
|
|
|
FString savename;
|
2020-10-04 16:31:48 +00:00
|
|
|
FString BackupSaveGame;
|
2020-09-10 20:45:42 +00:00
|
|
|
|
|
|
|
void DoLoadGame(const char* name);
|
|
|
|
|
2021-05-11 22:21:26 +00:00
|
|
|
bool sendsave;
|
|
|
|
FString savedescription;
|
|
|
|
FString savegamefile;
|
2020-08-29 22:55:49 +00:00
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2020-08-30 07:32:34 +00:00
|
|
|
|
|
|
|
void G_BuildTiccmd(ticcmd_t* cmd)
|
|
|
|
{
|
2021-05-11 22:21:26 +00:00
|
|
|
if (sendsave)
|
|
|
|
{
|
|
|
|
sendsave = false;
|
|
|
|
Net_WriteByte(DEM_SAVEGAME);
|
|
|
|
Net_WriteString(savegamefile);
|
|
|
|
Net_WriteString(savedescription);
|
|
|
|
savegamefile = "";
|
|
|
|
}
|
2021-03-15 09:11:49 +00:00
|
|
|
cmd->ucmd = {};
|
2020-08-30 20:52:20 +00:00
|
|
|
I_GetEvent();
|
2020-09-15 19:45:54 +00:00
|
|
|
auto input = CONTROL_GetInput();
|
2022-12-02 03:45:37 +00:00
|
|
|
gi->GetInput(&input, inputScale, &cmd->ucmd);
|
2020-09-13 17:27:05 +00:00
|
|
|
cmd->consistency = consistency[myconnectindex][(maketic / ticdup) % BACKUPTICS];
|
2020-08-30 07:32:34 +00:00
|
|
|
}
|
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2020-09-08 21:00:31 +00:00
|
|
|
bool newGameStarted;
|
2020-08-30 07:32:34 +00:00
|
|
|
|
2021-04-27 23:15:15 +00:00
|
|
|
void NewGame(MapRecord* map, int skill, bool ns = false)
|
|
|
|
{
|
|
|
|
newGameStarted = true;
|
2021-04-30 14:21:37 +00:00
|
|
|
ShowIntermission(nullptr, map, nullptr, [=](bool) {
|
|
|
|
gi->NewGame(map, skill, ns);
|
2021-05-13 17:48:27 +00:00
|
|
|
ResetStatusBar();
|
2021-11-07 21:51:02 +00:00
|
|
|
Net_ClearFifo();
|
2021-04-30 14:21:37 +00:00
|
|
|
});
|
2021-04-27 23:15:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
static void GameTicker()
|
2020-08-30 07:32:34 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
handleevents();
|
|
|
|
|
|
|
|
// Todo: Migrate state changes to here instead of doing them ad-hoc
|
|
|
|
while (gameaction != ga_nothing)
|
|
|
|
{
|
2020-09-03 21:10:28 +00:00
|
|
|
auto ga = gameaction;
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
switch (ga)
|
2020-08-30 07:32:34 +00:00
|
|
|
{
|
2020-09-03 21:10:28 +00:00
|
|
|
case ga_autoloadgame:
|
2020-10-25 11:47:07 +00:00
|
|
|
C_FlushDisplay();
|
2020-09-10 20:45:42 +00:00
|
|
|
if (BackupSaveGame.IsNotEmpty() && cl_resumesavegame)
|
|
|
|
{
|
|
|
|
DoLoadGame(BackupSaveGame);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_nextmap = currentLevel;
|
|
|
|
FX_StopAllSounds();
|
|
|
|
FX_SetReverb(0);
|
|
|
|
gi->FreeLevelData();
|
|
|
|
gameaction = ga_level;
|
2021-04-27 18:04:11 +00:00
|
|
|
NewGame(g_nextmap, -1);
|
2020-09-10 20:45:42 +00:00
|
|
|
BackupSaveGame = "";
|
|
|
|
}
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_completed:
|
|
|
|
FX_StopAllSounds();
|
|
|
|
FX_SetReverb(0);
|
2021-12-06 23:12:04 +00:00
|
|
|
gi->LevelCompleted(g_nextmap, g_nextskill);
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_nextlevel:
|
|
|
|
gi->FreeLevelData();
|
2020-09-05 13:43:34 +00:00
|
|
|
gameaction = ga_level;
|
2020-09-03 22:20:32 +00:00
|
|
|
gi->NextLevel(g_nextmap, g_nextskill);
|
2021-05-13 17:48:27 +00:00
|
|
|
ResetStatusBar();
|
2021-11-07 21:51:02 +00:00
|
|
|
Net_ClearFifo();
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_newgame:
|
|
|
|
FX_StopAllSounds();
|
2021-11-14 11:30:18 +00:00
|
|
|
[[fallthrough]];
|
2020-10-10 11:30:23 +00:00
|
|
|
case ga_newgamenostopsound:
|
2021-04-07 17:39:48 +00:00
|
|
|
DeleteScreenJob();
|
2020-09-03 21:10:28 +00:00
|
|
|
FX_SetReverb(0);
|
|
|
|
gi->FreeLevelData();
|
2020-10-25 11:47:07 +00:00
|
|
|
C_FlushDisplay();
|
2020-09-05 13:43:34 +00:00
|
|
|
gameaction = ga_level;
|
2020-09-10 20:45:42 +00:00
|
|
|
BackupSaveGame = "";
|
2021-04-27 18:04:11 +00:00
|
|
|
NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_startup:
|
2020-09-05 13:43:34 +00:00
|
|
|
Mus_Stop();
|
|
|
|
FX_StopAllSounds();
|
2020-09-03 21:10:28 +00:00
|
|
|
gi->FreeLevelData();
|
|
|
|
gamestate = GS_STARTUP;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_mainmenu:
|
2020-09-05 13:43:34 +00:00
|
|
|
FX_StopAllSounds();
|
2021-04-28 18:16:13 +00:00
|
|
|
if (isBlood()) Mus_Stop();
|
2021-11-14 11:30:18 +00:00
|
|
|
[[fallthrough]];
|
2020-09-05 13:43:34 +00:00
|
|
|
case ga_mainmenunostopsound:
|
2020-09-03 21:10:28 +00:00
|
|
|
gi->FreeLevelData();
|
2020-09-05 13:43:34 +00:00
|
|
|
gamestate = GS_MENUSCREEN;
|
2020-10-10 10:57:43 +00:00
|
|
|
M_StartControlPanel(ga == ga_mainmenu);
|
2020-09-05 13:43:34 +00:00
|
|
|
M_SetMenu(NAME_Mainmenu);
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
2020-09-04 18:46:44 +00:00
|
|
|
case ga_creditsmenu:
|
2020-09-05 13:43:34 +00:00
|
|
|
FX_StopAllSounds();
|
2020-09-04 18:46:44 +00:00
|
|
|
gi->FreeLevelData();
|
2020-09-05 13:43:34 +00:00
|
|
|
gamestate = GS_MENUSCREEN;
|
|
|
|
M_StartControlPanel(false);
|
|
|
|
M_SetMenu(NAME_Mainmenu);
|
2020-09-04 18:46:44 +00:00
|
|
|
M_SetMenu(NAME_CreditsMenu);
|
|
|
|
break;
|
|
|
|
|
2020-09-03 21:10:28 +00:00
|
|
|
case ga_savegame:
|
2021-05-11 22:21:26 +00:00
|
|
|
G_DoSaveGame(true, false, savegamefile, savedescription);
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
savegamefile = "";
|
|
|
|
savedescription = "";
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
2021-05-11 22:31:49 +00:00
|
|
|
case ga_loadgame:
|
|
|
|
case ga_loadgamehidecon:
|
|
|
|
//case ga_autoloadgame:
|
|
|
|
G_DoLoadGame();
|
|
|
|
break;
|
|
|
|
|
2020-09-03 21:10:28 +00:00
|
|
|
case ga_autosave:
|
2020-09-08 21:00:31 +00:00
|
|
|
if (gamestate == GS_LEVEL && !newGameStarted) M_Autosave();
|
|
|
|
newGameStarted = false;
|
2020-09-05 13:43:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_level:
|
|
|
|
gamestate = GS_LEVEL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_intro:
|
|
|
|
gamestate = GS_INTRO;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ga_intermission:
|
2021-05-22 11:02:34 +00:00
|
|
|
gamestate = GS_CUTSCENE;
|
2020-09-03 21:10:28 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-25 08:20:26 +00:00
|
|
|
case ga_fullconsole:
|
|
|
|
C_FullConsole();
|
|
|
|
Mus_Stop();
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
break;
|
|
|
|
|
2021-04-27 23:12:07 +00:00
|
|
|
case ga_endscreenjob:
|
|
|
|
EndScreenJob();
|
|
|
|
break;
|
|
|
|
|
2020-09-03 21:10:28 +00:00
|
|
|
// for later
|
|
|
|
// case ga_recordgame, // start a new demo recording (later)
|
|
|
|
// case ga_loadgameplaydemo, // load a savegame and play a demo.
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2020-08-30 07:32:34 +00:00
|
|
|
}
|
|
|
|
C_AdjustBottom();
|
|
|
|
}
|
|
|
|
|
|
|
|
// get commands, check consistancy, and build new consistancy check
|
|
|
|
int buf = (gametic / ticdup) % BACKUPTICS;
|
|
|
|
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (playeringame[i])
|
|
|
|
{
|
|
|
|
ticcmd_t* cmd = &playercmds[i];
|
|
|
|
ticcmd_t* newcmd = &netcmds[i][buf];
|
|
|
|
|
|
|
|
if ((gametic % ticdup) == 0)
|
|
|
|
{
|
|
|
|
RunNetSpecs(i, buf);
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if (demorecording)
|
|
|
|
{
|
|
|
|
G_WriteDemoTiccmd(newcmd, i, buf);
|
|
|
|
}
|
|
|
|
if (demoplayback)
|
|
|
|
{
|
|
|
|
G_ReadDemoTiccmd(cmd, i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
*cmd = *newcmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (netgame && /*!demoplayback &&*/ (gametic % ticdup) == 0)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
//players[i].inconsistant = 0;
|
|
|
|
if (gametic > BACKUPTICS * ticdup && consistancy[i][buf] != cmd->consistancy)
|
|
|
|
{
|
|
|
|
players[i].inconsistant = gametic - BACKUPTICS * ticdup;
|
|
|
|
}
|
|
|
|
#endif
|
2020-09-13 17:27:05 +00:00
|
|
|
consistency[i][buf] = gi->GetPlayerChecksum(i);
|
2020-08-30 07:32:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
C_RunDelayedCommands();
|
2020-08-30 17:59:46 +00:00
|
|
|
updatePauseStatus();
|
2020-08-30 07:32:34 +00:00
|
|
|
|
|
|
|
switch (gamestate)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case GS_STARTUP:
|
|
|
|
gi->Startup();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_LEVEL:
|
2020-08-30 10:02:32 +00:00
|
|
|
gameupdatetime.Reset();
|
|
|
|
gameupdatetime.Clock();
|
2020-08-30 07:32:34 +00:00
|
|
|
gi->Ticker();
|
2021-05-13 17:48:27 +00:00
|
|
|
TickStatusBar();
|
2020-09-08 16:28:41 +00:00
|
|
|
levelTextTime--;
|
2020-08-30 10:02:32 +00:00
|
|
|
gameupdatetime.Unclock();
|
2020-08-30 07:32:34 +00:00
|
|
|
break;
|
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
case GS_MENUSCREEN:
|
|
|
|
case GS_FULLCONSOLE:
|
2021-04-15 22:50:13 +00:00
|
|
|
break;
|
2021-05-22 11:02:34 +00:00
|
|
|
case GS_CUTSCENE:
|
2020-08-30 07:32:34 +00:00
|
|
|
case GS_INTRO:
|
2021-04-26 19:13:11 +00:00
|
|
|
if (ScreenJobTick())
|
|
|
|
{
|
|
|
|
// synchronize termination with the playsim.
|
|
|
|
Net_WriteByte(DEM_ENDSCREENJOB);
|
|
|
|
}
|
2020-08-30 07:32:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-04-25 15:26:17 +00:00
|
|
|
void DrawOverlays()
|
|
|
|
{
|
|
|
|
NetUpdate(); // send out any new accumulation
|
|
|
|
|
|
|
|
if (gamestate != GS_INTRO) // do not draw overlays on the intros
|
|
|
|
{
|
|
|
|
// Draw overlay elements
|
|
|
|
CT_Drawer();
|
|
|
|
C_DrawConsole();
|
|
|
|
M_Drawer();
|
|
|
|
FStat::PrintStat(twod);
|
|
|
|
}
|
|
|
|
DrawRateStuff();
|
|
|
|
}
|
|
|
|
|
2020-08-30 07:32:34 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
2020-08-30 08:42:44 +00:00
|
|
|
// Display
|
2020-08-30 07:32:34 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
2022-12-08 11:41:40 +00:00
|
|
|
CVAR(String, drawtile, "", 0) // debug stuff. Draws the tile with the given number on top of thze HUD
|
2020-08-30 07:32:34 +00:00
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
void Display()
|
2020-08-30 07:32:34 +00:00
|
|
|
{
|
2020-08-30 22:33:41 +00:00
|
|
|
if (screen == nullptr || (!AppActive && (screen->IsFullscreen() || !vid_activeinbackground)))
|
2020-08-30 07:32:34 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2022-04-25 15:26:17 +00:00
|
|
|
|
|
|
|
FTexture* wipestart = nullptr;
|
|
|
|
if (nextwipe != wipe_None)
|
|
|
|
{
|
|
|
|
wipestart = screen->WipeStartScreen();
|
|
|
|
}
|
2020-08-30 07:32:34 +00:00
|
|
|
|
|
|
|
screen->FrameTime = I_msTimeFS();
|
2020-10-07 21:22:29 +00:00
|
|
|
tileUpdateAnimations();
|
2020-08-30 07:32:34 +00:00
|
|
|
screen->BeginFrame();
|
2020-09-15 19:46:32 +00:00
|
|
|
twodpsp.Clear();
|
|
|
|
twodpsp.SetSize(screen->GetWidth(), screen->GetHeight());
|
2020-08-30 08:42:44 +00:00
|
|
|
twodpsp.ClearClipRect();
|
2020-09-15 19:46:32 +00:00
|
|
|
twod->Clear();
|
2020-10-04 19:42:40 +00:00
|
|
|
//twod->SetSize(screen->GetWidth(), screen->GetHeight());
|
|
|
|
twod->Begin(screen->GetWidth(), screen->GetHeight());
|
2020-08-30 07:32:34 +00:00
|
|
|
twod->ClearClipRect();
|
2020-08-30 08:42:44 +00:00
|
|
|
switch (gamestate)
|
2020-08-30 07:32:34 +00:00
|
|
|
{
|
2020-08-30 08:42:44 +00:00
|
|
|
case GS_MENUSCREEN:
|
2020-09-03 22:20:32 +00:00
|
|
|
case GS_FULLCONSOLE:
|
2020-08-30 08:42:44 +00:00
|
|
|
gi->DrawBackground();
|
|
|
|
break;
|
|
|
|
|
2020-08-30 10:49:21 +00:00
|
|
|
case GS_INTRO:
|
2021-05-22 11:02:34 +00:00
|
|
|
case GS_CUTSCENE:
|
2021-05-23 12:36:54 +00:00
|
|
|
ScreenJobDraw();
|
2020-08-30 08:42:44 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case GS_LEVEL:
|
|
|
|
if (gametic != 0)
|
|
|
|
{
|
2020-08-31 17:18:53 +00:00
|
|
|
screen->FrameTime = I_msTimeFS();
|
2020-08-30 17:59:46 +00:00
|
|
|
screen->BeginFrame();
|
|
|
|
screen->SetSceneRenderTarget(gl_ssao != 0);
|
2022-09-30 14:25:21 +00:00
|
|
|
//updateModelInterpolation();
|
2020-08-30 08:42:44 +00:00
|
|
|
gi->Render();
|
|
|
|
DrawFullscreenBlends();
|
2020-09-08 16:28:41 +00:00
|
|
|
drawMapTitle();
|
2020-09-05 13:59:32 +00:00
|
|
|
break;
|
2020-08-30 08:42:44 +00:00
|
|
|
}
|
2020-09-05 13:59:32 +00:00
|
|
|
[[fallthrough]];
|
2020-08-30 08:42:44 +00:00
|
|
|
|
|
|
|
default:
|
2020-09-03 22:20:32 +00:00
|
|
|
twod->ClearScreen();
|
2020-08-30 08:42:44 +00:00
|
|
|
break;
|
2020-08-30 07:32:34 +00:00
|
|
|
}
|
2022-04-25 15:26:17 +00:00
|
|
|
|
|
|
|
if (nextwipe == wipe_None)
|
2022-11-14 09:49:25 +00:00
|
|
|
{
|
2022-04-25 15:26:17 +00:00
|
|
|
DrawOverlays();
|
2022-12-08 11:41:40 +00:00
|
|
|
if (drawtile[0])
|
2022-11-14 09:49:25 +00:00
|
|
|
{
|
2022-12-08 11:41:40 +00:00
|
|
|
auto tex = TexMan.CheckForTexture(drawtile, ETextureType::Any);
|
|
|
|
if (!tex.isValid()) tex = tileGetTextureID(atoi(drawtile));
|
|
|
|
if (tex.isValid())
|
|
|
|
{
|
2022-12-10 16:52:25 +00:00
|
|
|
auto tx = TexMan.GetGameTexture(tex, true);
|
2022-12-08 11:41:40 +00:00
|
|
|
if (tx)
|
|
|
|
{
|
|
|
|
int width = (int)tx->GetDisplayWidth();
|
|
|
|
int height = (int)tx->GetDisplayHeight();
|
|
|
|
int dwidth, dheight;
|
|
|
|
if (width > height)
|
|
|
|
{
|
|
|
|
dwidth = screen->GetWidth() / 4;
|
|
|
|
dheight = height * dwidth / width;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dheight = screen->GetHeight() / 4;
|
|
|
|
dwidth = width * dheight / height;
|
|
|
|
}
|
|
|
|
DrawTexture(twod, tx, 0, 0, DTA_DestWidth, dwidth, DTA_DestHeight, dheight, TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
2022-11-14 09:49:25 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-25 15:26:17 +00:00
|
|
|
else
|
2020-08-30 10:49:21 +00:00
|
|
|
{
|
2022-04-25 15:26:17 +00:00
|
|
|
PerformWipe(wipestart, screen->WipeEndScreen(), nextwipe, true, DrawOverlays);
|
|
|
|
nextwipe = wipe_None;
|
2020-08-30 10:49:21 +00:00
|
|
|
}
|
2022-11-14 09:49:25 +00:00
|
|
|
|
2022-04-25 15:26:17 +00:00
|
|
|
screen->Update();
|
2020-08-30 07:32:34 +00:00
|
|
|
}
|
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
2020-08-30 07:32:34 +00:00
|
|
|
// Forces playsim processing time to be consistent across frames.
|
|
|
|
// This improves interpolation for frames in between tics.
|
|
|
|
//
|
|
|
|
// With this cvar off the mods with a high playsim processing time will appear
|
|
|
|
// less smooth as the measured time used for interpolation will vary.
|
2020-08-30 08:42:44 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
2020-08-30 07:32:34 +00:00
|
|
|
|
2020-08-29 22:55:49 +00:00
|
|
|
static void TicStabilityWait()
|
|
|
|
{
|
|
|
|
using namespace std::chrono;
|
|
|
|
using namespace std::this_thread;
|
|
|
|
|
|
|
|
if (!r_ticstability)
|
|
|
|
return;
|
|
|
|
|
|
|
|
uint64_t start = duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
uint64_t cur = duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
|
|
|
|
if (cur - start > stabilityticduration)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TicStabilityBegin()
|
|
|
|
{
|
|
|
|
using namespace std::chrono;
|
|
|
|
stabilitystarttime = duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void TicStabilityEnd()
|
|
|
|
{
|
|
|
|
using namespace std::chrono;
|
|
|
|
uint64_t stabilityendtime = duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
|
2021-10-30 08:21:43 +00:00
|
|
|
stabilityticduration = min(stabilityendtime - stabilitystarttime, (uint64_t)1'000'000);
|
2020-08-29 22:55:49 +00:00
|
|
|
}
|
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
//==========================================================================
|
2020-08-29 22:55:49 +00:00
|
|
|
//
|
2020-08-30 08:42:44 +00:00
|
|
|
// The most important function in the engine.
|
2020-08-29 22:55:49 +00:00
|
|
|
//
|
2020-08-30 08:42:44 +00:00
|
|
|
//==========================================================================
|
|
|
|
|
2020-08-29 22:55:49 +00:00
|
|
|
void TryRunTics (void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int lowtic;
|
|
|
|
int realtics;
|
|
|
|
int availabletics;
|
|
|
|
int counts;
|
|
|
|
int numplaying;
|
|
|
|
|
|
|
|
// If paused, do not eat more CPU time than we need, because it
|
|
|
|
// will all be wasted anyway.
|
2021-05-22 11:02:34 +00:00
|
|
|
bool doWait = (cl_capfps || pauseext || (r_NoInterpolate && !M_IsAnimated() && gamestate != GS_CUTSCENE && gamestate != GS_INTRO));
|
2020-08-29 22:55:49 +00:00
|
|
|
|
|
|
|
// get real tics
|
2020-09-18 21:18:42 +00:00
|
|
|
if (doWait)
|
2020-08-29 22:55:49 +00:00
|
|
|
{
|
|
|
|
entertic = I_WaitForTic (oldentertics);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entertic = I_GetTime ();
|
|
|
|
}
|
|
|
|
realtics = entertic - oldentertics;
|
|
|
|
oldentertics = entertic;
|
|
|
|
|
2022-12-06 23:31:17 +00:00
|
|
|
// update the scale factor for unsynchronised input here.
|
|
|
|
inputScale = I_GetInputFrac(SyncInput());
|
|
|
|
|
2020-08-29 22:55:49 +00:00
|
|
|
// get available tics
|
|
|
|
NetUpdate ();
|
|
|
|
|
|
|
|
if (pauseext)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lowtic = INT_MAX;
|
|
|
|
numplaying = 0;
|
|
|
|
for (i = 0; i < doomcom.numnodes; i++)
|
|
|
|
{
|
|
|
|
if (nodeingame[i])
|
|
|
|
{
|
|
|
|
numplaying++;
|
|
|
|
if (nettics[i] < lowtic)
|
|
|
|
lowtic = nettics[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
availabletics = lowtic - gametic / ticdup;
|
2020-08-29 22:55:49 +00:00
|
|
|
|
|
|
|
// decide how many tics to run
|
|
|
|
if (realtics < availabletics-1)
|
|
|
|
counts = realtics+1;
|
|
|
|
else if (realtics < availabletics)
|
|
|
|
counts = realtics;
|
|
|
|
else
|
|
|
|
counts = availabletics;
|
2021-12-30 09:30:21 +00:00
|
|
|
|
2020-08-29 22:55:49 +00:00
|
|
|
// Uncapped framerate needs seprate checks
|
|
|
|
if (counts == 0 && !doWait)
|
|
|
|
{
|
|
|
|
TicStabilityWait();
|
|
|
|
|
|
|
|
// Check possible stall conditions
|
|
|
|
Net_CheckLastReceived(counts);
|
|
|
|
if (realtics >= 1)
|
|
|
|
{
|
|
|
|
C_Ticker();
|
|
|
|
M_Ticker();
|
|
|
|
// Repredict the player for new buffered movement
|
|
|
|
#if 0
|
|
|
|
gi->Unpredict();
|
|
|
|
gi->Predict(myconnectindex);
|
|
|
|
#endif
|
|
|
|
}
|
2020-11-30 22:40:16 +00:00
|
|
|
if (!SyncInput())
|
2020-08-30 10:02:32 +00:00
|
|
|
{
|
2020-08-30 20:52:20 +00:00
|
|
|
I_GetEvent();
|
2020-09-15 19:45:54 +00:00
|
|
|
auto input = CONTROL_GetInput();
|
2022-12-02 03:45:37 +00:00
|
|
|
gi->GetInput(&input, inputScale);
|
2020-08-30 10:02:32 +00:00
|
|
|
}
|
2020-08-29 22:55:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (counts < 1)
|
|
|
|
counts = 1;
|
|
|
|
|
|
|
|
// wait for new tics if needed
|
|
|
|
while (lowtic < gametic + counts)
|
|
|
|
{
|
|
|
|
NetUpdate ();
|
|
|
|
lowtic = INT_MAX;
|
|
|
|
|
|
|
|
for (i = 0; i < doomcom.numnodes; i++)
|
|
|
|
if (nodeingame[i] && nettics[i] < lowtic)
|
|
|
|
lowtic = nettics[i];
|
|
|
|
|
|
|
|
lowtic = lowtic * ticdup;
|
|
|
|
|
|
|
|
if (lowtic < gametic)
|
|
|
|
I_Error ("TryRunTics: lowtic < gametic");
|
|
|
|
|
|
|
|
// Check possible stall conditions
|
|
|
|
Net_CheckLastReceived (counts);
|
|
|
|
|
|
|
|
// Update time returned by I_GetTime, but only if we are stuck in this loop
|
|
|
|
if (lowtic < gametic + counts)
|
|
|
|
I_SetFrameTime();
|
|
|
|
|
|
|
|
// don't stay in here forever -- give the menu a chance to work
|
|
|
|
if (I_GetTime () - entertic >= 1)
|
|
|
|
{
|
|
|
|
C_Ticker ();
|
|
|
|
M_Ticker ();
|
|
|
|
// Repredict the player for new buffered movement
|
|
|
|
#if 0
|
|
|
|
gi->Unpredict();
|
|
|
|
gi->Predict(myconnectindex);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Tic lowtic is high enough to process this gametic. Clear all possible waiting info
|
|
|
|
hadlate = false;
|
|
|
|
#if 0
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
players[i].waiting = false;
|
|
|
|
#endif
|
|
|
|
lastglobalrecvtime = I_GetTime (); //Update the last time the game tic'd over
|
|
|
|
|
|
|
|
// run the count tics
|
|
|
|
if (counts > 0)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
gi->Unpredict();
|
|
|
|
#endif
|
|
|
|
while (counts--)
|
|
|
|
{
|
|
|
|
TicStabilityBegin();
|
|
|
|
if (gametic > lowtic)
|
|
|
|
{
|
|
|
|
I_Error ("gametic>lowtic");
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if (advancedemo)
|
|
|
|
{
|
|
|
|
D_DoAdvanceDemo ();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
C_Ticker ();
|
|
|
|
M_Ticker ();
|
2020-08-30 08:42:44 +00:00
|
|
|
GameTicker();
|
2020-08-29 22:55:49 +00:00
|
|
|
gametic++;
|
|
|
|
|
|
|
|
NetUpdate (); // check for new console commands
|
|
|
|
TicStabilityEnd();
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
gi->Predict(myconnectindex);
|
|
|
|
#endif
|
|
|
|
gi->UpdateSounds();
|
|
|
|
soundEngine->UpdateSounds(I_GetTime());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TicStabilityWait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
2020-08-30 07:32:34 +00:00
|
|
|
// MainLoop - will never return aside from exceptions being thrown.
|
2020-08-29 22:55:49 +00:00
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void MainLoop ()
|
|
|
|
{
|
|
|
|
int lasttic = 0;
|
|
|
|
|
|
|
|
// Clamp the timer to TICRATE until the playloop has been entered.
|
|
|
|
r_NoInterpolate = true;
|
|
|
|
|
2021-04-27 18:04:11 +00:00
|
|
|
if (userConfig.CommandMap.IsNotEmpty())
|
|
|
|
{
|
|
|
|
auto maprecord = FindMapByName(userConfig.CommandMap);
|
2021-06-24 08:49:26 +00:00
|
|
|
if (maprecord == nullptr)
|
|
|
|
{
|
|
|
|
maprecord = SetupUserMap(userConfig.CommandMap, g_gameType & GAMEFLAG_DUKE? "dethtoll.mid" : nullptr);
|
|
|
|
}
|
2021-04-27 18:04:11 +00:00
|
|
|
userConfig.CommandMap = "";
|
|
|
|
if (maprecord)
|
|
|
|
{
|
2021-07-20 08:51:34 +00:00
|
|
|
DeferredStartGame(maprecord, g_nextskill);
|
2021-04-27 18:04:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 22:55:49 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// frame syncronous IO operations
|
|
|
|
if (gametic > lasttic)
|
|
|
|
{
|
|
|
|
lasttic = gametic;
|
|
|
|
I_StartFrame ();
|
|
|
|
}
|
|
|
|
I_SetFrameTime();
|
|
|
|
|
|
|
|
TryRunTics (); // will run at least one tic
|
|
|
|
// Update display, next frame, with current state.
|
2020-08-30 20:52:20 +00:00
|
|
|
I_StartTic();
|
2020-08-30 07:32:34 +00:00
|
|
|
|
2020-08-30 08:42:44 +00:00
|
|
|
Display();
|
2020-08-29 22:55:49 +00:00
|
|
|
Mus_UpdateMusic(); // must be at the end.
|
|
|
|
}
|
|
|
|
catch (CRecoverableError &error)
|
|
|
|
{
|
|
|
|
if (error.GetMessage ())
|
|
|
|
{
|
|
|
|
Printf (PRINT_BOLD, "\n%s\n", error.GetMessage());
|
|
|
|
}
|
|
|
|
gi->ErrorCleanup();
|
2020-10-08 20:53:12 +00:00
|
|
|
M_ClearMenus();
|
2020-08-29 22:55:49 +00:00
|
|
|
C_FullConsole();
|
2020-09-25 17:36:50 +00:00
|
|
|
gameaction = ga_nothing;
|
2020-08-29 22:55:49 +00:00
|
|
|
}
|
|
|
|
catch (CVMAbortException &error)
|
|
|
|
{
|
|
|
|
error.MaybePrintMessage();
|
|
|
|
Printf("%s", error.stacktrace.GetChars());
|
|
|
|
gi->ErrorCleanup();
|
2020-10-08 20:53:12 +00:00
|
|
|
twod->SetOffset(DVector2(0, 0));
|
|
|
|
M_ClearMenus();
|
2020-08-29 22:55:49 +00:00
|
|
|
C_FullConsole();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|