raze/source/blood/src/view.cpp
Christoph Oelckers 3455610031 - base palette cleanup.
Avoid passing this anywhere in the client code. It should only be set right before rendering the 3D view and the only code using the base palette should be the 3D renderer and hud_drawsprite.
Also make the palette override CVARs 3D view only in debug mode.
2020-08-14 21:18:14 +02:00

1298 lines
40 KiB
C++

//-------------------------------------------------------------------------
/*
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!
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "build.h"
#include "pragmas.h"
#include "mmulti.h"
#include "v_font.h"
#include "endgame.h"
#include "aistate.h"
#include "map2d.h"
#include "loadsave.h"
#include "sectorfx.h"
#include "choke.h"
#include "view.h"
#include "nnexts.h"
#include "zstring.h"
#include "menu.h"
#include "gstrings.h"
#include "v_2ddrawer.h"
#include "v_video.h"
#include "v_font.h"
#include "glbackend/glbackend.h"
BEGIN_BLD_NS
VIEW gPrevView[kMaxPlayers];
VIEWPOS gViewPos;
int gViewIndex;
struct INTERPOLATE {
void *pointer;
int value;
int value2;
INTERPOLATE_TYPE type;
};
int gViewMode = 3;
int gViewSize = 2;
double gInterpolate;
int nInterpolations;
char gInterpolateSprite[(kMaxSprites+7)>>3];
char gInterpolateWall[(kMaxWalls+7)>>3];
char gInterpolateSector[(kMaxSectors+7)>>3];
#define kMaxInterpolations 16384
INTERPOLATE gInterpolation[kMaxInterpolations];
int gViewXCenter, gViewYCenter;
int gViewX0, gViewY0, gViewX1, gViewY1;
int gViewX0S, gViewY0S, gViewX1S, gViewY1S;
int xscale, yscale, xstep, ystep;
int gScreenTilt;
FFont *gFont[kFontNum];
void FontSet(int id, int tile, int space)
{
if (id < 0 || id >= kFontNum || tile < 0 || tile >= kMaxTiles)
return;
GlyphSet glyphs;
for (int i = 1; i < 96; i++)
{
auto tex = tileGetTexture(tile + i);
if (tex && tex->isValid() && tex->GetTexelWidth() > 0 && tex->GetTexelHeight() > 0)
{
glyphs.Insert(i + 32, tex);
tex->SetOffsetsNotForFont();
}
}
const char *names[] = { "smallfont", "bigfont", "gothfont", "smallfont2", "digifont"};
const char *defs[] = { "defsmallfont", "defbigfont", nullptr, "defsmallfont2", nullptr};
FFont ** ptrs[] = { &SmallFont, &BigFont, nullptr, &SmallFont2, nullptr};
gFont[id] = new ::FFont(names[id], nullptr, defs[id], 0, 0, 0, 0, tileWidth(tile), false, false, false, &glyphs);
gFont[id]->SetKerning(space);
if (ptrs[id]) *ptrs[id] = gFont[id];
}
void viewToggle(int viewMode)
{
if (viewMode == 3)
gViewMode = 4;
else
{
gViewMode = 3;
viewResizeView(gViewSize);
}
}
void viewBackupView(int nPlayer)
{
PLAYER *pPlayer = &gPlayer[nPlayer];
VIEW *pView = &gPrevView[nPlayer];
pView->at30 = pPlayer->q16ang;
pView->at50 = pPlayer->pSprite->x;
pView->at54 = pPlayer->pSprite->y;
pView->at38 = pPlayer->zView;
pView->at34 = pPlayer->zWeapon-pPlayer->zView-0xc00;
pView->at24 = pPlayer->q16horiz;
pView->at28 = pPlayer->q16slopehoriz;
pView->at2c = pPlayer->slope;
pView->at8 = pPlayer->bobHeight;
pView->atc = pPlayer->bobWidth;
pView->at18 = pPlayer->swayHeight;
pView->at1c = pPlayer->swayWidth;
}
void viewCorrectViewOffsets(int nPlayer, vec3_t const *oldpos)
{
PLAYER *pPlayer = &gPlayer[nPlayer];
VIEW *pView = &gPrevView[nPlayer];
pView->at50 += pPlayer->pSprite->x-oldpos->x;
pView->at54 += pPlayer->pSprite->y-oldpos->y;
pView->at38 += pPlayer->pSprite->z-oldpos->z;
}
void viewClearInterpolations(void)
{
nInterpolations = 0;
memset(gInterpolateSprite, 0, sizeof(gInterpolateSprite));
memset(gInterpolateWall, 0, sizeof(gInterpolateWall));
memset(gInterpolateSector, 0, sizeof(gInterpolateSector));
}
void viewAddInterpolation(void *data, INTERPOLATE_TYPE type)
{
if (nInterpolations == kMaxInterpolations)
ThrowError("Too many interpolations");
INTERPOLATE *pInterpolate = &gInterpolation[nInterpolations++];
pInterpolate->pointer = data;
pInterpolate->type = type;
switch (type)
{
case INTERPOLATE_TYPE_INT:
pInterpolate->value = *((int*)data);
break;
case INTERPOLATE_TYPE_SHORT:
pInterpolate->value = *((short*)data);
break;
}
}
void CalcInterpolations(void)
{
int i;
INTERPOLATE *pInterpolate = gInterpolation;
for (i = 0; i < nInterpolations; i++, pInterpolate++)
{
switch (pInterpolate->type)
{
case INTERPOLATE_TYPE_INT:
{
pInterpolate->value2 = *((int*)pInterpolate->pointer);
int newValue = interpolate(pInterpolate->value, *((int*)pInterpolate->pointer), gInterpolate);
*((int*)pInterpolate->pointer) = newValue;
break;
}
case INTERPOLATE_TYPE_SHORT:
{
pInterpolate->value2 = *((short*)pInterpolate->pointer);
int newValue = interpolate(pInterpolate->value, *((short*)pInterpolate->pointer), gInterpolate);
*((short*)pInterpolate->pointer) = newValue;
break;
}
}
}
}
void RestoreInterpolations(void)
{
int i;
INTERPOLATE *pInterpolate = gInterpolation;
for (i = 0; i < nInterpolations; i++, pInterpolate++)
{
switch (pInterpolate->type)
{
case INTERPOLATE_TYPE_INT:
*((int*)pInterpolate->pointer) = pInterpolate->value2;
break;
case INTERPOLATE_TYPE_SHORT:
*((short*)pInterpolate->pointer) = pInterpolate->value2;
break;
}
}
}
void viewDrawText(int nFont, const char *pString, int x, int y, int nShade, int nPalette, int position, char shadow, unsigned int nStat, uint8_t alpha)
{
if (nFont < 0 || nFont >= kFontNum || !pString) return;
FFont *pFont = gFont[nFont];
//y += pFont->yoff;
if (position == 1) x -= pFont->StringWidth(pString) / 2;
if (position == 2) x -= pFont->StringWidth(pString);
if (shadow)
{
DrawText(twod, pFont, CR_UNDEFINED, x+1, y+1, pString, DTA_FullscreenScale, FSMode_ScaleToFit43, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200, DTA_Color, 0xff000000, DTA_Alpha, 0.5, TAG_DONE);
}
DrawText(twod, pFont, CR_UNDEFINED, x, y, pString, DTA_FullscreenScale, FSMode_ScaleToFit43, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, nPalette),
DTA_Color, shadeToLight(nShade), DTA_Alpha, alpha / 255., TAG_DONE);
}
void InitStatusBar(void)
{
tileLoadTile(2200);
}
GameStats GameInterface::getStats()
{
return { gKillMgr.at4, gKillMgr.at0, gSecretMgr.at4, gSecretMgr.at0, gLevelTime / kTicsPerSec, gPlayer[myconnectindex].fragCount };
}
void viewDrawMapTitle(void)
{
if (!hud_showmapname || M_Active())
return;
int const fadeStartTic = kTicsPerSec;
int const fadeEndTic = int(1.5f*kTicsPerSec);
if (gLevelTime > fadeEndTic)
return;
int const alpha = 255 - clamp((gLevelTime-fadeStartTic)*255/(fadeEndTic-fadeStartTic), 0, 255);
if (alpha != 0)
{
viewDrawText(1, currentLevel->DisplayName(), 160, 50, -128, 0, 1, 1, 0, alpha);
}
}
void viewDrawAimedPlayerName(void)
{
if (!cl_idplayers || (gView->aim.dx == 0 && gView->aim.dy == 0))
return;
int hit = HitScan(gView->pSprite, gView->pSprite->z, gView->aim.dx, gView->aim.dy, gView->aim.dz, CLIPMASK0, 512);
if (hit == 3)
{
spritetype* pSprite = &sprite[gHitInfo.hitsprite];
if (IsPlayerSprite(pSprite))
{
char nPlayer = pSprite->type-kDudePlayer1;
char* szName = gProfile[nPlayer].name;
int nPalette = (gPlayer[nPlayer].teamId&3)+11;
viewDrawText(4, szName, 160, 125, -128, nPalette, 1, 1);
}
}
}
void viewPrecacheTiles(void)
{
tilePrecacheTile(2173, 0);
tilePrecacheTile(2200, 0);
tilePrecacheTile(2201, 0);
tilePrecacheTile(2202, 0);
tilePrecacheTile(2207, 0);
tilePrecacheTile(2208, 0);
tilePrecacheTile(2209, 0);
tilePrecacheTile(2229, 0);
tilePrecacheTile(2260, 0);
tilePrecacheTile(2559, 0);
tilePrecacheTile(2169, 0);
tilePrecacheTile(2578, 0);
tilePrecacheTile(2586, 0);
tilePrecacheTile(2602, 0);
for (int i = 0; i < 10; i++)
{
tilePrecacheTile(2190 + i, 0);
tilePrecacheTile(2230 + i, 0);
tilePrecacheTile(2240 + i, 0);
tilePrecacheTile(2250 + i, 0);
tilePrecacheTile(kSBarNumberHealth + i, 0);
tilePrecacheTile(kSBarNumberAmmo + i, 0);
tilePrecacheTile(kSBarNumberInv + i, 0);
tilePrecacheTile(kSBarNumberArmor1 + i, 0);
tilePrecacheTile(kSBarNumberArmor2 + i, 0);
tilePrecacheTile(kSBarNumberArmor3 + i, 0);
}
/*
for (int i = 0; i < 5; i++)
{
tilePrecacheTile(gPackIcons[i], 0);
tilePrecacheTile(gPackIcons2[i].nTile, 0);
}
*/
for (int i = 0; i < 6; i++)
{
tilePrecacheTile(2220 + i, 0);
tilePrecacheTile(2552 + i, 0);
}
}
static TArray<uint8_t> lensdata;
int *lensTable;
int gZoom = 1024;
extern int dword_172CE0[16][3];
void viewInit(void)
{
Printf("Initializing status bar\n");
InitStatusBar();
FontSet(0, 4096, 0);
FontSet(1, 4192, 1);
FontSet(2, 4288, 1);
FontSet(3, 4384, 1);
FontSet(4, 4480, 0);
enginePostInit(); // This must not be done earlier!
lensdata = fileSystem.LoadFile("lens.dat");
dassert(lensdata.Size() == kLensSize * kLensSize * sizeof(int));
lensTable = (int*)lensdata.Data();
#if B_BIG_ENDIAN == 1
for (int i = 0; i < kLensSize*kLensSize; i++)
{
lensTable[i] = LittleLong(lensTable[i]);
}
#endif
uint8_t *data = tileAllocTile(4077, kLensSize, kLensSize);
memset(data, TRANSPARENT_INDEX, kLensSize*kLensSize);
for (int i = 0; i < 16; i++)
{
dword_172CE0[i][0] = mulscale16(wrand(), 2048);
dword_172CE0[i][1] = mulscale16(wrand(), 2048);
dword_172CE0[i][2] = mulscale16(wrand(), 2048);
}
gViewMap.sub_25C38(0, 0, gZoom, 0, gFollowMap);
}
void viewResizeView(int size)
{
int xdimcorrect = ClipHigh(scale(ydim, 4, 3), xdim);
gViewXCenter = xdim-xdim/2;
gViewYCenter = ydim-ydim/2;
xscale = divscale16(xdim, 320);
int xscalecorrect = divscale16(xdimcorrect, 320);
yscale = divscale16(ydim, 200);
xstep = divscale16(320, xdim);
ystep = divscale16(200, ydim);
gViewSize = ClipRange(size, 0, 7);
if (gViewSize <= 2)
{
gViewX0 = 0;
gViewX1 = xdim-1;
gViewY0 = 0;
gViewY1 = ydim-1;
if (gGameOptions.nGameType > 0 && gGameOptions.nGameType <= 3)
{
gViewY0 = (tilesiz[2229].y*ydim*((gNetPlayers+3)/4))/200;
}
gViewX0S = divscale16(gViewX0, xscalecorrect);
gViewY0S = divscale16(gViewY0, yscale);
gViewX1S = divscale16(gViewX1, xscalecorrect);
gViewY1S = divscale16(gViewY1, yscale);
}
else
{
gViewX0 = 0;
gViewY0 = 0;
gViewX1 = xdim-1;
int gy1 = (25 * ydim) / 200;
if (gViewSize == 3) // full status bar must scale the bottom to the actual display height.
gy1 = Scale(gy1, hud_scale, 100);
gViewY1 = ydim-1- gy1;
if (gGameOptions.nGameType > 0 && gGameOptions.nGameType <= 3)
{
gViewY0 = (tilesiz[2229].y*ydim*((gNetPlayers+3)/4))/200;
}
int height = gViewY1-gViewY0;
gViewX0 += mulscale16(xdim*(gViewSize-3),4096);
gViewX1 -= mulscale16(xdim*(gViewSize-3),4096);
gViewY0 += mulscale16(height*(gViewSize-3),4096);
gViewY1 -= mulscale16(height*(gViewSize-3),4096);
gViewX0S = divscale16(gViewX0, xscalecorrect);
gViewY0S = divscale16(gViewY0, yscale);
gViewX1S = divscale16(gViewX1, xscalecorrect);
gViewY1S = divscale16(gViewY1, yscale);
}
videoSetViewableArea(gViewX0, gViewY0, gViewX1, gViewY1);
}
void viewDrawInterface(ClockTicks arg)
{
UpdateStatusBar(arg);
}
int othercameradist = 1280;
int cameradist = -1;
int othercameraclock, cameraclock;
void CalcOtherPosition(spritetype *pSprite, int *pX, int *pY, int *pZ, int *vsectnum, int nAng, fix16_t zm)
{
int vX = mulscale30(-Cos(nAng), 1280);
int vY = mulscale30(-Sin(nAng), 1280);
int vZ = fix16_to_int(mulscale(zm, 1280, 3))-(16<<8);
int bakCstat = pSprite->cstat;
pSprite->cstat &= ~256;
dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
FindSector(*pX, *pY, *pZ, vsectnum);
short nHSector;
int hX, hY;
vec3_t pos = {*pX, *pY, *pZ};
hitdata_t hitdata;
hitscan(&pos, *vsectnum, vX, vY, vZ, &hitdata, CLIPMASK1);
nHSector = hitdata.sect;
hX = hitdata.pos.x;
hY = hitdata.pos.y;
int dX = hX-*pX;
int dY = hY-*pY;
if (klabs(vX)+klabs(vY) > klabs(dX)+klabs(dY))
{
*vsectnum = nHSector;
dX -= ksgn(vX)<<6;
dY -= ksgn(vY)<<6;
int nDist;
if (klabs(vX) > klabs(vY))
{
nDist = ClipHigh(divscale16(dX,vX), othercameradist);
}
else
{
nDist = ClipHigh(divscale16(dY,vY), othercameradist);
}
othercameradist = nDist;
}
*pX += mulscale16(vX, othercameradist);
*pY += mulscale16(vY, othercameradist);
*pZ += mulscale16(vZ, othercameradist);
othercameradist = ClipHigh(othercameradist+(((int)(totalclock-othercameraclock))<<10), 65536);
othercameraclock = (int)totalclock;
dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
FindSector(*pX, *pY, *pZ, vsectnum);
pSprite->cstat = bakCstat;
}
void CalcPosition(spritetype *pSprite, int *pX, int *pY, int *pZ, int *vsectnum, int nAng, fix16_t zm)
{
int vX = mulscale30(-Cos(nAng), 1280);
int vY = mulscale30(-Sin(nAng), 1280);
int vZ = fix16_to_int(mulscale(zm, 1280, 3))-(16<<8);
int bakCstat = pSprite->cstat;
pSprite->cstat &= ~256;
dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
FindSector(*pX, *pY, *pZ, vsectnum);
short nHSector;
int hX, hY;
hitscangoal.x = hitscangoal.y = 0x1fffffff;
vec3_t pos = { *pX, *pY, *pZ };
hitdata_t hitdata;
hitscan(&pos, *vsectnum, vX, vY, vZ, &hitdata, CLIPMASK1);
nHSector = hitdata.sect;
hX = hitdata.pos.x;
hY = hitdata.pos.y;
int dX = hX-*pX;
int dY = hY-*pY;
if (klabs(vX)+klabs(vY) > klabs(dX)+klabs(dY))
{
*vsectnum = nHSector;
dX -= ksgn(vX)<<6;
dY -= ksgn(vY)<<6;
int nDist;
if (klabs(vX) > klabs(vY))
{
nDist = ClipHigh(divscale16(dX,vX), cameradist);
}
else
{
nDist = ClipHigh(divscale16(dY,vY), cameradist);
}
cameradist = nDist;
}
*pX += mulscale16(vX, cameradist);
*pY += mulscale16(vY, cameradist);
*pZ += mulscale16(vZ, cameradist);
cameradist = ClipHigh(cameradist+(((int)(totalclock-cameraclock))<<10), 65536);
cameraclock = (int)totalclock;
dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
FindSector(*pX, *pY, *pZ, vsectnum);
pSprite->cstat = bakCstat;
}
// by NoOne: show warning msgs in game instead of throwing errors (in some cases)
void viewSetSystemMessage(const char* pMessage, ...) {
char buffer[1024]; va_list args; va_start(args, pMessage);
vsprintf(buffer, pMessage, args);
Printf(PRINT_HIGH | PRINT_NOTIFY, "%s\n", buffer); // print it also in console
}
void viewSetMessage(const char *pMessage, const int pal, const MESSAGE_PRIORITY priority)
{
int printlevel = priority < 0 ? PRINT_LOW : priority < MESSAGE_PRIORITY_SYSTEM ? PRINT_MEDIUM : PRINT_HIGH;
Printf(printlevel|PRINT_NOTIFY, "%s\n", pMessage);
}
char errMsg[256];
void viewSetErrorMessage(const char *pMessage)
{
if (!pMessage)
{
strcpy(errMsg, "");
}
else
{
strcpy(errMsg, pMessage);
}
}
void DoLensEffect(void)
{
// To investigate whether this can be implemented as a shader effect.
auto d = tileData(4077);
dassert(d != NULL);
auto s = tilePtr(4079);
dassert(s != NULL);
for (int i = 0; i < kLensSize*kLensSize; i++, d++)
if (lensTable[i] >= 0)
*d = s[lensTable[i]];
tileInvalidate(4077, -1, -1);
}
void UpdateDacs(int nPalette, bool bNoTint)
{
gLastPal = 0;
auto& tint = lookups.tables[MAXPALOOKUPS - 1];
tint.tintFlags = 0;
switch (nPalette)
{
case 0:
default:
tint.tintColor.r = 255;
tint.tintColor.g = 255;
tint.tintColor.b = 255;
break;
case 1:
tint.tintColor.r = 132;
tint.tintColor.g = 164;
tint.tintColor.b = 255;
break;
case 2:
tint.tintColor.r = 255;
tint.tintColor.g = 126;
tint.tintColor.b = 105;
break;
case 3:
tint.tintColor.r = 162;
tint.tintColor.g = 186;
tint.tintColor.b = 15;
break;
case 4:
tint.tintColor.r = 255;
tint.tintColor.g = 255;
tint.tintColor.b = 255;
break;
}
videoSetPalette(nPalette);
}
void UpdateBlend()
{
int nRed = 0;
int nGreen = 0;
int nBlue = 0;
nRed += gView->pickupEffect;
nGreen += gView->pickupEffect;
nBlue -= gView->pickupEffect;
nRed += ClipHigh(gView->painEffect, 85) * 2;
nGreen -= ClipHigh(gView->painEffect, 85) * 3;
nBlue -= ClipHigh(gView->painEffect, 85) * 3;
nRed -= gView->blindEffect;
nGreen -= gView->blindEffect;
nBlue -= gView->blindEffect;
nRed -= gView->chokeEffect >> 6;
nGreen -= gView->chokeEffect >> 5;
nBlue -= gView->chokeEffect >> 6;
nRed = ClipRange(nRed, -255, 255);
nGreen = ClipRange(nGreen, -255, 255);
nBlue = ClipRange(nBlue, -255, 255);
videoTintBlood(nRed, nGreen, nBlue);
}
char otherMirrorGotpic[2];
char bakMirrorGotpic[2];
// int gVisibility;
int deliriumTilt, deliriumTurn, deliriumPitch;
int gScreenTiltO, deliriumTurnO, deliriumPitchO;
int gShowFrameRate = 1;
void viewUpdateDelirium(void)
{
gScreenTiltO = gScreenTilt;
deliriumTurnO = deliriumTurn;
deliriumPitchO = deliriumPitch;
int powerCount;
if ((powerCount = powerupCheck(gView, kPwUpDeliriumShroom)) != 0)
{
int tilt1 = 170, tilt2 = 170, pitch = 20;
int timer = (int)gFrameClock*4;
if (powerCount < 512)
{
int powerScale = (powerCount<<16) / 512;
tilt1 = mulscale16(tilt1, powerScale);
tilt2 = mulscale16(tilt2, powerScale);
pitch = mulscale16(pitch, powerScale);
}
int sin2 = costable[(2*timer-512)&2047] / 2;
int sin3 = costable[(3*timer-512)&2047] / 2;
gScreenTilt = mulscale30(sin2+sin3,tilt1);
int sin4 = costable[(4*timer-512)&2047] / 2;
deliriumTurn = mulscale30(sin3+sin4,tilt2);
int sin5 = costable[(5*timer-512)&2047] / 2;
deliriumPitch = mulscale30(sin4+sin5,pitch);
return;
}
gScreenTilt = ((gScreenTilt+1024)&2047)-1024;
if (gScreenTilt > 0)
{
gScreenTilt -= 8;
if (gScreenTilt < 0)
gScreenTilt = 0;
}
else if (gScreenTilt < 0)
{
gScreenTilt += 8;
if (gScreenTilt >= 0)
gScreenTilt = 0;
}
}
int shakeHoriz, shakeAngle, shakeX, shakeY, shakeZ, shakeBobX, shakeBobY;
void viewUpdateShake(void)
{
shakeHoriz = 0;
shakeAngle = 0;
shakeX = 0;
shakeY = 0;
shakeZ = 0;
shakeBobX = 0;
shakeBobY = 0;
if (gView->flickerEffect)
{
int nValue = ClipHigh(gView->flickerEffect * 8, 2000);
shakeHoriz += QRandom2(nValue >> 8);
shakeAngle += QRandom2(nValue >> 8);
shakeX += QRandom2(nValue >> 4);
shakeY += QRandom2(nValue >> 4);
shakeZ += QRandom2(nValue);
shakeBobX += QRandom2(nValue);
shakeBobY += QRandom2(nValue);
}
if (gView->quakeEffect)
{
int nValue = ClipHigh(gView->quakeEffect * 8, 2000);
shakeHoriz += QRandom2(nValue >> 8);
shakeAngle += QRandom2(nValue >> 8);
shakeX += QRandom2(nValue >> 4);
shakeY += QRandom2(nValue >> 4);
shakeZ += QRandom2(nValue);
shakeBobX += QRandom2(nValue);
shakeBobY += QRandom2(nValue);
}
}
int gLastPal = 0;
int32_t g_frameRate;
void viewDrawScreen(bool sceneonly)
{
int nPalette = 0;
static ClockTicks lastUpdate;
int defaultHoriz = r_horizcenter ? 100 : 90;
#ifdef USE_OPENGL
polymostcenterhoriz = defaultHoriz;
#endif
ClockTicks delta = totalclock - lastUpdate;
if (delta < 0)
delta = 0;
lastUpdate = totalclock;
if (!paused && (!M_Active() || gGameOptions.nGameType != 0))
{
gInterpolate = CalcSmoothRatio(totalclock, gNetFifoClock - 4, 30);
}
else gInterpolate = 65536;
if (cl_interpolate)
{
CalcInterpolations();
}
if (gViewMode == 3 || gViewMode == 4 || gOverlayMap)
{
DoSectorLighting();
}
if (gViewMode == 3 || gOverlayMap)
{
int basepal = 0;
if (powerupCheck(gView, kPwUpDeathMask) > 0) basepal = 4;
else if (powerupCheck(gView, kPwUpReflectShots) > 0) basepal = 1;
else if (gView->isUnderwater) {
if (gView->nWaterPal) basepal = gView->nWaterPal;
else {
if (gView->pXSprite->medium == kMediumWater) basepal = 1;
else if (gView->pXSprite->medium == kMediumGoo) basepal = 3;
else basepal = 2;
}
}
UpdateDacs(basepal);
UpdateBlend();
int yxAspect = yxaspect;
int viewingRange = viewingrange;
if (r_usenewaspect)
{
newaspect_enable = 1;
videoSetCorrectedAspect();
}
int v1 = Blrintf(double(viewingrange) * tan(r_fov * (PI / 360.)));
renderSetAspect(v1, yxaspect);
int cX = gView->pSprite->x;
int cY = gView->pSprite->y;
int cZ = gView->zView;
double zDelta = gView->zWeapon - gView->zView - (12 << 8);
fix16_t cA = gView->q16ang;
fix16_t q16horiz = gView->q16horiz;
fix16_t q16slopehoriz = gView->q16slopehoriz;
int v74 = gView->bobWidth;
int v8c = gView->bobHeight;
double v4c = gView->swayWidth;
double v48 = gView->swayHeight;
int nSectnum = gView->pSprite->sectnum;
if (cl_interpolate)
{
if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0)
{
nSectnum = predict.at68;
cX = interpolate(predictOld.at50, predict.at50, gInterpolate);
cY = interpolate(predictOld.at54, predict.at54, gInterpolate);
cZ = interpolate(predictOld.at38, predict.at38, gInterpolate);
zDelta = finterpolate(predictOld.at34, predict.at34, gInterpolate);
cA = interpolateangfix16(predictOld.at30, predict.at30, gInterpolate);
q16horiz = interpolate(predictOld.at24, predict.at24, gInterpolate);
q16slopehoriz = interpolate(predictOld.at28, predict.at28, gInterpolate);
v74 = interpolate(predictOld.atc, predict.atc, gInterpolate);
v8c = interpolate(predictOld.at8, predict.at8, gInterpolate);
v4c = finterpolate(predictOld.at1c, predict.at1c, gInterpolate);
v48 = finterpolate(predictOld.at18, predict.at18, gInterpolate);
}
else
{
VIEW* pView = &gPrevView[gViewIndex];
cX = interpolate(pView->at50, cX, gInterpolate);
cY = interpolate(pView->at54, cY, gInterpolate);
cZ = interpolate(pView->at38, cZ, gInterpolate);
zDelta = finterpolate(pView->at34, zDelta, gInterpolate);
cA = interpolateangfix16(pView->at30, cA, gInterpolate);
q16horiz = interpolate(pView->at24, q16horiz, gInterpolate);
q16slopehoriz = interpolate(pView->at28, q16slopehoriz, gInterpolate);
v74 = interpolate(pView->atc, v74, gInterpolate);
v8c = interpolate(pView->at8, v8c, gInterpolate);
v4c = finterpolate(pView->at1c, v4c, gInterpolate);
v48 = finterpolate(pView->at18, v48, gInterpolate);
}
}
if (gView == gMe && (numplayers <= 1 || gPrediction) && gView->pXSprite->health != 0 && !VanillaMode())
{
int upAngle = 289;
int downAngle = -347;
fix16_t q16look;
cA = gViewAngle;
q16look = gViewLook;
q16horiz = fix16_from_float(100.f * tanf(fix16_to_float(q16look) * fPI / 1024.f));
}
viewUpdateShake();
q16horiz += fix16_from_int(shakeHoriz);
cA += fix16_from_int(shakeAngle);
cX += shakeX;
cY += shakeY;
cZ += shakeZ;
v4c += shakeBobX;
v48 += shakeBobY;
q16horiz += fix16_from_int(mulscale30(0x40000000 - Cos(gView->tiltEffect << 2), 30));
if (gViewPos == 0)
{
if (cl_viewbob)
{
if (cl_viewhbob)
{
cX -= mulscale30(v74, Sin(fix16_to_int(cA))) >> 4;
cY += mulscale30(v74, Cos(fix16_to_int(cA))) >> 4;
}
if (cl_viewvbob)
{
cZ += v8c;
}
}
if (cl_slopetilting)
{
q16horiz += q16slopehoriz;
}
cZ += fix16_to_int(q16horiz * 10);
cameradist = -1;
cameraclock = (int)totalclock;
}
else
{
CalcPosition(gView->pSprite, (int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum, fix16_to_int(cA), q16horiz);
}
CheckLink((int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum);
int v78 = interpolateang(gScreenTiltO, gScreenTilt, gInterpolate);
uint8_t v14 = 0;
uint8_t v10 = 0;
bool bDelirium = powerupCheck(gView, kPwUpDeliriumShroom) > 0;
static bool bDeliriumOld = false;
//int tiltcs, tiltdim;
uint8_t v4 = powerupCheck(gView, kPwUpCrystalBall) > 0;
#ifdef USE_OPENGL
renderSetRollAngle(0);
#endif
if (v78 || bDelirium)
{
renderSetRollAngle(v78);
}
else if (v4 && gNetPlayers > 1)
{
#if 0 // needs to be redone for pure hardware rendering.
int tmp = ((int)totalclock / 240) % (gNetPlayers - 1);
int i = connecthead;
while (1)
{
if (i == gViewIndex)
i = connectpoint2[i];
if (tmp == 0)
break;
i = connectpoint2[i];
tmp--;
}
PLAYER* pOther = &gPlayer[i];
//othercameraclock = gGameClock;
if (!tileData(4079))
{
tileAllocTile(4079, 128, 128);
}
r enderSetTarget(4079, 128, 128);
renderSetAspect(65536, 78643);
int vd8 = pOther->pSprite->x;
int vd4 = pOther->pSprite->y;
int vd0 = pOther->zView;
int vcc = pOther->pSprite->sectnum;
int v50 = pOther->pSprite->ang;
int v54 = 0;
if (pOther->flickerEffect)
{
int nValue = ClipHigh(pOther->flickerEffect * 8, 2000);
v54 += QRandom2(nValue >> 8);
v50 += QRandom2(nValue >> 8);
vd8 += QRandom2(nValue >> 4);
vd4 += QRandom2(nValue >> 4);
vd0 += QRandom2(nValue);
}
if (pOther->quakeEffect)
{
int nValue = ClipHigh(pOther->quakeEffect * 8, 2000);
v54 += QRandom2(nValue >> 8);
v50 += QRandom2(nValue >> 8);
vd8 += QRandom2(nValue >> 4);
vd4 += QRandom2(nValue >> 4);
vd0 += QRandom2(nValue);
}
CalcOtherPosition(pOther->pSprite, &vd8, &vd4, &vd0, &vcc, v50, 0);
CheckLink(&vd8, &vd4, &vd0, &vcc);
if (IsUnderwaterSector(vcc))
{
v14 = 10;
}
memcpy(bakMirrorGotpic, gotpic + 510, 2);
memcpy(gotpic + 510, otherMirrorGotpic, 2);
g_visibility = (int32_t)(ClipLow(gVisibility - 32 * pOther->visibility, 0));
int vc4, vc8;
getzsofslope(vcc, vd8, vd4, &vc8, &vc4);
if (vd0 >= vc4)
{
vd0 = vc4 - (gUpperLink[vcc] >= 0 ? 0 : (8 << 8));
}
if (vd0 <= vc8)
{
vd0 = vc8 + (gLowerLink[vcc] >= 0 ? 0 : (8 << 8));
}
v54 = ClipRange(v54, -200, 200);
RORHACKOTHER:
int ror_status[16];
for (int i = 0; i < 16; i++)
ror_status[i] = TestBitString(gotpic, 4080 + i);
yax_preparedrawrooms();
DrawMirrors(vd8, vd4, vd0, fix16_from_int(v50), fix16_from_int(v54 + defaultHoriz), gInterpolate, -1);
drawrooms(vd8, vd4, vd0, v50, v54 + defaultHoriz, vcc);
yax_drawrooms(viewProcessSprites, vcc, 0, gInterpolate);
bool do_ror_hack = false;
for (int i = 0; i < 16; i++)
if (ror_status[i] != TestBitString(gotpic, 4080 + i))
do_ror_hack = true;
if (do_ror_hack)
{
spritesortcnt = 0;
goto RORHACKOTHER;
}
memcpy(otherMirrorGotpic, gotpic+510, 2);
memcpy(gotpic+510, bakMirrorGotpic, 2);
viewProcessSprites(vd8, vd4, vd0, v50, gInterpolate);
renderDrawMasks();
renderRestoreTarget();
#endif
}
else
{
othercameraclock = (int)totalclock;
}
if (!bDelirium)
{
deliriumTilt = 0;
deliriumTurn = 0;
deliriumPitch = 0;
}
int nSprite = headspritestat[kStatExplosion];
int unk = 0;
while (nSprite >= 0)
{
spritetype* pSprite = &sprite[nSprite];
int nXSprite = pSprite->extra;
dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
XSPRITE* pXSprite = &xsprite[nXSprite];
if (TestBitString(gotsector, pSprite->sectnum))
{
unk += pXSprite->data3 * 32;
}
nSprite = nextspritestat[nSprite];
}
nSprite = headspritestat[kStatProjectile];
while (nSprite >= 0) {
spritetype* pSprite = &sprite[nSprite];
switch (pSprite->type) {
case kMissileFlareRegular:
case kMissileTeslaAlt:
case kMissileFlareAlt:
case kMissileTeslaRegular:
if (TestBitString(gotsector, pSprite->sectnum)) unk += 256;
break;
}
nSprite = nextspritestat[nSprite];
}
g_visibility = (int32_t)(ClipLow(gVisibility - 32 * gView->visibility - unk, 0));
cA = (cA + interpolateangfix16(fix16_from_int(deliriumTurnO), fix16_from_int(deliriumTurn), gInterpolate)) & 0x7ffffff;
int vfc, vf8;
getzsofslope(nSectnum, cX, cY, &vfc, &vf8);
if (cZ >= vf8)
{
cZ = vf8 - (gUpperLink[nSectnum] >= 0 ? 0 : (8 << 8));
}
if (cZ <= vfc)
{
cZ = vfc + (gLowerLink[nSectnum] >= 0 ? 0 : (8 << 8));
}
q16horiz = ClipRange(q16horiz, fix16_from_int(-200), fix16_from_int(200));
RORHACK:
int ror_status[16];
for (int i = 0; i < 16; i++)
ror_status[i] = TestBitString(gotpic, 4080 + i);
fix16_t deliriumPitchI = interpolate(fix16_from_int(deliriumPitchO), fix16_from_int(deliriumPitch), gInterpolate);
DrawMirrors(cX, cY, cZ, cA, q16horiz + fix16_from_int(defaultHoriz) + deliriumPitchI, gInterpolate, gViewIndex);
int bakCstat = gView->pSprite->cstat;
if (gViewPos == 0)
{
gView->pSprite->cstat |= 32768;
}
else
{
gView->pSprite->cstat |= 514;
}
renderDrawRoomsQ16(cX, cY, cZ, cA, q16horiz + fix16_from_int(defaultHoriz) + deliriumPitchI, nSectnum);
viewProcessSprites(cX, cY, cZ, fix16_to_int(cA), gInterpolate);
bool do_ror_hack = false;
for (int i = 0; i < 16; i++)
if (ror_status[i] != TestBitString(gotpic, 4080 + i))
do_ror_hack = true;
if (do_ror_hack)
{
gView->pSprite->cstat = bakCstat;
spritesortcnt = 0;
goto RORHACK;
}
sub_5571C(1);
int nSpriteSortCnt = spritesortcnt;
renderDrawMasks();
spritesortcnt = nSpriteSortCnt;
sub_5571C(0);
sub_557C4(cX, cY, gInterpolate);
renderDrawMasks();
gView->pSprite->cstat = bakCstat;
if ((v78 || bDelirium) && !sceneonly)
{
if (videoGetRenderMode() == REND_POLYMOST && gDeliriumBlur)
{
// todo: Implement using modern techniques instead of relying on deprecated old stuff that isn't well supported anymore.
/* names broken up so that searching for GL keywords won't find them anymore
if (!bDeliriumOld)
{
g lAccum(GL_LOAD, 1.f);
}
else
{
const float fBlur = pow(1.f/3.f, 30.f/g_frameRate);
g lAccum(GL _MULT, fBlur);
g lAccum(GL _ACCUM, 1.f-fBlur);
g lAccum(GL _RETURN, 1.f);
}
*/
}
}
bDeliriumOld = bDelirium && gDeliriumBlur;
if (r_usenewaspect)
newaspect_enable = 0;
renderSetAspect(viewingRange, yxAspect);
int nClipDist = gView->pSprite->clipdist << 2;
int ve8, vec, vf0, vf4;
GetZRange(gView->pSprite, &vf4, &vf0, &vec, &ve8, nClipDist, 0);
if (sceneonly) return;
#if 0
int tmpSect = nSectnum;
if ((vf0 & 0xc000) == 0x4000)
{
tmpSect = vf0 & (kMaxWalls - 1);
}
int v8 = byte_1CE5C2 > 0 && (sector[tmpSect].ceilingstat & 1);
if (gWeather.at12d8 > 0 || v8)
{
gWeather.Draw(cX, cY, cZ, cA, q16horiz + defaultHoriz + deliriumPitch, gWeather.at12d8);
if (v8)
{
gWeather.at12d8 = ClipRange(delta * 8 + gWeather.at12d8, 0, 4095);
}
else
{
gWeather.at12d8 = ClipRange(gWeather.at12d8 - delta * 64, 0, 4095);
}
}
#endif
hudDraw(gView, nSectnum, defaultHoriz, v4c, v48, zDelta, basepal);
}
UpdateDacs(0, true); // keep the view palette active only for the actual 3D view and its overlays.
if (gViewMode == 4)
{
gViewMap.sub_25DB0(gView->pSprite);
}
viewDrawInterface(delta);
int zn = ((gView->zWeapon-gView->zView-(12<<8))>>7)+220;
PLAYER *pPSprite = &gPlayer[gMe->pSprite->type-kDudePlayer1];
if (IsPlayerSprite(gMe->pSprite) && pPSprite->hand == 1)
{
//static int lastClock;
gChoke.sub_84110(160, zn);
//if ((gGameClock % 5) == 0 && gGameClock != lastClock)
//{
// gChoke.swayV(pPSprite);
//}
//lastClock = gGameClock;
}
#if 0
if (byte_1A76C6)
{
DrawStatSprite(2048, xdim-15, 20);
}
#endif
CalcFrameRate();
viewDrawMapTitle();
viewDrawAimedPlayerName();
if (paused)
{
viewDrawText(1, GStrings("TXTB_PAUSED"), 160, 10, 0, 0, 1, 0);
}
else if (gView != gMe)
{
FStringf gTempStr("] %s [", gProfile[gView->nPlayer].name);
viewDrawText(0, gTempStr, 160, 10, 0, 0, 1, 0);
}
if (errMsg[0])
{
viewDrawText(0, errMsg, 160, 20, 0, 0, 1, 0);
}
if (cl_interpolate)
{
RestoreInterpolations();
}
}
bool GameInterface::GenerateSavePic()
{
viewDrawScreen(true);
return true;
}
#define LOW_FPS 60
#define SLOW_FRAME_TIME 20
#if defined GEKKO
# define FPS_YOFFSET 16
#else
# define FPS_YOFFSET 0
#endif
FString GameInterface::statFPS(void)
{
FString output;
static int32_t frameCount;
static double cumulativeFrameDelay;
static double lastFrameTime;
static float lastFPS, minFPS = FLT_MAX, maxFPS;
static double minGameUpdate = DBL_MAX, maxGameUpdate;
double frameTime = timerGetHiTicks();
double frameDelay = frameTime - lastFrameTime;
cumulativeFrameDelay += frameDelay;
if (frameDelay >= 0)
{
int32_t x = (xdim <= 640);
//if (r_showfps)
{
output.AppendFormat("%.1f ms, %5.1f fps\n", frameDelay, lastFPS);
if (r_showfps > 1)
{
output.AppendFormat("max: %5.1f fps\n", maxFPS);
output.AppendFormat("min: %5.1f fps\n", minFPS);
}
if (r_showfps > 2)
{
if (g_gameUpdateTime > maxGameUpdate) maxGameUpdate = g_gameUpdateTime;
if (g_gameUpdateTime < minGameUpdate) minGameUpdate = g_gameUpdateTime;
output.AppendFormat("Game Update: %2.2f ms + draw: %2.2f ms\n", g_gameUpdateTime, g_gameUpdateAndDrawTime - g_gameUpdateTime);
output.AppendFormat("GU min/max/avg: %5.2f/%5.2f/%5.2f ms\n", minGameUpdate, maxGameUpdate, g_gameUpdateAvgTime);
output.AppendFormat("bufferjitter: %i\n", gBufferJitter);
#if 0
output.AppendFormat("G_MoveActors(): %.3f ms\n", g_moveActorsTime);
output.AppendFormat("G_MoveWorld(): %.3f ms\n", g_moveWorldTime);
#endif
}
#if 0
// lag meter
if (g_netClientPeer)
{
output.AppendFormat("%d +- %d ms\n", (g_netClientPeer->lastRoundTripTime + g_netClientPeer->roundTripTime)/2,
(g_netClientPeer->lastRoundTripTimeVariance + g_netClientPeer->roundTripTimeVariance)/2);
}
#endif
}
if (cumulativeFrameDelay >= 1000.0)
{
lastFPS = 1000.f * frameCount / cumulativeFrameDelay;
g_frameRate = Blrintf(lastFPS);
frameCount = 0;
cumulativeFrameDelay = 0.0;
if (r_showfps > 1)
{
if (lastFPS > maxFPS) maxFPS = lastFPS;
if (lastFPS < minFPS) minFPS = lastFPS;
static int secondCounter;
if (++secondCounter >= r_showfpsperiod)
{
maxFPS = (lastFPS + maxFPS) * .5f;
minFPS = (lastFPS + minFPS) * .5f;
maxGameUpdate = (g_gameUpdateTime + maxGameUpdate) * 0.5;
minGameUpdate = (g_gameUpdateTime + minGameUpdate) * 0.5;
secondCounter = 0;
}
}
}
frameCount++;
}
lastFrameTime = frameTime;
return output;
}
FString GameInterface::GetCoordString()
{
return "Player pos is unknown"; // todo: output at least something useful.
}
class ViewLoadSave : public LoadSave {
public:
void Load(void);
void Save(void);
};
static ViewLoadSave *myLoadSave;
static int messageTime;
static char message[256];
void ViewLoadSave::Load(void)
{
Read(&messageTime, sizeof(messageTime));
Read(message, sizeof(message));
Read(otherMirrorGotpic, sizeof(otherMirrorGotpic));
Read(bakMirrorGotpic, sizeof(bakMirrorGotpic));
Read(&gScreenTilt, sizeof(gScreenTilt));
Read(&deliriumTilt, sizeof(deliriumTilt));
Read(&deliriumTurn, sizeof(deliriumTurn));
Read(&deliriumPitch, sizeof(deliriumPitch));
}
void ViewLoadSave::Save(void)
{
Write(&messageTime, sizeof(messageTime));
Write(message, sizeof(message));
Write(otherMirrorGotpic, sizeof(otherMirrorGotpic));
Write(bakMirrorGotpic, sizeof(bakMirrorGotpic));
Write(&gScreenTilt, sizeof(gScreenTilt));
Write(&deliriumTilt, sizeof(deliriumTilt));
Write(&deliriumTurn, sizeof(deliriumTurn));
Write(&deliriumPitch, sizeof(deliriumPitch));
}
void ViewLoadSaveConstruct(void)
{
myLoadSave = new ViewLoadSave();
}
END_BLD_NS