raze-gles/source/sw/src/demo.cpp
Christoph Oelckers 0157446ad1 - thorough cleanup of the Shadow Warrior music code.
This was one huge mess where nothing fit together.
Also added an enhancement that the CD Audio boss theme tracks are also played when CD music is generally off, because these have no equivalent in MIDI. This needs to be checked if it's stylistically ok, though.
2019-12-07 19:57:19 +01:00

624 lines
14 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
This file is part of Shadow Warrior version 1.2
Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "ns.h"
//#define MAIN
//#define QUIET
#include "build.h"
#include "cache1d.h"
#include "keys.h"
#include "names2.h"
#include "panel.h"
#include "game.h"
#include "network.h"
#include "mytypes.h"
#include "control.h"
#include "gamecontrol.h"
#include "demo.h"
#include "player.h"
#include "menus.h"
BEGIN_SW_NS
DFILE DemoFileIn;
FILE *DemoFileOut;
SWBOOL DemoPlaying = FALSE;
SWBOOL DemoRecording = FALSE;
SWBOOL DemoEdit = FALSE;
SWBOOL DemoMode = FALSE;
SWBOOL DemoModeMenuState = FALSE;
SWBOOL DemoOverride = FALSE;
char DemoFileName[16] = "demo.dmo";
char DemoLevelName[16] = "";
extern SWBOOL NewGame;
// Demo sync stuff
FILE *DemoSyncFile;
SWBOOL DemoSyncTest = FALSE, DemoSyncRecord = FALSE;
char DemoTmpName[16] = "";
SW_PACKET DemoBuffer[DEMO_BUFFER_MAX];
int DemoRecCnt = 0; // Can only record 1-player game
SWBOOL DemoDone;
void DemoWriteHeader(void);
void DemoReadHeader(void);
void DemoReadBuffer(void);
//
// DemoDebug Vars
//
// DemoDebugMode will close the file after every write
SWBOOL DemoDebugMode = FALSE;
//SWBOOL DemoDebugMode = TRUE;
SWBOOL DemoInitOnce = FALSE;
short DemoDebugBufferMax = 1;
extern char LevelName[];
extern uint8_t FakeMultiNumPlayers;
extern SWBOOL QuitFlag;
///////////////////////////////////////////
//
// Demo File Manipulation
//
///////////////////////////////////////////
char *DemoSyncFileName(void)
{
static char file_name[32];
char *ptr;
strcpy(file_name, DemoFileName);
if ((ptr = strchr(file_name, '.')) == 0)
strcat(file_name, ".dms");
else
{
*ptr = '\0';
strcat(file_name, ".dms");
}
return file_name;
}
void
DemoSetup(void)
{
if (DemoRecording)
{
if (DemoSyncRecord)
DemoSyncFile = fopen(DemoSyncFileName(),"wb");
DemoWriteHeader();
memset(&DemoBuffer, -1, sizeof(DemoBuffer));
}
if (DemoPlaying)
{
if (DemoSyncRecord)
DemoSyncFile = fopen(DemoSyncFileName(),"wb");
if (DemoSyncTest)
DemoSyncFile = fopen(DemoSyncFileName(),"rb");
DemoReadHeader();
memset(&DemoBuffer, -1, sizeof(DemoBuffer));
DemoReadBuffer();
}
}
void
DemoRecordSetup(void)
{
if (DemoRecording)
{
if (DemoSyncRecord)
DemoSyncFile = fopen(DemoSyncFileName(),"wb");
DemoWriteHeader();
memset(&DemoBuffer, -1, sizeof(DemoBuffer));
}
}
void
DemoPlaySetup(void)
{
if (DemoPlaying)
{
if (DemoSyncRecord)
DemoSyncFile = fopen(DemoSyncFileName(),"wb");
if (DemoSyncTest)
DemoSyncFile = fopen(DemoSyncFileName(),"rb");
DemoReadHeader();
memset(&DemoBuffer, -1, sizeof(DemoBuffer));
DemoReadBuffer();
}
}
void
DemoWriteHeader(void)
{
DEMO_HEADER dh;
DEMO_START_POS dsp;
PLAYERp pp;
DemoFileOut = fopen(DemoFileName, "wb");
if (!DemoFileOut)
return;
strcpy(dh.map_name, LevelName);
strcpy(dh.LevelSong, "");
dh.Level = Level;
if (FakeMultiNumPlayers)
dh.numplayers = FakeMultiNumPlayers;
else
dh.numplayers = numplayers;
fwrite(&dh, sizeof(dh), 1, DemoFileOut);
for (pp = Player; pp < Player + dh.numplayers; pp++)
{
dsp.x = pp->posx;
dsp.y = pp->posy;
dsp.z = pp->posz;
fwrite(&dsp, sizeof(dsp), 1, DemoFileOut);
fwrite(&pp->Flags, sizeof(pp->Flags), 1, DemoFileOut);
fwrite(&pp->pang, sizeof(pp->pang), 1, DemoFileOut);
}
fwrite(&Skill, sizeof(Skill), 1, DemoFileOut);
fwrite(&gNet, sizeof(gNet), 1, DemoFileOut);
if (DemoDebugMode)
{
DemoDebugBufferMax = numplayers;
fclose(DemoFileOut);
}
}
void
DemoReadHeader(void)
{
DEMO_HEADER dh;
DEMO_START_POS dsp;
PLAYERp pp;
#if DEMO_FILE_TYPE != DEMO_FILE_GROUP
if (DemoEdit)
{
DemoFileIn = fopen(DemoFileName, "rb+");
}
else
#endif
{
//DemoFileIn = fopen(DemoFileName, "rb");
DemoFileIn = DOPEN_READ(DemoFileName);
}
if (DF_ERR(DemoFileIn))
{
TerminateGame();
printf("File %s is not a valid demo file.",DemoFileName);
exit(0);
}
DREAD(&dh, sizeof(dh), 1, DemoFileIn);
strcpy(DemoLevelName, dh.map_name);
Level = dh.Level;
if (dh.numplayers > 1)
{
FakeMultiNumPlayers = dh.numplayers;
}
else
numplayers = dh.numplayers;
for (pp = Player; pp < Player + dh.numplayers; pp++)
{
DREAD(&dsp, sizeof(dsp), 1, DemoFileIn);
pp->posx = dsp.x;
pp->posy = dsp.y;
pp->posz = dsp.z;
COVERupdatesector(pp->posx, pp->posy, &pp->cursectnum);
//pp->cursectnum = 0;
//updatesectorz(pp->posx, pp->posy, pp->posz, &pp->cursectnum);
DREAD(&pp->Flags, sizeof(pp->Flags), 1, DemoFileIn);
DREAD(&pp->pang, sizeof(pp->pang), 1, DemoFileIn);
}
DREAD(&Skill, sizeof(Skill), 1, DemoFileIn);
DREAD(&gNet, sizeof(gNet), 1, DemoFileIn);
}
void
DemoDebugWrite(void)
{
int size;
DemoFileOut = fopen(DemoFileName, "ab");
ASSERT(DemoFileOut);
size = sizeof(SW_PACKET) * DemoDebugBufferMax;
fwrite(&DemoBuffer, size, 1, DemoFileOut);
memset(&DemoBuffer, -1, size);
fclose(DemoFileOut);
}
void
DemoWriteBuffer(void)
{
fwrite(&DemoBuffer, sizeof(DemoBuffer), 1, DemoFileOut);
memset(&DemoBuffer, -1, sizeof(DemoBuffer));
}
void
DemoReadBuffer(void)
{
memset(&DemoBuffer, -1, sizeof(DemoBuffer));
DREAD(&DemoBuffer, sizeof(DemoBuffer), 1, DemoFileIn);
}
void
DemoBackupBuffer(void)
{
#if DEMO_FILE_TYPE != DEMO_FILE_GROUP
FILE *NewDemoFile;
FILE *OldDemoFile = DemoFileIn;
int pos,i;
char copy_buffer;
char NewDemoFileName[16] = "!";
// seek backwards to beginning of last buffer
fseek(OldDemoFile, -sizeof(DemoBuffer), SEEK_CUR);
pos = ftell(OldDemoFile);
// open a new edit file
strcat(NewDemoFileName, DemoFileName);
NewDemoFile = fopen(NewDemoFileName, "wb");
rewind(OldDemoFile);
// copy old demo to new demo
for (i = 0; i < pos; i++)
{
fread(&copy_buffer, sizeof(copy_buffer), 1, OldDemoFile);
fwrite(&copy_buffer,sizeof(copy_buffer), 1, NewDemoFile);
}
DemoFileOut = NewDemoFile;
fclose(OldDemoFile);
#endif
}
void
DemoTerm(void)
{
if (DemoRecording)
{
// if already closed
if (DemoFileOut == NULL)
return;
if (DemoDebugMode)
{
DemoFileOut = fopen(DemoFileName, "ab");
ASSERT(DemoFileOut);
}
else
{
// paste on a -1 record to the current buffer
if (DemoRecCnt < DEMO_BUFFER_MAX)
memset(&DemoBuffer[DemoRecCnt], -1, sizeof(DemoBuffer[DemoRecCnt]));
DemoWriteBuffer();
}
// write at least 1 record at the end filled with -1
// just for good measure
memset(&DemoBuffer[0], -1, sizeof(DemoBuffer[0]));
fwrite(&DemoBuffer[0], sizeof(DemoBuffer[0]), 1, DemoFileOut);
fclose(DemoFileOut);
DemoFileOut = NULL;
}
if (DemoPlaying)
{
if (DF_ERR(DemoFileIn))
return;
DCLOSE(DemoFileIn);
}
if (DemoSyncTest||DemoSyncRecord)
{
fclose(DemoSyncFile);
DemoSyncFile = NULL;
}
}
///////////////////////////////////////////
//
// Demo Play Back
//
///////////////////////////////////////////
void
DemoPlayBack(void)
{
int pnum, cnt;
static int buf_ndx;
PLAYERp pp;
ControlInfo info;
int Xdim, Ydim, ScreenSize;
// Initialize Game part of network code (When ready2send != 0)
InitNetVars();
// IMPORTANT - MUST be right before game loop
InitTimingVars();
// THIS STUFF DEPENDS ON MYCONNECTINDEX BEING SET RIGHT
pp = Player + myconnectindex;
SetRedrawScreen(pp);
if (!DemoInitOnce)
buf_ndx = 0;
// everything has been inited at least once for PLAYBACK
DemoInitOnce = TRUE;
cnt = 0;
ready2send = 0;
DemoDone = FALSE;
while (TRUE)
{
// makes code run at the same rate
while (totalclock > totalsynctics)
{
handleevents();
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ-1)] = DemoBuffer[buf_ndx];
pp->movefifoend++;
buf_ndx++;
if (pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ-1)].bits == -1)
{
DemoDone = TRUE;
break;
}
if (buf_ndx > DEMO_BUFFER_MAX - 1)
{
DemoReadBuffer();
buf_ndx = 0;
}
}
if (DemoDone)
break;
cnt++;
CONTROL_GetInput(&info);
domovethings();
// fast forward and slow mo
if (DemoEdit)
{
if (inputState.GetKeyStatus(KEYSC_F))
{
if (inputState.GetKeyStatus(KEYSC_LSHIFT) || inputState.GetKeyStatus(KEYSC_RSHIFT))
totalclock += synctics;
else
totalclock += synctics-1;
}
if (inputState.GetKeyStatus(KEYSC_S))
totalclock += 1-synctics;
}
else
{
if (buttonMap.ButtonDown(gamefunc_See_Coop_View))
{
buttonMap.ClearButton(gamefunc_See_Coop_View);
screenpeek = connectpoint2[screenpeek];
if (screenpeek < 0)
screenpeek = connecthead;
}
}
if (DemoSyncRecord)
demosync_record();
if (DemoSyncTest)
demosync_test(cnt);
}
// Put this back in later when keyboard stuff is stable
if (DemoEdit)
{
//CONTROL_GetButtonInput();
CONTROL_GetInput(&info);
// if a key is pressed, start recording from the point the key
// was pressed
if (buttonMap.ButtonDown(gamefunc_Move_Forward) ||
buttonMap.ButtonDown(gamefunc_Move_Backward) ||
buttonMap.ButtonDown(gamefunc_Turn_Left) ||
buttonMap.ButtonDown(gamefunc_Turn_Right) ||
buttonMap.ButtonDown(gamefunc_Fire) ||
buttonMap.ButtonDown(gamefunc_Open) ||
buttonMap.ButtonDown(gamefunc_Jump) ||
buttonMap.ButtonDown(gamefunc_Crouch) ||
buttonMap.ButtonDown(gamefunc_Look_Up) ||
buttonMap.ButtonDown(gamefunc_Look_Down))
{
DemoBackupBuffer();
DemoRecCnt = buf_ndx;
DemoPlaying = FALSE;
DemoRecording = TRUE;
return;
}
}
if (buttonMap.ButtonDown(gamefunc_See_Co_Op_View))
{
screenpeek += 1;
if (screenpeek > numplayers-1)
screenpeek = 0;
}
// demo is over
if (DemoDone)
break;
if (QuitFlag)
{
DemoMode = FALSE;
break;
}
if (ExitLevel)
{
// Quiting Demo
ExitLevel = FALSE;
if (DemoMode)
{
DemoPlaying = FALSE;
DemoMode = FALSE;
}
break;
}
drawscreen(Player + screenpeek);
}
// only exit if conditions are write
if (DemoDone && !DemoMode && !NewGame)
{
TerminateLevel();
TerminateGame();
exit(0);
}
}
//
// Still using old method of playback - this was for opening demo
//
void
ScenePlayBack(void)
{
int buf_ndx, pnum, cnt;
PLAYERp pp;
if (SW_SHAREWARE)
{
// code here needs to be similar to RunLevel startup code
PlaySong(nullptr, "yokoha03.mid", -1);
}
// IMPORTANT - MUST be right before game loop
InitTimingVars();
buf_ndx = 0;
cnt = 0;
ready2send = 0;
DemoDone = FALSE;
ResetKeys();
while (TRUE)
{
// makes code run at the same rate
while ((totalclock > totalsynctics))
{
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)] = DemoBuffer[buf_ndx];
pp->movefifoend++;
buf_ndx++;
if (pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)].bits == -1)
{
DemoDone = TRUE;
break;
}
if (buf_ndx > DEMO_BUFFER_MAX - 1)
{
DemoReadBuffer();
buf_ndx = 0;
}
}
if (KeyPressed())
DemoDone = TRUE;
if (DemoDone)
break;
cnt++;
//movethings();
domovethings();
}
// demo is over
if (DemoDone)
break;
drawscreen(Player + screenpeek);
}
}
END_SW_NS