#include "compat.h" #include "keyboard.h" #include "control.h" #include "engine.h" #include "config.h" #include "names.h" #include "view.h" #include "status.h" #include "exhumed.h" #include "player.h" #include "snake.h" #include "gun.h" #include "light.h" #include "init.h" #include "menu.h" #include "keyboard.h" #include "cd.h" #include "cdaudio.h" #include "typedefs.h" #include "map.h" #include "move.h" #include "sound.h" #include "engine.h" #include "trigdat.h" #include "runlist.h" #include short bSubTitles = kTrue; int zbob; fix16_t nDestVertPan[kMaxPlayers] = { 0 }; short dVertPan[kMaxPlayers]; fix16_t nVertPan[kMaxPlayers]; int nCamerax; int nCameray; int nCameraz; short bTouchFloor; short nQuake[kMaxPlayers] = { 0 }; short nChunkTotal = 0; fix16_t nCameraa; fix16_t nCamerapan; short nViewTop; short bClip = kFalse; short nViewBottom; short nViewRight; short besttarget; short nViewLeft; short bCamera = kFalse; short nViewy; int viewz; short enemy; short nEnemyPal = 0; #define MAXINTERPOLATIONS MAXSPRITES int32_t g_interpolationCnt; int32_t oldipos[MAXINTERPOLATIONS]; int32_t* curipos[MAXINTERPOLATIONS]; int32_t bakipos[MAXINTERPOLATIONS]; int viewSetInterpolation(int32_t *const posptr) { if (g_interpolationCnt >= MAXINTERPOLATIONS) return 1; for (bssize_t i = 0; i < g_interpolationCnt; ++i) if (curipos[i] == posptr) return 0; curipos[g_interpolationCnt] = posptr; oldipos[g_interpolationCnt] = *posptr; g_interpolationCnt++; return 0; } void viewStopInterpolation(const int32_t * const posptr) { for (bssize_t i = 0; i < g_interpolationCnt; ++i) if (curipos[i] == posptr) { g_interpolationCnt--; oldipos[i] = oldipos[g_interpolationCnt]; bakipos[i] = bakipos[g_interpolationCnt]; curipos[i] = curipos[g_interpolationCnt]; } } void viewDoInterpolations(int smoothRatio) { int32_t ndelta = 0; for (bssize_t i = 0, j = 0; i < g_interpolationCnt; ++i) { int32_t const odelta = ndelta; bakipos[i] = *curipos[i]; ndelta = (*curipos[i]) - oldipos[i]; if (odelta != ndelta) j = mulscale16(ndelta, smoothRatio); *curipos[i] = oldipos[i] + j; } } void viewUpdateInterpolations(void) //Stick at beginning of G_DoMoveThings { for (bssize_t i=g_interpolationCnt-1; i>=0; i--) oldipos[i] = *curipos[i]; } void viewRestoreInterpolations(void) //Stick at end of drawscreen { int32_t i=g_interpolationCnt-1; for (; i>=0; i--) *curipos[i] = bakipos[i]; } void InitView() { screensize = 0; #ifdef USE_OPENGL polymostcenterhoriz = 92; #endif } // NOTE - not to be confused with Ken's analyzesprites() static void analyzesprites() { short nPlayerSprite = PlayerList[nLocalPlayer].nSprite; int var_38 = 20; int var_2C = 30000; spritetype *pPlayerSprite = &sprite[nPlayerSprite]; besttarget = -1; int x = pPlayerSprite->x; int y = pPlayerSprite->y; int z = pPlayerSprite->z - (GetSpriteHeight(nPlayerSprite) / 2); short nSector = pPlayerSprite->sectnum; int nAngle = (2048 - pPlayerSprite->ang) & kAngleMask; int nTSprite; tspritetype *pTSprite; // int var_20 = var_24; for (nTSprite = spritesortcnt-1, pTSprite = &tsprite[nTSprite]; nTSprite >= 0; nTSprite--, pTSprite--) { int nSprite = pTSprite->owner; spritetype *pSprite = &sprite[nSprite]; if (pTSprite->sectnum >= 0) { sectortype *pSector = §or[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); if (pSprite->statnum > 0) { runlist_SignalRun(pSprite->lotag - 1, nTSprite | 0x90000); if ((pSprite->statnum < 150) && (pSprite->cstat & 0x101) && (nSprite != nPlayerSprite)) { int xval = pSprite->x - x; int yval = pSprite->y - y; int vcos = Cos(nAngle); int vsin = Sin(nAngle); int edx = ((vcos * yval) + (xval * vsin)) >> 14; int ebx = klabs(((vcos * xval) - (yval * vsin)) >> 14); if (!ebx) continue; edx = (klabs(edx) * 32) / ebx; if (ebx < 1000 && ebx < var_2C && edx < 10) { besttarget = nSprite; var_38 = edx; var_2C = ebx; } else if (ebx < 30000) { int t = var_38 - edx; if (t > 3 || (ebx < var_2C && klabs(t) < 5)) { var_38 = edx; var_2C = ebx; besttarget = nSprite; } } } } } if (besttarget != -1) { spritetype *pTarget = &sprite[besttarget]; nCreepyTimer = kCreepyCount; if (!cansee(x, y, z, nSector, pTarget->x, pTarget->y, pTarget->z - GetSpriteHeight(besttarget), pTarget->sectnum)) { besttarget = -1; } } } void ResetView() { videoSetGameMode(gSetup.fullscreen, gSetup.xdim, gSetup.ydim, gSetup.bpp, 0); DoOverscanSet(overscanindex); EraseScreen(overscanindex); memcpy(curpalettefaded, curpalette, sizeof(curpalette)); videoUpdatePalette(0, 256); #ifdef USE_OPENGL videoTintBlood(0, 0, 0); #endif LoadStatus(); } void SetView1() { } void FlushMessageLine() { int tileX = tilesiz[nBackgroundPic].x; int nTileOffset = 0; int xPos = 0; while (xPos < xdim) { overwritesprite(xPos, 0, nBackgroundPic + nTileOffset, -32, 0, kPalNormal); nTileOffset = nTileOffset == 0; xPos += tileX; } } void RefreshBackground() { if (screensize <= 0) return; int nTileOffset = 0; int tileX = tilesiz[nBackgroundPic].x; int tileY = tilesiz[nBackgroundPic].y; videoSetViewableArea(0, 0, xdim - 1, ydim - 1); MaskStatus(); for (int y = 0; y < nViewTop; y += tileY) { nTileOffset = (y/tileY)&1; for (int x = 0; x < xdim; x += tileX) { rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, 0, xdim-1, nViewTop-1); nTileOffset ^= 1; } } for (int y = (nViewTop/tileY)*tileY; y <= nViewBottom; y += tileY) { nTileOffset = (y/tileY)&1; for (int x = 0; x < nViewLeft; x += tileX) { rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, nViewTop, nViewLeft-1, nViewBottom); nTileOffset ^= 1; } } for (int y = (nViewTop/tileY)*tileY; y <= nViewBottom; y += tileY) { nTileOffset = ((y/tileY)^((nViewRight+1)/tileX))&1; for (int x = ((nViewRight+1)/tileX)*tileX; x < xdim; x += tileX) { rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, nViewRight+1, nViewTop, xdim-1, nViewBottom); nTileOffset ^= 1; } } for (int y = ((nViewBottom+1)/tileY)*tileY; y < ydim; y += tileY) { nTileOffset = (y/tileY)&1; for (int x = 0; x < xdim; x += tileX) { rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, nViewBottom+1, xdim-1, ydim-1); nTileOffset ^= 1; } } videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); } void MySetView(int x1, int y1, int x2, int y2) { if (!bFullScreen) { MaskStatus(); } nViewLeft = x1; nViewTop = y1; nViewRight = x2; nViewBottom = y2; videoSetViewableArea(x1, y1, x2, y2); nViewy = y1; } // unused function void TestLava() { } static inline int interpolate16(int a, int b, int smooth) { return a + mulscale16(b - a, smooth); } static inline fix16_t q16angle_interpolate16(fix16_t a, fix16_t b, int smooth) { return a + mulscale16(((b+F16(1024)-a)&0x7FFFFFF)-F16(1024), smooth); } void DrawView(int smoothRatio) { int playerX; int playerY; int playerZ; short nSector; fix16_t nAngle; fix16_t pan; #if 0 if (bgpages <= 0) { if (textpages > 0) { textpages--; FlushMessageLine(); } } else { RefreshBackground(); bgpages--; } #else FlushMessageLine(); RefreshBackground(); #endif if (!bFullScreen) { MaskStatus(); } zbob = Sin(2 * bobangle) >> 3; int nPlayerSprite = PlayerList[nLocalPlayer].nSprite; int nPlayerOldCstat = sprite[nPlayerSprite].cstat; int nDoppleOldCstat = sprite[nDoppleSprite[nLocalPlayer]].cstat; if (nSnakeCam >= 0) { int nSprite = SnakeList[nSnakeCam].nSprites[0]; playerX = sprite[nSprite].x; playerY = sprite[nSprite].y; playerZ = sprite[nSprite].z; nSector = sprite[nSprite].sectnum; nAngle = fix16_from_int(sprite[nSprite].ang); SetGreenPal(); UnMaskStatus(); enemy = SnakeList[nSnakeCam].nEnemy; if (enemy <= -1 || totalmoves & 1) { nEnemyPal = -1; } else { nEnemyPal = sprite[enemy].pal; sprite[enemy].pal = 5; } } else { playerX = interpolate16(PlayerList[nLocalPlayer].opos.x, sprite[nPlayerSprite].x, smoothRatio); playerY = interpolate16(PlayerList[nLocalPlayer].opos.y, sprite[nPlayerSprite].y, smoothRatio); playerZ = interpolate16(PlayerList[nLocalPlayer].opos.z, sprite[nPlayerSprite].z, smoothRatio) + interpolate16(oeyelevel[nLocalPlayer], eyelevel[nLocalPlayer], smoothRatio); nSector = nPlayerViewSect[nLocalPlayer]; nAngle = q16angle_interpolate16(PlayerList[nLocalPlayer].q16oangle, PlayerList[nLocalPlayer].q16angle, smoothRatio); if (!bCamera) { sprite[nPlayerSprite].cstat |= CSTAT_SPRITE_INVISIBLE; sprite[nDoppleSprite[nLocalPlayer]].cstat |= CSTAT_SPRITE_INVISIBLE; } } nCameraa = nAngle; if (!bCamera || nFreeze) { if (nSnakeCam >= 0) { pan = F16(92); viewz = playerZ; } else { viewz = playerZ + nQuake[nLocalPlayer]; int floorZ = sector[sprite[nPlayerSprite].sectnum].floorz; // pan = nVertPan[nLocalPlayer]; pan = interpolate16(PlayerList[nLocalPlayer].q16ohoriz, PlayerList[nLocalPlayer].q16horiz, smoothRatio); if (viewz > floorZ) viewz = floorZ; nCameraa += fix16_from_int((nQuake[nLocalPlayer] >> 7) % 31); nCameraa &= 0x7FFFFFF; } } else { clipmove_old((int32_t*)&playerX, (int32_t*)&playerY, (int32_t*)&playerZ, &nSector, -2000 * Sin(inita + 512), -2000 * Sin(inita), 4, 0, 0, CLIPMASK1); pan = F16(92); viewz = playerZ; } 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); UnMaskStatus(); } UpdateMap(); if (nFreeze != 3) { static uint8_t sectorFloorPal[MAXSECTORS]; static uint8_t sectorCeilingPal[MAXSECTORS]; static uint8_t wallPal[MAXWALLS]; int const viewingRange = viewingrange; if (r_usenewaspect) { newaspect_enable = 1; videoSetCorrectedAspect(); } 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]); } } renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa, nCamerapan, nSector); analyzesprites(); renderDrawMasks(); 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 (r_usenewaspect) { newaspect_enable = 0; renderSetAspect(viewingRange, tabledivide32_noinline(65536 * ydim * 8, xdim * 5)); } if (nFreeze) { nSnakeCam = -1; if (nFreeze == 2) { if (nHeadStage == 4) { nHeadStage = 5; sprite[nPlayerSprite].cstat |= 0x8000; int ang2 = fix16_to_int(nCameraa) - sprite[nPlayerSprite].ang; if (ang2 < 0) ang2 = -ang2; if (ang2 > 10) { inita -= (ang2 >> 3); return; } if (bSubTitles) { if (levelnum == 1) ReadyCinemaText(1); else ReadyCinemaText(5); } } else { if ((bSubTitles && !AdvanceCinemaText()) || KB_KeyDown[sc_Escape] || KB_KeyDown[sc_Return] || KB_KeyDown[sc_Space]) { levelnew = levelnum + 1; if (CDplaying()) fadecdaudio(); } videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); } } } else { if (nSnakeCam < 0) { DrawWeapons(smoothRatio); DrawMap(); DrawStatus(); } else { RestoreGreenPal(); if (nEnemyPal > -1) { sprite[enemy].pal = nEnemyPal; } DrawMap(); if (!bFullScreen) { MaskStatus(); } } } } else { videoClearScreen(overscanindex); DrawStatus(); } sprite[nPlayerSprite].cstat = nPlayerOldCstat; sprite[nDoppleSprite[nLocalPlayer]].cstat = nDoppleOldCstat; flash = 0; } void NoClip() { videoSetViewableArea(0, 0, xdim - 1, ydim - 1); bClip = kFalse; } void Clip() { videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); if (!bFullScreen) { MaskStatus(); } bClip = kTrue; }