PolygonTrigger.cpp 15 KB

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