crawler.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // crawler.h
  19. // Project: Nostril (aka Postal)
  20. //
  21. // History:
  22. // 04/19/97 MJR Started.
  23. //
  24. // 04/21/97 MJR/JMI Converted to 3D/Converted to Postal.
  25. //
  26. // 04/21/97 JMI Removed ASSERTs that values were greater than 0 in Move().
  27. // These were to protect against errors when using floor(),
  28. // which we no longer use.
  29. //
  30. // 04/29/97 JMI CanWalk() no longer sets the clip case of the get attribute
  31. // call to include a max height (it just relies on 'no walk'
  32. // now).
  33. //
  34. // 05/29/97 JMI Changed m_pRealm->m_pHeightMap->GetHeight() call to
  35. // m_pRealm->GetTerrainAttributes().
  36. //
  37. // 06/26/97 JMI Now uses m_prealm->Map3Dto2D() in Plot() (was using
  38. // the global version defined in reality.h which is no more).
  39. //
  40. // 07/01/97 JMI Now uses GetHeightAndNoWalk() in place of
  41. // GetTerrainAttributes().
  42. //
  43. // 08/10/97 JMI Now crawls to the 'pushed' point. If the crawler does make
  44. // it all the way to the 'pushed' point, we go back to the
  45. // point we crawled to just previously (which we know is
  46. // valid).
  47. //
  48. // 08/11/97 JMI Changed some casting in comparisons.
  49. //
  50. ////////////////////////////////////////////////////////////////////////////////
  51. #ifndef CRAWLER_H
  52. #define CRAWLER_H
  53. #include "RSPiX.h"
  54. #include "realm.h"
  55. #include "reality.h"
  56. class CCrawler
  57. {
  58. //------------------------------------------------------------------------------
  59. // Types, enums, etc.
  60. //------------------------------------------------------------------------------
  61. public:
  62. typedef struct
  63. {
  64. // These values are supplied by the user
  65. short sX;
  66. short sZ;
  67. short sHard;
  68. short sPushDir;
  69. double dPushMag;
  70. // These values are for internal use only
  71. double dPushX;
  72. double dPushZ;
  73. // double dDragX;
  74. // double dDragZ;
  75. short sHeight; // Stored height used only by IsGood().
  76. } Nub;
  77. //------------------------------------------------------------------------------
  78. // Variables
  79. //------------------------------------------------------------------------------
  80. protected:
  81. short m_sNum;
  82. Nub* m_pnub;
  83. double m_dPushX;
  84. double m_dPushZ;
  85. double m_dPushMaxX;
  86. double m_dPushMaxZ;
  87. public:
  88. CRealm* m_prealm; // Used to access the height map, the effect map,
  89. // the scene, all that good stuff.
  90. short m_sVertTolerance; // Maximum amount crawler can step up.
  91. //------------------------------------------------------------------------------
  92. // Functions
  93. //------------------------------------------------------------------------------
  94. public:
  95. ////////////////////////////////////////////////////////////////////////////////
  96. // Constructor
  97. ////////////////////////////////////////////////////////////////////////////////
  98. CCrawler()
  99. {
  100. m_pnub = 0;
  101. m_sNum = 0;
  102. m_prealm = 0;
  103. }
  104. ////////////////////////////////////////////////////////////////////////////////
  105. // Destructor
  106. ////////////////////////////////////////////////////////////////////////////////
  107. ~CCrawler()
  108. {
  109. m_pnub = 0;
  110. m_sNum = 0;
  111. }
  112. ////////////////////////////////////////////////////////////////////////////////
  113. // Setup crawler
  114. ////////////////////////////////////////////////////////////////////////////////
  115. void Setup(
  116. short sNum, // In: Number of Nub's in array
  117. Nub* pnub, // In: Pointer to array of Nub's
  118. double dPushMaxX, // In: Maximum push in x direction
  119. double dPushMaxZ) // In: Maximum push in z direction
  120. {
  121. // Save info
  122. m_sNum = sNum;
  123. m_pnub = pnub;
  124. m_dPushMaxX = dPushMaxX;
  125. m_dPushMaxZ = dPushMaxZ;
  126. // Precalculate push and drag values based on specified directions and magnitudes
  127. for (short s = 0; s < m_sNum; s++)
  128. {
  129. m_pnub[s].dPushX = rspCos(rspMod360(m_pnub[s].sPushDir)) * m_pnub[s].dPushMag;
  130. m_pnub[s].dPushZ = -rspSin(rspMod360(m_pnub[s].sPushDir)) * m_pnub[s].dPushMag;
  131. }
  132. }
  133. ////////////////////////////////////////////////////////////////////////////////
  134. // Start at position #1 and move along the "path" leading to position #2, the
  135. // goal being to reach position #2. The attributes encountered along the way
  136. // will determine how far we get before stopping.
  137. //
  138. // The returned position is the resulting position. In the best case, it will
  139. // be equal to position #2. Worst case, it will be equal to position #1.
  140. // Otherwise, it will be somewhere between the two.
  141. //
  142. // As long as position #1 is valid, this function will be successfull. This
  143. // should be the case as long as the caller starts out at a valid position and
  144. // never moves without using this function.
  145. //
  146. // If position #1 is NOT valid, this function will PROBABLY FAIL, as indicated
  147. // by a non-zero return value. It is possible that it will manage to find what
  148. // appears to be a valid position; however, not only is this unlikely, but it
  149. // is important to note that it may only APPEAR to be valid -- there is no way
  150. // to know whether it is ACTUALLY valid.
  151. ////////////////////////////////////////////////////////////////////////////////
  152. short Move( // Returns 0 if successfull, non-zero otherwise
  153. double dx1, // In: Position #1 xcoord
  154. double dy1, // In: Position #1 ycoord
  155. double dz1, // In: Position #1 zcoord
  156. double dx2, // In: Position #2 xcoord
  157. double dy2, // In: Position #2 ycoord
  158. double dz2, // In: Position #2 zcoord
  159. double* pdx, // Out: Final position xcoord
  160. double* pdy, // Out: Final position ycoord
  161. double* pdz, // Out: Final position zcoord
  162. short* psTerrainH) // Out: Final terrain height
  163. {
  164. short sResult = 0;
  165. short sx1 = (short)dx1;
  166. short sy1 = (short)dy1;
  167. short sz1 = (short)dz1;
  168. short sx2 = (short)dx2;
  169. short sy2 = (short)dy2;
  170. short sz2 = (short)dz2;
  171. // Check if starting position is valid
  172. if (IsGood(sx1, sy1, sz1, psTerrainH))
  173. {
  174. // Creep as far as possible towards new position
  175. short sx;
  176. short sy;
  177. short sz;
  178. CrawlWhileGood(sx1, sy1, sz1, sx2, sy2, sz2, &sx, &sy, &sz, psTerrainH);
  179. double dx = sx;
  180. double dy = sy;
  181. double dz = sz;
  182. // If we made it to the end point, we should include the fractional value
  183. // lost in converting to integers when we initially assigned into
  184. // sx2 and sz2.
  185. if (sx == sx2)
  186. {
  187. dx = dx2;
  188. }
  189. if (sz == sz2)
  190. {
  191. dz = dz2;
  192. }
  193. if (dy2 < 0.0)
  194. dy += floor(dy2) - dy2;
  195. else
  196. dy += dy2 - floor(dy2);
  197. // Limit amount of push
  198. if (m_dPushX > m_dPushMaxX)
  199. m_dPushX = m_dPushMaxX;
  200. if (m_dPushZ > m_dPushMaxZ)
  201. m_dPushZ = m_dPushMaxZ;
  202. // Calculate "pushed" position and see if it's valid
  203. double dx2 = dx + m_dPushX;
  204. double dy2 = dy;
  205. double dz2 = dz + m_dPushZ;
  206. short sTerrainH = *psTerrainH; // Start with a good value b/c CrawlWhileGood()
  207. // won't update the value unless there's movement.
  208. CrawlWhileGood((short)dx, (short)dy, (short)dz, (short)dx2, (short)dy2, (short)dz2, &sx, &sy, &sz, &sTerrainH);
  209. if ((short)dx2 == sx && (short)dy2 == sy && (short)dz2 == sz)
  210. {
  211. // Use pushed position
  212. *pdx = dx2;
  213. *pdy = dy2;
  214. *pdz = dz2;
  215. *psTerrainH = sTerrainH;
  216. }
  217. else
  218. {
  219. // Pushed position was not valid, so use known-good position instead
  220. *pdx = dx;
  221. *pdy = dy;
  222. *pdz = dz;
  223. }
  224. }
  225. else
  226. {
  227. // Starting position is invalid, so we're pretty much screwed. As a
  228. // last-ditch effort, check if the ending position is miraculously valid.
  229. short sTerrainH;
  230. if (IsGood(sx2, sy2, sz2, &sTerrainH) )
  231. {
  232. // Use the ending position. This might make things worse by moving to
  233. // past some off-limits barrier, but since we don't have any other way
  234. // to get valid again, we might as well try this and hope for the best.
  235. *pdx = dx2;
  236. *pdy = dy2;
  237. *pdz = dz2;
  238. *psTerrainH = sTerrainH;
  239. TRACE("Crawler::Move(): Starting position is invalid, using psuedo-valid ending position!\n");
  240. }
  241. else
  242. {
  243. // Stick with the starting position, which is, unfortunately, invalid
  244. *pdx = dx1;
  245. *pdy = dy1;
  246. *pdz = dz1;
  247. sResult = -1;
  248. TRACE("Crawler::Move(): Starting position is invalid!\n");
  249. }
  250. }
  251. return sResult;
  252. }
  253. protected:
  254. ////////////////////////////////////////////////////////////////////////////////
  255. // Crawl as far as possible along the path from positon #1 to position #2, the
  256. // goal being to reach position #2.
  257. //
  258. // This crawls along one pixel at a time (just like a line-draw algorithm),
  259. // checking each point to see if it's valid. If so, it goes on. If not, it
  260. // returns the previous point, which was valid.
  261. //
  262. // This function assumes position #1 is valid! If this is not the case, all
  263. // bets are off!!!
  264. ////////////////////////////////////////////////////////////////////////////////
  265. void CrawlWhileGood(
  266. short sx1,
  267. short sy1,
  268. short sz1,
  269. short sx2,
  270. short sy2,
  271. short sz2,
  272. short* psOutX,
  273. short* psOutY,
  274. short* psOutZ,
  275. short* psTerrainH) // In/Out: Terrain height.
  276. {
  277. short sMaxTerrainH;
  278. // Clear push values
  279. m_dPushX = 0.0;
  280. m_dPushZ = 0.0;
  281. // Make a copy of the starting position
  282. short sx = sx1;
  283. short sy = sy1;
  284. short sz = sz1;
  285. // Set initial "known good" position
  286. short sGoodX = sx;
  287. short sGoodY = sy;
  288. short sGoodZ = sz;
  289. short sGoodH = *psTerrainH;
  290. // Calculate delta x and delta y
  291. short sdx = sx2 - sx;
  292. short sdy = sy2 - sy;
  293. short sdz = sz2 - sz;
  294. // Make sure there's some movement, otherwise don't bother
  295. if (sdx || sdy || sdz)
  296. {
  297. // Calculate offsets for each delta
  298. short saddx = (sdx >= 0) ? +1 : -1;
  299. short saddy = (sdy >= 0) ? +1 : -1;
  300. short saddz = (sdz >= 0) ? +1 : -1;
  301. // Make all deltas positive
  302. sdx = ABS(sdx);
  303. sdy = ABS(sdy);
  304. sdz = ABS(sdz);
  305. // This uses a Bresenham-like algorithm to move one pixel at a time along the
  306. // line from (sx,sy,sz) to (sx2,sy2,sz2). We basically continue along until
  307. // we hit an invalid position or the ending position, whichever comes first.
  308. short serry;
  309. short serrx;
  310. short serrz;
  311. if ((sdx >= sdy) && (sdx >= sdz))
  312. {
  313. // dx is largest
  314. serry = sdx / 2;
  315. serrz = sdx / 2;
  316. do {
  317. serry += sdy;
  318. if (serry >= sdx)
  319. {
  320. serry -= sdx;
  321. sy += saddy;
  322. }
  323. serrz += sdz;
  324. if (serrz >= sdx)
  325. {
  326. serrz -= sdx;
  327. sz += saddz;
  328. }
  329. sx += saddx;
  330. if (!IsGood(sx, sy, sz, &sMaxTerrainH))
  331. break;
  332. sGoodX = sx;
  333. sGoodY = sy;
  334. sGoodZ = sz;
  335. sGoodH = sMaxTerrainH;
  336. } while (sx != sx2);
  337. }
  338. else if ((sdy >= sdx) && (sdy >= sdz))
  339. {
  340. // dy is largest
  341. serrx = sdy / 2;
  342. serrz = sdy / 2;
  343. do {
  344. serrx += sdx;
  345. if (serrx >= sdy)
  346. {
  347. serrx -= sdy;
  348. sx += saddx;
  349. }
  350. serrz += sdz;
  351. if (serrz >= sdy)
  352. {
  353. serrz -= sdy;
  354. sz += saddz;
  355. }
  356. sy += saddy;
  357. if (!IsGood(sx, sy, sz, &sMaxTerrainH))
  358. break;
  359. sGoodX = sx;
  360. sGoodY = sy;
  361. sGoodZ = sz;
  362. sGoodH = sMaxTerrainH;
  363. } while (sy != sy2);
  364. }
  365. else
  366. {
  367. // dz is largest
  368. serry = sdz / 2;
  369. serrx = sdz / 2;
  370. do {
  371. serry += sdy;
  372. if (serry >= sdz)
  373. {
  374. serry -= sdz;
  375. sy += saddy;
  376. }
  377. serrx += sdx;
  378. if (serrx >= sdz)
  379. {
  380. serrx -= sdz;
  381. sx += saddx;
  382. }
  383. sz += saddz;
  384. if (!IsGood(sx, sy, sz, &sMaxTerrainH))
  385. break;
  386. sGoodX = sx;
  387. sGoodY = sy;
  388. sGoodZ = sz;
  389. sGoodH = sMaxTerrainH;
  390. } while (sz != sz2);
  391. }
  392. }
  393. // Return good position
  394. *psOutX = sGoodX;
  395. *psOutY = sGoodY;
  396. *psOutZ = sGoodZ;
  397. *psTerrainH = sGoodH;
  398. }
  399. ////////////////////////////////////////////////////////////////////////////////
  400. // Determine whether specified position is valid.
  401. //
  402. // Also updates push values as specified by any nubs are not valid.
  403. ////////////////////////////////////////////////////////////////////////////////
  404. bool IsGood(
  405. short sBaseX, // In
  406. short sBaseY, // In
  407. short sBaseZ, // In
  408. short* psMaxTerrainH) // Out: Maximum terrain height from scan.
  409. {
  410. bool bResult = true;
  411. short sMaxTerrainH = -32768; // Default to lowest value.
  412. short sTerrainH;
  413. for (short s = 0; s < m_sNum; s++)
  414. {
  415. short sx = sBaseX + m_pnub[s].sX;
  416. short sz = sBaseZ + m_pnub[s].sZ;
  417. if (CanWalk(sx, sBaseY, sz, &sTerrainH) == true)
  418. {
  419. // Plot((U8)0xfa, sx, sBaseY, sz);
  420. }
  421. else
  422. {
  423. // Plot((U8)0xf9, sx, sBaseY, sz);
  424. if (m_pnub[s].sHard)
  425. bResult = false;
  426. m_dPushX += m_pnub[s].dPushX;
  427. m_dPushZ += m_pnub[s].dPushZ;
  428. }
  429. // If this is a hard nub . . .
  430. if (m_pnub[s].sHard)
  431. {
  432. // If this height is larger than the previous max . . .
  433. if (sTerrainH > sMaxTerrainH)
  434. {
  435. sMaxTerrainH = sTerrainH;
  436. }
  437. }
  438. }
  439. // Store for return.
  440. *psMaxTerrainH = sMaxTerrainH;
  441. return bResult;
  442. }
  443. ////////////////////////////////////////////////////////////////////////////////
  444. // Centralized location for checking attributes and deciding how to interpret
  445. // them.
  446. ////////////////////////////////////////////////////////////////////////////////
  447. bool CanWalk( // Returns true if we can walk there, false otherwise.
  448. short sx, // In: X position on attribute map.
  449. short sy, // In: Y position on attribute map.
  450. short sz, // In: Z position on attribute map.
  451. short* psH) // Out: Terrain height at X/Z.
  452. {
  453. bool bCanWalk;
  454. bool bCannotWalk;
  455. *psH = m_prealm->GetHeightAndNoWalk(sx, sz, &bCannotWalk);
  456. if (bCannotWalk == true // Not walkable
  457. || (*psH - sy > m_sVertTolerance) ) // Terrain higher by m_sVertTolerance.
  458. {
  459. bCanWalk = false;
  460. }
  461. else
  462. {
  463. bCanWalk = true;
  464. }
  465. return bCanWalk;
  466. }
  467. ////////////////////////////////////////////////////////////////////////////////
  468. // Plot a point via the CScene.
  469. ////////////////////////////////////////////////////////////////////////////////
  470. void Plot( // Returns nothing.
  471. U8 u8Color, // Color index.
  472. short sx, // In: X position.
  473. short sy, // In: Y position.
  474. short sz) // In: Z position.
  475. {
  476. // Create a line sprite.
  477. CSpriteLine2d* psl2d = new CSpriteLine2d;
  478. if (psl2d != NULL)
  479. {
  480. m_prealm->Map3Dto2D(
  481. sx,
  482. sy,
  483. sz,
  484. &(psl2d->m_sX2),
  485. &(psl2d->m_sY2) );
  486. psl2d->m_sX2End = psl2d->m_sX2;
  487. psl2d->m_sY2End = psl2d->m_sY2;
  488. psl2d->m_sPriority = sz;
  489. psl2d->m_sLayer = CRealm::GetLayerViaAttrib(m_prealm->GetLayer(sx, sz) );
  490. psl2d->m_u8Color = u8Color;
  491. // Destroy when done.
  492. psl2d->m_sInFlags = CSprite::InDeleteOnRender;
  493. // Put 'er there.
  494. m_prealm->m_scene.UpdateSprite(psl2d);
  495. }
  496. }
  497. };
  498. #endif //CRAWLER_H
  499. ////////////////////////////////////////////////////////////////////////////////
  500. // EOF
  501. ////////////////////////////////////////////////////////////////////////////////