#include "typedefs.h" #include "engine.h" #include "gameutil.h" #include "globals.h" #include "debug4g.h" #include "palette.h" #include "misc.h" #include "tile.h" /*********************************************************************** * AreSectorsNeighbors() * * Determines if two sectors adjoin **********************************************************************/ BOOL AreSectorsNeighbors(int sect1, int sect2) { int i, nWall, num1, num2; dassert(sect1 >= 0 && sect1 < kMaxSectors); dassert(sect2 >= 0 && sect2 < kMaxSectors); num1 = sector[sect1].wallnum; num2 = sector[sect2].wallnum; //Traverse walls of sector with fewest walls for speed if (num1 < num2) { nWall = sector[sect1].wallptr; for( i = 0; i < num1; i++, nWall++ ) if ( wall[nWall].nextsector == sect2 ) return TRUE; } else { nWall = sector[sect2].wallptr; for( i = 0; i < num2; i++, nWall++ ) if ( wall[nWall].nextsector == sect1 ) return TRUE; } return FALSE; } /******************************************************************************* FUNCTION: FindSector() DESCRIPTION: This function works like Build's updatesector() function except it takes into account z position, which makes it give correct values in areas where sectors overlap. PARAMETERS: You should supplies a starting search sector in the nSector variable. RETURNS: TRUE if point found and updates nSector, FALSE otherwise. *******************************************************************************/ BOOL FindSector(int x, int y, int z, short *nSector) { WALL *pWall; long ceilz, floorz; if ( inside(x, y, *nSector) ) { getzsofslope(*nSector, x, y, &ceilz, &floorz); if (z >= ceilz && z <= floorz) return TRUE; } pWall = &wall[sector[*nSector].wallptr]; for (int i = sector[*nSector].wallnum; i > 0; i--, pWall++) { short j = pWall->nextsector; if ( j >= 0 && inside(x, y, j) ) { getzsofslope(j, x, y, &ceilz, &floorz); if (z >= ceilz && z <= floorz) { *nSector = j; return TRUE; } } } for (i = 0; i < numsectors; i++) { if ( inside(x, y, (short)i) ) { getzsofslope((short)i, x, y, &ceilz, &floorz); if (z >= ceilz && z <= floorz) { *nSector = (short)i; return TRUE; } } } return FALSE; } #define FRAMES 64 void CalcFrameRate( void ) { static long ticks[FRAMES], index = 0; if (gFrameClock != ticks[index]) { gFrameRate = FRAMES * kTimerRate / (gFrameClock - ticks[index]); ticks[index] = gFrameClock; } index = (index + 1) & (FRAMES - 1); } /******************************************************************************* FUNCTION: CheckProximity() DESCRIPTION: Test to see if a point is within a given range to a sprite RETURNS: TRUE if the point is in range *******************************************************************************/ BOOL CheckProximity( SPRITE *pSprite, int x, int y, int z, int nSector, int dist ) { dassert(pSprite != NULL); int dx = qabs(x - pSprite->x) >> 4; if (dx < dist) { int dy = qabs(y - pSprite->y) >> 4; if (dy < dist) { int dz = qabs(z - pSprite->z) >> 8; if ( dz < dist && qdist(dx, dy) < dist ) { if ( cansee(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, x, y, z, (short)nSector) ) return TRUE; } } } return FALSE; } /******************************************************************************* FUNCTION: GetWallAngle() DESCRIPTION: Get the angle for a wall vector RETURNS: Angle in the range 0 - kMax360 NOTES: Add kAngle90 to get the wall Normal *******************************************************************************/ int GetWallAngle( int nWall ) { int dx = wall[wall[nWall].point2].x - wall[nWall].x; int dy = wall[wall[nWall].point2].y - wall[nWall].y; return getangle( dx, dy ); } void GetWallNormal( int nWall, int *nx, int *ny ) { int dx, dy; dx = -(wall[wall[nWall].point2].y - wall[nWall].y) >> 4; dy = (wall[wall[nWall].point2].x - wall[nWall].x) >> 4; int length = ksqrt(dx * dx + dy * dy); dassert(length != 0); *nx = divscale16(dx, length); *ny = divscale16(dy, length); } BOOL IntersectRay(long x1, long y1, long vx, long vy, long x3, long y3, long z3, long x4, long y4, long z4, long *intx, long *inty, long *intz) { //p1 towards p2 is a ray long topt, topu, t; long x34 = x3 - x4; long y34 = y3 - y4; long z34 = z3 - z4; long bot = vx * y34 - vy * x34; if (bot >= 0) { if (bot == 0) return(0); long x31 = x3-x1; long y31 = y3-y1; topt = x31*y34 - y31*x34; if ( topt < 0 ) return FALSE; topu = vx*y31 - vy*x31; if ( topu < 0 || topu >= bot ) return FALSE; } else { long x31 = x3-x1; long y31 = y3-y1; topt = x31*y34 - y31*x34; if (topt > 0) return FALSE; topu = vx*y31 - vy*x31; if ( topu > 0 || topu <= bot) return FALSE; } t = divscale16(topu,bot); *intx = x3 + mulscale16(x34,t); *inty = y3 + mulscale16(y34,t); *intz = z3 + mulscale16(z34,t); return TRUE; } /******************************************************************************* FUNCTION: HitScan() DESCRIPTION: Returns the object hit, prioritized in sprite/wall/object order. PARAMETERS: RETURNS: SS_SPRITE, SS_WALL, SS_FLOOR, SS_CEILING, or -1 NOTES: To simplify return object handling: SS_SPRITE is returned for flat and floor sprites. SS_WALL is returned for masked and one-way walls. In addition to the standard return value, HitScan fills the specified hitInfo structure with relevant hit data. *******************************************************************************/ int HitScan( SPRITE *pSprite, int z, int dx, int dy, int dz, HITINFO *hitInfo ) { dassert( pSprite != NULL ); dassert( hitInfo != NULL ); hitInfo->hitsect = -1; hitInfo->hitwall = -1; hitInfo->hitsprite = -1; // offset the starting point for the vector so we don't hit the source! int x = pSprite->x; // + mulscale30(16, Cos(pSprite->ang)); int y = pSprite->y; // + mulscale30(16, Sin(pSprite->ang)); short nSector = pSprite->sectnum; ushort oldcstat = pSprite->cstat; pSprite->cstat &= ~kSpriteHitscan; hitscan(x, y, z, nSector, dx, dy, dz << 4, &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite, &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz); pSprite->cstat = oldcstat; if (hitInfo->hitsprite >= kMaxSprites || hitInfo->hitwall >= kMaxWalls || hitInfo->hitsect >= kMaxSectors) { dprintf("hitscan failed!\n"); dprintf("hitscan(%i, %i, %i, %i, %i, %i, %i ...)\n", x, y, z, pSprite->sectnum, dx, dy, dz); dprintf(" hitsect = %i\n", hitInfo->hitsect); dprintf(" hitwall = %i\n", hitInfo->hitwall); dprintf(" hitsprite = %i\n", hitInfo->hitsprite); dprintf(" hitx = %i\n", hitInfo->hitx); dprintf(" hity = %i\n", hitInfo->hity); dprintf(" hitz = %i\n", hitInfo->hitz); return -1; } if (hitInfo->hitsprite >= 0) return SS_SPRITE; if (hitInfo->hitwall >= 0) { short nNextSector = wall[hitInfo->hitwall].nextsector; if ( nNextSector == -1 ) // single-sided wall return SS_WALL; else // double-sided wall, check if we hit a masked wall { long ceilz, floorz; getzsofslope(nNextSector, hitInfo->hitx, hitInfo->hity, &ceilz, &floorz); if ( hitInfo->hitz <= ceilz || hitInfo->hitz >= floorz ) return SS_WALL; return SS_MASKED; } } if (hitInfo->hitsect >= 0) return (hitInfo->hitz > z) ? SS_FLOOR : SS_CEILING; return -1; } int VectorScan( SPRITE *pSprite, int z, int dx, int dy, int dz, HITINFO *hitInfo ) { dassert( pSprite != NULL ); dassert( hitInfo != NULL ); hitInfo->hitsect = -1; hitInfo->hitwall = -1; hitInfo->hitsprite = -1; // offset the starting point for the vector so we don't hit the source! int x = pSprite->x; // + mulscale30(16, Cos(pSprite->ang)); int y = pSprite->y; // + mulscale30(16, Sin(pSprite->ang)); short nSector = pSprite->sectnum; ushort oldcstat = pSprite->cstat; pSprite->cstat &= ~kSpriteHitscan; hitscan(x, y, z, nSector, dx, dy, dz << 4, &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite, &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz); pSprite->cstat = oldcstat; retry: if (hitInfo->hitsprite >= kMaxSprites || hitInfo->hitwall >= kMaxWalls || hitInfo->hitsect >= kMaxSectors) { dprintf("hitscan failed!\n"); dprintf("hitscan(%i, %i, %i, %i, %i, %i, %i ...)\n", x, y, z, pSprite->sectnum, dx, dy, dz); dprintf(" hitsect = %i\n", hitInfo->hitsect); dprintf(" hitwall = %i\n", hitInfo->hitwall); dprintf(" hitsprite = %i\n", hitInfo->hitsprite); dprintf(" hitx = %i\n", hitInfo->hitx); dprintf(" hity = %i\n", hitInfo->hity); dprintf(" hitz = %i\n", hitInfo->hitz); return -1; } if (hitInfo->hitsprite >= 0) { SPRITE *pSprite = &sprite[hitInfo->hitsprite]; switch ( pSprite->cstat & kSpriteRMask ) { case kSpriteFace: { int nTile = pSprite->picnum; int height = (tilesizy[nTile] * pSprite->yrepeat << 2); int zBot = pSprite->z; if ( pSprite->cstat & kSpriteOriginAlign ) zBot += height / 2; if ( picanm[nTile].ycenter ) zBot -= picanm[nTile].ycenter * pSprite->yrepeat << 2; int texy = muldiv(zBot - hitInfo->hitz, tilesizy[nTile], height); if ( !(pSprite->cstat & kSpriteFlipY) ) texy = tilesizy[nTile] - texy; int width = tilesizx[nTile] * pSprite->xrepeat >> 2; width = width * 3 / 4; // aspect ratio correction? int top = dx * (y - pSprite->y) - dy * (x - pSprite->x); int bot = ksqrt(dx * dx + dy * dy); int dist = top / bot; int texx = muldiv(dist, tilesizx[nTile], width); texx += tilesizx[nTile] / 2 + picanm[nTile].xcenter; dprintf("Hit sprite at texel coor %d, %d\n", texx, texy); BYTE *pTile = tileLoadTile(nTile); BYTE texel = pTile[texx * tilesizy[nTile] + texy]; if ( texel == 255 ) // mask color { dprintf("Texel is clear\n"); // clear hitscan bits on sprite ushort oldcstat = pSprite->cstat; pSprite->cstat &= ~kSpriteHitscan; hitInfo->hitsect = -1; hitInfo->hitwall = -1; hitInfo->hitsprite = -1; x = hitInfo->hitx; y = hitInfo->hity; z = hitInfo->hitz; nSector = hitInfo->hitsect; hitscan(x, y, z, nSector, dx, dy, dz << 4, &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite, &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz); // restore the hitscan bits pSprite->cstat = oldcstat; goto retry; } break; } } return SS_SPRITE; } if (hitInfo->hitwall >= 0) { short nNextSector = wall[hitInfo->hitwall].nextsector; if ( nNextSector == -1 ) // single-sided wall return SS_WALL; else // double-sided wall, check if we hit a masked wall { long ceilz, floorz; getzsofslope(nNextSector, hitInfo->hitx, hitInfo->hity, &ceilz, &floorz); if ( hitInfo->hitz <= ceilz || hitInfo->hitz >= floorz ) return SS_WALL; WALL *pWall = &wall[hitInfo->hitwall]; if ( !(pWall->cstat & kWallMasked) ) { dprintf("Hitscan hit wall which is not masked\n", hitInfo->hitwall); dprintf("hitscan(%i, %i, %i, %i, %i, %i, %i ...)\n", x, y, z, pSprite->sectnum, dx, dy, dz); dprintf(" hitsect = %i\n", hitInfo->hitsect); dprintf(" hitwall = %i\n", hitInfo->hitwall); dprintf(" hitsprite = %i\n", hitInfo->hitsprite); dprintf(" hitx = %i\n", hitInfo->hitx); dprintf(" hity = %i\n", hitInfo->hity); dprintf(" hitz = %i\n", hitInfo->hitz); return SS_WALL; } // must be a masked wall int zOrg; if ( pWall->cstat & kWallBottomOrg ) zOrg = ClipHigh(sector[hitInfo->hitsect].floorz, sector[nNextSector].floorz); else zOrg = ClipLow(sector[hitInfo->hitsect].ceilingz, sector[nNextSector].ceilingz); int zOff = (hitInfo->hitz - zOrg) >> 8; if ( pWall->cstat & kWallFlipY) zOff = -zOff; int nTile = pWall->overpicnum; int tsizx = tilesizx[nTile]; int tsizy = tilesizy[nTile]; BOOL xnice = (1 << (picsiz[nTile] & 15)) == tsizx; BOOL ynice = (1 << (picsiz[nTile] >> 4)) == tsizy; // calculate y texel coord int texy = zOff * pWall->yrepeat / 8 + pWall->ypanning * tsizy / 256; int len = qdist(pWall->x - wall[pWall->point2].x, pWall->y - wall[pWall->point2].y); int dist; if ( pWall->cstat & kWallFlipX ) dist = qdist(hitInfo->hitx - wall[pWall->point2].x, hitInfo->hity - wall[pWall->point2].y); else dist = qdist(hitInfo->hitx - pWall->x, hitInfo->hity - pWall->y); // calculate x texel coord int texx = dist * pWall->xrepeat * 8 / len + pWall->xpanning; if ( xnice ) texx &= (tsizx - 1); else texx %= tsizx; if ( ynice ) texy &= (tsizy - 1); else texy %= tsizy; dprintf("Hitscan on masked wall %d at texel coord %d, %d\n", hitInfo->hitwall, texx, texy); BYTE *pTile = tileLoadTile(nTile); BYTE texel; if ( ynice ) texel = pTile[(texx << (picsiz[nTile] >> 4)) + texy]; else texel = pTile[texx * tilesizy[nTile] + texy]; if ( texel == 255 ) // mask color { dprintf("Texel is clear\n"); // clear hitscan bits on both sides of the wall ushort oldcstat1 = pWall->cstat; pWall->cstat &= ~kWallHitscan; ushort oldcstat2 = wall[pWall->point2].cstat; wall[pWall->point2].cstat &= ~kWallHitscan; hitInfo->hitsect = -1; hitInfo->hitwall = -1; hitInfo->hitsprite = -1; hitscan(x, y, z, nSector, dx, dy, dz << 4, &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite, &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz); // restore the hitscan bits pWall->cstat = oldcstat1; wall[pWall->point2].cstat = oldcstat2; goto retry; } return SS_MASKED; } } if (hitInfo->hitsect >= 0) return (hitInfo->hitz > z) ? SS_FLOOR : SS_CEILING; return -1; } /******************************************************************************* FUNCTION: GetZRange() DESCRIPTION: Cover function for Ken's getzrange PARAMETERS: RETURNS: NOTES: *******************************************************************************/ void GetZRange( SPRITE *pSprite, long *ceilZ, long *ceilHit, long *floorZ, long *floorHit, int clipdist, char cliptype ) { dassert(pSprite != NULL); short oldcstat = pSprite->cstat; pSprite->cstat &= ~kSpriteBlocking & ~kSpriteHitscan; getzrange(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, ceilZ, ceilHit, floorZ, floorHit, clipdist, cliptype); pSprite->cstat = oldcstat; } unsigned ClipMove( long *px, long *py, long *pz, short *pnSector, long dx, long dy, int wallDist, int ceilDist, int floorDist, char clipType ) { // return clipmove(px, py, pz, pnSector, dx << 14, dy << 14, // wallDist, ceilDist, floorDist, clipType); long x = *px; long y = *py; long z = *pz; short nSector = *pnSector; unsigned ccode = clipmove(px, py, pz, pnSector, dx << 14, dy << 14, wallDist, ceilDist, floorDist, clipType); // force temporary fix to ken's inside() bug if ( *pnSector == -1) { // return to last known good location *px = x; *py = y; *pz = z; *pnSector = nSector; } return ccode; } #if 0 int rintersect( int x, int y, int z, int vx, int vy, int vz, int xc, int yc, int xd, int yd, int *xint, int *yint, int *zint) { int lx = xd - xc; int ly = yd - yc; int qx = x - xc; int qy = y - yc; int den = vx * ly - vy * lx; if ( den <= 0 ) return 0; // lines are parallel or wrong side of line int snum = qy * vx - qx * vy; if ( snum < 0 || snum >= den ) return 0; // not within CD int rnum = qy * lx - qx * ly; if ( rnum < 0 ) return 0; // before AB int r = divscale16(rnum, den); *xint = x + mulscale16(vx, r); *yint = y + mulscale16(vy, r); *zint = z + mulscale16(vz, r); return 1; } #endif