P_MAPUTL.C 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. // P_maputl.c
  2. #include "DoomDef.h"
  3. #include "P_local.h"
  4. /*
  5. ===================
  6. =
  7. = P_AproxDistance
  8. =
  9. = Gives an estimation of distance (not exact)
  10. =
  11. ===================
  12. */
  13. fixed_t P_AproxDistance (fixed_t dx, fixed_t dy)
  14. {
  15. dx = abs(dx);
  16. dy = abs(dy);
  17. if (dx < dy)
  18. return dx+dy-(dx>>1);
  19. return dx+dy-(dy>>1);
  20. }
  21. /*
  22. ==================
  23. =
  24. = P_PointOnLineSide
  25. =
  26. = Returns 0 or 1
  27. ==================
  28. */
  29. int P_PointOnLineSide (fixed_t x, fixed_t y, line_t *line)
  30. {
  31. fixed_t dx,dy;
  32. fixed_t left, right;
  33. if (!line->dx)
  34. {
  35. if (x <= line->v1->x)
  36. return line->dy > 0;
  37. return line->dy < 0;
  38. }
  39. if (!line->dy)
  40. {
  41. if (y <= line->v1->y)
  42. return line->dx < 0;
  43. return line->dx > 0;
  44. }
  45. dx = (x - line->v1->x);
  46. dy = (y - line->v1->y);
  47. left = FixedMul ( line->dy>>FRACBITS , dx );
  48. right = FixedMul ( dy , line->dx>>FRACBITS );
  49. if (right < left)
  50. return 0; // front side
  51. return 1; // back side
  52. }
  53. /*
  54. =================
  55. =
  56. = P_BoxOnLineSide
  57. =
  58. = Considers the line to be infinite
  59. = Returns side 0 or 1, -1 if box crosses the line
  60. =================
  61. */
  62. int P_BoxOnLineSide (fixed_t *tmbox, line_t *ld)
  63. {
  64. int p1, p2;
  65. switch (ld->slopetype)
  66. {
  67. case ST_HORIZONTAL:
  68. p1 = tmbox[BOXTOP] > ld->v1->y;
  69. p2 = tmbox[BOXBOTTOM] > ld->v1->y;
  70. if (ld->dx < 0)
  71. {
  72. p1 ^= 1;
  73. p2 ^= 1;
  74. }
  75. break;
  76. case ST_VERTICAL:
  77. p1 = tmbox[BOXRIGHT] < ld->v1->x;
  78. p2 = tmbox[BOXLEFT] < ld->v1->x;
  79. if (ld->dy < 0)
  80. {
  81. p1 ^= 1;
  82. p2 ^= 1;
  83. }
  84. break;
  85. case ST_POSITIVE:
  86. p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld);
  87. p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld);
  88. break;
  89. case ST_NEGATIVE:
  90. p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld);
  91. p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld);
  92. break;
  93. }
  94. if (p1 == p2)
  95. return p1;
  96. return -1;
  97. }
  98. /*
  99. ==================
  100. =
  101. = P_PointOnDivlineSide
  102. =
  103. = Returns 0 or 1
  104. ==================
  105. */
  106. int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t *line)
  107. {
  108. fixed_t dx,dy;
  109. fixed_t left, right;
  110. if (!line->dx)
  111. {
  112. if (x <= line->x)
  113. return line->dy > 0;
  114. return line->dy < 0;
  115. }
  116. if (!line->dy)
  117. {
  118. if (y <= line->y)
  119. return line->dx < 0;
  120. return line->dx > 0;
  121. }
  122. dx = (x - line->x);
  123. dy = (y - line->y);
  124. // try to quickly decide by looking at sign bits
  125. if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 )
  126. {
  127. if ( (line->dy ^ dx) & 0x80000000 )
  128. return 1; // (left is negative)
  129. return 0;
  130. }
  131. left = FixedMul ( line->dy>>8, dx>>8 );
  132. right = FixedMul ( dy>>8 , line->dx>>8 );
  133. if (right < left)
  134. return 0; // front side
  135. return 1; // back side
  136. }
  137. /*
  138. ==============
  139. =
  140. = P_MakeDivline
  141. =
  142. ==============
  143. */
  144. void P_MakeDivline (line_t *li, divline_t *dl)
  145. {
  146. dl->x = li->v1->x;
  147. dl->y = li->v1->y;
  148. dl->dx = li->dx;
  149. dl->dy = li->dy;
  150. }
  151. /*
  152. ===============
  153. =
  154. = P_InterceptVector
  155. =
  156. = Returns the fractional intercept point along the first divline
  157. =
  158. = This is only called by the addthings and addlines traversers
  159. ===============
  160. */
  161. fixed_t P_InterceptVector (divline_t *v2, divline_t *v1)
  162. {
  163. #if 1
  164. fixed_t frac, num, den;
  165. den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy);
  166. if (den == 0)
  167. return 0;
  168. // I_Error ("P_InterceptVector: parallel");
  169. num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy) +
  170. FixedMul ( (v2->y - v1->y)>>8 , v1->dx);
  171. frac = FixedDiv (num , den);
  172. return frac;
  173. #else
  174. float frac, num, den, v1x,v1y,v1dx,v1dy,v2x,v2y,v2dx,v2dy;
  175. v1x = (float)v1->x/FRACUNIT;
  176. v1y = (float)v1->y/FRACUNIT;
  177. v1dx = (float)v1->dx/FRACUNIT;
  178. v1dy = (float)v1->dy/FRACUNIT;
  179. v2x = (float)v2->x/FRACUNIT;
  180. v2y = (float)v2->y/FRACUNIT;
  181. v2dx = (float)v2->dx/FRACUNIT;
  182. v2dy = (float)v2->dy/FRACUNIT;
  183. den = v1dy*v2dx - v1dx*v2dy;
  184. if (den == 0)
  185. return 0; // parallel
  186. num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;
  187. frac = num / den;
  188. return frac*FRACUNIT;
  189. #endif
  190. }
  191. /*
  192. ==================
  193. =
  194. = P_LineOpening
  195. =
  196. = Sets opentop and openbottom to the window through a two sided line
  197. = OPTIMIZE: keep this precalculated
  198. ==================
  199. */
  200. fixed_t opentop, openbottom, openrange;
  201. fixed_t lowfloor;
  202. void P_LineOpening (line_t *linedef)
  203. {
  204. sector_t *front, *back;
  205. if (linedef->sidenum[1] == -1)
  206. { // single sided line
  207. openrange = 0;
  208. return;
  209. }
  210. front = linedef->frontsector;
  211. back = linedef->backsector;
  212. if (front->ceilingheight < back->ceilingheight)
  213. opentop = front->ceilingheight;
  214. else
  215. opentop = back->ceilingheight;
  216. if (front->floorheight > back->floorheight)
  217. {
  218. openbottom = front->floorheight;
  219. lowfloor = back->floorheight;
  220. }
  221. else
  222. {
  223. openbottom = back->floorheight;
  224. lowfloor = front->floorheight;
  225. }
  226. openrange = opentop - openbottom;
  227. }
  228. /*
  229. ===============================================================================
  230. THING POSITION SETTING
  231. ===============================================================================
  232. */
  233. /*
  234. ===================
  235. =
  236. = P_UnsetThingPosition
  237. =
  238. = Unlinks a thing from block map and sectors
  239. =
  240. ===================
  241. */
  242. void P_UnsetThingPosition (mobj_t *thing)
  243. {
  244. int blockx, blocky;
  245. if ( ! (thing->flags & MF_NOSECTOR) )
  246. { // inert things don't need to be in blockmap
  247. // unlink from subsector
  248. if (thing->snext)
  249. thing->snext->sprev = thing->sprev;
  250. if (thing->sprev)
  251. thing->sprev->snext = thing->snext;
  252. else
  253. thing->subsector->sector->thinglist = thing->snext;
  254. }
  255. if ( ! (thing->flags & MF_NOBLOCKMAP) )
  256. { // inert things don't need to be in blockmap
  257. // unlink from block map
  258. if (thing->bnext)
  259. thing->bnext->bprev = thing->bprev;
  260. if (thing->bprev)
  261. thing->bprev->bnext = thing->bnext;
  262. else
  263. {
  264. blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT;
  265. blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT;
  266. if (blockx>=0 && blockx < bmapwidth
  267. && blocky>=0 && blocky <bmapheight)
  268. blocklinks[blocky*bmapwidth+blockx] = thing->bnext;
  269. }
  270. }
  271. }
  272. /*
  273. ===================
  274. =
  275. = P_SetThingPosition
  276. =
  277. = Links a thing into both a block and a subsector based on it's x y
  278. = Sets thing->subsector properly
  279. =
  280. ===================
  281. */
  282. void P_SetThingPosition (mobj_t *thing)
  283. {
  284. subsector_t *ss;
  285. sector_t *sec;
  286. int blockx, blocky;
  287. mobj_t **link;
  288. //
  289. // link into subsector
  290. //
  291. ss = R_PointInSubsector (thing->x,thing->y);
  292. thing->subsector = ss;
  293. if ( ! (thing->flags & MF_NOSECTOR) )
  294. { // invisible things don't go into the sector links
  295. sec = ss->sector;
  296. thing->sprev = NULL;
  297. thing->snext = sec->thinglist;
  298. if (sec->thinglist)
  299. sec->thinglist->sprev = thing;
  300. sec->thinglist = thing;
  301. }
  302. //
  303. // link into blockmap
  304. //
  305. if ( ! (thing->flags & MF_NOBLOCKMAP) )
  306. { // inert things don't need to be in blockmap
  307. blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT;
  308. blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT;
  309. if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky <bmapheight)
  310. {
  311. link = &blocklinks[blocky*bmapwidth+blockx];
  312. thing->bprev = NULL;
  313. thing->bnext = *link;
  314. if (*link)
  315. (*link)->bprev = thing;
  316. *link = thing;
  317. }
  318. else
  319. { // thing is off the map
  320. thing->bnext = thing->bprev = NULL;
  321. }
  322. }
  323. }
  324. /*
  325. ===============================================================================
  326. BLOCK MAP ITERATORS
  327. For each line/thing in the given mapblock, call the passed function.
  328. If the function returns false, exit with false without checking anything else.
  329. ===============================================================================
  330. */
  331. /*
  332. ==================
  333. =
  334. = P_BlockLinesIterator
  335. =
  336. = The validcount flags are used to avoid checking lines
  337. = that are marked in multiple mapblocks, so increment validcount before
  338. = the first call to P_BlockLinesIterator, then make one or more calls to it
  339. ===================
  340. */
  341. boolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) )
  342. {
  343. int offset;
  344. short *list;
  345. line_t *ld;
  346. if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight)
  347. return true;
  348. offset = y*bmapwidth+x;
  349. offset = *(blockmap+offset);
  350. for ( list = blockmaplump+offset ; *list != -1 ; list++)
  351. {
  352. ld = &lines[*list];
  353. if (ld->validcount == validcount)
  354. continue; // line has already been checked
  355. ld->validcount = validcount;
  356. if ( !func(ld) )
  357. return false;
  358. }
  359. return true; // everything was checked
  360. }
  361. /*
  362. ==================
  363. =
  364. = P_BlockThingsIterator
  365. =
  366. ==================
  367. */
  368. boolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) )
  369. {
  370. mobj_t *mobj;
  371. if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight)
  372. return true;
  373. for (mobj = blocklinks[y*bmapwidth+x] ; mobj ; mobj = mobj->bnext)
  374. if (!func( mobj ) )
  375. return false;
  376. return true;
  377. }
  378. /*
  379. ===============================================================================
  380. INTERCEPT ROUTINES
  381. ===============================================================================
  382. */
  383. intercept_t intercepts[MAXINTERCEPTS], *intercept_p;
  384. divline_t trace;
  385. boolean earlyout;
  386. int ptflags;
  387. /*
  388. ==================
  389. =
  390. = PIT_AddLineIntercepts
  391. =
  392. = Looks for lines in the given block that intercept the given trace
  393. = to add to the intercepts list
  394. = A line is crossed if its endpoints are on opposite sides of the trace
  395. = Returns true if earlyout and a solid line hit
  396. ==================
  397. */
  398. boolean PIT_AddLineIntercepts (line_t *ld)
  399. {
  400. int s1, s2;
  401. fixed_t frac;
  402. divline_t dl;
  403. // avoid precision problems with two routines
  404. if ( trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16
  405. || trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16)
  406. {
  407. s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace);
  408. s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace);
  409. }
  410. else
  411. {
  412. s1 = P_PointOnLineSide (trace.x, trace.y, ld);
  413. s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld);
  414. }
  415. if (s1 == s2)
  416. return true; // line isn't crossed
  417. //
  418. // hit the line
  419. //
  420. P_MakeDivline (ld, &dl);
  421. frac = P_InterceptVector (&trace, &dl);
  422. if (frac < 0)
  423. return true; // behind source
  424. // try to early out the check
  425. if (earlyout && frac < FRACUNIT && !ld->backsector)
  426. return false; // stop checking
  427. intercept_p->frac = frac;
  428. intercept_p->isaline = true;
  429. intercept_p->d.line = ld;
  430. intercept_p++;
  431. return true; // continue
  432. }
  433. /*
  434. ==================
  435. =
  436. = PIT_AddThingIntercepts
  437. =
  438. ==================
  439. */
  440. boolean PIT_AddThingIntercepts (mobj_t *thing)
  441. {
  442. fixed_t x1,y1, x2,y2;
  443. int s1, s2;
  444. boolean tracepositive;
  445. divline_t dl;
  446. fixed_t frac;
  447. tracepositive = (trace.dx ^ trace.dy)>0;
  448. // check a corner to corner crossection for hit
  449. if (tracepositive)
  450. {
  451. x1 = thing->x - thing->radius;
  452. y1 = thing->y + thing->radius;
  453. x2 = thing->x + thing->radius;
  454. y2 = thing->y - thing->radius;
  455. }
  456. else
  457. {
  458. x1 = thing->x - thing->radius;
  459. y1 = thing->y - thing->radius;
  460. x2 = thing->x + thing->radius;
  461. y2 = thing->y + thing->radius;
  462. }
  463. s1 = P_PointOnDivlineSide (x1, y1, &trace);
  464. s2 = P_PointOnDivlineSide (x2, y2, &trace);
  465. if (s1 == s2)
  466. return true; // line isn't crossed
  467. dl.x = x1;
  468. dl.y = y1;
  469. dl.dx = x2-x1;
  470. dl.dy = y2-y1;
  471. frac = P_InterceptVector (&trace, &dl);
  472. if (frac < 0)
  473. return true; // behind source
  474. intercept_p->frac = frac;
  475. intercept_p->isaline = false;
  476. intercept_p->d.thing = thing;
  477. intercept_p++;
  478. return true; // keep going
  479. }
  480. /*
  481. ====================
  482. =
  483. = P_TraverseIntercepts
  484. =
  485. = Returns true if the traverser function returns true for all lines
  486. ====================
  487. */
  488. boolean P_TraverseIntercepts ( traverser_t func, fixed_t maxfrac )
  489. {
  490. int count;
  491. fixed_t dist;
  492. intercept_t *scan, *in;
  493. count = intercept_p - intercepts;
  494. in = 0; // shut up compiler warning
  495. while (count--)
  496. {
  497. dist = MAXINT;
  498. for (scan = intercepts ; scan<intercept_p ; scan++)
  499. if (scan->frac < dist)
  500. {
  501. dist = scan->frac;
  502. in = scan;
  503. }
  504. if (dist > maxfrac)
  505. return true; // checked everything in range
  506. #if 0
  507. { // don't check these yet, ther may be others inserted
  508. in = scan = intercepts;
  509. for ( scan = intercepts ; scan<intercept_p ; scan++)
  510. if (scan->frac > maxfrac)
  511. *in++ = *scan;
  512. intercept_p = in;
  513. return false;
  514. }
  515. #endif
  516. if ( !func (in) )
  517. return false; // don't bother going farther
  518. in->frac = MAXINT;
  519. }
  520. return true; // everything was traversed
  521. }
  522. /*
  523. ==================
  524. =
  525. = P_PathTraverse
  526. =
  527. = Traces a line from x1,y1 to x2,y2, calling the traverser function for each
  528. = Returns true if the traverser function returns true for all lines
  529. ==================
  530. */
  531. boolean P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2,
  532. int flags, boolean (*trav) (intercept_t *))
  533. {
  534. fixed_t xt1,yt1,xt2,yt2;
  535. fixed_t xstep,ystep;
  536. fixed_t partial;
  537. fixed_t xintercept, yintercept;
  538. int mapx, mapy, mapxstep, mapystep;
  539. int count;
  540. earlyout = flags & PT_EARLYOUT;
  541. validcount++;
  542. intercept_p = intercepts;
  543. if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0)
  544. x1 += FRACUNIT; // don't side exactly on a line
  545. if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0)
  546. y1 += FRACUNIT; // don't side exactly on a line
  547. trace.x = x1;
  548. trace.y = y1;
  549. trace.dx = x2 - x1;
  550. trace.dy = y2 - y1;
  551. x1 -= bmaporgx;
  552. y1 -= bmaporgy;
  553. xt1 = x1>>MAPBLOCKSHIFT;
  554. yt1 = y1>>MAPBLOCKSHIFT;
  555. x2 -= bmaporgx;
  556. y2 -= bmaporgy;
  557. xt2 = x2>>MAPBLOCKSHIFT;
  558. yt2 = y2>>MAPBLOCKSHIFT;
  559. if (xt2 > xt1)
  560. {
  561. mapxstep = 1;
  562. partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1));
  563. ystep = FixedDiv (y2-y1,abs(x2-x1));
  564. }
  565. else if (xt2 < xt1)
  566. {
  567. mapxstep = -1;
  568. partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1);
  569. ystep = FixedDiv (y2-y1,abs(x2-x1));
  570. }
  571. else
  572. {
  573. mapxstep = 0;
  574. partial = FRACUNIT;
  575. ystep = 256*FRACUNIT;
  576. }
  577. yintercept = (y1>>MAPBTOFRAC) + FixedMul (partial, ystep);
  578. if (yt2 > yt1)
  579. {
  580. mapystep = 1;
  581. partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1));
  582. xstep = FixedDiv (x2-x1,abs(y2-y1));
  583. }
  584. else if (yt2 < yt1)
  585. {
  586. mapystep = -1;
  587. partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1);
  588. xstep = FixedDiv (x2-x1,abs(y2-y1));
  589. }
  590. else
  591. {
  592. mapystep = 0;
  593. partial = FRACUNIT;
  594. xstep = 256*FRACUNIT;
  595. }
  596. xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep);
  597. //
  598. // step through map blocks
  599. // Count is present to prevent a round off error from skipping the break
  600. mapx = xt1;
  601. mapy = yt1;
  602. for (count = 0 ; count < 64 ; count++)
  603. {
  604. if (flags & PT_ADDLINES)
  605. {
  606. if (!P_BlockLinesIterator (mapx, mapy,PIT_AddLineIntercepts))
  607. return false; // early out
  608. }
  609. if (flags & PT_ADDTHINGS)
  610. {
  611. if (!P_BlockThingsIterator (mapx, mapy,PIT_AddThingIntercepts))
  612. return false; // early out
  613. }
  614. if (mapx == xt2 && mapy == yt2)
  615. break;
  616. if ( (yintercept >> FRACBITS) == mapy)
  617. {
  618. yintercept += ystep;
  619. mapx += mapxstep;
  620. }
  621. else if ( (xintercept >> FRACBITS) == mapx)
  622. {
  623. xintercept += xstep;
  624. mapy += mapystep;
  625. }
  626. }
  627. //
  628. // go through the sorted list
  629. //
  630. return P_TraverseIntercepts ( trav, FRACUNIT );
  631. }