smash.cpp 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694
  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. #include "RSPiX.h"
  19. #include "smash.h"
  20. #include "realm.h"
  21. //#define SMASH_DEBUG
  22. #ifdef SMASH_DEBUG
  23. #include "debugSmash.H"
  24. #endif
  25. ////////////////////////////////////////////////////////////////////////////////
  26. //
  27. // smash.h (grid based edition)
  28. // Project: Postal
  29. //
  30. // History:
  31. // 05/26/97 JRD Started.
  32. //
  33. // 06/04/97 JRD Integrated with Postal for testing using NEW_SMASH project
  34. // setting so dependent files could still work with old smash
  35. //
  36. // 06/20/97 JRD Removed backwards smash compatibility
  37. //
  38. // 07/03/97 JRD Added an incremental tagged search to eliminate redundant
  39. // parts. Implemented a system of ray tracking accross the grid.
  40. //
  41. // 07/05/97 JRD Fixed MANY bugs in the line collision algorithms.
  42. // Handled special
  43. // cases of near vertical lines and vertical clipping.
  44. // Still can't deal with fat smash objects.
  45. //
  46. // 07/07/97 JRD Used a new class to implement fat smash objects without impacting
  47. // performance - CFatSmash.
  48. //
  49. // 07/08/97 JMI Added debug ASSERTs for "bridge's out" condition to help
  50. // BRH us catch which CThings don't remove their smash(es).
  51. // Also, added release mode protection against "bridge's out"
  52. // condition.
  53. // Also, added bits for flags and flag bases.
  54. // Also, moved definition of ~CSmash() into smash.cpp b/c
  55. // of circular dependency (a Philips chain of command of
  56. // sorts) between smashatorium and realm.
  57. // Also, moved CSmash b/c Bill thinks it's better to keep
  58. // the destructor with the constructor or ease of findage.
  59. //
  60. // 07/10/97 JRD Finally copleted and debugged ful support for Fat Smash
  61. // objects. The main criteria in determining what size is
  62. // "fat", is that "fat" objects shouldn't move very often.
  63. // NOTE that the n2 collision space in the new smash is
  64. // equivalent to NINE smash tiles, so if the tile size is
  65. // 72, the collision areas will be 216 x 216! So be careful!
  66. //
  67. // 08/09/97 JMI CSmashatorium::Update() was checking the realm's
  68. // m_flags.bEditPlay flag (which signifies that we're playing
  69. // a game from the editor) instead of the m_flags.bEditing
  70. // flag which indicates we're editting. Fixed.
  71. //
  72. // 08/18/97 BRH Fixed typo that used m_pSmasher instead of pSmasher
  73. // that was passed in. Fixed CollideCyl that was checking
  74. // X and Y collisions to checking X and Z collisions.
  75. //
  76. // 08/31/97 JMI Added some ASSERTs to foil SmartHeap's fancy smancy bounds
  77. // check padding areas and allow us to debug 'read' bounds
  78. // errors in debug mode.
  79. //
  80. // 09/02/97 JMI Added some more ASSERTs for debug mode bounds checking.
  81. //
  82. ////////////////////////////////////////////////////////////////////////////////
  83. ////////////////////////////////////////////////////////////////////////////////
  84. // NOTE: CURRENT USER LEVEL CHANGE FROM OLD SMASHATORIUM: You must pass
  85. // four parameters to allocate a CSmashatorium (there is no default anymore.)
  86. // These parameters are the world X and Z size, and a grid size larger than
  87. // any smash region.
  88. ////////////////////////////////////////////////////////////////////////////////
  89. // The new smashatorium attempts to partition lists of objects by location
  90. // Currently, each grid location only maintains a single hodge podge list as
  91. // the old Smash. This may be enhanced (memory permitting) to split into
  92. // multiple lists by type, or into multiple smashatoriums fo different grid
  93. // sizes so static objects could be contained in a fine grid.
  94. ////////////////////////////////////////////////////////////////////////////////
  95. //
  96. // The current model demands that objects in the smashatorium exist in no more
  97. // than four grid locations simultaneously. Large smash objects used to detect
  98. // objects-within-a-distance are hooked as a special type of smashee and use
  99. // different collision routines. Similarly, line collisions need higher level
  100. // logic to march through the grid. In short, it is a completely new
  101. // Smashatorium masquerading through the old API.
  102. //
  103. ////////////////////////////////////////////////////////////////////////////////
  104. //
  105. // The current Smashatorium "HIERARCHY OF OBJECTS" :
  106. //
  107. // CSmashLink -> A 128-bit struct which is the manipulation block for the grid.
  108. // It currently points back to it's CSmash parent and it's old grid
  109. // location. For efficiency, we could redundantly store the
  110. // smash bits here.
  111. //
  112. // CSmash -> One of more of these is held by actual game objects. It contains
  113. // a pointer back to the thing parent, a spherical collision region,
  114. // the smash bits, and four SmashLinks to track the corners of the
  115. // objects in the Smashatorium Grid.
  116. //
  117. // CSmashatoriumList -> manages each grid's linked list. The grid is a 2d array
  118. // of these nodes.
  119. //
  120. // CSmashatorium -> Hold the grid and world clipping info. Handles all the user
  121. // functions. Holds the state for the sequential checking state.
  122. //
  123. ////////////////////////////////////////////////////////////////////////////////
  124. CSmash::CSmash()
  125. {
  126. Erase();
  127. // Link up the links to their parent:
  128. m_link1.m_pParent = m_link2.m_pParent = m_link3.m_pParent = m_link4.m_pParent = this;
  129. }
  130. CSmash::~CSmash()
  131. {
  132. // Make sure we've been removed.
  133. ASSERT(m_link1.m_pLast == NULL);
  134. ASSERT(m_sInGrid == FALSE);
  135. // As a back up, if not removed . . .
  136. if (m_link1.m_pLast != NULL)
  137. {
  138. if (m_pThing != NULL)
  139. {
  140. // Use the pthing to get to the realm and finally the smashatorium that
  141. // we are in.
  142. // Remove this smash. This is for safety for uncaught release mode
  143. // bugs.
  144. m_pThing->m_pRealm->m_smashatorium.Remove(this);
  145. }
  146. }
  147. delete m_pFat; // Safe if NULL
  148. Erase();
  149. }
  150. ////////////////////////////////////////////////////////////////////////////////
  151. // CSmashatorium::CollideCyl - check for special collision with sphere case
  152. ////////////////////////////////////////////////////////////////////////////////
  153. // This is a VERY special case scenario - currently, if the Smashee is a CDude,
  154. // and IF his sphere collides, we THEN need to check if his "cylinder" collides.
  155. // In this case, his cylinder is fixed at half his sphere radius.
  156. ////////////////////////////////////////////////////////////////////////////////
  157. short CSmashatorium::CollideCyl(CSmash* pSmashee,RSphere* pSphere) // sphere of Smasher
  158. {
  159. if (!pSmashee->m_pThing) return SUCCESS; // not a dude
  160. if (pSmashee->m_pThing->GetClassID() != CThing::CDudeID) return SUCCESS; // not a dude
  161. // it's a dude ! see if the cylinder collides:
  162. long lCylR = pSmashee->m_sphere.sphere.lRadius / 3; // go with half the sphere radius
  163. // NOTE that if it's a DUDE colliding with a DUDE, only one uses a cylinder:
  164. if (ABS2(pSmashee->m_sphere.sphere.X - pSphere->X,
  165. pSmashee->m_sphere.sphere.Z - pSphere->Z) >
  166. SQR(lCylR + pSphere->lRadius) ) return FAILURE; // A MISS!
  167. return SUCCESS; // a hit!
  168. }
  169. ////////////////////////////////////////////////////////////////////////////////
  170. // CSmashatorium::CollideCyl - check for special collision with line case
  171. ////////////////////////////////////////////////////////////////////////////////
  172. // This is a VERY special case scenario - currently, if the Smashee is a CDude,
  173. // and IF his sphere collides, we THEN need to check if his "cylinder" collides.
  174. // In this case, his cylinder is fixed at half his sphere radius.
  175. ////////////////////////////////////////////////////////////////////////////////
  176. short CSmashatorium::CollideCyl(CSmash* pSmashee,R3DLine* pLine) // sphere of Smasher
  177. {
  178. if (!pSmashee->m_pThing) return SUCCESS; // not a dude
  179. if (pSmashee->m_pThing->GetClassID() != CThing::CDudeID) return SUCCESS; // not a dude
  180. // it's a dude ! see if the cylinder collides:
  181. long lCylR = pSmashee->m_sphere.sphere.lRadius / 3; // go with half the sphere radius
  182. long lOldR = pSmashee->m_sphere.sphere.lRadius;
  183. pSmashee->m_sphere.sphere.lRadius = lCylR; // shrink it:
  184. short sCollide = pSmashee->m_sphere.Collide(pLine);
  185. pSmashee->m_sphere.sphere.lRadius = lOldR; // shrink it:
  186. if (sCollide == COLLISION) return SUCCESS; // a hit!
  187. return FAILURE; // a miss!
  188. }
  189. ////////////////////////////////////////////////////////////////////////////////
  190. // CSmashatorium::Alloc - create a grid of smash lists:
  191. ////////////////////////////////////////////////////////////////////////////////
  192. short CSmashatorium::Alloc(short sWorldW,short sWorldH,short sTileW,short sTileH)
  193. {
  194. //-------------------------------------------------------------
  195. ASSERT(!m_psAccessX); // previous grid?
  196. ASSERT(!m_psAccessY);
  197. ASSERT(!m_ppslAccessY);
  198. ASSERT(!m_pGrid);
  199. ASSERT(sWorldW > 0); // bad input?
  200. ASSERT(sWorldH > 0);
  201. ASSERT(sTileW > 0);
  202. ASSERT(sTileH > 0);
  203. //-------------------------------------------------------------
  204. m_sWorldW = sWorldW;
  205. m_sWorldH = sWorldH;
  206. m_sClipW = m_sWorldW + (sTileW << 1); // Add a one tile border
  207. m_sClipH = m_sWorldH + (sTileH << 1);
  208. m_sTileW = sTileW; // For debugging & clipping
  209. m_sTileH = sTileH;
  210. m_sGridW = (m_sClipW + sTileW - 1) / sTileW;
  211. m_sGridH = (m_sClipH + sTileH - 1) / sTileH;
  212. // For current logic convenience, do not allow partial tiles to exist:
  213. m_sClipW = long(m_sGridW) * sTileW;
  214. m_sClipH = long(m_sGridH) * sTileH;
  215. // Note that we must add 2 tile lengths to each access line:
  216. m_pGrid = new CSmashatoriumList[long(m_sGridW) * m_sGridH];
  217. m_psAccessX = (short*) calloc(sizeof(short),m_sClipW);
  218. m_psAccessY = (short*) calloc(sizeof(short),m_sClipH);
  219. m_ppslAccessY = (CSmashatoriumList**) calloc(sizeof (CSmashatoriumList*),m_sClipH);
  220. if (!m_psAccessX || !m_psAccessX || !m_ppslAccessY || !m_pGrid)
  221. {
  222. TRACE("CSmashatorium::Ran out of memory!\n");
  223. Destroy();
  224. Erase();
  225. return FAILURE;
  226. }
  227. // THE OFFICIAL RANGE HERE is from
  228. // -m_sTileW to (m_sWorldW + m_sTileW)
  229. // FULL CLIP is from 0 to (m_sWorldW - 1)
  230. //
  231. m_psClipX = m_psAccessX + m_sTileW; // Offset values
  232. m_psClipY = m_psAccessY + m_sTileH; // Offset values
  233. m_ppslClipY = m_ppslAccessY + m_sTileH; // Offset values
  234. // Populate the access tables....
  235. short i,j,p,g;
  236. for (g=0, p = 0,i=0 ; i < m_sClipW; i += sTileW, g++)
  237. {
  238. for (j=0; j < sTileW; j++, p++)
  239. {
  240. m_psAccessX[p] = g;
  241. }
  242. }
  243. for (g=0, p = 0,i=0 ; i < m_sClipH; i += sTileH, g++)
  244. {
  245. for (j=0; j < sTileH; j++, p++)
  246. {
  247. m_psAccessY[p] = g;
  248. // Remember you are in pointer arithmetic mode!!!!
  249. m_ppslAccessY[p] = m_pGrid + long(m_sGridW) * g;
  250. }
  251. }
  252. m_sNumInSmash = m_sMaxNumInSmash = 0;
  253. return SUCCESS;
  254. }
  255. ////////////////////////////////////////////////////////////////////////////////
  256. //
  257. // Reset -
  258. //
  259. // Reset does NOT DEALLOCATE any portion of the Smashatorium.
  260. // It is just a short cut to reset each of the grid's
  261. // SmashLists. But Each Smash must reset it's own Links!
  262. //
  263. ////////////////////////////////////////////////////////////////////////////////
  264. void CSmashatorium::Reset()
  265. {
  266. //----------------------------------------------------------------
  267. // Reset any search in progress: STEAL this code for a real func
  268. m_pCurrentSmashee = NULL;
  269. m_pSmasher = NULL;
  270. m_sCurrentListX = m_sCurrentListY = m_sSearchW = m_sSearchH = 0;
  271. //----------------------------------------------------------------
  272. // Go down the list of CSmashatoriumList's:
  273. long lCur;
  274. for (lCur = 0; lCur < long(m_sGridW) * m_sGridH; lCur++)
  275. {
  276. CSmashatoriumList *pCur = m_pGrid + lCur;
  277. pCur->m_sNum = 0;
  278. pCur->m_slHead.Erase();
  279. pCur->m_slTail.Erase();
  280. }
  281. m_sNumInSmash = 0;
  282. }
  283. ////////////////////////////////////////////////////////////////////////////////
  284. //
  285. // QuickCheckReset
  286. //
  287. // Reset QuickCheckNext() using the specified parameters.
  288. // Begin a multicall collision search based on a smashee
  289. // If the Smashee is in the Smashatorium, it must be small enough to fit.
  290. //
  291. ////////////////////////////////////////////////////////////////////////////////
  292. void CSmashatorium::QuickCheckReset(// Returns true if collision detected, false otherwise
  293. CSmash* pSmasher, // In: CSmash to check
  294. CSmash::Bits include, // In: Bits that must be 1 to collide with a given CSmash
  295. CSmash::Bits dontcare, // In: Bits that you don't care about
  296. CSmash::Bits exclude) // In: Bits that must be 0 to collide with a given CSmash
  297. {
  298. ASSERT(pSmasher);
  299. //---------------------------
  300. m_pSmasher = pSmasher;
  301. m_include = include;
  302. m_dontcare = dontcare;
  303. m_exclude = exclude;
  304. //--------------------------- preset size and position: ---------
  305. // (1) cast into a square:
  306. //---------------------------------------------------------------
  307. RSphere* pSphere = &(m_pSmasher->m_sphere.sphere);
  308. long lR = pSphere->lRadius;
  309. // Find upper left & lower right position:
  310. long lX,lY,lX2,lY2;
  311. lX = pSphere->X - lR;
  312. lY = pSphere->Z - lR;
  313. m_lCurrentSearchCode++; // prepare for a new searching code
  314. // Now do something different for a smashee that's in the 'torium
  315. // and one that's not...
  316. if (pSmasher->m_sInGrid) // we KNOW it's fully clipped and of legal size...
  317. {
  318. if ( (lX <= -m_sTileW) || (lY < -m_sTileH) ||
  319. (lX >= m_sWorldW) || (lY >= m_sWorldH) )
  320. {
  321. // We have FULL CLIP OUT!
  322. m_pSmasher = NULL; // this search has ended!
  323. return;
  324. }
  325. // We know to do 2 x 2:
  326. m_pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
  327. m_sCurrentListX = m_sCurrentListY = 0;
  328. m_sSearchW = m_sSearchH = 2;
  329. m_pCurrentSmashee = NULL; // Pending first request
  330. if (m_lCurrentSearchCode < 0) // unfortunate wrapping around...
  331. {
  332. ASSERT(0); // Need to "detag" the smashatorium - detag could be a function
  333. }
  334. return; // Done!
  335. }
  336. // Handle the case of a monstrosity!
  337. lR += lR; // lR is a diameter now!
  338. lX2 = lX + lR;
  339. lY2 = lY + lR;
  340. //==========================================
  341. // Do tight clipping:
  342. //==========================================
  343. if (lX < 0) lX = 0;
  344. if (lY < 0) lY = 0;
  345. if (lX2 >= m_sWorldW) lX2 = m_sWorldW - 1;
  346. if (lY2 >= m_sWorldH) lY2 = m_sWorldH - 1;
  347. if ( (lX2 <= lX) || (lY2 <= lY) )
  348. {
  349. // Fully clipped out!
  350. m_pSmasher = NULL; // this search has ended!
  351. return;
  352. }
  353. // Set up the search parameters:
  354. m_pCurrentSmashee = NULL; // Pending first request
  355. m_pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
  356. m_sCurrentListX = 0; // m_psClipX[lX]; // CURRENTLY, these are used merely as iterators
  357. m_sCurrentListY = 0; //m_psClipY[lY];
  358. m_sSearchW = 1 + m_psClipX[lX2] - m_psClipX[lX];
  359. m_sSearchH = 1 + m_psClipY[lY2] - m_psClipY[lY];
  360. }
  361. ////////////////////////////////////////////////////////////////////////////////
  362. //
  363. //
  364. //
  365. //
  366. ////////////////////////////////////////////////////////////////////////////////
  367. ////////////////////////////////////////////////////////////////////////////////
  368. //
  369. //
  370. //
  371. //
  372. ////////////////////////////////////////////////////////////////////////////////
  373. ////////////////////////////////////////////////////////////////////////////////
  374. //
  375. //
  376. //
  377. //
  378. ////////////////////////////////////////////////////////////////////////////////
  379. //===========================================================================
  380. // Currently stubs for now...
  381. //===========================================================================
  382. // Reset QuickCheckNext() using the specified paramters.
  383. void CSmashatorium::QuickCheckReset( // Returns true if collision detected, false otherwise
  384. CSmash::Bits include, // In: Bits, of which, one must be set to collide with a given CSmash
  385. CSmash::Bits dontcare, // In: Bits that you don't care about
  386. CSmash::Bits exclude) // In: Bits that must be 0 to collide with a given CSmash
  387. {
  388. TRACE("NEVER USED!\n");
  389. ASSERT(0);
  390. }
  391. // Returns the next object being collided with, using the parameters that were
  392. // passed to QuickCheckReset(). This will return all the objects being collided
  393. // with in an arbitrary order. Other functions will someday return the objects
  394. // in some particular order. The function will return false when there are no
  395. // more colisions.
  396. bool CSmashatorium::QuickCheckNext( // Returns true if collision detected, false otherwise
  397. R3DLine* pline, // In: Line segment to collide against.
  398. CSmash** pSmashee, // Out: Thing being smashed into if any (unless 0)
  399. CSmash* pSmasher) // Out: Smash that should be excluded from search.
  400. {
  401. ASSERT(0);
  402. return false; // NEVER USED ANYMORE!
  403. }
  404. ////////////////////////////////////////////////////////////////////////////////
  405. //
  406. // QuickCheckNext - Smasher against smashee
  407. //
  408. // Returns true if collision detected, false otherwise
  409. // Out: The Next Thing being smashed into if any (unless 0)
  410. // *** ppSmashee is ONLY for output!
  411. //
  412. // NOTE: YOU Must set up this call using QuickCheckReset
  413. // Returns NULL AND resets the QuickSearch if no more to find:
  414. //
  415. ////////////////////////////////////////////////////////////////////////////////
  416. CSmash *CSmashatorium::GetNextSmash()
  417. {
  418. short sNextList = FALSE;
  419. CSmash *pReturn = NULL;
  420. short sSearching = TRUE;
  421. while (sSearching)
  422. {
  423. if (!m_pCurrentSmashee) // first in list:
  424. {
  425. m_pCurrentSmashee = &m_pCurrentList->m_slHead; // Start it off
  426. }
  427. if (m_pCurrentSmashee->m_pNext == &m_pCurrentList->m_slTail)
  428. {
  429. sNextList = TRUE;
  430. }
  431. else // We've got one!
  432. {
  433. m_pCurrentSmashee = m_pCurrentSmashee->m_pNext;
  434. pReturn = m_pCurrentSmashee->m_pParent;
  435. sSearching = FALSE;
  436. }
  437. if (sNextList)
  438. {
  439. m_pCurrentSmashee = NULL;
  440. sNextList = FALSE;
  441. // Find the next list
  442. m_sCurrentListX++;
  443. m_pCurrentList++;
  444. if (m_sCurrentListX >= m_sSearchW)
  445. {
  446. m_sCurrentListX = 0;
  447. m_sCurrentListY++;
  448. m_pCurrentList += m_sGridW - m_sSearchW;
  449. if (m_sCurrentListY >= m_sSearchH) // You're DONE
  450. {
  451. m_sCurrentListX = m_sCurrentListY = m_sSearchW =
  452. m_sSearchH = 0;
  453. m_pCurrentList = NULL;
  454. pReturn = NULL;
  455. sSearching = FALSE;
  456. m_pSmasher = NULL; // The real deactivation
  457. }
  458. }
  459. }
  460. }
  461. return pReturn;
  462. }
  463. bool CSmashatorium::QuickCheckNext(CSmash** ppSmashee)
  464. {
  465. // First, handle the easy case of a guaranteed 2x2 object:
  466. CSmash *pSmashee = NULL;
  467. // 1) Is a search in progress?
  468. if (!m_pSmasher) return false; // reset at end of search
  469. // 2) The QuickCheckReset parameters can tell the size:
  470. // Look for a collision with our requirements
  471. pSmashee = GetNextSmash();
  472. while (pSmashee) // compare this with what we want
  473. {
  474. if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
  475. { // Avoid redundancy
  476. pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;
  477. if (!(pSmashee->m_bits & m_exclude) && ((pSmashee->m_bits & ~m_dontcare)
  478. & m_include) && pSmashee != m_pSmasher)
  479. {
  480. if (pSmashee->m_sphere.Collide(&m_pSmasher->m_sphere) == COLLISION)
  481. {
  482. if (CollideCyl(pSmashee,&m_pSmasher->m_sphere.sphere) == SUCCESS)
  483. {
  484. *ppSmashee = m_pCurrentSmashee->m_pParent;
  485. return true;
  486. }
  487. }
  488. }
  489. }
  490. pSmashee = GetNextSmash();
  491. }
  492. return false; // USED BY FIRE
  493. }
  494. ////////////////////////////////////////////////////////////////////////////////
  495. //
  496. // QuickCheck - Smasher against smashee
  497. //
  498. // Returns true if collision detected, false otherwise
  499. // Sets *ppSmashee to the thing collided with.
  500. //
  501. // This function is like QuickCheckNext, except it just returns the
  502. // FIRST thing it finds that is a hit. (Arbitrary)
  503. //
  504. // Returns NULL if nothing is colliding
  505. //
  506. ////////////////////////////////////////////////////////////////////////////////
  507. bool CSmashatorium::QuickCheck( // Returns true if collision detected, false otherwise
  508. CSmash* pSmasher, // In: CSmash to check
  509. CSmash::Bits include, // In: Bits that must be 1 to collide with a given CSmash
  510. CSmash::Bits dontcare, // In: Bits that you don't care about
  511. CSmash::Bits exclude, // In: Bits that must be 0 to collide with a given CSmash
  512. CSmash** ppSmashee) // Out: Thing being smashed into if any (unless 0)
  513. {
  514. // This routine combines the logic of QuickCheckNext and QuickCheckReset into one!
  515. ASSERT(pSmasher);
  516. ASSERT(ppSmashee);
  517. m_lCurrentSearchCode++; // prepare for a new searching code
  518. if (m_lCurrentSearchCode < 0) // unfortunate wrapping around...
  519. {
  520. ASSERT(0); // Need to "detag" the smashatorium - detag could be a function
  521. }
  522. // Determine the grid expanse of the Smasher's radius:
  523. // (1) cast into a square:
  524. //---------------------------------------------------------------
  525. RSphere* pSphere = &(pSmasher->m_sphere.sphere);
  526. long lR = pSphere->lRadius;
  527. // Find upper left & lower right position:
  528. long lX,lY,lX2,lY2;
  529. lX = pSphere->X - lR;
  530. lY = pSphere->Z - lR;
  531. short sW=0,sH=0,i,j;
  532. CSmashatoriumList* pCurrentList = NULL;
  533. // Now do something different for a smashee that's in the 'torium
  534. // and one that's not...
  535. if (pSmasher->m_sInGrid) // we KNOW it's fully clipped and of legal size...
  536. {
  537. if ( (lX <= -m_sTileW) || (lY < -m_sTileH) ||
  538. (lX >= m_sWorldW) || (lY >= m_sWorldH) )
  539. {
  540. // We have FULL CLIP OUT!
  541. *ppSmashee = NULL;
  542. return false; // this search has ended!
  543. }
  544. // We know to do 2 x 2:
  545. pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
  546. sW = sH = 2;
  547. }
  548. else
  549. {
  550. // Handle the case of a monstrosity!
  551. lR += lR; // lR is a diameter now!
  552. lX2 = lX + lR;
  553. lY2 = lY + lR;
  554. //==========================================
  555. // Do tight clipping:
  556. //==========================================
  557. if (lX < 0) lX = 0;
  558. if (lY < 0) lY = 0;
  559. if (lX2 >= m_sWorldW) lX2 = m_sWorldW - 1;
  560. if (lY2 >= m_sWorldH) lY2 = m_sWorldH - 1;
  561. if ( (lX2 <= lX) || (lY2 <= lY) )
  562. {
  563. // Fully clipped out!
  564. *ppSmashee = NULL;
  565. return false; // this search has ended!
  566. }
  567. // Set up the search parameters:
  568. pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
  569. sW = 1 + m_psClipX[lX2] - m_psClipX[lX];
  570. sH = 1 + m_psClipY[lY2] - m_psClipY[lY];
  571. }
  572. // Do the search
  573. for (j=0; j < sH; j++, pCurrentList += m_sGridW - sW)
  574. {
  575. for (i=0; i < sW; i++,pCurrentList++)
  576. {
  577. if (pCurrentList->m_sNum)
  578. {
  579. CSmashLink* pLink = pCurrentList->m_slHead.m_pNext;
  580. CSmash* pSmashee;
  581. while (pLink != &pCurrentList->m_slTail)
  582. {
  583. pSmashee = pLink->m_pParent;
  584. // Test for the collision!
  585. if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
  586. { // Avoid redundancy
  587. pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;
  588. if (!(pSmashee->m_bits & exclude) && ((pSmashee->m_bits & ~dontcare)
  589. & include) && pSmashee != pSmasher)
  590. {
  591. if (pSmashee->m_sphere.Collide(&pSmasher->m_sphere) == COLLISION)
  592. {
  593. if (CollideCyl(pSmashee,&pSmasher->m_sphere.sphere) == SUCCESS)
  594. {
  595. *ppSmashee = pSmashee;
  596. return true;
  597. }
  598. }
  599. }
  600. }
  601. pLink = pLink->m_pNext;
  602. }
  603. }
  604. }
  605. }
  606. return false; // Used by missile
  607. }
  608. ////////////////////////////////////////////////////////////////////////////////
  609. //
  610. // QuickCheckClosest - Smasher against smashee
  611. //
  612. // Returns true if collision detected, false otherwise
  613. // Sets *ppSmashee to the thing collided with.
  614. //
  615. // This function is like QuickCheckNext, except it just returns the
  616. // CLOSEST thing it finds that is a hit. (Front or back)
  617. //
  618. // Returns NULL if nothing is colliding
  619. //
  620. ////////////////////////////////////////////////////////////////////////////////
  621. bool CSmashatorium::QuickCheckClosest( // Returns true if collision detected, false otherwise
  622. CSmash* pSmasher, // In: CSmash to check
  623. CSmash::Bits include, // In: Bits that must be 1 to collide with a given CSmash
  624. CSmash::Bits dontcare, // In: Bits that you don't care about
  625. CSmash::Bits exclude, // In: Bits that must be 0 to collide with a given CSmash
  626. CSmash** ppSmashee)
  627. {
  628. // This routine combines the logic of QuickCheckNext and QuickCheckReset into one!
  629. ASSERT(pSmasher);
  630. ASSERT(ppSmashee);
  631. m_lCurrentSearchCode++; // prepare for a new searching code
  632. if (m_lCurrentSearchCode < 0) // unfortunate wrapping around...
  633. {
  634. ASSERT(0); // Need to "detag" the smashatorium - detag could be a function
  635. }
  636. // Determine the grid expanse of the Smasher's radius:
  637. // (1) cast into a square:
  638. //---------------------------------------------------------------
  639. RSphere* pSphere = &(pSmasher->m_sphere.sphere);
  640. long lR = pSphere->lRadius;
  641. long lClosestDist2 = 2000000000; // a large number
  642. long lCurDist2;
  643. long lSmasherX = pSphere->X;
  644. long lSmasherY = pSphere->Z;
  645. CSmash* pClosestSmash = NULL;
  646. // Find upper left & lower right position:
  647. long lX,lY,lX2,lY2;
  648. lX = lSmasherX - lR;
  649. lY = lSmasherY - lR;
  650. short sW=0,sH=0,i,j;
  651. CSmashatoriumList* pCurrentList = NULL;
  652. // Now do something different for a smashee that's in the 'torium
  653. // and one that's not...
  654. if (pSmasher->m_sInGrid) // we KNOW it's fully clipped and of legal size...
  655. {
  656. if ( (lX <= -m_sTileW) || (lY < -m_sTileH) ||
  657. (lX >= m_sWorldW) || (lY >= m_sWorldH) )
  658. {
  659. // We have FULL CLIP OUT!
  660. *ppSmashee = NULL;
  661. return false; // this search has ended!
  662. }
  663. // We know to do 2 x 2:
  664. pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
  665. sW = sH = 2;
  666. }
  667. else
  668. {
  669. // Handle the case of a monstrosity!
  670. lR += lR; // lR is a diameter now!
  671. lX2 = lX + lR;
  672. lY2 = lY + lR;
  673. //==========================================
  674. // Do tight clipping:
  675. //==========================================
  676. if (lX < 0) lX = 0;
  677. if (lY < 0) lY = 0;
  678. if (lX2 >= m_sWorldW) lX2 = m_sWorldW - 1;
  679. if (lY2 >= m_sWorldH) lY2 = m_sWorldH - 1;
  680. if ( (lX2 <= lX) || (lY2 <= lY) )
  681. {
  682. // Fully clipped out!
  683. *ppSmashee = NULL;
  684. return false; // this search has ended!
  685. }
  686. // Set up the search parameters:
  687. pCurrentList = m_ppslClipY[lY] + m_psClipX[lX];
  688. sW = 1 + m_psClipX[lX2] - m_psClipX[lX];
  689. sH = 1 + m_psClipY[lY2] - m_psClipY[lY];
  690. }
  691. // Do the search
  692. for (j=0; j < sH; j++, pCurrentList += m_sGridW - sW)
  693. {
  694. for (i=0; i < sW; i++,pCurrentList++)
  695. {
  696. if (pCurrentList->m_sNum)
  697. {
  698. CSmashLink* pLink = pCurrentList->m_slHead.m_pNext;
  699. CSmash* pSmashee;
  700. while (pLink != &pCurrentList->m_slTail)
  701. {
  702. pSmashee = pLink->m_pParent;
  703. // Test for the collision!
  704. if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
  705. { // Avoid redundancy
  706. pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;
  707. // Test for the colision!
  708. if (!(pSmashee->m_bits & exclude) && ((pSmashee->m_bits & ~dontcare)
  709. & include) && pSmashee != pSmasher)
  710. {
  711. if (pSmashee->m_sphere.Collide(&pSmasher->m_sphere) == COLLISION)
  712. {
  713. if (CollideCyl(pSmashee,&pSmasher->m_sphere.sphere) == SUCCESS)
  714. {
  715. // Is this hit the closest?
  716. lCurDist2 = SQR(lSmasherX - pSmashee->m_sphere.sphere.X) +
  717. SQR(lSmasherY - pSmashee->m_sphere.sphere.Z);
  718. if (lCurDist2 < lClosestDist2)
  719. {
  720. pClosestSmash = pSmashee;
  721. lClosestDist2 = lCurDist2;
  722. }
  723. }
  724. }
  725. }
  726. }
  727. pLink = pLink->m_pNext;
  728. }
  729. }
  730. }
  731. }
  732. *ppSmashee = pClosestSmash;
  733. if (pClosestSmash) return true;
  734. return false; // Used by lock-on missile
  735. }
  736. ////////////////////////////////////////////////////////////////////////////////
  737. //
  738. // QuickCheck - collide a line with the smash
  739. //
  740. // Determine whether specified R3DLine is colliding with anything, and
  741. // if so, (optionally) return the first thing it's colliding with.
  742. //
  743. ////////////////////////////////////////////////////////////////////////////////
  744. bool CSmashatorium::QuickCheck(// Returns true if collision detected, false otherwise
  745. R3DLine* pLine, // In: Line to check
  746. CSmash::Bits include, // In: Bits that must be 1 to collide with a given CSmash
  747. CSmash::Bits dontcare, // In: Bits that you don't care about
  748. CSmash::Bits exclude, // In: Bits that must be 0 to collide with a given CSmash
  749. CSmash** ppSmashee, // Out: Thing being smashed into if any (unless 0)
  750. CSmash* pSmasher) // Out: Smash that should be excluded from search.
  751. {
  752. ASSERT(0);
  753. return false; // NEVER USED ANYMORE!
  754. }
  755. // I just use this for anything I need to debug at the moment.
  756. // Currently, it dumps out the grid amount numbers.
  757. void CSmashatorium::Debug()
  758. {
  759. // PURE DEBUGGING HELL!
  760. FILE* fp = fopen("smashout.txt","w");
  761. fprintf(fp,"GridW = %hd\nGridH = %hd\n",m_sGridW,m_sGridH);
  762. fprintf(fp,"# in smash = %hd; # in each list:\n\n",m_sNumInSmash);
  763. short di,dj;
  764. // Try direct grid access:
  765. short p = 0;
  766. for (dj = 0; dj < m_sGridH; dj++)
  767. {
  768. for (di = 0; di < m_sGridW; di++)
  769. {
  770. fprintf(fp,"%2hd ",m_pGrid[p++].m_sNum);
  771. }
  772. fprintf(fp,"\n");
  773. }
  774. fprintf(fp,"\n\n***********\n");
  775. // Try Indirect Access
  776. for (dj = 0; dj < m_sWorldH; dj+=m_sTileH)
  777. {
  778. for (di = 0; di < m_sWorldW;di+=m_sTileW)
  779. {
  780. fprintf(fp,"%2hd ",(m_ppslClipY[dj] + m_psClipX[di])->m_sNum);
  781. }
  782. fprintf(fp,"\n");
  783. }
  784. //
  785. fclose(fp);
  786. }
  787. ////////////////////////////////////////////////////////////////////////////////
  788. //
  789. // QuickCheckClosest - collide a line with the smash, excluding myself
  790. // Find the closest hit to the FIRST point in the line!
  791. //
  792. // Determine whether specified R3DLine is colliding with anything, and
  793. // if so, (optionally) return the closet thing (to the CSmash) it's colliding with.
  794. // Additionally, specify a CSmash to EXCLUDE from the search.
  795. //
  796. // Note that it is actually a line segment
  797. //
  798. ////////////////////////////////////////////////////////////////////////////////
  799. bool CSmashatorium::QuickCheckClosest( // Returns true if collision detected, false otherwise
  800. R3DLine* pline, // In: Line to check
  801. CSmash::Bits include, // In: Bits that must be 1 to collide with a given CSmash
  802. CSmash::Bits dontcare, // In: Bits that you don't care about
  803. CSmash::Bits exclude, // In: Bits that must be 0 to collide with a given CSmash
  804. CSmash** ppSmashee, // Out: Thing being smashed into if any.
  805. CSmash* pSmasher) // Out: Smash that should be excluded from search.
  806. {
  807. // This is a tricky line, because far from the standard 8-connect line, this must include
  808. // ALL regions the line even glances through! And cliping is a nightmare!
  809. // This routine combines the logic of QuickCheckNext and QuickCheckReset into one!
  810. // pSmasher can be NULL!
  811. ASSERT(ppSmashee);
  812. // Current Implementation:
  813. // 1) NO CLIPPING YET!
  814. // 2) NO vertical line case:
  815. // Set up the line, clipping where needed: (We only look at 2d)
  816. // We will draw the line left to right:
  817. //************************************************************************************
  818. // Sort points left to right:
  819. long lLeft = pline->X1;
  820. long lRight = pline->X2;
  821. long lLeftY = pline->Z1;
  822. long lRightY = pline->Z2;
  823. // Inverted case
  824. if (lRight < lLeft)
  825. {
  826. lLeft = pline->X2;
  827. lRight = pline->X1;
  828. lLeftY = pline->Z2;
  829. lRightY = pline->Z1;
  830. }
  831. //************************************************************************************
  832. // Calculate y major line coefficients: (later, adapt to clipping values)
  833. long lDelX = lRight - lLeft;
  834. long lDelY = lRightY - lLeftY;
  835. long lDet = lDelX * lRightY - lDelY * lRight;
  836. long lDetY = lDelY * lRight - lDelX * lRightY; // for x-major line form
  837. //************************************************************************************
  838. // Handle Clipping, x-major line caclulation, and special cases
  839. //*************** NYI! **************
  840. // Check for horizontal clipping (easy)
  841. long lClipLeft = lLeft;// = MAX(0,lLeft);
  842. long lClipRight = lRight;// = MIN(m_sWorldW - 1,lRight);
  843. long lClipLeftY = lLeftY;
  844. long lClipRightY = lRightY;
  845. short sVerticalStrip = false;
  846. long lGridLeft;
  847. long lGridRight;
  848. if (lDelX) // determine x-clipping values:
  849. {
  850. if (lLeft < 0)
  851. {
  852. lClipLeft = 0;
  853. lClipLeftY = lDet / lDelX; //********** CAREFUL
  854. }
  855. if (lRight >= m_sWorldW)
  856. {
  857. lClipRight = m_sWorldW - 1;
  858. lClipRightY = (lClipRight * lDelY + lDet) / lDelX; //******** CAREFUL!
  859. }
  860. }
  861. else // certical strip case:
  862. {
  863. if ( (lLeft < 0) || (lRight >= m_sWorldW) ) return false;
  864. }
  865. // Check for case of reverse clip out:
  866. if ( (lLeft >= m_sWorldW) || (lRight < 0) ) return false; // clipped out!
  867. lGridLeft = (long)m_psClipX[lClipLeft]; // These represent pts BETWEEN grid squares
  868. lGridRight = 1 + (long)m_psClipX[lClipRight];
  869. if ((lDelX == 0) || ( (lGridRight - lGridLeft) <= 1) )
  870. {
  871. sVerticalStrip = true; // initial x clipping
  872. }
  873. // }
  874. // else
  875. if (sVerticalStrip) // do special clipping:
  876. {
  877. // Handle the vertical strip case:
  878. if (lClipRight < lClipLeft) return false; // vertical strip off screen
  879. // Clip Vertically
  880. lGridRight = lGridLeft + 1; // for compatibility
  881. if (lClipRightY < 0) lClipRightY = 0;
  882. if (lClipRightY >= m_sWorldH) lClipRightY = m_sWorldH;
  883. if (lClipLeftY < 0) lClipLeftY = 0;
  884. if (lClipLeftY >= m_sWorldH) lClipLeftY = m_sWorldH;
  885. }
  886. //************************************************************************************
  887. // Check for Vertical Clipping: (even for strip case)
  888. // Must recalculate grid positions of X changes:
  889. // NEED true CLIP OUT SCENARIOS FOR VERTICAL!!!!
  890. short sClippingY = false;
  891. if (lDelY != 0) // Horizontal strip case
  892. { // Note that the vertical case has
  893. // twice the possibilities
  894. if (lDelY > 0) // lClipLeftY < lClipRightY:
  895. {
  896. if (lClipLeftY < 0)
  897. {
  898. lClipLeftY = 0;
  899. lClipLeft = lDetY / lDelY; //********** CAREFUL
  900. if ( (lClipLeft < 0) || (lClipLeft >= m_sWorldW) ) return false;
  901. lGridLeft = (long)m_psClipX[lClipLeft];
  902. sClippingY = true;
  903. }
  904. if (lClipRightY >= m_sWorldH)
  905. {
  906. lClipRightY = m_sWorldH - 1;
  907. lClipRight = (lClipRightY * lDelX + lDetY) / lDelY; //******** CAREFUL!
  908. if ( (lClipRight < 0) || (lClipRight >= m_sWorldW) ) return false;
  909. lGridRight = 1 + (long)m_psClipX[lClipRight];
  910. sClippingY = true;
  911. }
  912. // Check of clipout:
  913. if (lClipRightY < lClipLeftY) return false; // clipped out
  914. }
  915. else // lClipLeftY > lClipRightY
  916. {
  917. if (lClipRightY < 0)
  918. {
  919. // Since lDelY must be negative for us to get here,
  920. // lDetY must be negative for lClipRight to be
  921. // a positive index.
  922. //ASSERT(lDetY < 0);
  923. lClipRightY = 0;
  924. lClipRight = lDetY / lDelY; //********** CAREFUL
  925. // Assert on the actual value, duh! I don't know what
  926. // I was thinking with that fancy smancy ASSERT above.
  927. //ASSERT(lClipRight >= 0);
  928. if ( (lClipRight < 0) || (lClipRight >= m_sWorldW) ) return false;
  929. lGridRight = 1 + (long)m_psClipX[lClipRight];
  930. sClippingY = true;
  931. }
  932. if (lClipLeftY >= m_sWorldH)
  933. {
  934. lClipLeftY = m_sWorldH - 1;
  935. lClipLeft = (lClipLeftY * lDelX + lDetY) / lDelY; //******** CAREFUL!
  936. // Since lDelY must be negative for us to get here,
  937. // lDetY must be positive enough to get us out of negatives
  938. // OR lClipLeftY must be negative
  939. // OR lDelX must be negative
  940. // but not both, but perhaps all three could be true.
  941. // The easiest way to watch out for this value is by the result.
  942. //ASSERT(lClipLeft >= 0);
  943. if ( (lClipLeft < 0) || (lClipLeft >= m_sWorldW) ) return false;
  944. lGridLeft = (long)m_psClipX[lClipLeft];
  945. sClippingY = true;
  946. }
  947. // Check out clipout
  948. if (lClipRightY > lClipLeftY) return false; // clipped out
  949. }
  950. if (sClippingY) // recalculate clipping situation:
  951. {
  952. if ((lDelX == 0) || ( (lGridRight - lGridLeft) == 1) )
  953. sVerticalStrip = true;
  954. }
  955. }
  956. else // horizontal strip case:
  957. {
  958. if ( (lClipLeftY < 0) || (lClipLeftY >= m_sWorldH) ) return false;
  959. }
  960. //************************************************************************************
  961. // Calculate grid points for the line: (later, switch to clipped values)
  962. // allocate on the stack a local point chart:
  963. #define MAX_GRID_W 1024 //************************************ NEED TO DEAL WITH THIS!
  964. long alPointsY[MAX_GRID_W + 1];
  965. short i,x;
  966. // Get end points:
  967. alPointsY[lGridLeft] = (long)m_psClipY[lClipLeftY];
  968. alPointsY[lGridRight] = (long)m_psClipY[lClipRightY];
  969. //************************************************************************************
  970. // Handle vertical strip case, if applicable:
  971. if (sVerticalStrip == false) // Load theintermediate values:
  972. { // not vertical strip:
  973. // Populate the point array:
  974. x = lGridLeft * m_sTileW; // now using clip instead of access, which is one over
  975. for (i = lGridLeft + 1; i < lGridRight; i++, x += m_sTileW)
  976. {
  977. alPointsY[i] = (x * lDelY + lDet) / lDelX; // WARNING - watch for lDelX
  978. alPointsY[i] = (long)m_psClipY[alPointsY[i]]; // convert to grid coordinates
  979. }
  980. }
  981. //************************************************************************************
  982. // Move acros all the grid points crossed by the line, and process each Smash List!
  983. short j;
  984. // SPLIT between positive and negative cases:
  985. short sSignY = 1;
  986. if (lDelY < 0) sSignY = -1;
  987. // SET UP DISTANCE VARIABLES:
  988. long lClosestDist2 = 2000000000; // a large number
  989. long lCurDist2;
  990. CSmash* pClosestSmash = NULL;
  991. m_lCurrentSearchCode++; // prepare for a new searching code
  992. if (m_lCurrentSearchCode < 0) // unfortunate wrapping around...
  993. {
  994. ASSERT(0); // Need to "detag" the smashatorium - detag could be a function
  995. }
  996. for (i = lGridLeft; i < lGridRight; i++)
  997. {
  998. // Now, a little tricky - do a bidirectional loop to cover both quadrants:
  999. for (j = alPointsY[i]; j != (alPointsY[i + 1] + sSignY); j += sSignY)
  1000. {
  1001. #ifdef SMASH_DEBUG
  1002. DebugSmash.DrawSmashSquare(3,0,i-1,j-1);
  1003. #endif
  1004. // Get the list:
  1005. ASSERT(j * m_sTileH < m_sWorldH + 2 * m_sTileH);
  1006. ASSERT(i * m_sTileW < m_sWorldW + 2 * m_sTileW);
  1007. ASSERT(j * m_sTileH >= 0);
  1008. ASSERT(i * m_sTileW >= 0);
  1009. CSmashatoriumList* pCurrentList = m_ppslAccessY[j * m_sTileH] + m_psAccessX[i * m_sTileW];
  1010. //***************************************************************************
  1011. // Now, process this smash grid in a standard loop like any other.
  1012. if (pCurrentList->m_sNum)
  1013. {
  1014. CSmashLink* pLink = pCurrentList->m_slHead.m_pNext;
  1015. CSmash* pSmashee;
  1016. while (pLink != &pCurrentList->m_slTail)
  1017. {
  1018. pSmashee = pLink->m_pParent;
  1019. // Test for the collision!
  1020. if (pSmashee->m_lSearchTagCode != m_lCurrentSearchCode)
  1021. { // Avoid redundancy
  1022. pSmashee->m_lSearchTagCode = m_lCurrentSearchCode;
  1023. // Test for the colision!
  1024. if (!(pSmashee->m_bits & exclude) && ((pSmashee->m_bits & ~dontcare)
  1025. & include) && pSmashee != pSmasher)
  1026. {
  1027. if (pSmashee->m_sphere.Collide(pline) == COLLISION)
  1028. {
  1029. if (CollideCyl(pSmashee,pline) == SUCCESS)
  1030. {
  1031. // Is this hit the closest?
  1032. // Calculate distance from FIRST point in the line
  1033. lCurDist2 = ABS2(
  1034. pSmashee->m_sphere.sphere.X - pline->X1,
  1035. pSmashee->m_sphere.sphere.Y - pline->Y1,
  1036. pSmashee->m_sphere.sphere.Z - pline->Z1);
  1037. // If there's not currently a closest or this one is closer . . .
  1038. if (lCurDist2 < lClosestDist2)
  1039. {
  1040. // Make this the closest.
  1041. pClosestSmash = pSmashee;
  1042. lClosestDist2 = lCurDist2;
  1043. }
  1044. }
  1045. }
  1046. }
  1047. }
  1048. pLink = pLink->m_pNext;
  1049. }
  1050. }
  1051. }
  1052. }
  1053. // Set the result:
  1054. *ppSmashee = pClosestSmash;
  1055. if (pClosestSmash) return true;
  1056. return false; // #1 most used function! (All guns)
  1057. }
  1058. //==============================================================================
  1059. ////////////////////////////////////////////////////////////////////////////////
  1060. // The following functions SHOULD be moved back into the class so that
  1061. // they can be inlined. Do this as soon as the code is fully stable!
  1062. ////////////////////////////////////////////////////////////////////////////////
  1063. ////////////////////////////////////////////////////////////////////////////////
  1064. //
  1065. // AddLimb
  1066. //
  1067. // Lower level function for adding each leg of the Smash into it's quadrant
  1068. //
  1069. ////////////////////////////////////////////////////////////////////////////////
  1070. void CSmashatorium::AddLimb(CSmashatoriumList* pList, CSmashLink* pLink)
  1071. {
  1072. ASSERT(pLink);
  1073. ASSERT(pList);
  1074. //--------------------------------------
  1075. pList->m_sNum++;
  1076. pLink->m_pLast = pList;
  1077. CSmashLink* pTail = &pList->m_slTail;
  1078. CSmashLink* pPrev = pTail->m_pPrev;
  1079. pLink->m_pNext = pTail;
  1080. pLink->m_pPrev = pPrev;
  1081. pPrev->m_pNext = pLink;
  1082. pTail->m_pPrev = pLink;
  1083. }
  1084. ////////////////////////////////////////////////////////////////////////////////
  1085. //
  1086. // RemoveLimb
  1087. //
  1088. // Lower level function for removing each leg of the Smash from it's quadrant
  1089. //
  1090. ////////////////////////////////////////////////////////////////////////////////
  1091. void CSmashatorium::RemoveLimb(CSmashatoriumList* pList,CSmashLink* pLink)
  1092. {
  1093. ASSERT(pLink);
  1094. ASSERT(pList);
  1095. ASSERT(pList->m_sNum);
  1096. //--------------------------------------
  1097. pList->m_sNum--;
  1098. pLink->m_pLast = NULL;
  1099. CSmashLink* pPrev = pLink->m_pPrev;
  1100. CSmashLink* pNext = pLink->m_pNext;
  1101. pPrev->m_pNext = pNext;
  1102. pNext->m_pPrev = pPrev;
  1103. pLink->m_pPrev = pLink->m_pNext = NULL;
  1104. }
  1105. ////////////////////////////////////////////////////////////////////////////////
  1106. //
  1107. // Update
  1108. //
  1109. // Update the specified CSmash. If it isn't already in the smashatorium, it
  1110. // is automatically added. Whenever the CSmash is modified, this must be
  1111. // called before using the smashotorium to check for collisions!
  1112. // This version of the update is less efficient, as it always searches
  1113. // four quadrants, and usually the desired object is only in one of the
  1114. // four. But I am trying it because it offers very fast updates.
  1115. //
  1116. ////////////////////////////////////////////////////////////////////////////////
  1117. short sMaxRad = 0;
  1118. void CSmashatorium::Update(CSmash* pSmash) // In: CSmash to be updated
  1119. {
  1120. ASSERT(pSmash);
  1121. // Check the flag in the realm to see if we're editing.
  1122. // If so, DO NOT actually let things go in the smashatorium:
  1123. if (pSmash->m_pThing->m_pRealm->m_flags.bEditing
  1124. && (!pSmash->m_pThing->m_pRealm->m_flags.bEditPlay))
  1125. {
  1126. return; // pretend you're in the smash
  1127. }
  1128. // ASSUME THAT THE OBJECT IS GRID SIZED!
  1129. //---------------------------------------------------------------
  1130. // (1) Cast the sphere into a 2 point square
  1131. RSphere* pSphere = &(pSmash->m_sphere.sphere);
  1132. long lR = pSphere->lRadius;
  1133. long lD = lR << 1;
  1134. // Find upper left position:
  1135. long lX,lY;
  1136. lX = pSphere->X - lR;
  1137. lY = pSphere->Z - lR;
  1138. // Hook in the special case of a FAT body in the smash!
  1139. if ( (lD > m_sTileW) || (lD > m_sTileH) )
  1140. {
  1141. //================================================================== FAT SMASH
  1142. // Create and insert a fat smash into the smashatorium.
  1143. CFatSmash* pFat = pSmash->m_pFat;
  1144. if (!pSmash->m_pFat) // we need to create a fat for you boy!
  1145. {
  1146. ASSERT(!pSmash->m_sInGrid); // a growing small object has popped it's boundaries
  1147. //============================================ CREATE FAT EXTENSION
  1148. // Create a new fat extention to the smash:
  1149. pFat = new CFatSmash;
  1150. pSmash->m_pFat = pFat;
  1151. pFat->m_pParent = pSmash; // ahhhh, a family
  1152. if (!pFat)
  1153. {
  1154. TRACE("CSmashatorium::Update: memory alloc error! Couldn't add to smashatorium!\n");
  1155. return;
  1156. }
  1157. // Remember where it was
  1158. pFat->m_lX = lX;
  1159. pFat->m_lY = lY;
  1160. // Find dimensions of smash and allocate:
  1161. pFat->m_sW = m_psAccessX[lD + m_sTileW - 1] + 1; // min tiles + 1
  1162. pFat->m_sH = m_psAccessY[lD + m_sTileH - 1] + 1; // min tiles + 1
  1163. pFat->m_sNumGrids = pFat->m_sW * pFat->m_sH;
  1164. if (pFat->Alloc(pFat->m_sNumGrids) != SUCCESS)
  1165. {
  1166. TRACE("CSmashatorium::Update: memory alloc error! Couldn't add to smashatorium!\n");
  1167. return;
  1168. } // (all links are now NULL!
  1169. // Set all the SmashLinks to point to their parent:
  1170. for (short i=0; i < pFat->m_sNumGrids; i++)
  1171. {
  1172. pFat->m_pLinks[i].m_pParent = pSmash;
  1173. }
  1174. // at this point, assume the smash is fat
  1175. }
  1176. ///////////////////////////////////
  1177. // (2) Catch the case of FULL clipping:
  1178. if ( (lX <= -lD) || (lY <= -lD) ||
  1179. (lX >= m_sWorldW) || (lY >= m_sWorldH) )
  1180. {
  1181. // We have FULL CLIP OUT!
  1182. if (pSmash->m_sInGrid) RemoveFat(pFat); // set's InGrid to false
  1183. else pSmash->m_sInGrid = FALSE;
  1184. return;
  1185. }
  1186. ///////////////////////////////////
  1187. // (4) is it's position different?
  1188. // (If it wasn't in the grid, than this is irrelevant:)
  1189. //
  1190. // Has it changed or is it reentering the grid?
  1191. if ( (lX != pFat->m_lX) || (lY != pFat->m_lY) || (!pSmash->m_sInGrid))
  1192. {
  1193. if (pSmash->m_sInGrid)
  1194. {
  1195. RemoveFat(pFat); // Remove from old
  1196. }
  1197. ///////////////////////////////////
  1198. // ADD FAT AND SET FAT POSITION!
  1199. ///////////////////////////////////
  1200. // Remember where it was
  1201. pFat->m_lX = lX;
  1202. pFat->m_lY = lY;
  1203. // (3) calculate current grid clipping state:
  1204. // Because a fat smash should NOT be moving, we shouldn't be doing this
  1205. // more than once!
  1206. short sClipX = MAX(0L,lX);
  1207. short sClipY = MAX(0L,lY);
  1208. short sClipX2 = MIN(m_sWorldW-1L,lX + lD);
  1209. short sClipY2 = MIN(m_sWorldH-1L,lY + lD);
  1210. pFat->m_pClippedGrid = m_ppslClipY[sClipY] + m_psClipX[sClipX];
  1211. // We can't access grid locations if lX and lY are negative!
  1212. short sGridX,sGridY;
  1213. if (lX < 0) sGridX = -m_psAccessX[-lX] - 1; // mirror it!
  1214. else sGridX = m_psAccessX[lX];
  1215. if (lY < 0) sGridY = -m_psAccessY[-lY] - 1; // mirror it!
  1216. else sGridY = m_psAccessX[lY];
  1217. // Convert to grid coordinates:
  1218. sClipX = m_psAccessX[sClipX];
  1219. sClipY = m_psAccessY[sClipY];
  1220. sClipX2 = m_psAccessX[sClipX2];
  1221. sClipY2 = m_psAccessY[sClipY2];
  1222. // Map to local fat smash:
  1223. // These are relative grid positions:
  1224. pFat->m_sClipX = sClipX - sGridX;
  1225. pFat->m_sClipY = sClipY - sGridY;
  1226. pFat->m_sClipW = sClipX2 - sClipX + 1;
  1227. pFat->m_sClipH = sClipY2 - sClipY + 1;
  1228. // Where do we start in smashatorium?
  1229. pFat->m_pFirstLink = pFat->m_pLinks + pFat->m_sW * pFat->m_sClipY + pFat->m_sClipX;
  1230. AddFat(pFat); // Will set flag to in grid
  1231. }
  1232. return;
  1233. //================================================================== FAT SMASH
  1234. }
  1235. ///////////////////////////////////
  1236. // (2) Catch the case of FULL clipping:
  1237. if ( (lX <= -m_sTileW) || (lY < -m_sTileH) ||
  1238. (lX >= m_sWorldW) || (lY >= m_sWorldH) )
  1239. {
  1240. // We have FULL CLIP OUT!
  1241. if (pSmash->m_sInGrid) Remove(pSmash); // set's InGrid to false
  1242. else pSmash->m_sInGrid = FALSE;
  1243. return;
  1244. }
  1245. ///////////////////////////////////
  1246. // (3) calculate grid bas position:
  1247. CSmashatoriumList *pCurrent = m_ppslClipY[lY] + m_psClipX[lX];
  1248. ///////////////////////////////////
  1249. // (4) is it's position different?
  1250. // (If it wasn't in the grid, than this is irrelevant:)
  1251. //
  1252. if (!pSmash->m_sInGrid) // Re-entering Grid
  1253. {
  1254. Add(pSmash,pCurrent); // Will set flag to in grid
  1255. }
  1256. else if (pSmash->m_link1.m_pLast != pCurrent)
  1257. {
  1258. Remove(pSmash); // Remove from old
  1259. Add(pSmash,pCurrent); // Will set flag to in grid
  1260. }
  1261. }
  1262. ////////////////////////////////////////////////////////////////////////////////
  1263. //
  1264. // ADD
  1265. //
  1266. // Higher Level -> add an entire CSmash into the 'torium
  1267. // User calls Update, which checks for clipping
  1268. // This routine ASSUMES not clipped out!
  1269. //
  1270. ////////////////////////////////////////////////////////////////////////////////
  1271. void CSmashatorium::Add(CSmash* pSmash,CSmashatoriumList *pList)
  1272. {
  1273. ASSERT(pList);
  1274. ASSERT(pSmash);
  1275. if (pSmash->m_sInGrid) return; // Don't need to re-add it!
  1276. if (pSmash->m_pFat)
  1277. {
  1278. AddFat(pSmash->m_pFat);
  1279. return;
  1280. }
  1281. //------------------------------------
  1282. pSmash->m_sInGrid = TRUE;
  1283. AddLimb(pList,&pSmash->m_link1);
  1284. AddLimb(pList + m_sGridW,&pSmash->m_link3);
  1285. AddLimb(++pList,&pSmash->m_link2);
  1286. AddLimb(pList + m_sGridW,&pSmash->m_link4);
  1287. m_sNumInSmash++;
  1288. if (m_sNumInSmash > m_sMaxNumInSmash) m_sMaxNumInSmash = m_sNumInSmash;
  1289. }
  1290. ////////////////////////////////////////////////////////////////////////////////
  1291. //
  1292. // AddFat
  1293. //
  1294. // Higher Level -> add an entire CSmash into the 'torium
  1295. // User calls Update, which checks for clipping
  1296. // This routine ASSUMES not clipped out!
  1297. // This is specially tailored to a fat smash object.
  1298. // Clipping information should be set in the FatSmash before passing.
  1299. // All links should be NULLED if clipped!
  1300. //
  1301. ////////////////////////////////////////////////////////////////////////////////
  1302. void CSmashatorium::AddFat(CFatSmash* pFatSmash)
  1303. {
  1304. ASSERT(pFatSmash);
  1305. if (pFatSmash->m_pParent->m_sInGrid) return; // Don't need to re-add it!
  1306. //=====================================
  1307. pFatSmash->m_pParent->m_sInGrid = TRUE;
  1308. short i,j;
  1309. CSmashatoriumList* pList = pFatSmash->m_pClippedGrid; // assume not clipped out!
  1310. CSmashLink* pLink = pFatSmash->m_pFirstLink;
  1311. //-------------------------------------
  1312. for (j=0; j < pFatSmash->m_sClipH; j++,pList += m_sGridW - pFatSmash->m_sClipW,
  1313. pLink += pFatSmash->m_sW - pFatSmash->m_sClipW)
  1314. {
  1315. for (i=0; i < pFatSmash->m_sClipW; i++,pList++,pLink++)
  1316. {
  1317. AddLimb(pList,pLink); // ASSUME already cleared out!
  1318. }
  1319. }
  1320. //=====================================
  1321. m_sNumInSmash++;
  1322. if (m_sNumInSmash > m_sMaxNumInSmash) m_sMaxNumInSmash = m_sNumInSmash;
  1323. TRACE("Fat added\n");
  1324. }
  1325. ////////////////////////////////////////////////////////////////////////////////
  1326. //
  1327. //
  1328. // Remove
  1329. //
  1330. // This is on a per object level:
  1331. // Remove the CSmash from the smashatorium
  1332. //
  1333. ////////////////////////////////////////////////////////////////////////////////
  1334. void CSmashatorium::Remove(CSmash* pSmash)
  1335. {
  1336. if (pSmash->m_sInGrid == FALSE) return; // don't need to remove it!
  1337. if (pSmash->m_pFat)
  1338. {
  1339. RemoveFat(pSmash->m_pFat);
  1340. return;
  1341. }
  1342. m_sNumInSmash--;
  1343. pSmash->m_sInGrid = FALSE;
  1344. CSmashLink* pLink;
  1345. pLink = &pSmash->m_link1;
  1346. // In the current scheme, it's all or nothing:
  1347. if (pLink->m_pLast) // assume all four legs are here
  1348. {
  1349. RemoveLimb(pLink->m_pLast,pLink);
  1350. pLink = &pSmash->m_link2;
  1351. RemoveLimb(pLink->m_pLast,pLink);
  1352. pLink = &pSmash->m_link3;
  1353. RemoveLimb(pLink->m_pLast,pLink);
  1354. pLink = &pSmash->m_link4;
  1355. RemoveLimb(pLink->m_pLast,pLink);
  1356. }
  1357. }
  1358. ////////////////////////////////////////////////////////////////////////////////
  1359. //
  1360. //
  1361. // RemoveFat
  1362. //
  1363. // This is on a per object level:
  1364. // Remove the CSmash from the smashatorium
  1365. // Specialized to remove a fat object.
  1366. // It assumes that the range given takes clipping into account
  1367. //
  1368. ////////////////////////////////////////////////////////////////////////////////
  1369. void CSmashatorium::RemoveFat(CFatSmash* pFatSmash)
  1370. {
  1371. ASSERT(pFatSmash);
  1372. if (pFatSmash->m_pParent->m_sInGrid == FALSE) return; // don't need to remove it!
  1373. //===========================================
  1374. pFatSmash->m_pParent->m_sInGrid = FALSE;
  1375. short i,j;
  1376. //****** HERE IS A BIG DESIGN FLAW!!!!! *****
  1377. CSmashLink* pLink = pFatSmash->m_pLinks; // do them all!
  1378. //-------------------------------------
  1379. for (j=0; j < pFatSmash->m_sH; j++)
  1380. {
  1381. for (i=0; i < pFatSmash->m_sW; i++,pLink++) // pLink will wrap to the next line
  1382. {
  1383. if (pLink->m_pLast)
  1384. {
  1385. RemoveLimb(pLink->m_pLast,pLink); // ASSUME already cleared out!
  1386. }
  1387. }
  1388. }
  1389. //===========================================
  1390. m_sNumInSmash--;
  1391. TRACE("Fat removed\n");
  1392. }
  1393. //******************************************************************************
  1394. //******************************** CFatSmash *********************************
  1395. //******************************************************************************
  1396. ////////////////////////////////////////////////////////////////////////////////
  1397. //
  1398. // CFatSmash::Erase - clear all values but do not deallocate
  1399. //
  1400. ////////////////////////////////////////////////////////////////////////////////
  1401. void CFatSmash::Erase()
  1402. {
  1403. m_sClipX = m_sClipY = m_sClipW = m_sClipH = m_sW =
  1404. m_sH = m_sNumGrids = 0;
  1405. m_pClippedGrid = NULL;
  1406. m_pLinks = m_pFirstLink = NULL; // Must be deleted first!
  1407. m_pParent = NULL;
  1408. m_lX = m_lY = 0;
  1409. }
  1410. ////////////////////////////////////////////////////////////////////////////////
  1411. //
  1412. // CFatSmash::Destroy - deallocate the extra smash links...
  1413. //
  1414. ////////////////////////////////////////////////////////////////////////////////
  1415. void CFatSmash::Destroy()
  1416. {
  1417. ASSERT(m_pLinks);
  1418. delete [] m_pLinks; // SHOULD be safe
  1419. Erase();
  1420. }
  1421. ////////////////////////////////////////////////////////////////////////////////
  1422. //
  1423. // CFatSmash::Alloc - This instantiates a list of extra SmashLinks
  1424. //
  1425. // RETURNS: SUCCESS OR FAILURE
  1426. //
  1427. ////////////////////////////////////////////////////////////////////////////////
  1428. short CFatSmash::Alloc(short sNumLinks)
  1429. {
  1430. m_pLinks = new CSmashLink[sNumLinks];
  1431. if (m_pLinks) return SUCCESS;
  1432. return FAILURE;
  1433. }
  1434. ////////////////////////////////////////////////////////////////////////////////
  1435. //
  1436. //
  1437. //
  1438. //
  1439. ////////////////////////////////////////////////////////////////////////////////
  1440. ////////////////////////////////////////////////////////////////////////////////
  1441. ////////////////////////////////////////////////////////////////////////////////
  1442. ////////////////////////////////////////////////////////////////////////////////
  1443. //---------------------------------------------------------------------------