ParkingPlaceBehavior.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  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. // FILE: ParkingPlaceBehavior.cpp ///////////////////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, June 2002
  25. // Desc:
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/CRCDebug.h"
  30. #include "Common/Xfer.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "GameClient/Drawable.h"
  33. #include "GameLogic/AI.h"
  34. #include "GameLogic/AIPathfind.h"
  35. #include "GameLogic/Module/AIUpdate.h"
  36. #include "GameLogic/GameLogic.h"
  37. #include "GameLogic/Module/ParkingPlaceBehavior.h"
  38. #include "GameLogic/Module/ProductionUpdate.h"
  39. #include "GameLogic/Module/JetAIUpdate.h"
  40. #include "GameLogic/Object.h"
  41. #include "GameLogic/TerrainLogic.h"
  42. #include "Common/Team.h"
  43. #ifdef _INTERNAL
  44. // for occasional debugging...
  45. //#pragma optimize("", off)
  46. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  47. #endif
  48. //-------------------------------------------------------------------------------------------------
  49. //-------------------------------------------------------------------------------------------------
  50. ParkingPlaceBehavior::ParkingPlaceBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  51. {
  52. m_gotInfo = false;
  53. //Added By Sadullah Nader
  54. //Initializations
  55. m_heliRallyPoint.zero();
  56. //
  57. m_heliRallyPointExists = FALSE;
  58. m_nextHealFrame = FOREVER;
  59. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  60. }
  61. //-------------------------------------------------------------------------------------------------
  62. //-------------------------------------------------------------------------------------------------
  63. ParkingPlaceBehavior::~ParkingPlaceBehavior( void )
  64. {
  65. }
  66. //-------------------------------------------------------------------------------------------------
  67. //-------------------------------------------------------------------------------------------------
  68. void ParkingPlaceBehavior::buildInfo()
  69. {
  70. if (m_gotInfo)
  71. return;
  72. if (getObject()->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ||
  73. getObject()->testStatus(OBJECT_STATUS_SOLD))
  74. return;
  75. const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData();
  76. ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface();
  77. m_spaces.reserve(d->m_numRows * d->m_numCols);
  78. Int door = 0;
  79. {
  80. ParkingPlaceInfo info;
  81. for (Int row = 0; row < d->m_numRows; ++row)
  82. {
  83. for (Int col = 0; col < d->m_numCols; ++col)
  84. {
  85. AsciiString tmp;
  86. Matrix3D mtx;
  87. tmp.format("Runway%dPark%dHan",col+1,row+1);
  88. getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_hangarStart, &mtx);
  89. info.m_hangarStartOrient = mtx.Get_Z_Rotation();
  90. tmp.format("Runway%dParking%d",col+1,row+1);
  91. getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_location, &mtx);
  92. info.m_orientation = mtx.Get_Z_Rotation();
  93. tmp.format("Runway%dPrep%d",col+1,row+1);
  94. getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_prep, NULL);
  95. info.m_runway = col;
  96. info.m_door = (ExitDoorType)door++;
  97. info.m_objectInSpace = INVALID_ID;
  98. info.m_reservedForExit = false;
  99. if (pu)
  100. pu->setHoldDoorOpen(info.m_door, false);
  101. m_spaces.push_back(info);
  102. }
  103. }
  104. }
  105. if (d->m_hasRunways)
  106. {
  107. RunwayInfo info;
  108. m_runways.reserve(d->m_numCols);
  109. for (Int col = 0; col < d->m_numCols; ++col)
  110. {
  111. AsciiString tmp;
  112. tmp.format("RunwayStart%d",col+1);
  113. getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_start, NULL);
  114. tmp.format("RunwayEnd%d",col+1);
  115. getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_end, NULL);
  116. info.m_inUseBy = INVALID_ID;
  117. info.m_nextInLineForTakeoff = INVALID_ID;
  118. info.m_wasInLine = false;
  119. m_runways.push_back(info);
  120. }
  121. }
  122. m_gotInfo = true;
  123. }
  124. //-------------------------------------------------------------------------------------------------
  125. void ParkingPlaceBehavior::purgeDead()
  126. {
  127. buildInfo();
  128. ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface();
  129. {
  130. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  131. {
  132. if (it->m_objectInSpace != INVALID_ID)
  133. {
  134. Object* obj = TheGameLogic->findObjectByID(it->m_objectInSpace);
  135. if (obj == NULL || obj->isEffectivelyDead())
  136. {
  137. it->m_objectInSpace = INVALID_ID;
  138. it->m_reservedForExit = false;
  139. if (pu)
  140. pu->setHoldDoorOpen(it->m_door, false);
  141. }
  142. }
  143. }
  144. }
  145. {
  146. for (std::vector<RunwayInfo>::iterator it = m_runways.begin(); it != m_runways.end(); ++it)
  147. {
  148. if (it->m_inUseBy != INVALID_ID)
  149. {
  150. Object* obj = TheGameLogic->findObjectByID(it->m_inUseBy);
  151. if (obj == NULL || obj->isEffectivelyDead())
  152. {
  153. it->m_inUseBy = INVALID_ID;
  154. it->m_wasInLine = false;
  155. }
  156. }
  157. if (it->m_nextInLineForTakeoff != INVALID_ID)
  158. {
  159. Object* obj = TheGameLogic->findObjectByID(it->m_nextInLineForTakeoff);
  160. if (obj == NULL || obj->isEffectivelyDead())
  161. {
  162. it->m_nextInLineForTakeoff = INVALID_ID;
  163. }
  164. }
  165. }
  166. }
  167. {
  168. Bool anythingPurged = false;
  169. for (std::list<HealingInfo>::iterator it = m_healing.begin(); it != m_healing.end(); /*++it*/)
  170. {
  171. if (it->m_gettingHealedID != INVALID_ID)
  172. {
  173. Object* objToHeal = TheGameLogic->findObjectByID(it->m_gettingHealedID);
  174. if (objToHeal == NULL || objToHeal->isEffectivelyDead())
  175. {
  176. it = m_healing.erase(it);
  177. anythingPurged = true;
  178. }
  179. else
  180. {
  181. ++it;
  182. }
  183. }
  184. }
  185. if (anythingPurged)
  186. resetWakeFrame();
  187. }
  188. }
  189. //-------------------------------------------------------------------------------------------------
  190. // note: called from client, so MUST NOT modify self in any way, or desyncs will occur
  191. Bool ParkingPlaceBehavior::hasReservedSpace(ObjectID id) const
  192. {
  193. if (!m_gotInfo)
  194. return false;
  195. if (id == INVALID_ID) // shouldn't call this way, but Weapon mistakenly does sometimes, so check for it
  196. return false;
  197. for (std::vector<ParkingPlaceInfo>::const_iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  198. {
  199. if (it->m_objectInSpace == id)
  200. return true;
  201. }
  202. return false;
  203. }
  204. //-------------------------------------------------------------------------------------------------
  205. Int ParkingPlaceBehavior::getSpaceIndex( ObjectID id ) const
  206. {
  207. if( id == INVALID_ID )
  208. {
  209. return -1;
  210. }
  211. Int index = 0;
  212. for( std::vector<ParkingPlaceInfo>::const_iterator it = m_spaces.begin(); it != m_spaces.end(); it++, index++ )
  213. {
  214. if (it->m_objectInSpace == id)
  215. {
  216. return index;
  217. }
  218. }
  219. return -1;
  220. }
  221. //-------------------------------------------------------------------------------------------------
  222. ParkingPlaceBehavior::ParkingPlaceInfo* ParkingPlaceBehavior::findPPI(ObjectID id)
  223. {
  224. DEBUG_ASSERTCRASH(id != INVALID_ID, ("call findEmptyPPI instead"));
  225. if (!m_gotInfo || id == INVALID_ID)
  226. return NULL;
  227. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  228. {
  229. if (it->m_objectInSpace == id)
  230. return &(*it);
  231. }
  232. return NULL;
  233. }
  234. //-------------------------------------------------------------------------------------------------
  235. ParkingPlaceBehavior::ParkingPlaceInfo* ParkingPlaceBehavior::findEmptyPPI()
  236. {
  237. if (!m_gotInfo)
  238. return NULL;
  239. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  240. {
  241. if (it->m_objectInSpace == INVALID_ID && it->m_reservedForExit == false)
  242. return &(*it);
  243. }
  244. return NULL;
  245. }
  246. //-------------------------------------------------------------------------------------------------
  247. // note: called from client, so MUST NOT modify self in any way, or desyncs will occur
  248. Bool ParkingPlaceBehavior::shouldReserveDoorWhenQueued(const ThingTemplate* thing) const
  249. {
  250. if (thing->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  251. return false;
  252. return true;
  253. }
  254. //-------------------------------------------------------------------------------------------------
  255. // note: called from client, so MUST NOT modify self in any way, or desyncs will occur
  256. Bool ParkingPlaceBehavior::hasAvailableSpaceFor(const ThingTemplate* thing) const
  257. {
  258. if (!m_gotInfo) // degenerate case, shouldn't happen, but just in case...
  259. return false;
  260. if (thing->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  261. return true;
  262. for (std::vector<ParkingPlaceInfo>::const_iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  263. {
  264. ObjectID id = it->m_objectInSpace;
  265. // since this is const, and we can't purge the dead safely, just peek and see if we have a dead thing.
  266. if (id != INVALID_ID)
  267. {
  268. Object* obj = TheGameLogic->findObjectByID(id);
  269. if (obj == NULL || obj->isEffectivelyDead())
  270. {
  271. id = INVALID_ID;
  272. }
  273. }
  274. if (id == INVALID_ID && it->m_reservedForExit == false)
  275. {
  276. return true;
  277. }
  278. }
  279. return false;
  280. }
  281. //-------------------------------------------------------------------------------------------------
  282. Bool ParkingPlaceBehavior::reserveSpace(ObjectID id, Real parkingOffset, ParkingPlaceBehaviorInterface::PPInfo* info)
  283. {
  284. buildInfo();
  285. purgeDead();
  286. const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData();
  287. ParkingPlaceInfo* ppi = findPPI(id);
  288. if (ppi == NULL)
  289. {
  290. ppi = findEmptyPPI();
  291. if (ppi == NULL)
  292. {
  293. DEBUG_CRASH(("No parking places!"));
  294. return false; // nothing available
  295. }
  296. }
  297. ppi->m_objectInSpace = id;
  298. ppi->m_reservedForExit = false;
  299. if( d->m_landingDeckHeightOffset )
  300. {
  301. Object *obj = TheGameLogic->findObjectByID( id );
  302. if( obj )
  303. {
  304. obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) );
  305. }
  306. }
  307. if (info)
  308. {
  309. calcPPInfo( id, info );
  310. if (parkingOffset != 0.0f)
  311. {
  312. info->parkingSpace.x += parkingOffset * Cos(ppi->m_orientation);
  313. info->parkingSpace.y += parkingOffset * Sin(ppi->m_orientation);
  314. }
  315. }
  316. ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface();
  317. if (pu)
  318. pu->setHoldDoorOpen(ppi->m_door, true);
  319. return true;
  320. }
  321. //-------------------------------------------------------------------------------------------------
  322. void ParkingPlaceBehavior::calcPPInfo( ObjectID id, PPInfo *info )
  323. {
  324. ParkingPlaceInfo* ppi = findPPI( id );
  325. if( !ppi )
  326. {
  327. //Utter failure.
  328. return;
  329. }
  330. const RunwayInfo& rr = m_runways[ ppi->m_runway ];
  331. if( info )
  332. {
  333. const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData();
  334. info->parkingSpace = d->m_parkInHangars ? ppi->m_hangarStart : ppi->m_location;
  335. info->runwayPrep = ppi->m_prep;
  336. info->parkingOrientation = d->m_parkInHangars ? ppi->m_hangarStartOrient : ppi->m_orientation;
  337. info->runwayStart = rr.m_start;
  338. info->runwayEnd = rr.m_end;
  339. info->runwayApproach = rr.m_end;
  340. //const Real APPROACH_DIST = 0.5f; // no, too short, planes may not have enough space to drop altitude
  341. const Real APPROACH_DIST = 0.75f;
  342. info->runwayApproach.x += (rr.m_end.x - rr.m_start.x) * APPROACH_DIST;
  343. info->runwayApproach.y += (rr.m_end.y - rr.m_start.y) * APPROACH_DIST;
  344. info->runwayApproach.z = rr.m_end.z + d->m_approachHeight + d->m_landingDeckHeightOffset;
  345. info->runwayExit = info->runwayApproach;
  346. info->hangarInternal = ppi->m_hangarStart;
  347. info->hangarInternalOrient = ppi->m_hangarStartOrient;
  348. //Cache the runway's takeoff distance used by JetAIUpdate for calculating lift.
  349. Coord3D vector = info->runwayStart;
  350. vector.sub( &info->runwayEnd );
  351. info->runwayTakeoffDist = vector.length();
  352. for (std::vector<RunwayInfo>::iterator it = m_runways.begin(); it != m_runways.end(); ++it)
  353. {
  354. if (it->m_inUseBy == id && it->m_wasInLine)
  355. {
  356. info->runwayStart = info->runwayPrep;
  357. }
  358. }
  359. }
  360. }
  361. //-------------------------------------------------------------------------------------------------
  362. void ParkingPlaceBehavior::releaseSpace(ObjectID id)
  363. {
  364. buildInfo();
  365. purgeDead();
  366. ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface();
  367. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  368. {
  369. if (it->m_objectInSpace == id)
  370. {
  371. it->m_objectInSpace = INVALID_ID;
  372. it->m_reservedForExit = false;
  373. if (pu)
  374. pu->setHoldDoorOpen(it->m_door, false);
  375. }
  376. }
  377. Object *obj = TheGameLogic->findObjectByID( id );
  378. if( obj )
  379. {
  380. obj->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) );
  381. }
  382. }
  383. //-------------------------------------------------------------------------------------------------
  384. ObjectID ParkingPlaceBehavior::getRunwayReservation( Int runway, RunwayReservationType type )
  385. {
  386. //Note: We don't care about type because these runways share the runway for taking off and landing.
  387. buildInfo();
  388. purgeDead();
  389. return m_runways[runway].m_inUseBy;
  390. }
  391. //-------------------------------------------------------------------------------------------------
  392. void ParkingPlaceBehavior::transferRunwayReservationToNextInLineForTakeoff(ObjectID id)
  393. {
  394. buildInfo();
  395. purgeDead();
  396. for (std::vector<RunwayInfo>::iterator it = m_runways.begin(); it != m_runways.end(); ++it)
  397. {
  398. if (it->m_inUseBy == id && it->m_nextInLineForTakeoff != INVALID_ID)
  399. {
  400. it->m_inUseBy = it->m_nextInLineForTakeoff;
  401. it->m_wasInLine = true;
  402. it->m_nextInLineForTakeoff = INVALID_ID;
  403. }
  404. }
  405. }
  406. //-------------------------------------------------------------------------------------------------
  407. Bool ParkingPlaceBehavior::reserveRunway(ObjectID id, Bool forLanding)
  408. {
  409. buildInfo();
  410. purgeDead();
  411. Int runway = -1;
  412. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  413. {
  414. if (it->m_objectInSpace == id)
  415. {
  416. runway = it->m_runway;
  417. break;
  418. }
  419. }
  420. if (runway == -1)
  421. {
  422. DEBUG_CRASH(("only planes with reserved spaces can reserve runways"));
  423. return false;
  424. }
  425. RunwayInfo& info = m_runways[runway];
  426. if (info.m_inUseBy == id)
  427. {
  428. return true;
  429. }
  430. else if (info.m_inUseBy == INVALID_ID)
  431. {
  432. info.m_inUseBy = id;
  433. if (info.m_nextInLineForTakeoff == id)
  434. {
  435. info.m_nextInLineForTakeoff = INVALID_ID;
  436. info.m_wasInLine = true;
  437. }
  438. else
  439. {
  440. info.m_wasInLine = false;
  441. }
  442. return true;
  443. }
  444. else if (!forLanding && info.m_nextInLineForTakeoff == INVALID_ID)
  445. {
  446. info.m_nextInLineForTakeoff = id;
  447. return false; // yes, that's right
  448. }
  449. return false;
  450. }
  451. //-------------------------------------------------------------------------------------------------
  452. void ParkingPlaceBehavior::releaseRunway(ObjectID id)
  453. {
  454. buildInfo();
  455. purgeDead();
  456. for (std::vector<RunwayInfo>::iterator it = m_runways.begin(); it != m_runways.end(); ++it)
  457. {
  458. if (it->m_inUseBy == id)
  459. {
  460. it->m_inUseBy = INVALID_ID;
  461. it->m_wasInLine = false;
  462. }
  463. if (it->m_nextInLineForTakeoff == id)
  464. {
  465. it->m_nextInLineForTakeoff = INVALID_ID;
  466. }
  467. }
  468. }
  469. //-------------------------------------------------------------------------------------------------
  470. // don't really need to autoheal every frame....
  471. const Int HEAL_RATE_FRAMES = LOGICFRAMES_PER_SECOND / 5;
  472. //-------------------------------------------------------------------------------------------------
  473. void ParkingPlaceBehavior::resetWakeFrame()
  474. {
  475. if (m_healing.empty())
  476. {
  477. m_nextHealFrame = FOREVER;
  478. }
  479. else
  480. {
  481. m_nextHealFrame = TheGameLogic->getFrame() + HEAL_RATE_FRAMES;
  482. }
  483. }
  484. //-------------------------------------------------------------------------------------------------
  485. void ParkingPlaceBehavior::setHealee(Object* healee, Bool add)
  486. {
  487. if (add)
  488. {
  489. for (std::list<HealingInfo>::const_iterator it = m_healing.begin(); it != m_healing.end(); ++it)
  490. {
  491. if (it->m_gettingHealedID == healee->getID())
  492. return;
  493. }
  494. HealingInfo info;
  495. info.m_gettingHealedID = healee->getID();
  496. info.m_healStartFrame = TheGameLogic->getFrame();
  497. m_healing.push_back(info);
  498. resetWakeFrame();
  499. }
  500. else
  501. {
  502. for (std::list<HealingInfo>::iterator it = m_healing.begin(); it != m_healing.end(); /*++it*/)
  503. {
  504. if (it->m_gettingHealedID == healee->getID())
  505. {
  506. it = m_healing.erase(it);
  507. resetWakeFrame();
  508. }
  509. else
  510. {
  511. ++it;
  512. }
  513. }
  514. }
  515. }
  516. //-------------------------------------------------------------------------------------------------
  517. void ParkingPlaceBehavior::defectAllParkedUnits(Team* newTeam, UnsignedInt detectionTime)
  518. {
  519. buildInfo();
  520. purgeDead();
  521. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  522. {
  523. if (it->m_objectInSpace != INVALID_ID)
  524. {
  525. Object* obj = TheGameLogic->findObjectByID(it->m_objectInSpace);
  526. if (obj == NULL || obj->isEffectivelyDead())
  527. continue;
  528. // srj sez: evil. fix better someday.
  529. static NameKeyType jetKey = TheNameKeyGenerator->nameToKey("JetAIUpdate");
  530. JetAIUpdate* ju = (JetAIUpdate *)obj->findUpdateModule(jetKey);
  531. Bool takeoffOrLanding = ju ? ju->friend_isTakeoffOrLandingInProgress() : false;
  532. if (obj->isAboveTerrain() && !takeoffOrLanding)
  533. {
  534. // if the new team is a different controlling player, this guys loses his space.
  535. if (newTeam->getControllingPlayer() != obj->getControllingPlayer())
  536. {
  537. releaseSpace(obj->getID());
  538. if (obj->getProducerID() == getObject()->getID())
  539. obj->setProducer(NULL);
  540. }
  541. }
  542. else
  543. {
  544. obj->defect(newTeam, detectionTime);
  545. }
  546. }
  547. }
  548. purgeDead();
  549. }
  550. //-------------------------------------------------------------------------------------------------
  551. void ParkingPlaceBehavior::killAllParkedUnits()
  552. {
  553. buildInfo();
  554. purgeDead();
  555. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  556. {
  557. if (it->m_objectInSpace != INVALID_ID)
  558. {
  559. Object* obj = TheGameLogic->findObjectByID(it->m_objectInSpace);
  560. if (obj == NULL || obj->isEffectivelyDead())
  561. continue;
  562. // srj sez: evil. fix better someday.
  563. static NameKeyType jetKey = TheNameKeyGenerator->nameToKey("JetAIUpdate");
  564. JetAIUpdate* ju = (JetAIUpdate *)obj->findUpdateModule(jetKey);
  565. Bool takeoffOrLanding = ju ? ju->friend_isTakeoffOrLandingInProgress() : false;
  566. if (obj->isAboveTerrain() && !takeoffOrLanding)
  567. continue;
  568. obj->kill();
  569. }
  570. }
  571. purgeDead();
  572. }
  573. //-------------------------------------------------------------------------------------------------
  574. void ParkingPlaceBehavior::onDie( const DamageInfo *damageInfo )
  575. {
  576. killAllParkedUnits();
  577. }
  578. //-------------------------------------------------------------------------------------------------
  579. UpdateSleepTime ParkingPlaceBehavior::update()
  580. {
  581. // alas, we need to keep the buildInfo and dead-purged stuff pretty much up to date, for
  582. // the client to be able to peek at. at this late date, the most expedient way is to ensure
  583. // our update is run every frame, and do this manually. the extra cost should be trivial, since
  584. // there are generally at most only a few airfields at any given time.
  585. buildInfo();
  586. purgeDead();
  587. UnsignedInt now = TheGameLogic->getFrame();
  588. if (now >= m_nextHealFrame)
  589. {
  590. m_nextHealFrame = now + HEAL_RATE_FRAMES;
  591. const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData();
  592. for (std::list<HealingInfo>::iterator it = m_healing.begin(); it != m_healing.end(); /*++it*/)
  593. {
  594. if (it->m_gettingHealedID != INVALID_ID)
  595. {
  596. Object* objToHeal = TheGameLogic->findObjectByID(it->m_gettingHealedID);
  597. if (objToHeal == NULL || objToHeal->isEffectivelyDead())
  598. {
  599. it = m_healing.erase(it);
  600. }
  601. else
  602. {
  603. DamageInfo healInfo;
  604. healInfo.in.m_damageType = DAMAGE_HEALING;
  605. healInfo.in.m_deathType = DEATH_NONE;
  606. healInfo.in.m_sourceID = getObject()->getID();
  607. healInfo.in.m_amount = HEAL_RATE_FRAMES * d->m_healAmount * SECONDS_PER_LOGICFRAME_REAL;
  608. // if ( objToHeal->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
  609. // healInfo.in.m_amount += HEAL_RATE_FRAMES * d->m_extraHealAmount4Helicopters * SECONDS_PER_LOGICFRAME_REAL;
  610. BodyModuleInterface *body = objToHeal->getBodyModule();
  611. body->attemptHealing( &healInfo );
  612. ++it;
  613. }
  614. }
  615. }
  616. }
  617. return UPDATE_SLEEP_NONE;
  618. }
  619. //-------------------------------------------------------------------------------------------------
  620. ExitDoorType ParkingPlaceBehavior::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
  621. {
  622. buildInfo();
  623. purgeDead();
  624. if (objType->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  625. {
  626. return DOOR_NONE_NEEDED;
  627. }
  628. // note that this never allocates a per-unit parking space, even if specificObject is null.
  629. // this is by design and is assumed by exitObjectViaDoor.
  630. ParkingPlaceInfo* ppi = findEmptyPPI();
  631. if (ppi)
  632. {
  633. ppi->m_objectInSpace = INVALID_ID;
  634. ppi->m_reservedForExit = true;
  635. return ppi->m_door;
  636. }
  637. return DOOR_NONE_AVAILABLE;
  638. }
  639. //-------------------------------------------------------------------------------------------------
  640. void ParkingPlaceBehavior::exitObjectViaDoor( Object *newObj, ExitDoorType exitDoor ) ///< Here is the thing I want you to exit
  641. {
  642. if (exitDoor != DOOR_NONE_NEEDED)
  643. {
  644. ParkingPlaceInfo* ppi = NULL;
  645. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  646. {
  647. if (it->m_objectInSpace == INVALID_ID && it->m_reservedForExit == TRUE && it->m_door == exitDoor)
  648. {
  649. ppi = &(*it);
  650. break;
  651. }
  652. }
  653. if (!ppi)
  654. {
  655. DEBUG_CRASH(("could not find the space. what?"));
  656. return;
  657. }
  658. ppi->m_objectInSpace = newObj->getID();
  659. ppi->m_reservedForExit = false;
  660. }
  661. /// @todo srj -- this is evil. fix.
  662. static NameKeyType jetKey = TheNameKeyGenerator->nameToKey( "JetAIUpdate" );
  663. JetAIUpdate* ju = (JetAIUpdate *)newObj->findUpdateModule( jetKey );
  664. Real parkingOffset = ju ? ju->friend_getParkingOffset() : 0.0f;
  665. Bool producedAtHelipad = newObj->isKindOf(KINDOF_PRODUCED_AT_HELIPAD);
  666. PPInfo ppinfo;
  667. DUMPMATRIX3D(getObject()->getTransformMatrix());
  668. DUMPCOORD3D(getObject()->getPosition());
  669. if (producedAtHelipad)
  670. {
  671. CRCDEBUG_LOG(("Produced at helipad (door = %d)\n", exitDoor));
  672. DEBUG_ASSERTCRASH(exitDoor == DOOR_NONE_NEEDED, ("Hmm, unlikely"));
  673. Matrix3D mtx;
  674. #ifdef DEBUG_CRASHING
  675. Bool boneOk =
  676. #endif
  677. getObject()->getSingleLogicalBonePosition("HeliPark01", &ppinfo.hangarInternal, &mtx);
  678. DEBUG_ASSERTCRASH(boneOk, ("Could not get bone!"));
  679. ppinfo.hangarInternalOrient = mtx.Get_Z_Rotation();
  680. ppinfo.parkingSpace = ppinfo.hangarInternal;
  681. ppinfo.parkingOrientation = ppinfo.hangarInternalOrient;
  682. }
  683. else
  684. {
  685. CRCDEBUG_LOG(("Produced at hangar (door = %d)\n", exitDoor));
  686. DEBUG_ASSERTCRASH(exitDoor != DOOR_NONE_NEEDED, ("Hmm, unlikely"));
  687. if (!reserveSpace(newObj->getID(), parkingOffset, &ppinfo)) //&loc, &orient, NULL, NULL, NULL, NULL, &hangarInternal, &hangOrient))
  688. {
  689. DEBUG_CRASH(("no spaces available, how did we get here?"));
  690. ppinfo.parkingSpace = *getObject()->getPosition();
  691. ppinfo.parkingOrientation = getObject()->getOrientation();
  692. }
  693. }
  694. DUMPCOORD3D(&ppinfo.hangarInternal);
  695. DUMPREAL(ppinfo.hangarInternalOrient);
  696. DUMPCOORD3D(&ppinfo.parkingSpace);
  697. DUMPREAL(ppinfo.parkingOrientation);
  698. newObj->setPosition( &ppinfo.hangarInternal );
  699. newObj->setOrientation( ppinfo.hangarInternalOrient );
  700. TheAI->pathfinder()->addObjectToPathfindMap( newObj );
  701. AIUpdateInterface *ai = newObj->getAIUpdateInterface();
  702. if( ai )
  703. {
  704. Bool movedToRallyPoint = FALSE;
  705. if( producedAtHelipad )
  706. {
  707. const Coord3D *rallyPoint = getRallyPoint();
  708. if( rallyPoint )
  709. {
  710. ai->aiMoveToPosition( rallyPoint, CMD_FROM_AI );
  711. movedToRallyPoint = TRUE;
  712. }
  713. }
  714. if( !movedToRallyPoint )
  715. {
  716. if( !newObj->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
  717. {
  718. std::vector<Coord3D> exitPath;
  719. exitPath.push_back(ppinfo.parkingSpace);
  720. ai->aiFollowExitProductionPath( &exitPath, getObject(), CMD_FROM_AI );
  721. }
  722. else
  723. {
  724. // Lorenzen sez: aiMoveToPosition has an added benefit.
  725. // It invokes the pathfinder to find a vacant destination.
  726. ai->aiMoveToPosition( &ppinfo.parkingSpace, CMD_FROM_AI );
  727. }
  728. }
  729. }
  730. }
  731. //-------------------------------------------------------------------------------------------------
  732. void ParkingPlaceBehavior::unreserveDoorForExit( ExitDoorType exitDoor )
  733. {
  734. if (exitDoor != DOOR_NONE_NEEDED)
  735. {
  736. for (std::vector<ParkingPlaceInfo>::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it)
  737. {
  738. if (it->m_door == exitDoor)
  739. {
  740. //DEBUG_ASSERTCRASH(it->m_reservedForExit, ("ParkingPlaceBehavior::unreserveDoorForExit: door %d was not reserved\n",exitDoor));
  741. it->m_objectInSpace = INVALID_ID;
  742. it->m_reservedForExit = false;
  743. return;
  744. }
  745. }
  746. DEBUG_CRASH(("ParkingPlaceBehavior::unreserveDoorForExit: door %d was not found\n",exitDoor));
  747. }
  748. }
  749. //-------------------------------------------------------------------------------------------------
  750. void ParkingPlaceBehavior::setRallyPoint( const Coord3D *pos )
  751. {
  752. m_heliRallyPointExists = TRUE;
  753. m_heliRallyPoint.set( pos );
  754. // nothing
  755. }
  756. //-------------------------------------------------------------------------------------------------
  757. const Coord3D* ParkingPlaceBehavior::getRallyPoint( void ) const
  758. {
  759. if( m_heliRallyPointExists )
  760. {
  761. return &m_heliRallyPoint;
  762. }
  763. return NULL;
  764. }
  765. //-------------------------------------------------------------------------------------------------
  766. //We only use this for the helipad -- therefore the exit position is the helipad creation point.
  767. //-------------------------------------------------------------------------------------------------
  768. Bool ParkingPlaceBehavior::getExitPosition( Coord3D& exitPosition ) const
  769. {
  770. Matrix3D mtx;
  771. return getObject()->getSingleLogicalBonePosition("HeliPark01", &exitPosition, &mtx );
  772. }
  773. //-------------------------------------------------------------------------------------------------
  774. Bool ParkingPlaceBehavior::getNaturalRallyPoint( Coord3D& rallyPoint, Bool offset ) const
  775. {
  776. Matrix3D mtx;
  777. return getObject()->getSingleLogicalBonePosition("HeliPark01", &rallyPoint, &mtx );
  778. }
  779. // ------------------------------------------------------------------------------------------------
  780. /** CRC */
  781. // ------------------------------------------------------------------------------------------------
  782. void ParkingPlaceBehavior::crc( Xfer *xfer )
  783. {
  784. // extend base class
  785. UpdateModule::crc( xfer );
  786. } // end crc
  787. // ------------------------------------------------------------------------------------------------
  788. /** Xfer method
  789. * Version Info:
  790. * 1: Initial version */
  791. // ------------------------------------------------------------------------------------------------
  792. void ParkingPlaceBehavior::xfer( Xfer *xfer )
  793. {
  794. Int i;
  795. // version
  796. const XferVersion currentVersion = 3;
  797. XferVersion version = currentVersion;
  798. xfer->xferVersion( &version, currentVersion );
  799. // extend base class
  800. UpdateModule::xfer( xfer );
  801. if( xfer->getXferMode() == XFER_LOAD )
  802. {
  803. // first, build our info, so it won't be overwritten later.
  804. buildInfo();
  805. }
  806. // spaces info count and data
  807. UnsignedByte spacesCount = m_spaces.size();
  808. xfer->xferUnsignedByte( &spacesCount );
  809. if( xfer->getXferMode() == XFER_SAVE )
  810. {
  811. // save all elements
  812. std::vector< ParkingPlaceInfo >::iterator it;
  813. for( it = m_spaces.begin(); it != m_spaces.end(); ++it )
  814. {
  815. // object in this space
  816. xfer->xferObjectID( &((*it).m_objectInSpace) );
  817. // reserved for exit
  818. xfer->xferBool( &((*it).m_reservedForExit) );
  819. } // end for, it
  820. } // end if, save
  821. else if( xfer->getXferMode() == XFER_LOAD )
  822. {
  823. ObjectID objectID;
  824. Bool reservedForExit;
  825. // read all elements
  826. std::vector< ParkingPlaceInfo >::iterator it;
  827. it = m_spaces.begin();
  828. for( i = 0; i < spacesCount; ++i )
  829. {
  830. // read object id
  831. xfer->xferObjectID( &objectID );
  832. // read reserved flag
  833. xfer->xferBool( &reservedForExit );
  834. // store in vector if the vector does indeed still have room for this entry
  835. if( it != m_spaces.end() )
  836. {
  837. (*it).m_objectInSpace = objectID;
  838. (*it).m_reservedForExit = reservedForExit;
  839. ++it;
  840. } // end if
  841. } // end for, i
  842. } // end else, load
  843. // runways cound and info
  844. UnsignedByte runwaysCount = m_runways.size();
  845. xfer->xferUnsignedByte( &runwaysCount );
  846. if( xfer->getXferMode() == XFER_SAVE )
  847. {
  848. // save all elements
  849. std::vector< RunwayInfo >::iterator it;
  850. for( it = m_runways.begin(); it != m_runways.end(); ++it )
  851. {
  852. // save object ID
  853. xfer->xferObjectID( &((*it).m_inUseBy) );
  854. xfer->xferObjectID( &((*it).m_nextInLineForTakeoff) );
  855. xfer->xferBool( &((*it).m_wasInLine) );
  856. } // end for, it
  857. } // end if, save
  858. else if( xfer->getXferMode() == XFER_LOAD )
  859. {
  860. // read all elements
  861. std::vector< RunwayInfo >::iterator it;
  862. it = m_runways.begin();
  863. for( i = 0; i < runwaysCount; ++i )
  864. {
  865. ObjectID inUseBy;
  866. ObjectID nextInLineForTakeoff;
  867. Bool wasInLine;
  868. // read object ID
  869. xfer->xferObjectID( &inUseBy );
  870. xfer->xferObjectID( &nextInLineForTakeoff );
  871. xfer->xferBool( &wasInLine );
  872. // store in vector if the vector does indeed still have room for this entry
  873. if( it != m_runways.end() )
  874. {
  875. (*it).m_inUseBy = inUseBy;
  876. (*it).m_nextInLineForTakeoff = nextInLineForTakeoff;
  877. (*it).m_wasInLine = wasInLine;
  878. ++it;
  879. } // end if
  880. } // end for, i
  881. } // end else, load
  882. // healees
  883. UnsignedByte healCount = m_healing.size();
  884. xfer->xferUnsignedByte( &healCount );
  885. if( xfer->getXferMode() == XFER_SAVE )
  886. {
  887. // save all elements
  888. std::list< HealingInfo >::iterator it;
  889. for( it = m_healing.begin(); it != m_healing.end(); ++it )
  890. {
  891. // save object ID
  892. xfer->xferObjectID( &((*it).m_gettingHealedID) );
  893. xfer->xferUnsignedInt( &((*it).m_healStartFrame) );
  894. } // end for, it
  895. } // end if, save
  896. else if( xfer->getXferMode() == XFER_LOAD )
  897. {
  898. // read all elements
  899. m_healing.clear();
  900. for( i = 0; i < healCount; ++i )
  901. {
  902. HealingInfo info;
  903. // read object ID
  904. xfer->xferObjectID( &info.m_gettingHealedID );
  905. xfer->xferUnsignedInt( &info.m_healStartFrame );
  906. m_healing.push_back(info);
  907. } // end for, i
  908. } // end else, load
  909. if (version >= 2)
  910. {
  911. xfer->xferCoord3D(&m_heliRallyPoint);
  912. xfer->xferBool(&m_heliRallyPointExists);
  913. }
  914. if (version >= 3)
  915. {
  916. xfer->xferUnsignedInt(&m_nextHealFrame);
  917. }
  918. else
  919. {
  920. // old file .. just wake it up right away, and it will fall into the correct pattern
  921. if (xfer->getXferMode() == XFER_LOAD)
  922. {
  923. m_nextHealFrame = 0;
  924. }
  925. }
  926. } // end xfer
  927. // ------------------------------------------------------------------------------------------------
  928. /** Load post process */
  929. // ------------------------------------------------------------------------------------------------
  930. void ParkingPlaceBehavior::loadPostProcess( void )
  931. {
  932. // extend base class
  933. UpdateModule::loadPostProcess();
  934. // no, this is bad.. it is NOT SAFE to call setWakeFrame from the xfer system. crap. (srj)
  935. // make sure we are awake... old save games let us sleep
  936. //setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  937. } // end loadPostProcess