raze/source/blood/src/blood.cpp

531 lines
12 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"
#include "common_game.h"
#include "g_input.h"
2020-09-06 10:44:58 +00:00
#include "automap.h"
2019-09-19 22:42:45 +00:00
#include "db.h"
#include "blood.h"
#include "choke.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 "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"
#include "choke.h"
#include "d_net.h"
#include "v_video.h"
#include "statusbar.h"
2019-09-19 22:42:45 +00:00
BEGIN_BLD_NS
void InitCheats();
2019-09-19 22:42:45 +00:00
bool bNoDemo = false;
int gNetPlayers;
int gChokeCounter = 0;
int blood_globalflags;
PLAYER gPlayerTemp[kMaxPlayers];
int gHealthTemp[kMaxPlayers];
vec3_t startpos;
int16_t startang, startsectnum;
2019-09-19 22:42:45 +00:00
void QuitGame(void)
{
throw CExitEvent(0);
2019-09-19 22:42:45 +00:00
}
void EndLevel(void)
{
gViewPos = VIEWPOS_0;
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
2019-09-19 22:42:45 +00:00
}
void StartLevel(MapRecord* level)
2019-09-19 22:42:45 +00:00
{
if (!level) return;
gFrameCount = 0;
gFrameClock = 0;
STAT_Update(0);
EndLevel();
currentLevel = level;
if (gGameOptions.nGameType == 0)
{
///////
gGameOptions.weaponsV10x = gWeaponsV10x;
///////
}
#if 0
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel))
{
// todo
gBlueFlagDropped = false;
gRedFlagDropped = false;
}
#endif
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
{
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;
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("%s: Unable to load map", level->DisplayName());
}
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
STAT_NewLevel(currentLevel->fileName);
wsrand(dbReadMapCRC(currentLevel->LabelName()));
gKillMgr.Clear();
gSecretMgr.Clear();
automapping = 1;
int modernTypesErased = 0;
for (int i = 0; i < kMaxSprites; i++)
{
spritetype* pSprite = &sprite[i];
if (pSprite->statnum < kMaxStatus && pSprite->extra > 0) {
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)) {
DeleteSprite(i);
continue;
}
#ifdef NOONE_EXTENSIONS
if (!gModernMap && nnExtEraseModernStuff(pSprite, pXSprite))
modernTypesErased++;
#endif
}
}
#ifdef NOONE_EXTENSIONS
if (!gModernMap && modernTypesErased > 0)
Printf(PRINT_NONOTIFY, "> Modern types erased: %d.\n", modernTypesErased);
#endif
startpos.z = getflorzofslope(startsectnum, startpos.x, startpos.y);
for (int i = 0; i < kMaxPlayers; i++) {
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) {
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
}
InitSectorFX();
warpInit();
actInit(false);
evInit();
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
if (!(gGameOptions.uGameFlags & GF_AdvanceLevel))
{
if (numplayers == 1)
{
gProfile[i].skill = gSkill;
}
playerInit(i, 0);
}
playerStart(i, 1);
}
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
{
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;
}
}
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
PreloadCache();
InitMirrors();
trInit();
if (!bVanilla && !gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
sfxSetReverb(0);
ambInit();
Net_ClearFifo();
gChokeCounter = 0;
M_ClearMenus();
// viewSetMessage("");
viewSetErrorMessage("");
paused = 0;
levelTryPlayMusic();
gChoke.reset();
setLevelStarted(level);
2019-09-19 22:42:45 +00:00
}
2019-09-24 02:14:59 +00:00
void NewLevel(MapRecord *sng, int skill)
{
auto completion = [=](bool = false)
{
if (skill != -1) gGameOptions.nDifficulty = skill;
gSkill = skill;
cheatReset();
StartLevel(sng);
gameaction = ga_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);
}
void GameInterface::NewGame(MapRecord *sng, int skill)
{
gGameOptions.uGameFlags = 0;
NewLevel(sng, skill);
}
void GameInterface::NextLevel(MapRecord *map, int skill)
{
gGameOptions.uGameFlags = GF_AdvanceLevel;
NewLevel(map, skill);
}
void GameInterface::Ticker()
2019-09-19 22:42:45 +00:00
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
auto& inp = gPlayer[i].input;
auto oldactions = inp.actions;
inp = playercmds[i].ucmd;
inp.actions |= oldactions & ~(SB_BUTTON_MASK | SB_RUN | SB_WEAPONMASK_BITS); // should be everything non-button and non-weapon
int newweap = inp.getNewWeapon();
if (newweap > 0 && newweap < WeaponSel_MaxBlood) gPlayer[i].newWeapon = newweap;
}
viewClearInterpolations();
if (!(paused || (gGameOptions.nGameType == 0 && M_Active())))
{
thinktime.Reset();
thinktime.Clock();
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
viewBackupView(i);
playerProcess(&gPlayer[i]);
}
trProcessBusy();
evProcess(gFrameClock);
seqProcess(4);
DoSectorPanning();
actortime.Reset();
actortime.Clock();
actProcessSprites();
actPostProcess();
actortime.Unclock();
viewCorrectPrediction();
ambProcess();
viewUpdateDelirium();
viewUpdateShake();
gi->UpdateSounds();
if (gMe->hand == 1)
{
const int CHOKERATE = 8;
const int COUNTRATE = 30;
gChokeCounter += CHOKERATE;
while (gChokeCounter >= COUNTRATE)
{
gChoke.at1c(gMe);
gChokeCounter -= COUNTRATE;
}
}
thinktime.Unclock();
gFrameCount++;
gFrameClock += 4;
if (gFrameClock == 8) gameaction = ga_autosave; // let the game run for 1 frame before saving.
for (int i = 0; i < 8; i++)
{
dword_21EFD0[i] = dword_21EFD0[i] -= 4;
if (dword_21EFD0[i] < 0)
dword_21EFD0[i] = 0;
}
if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0)
{
seqKillAll();
if (gGameOptions.uGameFlags & GF_EndGame)
{
STAT_Update(true);
if (gGameOptions.nGameType == 0)
{
auto completion = [](bool) {
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
gameaction = ga_creditsmenu;
};
if (gGameOptions.uGameFlags & GF_PlayCutscene)
{
levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion);
}
else completion(false);
}
else
{
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
}
}
else
{
STAT_Update(false);
EndLevel();
Mus_Stop();
// Fixme: Link maps, not episode/level pairs.
int ep = volfromlevelnum(currentLevel->levelNumber);
auto map = FindMapByLevelNum(levelnum(ep, gNextLevel));
CompleteLevel(map);
}
}
r_NoInterpolate = false;
}
else r_NoInterpolate = true;
}
void GameInterface::DrawBackground()
{
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(2518, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
2019-09-19 22:42:45 +00:00
}
2019-09-19 22:42:45 +00:00
void ReadAllRFS();
void GameInterface::app_init()
2019-09-19 22:42:45 +00:00
{
InitCheats();
memcpy(&gGameOptions, &gSingleGameOptions, sizeof(GAMEOPTIONS));
gGameOptions.nMonsterSettings = !userConfig.nomonsters;
ReadAllRFS();
HookReplaceFunctions();
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");
levelLoadDefaults();
LoadDefinitions();
TileFiles.SetBackup();
powerupInit();
Printf(PRINT_NONOTIFY, "Loading cosine table\n");
trigInit();
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
viewInit();
Printf(PRINT_NONOTIFY, "Initializing dynamic fire\n");
FireInit();
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
WeaponInit();
LoadSaveSetup();
myconnectindex = connecthead = 0;
gNetPlayers = numplayers = 1;
connectpoint2[0] = -1;
gGameOptions.nGameType = 0;
UpdateNetworkMenus();
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
sndInit();
registerosdcommands();
gChoke.init(518, sub_84230);
UpdateDacs(0, true);
enginecompatibility_mode = ENGINECOMPATIBILITY_19960925;//bVanilla;
}
static void gameInit()
{
//RESTART:
gViewIndex = myconnectindex;
gMe = gView = &gPlayer[myconnectindex];
PROFILE* pProfile = &gProfile[myconnectindex];
strcpy(pProfile->name, playername);
pProfile->skill = gSkill;
UpdateNetworkMenus();
if (gGameOptions.nGameType > 0)
{
inputState.ClearAllInput();
}
}
2020-01-29 13:19:05 +00:00
void GameInterface::Startup()
{
gameInit();
if (userConfig.CommandMap.IsNotEmpty())
{
}
else
{
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
else
{
gameaction = ga_mainmenu;
}
}
}
void GameInterface::Render()
{
drawtime.Reset();
drawtime.Clock();
viewDrawScreen();
drawtime.Unclock();
2019-09-19 22:42:45 +00:00
}
void sndPlaySpecialMusicOrNothing(int nMusic)
{
if (!Mus_Play(nullptr, quoteMgr.GetQuote(nMusic), true))
{
Mus_Stop();
}
}
extern IniFile* BloodINI;
void GameInterface::FreeGameData()
{
if (BloodINI) delete BloodINI;
}
void GameInterface::FreeLevelData()
{
EndLevel();
::GameInterface::FreeLevelData();
}
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