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.
|
|
|
|
*/
|
|
|
|
//-------------------------------------------------------------------------
|
2019-09-21 18:59:54 +00:00
|
|
|
|
|
|
|
#include "ns.h" // Must come before everything else!
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
#include "build.h"
|
2020-08-16 07:46:37 +00:00
|
|
|
#include "g_input.h"
|
2020-09-06 10:44:58 +00:00
|
|
|
#include "automap.h"
|
2019-09-19 22:42:45 +00:00
|
|
|
|
|
|
|
#include "blood.h"
|
|
|
|
#include "choke.h"
|
2020-12-09 14:56:32 +00:00
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
#include "view.h"
|
2020-07-25 22:07:59 +00:00
|
|
|
#include "misc.h"
|
2019-10-26 18:47:37 +00:00
|
|
|
#include "gameconfigfile.h"
|
2019-11-01 18:25:42 +00:00
|
|
|
#include "gamecontrol.h"
|
|
|
|
#include "m_argv.h"
|
2019-11-12 21:59:51 +00:00
|
|
|
#include "statistics.h"
|
2020-10-04 16:31:48 +00:00
|
|
|
#include "razemenu.h"
|
2020-04-12 06:09:38 +00:00
|
|
|
#include "raze_sound.h"
|
2020-07-25 22:07:59 +00:00
|
|
|
#include "secrets.h"
|
2020-07-29 21:18:08 +00:00
|
|
|
#include "gamestate.h"
|
2021-05-21 23:34:00 +00:00
|
|
|
#include "screenjob_.h"
|
2020-08-16 11:26:57 +00:00
|
|
|
#include "mapinfo.h"
|
2020-09-01 21:34:04 +00:00
|
|
|
#include "d_net.h"
|
|
|
|
#include "v_video.h"
|
2020-10-04 16:31:48 +00:00
|
|
|
#include "v_draw.h"
|
2020-10-06 18:05:51 +00:00
|
|
|
#include "texturemanager.h"
|
2020-09-08 16:28:41 +00:00
|
|
|
#include "statusbar.h"
|
2021-04-28 18:16:13 +00:00
|
|
|
#include "vm.h"
|
2019-09-19 22:42:45 +00:00
|
|
|
|
2019-09-22 06:39:22 +00:00
|
|
|
BEGIN_BLD_NS
|
|
|
|
|
2021-12-05 19:55:19 +00:00
|
|
|
|
|
|
|
IMPLEMENT_CLASS(DBloodActor, false, true)
|
|
|
|
IMPLEMENT_POINTERS_START(DBloodActor)
|
|
|
|
IMPLEMENT_POINTER(prevmarker)
|
|
|
|
IMPLEMENT_POINTER(ownerActor)
|
|
|
|
IMPLEMENT_POINTER(hit.hit.hitActor)
|
|
|
|
IMPLEMENT_POINTER(hit.ceilhit.hitActor)
|
|
|
|
IMPLEMENT_POINTER(hit.florhit.hitActor)
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.pLifeLeech)
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[0])
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[1])
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[2])
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[3])
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[4])
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[5])
|
|
|
|
IMPLEMENT_POINTER(genDudeExtra.slave[6])
|
2021-12-22 08:49:00 +00:00
|
|
|
IMPLEMENT_POINTER(xspr.burnSource)
|
|
|
|
IMPLEMENT_POINTER(xspr.target)
|
2021-12-05 19:55:19 +00:00
|
|
|
IMPLEMENT_POINTERS_END
|
|
|
|
|
|
|
|
size_t DBloodActor::PropagateMark()
|
|
|
|
{
|
|
|
|
condition[0].Mark();
|
|
|
|
condition[1].Mark();
|
2021-12-06 16:00:15 +00:00
|
|
|
return Super::PropagateMark();
|
2021-12-05 19:55:19 +00:00
|
|
|
}
|
|
|
|
|
2021-12-05 22:56:35 +00:00
|
|
|
static void markgcroots()
|
|
|
|
{
|
|
|
|
GC::MarkArray(gProxySpritesList, gProxySpritesCount);
|
|
|
|
GC::MarkArray(gSightSpritesList, gSightSpritesCount);
|
|
|
|
GC::MarkArray(gPhysSpritesList, gPhysSpritesCount);
|
|
|
|
GC::MarkArray(gImpactSpritesList, gImpactSpritesCount);
|
|
|
|
for (auto& pl : gPlayer)
|
|
|
|
{
|
|
|
|
GC::Mark(pl.actor);
|
|
|
|
GC::MarkArray(pl.ctfFlagState, 2);
|
|
|
|
GC::Mark(pl.aimTarget);
|
|
|
|
GC::MarkArray(pl.aimTargets, 16);
|
|
|
|
GC::Mark(pl.fragger);
|
|
|
|
GC::Mark(pl.voodooTarget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-03 18:51:31 +00:00
|
|
|
void InitCheats();
|
2019-09-22 06:39:22 +00:00
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
bool bNoDemo = false;
|
|
|
|
int gNetPlayers;
|
|
|
|
int gChokeCounter = 0;
|
|
|
|
int blood_globalflags;
|
2020-09-01 21:34:04 +00:00
|
|
|
PLAYER gPlayerTemp[kMaxPlayers];
|
|
|
|
int gHealthTemp[kMaxPlayers];
|
|
|
|
vec3_t startpos;
|
2021-11-16 17:20:24 +00:00
|
|
|
int16_t startang;
|
2021-11-23 23:55:57 +00:00
|
|
|
sectortype* startsector;
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
|
|
|
|
void QuitGame(void)
|
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
throw CExitEvent(0);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EndLevel(void)
|
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
gViewPos = VIEWPOS_0;
|
|
|
|
sndKillAllSounds();
|
|
|
|
sfxKillAllSounds();
|
|
|
|
ambKillAll();
|
|
|
|
seqKillAll();
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
2021-12-03 21:45:54 +00:00
|
|
|
TArray<DBloodActor*> SpawnActors(BloodSpawnSpriteDef& sprites)
|
2021-12-03 21:19:28 +00:00
|
|
|
{
|
|
|
|
TArray<DBloodActor*> spawns(sprites.sprites.Size(), true);
|
2021-12-04 21:04:16 +00:00
|
|
|
InitSpriteLists();
|
|
|
|
int j = 0;
|
2021-12-03 21:19:28 +00:00
|
|
|
for (unsigned i = 0; i < sprites.sprites.Size(); i++)
|
|
|
|
{
|
2021-12-04 21:04:16 +00:00
|
|
|
if (sprites.sprites[i].statnum == MAXSTATUS)
|
|
|
|
{
|
|
|
|
spawns.Pop();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto sprt = &sprites.sprites[i];
|
|
|
|
auto actor = InsertSprite(sprt->sector(), sprt->statnum);
|
|
|
|
spawns[j++] = actor;
|
2021-12-03 21:19:28 +00:00
|
|
|
actor->s() = sprites.sprites[i];
|
2021-12-03 21:45:54 +00:00
|
|
|
if (sprites.sprext.Size()) actor->sx() = sprites.sprext[i];
|
|
|
|
else actor->sx() = {};
|
|
|
|
actor->sm() = {};
|
|
|
|
|
2021-12-03 21:19:28 +00:00
|
|
|
if (sprites.sprites[i].extra > 0)
|
|
|
|
{
|
|
|
|
actor->addX();
|
|
|
|
actor->x() = sprites.xspr[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return spawns;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropagateMarkerReferences(void)
|
|
|
|
{
|
|
|
|
BloodStatIterator it(kStatMarker);
|
|
|
|
while (auto actor = it.Next())
|
|
|
|
{
|
2021-12-21 22:18:23 +00:00
|
|
|
switch (actor->spr.type)
|
2021-12-03 21:19:28 +00:00
|
|
|
{
|
|
|
|
case kMarkerOff:
|
|
|
|
case kMarkerAxis:
|
|
|
|
case kMarkerWarpDest:
|
|
|
|
{
|
2021-12-21 22:18:23 +00:00
|
|
|
int nOwner = actor->spr.owner;
|
2021-12-21 09:51:41 +00:00
|
|
|
if (validSectorIndex(nOwner))
|
2021-12-03 21:19:28 +00:00
|
|
|
{
|
|
|
|
if (sector[nOwner].hasX())
|
|
|
|
{
|
|
|
|
sector[nOwner].xs().marker0 = actor;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kMarkerOn:
|
|
|
|
{
|
2021-12-21 22:18:23 +00:00
|
|
|
int nOwner = actor->spr.owner;
|
2021-12-21 09:51:41 +00:00
|
|
|
if (validSectorIndex(nOwner))
|
2021-12-03 21:19:28 +00:00
|
|
|
{
|
|
|
|
if (sector[nOwner].hasX())
|
|
|
|
{
|
|
|
|
sector[nOwner].xs().marker1 = actor;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DeleteSprite(actor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-02 07:08:57 +00:00
|
|
|
void StartLevel(MapRecord* level, bool newgame)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
if (!level) return;
|
2020-09-02 05:47:26 +00:00
|
|
|
gFrameCount = 0;
|
2021-02-18 10:46:36 +00:00
|
|
|
PlayClock = 0;
|
2020-09-23 14:20:40 +00:00
|
|
|
inputState.ClearAllInput();
|
2020-09-01 21:34:04 +00:00
|
|
|
currentLevel = level;
|
|
|
|
|
|
|
|
if (gGameOptions.nGameType == 0)
|
|
|
|
{
|
|
|
|
///////
|
2021-08-02 14:47:05 +00:00
|
|
|
gGameOptions.weaponsV10x = cl_bloodoldweapbalance;
|
2020-09-01 21:34:04 +00:00
|
|
|
///////
|
|
|
|
}
|
2020-08-16 11:26:57 +00:00
|
|
|
#if 0
|
2021-05-02 07:08:57 +00:00
|
|
|
else if (gGameOptions.nGameType > 0 && newgame)
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
|
|
|
// todo
|
|
|
|
gBlueFlagDropped = false;
|
|
|
|
gRedFlagDropped = false;
|
|
|
|
}
|
2020-08-16 11:26:57 +00:00
|
|
|
#endif
|
2020-09-01 21:34:04 +00:00
|
|
|
//drawLoadingScreen();
|
2021-12-03 21:45:54 +00:00
|
|
|
BloodSpawnSpriteDef sprites;
|
2021-12-03 21:19:28 +00:00
|
|
|
dbLoadMap(currentLevel->fileName, (int*)&startpos.x, (int*)&startpos.y, (int*)&startpos.z, &startang, &startsector, nullptr, sprites);
|
2020-09-01 21:34:04 +00:00
|
|
|
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
|
2020-04-01 20:34:49 +00:00
|
|
|
STAT_NewLevel(currentLevel->fileName);
|
2020-09-01 21:34:04 +00:00
|
|
|
wsrand(dbReadMapCRC(currentLevel->LabelName()));
|
|
|
|
gKillMgr.Clear();
|
|
|
|
gSecretMgr.Clear();
|
|
|
|
automapping = 1;
|
|
|
|
|
2021-12-03 20:36:32 +00:00
|
|
|
// Here is where later the actors must be spawned.
|
2021-12-03 21:19:28 +00:00
|
|
|
auto actorlist = SpawnActors(sprites);
|
|
|
|
PropagateMarkerReferences();
|
2020-09-01 21:34:04 +00:00
|
|
|
int modernTypesErased = 0;
|
2021-12-03 21:19:28 +00:00
|
|
|
for (auto actor : actorlist)
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
2021-12-03 21:19:28 +00:00
|
|
|
|
2021-08-27 12:11:59 +00:00
|
|
|
spritetype* pSprite = &actor->s();
|
2021-12-03 20:36:32 +00:00
|
|
|
if (actor->exists() && actor->hasX())
|
2021-08-27 12:11:59 +00:00
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2021-08-27 12:11:59 +00:00
|
|
|
XSPRITE* pXSprite = &actor->x();
|
2020-09-01 21:34:04 +00:00
|
|
|
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)) {
|
|
|
|
|
2021-09-04 10:47:34 +00:00
|
|
|
DeleteSprite(actor);
|
2020-09-01 21:34:04 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
2021-08-27 12:11:59 +00:00
|
|
|
if (!gModernMap && nnExtEraseModernStuff(actor))
|
2020-09-01 21:34:04 +00:00
|
|
|
modernTypesErased++;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
|
|
|
if (!gModernMap && modernTypesErased > 0)
|
|
|
|
Printf(PRINT_NONOTIFY, "> Modern types erased: %d.\n", modernTypesErased);
|
|
|
|
#endif
|
|
|
|
|
2021-11-23 23:55:57 +00:00
|
|
|
startpos.z = getflorzofslopeptr(startsector, startpos.x, startpos.y);
|
2020-09-01 21:34:04 +00:00
|
|
|
for (int i = 0; i < kMaxPlayers; i++) {
|
|
|
|
gStartZone[i].x = startpos.x;
|
|
|
|
gStartZone[i].y = startpos.y;
|
|
|
|
gStartZone[i].z = startpos.z;
|
2021-11-23 23:55:57 +00:00
|
|
|
gStartZone[i].sector = startsector;
|
2020-09-01 21:34:04 +00:00
|
|
|
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;
|
2021-11-23 23:55:57 +00:00
|
|
|
gStartZoneTeam1[i].sector = startsector;
|
2020-09-01 21:34:04 +00:00
|
|
|
gStartZoneTeam1[i].ang = startang;
|
|
|
|
|
|
|
|
gStartZoneTeam2[i].x = startpos.x;
|
|
|
|
gStartZoneTeam2[i].y = startpos.y;
|
|
|
|
gStartZoneTeam2[i].z = startpos.z;
|
2021-11-23 23:55:57 +00:00
|
|
|
gStartZoneTeam2[i].sector = startsector;
|
2020-09-01 21:34:04 +00:00
|
|
|
gStartZoneTeam2[i].ang = startang;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
InitSectorFX();
|
2021-12-03 20:36:32 +00:00
|
|
|
warpInit(actorlist);
|
|
|
|
actInit(actorlist);
|
|
|
|
evInit(actorlist);
|
2020-09-01 21:34:04 +00:00
|
|
|
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
|
|
|
{
|
2021-05-02 07:08:57 +00:00
|
|
|
if (newgame)
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
|
|
|
playerInit(i, 0);
|
|
|
|
}
|
|
|
|
playerStart(i, 1);
|
|
|
|
}
|
2021-05-02 07:08:57 +00:00
|
|
|
if (!newgame)
|
2020-09-01 21:34:04 +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;
|
2021-08-05 02:38:26 +00:00
|
|
|
pPlayer->qavLastTick = gPlayerTemp[i].qavLastTick;
|
|
|
|
pPlayer->qavTimer = gPlayerTemp[i].qavTimer;
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
PreloadCache();
|
|
|
|
InitMirrors();
|
2021-12-03 20:36:32 +00:00
|
|
|
trInit(actorlist);
|
2020-09-16 00:14:04 +00:00
|
|
|
if (!gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
|
2020-09-01 21:34:04 +00:00
|
|
|
sfxSetReverb(0);
|
|
|
|
ambInit();
|
|
|
|
Net_ClearFifo();
|
|
|
|
gChokeCounter = 0;
|
2020-07-27 22:01:16 +00:00
|
|
|
M_ClearMenus();
|
2020-09-01 21:34:04 +00:00
|
|
|
// viewSetMessage("");
|
|
|
|
viewSetErrorMessage("");
|
|
|
|
paused = 0;
|
|
|
|
levelTryPlayMusic();
|
2020-09-01 18:08:37 +00:00
|
|
|
gChoke.reset();
|
2020-09-08 16:28:41 +00:00
|
|
|
setLevelStarted(level);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 02:14:59 +00:00
|
|
|
|
2021-05-02 07:08:57 +00:00
|
|
|
void NewLevel(MapRecord *sng, int skill, bool newgame)
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
2021-04-28 18:16:13 +00:00
|
|
|
if (skill != -1) gGameOptions.nDifficulty = skill;
|
|
|
|
gSkill = gGameOptions.nDifficulty;
|
2021-05-02 07:08:57 +00:00
|
|
|
StartLevel(sng, newgame);
|
2021-04-28 18:16:13 +00:00
|
|
|
gameaction = ga_level;
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 16:50:48 +00:00
|
|
|
void GameInterface::NewGame(MapRecord *sng, int skill, bool)
|
2020-09-04 18:46:44 +00:00
|
|
|
{
|
|
|
|
gGameOptions.uGameFlags = 0;
|
2020-10-10 18:20:12 +00:00
|
|
|
cheatReset();
|
2021-05-02 07:08:57 +00:00
|
|
|
NewLevel(sng, skill, true);
|
2020-09-04 18:46:44 +00:00
|
|
|
}
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2020-09-04 18:46:44 +00:00
|
|
|
void GameInterface::NextLevel(MapRecord *map, int skill)
|
|
|
|
{
|
2021-05-02 07:08:57 +00:00
|
|
|
NewLevel(map, skill, false);
|
2020-09-04 18:46:44 +00:00
|
|
|
}
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2021-07-20 08:50:46 +00:00
|
|
|
int GameInterface::GetCurrentSkill()
|
|
|
|
{
|
|
|
|
return gGameOptions.nDifficulty;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-01 21:34:04 +00:00
|
|
|
void GameInterface::Ticker()
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
2020-09-01 21:34:04 +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;
|
|
|
|
}
|
|
|
|
|
2021-09-05 10:35:13 +00:00
|
|
|
BloodSpriteIterator it;
|
|
|
|
while (DBloodActor* act = it.Next()) act->interpolated = false;
|
|
|
|
|
2020-11-26 16:19:42 +00:00
|
|
|
ClearMovementInterpolations();
|
|
|
|
UpdateInterpolations();
|
2020-09-02 05:47:26 +00:00
|
|
|
|
2020-09-02 05:11:47 +00:00
|
|
|
if (!(paused || (gGameOptions.nGameType == 0 && M_Active())))
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
2020-09-02 05:11:47 +00:00
|
|
|
thinktime.Reset();
|
|
|
|
thinktime.Clock();
|
|
|
|
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
|
|
|
{
|
|
|
|
viewBackupView(i);
|
|
|
|
playerProcess(&gPlayer[i]);
|
|
|
|
}
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2020-09-02 05:11:47 +00:00
|
|
|
trProcessBusy();
|
2021-02-18 10:46:36 +00:00
|
|
|
evProcess(PlayClock);
|
2020-09-02 05:11:47 +00:00
|
|
|
seqProcess(4);
|
|
|
|
DoSectorPanning();
|
|
|
|
|
|
|
|
actortime.Reset();
|
|
|
|
actortime.Clock();
|
|
|
|
actProcessSprites();
|
|
|
|
actPostProcess();
|
|
|
|
actortime.Unclock();
|
|
|
|
|
|
|
|
viewCorrectPrediction();
|
|
|
|
ambProcess();
|
|
|
|
viewUpdateDelirium();
|
|
|
|
gi->UpdateSounds();
|
|
|
|
if (gMe->hand == 1)
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
2020-09-02 05:11:47 +00:00
|
|
|
const int CHOKERATE = 8;
|
|
|
|
const int COUNTRATE = 30;
|
|
|
|
gChokeCounter += CHOKERATE;
|
|
|
|
while (gChokeCounter >= COUNTRATE)
|
|
|
|
{
|
2020-11-21 14:45:37 +00:00
|
|
|
gChoke.callback(gMe);
|
2020-09-02 05:11:47 +00:00
|
|
|
gChokeCounter -= COUNTRATE;
|
|
|
|
}
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
2020-09-02 05:11:47 +00:00
|
|
|
thinktime.Unclock();
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2020-09-02 05:11:47 +00:00
|
|
|
gFrameCount++;
|
2021-02-18 10:46:36 +00:00
|
|
|
PlayClock += kTicsPerFrame;
|
|
|
|
if (PlayClock == 8) gameaction = ga_autosave; // let the game run for 1 frame before saving.
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2020-09-02 05:11:47 +00:00
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
{
|
2021-11-14 12:33:35 +00:00
|
|
|
team_ticker[i] -= 4;
|
2020-11-22 12:16:58 +00:00
|
|
|
if (team_ticker[i] < 0)
|
|
|
|
team_ticker[i] = 0;
|
2020-09-02 05:11:47 +00:00
|
|
|
}
|
2020-09-01 18:14:15 +00:00
|
|
|
|
2021-05-02 07:08:57 +00:00
|
|
|
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
2021-05-02 07:08:57 +00:00
|
|
|
gGameOptions.uGameFlags &= ~GF_AdvanceLevel;
|
2020-09-02 05:11:47 +00:00
|
|
|
seqKillAll();
|
2021-05-02 07:08:57 +00:00
|
|
|
STAT_Update(gNextLevel == nullptr);
|
|
|
|
CompleteLevel(gNextLevel);
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
2020-09-02 05:11:47 +00:00
|
|
|
r_NoInterpolate = false;
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
2020-09-02 05:11:47 +00:00
|
|
|
else r_NoInterpolate = true;
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameInterface::DrawBackground()
|
|
|
|
{
|
|
|
|
twod->ClearScreen();
|
2021-05-18 22:07:50 +00:00
|
|
|
auto tex = TexMan.CheckForTexture("titlescreen", ETextureType::Any);
|
|
|
|
DrawTexture(twod, TexMan.GetGameTexture(tex), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 18:05:51 +00:00
|
|
|
#define x(a, b) registerName(#a, b);
|
|
|
|
static void SetTileNames()
|
|
|
|
{
|
|
|
|
auto registerName = [](const char* name, int index)
|
|
|
|
{
|
|
|
|
TexMan.AddAlias(name, tileGetTexture(index));
|
|
|
|
};
|
|
|
|
#include "namelist.h"
|
2021-05-18 22:07:50 +00:00
|
|
|
// Oh Joy! Plasma Pak changes the tile number of the title screen, but we preferably want mods that use the original one to display it.
|
|
|
|
// So let's make this remapping depend on the CRC.
|
|
|
|
if (tileGetCRC32(2518) == 1170870757 && (tileGetCRC32(2046) != 290208654 || tileWidth(2518) == 0)) registerName("titlescreen", 2046);
|
|
|
|
else registerName("titlescreen", 2518);
|
2020-10-06 18:05:51 +00:00
|
|
|
}
|
|
|
|
#undef x
|
2019-10-28 21:19:50 +00:00
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
|
2020-01-22 20:09:45 +00:00
|
|
|
void ReadAllRFS();
|
|
|
|
|
2020-11-10 21:07:45 +00:00
|
|
|
void GameInterface::loadPalette(void)
|
|
|
|
{
|
|
|
|
// in nearly typical Blood fashion it had to use an inverse of the original translucency settings...
|
|
|
|
static glblend_t const bloodglblend =
|
|
|
|
{
|
|
|
|
{
|
|
|
|
{ 1.f / 3.f, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 },
|
|
|
|
{ 2.f / 3.f, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 },
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char* PLU[15] = {
|
|
|
|
"NORMAL.PLU",
|
|
|
|
"SATURATE.PLU",
|
|
|
|
"BEAST.PLU",
|
|
|
|
"TOMMY.PLU",
|
|
|
|
"SPIDER3.PLU",
|
|
|
|
"GRAY.PLU",
|
|
|
|
"GRAYISH.PLU",
|
|
|
|
"SPIDER1.PLU",
|
|
|
|
"SPIDER2.PLU",
|
|
|
|
"FLAME.PLU",
|
|
|
|
"COLD.PLU",
|
|
|
|
"P1.PLU",
|
|
|
|
"P2.PLU",
|
|
|
|
"P3.PLU",
|
|
|
|
"P4.PLU"
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char* PAL[5] = {
|
|
|
|
"BLOOD.PAL",
|
|
|
|
"WATER.PAL",
|
|
|
|
"BEAST.PAL",
|
|
|
|
"SEWER.PAL",
|
|
|
|
"INVULN1.PAL"
|
|
|
|
};
|
|
|
|
|
|
|
|
for (auto& x : glblend) x = bloodglblend;
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
|
|
{
|
|
|
|
auto pal = fileSystem.LoadFile(PAL[i]);
|
|
|
|
if (pal.Size() < 768) I_FatalError("%s: file too small", PAL[i]);
|
|
|
|
paletteSetColorTable(i, pal.Data(), false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
numshades = 64;
|
|
|
|
for (int i = 0; i < MAXPALOOKUPS; i++)
|
|
|
|
{
|
|
|
|
int lump = i < 15 ? fileSystem.FindFile(PLU[i]) : fileSystem.FindResource(i, "PLU");
|
|
|
|
if (lump < 0)
|
|
|
|
{
|
|
|
|
if (i < 15) I_FatalError("%s: file not found", PLU[i]);
|
|
|
|
else continue;
|
|
|
|
}
|
|
|
|
auto data = fileSystem.GetFileData(lump);
|
|
|
|
if (data.Size() != 64 * 256)
|
|
|
|
{
|
|
|
|
if (i < 15) I_FatalError("%s: Incorrect PLU size", PLU[i]);
|
|
|
|
else continue;
|
|
|
|
}
|
|
|
|
lookups.setTable(i, data.Data());
|
|
|
|
}
|
|
|
|
|
|
|
|
lookups.setFadeColor(1, 255, 255, 255);
|
|
|
|
paletteloaded = PALETTE_SHADE | PALETTE_TRANSLUC | PALETTE_MAIN;
|
|
|
|
}
|
|
|
|
|
2020-08-23 15:47:05 +00:00
|
|
|
void GameInterface::app_init()
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
2021-12-05 22:56:35 +00:00
|
|
|
GC::AddMarkerFunc(markgcroots);
|
2020-09-01 21:34:04 +00:00
|
|
|
InitCheats();
|
|
|
|
memcpy(&gGameOptions, &gSingleGameOptions, sizeof(GAMEOPTIONS));
|
|
|
|
gGameOptions.nMonsterSettings = !userConfig.nomonsters;
|
|
|
|
ReadAllRFS();
|
|
|
|
|
|
|
|
levelLoadDefaults();
|
2021-04-28 18:16:13 +00:00
|
|
|
|
|
|
|
//---------
|
2020-10-06 18:05:51 +00:00
|
|
|
SetTileNames();
|
2020-10-25 14:31:20 +00:00
|
|
|
C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25);
|
2020-09-01 21:34:04 +00:00
|
|
|
|
|
|
|
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
|
|
|
|
viewInit();
|
|
|
|
Printf(PRINT_NONOTIFY, "Initializing dynamic fire\n");
|
|
|
|
FireInit();
|
|
|
|
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
|
|
|
|
WeaponInit();
|
|
|
|
|
2021-05-02 16:10:59 +00:00
|
|
|
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
|
|
|
|
sndInit();
|
|
|
|
|
2020-09-01 21:34:04 +00:00
|
|
|
myconnectindex = connecthead = 0;
|
|
|
|
gNetPlayers = numplayers = 1;
|
|
|
|
connectpoint2[0] = -1;
|
|
|
|
gGameOptions.nGameType = 0;
|
|
|
|
UpdateNetworkMenus();
|
|
|
|
|
2020-11-21 19:10:45 +00:00
|
|
|
gChoke.init(518, chokeCallback);
|
2020-09-01 21:34:04 +00:00
|
|
|
UpdateDacs(0, true);
|
|
|
|
|
2020-09-16 00:14:04 +00:00
|
|
|
enginecompatibility_mode = ENGINECOMPATIBILITY_19960925;
|
2021-06-26 08:11:50 +00:00
|
|
|
|
|
|
|
gViewIndex = myconnectindex;
|
|
|
|
gMe = gView = &gPlayer[myconnectindex];
|
2020-07-29 21:18:08 +00:00
|
|
|
}
|
2020-01-23 10:50:12 +00:00
|
|
|
|
2020-07-29 21:18:08 +00:00
|
|
|
static void gameInit()
|
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
gViewIndex = myconnectindex;
|
|
|
|
gMe = gView = &gPlayer[myconnectindex];
|
|
|
|
|
2020-07-29 21:18:08 +00:00
|
|
|
UpdateNetworkMenus();
|
2020-09-01 21:34:04 +00:00
|
|
|
if (gGameOptions.nGameType > 0)
|
|
|
|
{
|
|
|
|
inputState.ClearAllInput();
|
|
|
|
}
|
2020-07-29 21:18:08 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-01-29 13:19:05 +00:00
|
|
|
|
2020-09-01 21:34:04 +00:00
|
|
|
void GameInterface::Startup()
|
2020-07-29 21:18:08 +00:00
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
gameInit();
|
2021-04-27 18:04:11 +00:00
|
|
|
PlayLogos(ga_mainmenu, ga_mainmenu, true);
|
2020-07-29 21:18:08 +00:00
|
|
|
}
|
2020-01-30 13:05:34 +00:00
|
|
|
|
2020-09-01 21:34:04 +00:00
|
|
|
|
2020-07-29 21:18:08 +00:00
|
|
|
|
2020-09-01 21:34:04 +00:00
|
|
|
void GameInterface::Render()
|
2020-07-29 21:18:08 +00:00
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
drawtime.Reset();
|
|
|
|
drawtime.Clock();
|
|
|
|
viewDrawScreen();
|
|
|
|
drawtime.Unclock();
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
2019-06-28 12:45:39 +00:00
|
|
|
|
|
|
|
void sndPlaySpecialMusicOrNothing(int nMusic)
|
|
|
|
{
|
2021-05-11 23:58:42 +00:00
|
|
|
if (!Mus_Play(quoteMgr.GetQuote(nMusic), true))
|
2020-09-01 21:34:04 +00:00
|
|
|
{
|
|
|
|
Mus_Stop();
|
|
|
|
}
|
2019-06-28 12:45:39 +00:00
|
|
|
}
|
2019-09-21 20:53:00 +00:00
|
|
|
|
2019-12-24 18:47:34 +00:00
|
|
|
extern IniFile* BloodINI;
|
|
|
|
void GameInterface::FreeGameData()
|
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
if (BloodINI) delete BloodINI;
|
2019-12-24 18:47:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-03 21:10:28 +00:00
|
|
|
void GameInterface::FreeLevelData()
|
|
|
|
{
|
|
|
|
EndLevel();
|
|
|
|
::GameInterface::FreeLevelData();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-16 00:55:50 +00:00
|
|
|
ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
|
2020-01-01 08:49:06 +00:00
|
|
|
{
|
2020-09-01 21:34:04 +00:00
|
|
|
int top = 0;
|
|
|
|
if (gGameOptions.nGameType > 0 && gGameOptions.nGameType <= 3)
|
|
|
|
{
|
2020-11-22 23:18:07 +00:00
|
|
|
top = (tileHeight(2229) * ((gNetPlayers + 3) / 4));
|
2020-09-01 21:34:04 +00:00
|
|
|
}
|
|
|
|
return { top, 25 };
|
2020-01-01 08:49:06 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 11:32:58 +00:00
|
|
|
::GameInterface* CreateInterface()
|
|
|
|
{
|
|
|
|
return new GameInterface;
|
|
|
|
}
|
2019-09-22 06:39:22 +00:00
|
|
|
|
2021-04-28 18:16:13 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
kLoadScreenCRC = -2051908571,
|
|
|
|
kLoadScreenWideBackWidth = 256,
|
|
|
|
kLoadScreenWideSideWidth = 128,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen)
|
|
|
|
{
|
|
|
|
static int bLoadScreenCrcMatch = -1;
|
|
|
|
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
|
|
|
|
ACTION_RETURN_INT(bLoadScreenCrcMatch);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic)
|
|
|
|
{
|
2021-05-11 23:58:42 +00:00
|
|
|
Mus_Play("PESTIS.MID", false);
|
2021-04-28 18:16:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION(_Blood, sndStartSample)
|
|
|
|
{
|
|
|
|
PARAM_PROLOGUE;
|
|
|
|
PARAM_INT(id);
|
|
|
|
PARAM_INT(vol);
|
|
|
|
PARAM_INT(chan);
|
|
|
|
PARAM_BOOL(looped);
|
|
|
|
PARAM_INT(chanflags);
|
|
|
|
sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed)
|
|
|
|
{
|
|
|
|
PARAM_PROLOGUE;
|
|
|
|
PARAM_STRING(id);
|
|
|
|
PARAM_INT(vol);
|
|
|
|
PARAM_INT(chan);
|
|
|
|
sndStartSample(id, vol, chan);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:21:52 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(_Blood, PowerupIcon)
|
|
|
|
{
|
|
|
|
PARAM_PROLOGUE;
|
|
|
|
PARAM_INT(pwup);
|
|
|
|
int tile = -1;
|
|
|
|
if (pwup >= 0 && pwup < (int)countof(gPowerUpInfo))
|
|
|
|
{
|
|
|
|
tile = gPowerUpInfo[pwup].picnum;
|
|
|
|
}
|
|
|
|
FGameTexture* tex = tileGetTexture(tile);
|
|
|
|
ACTION_RETURN_INT(tex ? tex->GetID().GetIndex() : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION(_Blood, GetViewPlayer)
|
|
|
|
{
|
|
|
|
PARAM_PROLOGUE;
|
|
|
|
ACTION_RETURN_POINTER(gView);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION(_BloodPlayer, GetHealth)
|
|
|
|
{
|
|
|
|
PARAM_SELF_STRUCT_PROLOGUE(PLAYER);
|
|
|
|
XSPRITE* pXSprite = self->pXSprite;
|
|
|
|
ACTION_RETURN_INT(pXSprite->health);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_ACTION_FUNCTION_NATIVE(_BloodPlayer, powerupCheck, powerupCheck)
|
|
|
|
{
|
|
|
|
PARAM_SELF_STRUCT_PROLOGUE(PLAYER);
|
|
|
|
PARAM_INT(pwup);
|
|
|
|
ACTION_RETURN_INT(powerupCheck(self, pwup));
|
|
|
|
}
|
|
|
|
|
2019-09-22 06:39:22 +00:00
|
|
|
END_BLD_NS
|