GAMEUTIL.CPP 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. #include "typedefs.h"
  2. #include "engine.h"
  3. #include "gameutil.h"
  4. #include "globals.h"
  5. #include "debug4g.h"
  6. #include "palette.h"
  7. #include "misc.h"
  8. #include "tile.h"
  9. /***********************************************************************
  10. * AreSectorsNeighbors()
  11. *
  12. * Determines if two sectors adjoin
  13. **********************************************************************/
  14. BOOL AreSectorsNeighbors(int sect1, int sect2)
  15. {
  16. int i, nWall, num1, num2;
  17. dassert(sect1 >= 0 && sect1 < kMaxSectors);
  18. dassert(sect2 >= 0 && sect2 < kMaxSectors);
  19. num1 = sector[sect1].wallnum;
  20. num2 = sector[sect2].wallnum;
  21. //Traverse walls of sector with fewest walls for speed
  22. if (num1 < num2)
  23. {
  24. nWall = sector[sect1].wallptr;
  25. for( i = 0; i < num1; i++, nWall++ )
  26. if ( wall[nWall].nextsector == sect2 )
  27. return TRUE;
  28. }
  29. else
  30. {
  31. nWall = sector[sect2].wallptr;
  32. for( i = 0; i < num2; i++, nWall++ )
  33. if ( wall[nWall].nextsector == sect1 )
  34. return TRUE;
  35. }
  36. return FALSE;
  37. }
  38. /*******************************************************************************
  39. FUNCTION: FindSector()
  40. DESCRIPTION: This function works like Build's updatesector() function
  41. except it takes into account z position, which makes it
  42. give correct values in areas where sectors overlap.
  43. PARAMETERS: You should supplies a starting search sector in the nSector
  44. variable.
  45. RETURNS: TRUE if point found and updates nSector, FALSE otherwise.
  46. *******************************************************************************/
  47. BOOL FindSector(int x, int y, int z, short *nSector)
  48. {
  49. WALL *pWall;
  50. long ceilz, floorz;
  51. if ( inside(x, y, *nSector) )
  52. {
  53. getzsofslope(*nSector, x, y, &ceilz, &floorz);
  54. if (z >= ceilz && z <= floorz)
  55. return TRUE;
  56. }
  57. pWall = &wall[sector[*nSector].wallptr];
  58. for (int i = sector[*nSector].wallnum; i > 0; i--, pWall++)
  59. {
  60. short j = pWall->nextsector;
  61. if ( j >= 0 && inside(x, y, j) )
  62. {
  63. getzsofslope(j, x, y, &ceilz, &floorz);
  64. if (z >= ceilz && z <= floorz)
  65. {
  66. *nSector = j;
  67. return TRUE;
  68. }
  69. }
  70. }
  71. for (i = 0; i < numsectors; i++)
  72. {
  73. if ( inside(x, y, (short)i) )
  74. {
  75. getzsofslope((short)i, x, y, &ceilz, &floorz);
  76. if (z >= ceilz && z <= floorz)
  77. {
  78. *nSector = (short)i;
  79. return TRUE;
  80. }
  81. }
  82. }
  83. return FALSE;
  84. }
  85. #define FRAMES 64
  86. void CalcFrameRate( void )
  87. {
  88. static long ticks[FRAMES], index = 0;
  89. if (gFrameClock != ticks[index])
  90. {
  91. gFrameRate = FRAMES * kTimerRate / (gFrameClock - ticks[index]);
  92. ticks[index] = gFrameClock;
  93. }
  94. index = (index + 1) & (FRAMES - 1);
  95. }
  96. /*******************************************************************************
  97. FUNCTION: CheckProximity()
  98. DESCRIPTION: Test to see if a point is within a given range to a sprite
  99. RETURNS: TRUE if the point is in range
  100. *******************************************************************************/
  101. BOOL CheckProximity( SPRITE *pSprite, int x, int y, int z, int nSector, int dist )
  102. {
  103. dassert(pSprite != NULL);
  104. int dx = qabs(x - pSprite->x) >> 4;
  105. if (dx < dist)
  106. {
  107. int dy = qabs(y - pSprite->y) >> 4;
  108. if (dy < dist)
  109. {
  110. int dz = qabs(z - pSprite->z) >> 8;
  111. if ( dz < dist && qdist(dx, dy) < dist )
  112. {
  113. if ( cansee(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum,
  114. x, y, z, (short)nSector) )
  115. return TRUE;
  116. }
  117. }
  118. }
  119. return FALSE;
  120. }
  121. /*******************************************************************************
  122. FUNCTION: GetWallAngle()
  123. DESCRIPTION: Get the angle for a wall vector
  124. RETURNS: Angle in the range 0 - kMax360
  125. NOTES: Add kAngle90 to get the wall Normal
  126. *******************************************************************************/
  127. int GetWallAngle( int nWall )
  128. {
  129. int dx = wall[wall[nWall].point2].x - wall[nWall].x;
  130. int dy = wall[wall[nWall].point2].y - wall[nWall].y;
  131. return getangle( dx, dy );
  132. }
  133. void GetWallNormal( int nWall, int *nx, int *ny )
  134. {
  135. int dx, dy;
  136. dx = -(wall[wall[nWall].point2].y - wall[nWall].y) >> 4;
  137. dy = (wall[wall[nWall].point2].x - wall[nWall].x) >> 4;
  138. int length = ksqrt(dx * dx + dy * dy);
  139. dassert(length != 0);
  140. *nx = divscale16(dx, length);
  141. *ny = divscale16(dy, length);
  142. }
  143. BOOL IntersectRay(long x1, long y1, long vx, long vy,
  144. long x3, long y3, long z3, long x4, long y4, long z4,
  145. long *intx, long *inty, long *intz)
  146. { //p1 towards p2 is a ray
  147. long topt, topu, t;
  148. long x34 = x3 - x4;
  149. long y34 = y3 - y4;
  150. long z34 = z3 - z4;
  151. long bot = vx * y34 - vy * x34;
  152. if (bot >= 0)
  153. {
  154. if (bot == 0) return(0);
  155. long x31 = x3-x1;
  156. long y31 = y3-y1;
  157. topt = x31*y34 - y31*x34;
  158. if ( topt < 0 ) return FALSE;
  159. topu = vx*y31 - vy*x31;
  160. if ( topu < 0 || topu >= bot ) return FALSE;
  161. }
  162. else
  163. {
  164. long x31 = x3-x1;
  165. long y31 = y3-y1;
  166. topt = x31*y34 - y31*x34;
  167. if (topt > 0) return FALSE;
  168. topu = vx*y31 - vy*x31;
  169. if ( topu > 0 || topu <= bot) return FALSE;
  170. }
  171. t = divscale16(topu,bot);
  172. *intx = x3 + mulscale16(x34,t);
  173. *inty = y3 + mulscale16(y34,t);
  174. *intz = z3 + mulscale16(z34,t);
  175. return TRUE;
  176. }
  177. /*******************************************************************************
  178. FUNCTION: HitScan()
  179. DESCRIPTION: Returns the object hit, prioritized in sprite/wall/object
  180. order.
  181. PARAMETERS:
  182. RETURNS: SS_SPRITE, SS_WALL, SS_FLOOR, SS_CEILING, or -1
  183. NOTES: To simplify return object handling: SS_SPRITE is returned
  184. for flat and floor sprites. SS_WALL is returned for masked
  185. and one-way walls. In addition to the standard return
  186. value, HitScan fills the specified hitInfo structure with
  187. relevant hit data.
  188. *******************************************************************************/
  189. int HitScan( SPRITE *pSprite, int z, int dx, int dy, int dz, HITINFO *hitInfo )
  190. {
  191. dassert( pSprite != NULL );
  192. dassert( hitInfo != NULL );
  193. hitInfo->hitsect = -1;
  194. hitInfo->hitwall = -1;
  195. hitInfo->hitsprite = -1;
  196. // offset the starting point for the vector so we don't hit the source!
  197. int x = pSprite->x; // + mulscale30(16, Cos(pSprite->ang));
  198. int y = pSprite->y; // + mulscale30(16, Sin(pSprite->ang));
  199. short nSector = pSprite->sectnum;
  200. ushort oldcstat = pSprite->cstat;
  201. pSprite->cstat &= ~kSpriteHitscan;
  202. hitscan(x, y, z, nSector, dx, dy, dz << 4,
  203. &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite,
  204. &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz);
  205. pSprite->cstat = oldcstat;
  206. if (hitInfo->hitsprite >= kMaxSprites || hitInfo->hitwall >= kMaxWalls || hitInfo->hitsect >= kMaxSectors)
  207. {
  208. dprintf("hitscan failed!\n");
  209. dprintf("hitscan(%i, %i, %i, %i, %i, %i, %i ...)\n", x, y, z, pSprite->sectnum, dx, dy, dz);
  210. dprintf(" hitsect = %i\n", hitInfo->hitsect);
  211. dprintf(" hitwall = %i\n", hitInfo->hitwall);
  212. dprintf(" hitsprite = %i\n", hitInfo->hitsprite);
  213. dprintf(" hitx = %i\n", hitInfo->hitx);
  214. dprintf(" hity = %i\n", hitInfo->hity);
  215. dprintf(" hitz = %i\n", hitInfo->hitz);
  216. return -1;
  217. }
  218. if (hitInfo->hitsprite >= 0)
  219. return SS_SPRITE;
  220. if (hitInfo->hitwall >= 0)
  221. {
  222. short nNextSector = wall[hitInfo->hitwall].nextsector;
  223. if ( nNextSector == -1 ) // single-sided wall
  224. return SS_WALL;
  225. else // double-sided wall, check if we hit a masked wall
  226. {
  227. long ceilz, floorz;
  228. getzsofslope(nNextSector, hitInfo->hitx, hitInfo->hity, &ceilz, &floorz);
  229. if ( hitInfo->hitz <= ceilz || hitInfo->hitz >= floorz )
  230. return SS_WALL;
  231. return SS_MASKED;
  232. }
  233. }
  234. if (hitInfo->hitsect >= 0)
  235. return (hitInfo->hitz > z) ? SS_FLOOR : SS_CEILING;
  236. return -1;
  237. }
  238. int VectorScan( SPRITE *pSprite, int z, int dx, int dy, int dz, HITINFO *hitInfo )
  239. {
  240. dassert( pSprite != NULL );
  241. dassert( hitInfo != NULL );
  242. hitInfo->hitsect = -1;
  243. hitInfo->hitwall = -1;
  244. hitInfo->hitsprite = -1;
  245. // offset the starting point for the vector so we don't hit the source!
  246. int x = pSprite->x; // + mulscale30(16, Cos(pSprite->ang));
  247. int y = pSprite->y; // + mulscale30(16, Sin(pSprite->ang));
  248. short nSector = pSprite->sectnum;
  249. ushort oldcstat = pSprite->cstat;
  250. pSprite->cstat &= ~kSpriteHitscan;
  251. hitscan(x, y, z, nSector, dx, dy, dz << 4,
  252. &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite,
  253. &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz);
  254. pSprite->cstat = oldcstat;
  255. retry:
  256. if (hitInfo->hitsprite >= kMaxSprites || hitInfo->hitwall >= kMaxWalls || hitInfo->hitsect >= kMaxSectors)
  257. {
  258. dprintf("hitscan failed!\n");
  259. dprintf("hitscan(%i, %i, %i, %i, %i, %i, %i ...)\n", x, y, z, pSprite->sectnum, dx, dy, dz);
  260. dprintf(" hitsect = %i\n", hitInfo->hitsect);
  261. dprintf(" hitwall = %i\n", hitInfo->hitwall);
  262. dprintf(" hitsprite = %i\n", hitInfo->hitsprite);
  263. dprintf(" hitx = %i\n", hitInfo->hitx);
  264. dprintf(" hity = %i\n", hitInfo->hity);
  265. dprintf(" hitz = %i\n", hitInfo->hitz);
  266. return -1;
  267. }
  268. if (hitInfo->hitsprite >= 0)
  269. {
  270. SPRITE *pSprite = &sprite[hitInfo->hitsprite];
  271. switch ( pSprite->cstat & kSpriteRMask )
  272. {
  273. case kSpriteFace:
  274. {
  275. int nTile = pSprite->picnum;
  276. int height = (tilesizy[nTile] * pSprite->yrepeat << 2);
  277. int zBot = pSprite->z;
  278. if ( pSprite->cstat & kSpriteOriginAlign )
  279. zBot += height / 2;
  280. if ( picanm[nTile].ycenter )
  281. zBot -= picanm[nTile].ycenter * pSprite->yrepeat << 2;
  282. int texy = muldiv(zBot - hitInfo->hitz, tilesizy[nTile], height);
  283. if ( !(pSprite->cstat & kSpriteFlipY) )
  284. texy = tilesizy[nTile] - texy;
  285. int width = tilesizx[nTile] * pSprite->xrepeat >> 2;
  286. width = width * 3 / 4; // aspect ratio correction?
  287. int top = dx * (y - pSprite->y) - dy * (x - pSprite->x);
  288. int bot = ksqrt(dx * dx + dy * dy);
  289. int dist = top / bot;
  290. int texx = muldiv(dist, tilesizx[nTile], width);
  291. texx += tilesizx[nTile] / 2 + picanm[nTile].xcenter;
  292. dprintf("Hit sprite at texel coor %d, %d\n", texx, texy);
  293. BYTE *pTile = tileLoadTile(nTile);
  294. BYTE texel = pTile[texx * tilesizy[nTile] + texy];
  295. if ( texel == 255 ) // mask color
  296. {
  297. dprintf("Texel is clear\n");
  298. // clear hitscan bits on sprite
  299. ushort oldcstat = pSprite->cstat;
  300. pSprite->cstat &= ~kSpriteHitscan;
  301. hitInfo->hitsect = -1;
  302. hitInfo->hitwall = -1;
  303. hitInfo->hitsprite = -1;
  304. x = hitInfo->hitx;
  305. y = hitInfo->hity;
  306. z = hitInfo->hitz;
  307. nSector = hitInfo->hitsect;
  308. hitscan(x, y, z, nSector, dx, dy, dz << 4,
  309. &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite,
  310. &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz);
  311. // restore the hitscan bits
  312. pSprite->cstat = oldcstat;
  313. goto retry;
  314. }
  315. break;
  316. }
  317. }
  318. return SS_SPRITE;
  319. }
  320. if (hitInfo->hitwall >= 0)
  321. {
  322. short nNextSector = wall[hitInfo->hitwall].nextsector;
  323. if ( nNextSector == -1 ) // single-sided wall
  324. return SS_WALL;
  325. else // double-sided wall, check if we hit a masked wall
  326. {
  327. long ceilz, floorz;
  328. getzsofslope(nNextSector, hitInfo->hitx, hitInfo->hity, &ceilz, &floorz);
  329. if ( hitInfo->hitz <= ceilz || hitInfo->hitz >= floorz )
  330. return SS_WALL;
  331. WALL *pWall = &wall[hitInfo->hitwall];
  332. if ( !(pWall->cstat & kWallMasked) )
  333. {
  334. dprintf("Hitscan hit wall which is not masked\n",
  335. hitInfo->hitwall);
  336. dprintf("hitscan(%i, %i, %i, %i, %i, %i, %i ...)\n", x, y, z, pSprite->sectnum, dx, dy, dz);
  337. dprintf(" hitsect = %i\n", hitInfo->hitsect);
  338. dprintf(" hitwall = %i\n", hitInfo->hitwall);
  339. dprintf(" hitsprite = %i\n", hitInfo->hitsprite);
  340. dprintf(" hitx = %i\n", hitInfo->hitx);
  341. dprintf(" hity = %i\n", hitInfo->hity);
  342. dprintf(" hitz = %i\n", hitInfo->hitz);
  343. return SS_WALL;
  344. }
  345. // must be a masked wall
  346. int zOrg;
  347. if ( pWall->cstat & kWallBottomOrg )
  348. zOrg = ClipHigh(sector[hitInfo->hitsect].floorz, sector[nNextSector].floorz);
  349. else
  350. zOrg = ClipLow(sector[hitInfo->hitsect].ceilingz, sector[nNextSector].ceilingz);
  351. int zOff = (hitInfo->hitz - zOrg) >> 8;
  352. if ( pWall->cstat & kWallFlipY)
  353. zOff = -zOff;
  354. int nTile = pWall->overpicnum;
  355. int tsizx = tilesizx[nTile];
  356. int tsizy = tilesizy[nTile];
  357. BOOL xnice = (1 << (picsiz[nTile] & 15)) == tsizx;
  358. BOOL ynice = (1 << (picsiz[nTile] >> 4)) == tsizy;
  359. // calculate y texel coord
  360. int texy = zOff * pWall->yrepeat / 8 + pWall->ypanning * tsizy / 256;
  361. int len = qdist(pWall->x - wall[pWall->point2].x, pWall->y - wall[pWall->point2].y);
  362. int dist;
  363. if ( pWall->cstat & kWallFlipX )
  364. dist = qdist(hitInfo->hitx - wall[pWall->point2].x, hitInfo->hity - wall[pWall->point2].y);
  365. else
  366. dist = qdist(hitInfo->hitx - pWall->x, hitInfo->hity - pWall->y);
  367. // calculate x texel coord
  368. int texx = dist * pWall->xrepeat * 8 / len + pWall->xpanning;
  369. if ( xnice )
  370. texx &= (tsizx - 1);
  371. else
  372. texx %= tsizx;
  373. if ( ynice )
  374. texy &= (tsizy - 1);
  375. else
  376. texy %= tsizy;
  377. dprintf("Hitscan on masked wall %d at texel coord %d, %d\n",
  378. hitInfo->hitwall, texx, texy);
  379. BYTE *pTile = tileLoadTile(nTile);
  380. BYTE texel;
  381. if ( ynice )
  382. texel = pTile[(texx << (picsiz[nTile] >> 4)) + texy];
  383. else
  384. texel = pTile[texx * tilesizy[nTile] + texy];
  385. if ( texel == 255 ) // mask color
  386. {
  387. dprintf("Texel is clear\n");
  388. // clear hitscan bits on both sides of the wall
  389. ushort oldcstat1 = pWall->cstat;
  390. pWall->cstat &= ~kWallHitscan;
  391. ushort oldcstat2 = wall[pWall->point2].cstat;
  392. wall[pWall->point2].cstat &= ~kWallHitscan;
  393. hitInfo->hitsect = -1;
  394. hitInfo->hitwall = -1;
  395. hitInfo->hitsprite = -1;
  396. hitscan(x, y, z, nSector, dx, dy, dz << 4,
  397. &hitInfo->hitsect, &hitInfo->hitwall, &hitInfo->hitsprite,
  398. &hitInfo->hitx, &hitInfo->hity, &hitInfo->hitz);
  399. // restore the hitscan bits
  400. pWall->cstat = oldcstat1;
  401. wall[pWall->point2].cstat = oldcstat2;
  402. goto retry;
  403. }
  404. return SS_MASKED;
  405. }
  406. }
  407. if (hitInfo->hitsect >= 0)
  408. return (hitInfo->hitz > z) ? SS_FLOOR : SS_CEILING;
  409. return -1;
  410. }
  411. /*******************************************************************************
  412. FUNCTION: GetZRange()
  413. DESCRIPTION: Cover function for Ken's getzrange
  414. PARAMETERS:
  415. RETURNS:
  416. NOTES:
  417. *******************************************************************************/
  418. void GetZRange( SPRITE *pSprite, long *ceilZ, long *ceilHit, long *floorZ, long *floorHit,
  419. int clipdist, char cliptype )
  420. {
  421. dassert(pSprite != NULL);
  422. short oldcstat = pSprite->cstat;
  423. pSprite->cstat &= ~kSpriteBlocking & ~kSpriteHitscan;
  424. getzrange(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum,
  425. ceilZ, ceilHit, floorZ, floorHit, clipdist, cliptype);
  426. pSprite->cstat = oldcstat;
  427. }
  428. unsigned ClipMove( long *px, long *py, long *pz, short *pnSector, long dx, long dy,
  429. int wallDist, int ceilDist, int floorDist, char clipType )
  430. {
  431. // return clipmove(px, py, pz, pnSector, dx << 14, dy << 14,
  432. // wallDist, ceilDist, floorDist, clipType);
  433. long x = *px;
  434. long y = *py;
  435. long z = *pz;
  436. short nSector = *pnSector;
  437. unsigned ccode = clipmove(px, py, pz, pnSector, dx << 14, dy << 14,
  438. wallDist, ceilDist, floorDist, clipType);
  439. // force temporary fix to ken's inside() bug
  440. if ( *pnSector == -1)
  441. {
  442. // return to last known good location
  443. *px = x;
  444. *py = y;
  445. *pz = z;
  446. *pnSector = nSector;
  447. }
  448. return ccode;
  449. }
  450. #if 0
  451. int rintersect( int x, int y, int z, int vx, int vy, int vz, int xc,
  452. int yc, int xd, int yd, int *xint, int *yint, int *zint)
  453. {
  454. int lx = xd - xc;
  455. int ly = yd - yc;
  456. int qx = x - xc;
  457. int qy = y - yc;
  458. int den = vx * ly - vy * lx;
  459. if ( den <= 0 )
  460. return 0; // lines are parallel or wrong side of line
  461. int snum = qy * vx - qx * vy;
  462. if ( snum < 0 || snum >= den )
  463. return 0; // not within CD
  464. int rnum = qy * lx - qx * ly;
  465. if ( rnum < 0 )
  466. return 0; // before AB
  467. int r = divscale16(rnum, den);
  468. *xint = x + mulscale16(vx, r);
  469. *yint = y + mulscale16(vy, r);
  470. *zint = z + mulscale16(vz, r);
  471. return 1;
  472. }
  473. #endif