PolygonTrigger.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // PolygonTrigger.cpp
  24. // Class to encapsulate polygon trigger areas.
  25. // Author: John Ahlquist, November 2001
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/DataChunk.h"
  28. #include "Common/MapObject.h"
  29. #include "Common/MapReaderWriterInfo.h"
  30. #include "Common/Xfer.h"
  31. #include "GameLogic/PolygonTrigger.h"
  32. #include "GameLogic/TerrainLogic.h"
  33. /* ********* PolygonTrigger class ****************************/
  34. PolygonTrigger *PolygonTrigger::ThePolygonTriggerListPtr = NULL;
  35. Int PolygonTrigger::s_currentID = 1;
  36. /**
  37. PolygonTrigger - Constructor.
  38. */
  39. PolygonTrigger::PolygonTrigger(Int initialAllocation) :
  40. m_nextPolygonTrigger(NULL),
  41. m_points(NULL),
  42. m_numPoints(0),
  43. m_sizePoints(0),
  44. m_exportWithScripts(false),
  45. m_isWaterArea(false),
  46. m_shouldRender(true),
  47. m_selected(false),
  48. //Added By Sadullah Nader
  49. //Initializations inserted
  50. m_isRiver(FALSE),
  51. m_riverStart(0)
  52. //
  53. {
  54. if (initialAllocation < 2) initialAllocation = 2;
  55. m_points = NEW ICoord3D[initialAllocation]; // pool[]ify
  56. m_sizePoints = initialAllocation;
  57. m_triggerID = s_currentID++;
  58. m_waterHandle.m_polygon = this;
  59. }
  60. /**
  61. PolygonTrigger - Destructor - note - if linked, deletes linked items.
  62. */
  63. PolygonTrigger::~PolygonTrigger(void)
  64. {
  65. if (m_points) {
  66. delete [] m_points;
  67. m_points = NULL;
  68. }
  69. if (m_nextPolygonTrigger) {
  70. PolygonTrigger *cur = m_nextPolygonTrigger;
  71. PolygonTrigger *next;
  72. while (cur) {
  73. next = cur->getNext();
  74. cur->setNextPoly(NULL); // prevents recursion.
  75. cur->deleteInstance();
  76. cur = next;
  77. }
  78. }
  79. }
  80. /**
  81. PolygonTrigger::reallocate - increases the size of the points list.
  82. NOTE: It is expected that this will only get called in the editor, as in the game
  83. the poly triggers don't change.
  84. */
  85. void PolygonTrigger::reallocate(void)
  86. {
  87. DEBUG_ASSERTCRASH(m_numPoints <= m_sizePoints, ("Invalid m_numPoints."));
  88. if (m_numPoints == m_sizePoints) {
  89. // Reallocate.
  90. m_sizePoints += m_sizePoints;
  91. ICoord3D *newPts = NEW ICoord3D[m_sizePoints];
  92. Int i;
  93. for (i=0; i<m_numPoints; i++) {
  94. newPts[i] = m_points[i];
  95. }
  96. delete [] m_points;
  97. m_points = newPts;
  98. }
  99. }
  100. /**
  101. * Find the polygon trigger with the matching ID
  102. */
  103. PolygonTrigger *PolygonTrigger::getPolygonTriggerByID(Int triggerID)
  104. {
  105. for( PolygonTrigger *poly = PolygonTrigger::getFirstPolygonTrigger();
  106. poly; poly = poly->getNext() )
  107. if( poly->getID() == triggerID )
  108. return poly;
  109. // not found
  110. return NULL;
  111. }
  112. /**
  113. * PolygonTrigger::ParsePolygonTriggersDataChunk - read a polygon triggers chunk.
  114. * Format is the newer CHUNKY format.
  115. * See PolygonTrigger::WritePolygonTriggersDataChunk for the writer.
  116. * Input: DataChunkInput
  117. *
  118. */
  119. Bool PolygonTrigger::ParsePolygonTriggersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  120. {
  121. Int count;
  122. Int numPoints;
  123. Int triggerID;
  124. Int maxTriggerId = 0;
  125. Bool isWater;
  126. Bool isRiver;
  127. Int riverStart;
  128. AsciiString triggerName;
  129. AsciiString layerName;
  130. // Remove any existing polygon triggers, if any.
  131. PolygonTrigger::deleteTriggers(); // just in case.
  132. PolygonTrigger *pPrevTrig = NULL;
  133. ICoord3D loc;
  134. count = file.readInt();
  135. while (count>0) {
  136. count--;
  137. triggerName = file.readAsciiString();
  138. if (info->version >= K_TRIGGERS_VERSION_4) {
  139. layerName = file.readAsciiString();
  140. }
  141. triggerID = file.readInt();
  142. isWater = false;
  143. if (info->version >= K_TRIGGERS_VERSION_2) {
  144. isWater = file.readByte();
  145. }
  146. isRiver = false;
  147. riverStart = 0;
  148. if (info->version >= K_TRIGGERS_VERSION_3) {
  149. isRiver = file.readByte();
  150. riverStart = file.readInt();
  151. }
  152. numPoints = file.readInt();
  153. PolygonTrigger *pTrig = newInstance(PolygonTrigger)(numPoints+1);
  154. pTrig->setTriggerName(triggerName);
  155. if (info->version >= K_TRIGGERS_VERSION_4) {
  156. pTrig->setLayerName(layerName);
  157. }
  158. pTrig->setWaterArea(isWater);
  159. pTrig->setRiver(isRiver);
  160. pTrig->setRiverStart(riverStart);
  161. pTrig->m_triggerID = triggerID;
  162. if (triggerID > maxTriggerId) {
  163. maxTriggerId = triggerID;
  164. }
  165. Int i;
  166. for (i=0; i<numPoints; i++) {
  167. loc.x = file.readInt();
  168. loc.y = file.readInt();
  169. loc.z = file.readInt();
  170. pTrig->addPoint(loc);
  171. }
  172. if (numPoints<2) {
  173. DEBUG_LOG(("Deleting polygon trigger '%s' with %d points.\n",
  174. pTrig->getTriggerName().str(), numPoints));
  175. pTrig->deleteInstance();
  176. continue;
  177. }
  178. if (pPrevTrig) {
  179. pPrevTrig->setNextPoly(pTrig);
  180. } else {
  181. PolygonTrigger::addPolygonTrigger(pTrig);
  182. }
  183. pPrevTrig = pTrig;
  184. }
  185. if (info->version == K_TRIGGERS_VERSION_1)
  186. {
  187. // before water areas existed, so create a default one.
  188. PolygonTrigger *pTrig = newInstance(PolygonTrigger)(4);
  189. pTrig->setWaterArea(true);
  190. #ifdef _DEBUG
  191. pTrig->setTriggerName("AutoAddedWaterAreaTrigger");
  192. #endif
  193. pTrig->m_triggerID = maxTriggerId++;
  194. loc.x = -30*MAP_XY_FACTOR;
  195. loc.y = -30*MAP_XY_FACTOR;
  196. loc.z = 7; // The old water position.
  197. pTrig->addPoint(loc);
  198. loc.x = 30*MAP_XY_FACTOR + TheGlobalData->m_waterExtentX;
  199. pTrig->addPoint(loc);
  200. loc.y = 30*MAP_XY_FACTOR + TheGlobalData->m_waterExtentY;
  201. pTrig->addPoint(loc);
  202. loc.x = -30*MAP_XY_FACTOR;
  203. pTrig->addPoint(loc);
  204. if (pPrevTrig) {
  205. pPrevTrig->setNextPoly(pTrig);
  206. } else {
  207. PolygonTrigger::addPolygonTrigger(pTrig);
  208. }
  209. pPrevTrig = pTrig;
  210. }
  211. s_currentID = maxTriggerId+1;
  212. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Incorrect data file length."));
  213. return true;
  214. }
  215. /**
  216. * PolygonTrigger::WritePolygonTriggersDataChunk - Writes a Polygon triggers chunk.
  217. * Format is the newer CHUNKY format.
  218. * See PolygonTrigger::ParsePolygonTriggersDataChunk for the reader.
  219. * Input: DataChunkInput
  220. *
  221. */
  222. void PolygonTrigger::WritePolygonTriggersDataChunk(DataChunkOutput &chunkWriter)
  223. {
  224. chunkWriter.openDataChunk("PolygonTriggers", K_TRIGGERS_VERSION_4);
  225. PolygonTrigger *pTrig;
  226. Int count = 0;
  227. for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
  228. count++;
  229. }
  230. chunkWriter.writeInt(count);
  231. for (pTrig=PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
  232. chunkWriter.writeAsciiString(pTrig->getTriggerName());
  233. chunkWriter.writeAsciiString(pTrig->getLayerName());
  234. chunkWriter.writeInt(pTrig->getID());
  235. chunkWriter.writeByte(pTrig->isWaterArea());
  236. chunkWriter.writeByte(pTrig->isRiver());
  237. chunkWriter.writeInt(pTrig->getRiverStart());
  238. chunkWriter.writeInt(pTrig->getNumPoints());
  239. Int i;
  240. for (i=0; i<pTrig->getNumPoints(); i++) {
  241. ICoord3D loc = *pTrig->getPoint(i);
  242. chunkWriter.writeInt( loc.x);
  243. chunkWriter.writeInt( loc.y);
  244. chunkWriter.writeInt( loc.z);
  245. }
  246. }
  247. chunkWriter.closeDataChunk();
  248. }
  249. /**
  250. PolygonTrigger::updateBounds - Updates the bounds.
  251. */
  252. void PolygonTrigger::updateBounds(void) const
  253. {
  254. const Int BIG_INT=0x7ffff0;
  255. m_bounds.lo.x = m_bounds.lo.y = BIG_INT;
  256. m_bounds.hi.x = m_bounds.hi.y = -BIG_INT;
  257. Int i;
  258. for (i=0; i<m_numPoints; i++) {
  259. if (m_points[i].x < m_bounds.lo.x) m_bounds.lo.x = m_points[i].x;
  260. if (m_points[i].y < m_bounds.lo.y) m_bounds.lo.y = m_points[i].y;
  261. if (m_points[i].x > m_bounds.hi.x) m_bounds.hi.x = m_points[i].x;
  262. if (m_points[i].y > m_bounds.hi.y) m_bounds.hi.y = m_points[i].y;
  263. }
  264. m_boundsNeedsUpdate = 0;
  265. Real halfWidth = (m_bounds.hi.x - m_bounds.lo.x) / 2.0f;
  266. Real halfHeight = (m_bounds.hi.y + m_bounds.lo.y) / 2.0f;
  267. m_radius = sqrt(halfHeight*halfHeight + halfWidth*halfWidth);
  268. }
  269. /**
  270. PolygonTrigger::addPolygonTrigger adds a trigger to the list of triggers.
  271. */
  272. void PolygonTrigger::addPolygonTrigger(PolygonTrigger *pTrigger)
  273. {
  274. for (PolygonTrigger *pTrig=getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
  275. DEBUG_ASSERTCRASH(pTrig != pTrigger, ("Attempting to add trigger already in list."));
  276. if (pTrig==pTrigger) return;
  277. }
  278. pTrigger->m_nextPolygonTrigger = ThePolygonTriggerListPtr;
  279. ThePolygonTriggerListPtr = pTrigger;
  280. }
  281. /**
  282. PolygonTrigger::removePolygonTrigger removes a trigger to the list of
  283. triggers. note - does NOT delete pTrigger.
  284. */
  285. void PolygonTrigger::removePolygonTrigger(PolygonTrigger *pTrigger)
  286. {
  287. PolygonTrigger *pPrev = NULL;
  288. for (PolygonTrigger *pTrig=getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
  289. if (pTrig==pTrigger) break;
  290. pPrev = pTrig;
  291. }
  292. DEBUG_ASSERTCRASH(pTrig, ("Attempting to remove a polygon not in the list."));
  293. if (pTrig) {
  294. if (pPrev) {
  295. DEBUG_ASSERTCRASH(pTrigger==pPrev->m_nextPolygonTrigger, ("Logic errror. jba."));
  296. pPrev->m_nextPolygonTrigger = pTrig->m_nextPolygonTrigger;
  297. } else {
  298. DEBUG_ASSERTCRASH(pTrigger==ThePolygonTriggerListPtr, ("Logic errror. jba."));
  299. ThePolygonTriggerListPtr = pTrig->m_nextPolygonTrigger;
  300. }
  301. }
  302. pTrigger->m_nextPolygonTrigger = NULL;
  303. }
  304. /**
  305. PolygonTrigger::deleteTriggers Deletes list of triggers.
  306. */
  307. void PolygonTrigger::deleteTriggers(void)
  308. {
  309. PolygonTrigger *pList = ThePolygonTriggerListPtr;
  310. ThePolygonTriggerListPtr = NULL;
  311. s_currentID = 1;
  312. pList->deleteInstance();
  313. }
  314. /**
  315. PolygonTrigger::addPoint adds a point at the end of the polygon.
  316. NOTE: It is expected that this will only get called in the editor, as in the game
  317. the poly triggers don't change.
  318. */
  319. void PolygonTrigger::addPoint(const ICoord3D &point)
  320. {
  321. DEBUG_ASSERTCRASH(m_numPoints <= m_sizePoints, ("Invalid m_numPoints."));
  322. if (m_numPoints == m_sizePoints) {
  323. reallocate();
  324. }
  325. m_points[m_numPoints] = point;
  326. m_numPoints++;
  327. m_boundsNeedsUpdate = true;
  328. }
  329. /**
  330. PolygonTrigger::setPoint sets the point at index ndx.
  331. NOTE: It is expected that this will only get called in the editor, as in the game
  332. the poly triggers don't change.
  333. */
  334. void PolygonTrigger::setPoint(const ICoord3D &point, Int ndx)
  335. {
  336. DEBUG_ASSERTCRASH(ndx>=0 && ndx <= m_numPoints, ("Invalid ndx."));
  337. if (ndx<0) return;
  338. if (ndx == m_numPoints) { // we are setting first available unused point
  339. addPoint(point);
  340. return;
  341. }
  342. if (ndx>m_numPoints) { // Can't skip points.
  343. return;
  344. }
  345. m_points[ndx] = point;
  346. m_boundsNeedsUpdate = true;
  347. }
  348. /**
  349. PolygonTrigger::insertPoint .
  350. NOTE: It is expected that this will only get called in the editor, as in the game
  351. the poly triggers don't change.
  352. */
  353. void PolygonTrigger::insertPoint(const ICoord3D &point, Int ndx)
  354. {
  355. DEBUG_ASSERTCRASH(ndx>=0 && ndx <= m_numPoints, ("Invalid ndx."));
  356. if (ndx<0) return;
  357. if (ndx == m_numPoints) { // we are setting first available unused point
  358. addPoint(point);
  359. return;
  360. }
  361. if (m_numPoints == m_sizePoints) {
  362. reallocate();
  363. }
  364. Int i;
  365. for (i=m_numPoints; i>ndx; i--) {
  366. m_points[i] = m_points[i-1];
  367. }
  368. m_points[ndx] = point;
  369. m_numPoints++;
  370. m_boundsNeedsUpdate = true;
  371. }
  372. /**
  373. PolygonTrigger::deletePoint .
  374. NOTE: It is expected that this will only get called in the editor, as in the game
  375. the poly triggers don't change.
  376. */
  377. void PolygonTrigger::deletePoint(Int ndx)
  378. {
  379. DEBUG_ASSERTCRASH(ndx>=0 && ndx < m_numPoints, ("Invalid ndx."));
  380. if (ndx<0 || ndx>=m_numPoints) return;
  381. Int i;
  382. for (i=ndx; i<m_numPoints-1; i++) {
  383. m_points[i] = m_points[i+1];
  384. }
  385. m_numPoints--;
  386. m_boundsNeedsUpdate = true;
  387. }
  388. void PolygonTrigger::getCenterPoint(Coord3D* pOutCoord) const
  389. {
  390. DEBUG_ASSERTCRASH(pOutCoord != NULL, ("pOutCoord was null. Non-Fatal, but shouldn't happen."));
  391. if (!pOutCoord) {
  392. return;
  393. }
  394. if (m_boundsNeedsUpdate) {
  395. updateBounds();
  396. }
  397. (*pOutCoord).x = (m_bounds.lo.x + m_bounds.hi.x) / 2.0f;
  398. (*pOutCoord).y = (m_bounds.lo.y + m_bounds.hi.y) / 2.0f;
  399. (*pOutCoord).z = TheTerrainLogic->getGroundHeight(pOutCoord->x, pOutCoord->y);
  400. }
  401. Real PolygonTrigger::getRadius(void) const
  402. {
  403. if (m_boundsNeedsUpdate) {
  404. updateBounds();
  405. }
  406. return m_radius;
  407. }
  408. /**
  409. PolygonTrigger - pointInTrigger.
  410. */
  411. Bool PolygonTrigger::pointInTrigger(ICoord3D &point) const
  412. {
  413. if (m_boundsNeedsUpdate) {
  414. updateBounds();
  415. }
  416. if (point.x < m_bounds.lo.x) return false;
  417. if (point.y < m_bounds.lo.y) return false;
  418. if (point.x > m_bounds.hi.x) return false;
  419. if (point.y > m_bounds.hi.y) return false;
  420. Bool inside = false;
  421. Int i;
  422. for (i=0; i<m_numPoints; i++) {
  423. ICoord3D pt1 = m_points[i];
  424. ICoord3D pt2;
  425. if (i==m_numPoints-1) {
  426. pt2 = m_points[0];
  427. } else {
  428. pt2 = m_points[i+1];
  429. }
  430. if (pt1.y == pt2.y) {
  431. continue; // ignore horizontal lines.
  432. }
  433. if (pt1.y < point.y && pt2.y < point.y) continue;
  434. if (pt1.y >= point.y && pt2.y >= point.y) continue;
  435. if (pt1.x<point.x && pt2.x < point.x) continue;
  436. // Line segment crosses ray from point x->infinity.
  437. Int dy = pt2.y-pt1.y;
  438. Int dx = pt2.x-pt1.x;
  439. Real intersectionX = pt1.x + (dx * (point.y-pt1.y)) / ((Real)dy);
  440. if (intersectionX >= point.x) {
  441. inside = !inside;
  442. }
  443. }
  444. return inside;
  445. }
  446. // ------------------------------------------------------------------------------------------------
  447. const WaterHandle* PolygonTrigger::getWaterHandle(void) const
  448. {
  449. if( isWaterArea() )
  450. return &m_waterHandle;
  451. return NULL; // this polygon trigger is not a water area
  452. }
  453. Bool PolygonTrigger::isValid(void) const
  454. {
  455. if (m_numPoints == 0) {
  456. return FALSE;
  457. }
  458. return TRUE;
  459. }
  460. // ------------------------------------------------------------------------------------------------
  461. /** CRC */
  462. // ------------------------------------------------------------------------------------------------
  463. void PolygonTrigger::crc( Xfer *xfer )
  464. {
  465. } // end crc
  466. // ------------------------------------------------------------------------------------------------
  467. /** Xfer method
  468. * Version Info:
  469. * 1: Initial version */
  470. // ------------------------------------------------------------------------------------------------
  471. void PolygonTrigger::xfer( Xfer *xfer )
  472. {
  473. // version
  474. XferVersion currentVersion = 1;
  475. XferVersion version = currentVersion;
  476. xfer->xferVersion( &version, currentVersion );
  477. // number of data points
  478. xfer->xferInt( &m_numPoints );
  479. // xfer all data points
  480. ICoord3D *point;
  481. for( Int i = 0; i < m_numPoints; ++i )
  482. {
  483. // get this point
  484. point = &m_points[ i ];
  485. // xfer point
  486. xfer->xferICoord3D( point );
  487. } // end for, i
  488. // bounds
  489. xfer->xferIRegion2D( &m_bounds );
  490. // radius
  491. xfer->xferReal( &m_radius );
  492. // bounds need update
  493. xfer->xferBool( &m_boundsNeedsUpdate );
  494. } // end xfer
  495. // ------------------------------------------------------------------------------------------------
  496. /** Load post process */
  497. // ------------------------------------------------------------------------------------------------
  498. void PolygonTrigger::loadPostProcess( void )
  499. {
  500. } // end loadPostProcess