raze/source/blood/src/blood.cpp

953 lines
25 KiB
C++
Raw Normal View History

2019-09-19 22:42:45 +00:00
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#include "ns.h" // Must come before everything else!
2019-09-19 22:42:45 +00:00
#include "build.h"
#include "mmulti.h"
#include "compat.h"
2019-12-29 16:04:38 +00:00
#include "baselayer.h"
2019-09-19 22:42:45 +00:00
#include "common.h"
#include "common_game.h"
#include "g_input.h"
2019-09-19 22:42:45 +00:00
#include "db.h"
#include "blood.h"
#include "choke.h"
#include "controls.h"
#include "dude.h"
#include "endgame.h"
#include "eventq.h"
#include "fx.h"
#include "gib.h"
2019-09-19 22:42:45 +00:00
#include "globals.h"
#include "levels.h"
#include "loadsave.h"
#include "network.h"
#include "sectorfx.h"
#include "seq.h"
#include "sound.h"
#include "triggers.h"
#include "view.h"
#include "misc.h"
#include "gameconfigfile.h"
#include "gamecontrol.h"
#include "m_argv.h"
#include "statistics.h"
#include "menu.h"
#include "raze_sound.h"
#include "nnexts.h"
#include "secrets.h"
#include "gamestate.h"
#include "screenjob.h"
#include "mapinfo.h"
2019-09-19 22:42:45 +00:00
BEGIN_BLD_NS
void LocalKeys(void);
void InitCheats();
2019-09-19 22:42:45 +00:00
bool bNoDemo = false;
char gUserMapFilename[BMAX_PATH];
short BloodVersion = 0x115;
int gNetPlayers;
int gChokeCounter = 0;
double g_gameUpdateTime, g_gameUpdateAndDrawTime;
double g_gameUpdateAvgTime = 0.001;
bool gQuitGame;
int gQuitRequest;
2019-09-19 22:42:45 +00:00
enum gametokens
{
T_INCLUDE = 0,
T_INTERFACE = 0,
T_LOADGRP = 1,
T_MODE = 1,
T_CACHESIZE = 2,
T_ALLOW = 2,
T_NOAUTOLOAD,
T_INCLUDEDEFAULT,
T_SOUND,
T_FILE,
//T_CUTSCENE,
//T_ANIMSOUNDS,
//T_NOFLOORPALRANGE,
T_ID,
T_MINPITCH,
T_MAXPITCH,
T_PRIORITY,
T_TYPE,
T_DISTANCE,
T_VOLUME,
T_DELAY,
T_RENAMEFILE,
T_GLOBALGAMEFLAGS,
T_ASPECT,
T_FORCEFILTER,
T_FORCENOFILTER,
T_TEXTUREFILTER,
T_RFFDEFINEID,
T_TILEFROMTEXTURE,
T_IFCRC, T_IFMATCH, T_CRC32,
T_SIZE,
T_SURFACE,
T_VOXEL,
T_VIEW,
T_SHADE,
2019-09-19 22:42:45 +00:00
};
int blood_globalflags;
void QuitGame(void)
{
throw CExitEvent(0);
2019-09-19 22:42:45 +00:00
}
void PrecacheDude(spritetype *pSprite)
{
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID);
seqPrecacheId(pDudeInfo->seqStartID+5);
seqPrecacheId(pDudeInfo->seqStartID+1);
seqPrecacheId(pDudeInfo->seqStartID+2);
switch (pSprite->type)
{
case kDudeCultistTommy:
case kDudeCultistShotgun:
case kDudeCultistTesla:
case kDudeCultistTNT:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
seqPrecacheId(pDudeInfo->seqStartID+8);
seqPrecacheId(pDudeInfo->seqStartID+9);
seqPrecacheId(pDudeInfo->seqStartID+13);
seqPrecacheId(pDudeInfo->seqStartID+14);
seqPrecacheId(pDudeInfo->seqStartID+15);
break;
case kDudeZombieButcher:
case kDudeGillBeast:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
seqPrecacheId(pDudeInfo->seqStartID+8);
seqPrecacheId(pDudeInfo->seqStartID+9);
seqPrecacheId(pDudeInfo->seqStartID+10);
seqPrecacheId(pDudeInfo->seqStartID+11);
break;
case kDudeGargoyleStatueFlesh:
case kDudeGargoyleStatueStone:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+6);
fallthrough__;
case kDudeGargoyleFlesh:
case kDudeGargoyleStone:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
seqPrecacheId(pDudeInfo->seqStartID+8);
seqPrecacheId(pDudeInfo->seqStartID+9);
break;
case kDudePhantasm:
case kDudeHellHound:
case kDudeSpiderBrown:
case kDudeSpiderRed:
case kDudeSpiderBlack:
case kDudeSpiderMother:
case kDudeTchernobog:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
seqPrecacheId(pDudeInfo->seqStartID+8);
break;
case kDudeCerberusTwoHead:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
fallthrough__;
case kDudeHand:
case kDudeBoneEel:
case kDudeBat:
case kDudeRat:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
break;
case kDudeCultistBeast:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
break;
case kDudeZombieAxeBuried:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+12);
seqPrecacheId(pDudeInfo->seqStartID+9);
fallthrough__;
case kDudeZombieAxeLaying:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+10);
fallthrough__;
case kDudeZombieAxeNormal:
2019-09-19 22:42:45 +00:00
seqPrecacheId(pDudeInfo->seqStartID+6);
seqPrecacheId(pDudeInfo->seqStartID+7);
seqPrecacheId(pDudeInfo->seqStartID+8);
seqPrecacheId(pDudeInfo->seqStartID+11);
seqPrecacheId(pDudeInfo->seqStartID+13);
seqPrecacheId(pDudeInfo->seqStartID+14);
break;
}
}
void PrecacheThing(spritetype *pSprite) {
switch (pSprite->type) {
case kThingGlassWindow: // worthless...
case kThingFluorescent:
seqPrecacheId(12);
break;
case kThingSpiderWeb:
seqPrecacheId(15);
break;
case kThingMetalGrate:
seqPrecacheId(21);
break;
case kThingFlammableTree:
seqPrecacheId(25);
seqPrecacheId(26);
break;
case kTrapMachinegun:
seqPrecacheId(38);
seqPrecacheId(40);
seqPrecacheId(28);
break;
case kThingObjectGib:
//case kThingObjectExplode: weird that only gib object is precached and this one is not
break;
2019-09-19 22:42:45 +00:00
}
tilePrecacheTile(pSprite->picnum);
2019-09-19 22:42:45 +00:00
}
void PreloadTiles(void)
{
2020-04-11 07:12:23 +00:00
nPrecacheCount = 0;
2019-09-19 22:42:45 +00:00
int skyTile = -1;
memset(gotpic,0,sizeof(gotpic));
2020-01-02 09:45:31 +00:00
// Fonts
2019-09-19 22:42:45 +00:00
for (int i = 0; i < numsectors; i++)
{
tilePrecacheTile(sector[i].floorpicnum, 0);
tilePrecacheTile(sector[i].ceilingpicnum, 0);
if ((sector[i].ceilingstat&1) != 0 && skyTile == -1)
skyTile = sector[i].ceilingpicnum;
}
for (int i = 0; i < numwalls; i++)
{
tilePrecacheTile(wall[i].picnum, 0);
if (wall[i].overpicnum >= 0)
tilePrecacheTile(wall[i].overpicnum, 0);
}
for (int i = 0; i < kMaxSprites; i++)
{
if (sprite[i].statnum < kMaxStatus)
{
spritetype *pSprite = &sprite[i];
switch (pSprite->statnum)
{
case kStatDude:
2019-09-19 22:42:45 +00:00
PrecacheDude(pSprite);
break;
case kStatThing:
2019-09-19 22:42:45 +00:00
PrecacheThing(pSprite);
break;
default:
tilePrecacheTile(pSprite->picnum);
break;
}
}
}
// Precache common SEQs
for (int i = 0; i < 100; i++)
2019-09-19 22:42:45 +00:00
{
seqPrecacheId(i);
2019-09-19 22:42:45 +00:00
}
tilePrecacheTile(1147); // water drip
tilePrecacheTile(1160); // blood drip
// Player SEQs
seqPrecacheId(dudeInfo[31].seqStartID+6);
seqPrecacheId(dudeInfo[31].seqStartID+7);
seqPrecacheId(dudeInfo[31].seqStartID+8);
seqPrecacheId(dudeInfo[31].seqStartID+9);
seqPrecacheId(dudeInfo[31].seqStartID+10);
seqPrecacheId(dudeInfo[31].seqStartID+14);
seqPrecacheId(dudeInfo[31].seqStartID+15);
seqPrecacheId(dudeInfo[31].seqStartID+12);
seqPrecacheId(dudeInfo[31].seqStartID+16);
seqPrecacheId(dudeInfo[31].seqStartID+17);
seqPrecacheId(dudeInfo[31].seqStartID+18);
2019-09-19 22:42:45 +00:00
if (skyTile > -1 && skyTile < kMaxTiles)
{
for (int i = 1; i < gSkyCount; i++)
tilePrecacheTile(skyTile+i, 0);
}
WeaponPrecache();
viewPrecacheTiles();
fxPrecache();
gibPrecache();
I_GetEvent();
2019-09-19 22:42:45 +00:00
}
void PreloadCache(void)
{
PreloadTiles();
int cnt = 0;
int percentDisplayed = -1;
for (int i = 0; i < kMaxTiles; i++)
2019-09-19 22:42:45 +00:00
{
if (TestBitString(gotpic, i))
{
// For the hardware renderer precaching the raw pixel data is pointless.
if (videoGetRenderMode() < REND_POLYMOST)
tileLoad(i);
2019-09-19 22:42:45 +00:00
2019-10-23 12:39:33 +00:00
if (r_precache) PrecacheHardwareTextures(i);
2019-09-19 22:42:45 +00:00
if ((++cnt & 7) == 0)
I_GetEvent();
}
}
2019-09-19 22:42:45 +00:00
memset(gotpic,0,sizeof(gotpic));
}
void EndLevel(void)
{
gViewPos = VIEWPOS_0;
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
}
2019-09-19 22:42:45 +00:00
PLAYER gPlayerTemp[kMaxPlayers];
int gHealthTemp[kMaxPlayers];
vec3_t startpos;
int16_t startang, startsectnum;
void StartLevel(MapRecord *level)
2019-09-19 22:42:45 +00:00
{
if (!level) return;
STAT_Update(0);
2019-09-19 22:42:45 +00:00
EndLevel();
gInput = {};
gStartNewGame = nullptr;
2019-09-19 22:42:45 +00:00
ready2send = 0;
netWaitForEveryone(0);
currentLevel = level;
2019-09-19 22:42:45 +00:00
if (gGameOptions.nGameType == 0)
{
///////
gGameOptions.weaponsV10x = gWeaponsV10x;
///////
2019-09-19 22:42:45 +00:00
}
#if 0
2019-09-19 22:42:45 +00:00
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags&1))
{
// todo
gBlueFlagDropped = false;
gRedFlagDropped = false;
2019-09-19 22:42:45 +00:00
}
#endif
if (gGameOptions.uGameFlags&1)
2019-09-19 22:42:45 +00:00
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
memcpy(&gPlayerTemp[i],&gPlayer[i],sizeof(PLAYER));
gHealthTemp[i] = xsprite[gPlayer[i].pSprite->extra].health;
}
}
bVanilla = false;
enginecompatibility_mode = ENGINECOMPATIBILITY_19960925;//bVanilla;
2019-09-19 22:42:45 +00:00
memset(xsprite,0,sizeof(xsprite));
memset(sprite,0,kMaxSprites*sizeof(spritetype));
//drawLoadingScreen();
if (dbLoadMap(currentLevel->fileName,(int*)&startpos.x,(int*)&startpos.y,(int*)&startpos.z,&startang,&startsectnum,nullptr))
{
I_Error("Unable to load map");
}
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
STAT_NewLevel(currentLevel->fileName);
G_LoadMapHack(currentLevel->fileName);
wsrand(dbReadMapCRC(currentLevel->LabelName()));
2019-09-19 22:42:45 +00:00
gKillMgr.Clear();
gSecretMgr.Clear();
gLevelTime = 0;
automapping = 1;
int modernTypesErased = 0;
2019-09-19 22:42:45 +00:00
for (int i = 0; i < kMaxSprites; i++)
{
spritetype *pSprite = &sprite[i];
if (pSprite->statnum < kMaxStatus && pSprite->extra > 0) {
2019-09-19 22:42:45 +00:00
XSPRITE *pXSprite = &xsprite[pSprite->extra];
if ((pXSprite->lSkill & (1 << gGameOptions.nDifficulty)) || (pXSprite->lS && gGameOptions.nGameType == 0)
|| (pXSprite->lB && gGameOptions.nGameType == 2) || (pXSprite->lT && gGameOptions.nGameType == 3)
|| (pXSprite->lC && gGameOptions.nGameType == 1)) {
2019-09-19 22:42:45 +00:00
DeleteSprite(i);
continue;
}
#ifdef NOONE_EXTENSIONS
if (!gModernMap && nnExtEraseModernStuff(pSprite, pXSprite))
modernTypesErased++;
#endif
}
}
#ifdef NOONE_EXTENSIONS
if (!gModernMap)
Printf(PRINT_NONOTIFY, "> Modern types erased: %d.\n", modernTypesErased);
#endif
2019-09-19 22:42:45 +00:00
startpos.z = getflorzofslope(startsectnum,startpos.x,startpos.y);
for (int i = 0; i < kMaxPlayers; i++) {
2019-09-19 22:42:45 +00:00
gStartZone[i].x = startpos.x;
gStartZone[i].y = startpos.y;
gStartZone[i].z = startpos.z;
gStartZone[i].sectnum = startsectnum;
gStartZone[i].ang = startang;
#ifdef NOONE_EXTENSIONS
// Create spawn zones for players in teams mode.
if (gModernMap && i <= kMaxPlayers / 2) {
2019-09-19 22:42:45 +00:00
gStartZoneTeam1[i].x = startpos.x;
gStartZoneTeam1[i].y = startpos.y;
gStartZoneTeam1[i].z = startpos.z;
gStartZoneTeam1[i].sectnum = startsectnum;
gStartZoneTeam1[i].ang = startang;
gStartZoneTeam2[i].x = startpos.x;
gStartZoneTeam2[i].y = startpos.y;
gStartZoneTeam2[i].z = startpos.z;
gStartZoneTeam2[i].sectnum = startsectnum;
gStartZoneTeam2[i].ang = startang;
}
#endif
2019-09-19 22:42:45 +00:00
}
InitSectorFX();
warpInit();
actInit(false);
2019-09-19 22:42:45 +00:00
evInit();
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
if (!(gGameOptions.uGameFlags&1))
2019-09-19 22:42:45 +00:00
{
if (numplayers == 1)
{
gProfile[i].skill = gSkill;
2019-10-21 22:05:21 +00:00
gProfile[i].nAutoAim = cl_autoaim;
2019-10-22 00:31:14 +00:00
gProfile[i].nWeaponSwitch = cl_weaponswitch;
2019-09-19 22:42:45 +00:00
}
playerInit(i,0);
}
playerStart(i, 1);
2019-09-19 22:42:45 +00:00
}
if (gGameOptions.uGameFlags&1)
2019-09-19 22:42:45 +00:00
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
PLAYER *pPlayer = &gPlayer[i];
pPlayer->pXSprite->health &= 0xf000;
pPlayer->pXSprite->health |= gHealthTemp[i];
pPlayer->weaponQav = gPlayerTemp[i].weaponQav;
pPlayer->curWeapon = gPlayerTemp[i].curWeapon;
pPlayer->weaponState = gPlayerTemp[i].weaponState;
pPlayer->weaponAmmo = gPlayerTemp[i].weaponAmmo;
pPlayer->qavCallback = gPlayerTemp[i].qavCallback;
pPlayer->qavLoop = gPlayerTemp[i].qavLoop;
pPlayer->weaponTimer = gPlayerTemp[i].weaponTimer;
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
2019-09-19 22:42:45 +00:00
}
}
gGameOptions.uGameFlags &= ~3;
2019-09-19 22:42:45 +00:00
PreloadCache();
InitMirrors();
gFrameClock = 0;
trInit();
if (!bVanilla && !gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
2019-09-19 22:42:45 +00:00
sfxSetReverb(0);
ambInit();
netReset();
gFrameCount = 0;
2019-09-19 22:42:45 +00:00
gChokeCounter = 0;
M_ClearMenus();
2019-09-19 22:42:45 +00:00
// viewSetMessage("");
viewSetErrorMessage("");
netWaitForEveryone(0);
gameclock = 0;
lastTic = -1;
2020-05-29 01:20:40 +00:00
paused = 0;
2019-09-19 22:42:45 +00:00
ready2send = 1;
levelTryPlayMusic();
2019-09-19 22:42:45 +00:00
}
2019-09-24 02:14:59 +00:00
2019-09-19 22:42:45 +00:00
bool gRestartGame = false;
void ProcessFrame(void)
{
char buffer[128];
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
gPlayer[i].input.syncFlags.value &= ~flag_buttonmask;
gPlayer[i].input.syncFlags.value |= gFifoInput[gNetFifoTail & 255][i].syncFlags.value;
2020-08-26 22:21:11 +00:00
int newweap = gFifoInput[gNetFifoTail & 255][i].getNewWeapon();
if (newweap) gPlayer[i].newWeapon = newweap;
gPlayer[i].input.fvel = gFifoInput[gNetFifoTail&255][i].fvel;
gPlayer[i].input.q16avel = gFifoInput[gNetFifoTail&255][i].q16avel;
gPlayer[i].input.svel = gFifoInput[gNetFifoTail&255][i].svel;
gPlayer[i].input.q16horz = gFifoInput[gNetFifoTail&255][i].q16horz;
2019-09-19 22:42:45 +00:00
}
gNetFifoTail++;
2020-08-25 21:04:08 +00:00
2019-09-19 22:42:45 +00:00
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
if (gPlayer[i].input.syncFlags.quit)
2019-09-19 22:42:45 +00:00
{
gPlayer[i].input.syncFlags.quit = 0;
2019-09-19 22:42:45 +00:00
netBroadcastPlayerLogoff(i);
if (i == myconnectindex)
{
// netBroadcastMyLogoff(gQuitRequest == 2);
gQuitGame = true;
gRestartGame = gQuitRequest == 2;
netDeinitialize();
netResetToSinglePlayer();
return;
}
}
if (gPlayer[i].input.syncFlags.restart)
2019-09-19 22:42:45 +00:00
{
gPlayer[i].input.syncFlags.restart = 0;
2019-09-19 22:42:45 +00:00
levelRestart();
return;
}
if (gPlayer[i].input.syncFlags.pause)
2019-09-19 22:42:45 +00:00
{
gPlayer[i].input.syncFlags.pause = 0;
2020-05-29 01:20:40 +00:00
if (paused && gGameOptions.nGameType > 0 && numplayers > 1)
2019-09-19 22:42:45 +00:00
{
sprintf(buffer,"%s paused the game",gProfile[i].name);
viewSetMessage(buffer);
}
}
}
viewClearInterpolations();
{
2020-05-29 01:20:40 +00:00
if (paused || gEndGameMgr.at0 || (gGameOptions.nGameType == 0 && M_Active()))
2019-09-19 22:42:45 +00:00
return;
}
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
viewBackupView(i);
playerProcess(&gPlayer[i]);
}
trProcessBusy();
evProcess(gFrameClock);
2019-09-19 22:42:45 +00:00
seqProcess(4);
DoSectorPanning();
actProcessSprites();
actPostProcess();
#ifdef POLYMER
G_RefreshLights();
#endif
2019-09-19 22:42:45 +00:00
viewCorrectPrediction();
ambProcess();
viewUpdateDelirium();
viewUpdateShake();
sfxUpdate3DSounds();
if (gMe->hand == 1)
2019-09-19 22:42:45 +00:00
{
const int CHOKERATE = 8;
const int COUNTRATE = 30;
2019-09-19 22:42:45 +00:00
gChokeCounter += CHOKERATE;
while (gChokeCounter >= COUNTRATE)
2019-09-19 22:42:45 +00:00
{
gChoke.at1c(gMe);
gChokeCounter -= COUNTRATE;
2019-09-19 22:42:45 +00:00
}
}
gLevelTime++;
gFrameCount++;
2019-09-19 22:42:45 +00:00
gFrameClock += 4;
if ((gGameOptions.uGameFlags&1) != 0 && !gStartNewGame)
{
ready2send = 0;
#if 0
2019-09-19 22:42:45 +00:00
if (gNetPlayers > 1 && gNetMode == NETWORK_SERVER && gPacketMode == PACKETMODE_1 && myconnectindex == connecthead)
{
while (gNetFifoMasterTail < gNetFifoTail)
{
netGetPackets();
h andleevents();
2019-09-19 22:42:45 +00:00
netMasterUpdate();
}
}
#endif
2019-09-19 22:42:45 +00:00
seqKillAll();
if (gGameOptions.uGameFlags&2)
{
STAT_Update(true);
2019-09-19 22:42:45 +00:00
if (gGameOptions.nGameType == 0)
{
auto completion = [] (bool) {
gamestate = GS_MENUSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
M_SetMenu(NAME_CreditsMenu);
gGameOptions.uGameFlags &= ~3;
gQuitGame = 1;
gRestartGame = true;
};
2019-09-19 22:42:45 +00:00
if (gGameOptions.uGameFlags&8)
{
levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion);
}
else completion(false);
2019-09-19 22:42:45 +00:00
}
else
{
gGameOptions.uGameFlags &= ~3;
gRestartGame = 1;
gQuitGame = 1;
}
2019-09-19 22:42:45 +00:00
}
else
{
gEndGameMgr.Setup();
}
}
}
2019-09-19 22:42:45 +00:00
void ParseOptions(void)
{
2019-09-19 22:42:45 +00:00
}
void ReadAllRFS();
void GameInterface::app_init()
2019-09-19 22:42:45 +00:00
{
InitCheats();
2019-09-19 22:42:45 +00:00
memcpy(&gGameOptions, &gSingleGameOptions, sizeof(GAMEOPTIONS));
gGameOptions.nMonsterSettings = !userConfig.nomonsters;
ReadAllRFS();
2019-09-19 22:42:45 +00:00
HookReplaceFunctions();
2019-09-19 22:42:45 +00:00
Printf(PRINT_NONOTIFY, "Initializing Build 3D engine\n");
engineInit();
Printf(PRINT_NONOTIFY, "Loading tiles\n");
if (!tileInit(0, NULL))
I_FatalError("TILES###.ART files not found");
2019-09-19 22:42:45 +00:00
levelLoadDefaults();
2019-08-09 18:25:44 +00:00
loaddefinitionsfile(BLOODWIDESCREENDEF);
const char* defsfile = G_DefFile();
uint32_t stime = I_msTime();
2019-09-19 22:42:45 +00:00
if (!loaddefinitionsfile(defsfile))
{
uint32_t etime = I_msTime();
Printf(PRINT_NONOTIFY, "Definitions file \"%s\" loaded in %d ms.\n", defsfile, etime - stime);
2019-09-19 22:42:45 +00:00
}
powerupInit();
Printf(PRINT_NONOTIFY, "Loading cosine table\n");
2020-04-11 21:54:33 +00:00
trigInit();
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
2019-09-19 22:42:45 +00:00
viewInit();
Printf(PRINT_NONOTIFY, "Initializing dynamic fire\n");
2019-09-19 22:42:45 +00:00
FireInit();
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
2019-09-19 22:42:45 +00:00
WeaponInit();
LoadSaveSetup();
LoadSavedInfo();
Printf(PRINT_NONOTIFY, "Initializing network users\n");
2019-09-19 22:42:45 +00:00
netInitialize(true);
videoInit();
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
2019-09-19 22:42:45 +00:00
sndInit();
registerosdcommands();
registerinputcommands();
2019-09-19 22:42:45 +00:00
gChoke.sub_83ff0(518, sub_84230);
UpdateDacs(0, true);
}
static void gameInit()
{
//RESTART:
netReset();
2019-09-19 22:42:45 +00:00
gViewIndex = myconnectindex;
gMe = gView = &gPlayer[myconnectindex];
netBroadcastPlayerInfo(myconnectindex);
#if 0
Printf("Waiting for network players!\n");
2019-09-19 22:42:45 +00:00
netWaitForEveryone(0);
if (gRestartGame)
{
// Network error
gQuitGame = false;
gRestartGame = false;
netDeinitialize();
netResetToSinglePlayer();
goto RESTART;
}
#endif
UpdateNetworkMenus();
2019-09-19 22:42:45 +00:00
gQuitGame = 0;
gRestartGame = 0;
if (gGameOptions.nGameType > 0)
{
2020-01-01 10:35:47 +00:00
inputState.ClearAllInput();
2019-09-19 22:42:45 +00:00
}
}
static void gameTicker()
{
bool gameUpdate = false;
double const gameUpdateStartTime = I_msTimeF();
while (gPredictTail < gNetFifoHead[myconnectindex] && !paused)
2019-09-19 22:42:45 +00:00
{
viewUpdatePrediction(&gFifoInput[gPredictTail & 255][myconnectindex]);
}
if (numplayers == 1)
gBufferJitter = 0;
int const currentTic = I_GetTime();
gameclock = I_GetBuildTime();
while (currentTic - lastTic >= 1 && ready2send)
{
gNetInput = gInput;
gInput = {};
netGetInput();
lastTic = currentTic;
while (gNetFifoHead[myconnectindex] - gNetFifoTail > gBufferJitter && !gStartNewGame && !gQuitGame)
2019-09-19 22:42:45 +00:00
{
int i;
for (i = connecthead; i >= 0; i = connectpoint2[i])
if (gNetFifoHead[i] == gNetFifoTail)
break;
if (i >= 0)
2020-01-29 13:19:05 +00:00
break;
ProcessFrame();
gameUpdate = true;
}
}
if (gameUpdate)
{
g_gameUpdateTime = I_msTimeF() - 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/* I_msTimeF()*/ - gameUpdateStartTime;
}
}
2020-01-29 13:19:05 +00:00
static void drawBackground()
{
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(2518, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
if (gQuitRequest && !gQuitGame)
netBroadcastMyLogoff(gQuitRequest == 2);
}
static void commonTicker()
{
if (TestBitString(gotpic, 2342))
{
FireProcess();
ClearBitString(gotpic, 2342);
}
if (gStartNewGame)
{
auto sng = gStartNewGame;
gStartNewGame = nullptr;
gQuitGame = false;
auto completion = [=](bool = false)
{
StartLevel(sng);
gFrameClock = gameclock;
gamestate = GS_LEVEL;
};
bool startedCutscene = false;
if (!(sng->flags & MI_USERMAP))
{
int episode = volfromlevelnum(sng->levelNumber);
int level = mapfromlevelnum(sng->levelNumber);
if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0])
{
levelPlayIntroScene(episode, completion);
startedCutscene = true;
}
}
if (!startedCutscene) completion(false);
2019-09-19 22:42:45 +00:00
}
else if (gRestartGame)
2019-09-19 22:42:45 +00:00
{
Mus_Stop();
soundEngine->StopAllChannels();
2019-09-19 22:42:45 +00:00
gQuitGame = 0;
gQuitRequest = 0;
gRestartGame = 0;
// Don't switch to startup if we're already outside the game.
if (gamestate == GS_LEVEL)
{
gamestate = GS_MENUSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
}
2019-09-19 22:42:45 +00:00
}
}
void GameInterface::RunGameFrame()
{
commonTicker();
netGetPackets();
ctrlGetInput();
switch (gamestate)
{
default:
case GS_STARTUP:
gameInit();
if (userConfig.CommandMap.IsNotEmpty())
{
}
else
{
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
else
{
gamestate = GS_MENUSCREEN;
M_StartControlPanel(false);
M_SetMenu(NAME_Mainmenu);
}
}
break;
case GS_MENUSCREEN:
case GS_FULLCONSOLE:
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;
}
2019-09-19 22:42:45 +00:00
}
bool DemoRecordStatus(void) {
return false;
2019-09-19 22:42:45 +00:00
}
bool VanillaMode() {
return false;
2019-09-19 22:42:45 +00:00
}
int sndTryPlaySpecialMusic(int nMusic)
{
if (Mus_Play(nullptr, quoteMgr.GetQuote(nMusic), true))
{
return 0;
}
return 1;
}
void sndPlaySpecialMusicOrNothing(int nMusic)
{
if (sndTryPlaySpecialMusic(nMusic))
{
Mus_Stop();
}
}
extern IniFile* BloodINI;
void GameInterface::FreeGameData()
{
if (BloodINI) delete BloodINI;
netDeinitialize();
}
ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
{
int top = 0;
if (gGameOptions.nGameType > 0 && gGameOptions.nGameType <= 3)
{
top = (tilesiz[2229].y * ((gNetPlayers + 3) / 4));
}
return { top, 25 };
}
::GameInterface* CreateInterface()
{
return new GameInterface;
}
END_BLD_NS