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

914 lines
28 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 <stdlib.h>
#include <string.h>
#include "compat.h"
#include "build.h"
#include "mmulti.h"
2020-07-31 19:05:09 +00:00
#include "v_font.h"
2019-09-19 22:42:45 +00:00
#include "blood.h"
2020-07-31 19:05:09 +00:00
#include "choke.h"
#include "zstring.h"
#include "razemenu.h"
#include "gstrings.h"
#include "v_2ddrawer.h"
#include "v_video.h"
2020-07-31 19:05:09 +00:00
#include "v_font.h"
2020-09-06 08:59:45 +00:00
#include "statusbar.h"
2020-09-06 10:44:58 +00:00
#include "automap.h"
#include "gamefuncs.h"
#include "v_draw.h"
2021-03-20 22:01:16 +00:00
#include "render.h"
#include "glbackend/glbackend.h"
2019-09-19 22:42:45 +00:00
2021-03-20 22:01:16 +00:00
EXTERN_CVAR(Bool, testnewrenderer)
BEGIN_BLD_NS
FixedBitArray<kMaxSprites> gInterpolateSprite;
2019-09-19 22:42:45 +00:00
VIEW gPrevView[kMaxPlayers];
VIEWPOS gViewPos;
int gViewIndex;
double gInterpolate;
2019-09-19 22:42:45 +00:00
int gScreenTilt;
2020-07-31 19:05:09 +00:00
FFont *gFont[kFontNum];
2019-09-19 22:42:45 +00:00
void FontSet(int id, int tile, int space)
{
2020-01-02 09:45:31 +00:00
if (id < 0 || id >= kFontNum || tile < 0 || tile >= kMaxTiles)
2019-09-19 22:42:45 +00:00
return;
2020-07-31 19:05:09 +00:00
GlyphSet glyphs;
for (int i = 1; i < 96; i++)
2019-09-19 22:42:45 +00:00
{
2020-07-31 19:05:09 +00:00
auto tex = tileGetTexture(tile + i);
2020-07-31 19:05:57 +00:00
if (tex && tex->isValid() && tex->GetTexelWidth() > 0 && tex->GetTexelHeight() > 0)
{
2020-07-31 19:05:09 +00:00
glyphs.Insert(i + 32, tex);
2020-07-31 19:05:57 +00:00
tex->SetOffsetsNotForFont();
}
2020-07-31 19:05:09 +00:00
2019-09-19 22:42:45 +00:00
}
2020-07-31 19:05:09 +00:00
const char *names[] = { "smallfont", "bigfont", "gothfont", "smallfont2", "digifont"};
const char *defs[] = { "defsmallfont", "defbigfont", nullptr, "defsmallfont2", nullptr};
FFont ** ptrs[] = { &SmallFont, &BigFont, nullptr, &SmallFont2, nullptr};
2020-07-31 19:05:57 +00:00
gFont[id] = new ::FFont(names[id], nullptr, defs[id], 0, 0, 0, 0, tileWidth(tile), false, false, false, &glyphs);
gFont[id]->SetKerning(space);
2020-07-31 19:05:09 +00:00
if (ptrs[id]) *ptrs[id] = gFont[id];
2019-09-19 22:42:45 +00:00
}
void viewBackupView(int nPlayer)
{
PLAYER *pPlayer = &gPlayer[nPlayer];
VIEW *pView = &gPrevView[nPlayer];
pView->angle = pPlayer->angle.ang;
pView->x = pPlayer->pSprite->x;
pView->y = pPlayer->pSprite->y;
pView->viewz = pPlayer->zView;
pView->weaponZ = pPlayer->zWeapon-pPlayer->zView-0xc00;
pView->horiz = pPlayer->horizon.horiz;
pView->horizoff = pPlayer->horizon.horizoff;
pView->at2c = pPlayer->slope;
pView->bobHeight = pPlayer->bobHeight;
pView->bobWidth = pPlayer->bobWidth;
pView->shakeBobY = pPlayer->swayHeight;
pView->shakeBobX = pPlayer->swayWidth;
pView->look_ang = pPlayer->angle.look_ang;
pView->rotscrnang = pPlayer->angle.rotscrnang;
pPlayer->angle.backup();
pPlayer->horizon.backup();
2019-09-19 22:42:45 +00:00
}
2019-09-07 16:39:26 +00:00
void viewCorrectViewOffsets(int nPlayer, vec3_t const *oldpos)
{
PLAYER *pPlayer = &gPlayer[nPlayer];
VIEW *pView = &gPrevView[nPlayer];
pView->x += pPlayer->pSprite->x-oldpos->x;
pView->y += pPlayer->pSprite->y-oldpos->y;
pView->viewz += pPlayer->pSprite->z-oldpos->z;
2019-09-07 16:39:26 +00:00
}
2019-09-19 22:42:45 +00:00
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)
{
2020-01-02 09:45:31 +00:00
if (nFont < 0 || nFont >= kFontNum || !pString) return;
2020-07-31 19:05:09 +00:00
FFont *pFont = gFont[nFont];
//y += pFont->yoff;
2020-08-10 19:14:42 +00:00
if (position == 1) x -= pFont->StringWidth(pString) / 2;
if (position == 2) x -= pFont->StringWidth(pString);
2020-07-31 19:05:09 +00:00
if (shadow)
{
DrawText(twod, pFont, CR_UNDEFINED, x+1, y+1, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, 0.5, TAG_DONE);
2020-07-31 19:05:09 +00:00
}
DrawText(twod, pFont, CR_UNDEFINED, x, y, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, nPalette),
DTA_Color, shadeToLight(nShade), DTA_Alpha, alpha / 255., TAG_DONE);
2020-08-01 18:07:32 +00:00
2019-09-19 22:42:45 +00:00
}
void InitStatusBar(void)
{
if (r_precache) PrecacheHardwareTextures(2200);
2019-09-19 22:42:45 +00:00
}
GameStats GameInterface::getStats()
{
return { gKillMgr.Kills, gKillMgr.TotalKills, gSecretMgr.Founds, gSecretMgr.Total, gFrameCount / kTicsPerSec, gPlayer[myconnectindex].fragCount };
}
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))
{
int nPlayer = pSprite->type-kDudePlayer1;
const char* szName = PlayerName(nPlayer);
int nPalette = (gPlayer[nPlayer].teamId&3)+11;
viewDrawText(4, szName, 160, 125, -128, nPalette, 1, 1);
}
}
}
2020-04-11 21:54:33 +00:00
static TArray<uint8_t> lensdata;
2019-09-19 22:42:45 +00:00
int *lensTable;
2020-08-02 17:59:14 +00:00
extern int dword_172CE0[16][3];
2019-09-19 22:42:45 +00:00
void viewInit(void)
{
Printf("Initializing status bar\n");
2019-09-19 22:42:45 +00:00
InitStatusBar();
FontSet(0, 4096, 0);
FontSet(1, 4192, 1);
FontSet(2, 4288, 1);
FontSet(3, 4384, 1);
FontSet(4, 4480, 0);
2020-04-11 21:54:33 +00:00
lensdata = fileSystem.LoadFile("lens.dat");
assert(lensdata.Size() == kLensSize * kLensSize * sizeof(int));
2019-09-19 22:42:45 +00:00
2020-04-11 21:54:33 +00:00
lensTable = (int*)lensdata.Data();
2019-09-19 22:42:45 +00:00
#if B_BIG_ENDIAN == 1
for (int i = 0; i < kLensSize*kLensSize; i++)
{
lensTable[i] = LittleLong(lensTable[i]);
2019-09-19 22:42:45 +00:00
}
#endif
uint8_t *data = TileFiles.tileCreate(4077, kLensSize, kLensSize);
2020-04-11 22:04:02 +00:00
memset(data, TRANSPARENT_INDEX, kLensSize*kLensSize);
2019-09-19 22:42:45 +00:00
for (int i = 0; i < 16; i++)
{
dword_172CE0[i][0] = MulScale(wrand(), 2048, 16);
dword_172CE0[i][1] = MulScale(wrand(), 2048, 16);
dword_172CE0[i][2] = MulScale(wrand(), 2048, 16);
2019-09-19 22:42:45 +00:00
}
}
int othercameradist = 1280;
int othercameraclock;
2019-09-19 22:42:45 +00:00
void CalcOtherPosition(spritetype *pSprite, int *pX, int *pY, int *pZ, int *vsectnum, int nAng, fixed_t zm, int smoothratio)
2019-09-19 22:42:45 +00:00
{
int vX = MulScale(-Cos(nAng), 1280, 30);
int vY = MulScale(-Sin(nAng), 1280, 30);
int vZ = FixedToInt(MulScale(zm, 1280, 3))-(16<<8);
2019-09-19 22:42:45 +00:00
int bakCstat = pSprite->cstat;
pSprite->cstat &= ~256;
assert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
2019-09-19 22:42:45 +00:00
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 (abs(vX)+abs(vY) > abs(dX)+abs(dY))
2019-09-19 22:42:45 +00:00
{
*vsectnum = nHSector;
dX -= Sgn(vX)<<6;
dY -= Sgn(vY)<<6;
2019-09-19 22:42:45 +00:00
int nDist;
if (abs(vX) > abs(vY))
2019-09-19 22:42:45 +00:00
{
nDist = ClipHigh(DivScale(dX,vX, 16), othercameradist);
2019-09-19 22:42:45 +00:00
}
else
{
nDist = ClipHigh(DivScale(dY,vY, 16), othercameradist);
2019-09-19 22:42:45 +00:00
}
othercameradist = nDist;
}
*pX += MulScale(vX, othercameradist, 16);
*pY += MulScale(vY, othercameradist, 16);
*pZ += MulScale(vZ, othercameradist, 16);
int myclock = PlayClock + MulScale(4, smoothratio, 16);
othercameradist = ClipHigh(othercameradist+((myclock-othercameraclock)<<10), 65536);
othercameraclock = myclock;
assert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
2019-09-19 22:42:45 +00:00
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)
2019-09-19 22:42:45 +00:00
{
int printlevel = priority <= MESSAGE_PRIORITY_NORMAL ? PRINT_LOW : priority < MESSAGE_PRIORITY_SYSTEM ? PRINT_MEDIUM : PRINT_HIGH;
Printf(printlevel|PRINT_NOTIFY, "%s\n", pMessage);
2019-09-19 22:42:45 +00:00
}
void viewSetErrorMessage(const char *pMessage)
{
Printf(PRINT_BOLD|PRINT_NOTIFY, "%s\n", pMessage);
2019-09-19 22:42:45 +00:00
}
void DoLensEffect(void)
{
// To investigate whether this can be implemented as a shader effect.
auto d = tileData(4077);
assert(d != NULL);
auto s = tilePtr(4079);
assert(s != NULL);
2019-09-19 22:42:45 +00:00
for (int i = 0; i < kLensSize*kLensSize; i++, d++)
if (lensTable[i] >= 0)
*d = s[lensTable[i]];
TileFiles.InvalidateTile(4077);
2019-09-19 22:42:45 +00:00
}
void UpdateDacs(int nPalette, bool bNoTint)
{
gLastPal = 0;
auto& tint = lookups.tables[MAXPALOOKUPS - 1];
tint.tintFlags = 0;
switch (nPalette)
2019-09-19 22:42:45 +00:00
{
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);
}
2019-09-19 22:42:45 +00:00
void UpdateBlend()
{
int nRed = 0;
int nGreen = 0;
int nBlue = 0;
2019-09-19 22:42:45 +00:00
nRed += gView->pickupEffect;
nGreen += gView->pickupEffect;
nBlue -= gView->pickupEffect;
2019-09-19 22:42:45 +00:00
nRed += ClipHigh(gView->painEffect, 85) * 2;
nGreen -= ClipHigh(gView->painEffect, 85) * 3;
nBlue -= ClipHigh(gView->painEffect, 85) * 3;
2019-09-19 22:42:45 +00:00
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);
2019-09-19 22:42:45 +00:00
}
uint8_t otherMirrorGotpic[2];
uint8_t bakMirrorGotpic[2];
2019-09-19 22:42:45 +00:00
// 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)
2019-09-19 22:42:45 +00:00
{
int tilt1 = 170, tilt2 = 170, pitch = 20;
int timer = PlayClock*4;
2019-09-19 22:42:45 +00:00
if (powerCount < 512)
{
int powerScale = IntToFixed(powerCount) / 512;
tilt1 = MulScale(tilt1, powerScale, 16);
tilt2 = MulScale(tilt2, powerScale, 16);
pitch = MulScale(pitch, powerScale, 16);
2019-09-19 22:42:45 +00:00
}
int sin2 = costable[(2*timer-512)&2047] / 2;
int sin3 = costable[(3*timer-512)&2047] / 2;
gScreenTilt = MulScale(sin2+sin3,tilt1, 30);
2019-09-19 22:42:45 +00:00
int sin4 = costable[(4*timer-512)&2047] / 2;
deliriumTurn = MulScale(sin3+sin4,tilt2, 30);
2019-09-19 22:42:45 +00:00
int sin5 = costable[(5*timer-512)&2047] / 2;
deliriumPitch = MulScale(sin4+sin5,pitch, 30);
2019-09-19 22:42:45 +00:00
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;
}
}
void viewUpdateShake(int& cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, double& pshakeX, double& pshakeY)
2019-09-19 22:42:45 +00:00
{
int shakeHoriz = 0;
int shakeAngle = 0;
int shakeX = 0;
int shakeY = 0;
int shakeZ = 0;
int shakeBobX = 0;
int shakeBobY = 0;
if (gView->flickerEffect)
2019-09-19 22:42:45 +00:00
{
int nValue = ClipHigh(gView->flickerEffect * 8, 2000);
2019-09-19 22:42:45 +00:00
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)
2019-09-19 22:42:45 +00:00
{
int nValue = ClipHigh(gView->quakeEffect * 8, 2000);
2019-09-19 22:42:45 +00:00
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);
}
cH += buildhoriz(shakeHoriz);
cA += buildang(shakeAngle);
cX += shakeX;
cY += shakeY;
cZ += shakeZ;
pshakeX += shakeBobX;
pshakeY += shakeBobY;
2019-09-19 22:42:45 +00:00
}
int gLastPal = 0;
int32_t g_frameRate;
2020-09-06 08:59:45 +00:00
static void DrawMap(spritetype* pSprite)
{
int tm = 0;
if (windowxy1.x > 0)
{
setViewport(Hud_Stbar);
tm = 1;
}
VIEW* pView = &gPrevView[gViewIndex];
int x = interpolate(pView->x, pSprite->x, gInterpolate);
int y = interpolate(pView->y, pSprite->y, gInterpolate);
int ang = (!SyncInput() ? gView->angle.sum() : gView->angle.interpolatedsum(gInterpolate)).asbuild();
DrawOverheadMap(x, y, ang, gInterpolate);
2020-09-06 08:59:45 +00:00
if (tm)
setViewport(hud_size);
}
void SetupView(int &cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, int& nSectnum, double& zDelta, double& shakeX, double& shakeY, lookangle& rotscrnang)
2021-03-20 22:01:16 +00:00
{
int bobWidth, bobHeight;
2021-03-20 22:01:16 +00:00
nSectnum = gView->pSprite->sectnum;
if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0)
{
nSectnum = predict.sectnum;
cX = interpolate(predictOld.x, predict.x, gInterpolate);
cY = interpolate(predictOld.y, predict.y, gInterpolate);
cZ = interpolate(predictOld.viewz, predict.viewz, gInterpolate);
zDelta = finterpolate(predictOld.weaponZ, predict.weaponZ, gInterpolate);
bobWidth = interpolate(predictOld.bobWidth, predict.bobWidth, gInterpolate);
bobHeight = interpolate(predictOld.bobHeight, predict.bobHeight, gInterpolate);
shakeX = finterpolate(predictOld.shakeBobX, predict.shakeBobX, gInterpolate);
shakeY = finterpolate(predictOld.shakeBobY, predict.shakeBobY, gInterpolate);
if (!SyncInput())
{
cA = bamang(predict.angle.asbam() + predict.look_ang.asbam());
cH = predict.horiz + predict.horizoff;
rotscrnang = predict.rotscrnang;
}
else
{
uint32_t oang = predictOld.angle.asbam() + predictOld.look_ang.asbam();
uint32_t ang = predict.angle.asbam() + predict.look_ang.asbam();
cA = interpolateangbin(oang, ang, gInterpolate);
fixed_t ohoriz = (predictOld.horiz + predictOld.horizoff).asq16();
fixed_t horiz = (predict.horiz + predict.horizoff).asq16();
cH = q16horiz(interpolate(ohoriz, horiz, gInterpolate));
rotscrnang = interpolateanglook(predictOld.rotscrnang.asbam(), predict.rotscrnang.asbam(), gInterpolate);
}
}
else
{
VIEW* pView = &gPrevView[gViewIndex];
cX = interpolate(pView->x, gView->pSprite->x, gInterpolate);
cY = interpolate(pView->y, gView->pSprite->y, gInterpolate);
cZ = interpolate(pView->viewz, gView->zView, gInterpolate);
zDelta = finterpolate(pView->weaponZ, gView->zWeapon - gView->zView - (12 << 8), gInterpolate);
bobWidth = interpolate(pView->bobWidth, gView->bobWidth, gInterpolate);
bobHeight = interpolate(pView->bobHeight, gView->bobHeight, gInterpolate);
shakeX = finterpolate(pView->shakeBobX, gView->swayWidth, gInterpolate);
shakeY = finterpolate(pView->shakeBobY, gView->swayHeight, gInterpolate);
if (!SyncInput())
{
cA = gView->angle.sum();
cH = gView->horizon.sum();
rotscrnang = gView->angle.rotscrnang;
}
else
{
cA = gView->angle.interpolatedsum(gInterpolate);
cH = gView->horizon.interpolatedsum(gInterpolate);
rotscrnang = gView->angle.interpolatedrotscrn(gInterpolate);
}
}
viewUpdateShake(cX, cY, cZ, cA, cH, shakeX, shakeY);
2021-03-20 22:01:16 +00:00
cH += buildhoriz(MulScale(0x40000000 - Cos(gView->tiltEffect << 2), 30, 30));
if (gViewPos == 0)
{
if (cl_viewhbob)
{
cX -= MulScale(bobWidth, Sin(cA.asbuild()), 30) >> 4;
cY += MulScale(bobWidth, Cos(cA.asbuild()), 30) >> 4;
}
if (cl_viewvbob)
{
cZ += bobHeight;
}
cZ += xs_CRoundToInt(cH.asq16() / 6553.6);
cameradist = -1;
cameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16);
}
else
{
calcChaseCamPos((int*)&cX, (int*)&cY, (int*)&cZ, gView->pSprite, (short*)&nSectnum, cA, cH, gInterpolate);
}
CheckLink((int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum);
}
void renderCrystalBall()
{
#if 0
// needs to be redone for pure hardware rendering when MP is working again.
int tmp = (PlayClock / 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 = PlayClock + MulScale(4, (int)gInterpolate, 16);;
if (!tileData(4079))
{
TileFiles.tileCreate(4079, 128, 128);
}
//renderSetTarget(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, (int)gInterpolate);
CheckLink(&vd8, &vd4, &vd0, &vcc);
uint8_t v14 = 0;
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);
DrawMirrors(vd8, vd4, vd0, IntToFixed(v50), IntToFixed(v54), gInterpolate, -1);
drawrooms(vd8, vd4, vd0, v50, v54, vcc);
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
}
2021-03-20 22:01:16 +00:00
void render3DViewPolymost(int nSectnum, int cX, int cY, int cZ, binangle cA, fixedhoriz cH);
2020-09-06 08:59:45 +00:00
2020-01-14 20:20:46 +00:00
void viewDrawScreen(bool sceneonly)
2019-09-19 22:42:45 +00:00
{
int nPalette = 0;
if (TestBitString(gotpic, 2342))
{
FireProcess();
ClearBitString(gotpic, 2342);
}
2020-05-29 01:20:40 +00:00
if (!paused && (!M_Active() || gGameOptions.nGameType != 0))
2019-09-19 22:42:45 +00:00
{
gInterpolate = I_GetTimeFrac() * MaxSmoothRatio;
2019-09-19 22:42:45 +00:00
}
else gInterpolate = MaxSmoothRatio;
2019-10-22 00:15:24 +00:00
if (cl_interpolate)
2019-09-19 22:42:45 +00:00
{
DoInterpolations(gInterpolate / MaxSmoothRatio);
2019-09-19 22:42:45 +00:00
}
2020-08-24 17:47:09 +00:00
if (automapMode != am_full)
2019-09-19 22:42:45 +00:00
{
2020-02-05 21:37:20 +00:00
DoSectorLighting();
2019-09-19 22:42:45 +00:00
}
2020-08-24 17:47:09 +00:00
if (automapMode == am_off)
2019-09-19 22:42:45 +00:00
{
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();
2021-03-20 22:01:16 +00:00
int cX, cY, cZ;
binangle cA;
fixedhoriz cH;
2021-03-20 22:01:16 +00:00
int nSectnum;
double zDelta;
double shakeX, shakeY;
lookangle rotscrnang;
SetupView(cX, cY, cZ, cA, cH, nSectnum, zDelta, shakeX, shakeY, rotscrnang);
2021-03-20 22:01:16 +00:00
int tilt = interpolateang(gScreenTiltO, gScreenTilt, gInterpolate);
uint8_t v14 = 0;
uint8_t v10 = 0;
bool bDelirium = powerupCheck(gView, kPwUpDeliriumShroom) > 0;
2019-09-19 22:42:45 +00:00
static bool bDeliriumOld = false;
//int tiltcs, tiltdim;
2021-03-20 22:01:16 +00:00
uint8_t otherview = powerupCheck(gView, kPwUpCrystalBall) > 0;
if (tilt || bDelirium)
2019-09-19 22:42:45 +00:00
{
rotscrnang = buildlook(tilt);
2019-09-19 22:42:45 +00:00
}
2021-03-20 22:01:16 +00:00
else if (otherview && gNetPlayers > 1)
2019-09-19 22:42:45 +00:00
{
#if 0
renderCrystalBall();
#endif
2019-09-19 22:42:45 +00:00
}
else
{
othercameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16);
2019-09-19 22:42:45 +00:00
}
if (!bDelirium)
{
deliriumTilt = 0;
deliriumTurn = 0;
deliriumPitch = 0;
}
2021-03-20 22:01:16 +00:00
int brightness = 0;
2020-10-15 15:15:45 +00:00
int nSprite;
StatIterator it(kStatExplosion);
while ((nSprite = it.NextIndex()) >= 0)
2019-09-19 22:42:45 +00:00
{
spritetype* pSprite = &sprite[nSprite];
2019-09-19 22:42:45 +00:00
int nXSprite = pSprite->extra;
assert(nXSprite > 0 && nXSprite < kMaxXSprites);
XSPRITE* pXSprite = &xsprite[nXSprite];
2019-09-19 22:42:45 +00:00
if (TestBitString(gotsector, pSprite->sectnum))
{
2021-03-20 22:01:16 +00:00
brightness += pXSprite->data3 * 32;
2019-09-19 22:42:45 +00:00
}
}
2020-10-15 15:15:45 +00:00
it.Reset(kStatProjectile);
while ((nSprite = it.NextIndex()) >= 0)
{
spritetype* pSprite = &sprite[nSprite];
switch (pSprite->type) {
case kMissileFlareRegular:
case kMissileTeslaAlt:
case kMissileFlareAlt:
case kMissileTeslaRegular:
2021-03-20 22:01:16 +00:00
if (TestBitString(gotsector, pSprite->sectnum)) brightness += 256;
break;
2019-09-19 22:42:45 +00:00
}
}
2021-03-20 22:01:16 +00:00
g_visibility = (int32_t)(ClipLow(gVisibility - 32 * gView->visibility - brightness, 0));
cA += q16ang(interpolateangfix16(IntToFixed(deliriumTurnO), IntToFixed(deliriumTurn), gInterpolate));
2021-03-20 22:01:16 +00:00
int ceilingZ, floorZ;
getzsofslope(nSectnum, cX, cY, &ceilingZ, &floorZ);
if (cZ >= floorZ)
2019-09-19 22:42:45 +00:00
{
2021-03-20 22:01:16 +00:00
cZ = floorZ - (gUpperLink[nSectnum] >= 0 ? 0 : (8 << 8));
2019-09-19 22:42:45 +00:00
}
2021-03-20 22:01:16 +00:00
if (cZ <= ceilingZ)
2019-09-19 22:42:45 +00:00
{
2021-03-20 22:01:16 +00:00
cZ = ceilingZ + (gLowerLink[nSectnum] >= 0 ? 0 : (8 << 8));
2019-09-19 22:42:45 +00:00
}
cH = q16horiz(ClipRange(cH.asq16(), gi->playerHorizMin(), gi->playerHorizMax()));
2021-03-20 22:01:16 +00:00
if ((tilt || bDelirium) && !sceneonly)
2019-09-19 22:42:45 +00:00
{
2021-03-20 22:01:16 +00:00
if (gDeliriumBlur)
{
// todo: Set up a blurring postprocessing shader.
//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);
}
2019-09-19 22:42:45 +00:00
}
2021-03-20 22:01:16 +00:00
if (testnewrenderer)
2019-09-19 22:42:45 +00:00
{
fixedhoriz deliriumPitchI = q16horiz(interpolate(IntToFixed(deliriumPitchO), IntToFixed(deliriumPitch), gInterpolate));
2021-03-20 22:01:16 +00:00
int bakCstat = gView->pSprite->cstat;
gView->pSprite->cstat |= (gViewPos == 0) ? CSTAT_SPRITE_INVISIBLE : CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_TRANSLUCENT_INVERT;
render_drawrooms(gView->pSprite, { cX, cY, cZ }, nSectnum, cA, cH + deliriumPitchI, rotscrnang);
2019-09-19 22:42:45 +00:00
gView->pSprite->cstat = bakCstat;
}
2021-03-20 22:01:16 +00:00
else
2019-09-19 22:42:45 +00:00
{
renderSetRollAngle(rotscrnang.asbuildf());
2021-03-20 22:01:16 +00:00
render3DViewPolymost(nSectnum, cX, cY, cZ, cA, cH);
2019-09-19 22:42:45 +00:00
}
bDeliriumOld = bDelirium && gDeliriumBlur;
int nClipDist = gView->pSprite->clipdist << 2;
2019-09-19 22:42:45 +00:00
int ve8, vec, vf0, vf4;
GetZRange(gView->pSprite, &vf4, &vf0, &vec, &ve8, nClipDist, 0);
if (sceneonly) return;
2019-09-19 22:42:45 +00:00
#if 0
int tmpSect = nSectnum;
if ((vf0 & 0xc000) == 0x4000)
2019-09-19 22:42:45 +00:00
{
tmpSect = vf0 & (kMaxWalls - 1);
2019-09-19 22:42:45 +00:00
}
int v8 = byte_1CE5C2 > 0 && (sector[tmpSect].ceilingstat & 1);
2019-09-19 22:42:45 +00:00
if (gWeather.at12d8 > 0 || v8)
{
gWeather.Draw(cX, cY, cZ, cA.asq16(), cH.asq16() + deliriumPitch, gWeather.at12d8);
2019-09-19 22:42:45 +00:00
if (v8)
{
gWeather.at12d8 = ClipRange(delta * 8 + gWeather.at12d8, 0, 4095);
2019-09-19 22:42:45 +00:00
}
else
{
gWeather.at12d8 = ClipRange(gWeather.at12d8 - delta * 64, 0, 4095);
2019-09-19 22:42:45 +00:00
}
}
#endif
hudDraw(gView, nSectnum, shakeX, shakeY, zDelta, basepal, gInterpolate);
}
UpdateDacs(0, true); // keep the view palette active only for the actual 3D view and its overlays.
2020-08-24 17:47:09 +00:00
if (automapMode != am_off)
2019-09-19 22:42:45 +00:00
{
2020-09-06 08:59:45 +00:00
DrawMap (gView->pSprite);
2019-09-19 22:42:45 +00:00
}
UpdateStatusBar();
int zn = ((gView->zWeapon-gView->zView-(12<<8))>>7)+220;
2019-09-19 22:42:45 +00:00
PLAYER *pPSprite = &gPlayer[gMe->pSprite->type-kDudePlayer1];
if (IsPlayerSprite(gMe->pSprite) && pPSprite->hand == 1)
2019-09-19 22:42:45 +00:00
{
gChoke.animateChoke(160, zn, (int)gInterpolate);
2019-09-19 22:42:45 +00:00
}
#if 0
if (drawtile_2048)
2019-09-19 22:42:45 +00:00
{
DrawStatSprite(2048, xdim-15, 20);
}
#endif
viewDrawAimedPlayerName();
2020-05-29 01:20:40 +00:00
if (paused)
2019-09-19 22:42:45 +00:00
{
viewDrawText(1, GStrings("TXTB_PAUSED"), 160, 10, 0, 0, 1, 0);
2019-09-19 22:42:45 +00:00
}
else if (gView != gMe)
{
FStringf gTempStr("] %s [", PlayerName(gView->nPlayer));
2019-09-19 22:42:45 +00:00
viewDrawText(0, gTempStr, 160, 10, 0, 0, 1, 0);
}
2019-10-22 00:15:24 +00:00
if (cl_interpolate)
2019-09-19 22:42:45 +00:00
{
RestoreInterpolations();
}
}
2020-01-14 20:20:46 +00:00
bool GameInterface::GenerateSavePic()
{
viewDrawScreen(true);
return true;
}
FString GameInterface::GetCoordString()
{
FString out;
out.Format("pos= %d, %d, %d - angle = %2.3f",
gMe->pSprite->x, gMe->pSprite->y, gMe->pSprite->z, gMe->pSprite->ang * (360./2048));
return out;
}
bool GameInterface::DrawAutomapPlayer(int x, int y, int z, int a, double const smoothratio)
{
// [MR]: Confirm that this is correct as math doesn't match the variable names.
int nCos = z * -bsin(a);
int nSin = z * -bcos(a);
int nPSprite = gView->pSprite->index;
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
PLAYER* pPlayer = &gPlayer[i];
spritetype* pSprite = pPlayer->pSprite;
int x1 = pSprite->x - x;
int y1 = pSprite->y - y;
int pa = (pSprite->ang - a) & 2047;
if (i == gView->nPlayer || gGameOptions.nGameType == 1)
{
int nTile = pSprite->picnum;
int ceilZ, ceilHit, floorZ, floorHit;
GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, (pSprite->clipdist << 2) + 16, CLIPMASK0, PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR);
int nTop, nBottom;
GetSpriteExtents(pSprite, &nTop, &nBottom);
int nScale = (pSprite->yrepeat + ((floorZ - nBottom) >> 8)) * z;
nScale = ClipRange(nScale, 8000, 65536 << 1);
// Players on automap
double x = xdim / 2. + x1 / double(1 << 12);
double y = ydim / 2. + y1 / double(1 << 12);
// This very likely needs fixing later
DrawTexture(twod, tileGetTexture(nTile, true), x, y, DTA_ClipLeft, windowxy1.x, DTA_ClipTop, windowxy1.y, DTA_ScaleX, z/1536., DTA_ScaleY, z/1536., DTA_CenterOffset, true,
DTA_ClipRight, windowxy2.x + 1, DTA_ClipBottom, windowxy2.y + 1, DTA_Alpha, (pSprite->cstat & 2 ? 0.5 : 1.), TAG_DONE);
}
}
return true;
}
void SerializeView(FSerializer& arc)
2019-09-19 22:42:45 +00:00
{
if (arc.BeginObject("view"))
{
arc("screentilt", gScreenTilt)
("deliriumtilt", deliriumTilt)
("deliriumturn", deliriumTurn)
("deliriumpitch", deliriumPitch)
.EndObject();
}
2019-09-19 22:42:45 +00:00
}
END_BLD_NS