raze/source/games/exhumed/src/view.cpp

504 lines
14 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed 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"
#include "compat.h"
#include "engine.h"
#include "gamefuncs.h"
#include "names.h"
#include "view.h"
#include "status.h"
#include "exhumed.h"
#include "player.h"
#include "aistuff.h"
#include "sound.h"
#include "mapinfo.h"
#include "v_video.h"
#include "interpolate.h"
#include "v_draw.h"
#include "render.h"
#include <string.h>
EXTERN_CVAR(Bool, testnewrenderer)
BEGIN_PS_NS
short bSubTitles = true;
int zbob;
short dVertPan[kMaxPlayers];
int nCamerax;
int nCameray;
int nCameraz;
short bTouchFloor;
short nQuake[kMaxPlayers] = { 0 };
short nChunkTotal = 0;
binangle nCameraa;
fixedhoriz nCamerapan;
short nViewTop;
bool bCamera = false;
int viewz;
DExhumedActor* pEnemy;
short nEnemyPal = 0;
// We cannot drag these through the entire event system... :(
spritetype* mytsprite;
int* myspritesortcnt;
// NOTE - not to be confused with Ken's analyzesprites()
static void analyzesprites(spritetype* tsprite, int& spritesortcnt, int x, int y, int z, double const smoothratio)
{
tspritetype *pTSprite;
mytsprite = tsprite;
myspritesortcnt = &spritesortcnt;
for (int i = 0; i < spritesortcnt; i++) {
pTSprite = &tsprite[i];
if (pTSprite->owner != -1)
{
// interpolate sprite position
pTSprite->pos = pTSprite->interpolatedvec3(smoothratio);
pTSprite->ang = pTSprite->interpolatedang(smoothratio);
}
}
auto pPlayerActor = PlayerList[nLocalPlayer].Actor();
int var_38 = 20;
int var_2C = 30000;
spritetype *pPlayerSprite = &pPlayerActor->s();
bestTarget = nullptr;
short nSector = pPlayerSprite->sectnum;
int nAngle = (2048 - pPlayerSprite->ang) & kAngleMask;
int nTSprite;
// int var_20 = var_24;
for (nTSprite = spritesortcnt-1, pTSprite = &tsprite[nTSprite]; nTSprite >= 0; nTSprite--, pTSprite--)
{
int nSprite = pTSprite->owner;
auto pActor = &exhumedActors[nSprite];
spritetype *pSprite = &pActor->s();
if (pTSprite->sectnum >= 0)
{
sectortype *pSector = &sector[pTSprite->sectnum];
int nSectShade = (pSector->ceilingstat & 1) ? pSector->ceilingshade : pSector->floorshade;
int nShade = pTSprite->shade + nSectShade + 6;
pTSprite->shade = clamp(nShade, -128, 127);
}
pTSprite->pal = RemapPLU(pTSprite->pal);
// PowerSlaveGDX: Torch bouncing fix
if ((pTSprite->picnum == kTorch1 || pTSprite->picnum == kTorch2) && (pTSprite->cstat & 0x80) == 0)
{
pTSprite->cstat |= 0x80;
int nTileY = (tileHeight(pTSprite->picnum) * pTSprite->yrepeat) * 2;
pTSprite->z -= nTileY;
}
if (pSprite->statnum > 0)
{
RunListEvent ev{};
ev.pTSprite = pTSprite;
runlist_SignalRun(pSprite->lotag - 1, nTSprite, &ExhumedAI::Draw, &ev);
if ((pSprite->statnum < 150) && (pSprite->cstat & 0x101) && (pActor != pPlayerActor))
{
int xval = pSprite->x - x;
int yval = pSprite->y - y;
int vcos = bcos(nAngle);
int vsin = bsin(nAngle);
int edx = ((vcos * yval) + (xval * vsin)) >> 14;
int ebx = abs(((vcos * xval) - (yval * vsin)) >> 14);
if (!ebx)
continue;
edx = (abs(edx) * 32) / ebx;
if (ebx < 1000 && ebx < var_2C && edx < 10)
{
bestTarget = pActor;
var_38 = edx;
var_2C = ebx;
}
else if (ebx < 30000)
{
int t = var_38 - edx;
if (t > 3 || (ebx < var_2C && abs(t) < 5))
{
var_38 = edx;
var_2C = ebx;
bestTarget = pActor;
}
}
}
}
}
if (bestTarget != nullptr)
{
spritetype *pTarget = &bestTarget->s();
nCreepyTimer = kCreepyCount;
if (!cansee(x, y, z, nSector, pTarget->x, pTarget->y, pTarget->z - GetActorHeight(bestTarget), pTarget->sectnum))
{
bestTarget = nullptr;
}
}
mytsprite = nullptr;
myspritesortcnt = nullptr;
}
void ResetView()
{
EraseScreen(overscanindex);
#ifdef USE_OPENGL
videoTintBlood(0, 0, 0);
#endif
}
static TextOverlay subtitleOverlay;
void DrawView(double smoothRatio, bool sceneonly)
{
int playerX;
int playerY;
int playerZ;
int nSector;
binangle nAngle, rotscrnang;
fixedhoriz pan;
fixed_t dang = IntToFixed(1024);
zbob = bsin(2 * bobangle, -3);
DoInterpolations(smoothRatio / 65536.);
pm_smoothratio = (int)smoothRatio;
auto pPlayerActor = PlayerList[nLocalPlayer].Actor();
auto pPlayerSprite = &pPlayerActor->s();
int nPlayerOldCstat = pPlayerSprite->cstat;
auto pDop = &PlayerList[nLocalPlayer].pDoppleSprite->s();
int nDoppleOldCstat = pDop->cstat;
if (nSnakeCam >= 0 && !sceneonly)
{
auto pActor = SnakeList[nSnakeCam].pSprites[0];
auto pSprite = &pActor->s();
playerX = pSprite->x;
playerY = pSprite->y;
playerZ = pSprite->z;
nSector = pSprite->sectnum;
nAngle = buildang(pSprite->ang);
rotscrnang = buildang(0);
SetGreenPal();
pEnemy = SnakeList[nSnakeCam].pEnemy;
if (pEnemy == nullptr || totalmoves & 1)
{
nEnemyPal = -1;
}
else
{
nEnemyPal = pEnemy->s().pal;
pEnemy->s().pal = 5;
}
}
else
{
playerX = pPlayerSprite->interpolatedx(smoothRatio);
playerY = pPlayerSprite->interpolatedy(smoothRatio);
playerZ = pPlayerSprite->interpolatedz(smoothRatio) + interpolatedvalue(PlayerList[nLocalPlayer].oeyelevel, PlayerList[nLocalPlayer].eyelevel, smoothRatio);
nSector = PlayerList[nLocalPlayer].nPlayerViewSect;
updatesector(playerX, playerY, &nSector);
if (!SyncInput())
{
pan = PlayerList[nLocalPlayer].horizon.sum();
nAngle = PlayerList[nLocalPlayer].angle.sum();
rotscrnang = PlayerList[nLocalPlayer].angle.rotscrnang;
}
else
{
pan = PlayerList[nLocalPlayer].horizon.interpolatedsum(smoothRatio);
nAngle = PlayerList[nLocalPlayer].angle.interpolatedsum(smoothRatio);
rotscrnang = PlayerList[nLocalPlayer].angle.interpolatedrotscrn(smoothRatio);
}
if (!bCamera)
{
pPlayerSprite->cstat |= CSTAT_SPRITE_INVISIBLE;
pDop->cstat |= CSTAT_SPRITE_INVISIBLE;
}
else
{
pPlayerSprite->cstat |= CSTAT_SPRITE_TRANSLUCENT;
pDop->cstat |= CSTAT_SPRITE_INVISIBLE;
}
pan = q16horiz(clamp(pan.asq16(), gi->playerHorizMin(), gi->playerHorizMax()));
}
nCameraa = nAngle;
if (nSnakeCam >= 0 && !sceneonly)
{
pan = q16horiz(0);
viewz = playerZ;
}
else
{
viewz = playerZ + nQuake[nLocalPlayer];
int floorZ = sector[pPlayerSprite->sectnum].floorz;
if (viewz > floorZ)
viewz = floorZ;
nCameraa += buildang((nQuake[nLocalPlayer] >> 7) % 31);
if (bCamera)
{
viewz -= 2560;
if (!calcChaseCamPos(&playerX, &playerY, &viewz, pPlayerSprite, &nSector, nAngle, pan, smoothRatio))
{
viewz += 2560;
calcChaseCamPos(&playerX, &playerY, &viewz, pPlayerSprite, &nSector, nAngle, pan, smoothRatio);
}
}
}
nCamerax = playerX;
nCameray = playerY;
nCameraz = playerZ;
int Z = sector[nSector].ceilingz + 256;
if (Z <= viewz)
{
Z = sector[nSector].floorz - 256;
if (Z < viewz)
viewz = Z;
}
else {
viewz = Z;
}
nCamerapan = pan;
if (nFreeze == 2 || nFreeze == 1)
{
nSnakeCam = -1;
videoSetViewableArea(0, 0, xdim - 1, ydim - 1);
}
UpdateMap();
if (nFreeze != 3)
{
static uint8_t sectorFloorPal[MAXSECTORS];
static uint8_t sectorCeilingPal[MAXSECTORS];
static uint8_t wallPal[MAXWALLS];
int const viewingRange = viewingrange;
int const vr = xs_CRoundToInt(65536. * tan(r_fov * (pi::pi() / 360.)));
videoSetCorrectedAspect();
renderSetAspect(MulScale(vr, viewingrange, 16), yxaspect);
if (HavePLURemap())
{
for (int i = 0; i < numsectors; i++)
{
sectorFloorPal[i] = sector[i].floorpal;
sectorCeilingPal[i] = sector[i].ceilingpal;
sector[i].floorpal = RemapPLU(sectorFloorPal[i]);
sector[i].ceilingpal = RemapPLU(sectorCeilingPal[i]);
}
for (int i = 0; i < numwalls; i++)
{
wallPal[i] = wall[i].pal;
wall[i].pal = RemapPLU(wallPal[i]);
}
}
if (!testnewrenderer)
{
renderSetRollAngle((float)rotscrnang.asbuildf());
renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa.asq16(), nCamerapan.asq16(), nSector);
analyzesprites(pm_tsprite, pm_spritesortcnt, nCamerax, nCameray, viewz, smoothRatio);
renderDrawMasks();
}
else
{
render_drawrooms(nullptr, { nCamerax, nCameray, viewz }, nSector, nCameraa, nCamerapan, rotscrnang, smoothRatio);
}
if (HavePLURemap())
{
for (int i = 0; i < numsectors; i++)
{
sector[i].floorpal = sectorFloorPal[i];
sector[i].ceilingpal = sectorCeilingPal[i];
}
for (int i = 0; i < numwalls; i++)
{
wall[i].pal = wallPal[i];
}
}
if (nFreeze)
{
nSnakeCam = -1;
if (nFreeze == 2)
{
if (nHeadStage == 4)
{
nHeadStage = 5;
pPlayerSprite->cstat |= 0x8000;
int ang2 = nCameraa.asbuild() - pPlayerSprite->ang;
if (ang2 < 0)
ang2 = -ang2;
if (ang2 > 10)
{
inita -= (ang2 >> 3);
return;
}
if (bSubTitles)
{
subtitleOverlay.Start(I_GetTimeNS() * (120. / 1'000'000'000));
subtitleOverlay.ReadyCinemaText(currentLevel->ex_ramses_text);
}
inputState.ClearAllInput();
}
else if (nHeadStage == 5)
{
if ((bSubTitles && !subtitleOverlay.AdvanceCinemaText(I_GetTimeNS() * (120. / 1'000'000'000))) || inputState.CheckAllInput())
{
inputState.ClearAllInput();
LevelFinished();
EndLevel = 1;
if (CDplaying()) {
fadecdaudio();
}
}
else subtitleOverlay.DisplayText();
}
}
}
else if (!sceneonly)
{
if (nSnakeCam < 0)
{
DrawWeapons(smoothRatio);
DrawMap(smoothRatio);
}
else
{
RestoreGreenPal();
if (nEnemyPal > -1) {
pEnemy->s().pal = (uint8_t)nEnemyPal;
}
DrawMap(smoothRatio);
}
}
}
else
{
twod->ClearScreen();
}
pPlayerSprite->cstat = nPlayerOldCstat;
pDop->cstat = nDoppleOldCstat;
RestoreInterpolations();
flash = 0;
}
bool GameInterface::GenerateSavePic()
{
DrawView(65536, true);
return true;
}
void GameInterface::processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio)
{
analyzesprites(tsprite, spritesortcnt, viewx, viewy, viewz, smoothRatio);
}
void NoClip()
{
}
void Clip()
{
}
void SerializeView(FSerializer& arc)
{
arc("camerax", nCamerax)
("cameray", nCameray)
("cameraz", nCameraz)
("touchfloor", bTouchFloor)
("chunktotal", nChunkTotal)
("cameraa", nCameraa)
("camerapan", nCamerapan)
("camera", bCamera)
("viewz", viewz)
("enemy", pEnemy)
("enemypal", nEnemyPal)
.Array("vertpan", dVertPan, countof(dVertPan))
.Array("quake", nQuake, countof(nQuake))
.EndObject();
}
END_PS_NS