- refactored Blood's main loop.

* moved Smacker video playing code into the backend, so now all games can play all supported video formats
* logos and level intro/exit videos use ScreenJob
This commit is contained in:
Christoph Oelckers 2020-07-29 23:18:08 +02:00
parent 0958bccade
commit 347ed51036
19 changed files with 444 additions and 549 deletions

View file

@ -34,7 +34,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "blood.h"
#include "choke.h"
#include "controls.h"
#include "credits.h"
#include "dude.h"
#include "endgame.h"
#include "eventq.h"
@ -59,16 +58,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "raze_sound.h"
#include "nnexts.h"
#include "secrets.h"
#include "gamestate.h"
#include "screenjob.h"
BEGIN_BLD_NS
INPUT_MODE gInputMode;
#ifdef USE_QHEAP
unsigned int nMaxAlloc = 0x4000000;
#endif
char bAddUserMap = false;
bool bNoDemo = false;
bool bQuickStart = true;
@ -80,8 +75,6 @@ short BloodVersion = 0x115;
int gNetPlayers;
char *pUserTiles = NULL;
int gChokeCounter = 0;
double g_gameUpdateTime, g_gameUpdateAndDrawTime;
@ -131,21 +124,9 @@ enum gametokens
int blood_globalflags;
void ShutDown(void)
{
if (!in3dmode())
return;
netDeinitialize();
//sndTerm();
sfxTerm();
// PORT_TODO: Check argument
DO_FREE_AND_NULL(pUserTiles);
}
void QuitGame(void)
{
ShutDown();
Bexit(0);
throw CExitEvent(0);
}
void PrecacheDude(spritetype *pSprite)
@ -412,8 +393,6 @@ void StartLevel(GAMEOPTIONS *gameOptions)
if (gEpisodeInfo[gGameOptions.nEpisode].cutALevel == gGameOptions.nLevel
&& gEpisodeInfo[gGameOptions.nEpisode].at8f08)
gGameOptions.uGameFlags |= 4;
if ((gGameOptions.uGameFlags&4))
levelPlayIntroScene(gGameOptions.nEpisode);
///////
gGameOptions.weaponsV10x = gWeaponsV10x;
@ -455,11 +434,10 @@ void StartLevel(GAMEOPTIONS *gameOptions)
enginecompatibility_mode = ENGINECOMPATIBILITY_19960925;//bVanilla;
memset(xsprite,0,sizeof(xsprite));
memset(sprite,0,kMaxSprites*sizeof(spritetype));
drawLoadingScreen();
//drawLoadingScreen();
if (dbLoadMap(gameOptions->zLevelName,(int*)&startpos.x,(int*)&startpos.y,(int*)&startpos.z,&startang,&startsectnum,(unsigned int*)&gameOptions->uMapCRC))
{
gQuitGame = true;
return;
I_Error("Unable to load map");
}
currentLevel = &mapList[gGameOptions.nEpisode * kMaxLevels + gGameOptions.nLevel];
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
@ -571,7 +549,6 @@ void StartLevel(GAMEOPTIONS *gameOptions)
gFrame = 0;
gChokeCounter = 0;
M_ClearMenus();
levelTryPlayMusicOrNothing(gGameOptions.nEpisode, gGameOptions.nLevel);
// viewSetMessage("");
viewSetErrorMessage("");
viewResizeView(gViewSize);
@ -580,40 +557,9 @@ void StartLevel(GAMEOPTIONS *gameOptions)
netWaitForEveryone(0);
totalclock = 0;
paused = 0;
gGameStarted = 1;
ready2send = 1;
}
void StartNetworkLevel(void)
{
if (!(gGameOptions.uGameFlags&1))
{
gGameOptions.nEpisode = gPacketStartGame.episodeId;
gGameOptions.nLevel = gPacketStartGame.levelId;
gGameOptions.nGameType = gPacketStartGame.gameType;
gGameOptions.nDifficulty = gPacketStartGame.difficulty;
gGameOptions.nMonsterSettings = gPacketStartGame.monsterSettings;
gGameOptions.nWeaponSettings = gPacketStartGame.weaponSettings;
gGameOptions.nItemSettings = gPacketStartGame.itemSettings;
gGameOptions.nRespawnSettings = gPacketStartGame.respawnSettings;
gGameOptions.bFriendlyFire = gPacketStartGame.bFriendlyFire;
gGameOptions.bKeepKeysOnRespawn = gPacketStartGame.bKeepKeysOnRespawn;
///////
gGameOptions.weaponsV10x = gPacketStartGame.weaponsV10x;
///////
gBlueFlagDropped = false;
gRedFlagDropped = false;
if (gPacketStartGame.userMap)
levelAddUserMap(gPacketStartGame.userMapName);
else
levelSetupOptions(gGameOptions.nEpisode, gGameOptions.nLevel);
}
StartLevel(&gGameOptions);
}
void LocalKeys(void)
{
@ -778,22 +724,33 @@ void ProcessFrame(void)
netMasterUpdate();
}
}
Mus_Fade(4000);
seqKillAll();
if (gGameOptions.uGameFlags&2)
{
STAT_Update(true);
if (gGameOptions.nGameType == 0)
{
auto completion = [] (bool) {
gamestate = GS_DEMOSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_CreditsMenu);
gGameOptions.uGameFlags &= ~3;
gRestartGame = 1;
gQuitGame = 1;
};
if (gGameOptions.uGameFlags&8)
levelPlayEndScene(gGameOptions.nEpisode);
M_StartControlPanel(false);
M_SetMenu(NAME_CreditsMenu);
{
levelPlayEndScene(gGameOptions.nEpisode, completion);
}
else completion(false);
}
gGameOptions.uGameFlags &= ~3;
gRestartGame = 1;
gQuitGame = 1;
else
{
gGameOptions.uGameFlags &= ~3;
gRestartGame = 1;
gQuitGame = 1;
}
}
else
{
@ -876,13 +833,12 @@ static const char* actions[] = {
"Jetpack"
};
int GameInterface::app_main()
static void app_init()
{
buttonMap.SetButtons(actions, NUM_ACTIONS);
memcpy(&gGameOptions, &gSingleGameOptions, sizeof(GAMEOPTIONS));
gGameOptions.nMonsterSettings = !userConfig.nomonsters;
bQuickStart = userConfig.nologo;
gGameOptions.nMonsterSettings = !userConfig.nomonsters;
bQuickStart = userConfig.nologo;
ReadAllRFS();
HookReplaceFunctions();
@ -891,29 +847,20 @@ int GameInterface::app_main()
engineInit();
Printf("Loading tiles\n");
if (pUserTiles)
{
FStringf buffer("%s%%03i.ART", pUserTiles);
if (!tileInit(0,buffer))
ThrowError("User specified ART files not found");
}
else
{
if (!tileInit(0,NULL))
ThrowError("TILES###.ART files not found");
}
if (!tileInit(0, NULL))
I_FatalError("TILES###.ART files not found");
levelLoadDefaults();
loaddefinitionsfile(BLOODWIDESCREENDEF);
loaddefinitions_game(BLOODWIDESCREENDEF, FALSE);
const char *defsfile = G_DefFile();
const char* defsfile = G_DefFile();
uint32_t stime = timerGetTicks();
if (!loaddefinitionsfile(defsfile))
{
uint32_t etime = timerGetTicks();
Printf("Definitions file \"%s\" loaded in %d ms.\n", defsfile, etime-stime);
Printf("Definitions file \"%s\" loaded in %d ms.\n", defsfile, etime - stime);
}
loaddefinitions_game(defsfile, FALSE);
powerupInit();
@ -931,11 +878,10 @@ int GameInterface::app_main()
ctrlInit();
timerInit(120);
timerSetCallback(ClockStrobe);
// PORT-TODO: CD audio init
Printf("Initializing network users\n");
netInitialize(true);
videoInit();
videoInit();
hud_size.Callback();
Printf("Initializing sound system\n");
sndInit();
@ -948,18 +894,17 @@ int GameInterface::app_main()
gStartNewGame = 1;
}
videoSetViewableArea(0, 0, xdim - 1, ydim - 1);
bool playvideo = !bQuickStart;
if (playvideo)
credLogosDos();
UpdateDacs(0, true);
}
RESTART:
static void gameInit()
{
//RESTART:
sub_79760();
gViewIndex = myconnectindex;
gMe = gView = &gPlayer[myconnectindex];
netBroadcastPlayerInfo(myconnectindex);
#if 0
Printf("Waiting for network players!\n");
netWaitForEveryone(0);
if (gRestartGame)
@ -971,134 +916,94 @@ RESTART:
netResetToSinglePlayer();
goto RESTART;
}
UpdateNetworkMenus();
#endif
UpdateNetworkMenus();
gQuitGame = 0;
gRestartGame = 0;
if (gGameOptions.nGameType > 0)
{
inputState.ClearAllInput();
}
if (!bAddUserMap && !gGameStarted)
{
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
}
ready2send = 1;
while (!gQuitGame)
}
static void gameTicker()
{
bool gameUpdate = false;
double const gameUpdateStartTime = timerGetHiTicks();
while (gPredictTail < gNetFifoHead[myconnectindex] && !paused)
{
bool bDraw;
D_ProcessEvents();
if (gGameStarted) // gGameStarted: gameState == GS_LEVEL
{
char gameUpdate = false;
double const gameUpdateStartTime = timerGetHiTicks();
while (gPredictTail < gNetFifoHead[myconnectindex] && !paused)
{
viewUpdatePrediction(&gFifoInput[gPredictTail&255][myconnectindex]);
}
if (numplayers == 1)
gBufferJitter = 0;
while (totalclock >= gNetFifoClock && ready2send)
{
gNetInput = gInput;
gInput = {};
netGetInput();
gNetFifoClock += 4;
while (gNetFifoHead[myconnectindex]-gNetFifoTail > gBufferJitter && !gStartNewGame && !gQuitGame)
{
int i;
for (i = connecthead; i >= 0; i = connectpoint2[i])
if (gNetFifoHead[i] == gNetFifoTail)
break;
if (i >= 0)
break;
//faketimerhandler();
ProcessFrame();
gameUpdate = true;
}
}
if (gameUpdate)
{
g_gameUpdateTime = timerGetHiTicks() - gameUpdateStartTime;
if (g_gameUpdateAvgTime < 0.f)
g_gameUpdateAvgTime = g_gameUpdateTime;
g_gameUpdateAvgTime = ((GAMEUPDATEAVGTIMENUMSAMPLES-1.f)*g_gameUpdateAvgTime+g_gameUpdateTime)/((float) GAMEUPDATEAVGTIMENUMSAMPLES);
}
bDraw = G_FPSLimit() != 0;
if (gQuitRequest && gQuitGame)
videoClearScreen(0);
else
{
netCheckSync();
if (bDraw)
{
viewDrawScreen();
g_gameUpdateAndDrawTime = g_beforeSwapTime/* timerGetHiTicks()*/ - gameUpdateStartTime;
}
}
}
else // gameState == GS_DEMOSCREEN
{
// Menu background
bDraw = G_FPSLimit() != 0;
if (bDraw)
{
twod->ClearScreen();
rotatesprite(160<<16,100<<16,65536,0,2518,0,0,0x4a,0,0,xdim-1,ydim-1);
}
if (gQuitRequest && !gQuitGame)
netBroadcastMyLogoff(gQuitRequest == 2);
}
if (bDraw)
{
gameHandleEvents();
inputState.SetBindsEnabled(gInputMode == kInputGame);
switch (gInputMode)
{
case kInputGame:
LocalKeys();
break;
default:
break;
}
if (gQuitGame)
continue;
C_RunDelayedCommands();
ctrlGetInput();
switch (gInputMode)
{
case kInputMessage:
gPlayerMsg.ProcessKeys();
gPlayerMsg.Draw();
break;
case kInputEndGame:
gEndGameMgr.ProcessKeys();
gEndGameMgr.Draw();
break;
default:
break;
}
videoNextPage();
}
if (TestBitString(gotpic, 2342))
{
FireProcess();
ClearBitString(gotpic, 2342);
}
//if (byte_148e29 && gStartNewGame)
//{
// gStartNewGame = 0;
// gQuitGame = 1;
//}
if (gStartNewGame)
{
StartLevel(&gGameOptions);
}
viewUpdatePrediction(&gFifoInput[gPredictTail & 255][myconnectindex]);
}
if (numplayers == 1)
gBufferJitter = 0;
while (totalclock >= gNetFifoClock && ready2send)
{
gNetInput = gInput;
gInput = {};
netGetInput();
gNetFifoClock += 4;
while (gNetFifoHead[myconnectindex] - gNetFifoTail > gBufferJitter && !gStartNewGame && !gQuitGame)
{
int i;
for (i = connecthead; i >= 0; i = connectpoint2[i])
if (gNetFifoHead[i] == gNetFifoTail)
break;
if (i >= 0)
break;
//faketimerhandler();
ProcessFrame();
gameUpdate = true;
}
}
if (gameUpdate)
{
g_gameUpdateTime = timerGetHiTicks() - gameUpdateStartTime;
if (g_gameUpdateAvgTime < 0.f)
g_gameUpdateAvgTime = g_gameUpdateTime;
g_gameUpdateAvgTime = ((GAMEUPDATEAVGTIMENUMSAMPLES - 1.f) * g_gameUpdateAvgTime + g_gameUpdateTime) / ((float)GAMEUPDATEAVGTIMENUMSAMPLES);
}
if (gQuitRequest && gQuitGame)
videoClearScreen(0);
else
{
netCheckSync();
viewDrawScreen();
g_gameUpdateAndDrawTime = g_beforeSwapTime/* timerGetHiTicks()*/ - gameUpdateStartTime;
}
}
static void drawBackground()
{
twod->ClearScreen();
rotatesprite(160 << 16, 100 << 16, 65536, 0, 2518, 0, 0, 0x4a, 0, 0, xdim - 1, ydim - 1);
if (gQuitRequest && !gQuitGame)
netBroadcastMyLogoff(gQuitRequest == 2);
}
static void commonTicker(bool &playvideo)
{
if (TestBitString(gotpic, 2342))
{
FireProcess();
ClearBitString(gotpic, 2342);
}
if (gStartNewGame)
{
StartLevel(&gGameOptions);
auto completion = [](bool = false)
{
levelTryPlayMusicOrNothing(gGameOptions.nEpisode, gGameOptions.nLevel);
gamestate = GS_LEVEL;
};
if ((gGameOptions.uGameFlags & 4))
levelPlayIntroScene(gGameOptions.nEpisode, completion);
else
completion(false);
}
ready2send = 0;
if (gRestartGame)
{
Mus_Stop();
@ -1106,21 +1011,70 @@ RESTART:
gQuitGame = 0;
gQuitRequest = 0;
gRestartGame = 0;
gGameStarted = 0;
levelSetupOptions(0,0);
levelSetupOptions(0, 0);
if (gGameOptions.nGameType != 0)
{
videoSetViewableArea(0,0,xdim-1,ydim-1);
playvideo = !bQuickStart;
playvideo = !bQuickStart;
}
else playvideo = false;
goto RESTART;
else playvideo = false;
gamestate = GS_STARTUP;
}
ShutDown();
}
int GameInterface::app_main()
{
app_init();
gamestate = GS_STARTUP;
bool playvideo = !bQuickStart;
while (true)
{
if (gamestate == GS_STARTUP) gameInit();
commonTicker(playvideo);
gameHandleEvents();
D_ProcessEvents();
ctrlGetInput();
switch (gamestate)
{
default:
case GS_STARTUP:
if (playvideo) playlogos();
else
{
gamestate = GS_DEMOSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
}
break;
case GS_DEMOSCREEN:
drawBackground();
break;
case GS_INTRO:
case GS_INTERMISSION:
RunScreenJobFrame(); // This handles continuation through its completion callback.
break;
case GS_LEVEL:
gameTicker();
LocalKeys();
break;
case GS_FINALE:
gEndGameMgr.ProcessKeys();
gEndGameMgr.Draw();
break;
}
videoNextPage();
}
return 0;
}
@ -1449,7 +1403,7 @@ extern IniFile* BloodINI;
void GameInterface::FreeGameData()
{
if (BloodINI) delete BloodINI;
ShutDown();
netDeinitialize();
}
void GameInterface::UpdateScreenSize()

View file

@ -106,13 +106,6 @@ struct INICHAIN {
extern INICHAIN *pINIChain;
enum INPUT_MODE {
kInputGame = 0,
kInputMessage,
kInputEndGame,
};
extern INPUT_MODE gInputMode;
extern short BloodVersion;
extern int gNetPlayers;
extern bool gRestartGame;

View file

@ -35,6 +35,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "map2d.h"
#include "view.h"
#include "d_event.h"
#include "gamestate.h"
BEGIN_BLD_NS
@ -83,7 +84,7 @@ void ctrlGetInput(void)
auto scaleAdjustmentToInterval = [=](double x) { return x * kTicsPerSec / (1000.0 / elapsedInputTicks); };
if (!gGameStarted || gInputMode != kInputGame)
if (gamestate != GS_LEVEL || System_WantGuiCapture())
{
gInput = {};
CONTROL_GetInput(&info);
@ -414,15 +415,4 @@ void ctrlGetInput(void)
}
}
#if 0
if (gGameStarted && gInputMode != kInputMessage
&& buttonMap.ButtonDown(gamefunc_SendMessage))
{
buttonMap.ClearButton(gamefunc_SendMessage);
inputState.keyFlushScans();
gInputMode = kInputMessage;
}
#endif
END_BLD_NS

View file

@ -37,212 +37,113 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "../glbackend/glbackend.h"
#include "raze_sound.h"
#include "v_2ddrawer.h"
#include "screenjob.h"
#include "gamestate.h"
#include "seq.h"
#include "menu.h"
BEGIN_BLD_NS
char exitCredits = 0;
char Wait(int nTicks)
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void playlogos()
{
totalclock = 0;
while (totalclock < nTicks)
{
gameHandleEvents();
auto key = inputState.keyGetScan();
if (key)
{
if (key == sc_Escape) // sc_Escape
exitCredits = 1;
return 0;
}
}
return 1;
JobDesc jobs[6];
int job = 0;
static AnimSound logosound[] =
{
{ 1, -1 },
{ -1, -1 },
{ 1, -1 },
{ -1,-1 }
};
if (logosound[0].soundnum == -1)
{
logosound[0].soundnum = S_FindSound("logo.wav");
logosound[2].soundnum = S_FindSound("gt.wav");
}
if (fileSystem.FindFile("logo.smk"))
{
jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) };
}
else
{
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); }};
jobs[job++] = { Create<DImageScreen>(2050) };
}
if (fileSystem.FindFile("gti.smk"))
{
jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) };
}
else
{
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); }};
jobs[job++] = { Create<DImageScreen>(2052) };
}
jobs[job++] = { Create<DBlackScreen>(1), []() { sndPlaySpecialMusicOrNothing(MUS_INTRO); sndStartSample("THUNDER2", 128, -1); }};
jobs[job++] = { Create<DImageScreen>(2518, DScreenJob::fadein) };
RunScreenJob(jobs, job, [](bool) {
gamestate = GS_DEMOSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
}, true, true);
}
char DoFade(char r, char g, char b, int nTicks)
void playSmk(const char *smk, const char *wav, int wavid, CompletionFunc func)
{
return 1;
JobDesc jobs{};
static AnimSound smksound[] =
{
{ 1, -1 },
{ -1, -1 },
};
int id = S_FindSoundByResID(wavid);
if (id <= 0)
{
FString wavv = wav;
FixPathSeperator(wavv);
id = S_FindSound(wavv);
}
FString smkk = smk;
FixPathSeperator(smkk);
smksound[0].soundnum = id;
jobs.job = PlayVideo(smkk, smksound, nullptr);
RunScreenJob(&jobs, 1, func);
}
char DoUnFade(int nTicks)
void levelPlayIntroScene(int nEpisode, CompletionFunc completion)
{
return 1;
gGameOptions.uGameFlags &= ~4;
Mus_Stop();
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
playSmk(pEpisode->at8f08, pEpisode->at9030, pEpisode->at9028, completion);
}
void credPlaySmk(const char* _pzSMK, const char* _pzWAV, int nWav);
void credLogosDos(void)
void levelPlayEndScene(int nEpisode, CompletionFunc completion)
{
char bShift = inputState.ShiftPressed();
videoSetViewableArea(0, 0, xdim-1, ydim-1);
DoUnFade(1);
if (bShift)
return;
{
if (fileSystem.FindFile("logo.smk"))
{
credPlaySmk("logo.smk", "logo.wav", -1);
}
else
{
twod->ClearScreen();
rotatesprite(160<<16, 100<<16, 65536, 0, 2050, 0, 0, 0x4a, 0, 0, xdim-1, ydim-1);
sndStartSample("THUNDER2", 128, -1);
videoNextPage();
if (!Wait(360))
return;
if (!DoFade(0, 0, 0, 60))
return;
}
if (fileSystem.FindFile("gti.smk"))
{
credPlaySmk("gti.smk", "gt.wav", -1);
}
else
{
twod->ClearScreen();
rotatesprite(160<<16, 100<<16, 65536, 0, 2052, 0, 0, 0x0a, 0, 0, xdim-1, ydim-1);
videoNextPage();
DoUnFade(1);
sndStartSample("THUNDER2", 128, -1);
if (!Wait(360))
return;
}
}
sndPlaySpecialMusicOrNothing(MUS_INTRO);
sndStartSample("THUNDER2", 128, -1);
if (!DoFade(0, 0, 0, 60))
return;
twod->ClearScreen();
videoNextPage();
if (!DoUnFade(1))
return;
twod->ClearScreen();
rotatesprite(160<<16, 100<<16, 65536, 0, 2518, 0, 0, 0x4a, 0, 0, xdim-1, ydim-1);
videoNextPage();
Wait(360);
//Mus_Fade(4000);
}
void credReset(void)
{
twod->ClearScreen();
videoNextPage();
DoFade(0,0,0,1);
DoUnFade(1);
}
bool credKOpen4Load(FString &pzFile)
{
int nLen = strlen(pzFile);
FixPathSeperator(pzFile);
auto nHandle = fileSystem.FindFile(pzFile);
if (nHandle < 0)
{
// Strip the drive letter and retry.
if (nLen >= 3 && isalpha(pzFile[0]) && pzFile[1] == ':' && pzFile[2] == '/')
{
pzFile = pzFile.Mid(3);
nHandle = fileSystem.FindFile(pzFile);
}
}
return nHandle;
}
void credPlaySmk(const char *_pzSMK, const char *_pzWAV, int nWav)
{
if (!_pzSMK || !*_pzSMK)
return;
FString pzSMK = _pzSMK;
FString pzWAV = _pzWAV;
auto nHandleSMK = credKOpen4Load(pzSMK);
if (!nHandleSMK)
{
return;
}
SmackerHandle hSMK = Smacker_Open(pzSMK);
if (!hSMK.isValid)
{
return;
}
uint32_t nWidth, nHeight;
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
uint8_t palette[768];
AnimTextures animtex;
TArray<uint8_t> pFrame(nWidth * nHeight + std::max(nWidth, nHeight), true);
animtex.SetSize(AnimTexture::Paletted, nWidth, nHeight);
int nFrameRate = Smacker_GetFrameRate(hSMK);
int nFrames = Smacker_GetNumFrames(hSMK);
Smacker_GetPalette(hSMK, palette);
gGameOptions.uGameFlags &= ~8;
Mus_Stop();
int nScale;
int nStat;
if (nWidth <= 320 && nHeight <= 200)
{
if ((nWidth / (nHeight * 1.2f)) > (1.f * xdim / ydim))
nScale = divscale16(320 * xdim * 3, nWidth * ydim * 4);
else
nScale = divscale16(200, nHeight);
nStat = 2 | 8 | 64;
}
else
{
// DOS Blood v1.11: 320x240, 320x320, 640x400, and 640x480 SMKs all display 1:1 and centered in a 640x480 viewport
int num = scale(65536, ydim << 2, xdim * 3);
int div = (max(nHeight, 240 + 1u) + 239) / 240;
nScale = num / div;
nStat = 2 | 8 | 64 | 1024;
renderSetAspect(viewingrange, 65536);
}
if (nWav > 0)
sndStartWavID(nWav, 255);
else
{
auto nHandleWAV = credKOpen4Load(pzWAV);
if (nHandleWAV)
{
sndStartWavDisk(pzWAV, 255);
}
}
gameHandleEvents();
ClockTicks nStartTime = totalclock;
inputState.ClearAllInput();
int nFrame = 0;
hw_int_useindexedcolortextures = false;
do
{
gameHandleEvents();
if (scale((int)(totalclock-nStartTime), nFrameRate, kTicRate) < nFrame)
continue;
if (inputState.CheckAllInput())
break;
twod->ClearScreen();
Smacker_GetPalette(hSMK, palette);
Smacker_GetFrame(hSMK, pFrame.Data());
animtex.SetFrame(palette, pFrame.Data());
rotatesprite_fs(160<<16, 100<<16, nScale, 0, -1, 0, 0, nStat, animtex.GetFrame());
videoNextPage();
nFrame++;
Smacker_GetNextFrame(hSMK);
} while(nFrame < nFrames);
hw_int_useindexedcolortextures = hw_useindexedcolortextures;
Smacker_Close(hSMK);
inputState.ClearAllInput();
soundEngine->StopAllChannels();
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
playSmk(pEpisode->at8f98, pEpisode->at90c0, pEpisode->at902c, completion);
}
END_BLD_NS

View file

@ -1,31 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood 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.
*/
//-------------------------------------------------------------------------
#pragma once
BEGIN_BLD_NS
void credLogosDos(void);
void credReset(void);
void credPlaySmk(const char *pzSMK, const char *pzWAV, int nWAV);
END_BLD_NS

View file

@ -37,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "c_bind.h"
#include "menu.h"
#include "sound.h"
#include "gamestate.h"
bool ShowOptionMenu();
@ -247,7 +248,7 @@ void GameInterface::MenuClosed()
bool GameInterface::CanSave()
{
return (gGameStarted && gPlayer[myconnectindex].pXSprite->health != 0);
return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0);
}
void GameInterface::StartGame(FNewGameStartup& gs)

View file

@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "messages.h"
#include "statistics.h"
#include "gstrings.h"
#include "gamestate.h"
#include "raze_sound.h"
BEGIN_BLD_NS
@ -86,8 +87,7 @@ void CEndGameMgr::ProcessKeys(void)
//}
//else
{
char ch = inputState.keyGetScan();
if (!ch)
if (!inputState.CheckAllInput())
return;
if (gGameOptions.nGameType > 0 || numplayers > 1)
netWaitForEveryone(0);
@ -99,8 +99,7 @@ extern void EndLevel(void);
void CEndGameMgr::Setup(void)
{
at1 = gInputMode;
gInputMode = kInputEndGame;
gamestate = GS_FINALE;
at0 = 1;
STAT_Update(false);
EndLevel();
@ -119,7 +118,6 @@ void CEndGameMgr::Finish(void)
gInitialNetPlayers = numplayers;
soundEngine->StopAllChannels();
gStartNewGame = 1;
gInputMode = (INPUT_MODE)at1;
at0 = 0;
}

View file

@ -29,7 +29,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "blood.h"
#include "globals.h"
#include "credits.h"
#include "endgame.h"
#include "inifile.h"
#include "levels.h"
@ -56,7 +55,6 @@ EPISODEINFO gEpisodeInfo[kMaxEpisodes+1];
int gSkill = 2;
int gEpisodeCount;
int gNextLevel;
bool gGameStarted;
int gLevelTime;
@ -83,35 +81,6 @@ void levelOverrideINI(const char *pzIni)
strcpy(BloodIniFile, pzIni);
}
void levelPlayIntroScene(int nEpisode)
{
gGameOptions.uGameFlags &= ~4;
Mus_SetPaused(true);
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
credPlaySmk(pEpisode->at8f08, pEpisode->at9030, pEpisode->at9028);
viewResizeView(gViewSize);
credReset();
Mus_SetPaused(false);
}
void levelPlayEndScene(int nEpisode)
{
gGameOptions.uGameFlags &= ~8;
Mus_Stop();
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
credPlaySmk(pEpisode->at8f98, pEpisode->at90c0, pEpisode->at902c);
viewResizeView(gViewSize);
credReset();
}
void levelClearSecrets(void)
{
gSecretMgr.Clear();
@ -412,14 +381,12 @@ void LevelsLoadSave::Load(void)
{
Read(&gNextLevel, sizeof(gNextLevel));
Read(&gGameOptions, sizeof(gGameOptions));
Read(&gGameStarted, sizeof(gGameStarted));
}
void LevelsLoadSave::Save(void)
{
Write(&gNextLevel, sizeof(gNextLevel));
Write(&gGameOptions, sizeof(gGameOptions));
Write(&gGameStarted, sizeof(gGameStarted));
}
void LevelsLoadSaveConstruct(void)

View file

@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once
#include "screenjob.h"
#include "common_game.h"
#include "inifile.h"
#include "mapinfo.h"
@ -98,8 +99,8 @@ extern int gLevelTime;
void levelInitINI(const char *pzIni);
void levelOverrideINI(const char *pzIni);
void levelPlayIntroScene(int nEpisode);
void levelPlayEndScene(int nEpisode);
void levelPlayIntroScene(int nEpisode, CompletionFunc completion);
void levelPlayEndScene(int nEpisode, CompletionFunc completion);
void levelSetupSecret(int nCount);
void levelTriggerSecret(int nSecret);
void CheckSectionAbend(const char *pzSection);

View file

@ -44,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "savegamehelp.h"
#include "raze_music.h"
#include "mapinfo.h"
#include "gamestate.h"
#include "aistate.h"
#include "aiunicult.h"
@ -480,7 +481,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
sfxKillAllSounds();
ambKillAll();
seqKillAll();
if (!gGameStarted)
if (gamestate != GS_LEVEL)
{
memset(xsprite, 0, sizeof(xsprite));
}
@ -524,7 +525,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
else
gGameMessageMgr.Clear();
viewSetErrorMessage("");
if (!gGameStarted)
if (gamestate != GS_LEVEL)
{
netWaitForEveryone(0);
memset(gPlayerReady, 0, sizeof(gPlayerReady));
@ -534,7 +535,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
gFrameRate = 0;
totalclock = 0;
paused = 0;
gGameStarted = 1;
gamestate = GS_LEVEL;
bVanilla = false;

View file

@ -337,7 +337,7 @@ void CGameMessageMgr::Display(void)
{
if (VanillaMode())
{
if (numberOfDisplayedMessages && this->state && gInputMode != kInputMessage)
if (numberOfDisplayedMessages && this->state)
{
int initialNrOfDisplayedMsgs = numberOfDisplayedMessages;
int initialMessagesIndex = messagesIndex;
@ -368,7 +368,7 @@ void CGameMessageMgr::Display(void)
}
else
{
if (this->state && gInputMode != kInputMessage)
if (this->state)
{
messageStruct* currentMessages[kMessageLogSize];
int currentMessagesCount = 0;
@ -500,7 +500,6 @@ void CPlayerMsg::Clear(void)
void CPlayerMsg::Term(void)
{
Clear();
gInputMode = kInputGame;
}
void CPlayerMsg::Draw(void)

View file

@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_BLD_NS
void playlogos();
void *ResReadLine(char *buffer, unsigned int nBytes, void **pRes);
unsigned int qrand(void);
int wrand(void);

View file

@ -37,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sound.h"
#include "view.h"
#include "menu.h"
#include "gamestate.h"
extern bool gHaveNetworking;
@ -1326,7 +1327,7 @@ void netPlayerQuit(int nPlayer)
char buffer[128];
sprintf(buffer, "%s left the game with %d frags.", gProfile[nPlayer].name, gPlayer[nPlayer].fragCount);
viewSetMessage(buffer);
if (gGameStarted)
if (gamestate == GS_LEVEL)
{
seqKill(3, gPlayer[nPlayer].pSprite->extra);
actPostSprite(gPlayer[nPlayer].nSprite, kStatFree);

View file

@ -37,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sound.h"
#include "view.h"
#include "mapinfo.h"
#include "gamestate.h"
BEGIN_BLD_NS
@ -89,7 +90,7 @@ static int osdcmd_map(CCmdFuncPtr parm)
static int osdcmd_give(CCmdFuncPtr parm)
{
if (numplayers != 1 || !gGameStarted || gMe->pXSprite->health == 0)
if (numplayers != 1 || gamestate != GS_LEVEL|| gMe->pXSprite->health == 0)
{
Printf("give: Cannot give while dead or not in a single-player game.\n");
return CCMD_OK;
@ -149,7 +150,7 @@ static int osdcmd_give(CCmdFuncPtr parm)
static int osdcmd_god(CCmdFuncPtr UNUSED(parm))
{
UNREFERENCED_CONST_PARAMETER(parm);
if (numplayers == 1 && gGameStarted)
if (numplayers == 1 && gamestate == GS_LEVEL)
{
SetGodMode(!gMe->godMode);
gCheatMgr.m_bPlayerCheated = true;
@ -164,7 +165,7 @@ static int osdcmd_noclip(CCmdFuncPtr UNUSED(parm))
{
UNREFERENCED_CONST_PARAMETER(parm);
if (numplayers == 1 && gGameStarted)
if (numplayers == 1 && gamestate == GS_LEVEL)
{
SetClipMode(!gNoClip);
gCheatMgr.m_bPlayerCheated = true;

View file

@ -225,12 +225,6 @@ void Mus_Stop()
S_StopMusic(true);
}
void Mus_Fade(double seconds)
{
// Todo: Blood uses this, but the streamer cannot currently fade the volume.
Mus_Stop();
}
void Mus_SetPaused(bool on)
{
if (on) S_PauseMusic();

View file

@ -13,7 +13,6 @@ void Mus_UpdateMusic();
int Mus_Play(const char *mapname, const char *fn, bool loop);
void Mus_Stop();
bool Mus_IsPlaying();
void Mus_Fade(double seconds);
void Mus_SetPaused(bool on);
void Mus_ResumeSaved();
FString G_SetupFilenameBasedMusic(const char* fileName, const char *defaultfn);

View file

@ -45,6 +45,7 @@
#include "gamestate.h"
#include "menu.h"
#include "raze_sound.h"
#include "SmackerDecoder.h"
#include "movie/playmve.h"
#include "gamecontrol.h"
@ -52,6 +53,14 @@
IMPLEMENT_CLASS(DScreenJob, true, false)
IMPLEMENT_CLASS(DImageScreen, true, false)
int DBlackScreen::Frame(uint64_t clock, bool skiprequest)
{
int span = int(clock / 1'000'000);
twod->ClearScreen();
return span < wait ? 1 : -1;
}
//---------------------------------------------------------------------------
//
//
@ -69,7 +78,7 @@ int DImageScreen::Frame(uint64_t clock, bool skiprequest)
twod->ClearScreen();
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, 3, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
// Only end after having faded out.
return skiprequest ? -1 : 1;
return skiprequest ? -1 : span > waittime? 0 : 1;
}
//---------------------------------------------------------------------------
@ -212,6 +221,103 @@ public:
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DSmkPlayer : public DScreenJob
{
SmackerHandle hSMK{};
uint32_t nWidth, nHeight;
uint8_t palette[768];
AnimTextures animtex;
TArray<uint8_t> pFrame;
int nFrameRate;
int nFrames;
bool fullscreenScale;
uint64_t nFrameNs;
int nFrame = 0;
const AnimSound* animSnd;
FString filename;
public:
bool isvalid() { return hSMK.isValid; }
DSmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport)
{
hSMK = Smacker_Open(fn);
if (!hSMK.isValid)
{
return;
}
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
nFrameRate = Smacker_GetFrameRate(hSMK);
nFrameNs = 1'000'000'000 / nFrameRate;
nFrames = Smacker_GetNumFrames(hSMK);
Smacker_GetPalette(hSMK, palette);
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
animSnd = ans;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int Frame(uint64_t clock, bool skiprequest) override
{
int frame = clock / nFrameNs;
if (clock == 0)
{
animtex.SetSize(AnimTexture::Paletted, nWidth, nHeight);
}
twod->ClearScreen();
if (frame > nFrame)
{
Smacker_GetPalette(hSMK, palette);
Smacker_GetFrame(hSMK, pFrame.Data());
animtex.SetFrame(palette, pFrame.Data());
}
if (fullscreenScale)
{
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, 3, TAG_DONE);
}
else
{
DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE);
}
if (frame > nFrame)
{
nFrame++;
Smacker_GetNextFrame(hSMK);
for (int i = 0; animSnd[i].framenum >= 0; i++)
{
if (animSnd[i].framenum == nFrame)
{
int sound = animSnd[i].soundnum;
if (sound == -1)
soundEngine->StopAllChannels();
else
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_UI, sound, 1.f, ATTN_NONE);
}
}
}
return skiprequest ? -1 : nFrame < nFrames ? 1 : 0;
}
void OnDestroy() override
{
Smacker_Close(hSMK);
soundEngine->StopAllChannels();
}
};
//---------------------------------------------------------------------------
//
//
@ -228,6 +334,13 @@ DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* fra
auto fr = fileSystem.OpenFileReader(filename);
if (!fr.isOpen())
{
int nLen = strlen(filename);
// Strip the drive letter and retry.
if (nLen >= 3 && isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
{
filename += 3;
fr = fileSystem.OpenFileReader(filename);
}
Printf("%s: Unable to open video\n", filename);
return nothing();
}
@ -243,13 +356,21 @@ DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* fra
{
Printf("%s: invalid ANM file.\n", filename);
anm->Destroy();
return nothing();
return nothing();
}
return anm;
}
else if (!memcmp(id, "SMK2", 4))
{
// todo
fr.Close();
auto anm = Create<DSmkPlayer>(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently.
if (!anm->isvalid())
{
Printf("%s: invalid SMK file.\n", filename);
anm->Destroy();
return nothing();
}
return anm;
}
else if (!memcmp(id, "Interplay MVE File", 18))
{
@ -317,7 +438,6 @@ public:
{
for (auto& job : jobs)
{
job.job->Destroy();
job.job->ObjectFlags |= OF_YesReallyDelete;
delete job.job;
}
@ -326,9 +446,17 @@ public:
void AdvanceJob(bool skip)
{
if (index >= 0 && jobs[index].postAction) jobs[index].postAction();
if (index >= 0)
{
if (jobs[index].postAction) jobs[index].postAction();
jobs[index].job->Destroy();
}
index++;
while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped))) index++;
while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped)))
{
if (jobs[index].job != nullptr) jobs[index].job->Destroy();
index++;
}
actionState = clearbefore ? State_Clear : State_Run;
if (index < jobs.Size()) screenfade = jobs[index].job->fadestyle & DScreenJob::fadein ? 0.f : 1.f;
startTime = -1;

View file

@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include "dobject.h"
#include "v_2ddrawer.h"
using CompletionFunc = std::function<void(bool)>;
struct JobDesc;
@ -47,20 +48,36 @@ public:
//
//---------------------------------------------------------------------------
class DBlackScreen : public DScreenJob
{
int wait;
public:
DBlackScreen(int w) : wait(w) {}
int Frame(uint64_t clock, bool skiprequest) override;
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DImageScreen : public DScreenJob
{
DECLARE_CLASS(DImageScreen, DScreenJob)
int tilenum = -1;
int waittime; // in ms.
FGameTexture* tex = nullptr;
public:
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout) : DScreenJob(fade)
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000) : DScreenJob(fade), waittime(wait)
{
tex = tile;
}
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout) : DScreenJob(fade)
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000) : DScreenJob(fade), waittime(wait)
{
tilenum = tile;
}

View file

@ -373,26 +373,6 @@ public:
//
//---------------------------------------------------------------------------
class DBlackScreen : public DScreenJob
{
int wait;
public:
DBlackScreen(int w) : wait(w) {}
int Frame(uint64_t clock, bool skiprequest)
{
int span = int(clock / 1'000'000);
twod->ClearScreen();
return span < wait ? 1 : -1;
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DEpisode3End : public DImageScreen
{
int sound = 0;