ProductionUpdate.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407
  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: ProductionUpdate.cpp /////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, March 2002
  25. // Desc: This module allows things to be "constructed" from a building
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/BitFlagsIO.h"
  30. #include "Common/BuildAssistant.h"
  31. #include "Common/CRCDebug.h"
  32. #include "Common/GameState.h"
  33. #include "Common/Player.h"
  34. #include "Common/Radar.h"
  35. #include "Common/ThingFactory.h"
  36. #include "Common/ThingTemplate.h"
  37. #include "Common/Upgrade.h"
  38. #include "Common/Xfer.h"
  39. #include "Common/XferCRC.h"
  40. #include "GameClient/ControlBar.h"
  41. #include "GameClient/Drawable.h"
  42. #include "GameClient/Eva.h"
  43. #include "GameClient/GameText.h"
  44. #include "GameClient/InGameUI.h"
  45. #include "GameLogic/GameLogic.h"
  46. #include "GameLogic/Module/CreateModule.h"
  47. #include "GameLogic/Module/ParkingPlaceBehavior.h"
  48. #include "GameLogic/Module/ProductionUpdate.h"
  49. #include "GameLogic/Object.h"
  50. #include "GameLogic/ScriptEngine.h"
  51. #ifdef _INTERNAL
  52. // for occasional debugging...
  53. //#pragma optimize("", off)
  54. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  55. #endif
  56. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  57. static const ModelConditionFlagType theOpeningFlags[DOOR_COUNT_MAX] =
  58. {
  59. MODELCONDITION_DOOR_1_OPENING,
  60. MODELCONDITION_DOOR_2_OPENING,
  61. MODELCONDITION_DOOR_3_OPENING,
  62. MODELCONDITION_DOOR_4_OPENING
  63. };
  64. static const ModelConditionFlagType theClosingFlags[DOOR_COUNT_MAX] =
  65. {
  66. MODELCONDITION_DOOR_1_CLOSING,
  67. MODELCONDITION_DOOR_2_CLOSING,
  68. MODELCONDITION_DOOR_3_CLOSING,
  69. MODELCONDITION_DOOR_4_CLOSING
  70. };
  71. static const ModelConditionFlagType theWaitingOpenFlags[DOOR_COUNT_MAX] =
  72. {
  73. MODELCONDITION_DOOR_1_WAITING_OPEN,
  74. MODELCONDITION_DOOR_2_WAITING_OPEN,
  75. MODELCONDITION_DOOR_3_WAITING_OPEN,
  76. MODELCONDITION_DOOR_4_WAITING_OPEN
  77. };
  78. static const ModelConditionFlagType theWaitingToCloseFlags[DOOR_COUNT_MAX] =
  79. {
  80. MODELCONDITION_DOOR_1_WAITING_TO_CLOSE,
  81. MODELCONDITION_DOOR_2_WAITING_TO_CLOSE,
  82. MODELCONDITION_DOOR_3_WAITING_TO_CLOSE,
  83. MODELCONDITION_DOOR_4_WAITING_TO_CLOSE
  84. };
  85. //-------------------------------------------------------------------------------------------------
  86. //-------------------------------------------------------------------------------------------------
  87. ProductionUpdateModuleData::ProductionUpdateModuleData( void )
  88. {
  89. // someday, might need separate times for each door. but not yet.
  90. m_numDoorAnimations = 0;
  91. m_doorOpeningTime = 0;
  92. m_doorWaitOpenTime = 0;
  93. m_doorClosingTime = 0;
  94. m_constructionCompleteDuration = 0;
  95. m_quantityModifiers.clear();
  96. m_maxQueueEntries = 9;
  97. m_disabledTypesToProcess = MAKE_DISABLED_MASK(DISABLED_HELD);
  98. }
  99. //-------------------------------------------------------------------------------------------------
  100. /*static*/ void ProductionUpdateModuleData::parseAppendQuantityModifier( INI* ini, void *instance, void *store, const void* /*userData*/ )
  101. {
  102. ProductionUpdateModuleData* data = (ProductionUpdateModuleData*)instance;
  103. const char* name = ini->getNextToken();
  104. const char* countStr = ini->getNextTokenOrNull();
  105. Int count = countStr ? INI::scanInt(countStr) : 1;
  106. QuantityModifier qm;
  107. qm.m_quantity = count;
  108. qm.m_templateName.set( name );
  109. data->m_quantityModifiers.push_back( qm );
  110. }
  111. //-------------------------------------------------------------------------------------------------
  112. //-------------------------------------------------------------------------------------------------
  113. /*static*/ void ProductionUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  114. {
  115. UpdateModuleData::buildFieldParse( p );
  116. static const FieldParse dataFieldParse[] =
  117. {
  118. { "MaxQueueEntries", INI::parseInt, NULL, offsetof( ProductionUpdateModuleData, m_maxQueueEntries ) },
  119. { "NumDoorAnimations", INI::parseInt, NULL, offsetof( ProductionUpdateModuleData, m_numDoorAnimations ) },
  120. { "DoorOpeningTime", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_doorOpeningTime ) },
  121. { "DoorWaitOpenTime", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_doorWaitOpenTime ) },
  122. { "DoorCloseTime", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_doorClosingTime ) },
  123. { "ConstructionCompleteDuration", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_constructionCompleteDuration ) },
  124. { "QuantityModifier", parseAppendQuantityModifier, NULL, offsetof( ProductionUpdateModuleData, m_quantityModifiers ) },
  125. { "DisabledTypesToProcess", DisabledMaskType::parseFromINI, NULL, offsetof( ProductionUpdateModuleData, m_disabledTypesToProcess ) },
  126. { 0, 0, 0, 0 }
  127. };
  128. p.add(dataFieldParse);
  129. }
  130. ///////////////////////////////////////////////////////////////////////////////////////////////////
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  132. ///////////////////////////////////////////////////////////////////////////////////////////////////
  133. //-------------------------------------------------------------------------------------------------
  134. //-------------------------------------------------------------------------------------------------
  135. ProductionEntry::ProductionEntry( void )
  136. {
  137. m_type = PRODUCTION_INVALID;
  138. m_objectToProduce = NULL;
  139. m_upgradeToResearch = NULL;
  140. m_productionID = (ProductionID)1;
  141. m_percentComplete = 0.0f;
  142. m_framesUnderConstruction = 0;
  143. m_next = NULL;
  144. m_prev = NULL;
  145. //Added By Sadullah Nader
  146. //Initializations inserted
  147. m_productionQuantityProduced = 0;
  148. m_productionQuantityTotal = 0;
  149. //
  150. } // end ProductionEntry
  151. //-------------------------------------------------------------------------------------------------
  152. //-------------------------------------------------------------------------------------------------
  153. ProductionEntry::~ProductionEntry( void )
  154. {
  155. } // end ~ProductionEntry
  156. ///////////////////////////////////////////////////////////////////////////////////////////////////
  157. ///////////////////////////////////////////////////////////////////////////////////////////////////
  158. ///////////////////////////////////////////////////////////////////////////////////////////////////
  159. //-------------------------------------------------------------------------------------------------
  160. //-------------------------------------------------------------------------------------------------
  161. ProductionUpdate::ProductionUpdate( Thing *thing, const ModuleData* moduleData ) :
  162. UpdateModule( thing, moduleData )
  163. {
  164. m_productionQueue = NULL;
  165. m_productionQueueTail = NULL;
  166. m_productionCount = 0;
  167. m_uniqueID = (ProductionID)1;
  168. for (Int i = 0; i < DOOR_COUNT_MAX; ++i)
  169. {
  170. m_doors[i].m_doorOpenedFrame = 0;
  171. m_doors[i].m_doorWaitOpenFrame = 0;
  172. m_doors[i].m_doorClosedFrame = 0;
  173. m_doors[i].m_holdOpen = false;
  174. }
  175. m_constructionCompleteFrame = 0;
  176. m_clearFlags.clear();
  177. m_setFlags.clear();
  178. m_flagsDirty = FALSE;
  179. m_specialPowerConstructionCommandButton = NULL;
  180. } // end ProductionUpdate
  181. //-------------------------------------------------------------------------------------------------
  182. //-------------------------------------------------------------------------------------------------
  183. ProductionUpdate::~ProductionUpdate( void )
  184. {
  185. // destroy any queued productions
  186. ProductionEntry *production;
  187. while( m_productionQueue )
  188. {
  189. production = m_productionQueue;
  190. removeFromProductionQueue( production );
  191. } // end while
  192. } // end ~ProductionUpdate
  193. //-------------------------------------------------------------------------------------------------
  194. //-------------------------------------------------------------------------------------------------
  195. CanMakeType ProductionUpdate::canQueueUpgrade( const UpgradeTemplate *upgrade ) const
  196. {
  197. if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
  198. return CANMAKE_QUEUE_FULL;
  199. return CANMAKE_OK;
  200. }
  201. //-------------------------------------------------------------------------------------------------
  202. //-------------------------------------------------------------------------------------------------
  203. CanMakeType ProductionUpdate::canQueueCreateUnit( const ThingTemplate *unitType ) const
  204. {
  205. /// @todo srj -- this is horrible, but the "right" way to do it is to move
  206. // ProductionUpdate to be part of ParkingPlaceBehavior, which I don't currently
  207. // have time for...
  208. ParkingPlaceBehaviorInterface* pp = NULL;
  209. for (BehaviorModule** i = getObject()->getBehaviorModules(); *i; ++i)
  210. {
  211. if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
  212. {
  213. if (pp->shouldReserveDoorWhenQueued(unitType) && !pp->hasAvailableSpaceFor(unitType))
  214. return CANMAKE_PARKING_PLACES_FULL;
  215. }
  216. }
  217. if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
  218. return CANMAKE_QUEUE_FULL;
  219. return CANMAKE_OK;
  220. }
  221. //-------------------------------------------------------------------------------------------------
  222. /** Queue an upgrade to be produced */
  223. //-------------------------------------------------------------------------------------------------
  224. Bool ProductionUpdate::queueUpgrade( const UpgradeTemplate *upgrade )
  225. {
  226. // sanity
  227. if( upgrade == NULL )
  228. return FALSE;
  229. // get the player
  230. Player *player = getObject()->getControllingPlayer();
  231. // sanity check to make sure we can build this upgrade
  232. if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER &&
  233. TheUpgradeCenter->canAffordUpgrade( player, upgrade ) == FALSE )
  234. return FALSE;
  235. else if( upgrade->getUpgradeType() == UPGRADE_TYPE_OBJECT &&
  236. (getObject()->hasUpgrade( upgrade ) == TRUE ||
  237. getObject()->affectedByUpgrade( upgrade ) == FALSE) )
  238. return FALSE;
  239. // you cannot queue the production of an upgrade twice in this queue
  240. if( isUpgradeInQueue( upgrade ) == TRUE )
  241. return FALSE;
  242. // STOP cheaters by making sure they can actually build this
  243. if( !getObject()->canProduceUpgrade(upgrade) )
  244. return FALSE;
  245. //
  246. // you cannot queue a player upgrade production if you are producing one already somewhere else
  247. // (or that somewhere else could even possibly be here)
  248. //
  249. if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER &&
  250. (player->hasUpgradeComplete( upgrade ) || player->hasUpgradeInProduction( upgrade )) )
  251. return FALSE;
  252. if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
  253. {
  254. DEBUG_CRASH(("Production Queue is full... how did we get here?"));
  255. return FALSE;
  256. }
  257. // take the cost for the build away from the player
  258. Money *money = player->getMoney();
  259. money->withdraw( upgrade->calcCostToBuild( player ) );
  260. // allocate a new production entry
  261. ProductionEntry *production = newInstance(ProductionEntry);
  262. // assing production entry data
  263. production->m_type = PRODUCTION_UPGRADE;
  264. production->m_upgradeToResearch = upgrade;
  265. production->m_productionID = PRODUCTIONID_INVALID; // not needed for upgrades, you can only have one of
  266. // this type in the queue
  267. // tie to the end of the production queue
  268. addToProductionQueue( production );
  269. // add this upgrade as in progress in the player
  270. player->addUpgrade( upgrade, UPGRADE_STATUS_IN_PRODUCTION );
  271. return TRUE; // queued
  272. } // end queueUpgrade
  273. //-------------------------------------------------------------------------------------------------
  274. /** Cancel an upgrade being produced here */
  275. //-------------------------------------------------------------------------------------------------
  276. void ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade )
  277. {
  278. // sanity
  279. if( upgrade == NULL )
  280. return;
  281. // get the player
  282. Player *player = getObject()->getControllingPlayer();
  283. // sanity, you can't cancel it if the player isn't actually building one
  284. if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER && player->hasUpgradeInProduction( upgrade ) == FALSE )
  285. return;
  286. //
  287. // find the production entry for this upgrade in the queue here, there can only be one
  288. // of this type in the queue
  289. //
  290. ProductionEntry *production;
  291. for( production = m_productionQueue; production; production = production->m_next )
  292. {
  293. if( production->m_type == PRODUCTION_UPGRADE &&
  294. production->m_upgradeToResearch == upgrade )
  295. break;
  296. } // end for
  297. // sanity, entry not found
  298. if( production == NULL )
  299. return;
  300. // refund money back to the player
  301. Money *money = player->getMoney();
  302. money->deposit( production->m_upgradeToResearch->calcCostToBuild( player ) );
  303. // remove this production from the queue
  304. removeFromProductionQueue( production );
  305. // delete production instance
  306. production->deleteInstance();
  307. //
  308. // remove the IN_PRODUCTION status of this upgrade from the player, object upgrades don't
  309. // have any other IN_PRODUCTION status other than their existence in the build queue
  310. //
  311. if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER )
  312. player->removeUpgrade( upgrade );
  313. } // end cancelUpgrade
  314. //-------------------------------------------------------------------------------------------------
  315. /** Queue the prodcution of a unit. Returns TRUE if unit was added to queue, FALSE if it
  316. * was not */
  317. //-------------------------------------------------------------------------------------------------
  318. Bool ProductionUpdate::queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID )
  319. {
  320. const ProductionUpdateModuleData *data = getProductionUpdateModuleData();
  321. // if we can't create the unit do nothing
  322. if( TheBuildAssistant->canMakeUnit( getObject(), unitType ) != CANMAKE_OK )
  323. return FALSE;
  324. ExitDoorType exitDoor = DOOR_NONE_AVAILABLE;
  325. /// @todo srj -- this is horrible, but the "right" way to do it is to move
  326. // ProductionUpdate to be part of ParkingPlaceBehavior, which I don't currently
  327. // have time for...
  328. ParkingPlaceBehaviorInterface* pp = NULL;
  329. for (BehaviorModule** i = getObject()->getBehaviorModules(); *i; ++i)
  330. {
  331. if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
  332. {
  333. if (pp->shouldReserveDoorWhenQueued(unitType))
  334. {
  335. ExitInterface* exitInterface = getObject()->getObjectExitInterface();
  336. if (exitInterface)
  337. {
  338. exitDoor = exitInterface->reserveDoorForExit(unitType, NULL);
  339. }
  340. if (exitDoor == DOOR_NONE_AVAILABLE)
  341. {
  342. return FALSE;
  343. }
  344. break;
  345. }
  346. }
  347. }
  348. if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
  349. {
  350. DEBUG_CRASH(("Production Queue is full... how did we get here?"));
  351. return FALSE;
  352. }
  353. // take the cost for the build away from the player
  354. Player *player = getObject()->getControllingPlayer();
  355. Money *money = player->getMoney();
  356. money->withdraw( unitType->calcCostToBuild( player ) );
  357. // allocate a new production entry
  358. ProductionEntry *production = newInstance(ProductionEntry);
  359. // Check to see if there is a quantity modifier entry. What this means is if we are building a particular unit
  360. // and it has a modifier, we build that number of objects instead of just one. A good example is the Chinese
  361. // barracks building redguards -- they are built 4 at a time, but the user or script only pays for one and
  362. // builds four for that price!
  363. production->m_productionQuantityTotal = 1;
  364. production->m_productionQuantityProduced = 0;
  365. for( std::vector<QuantityModifier>::const_iterator it = data->m_quantityModifiers.begin(); it != data->m_quantityModifiers.end(); ++it )
  366. {
  367. const ThingTemplate* productionTemplate = TheThingFactory->findTemplate( it->m_templateName );
  368. if( productionTemplate && productionTemplate->isEquivalentTo( unitType ) )
  369. {
  370. production->m_productionQuantityTotal = it->m_quantity;
  371. break;
  372. }
  373. }
  374. // assing production entry data
  375. production->m_type = PRODUCTION_UNIT;
  376. production->m_objectToProduce = unitType;
  377. production->m_productionID = productionID;
  378. production->m_exitDoor = exitDoor;
  379. // tie to the end of the production queue
  380. addToProductionQueue( production );
  381. return TRUE; // unit queued
  382. } // end queueMakeUnit
  383. //-------------------------------------------------------------------------------------------------
  384. /** Cancel the construction of the unit with the matching production ID */
  385. //-------------------------------------------------------------------------------------------------
  386. void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
  387. {
  388. // search for the production entry in our queue
  389. ProductionEntry *production;
  390. for( production = m_productionQueue; production; production = production->m_next )
  391. {
  392. // are we at the one we want get rid of it
  393. if( production->m_productionID == productionID )
  394. {
  395. // give the player the cost of the object back
  396. Player *player = getObject()->getControllingPlayer();
  397. Money *money = player->getMoney();
  398. money->deposit( production->m_objectToProduce->calcCostToBuild( player ) );
  399. // remove from queue list
  400. removeFromProductionQueue( production );
  401. // delete the production entry
  402. production->deleteInstance();
  403. return;
  404. } // end if
  405. } // end for
  406. } // end cancelUnitCreate
  407. //-------------------------------------------------------------------------------------------------
  408. /** Cancel all production of type unitType */
  409. //-------------------------------------------------------------------------------------------------
  410. void ProductionUpdate::cancelAllUnitsOfType( const ThingTemplate *unitType)
  411. {
  412. // search for the production entry in our queue
  413. ProductionEntry *production;
  414. for( production = m_productionQueue; production; )
  415. {
  416. // are we at the one we want get rid of it
  417. if( production->getProductionType() == PRODUCTION_UNIT &&
  418. production->getProductionObject() == unitType )
  419. {
  420. ProductionEntry *temp = production->m_next;
  421. // cancel the production
  422. cancelUnitCreate( production->getProductionID() );
  423. // advance
  424. production = temp;
  425. } // end if
  426. else
  427. {
  428. // advance
  429. production = production->m_next;
  430. } // end else
  431. } // end for
  432. } // end cancelAllUnitsOfType
  433. //-------------------------------------------------------------------------------------------------
  434. /** Update the door behavior */
  435. //-------------------------------------------------------------------------------------------------
  436. void ProductionUpdate::updateDoors()
  437. {
  438. const ProductionUpdateModuleData *d = getProductionUpdateModuleData();
  439. UnsignedInt now = TheGameLogic->getFrame();
  440. for (Int i = 0; i < DOOR_COUNT_MAX; ++i)
  441. {
  442. //
  443. // if we have an open door, see if enough time has passed for us to close it ... likewise
  444. // if we were closing a door we will clear the closing condition after an amount of time
  445. // has passed
  446. //
  447. if( m_doors[i].m_doorOpenedFrame )
  448. {
  449. if( now - m_doors[i].m_doorOpenedFrame > d->m_doorOpeningTime )
  450. {
  451. // set our frame markers for door states
  452. m_doors[i].m_doorOpenedFrame = 0;
  453. m_doors[i].m_doorWaitOpenFrame = now;
  454. // set the flags that will show the difference in the model
  455. m_clearFlags.set( theOpeningFlags[i], true );
  456. m_setFlags.set( theOpeningFlags[i], false );
  457. m_setFlags.set( theWaitingOpenFlags[i] );
  458. m_flagsDirty = TRUE;
  459. } // end if
  460. } // end if
  461. else if( m_doors[i].m_doorWaitOpenFrame )
  462. {
  463. if( now - m_doors[i].m_doorWaitOpenFrame > d->m_doorWaitOpenTime && !m_doors[i].m_holdOpen )
  464. {
  465. // set our frame marker for closing
  466. m_doors[i].m_doorWaitOpenFrame = 0;
  467. m_doors[i].m_doorClosedFrame = now;
  468. // set the flags that show the difference in the art
  469. m_clearFlags.set( theWaitingOpenFlags[i], true );
  470. m_setFlags.set( theWaitingOpenFlags[i], false );
  471. m_setFlags.set( theClosingFlags[i] );
  472. m_flagsDirty = TRUE;
  473. } // end if
  474. } // end if
  475. else if( m_doors[i].m_doorClosedFrame && !m_doors[i].m_holdOpen )
  476. {
  477. if( now - m_doors[i].m_doorClosedFrame > d->m_doorClosingTime )
  478. {
  479. // set our frame marker for the closed door frame
  480. m_doors[i].m_doorClosedFrame = 0;
  481. // set the flags that will show the difference in the model
  482. m_clearFlags.set( theClosingFlags[i], true );
  483. m_setFlags.set( theClosingFlags[i], false );
  484. m_flagsDirty = TRUE;
  485. } // end if
  486. } // end else if
  487. }
  488. }
  489. //-------------------------------------------------------------------------------------------------
  490. //-------------------------------------------------------------------------------------------------
  491. UpdateSleepTime ProductionUpdate::update( void )
  492. {
  493. /// @todo srj use SLEEPY_UPDATE here
  494. ProductionEntry *production = m_productionQueue;
  495. const ProductionUpdateModuleData *d = getProductionUpdateModuleData();
  496. UnsignedInt now = TheGameLogic->getFrame();
  497. Object *us = getObject();
  498. // update the door behaviors
  499. if( d->m_numDoorAnimations > 0 )
  500. updateDoors();
  501. //
  502. // if we're in the construction complete state ... we need to check frame
  503. // we're in it and get out of it when the time is up
  504. //
  505. if( m_constructionCompleteFrame )
  506. {
  507. if( now - m_constructionCompleteFrame > d->m_constructionCompleteDuration )
  508. {
  509. // set our frame marker to be out of construction complete state
  510. m_constructionCompleteFrame = 0;
  511. // set the flags that show the difference in the model
  512. m_clearFlags.set( MODELCONDITION_CONSTRUCTION_COMPLETE, true );
  513. m_setFlags.set( MODELCONDITION_CONSTRUCTION_COMPLETE, false );
  514. m_flagsDirty = TRUE;
  515. } // end if
  516. } // end if
  517. // if we have dirty bits that need to be set and cleared, do it here all at once
  518. if( m_flagsDirty == TRUE )
  519. {
  520. Drawable *draw = us->getDrawable();
  521. if( draw )
  522. draw->clearAndSetModelConditionFlags( m_clearFlags, m_setFlags );
  523. // clear the things we just set and turn off our dirty flag
  524. m_clearFlags.clear();
  525. m_setFlags.clear();
  526. m_flagsDirty = FALSE;
  527. } // end if
  528. // if nothing in the queue get outta here
  529. if( production == NULL )
  530. return UPDATE_SLEEP_NONE;
  531. //
  532. // if we've become OBJECT_STATUS_SOLD, halt all production ... leave things in the
  533. // queue because if the sell process completes we get money back for them, for now we
  534. // will be just frozen in time
  535. // Actually, there will be nothing in the queue since everything gets cancel/refunded
  536. // at the start of sell, but we still don't want to do anything here.
  537. //
  538. if( us->getStatusBits().test( OBJECT_STATUS_SOLD ) )
  539. return UPDATE_SLEEP_NONE;
  540. // get the player that is building this thing
  541. Player *player = us->getControllingPlayer();
  542. // sanity
  543. if( player == NULL )
  544. {
  545. // remove from queue list
  546. removeFromProductionQueue( production );
  547. // delete the production entry
  548. production->deleteInstance();
  549. return UPDATE_SLEEP_NONE;
  550. } // end if
  551. //
  552. // you can disallow types of units on the fly in scripts, so if there is something
  553. // in the queue that suddenly can't be build then get rid of it
  554. //
  555. if( production->getProductionType() == PRODUCTION_UNIT &&
  556. player->allowedToBuild( production->getProductionObject() ) == FALSE )
  557. {
  558. // Don't cancel dozers in the queue. jba.
  559. if (!production->getProductionObject()->isKindOf(KINDOF_DOZER))
  560. {
  561. cancelUnitCreate(production->getProductionID());
  562. return UPDATE_SLEEP_NONE;
  563. } // end if
  564. } // end if
  565. // increase the frames we've been under production for
  566. production->m_framesUnderConstruction++;
  567. // how many total logic frames does it take to produce this unit
  568. Int totalProductionFrames;
  569. if( production->m_type == PRODUCTION_UNIT )
  570. totalProductionFrames = production->m_objectToProduce->calcTimeToBuild( player );
  571. else
  572. totalProductionFrames = production->m_upgradeToResearch->calcTimeToBuild( player );
  573. // figure out our percent complete
  574. production->m_percentComplete = INT_TO_REAL( production->m_framesUnderConstruction ) /
  575. INT_TO_REAL( totalProductionFrames ) *
  576. 100.0f;
  577. // if we've reached 100% or more we're done, tada!
  578. if( production->m_percentComplete >= 100.0f )
  579. {
  580. // handle unit production items
  581. if( production->m_type == PRODUCTION_UNIT )
  582. {
  583. Object *creationBuilding = us;
  584. // create this unit
  585. //if I've got a ExitModule
  586. ExitInterface *exitInterface = creationBuilding->getObjectExitInterface();
  587. if( exitInterface )
  588. {
  589. //
  590. // only exit if the exit interface will let us ... if we can't we'll just keep this
  591. // unit in the production and keep "overbuilding" it, every frame we will check to see
  592. // if we can exit and then move onto production of the next one
  593. //
  594. Int numberToTry = production->getProductionQuantityRemaining();// this will change midloop, so it can't be the For clause
  595. for( int i = 0; i < numberToTry; i++ )
  596. {
  597. ExitDoorType exitDoor = production->getExitDoor();
  598. if (exitDoor == DOOR_NONE_AVAILABLE)
  599. {
  600. exitDoor = exitInterface->reserveDoorForExit(production->m_objectToProduce, NULL);
  601. production->setExitDoor(exitDoor);
  602. }
  603. if (exitDoor != DOOR_NONE_AVAILABLE)
  604. {
  605. // note, could be DOOR_NONE_NEEDED! so door could be null. (srj)
  606. DoorInfo* door = (exitDoor >= 0 && exitDoor < DOOR_COUNT_MAX) ? &m_doors[exitDoor] : NULL;
  607. //
  608. // if the producing structure has a door opening animation we will set the condition
  609. // bits to open the door (note that we must also take care to remove any condition
  610. // that had us previously closing a door)
  611. //
  612. const ProductionUpdateModuleData *d = getProductionUpdateModuleData();
  613. if( d->m_numDoorAnimations > 0 && door != NULL )
  614. {
  615. // if the door is closed, open it
  616. if( door->m_doorOpenedFrame == 0 && door->m_doorWaitOpenFrame == 0 && door->m_doorClosedFrame == 0 )
  617. {
  618. door->m_doorOpenedFrame = now;
  619. m_setFlags.set( theOpeningFlags[exitDoor] );
  620. m_flagsDirty = TRUE;
  621. } // end if
  622. // if the door is waiting-open, keep it there
  623. else if( door->m_doorWaitOpenFrame != 0 )
  624. {
  625. door->m_doorWaitOpenFrame = now;
  626. } // end else if
  627. // if the door is closing, for now, pop it to waiting open
  628. else if( door->m_doorClosedFrame != 0 )
  629. {
  630. door->m_doorWaitOpenFrame = now;
  631. m_clearFlags.set( theOpeningFlags[exitDoor], true );
  632. m_clearFlags.set( theClosingFlags[exitDoor], true );
  633. m_setFlags.set( theOpeningFlags[exitDoor], false );
  634. m_setFlags.set( theClosingFlags[exitDoor], false );
  635. m_setFlags.set( theWaitingOpenFlags[exitDoor] );
  636. m_flagsDirty = TRUE;
  637. } // end else
  638. } // end if, has door animation
  639. // we now go into the construction complete condition for a while
  640. if( m_constructionCompleteFrame == 0 )
  641. {
  642. m_constructionCompleteFrame = now;
  643. m_setFlags.set( MODELCONDITION_CONSTRUCTION_COMPLETE );
  644. m_flagsDirty = TRUE;
  645. } // end if
  646. //
  647. // make a new object, note that for production buildings that have door
  648. // animations we will not make the object until the door has been totally
  649. // opened
  650. //
  651. if( d->m_numDoorAnimations == 0 || door == NULL || door->m_doorWaitOpenFrame != 0 )
  652. {
  653. Object *newObj = TheThingFactory->newObject( production->m_objectToProduce,
  654. creationBuilding->getControllingPlayer()->getDefaultTeam() );
  655. newObj->setProducer(creationBuilding);
  656. // call the exit interface to do the rally point and position stuff
  657. exitInterface->exitObjectViaDoor( newObj, exitDoor );
  658. // since we successfully exited via this door, we should NOT call unreserveDoorForExit
  659. // when we toss this production entry. so set it back to an innocuous value.
  660. production->setExitDoor(DOOR_NONE_AVAILABLE);
  661. // Now, play the sound associated with the new object's production.
  662. AudioEventRTS voiceCreate = *newObj->getTemplate()->getVoiceCreated();
  663. voiceCreate.setObjectID(newObj->getID());
  664. TheAudio->addAudioEvent(&voiceCreate);
  665. // call the onUnitCreated for the player
  666. creationBuilding->getControllingPlayer()->onUnitCreated( creationBuilding, newObj );
  667. // onCreates have been called on newObj, and after that the owner was set,
  668. // so now is the time to call the game side of CreateModules
  669. for (BehaviorModule** m = newObj->getBehaviorModules(); *m; ++m)
  670. {
  671. CreateModuleInterface* create = (*m)->getCreate();
  672. if (!create)
  673. continue;
  674. create->onBuildComplete();
  675. }
  676. if( production->getProductionQuantity() == production->getProductionQuantityRemaining() )
  677. {
  678. //Call the voice created for the 1st object -- because it's possible to create multiple objects like redguards!
  679. AudioEventRTS sound = *newObj->getTemplate()->getPerUnitSound( "VoiceCreate" );
  680. sound.setObjectID( newObj->getID() );
  681. TheAudio->addAudioEvent( &sound );
  682. }
  683. creationBuilding->getControllingPlayer()->getAcademyStats()->recordProduction( newObj, creationBuilding );
  684. //We created one guy, but we may want to do more so we should stay in this node of production.
  685. // This is last so the voice check can easily check for "first" guy
  686. production->oneProductionSuccessful();
  687. } // end if, door open or no door animation ... make the object
  688. } // end, if we got a door reservation
  689. } //end of trying to exit all the things we were planning on attempting
  690. if( production->getProductionQuantityRemaining() == 0 )
  691. {
  692. // remove this production entry so we can go on to the next if we are totally finished
  693. removeFromProductionQueue( production );
  694. // delete the production entry
  695. production->deleteInstance();
  696. }
  697. } // end if we found an exit interface
  698. else
  699. {
  700. // there is no exit interface, this is an error
  701. DEBUG_ASSERTCRASH( 0, ("Cannot create '%s', there is no ExitUpdate interface defined for producer object '%s'\n",
  702. production->m_objectToProduce->getName().str(),
  703. creationBuilding->getTemplate()->getName().str()) );
  704. // remove this item from the production queue
  705. removeFromProductionQueue( production );
  706. // delete the production entry
  707. production->deleteInstance();
  708. } // end else
  709. } // end if, production unit
  710. else if( production->m_type == PRODUCTION_UPGRADE )
  711. {
  712. const UpgradeTemplate *upgrade = production->m_upgradeToResearch;
  713. // we finished an upgrade, lets add that money spent on it to the scorekeeper
  714. player->getScoreKeeper()->addMoneySpent(upgrade->calcCostToBuild(player));
  715. // notify the script engine
  716. TheScriptEngine->notifyOfCompletedUpgrade(
  717. us->getControllingPlayer()->getPlayerIndex(),
  718. upgrade->getUpgradeName(),
  719. us->getID());
  720. // print a message to the local player, if it wants one
  721. if( us->isLocallyControlled() && !upgrade->getDisplayNameLabel().isEmpty() )
  722. {
  723. UnicodeString msg;
  724. UnicodeString format = TheGameText->fetch( "UPGRADE:UpgradeComplete" );
  725. UnicodeString upgradeName = TheGameText->fetch( upgrade->getDisplayNameLabel().str() );
  726. msg.format( format.str(), upgradeName.str() );
  727. TheInGameUI->message( msg );
  728. // upgrades are a more rare event, play a nifty radar event thingie
  729. TheRadar->createEvent( us->getPosition(), RADAR_EVENT_UPGRADE );
  730. //Play the sound for the upgrade, because we just built it!
  731. AudioEventRTS sound = *upgrade->getResearchCompleteSound();
  732. if( TheAudio->isValidAudioEvent( &sound ) )
  733. {
  734. //We have a custom upgrade complete sound.
  735. sound.setObjectID( us->getID() );
  736. TheAudio->addAudioEvent( &sound );
  737. }
  738. else
  739. {
  740. //Use a generic EVA event.
  741. TheEva->setShouldPlay(EVA_UpgradeComplete);
  742. }
  743. //Play any available unit specific sound for upgrade.
  744. sound = *upgrade->getUnitSpecificSound();
  745. sound.setObjectID( us->getID() );
  746. TheAudio->addAudioEvent( &sound );
  747. } // end if
  748. // update the upgrade status in the player or give the upgrade to the object
  749. if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER )
  750. {
  751. player->addUpgrade( upgrade, UPGRADE_STATUS_COMPLETE );
  752. }
  753. else
  754. {
  755. us->giveUpgrade( upgrade );
  756. }
  757. player->getAcademyStats()->recordUpgrade( upgrade, FALSE );
  758. //Also mark the UI dirty -- incase object with upgrade cameo is selected.
  759. Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
  760. Object *selectedObject = draw ? draw->getObject() : NULL;
  761. if( selectedObject )
  762. {
  763. const ThingTemplate *thing = selectedObject->getTemplate();
  764. for( Int i = 0; i < MAX_UPGRADE_CAMEO_UPGRADES; i++ )
  765. {
  766. AsciiString upgradeName = thing->getUpgradeCameoName( i );
  767. const UpgradeTemplate *testUpgrade = TheUpgradeCenter->findUpgrade( upgradeName );
  768. if( testUpgrade == upgrade )
  769. {
  770. //Our selected object has the upgrade
  771. TheControlBar->markUIDirty();
  772. break;
  773. }
  774. }
  775. }
  776. // remove this production entry so we can go on to the next
  777. removeFromProductionQueue( production );
  778. // delete the production entry
  779. production->deleteInstance();
  780. } // end else, production upgrade
  781. } // end if, production is 100% complete
  782. return UPDATE_SLEEP_NONE;
  783. } // end update
  784. //-------------------------------------------------------------------------------------------------
  785. /** Add the production entry to the *END* of the production queue list */
  786. //-------------------------------------------------------------------------------------------------
  787. void ProductionUpdate::addToProductionQueue( ProductionEntry *production )
  788. {
  789. // check for empty list
  790. if( m_productionQueue == NULL )
  791. m_productionQueue = production;
  792. // make any existing tail pointer now point to us, and we point back to them
  793. if( m_productionQueueTail )
  794. {
  795. m_productionQueueTail->m_next = production;
  796. production->m_prev = m_productionQueueTail;
  797. } // end if
  798. // this production entry is now the new tail at the end of the list
  799. m_productionQueueTail = production;
  800. // we now have one more production item
  801. ++m_productionCount;
  802. // when something is in the queue ... we are in the actively constructing state
  803. Drawable *draw = getObject()->getDrawable();
  804. if( draw )
  805. {
  806. ModelConditionFlags condition = draw->getModelConditionFlags();
  807. if( condition.test( MODELCONDITION_ACTIVELY_CONSTRUCTING ) == FALSE )
  808. {
  809. m_setFlags.set( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  810. m_flagsDirty = TRUE;
  811. } // end if
  812. } // end if
  813. } // end addToProductionQueue
  814. //-------------------------------------------------------------------------------------------------
  815. /** Remove the production entry from the production queue list */
  816. //-------------------------------------------------------------------------------------------------
  817. void ProductionUpdate::removeFromProductionQueue( ProductionEntry *production )
  818. {
  819. if (production->m_type == PRODUCTION_UNIT &&
  820. production->m_exitDoor != DOOR_NONE_AVAILABLE)
  821. {
  822. ExitInterface* exitInterface = getObject()->getObjectExitInterface();
  823. if (exitInterface)
  824. {
  825. exitInterface->unreserveDoorForExit(production->m_exitDoor);
  826. }
  827. }
  828. // detach prev pointer, keep head pointer to the whole queue in tact
  829. if( production->m_prev )
  830. production->m_prev->m_next = production->m_next;
  831. else
  832. m_productionQueue = production->m_next;
  833. // detach next pointer, keep tail poitner to the whole queue in tact
  834. if( production->m_next )
  835. production->m_next->m_prev = production->m_prev;
  836. else
  837. m_productionQueueTail = production->m_prev;
  838. // we now have one less production item
  839. --m_productionCount;
  840. // when we go back to zero things in our queue, we clear the constructing state
  841. Drawable *draw = getObject()->getDrawable();
  842. if( draw )
  843. {
  844. ModelConditionFlags condition = draw->getModelConditionFlags();
  845. if( m_productionCount == 0 && condition.test( MODELCONDITION_ACTIVELY_CONSTRUCTING ) == TRUE )
  846. {
  847. m_clearFlags.set( MODELCONDITION_ACTIVELY_CONSTRUCTING, true );
  848. m_setFlags.set( MODELCONDITION_ACTIVELY_CONSTRUCTING, false );
  849. m_flagsDirty = TRUE;
  850. } // end if
  851. } // end if
  852. /*
  853. // Debugging
  854. UnicodeString msg;
  855. if( production->getProductionType() == ProductionEntry::PRODUCTION_UNIT )
  856. {
  857. msg.format( L"Removed unit '%S'(%d)", production->getProductionObject()->getName().str(),
  858. production->getProductionID() );
  859. TheInGameUI->message( msg );
  860. }
  861. else
  862. {
  863. msg.format( L"Remove upgrade '%S'", production->getProductionUpgrade()->getName().str() );
  864. TheInGameUI->message( msg );
  865. }
  866. */
  867. } // end removeFromProductionQueue
  868. //-------------------------------------------------------------------------------------------------
  869. /** Is the upgrade already in the production queue. Note that you can only have one
  870. * production entry for any given upgrade in the queue */
  871. //-------------------------------------------------------------------------------------------------
  872. Bool ProductionUpdate::isUpgradeInQueue( const UpgradeTemplate *upgrade ) const
  873. {
  874. const ProductionEntry *production;
  875. for( production = firstProduction(); production; production = nextProduction( production ) )
  876. if( production->getProductionType() == PRODUCTION_UPGRADE &&
  877. production->getProductionUpgrade() == upgrade )
  878. return TRUE;
  879. return FALSE; // not in queue
  880. } // end isUpgradeInQueue
  881. // ------------------------------------------------------------------------------------------------
  882. /** count number of units with matching unit type in the production queue */
  883. // ------------------------------------------------------------------------------------------------
  884. UnsignedInt ProductionUpdate::countUnitTypeInQueue( const ThingTemplate *unitType ) const
  885. {
  886. UnsignedInt count = 0;
  887. const ProductionEntry *production;
  888. for( production = firstProduction(); production; production = nextProduction( production ) )
  889. if( production->getProductionType() == PRODUCTION_UNIT &&
  890. production->getProductionObject() == unitType )
  891. count++;
  892. return count;
  893. } // end countUnitTypeInQueue
  894. // ------------------------------------------------------------------------------------------------
  895. // ------------------------------------------------------------------------------------------------
  896. void ProductionUpdate::onDie( const DamageInfo *damageInfo )
  897. {
  898. // we need to cancel all of our production on death
  899. cancelAndRefundAllProduction();
  900. }
  901. // ------------------------------------------------------------------------------------------------
  902. /** Cancel each of the production items in the queue. By going through the actual cancel
  903. * methods, the cost of each of those items will be refunded to the player */
  904. // ------------------------------------------------------------------------------------------------
  905. void ProductionUpdate::cancelAndRefundAllProduction( void )
  906. {
  907. // Empirically, in release the code can loop forever. So we limit to 100 passes. jba. [8/31/2003]
  908. const Int productionLimit = 100;// With luck, we never queue up 100 units. [8/31/2003]
  909. Int i;
  910. for (i=0; i<productionLimit; i++)
  911. {
  912. // iterate through our production queue
  913. if( m_productionQueue )
  914. {
  915. if( m_productionQueue->getProductionType() == PRODUCTION_UNIT )
  916. cancelUnitCreate( m_productionQueue->getProductionID() );
  917. else if( m_productionQueue->getProductionType() == PRODUCTION_UPGRADE )
  918. cancelUpgrade( m_productionQueue->getProductionUpgrade() );
  919. else
  920. {
  921. // unknown production type
  922. DEBUG_CRASH(( "ProductionUpdate::cancelAndRefundAllProduction - Unknown production type '%d'\n", m_productionQueue->getProductionType() ));
  923. return;
  924. } // end else
  925. } // end if
  926. }
  927. } // end cancelAndRefundAllProduction
  928. // ------------------------------------------------------------------------------------------------
  929. // ------------------------------------------------------------------------------------------------
  930. void ProductionUpdate::setHoldDoorOpen(ExitDoorType exitDoor, Bool holdIt)
  931. {
  932. if (exitDoor >= DOOR_1 && exitDoor < DOOR_COUNT_MAX)
  933. {
  934. DoorInfo& door = m_doors[exitDoor];
  935. door.m_holdOpen = holdIt;
  936. if (holdIt && door.m_doorOpenedFrame == 0 && door.m_doorWaitOpenFrame == 0 && door.m_doorClosedFrame == 0)
  937. {
  938. door.m_doorOpenedFrame = TheGameLogic->getFrame();
  939. m_setFlags.set( theOpeningFlags[exitDoor] );
  940. m_flagsDirty = TRUE;
  941. }
  942. }
  943. }
  944. // ------------------------------------------------------------------------------------------------
  945. /** Helper method to retrieve a production update interface from an object if one is present */
  946. // ------------------------------------------------------------------------------------------------
  947. /*static*/ ProductionUpdateInterface *ProductionUpdate::getProductionUpdateInterfaceFromObject( Object *obj )
  948. {
  949. // sanity
  950. if( obj == NULL )
  951. return NULL;
  952. BehaviorModule **bmi;
  953. ProductionUpdateInterface *pui = NULL;
  954. for( bmi = obj->getBehaviorModules(); *bmi; ++bmi )
  955. {
  956. pui = (*bmi)->getProductionUpdateInterface();
  957. if( pui )
  958. return pui;
  959. } // end for, bmi
  960. // interface not found
  961. return NULL;
  962. } // end getProductionUpdateInterfaceFromObject
  963. // ------------------------------------------------------------------------------------------------
  964. /** CRC */
  965. // ------------------------------------------------------------------------------------------------
  966. void ProductionUpdate::crc( Xfer *xfer )
  967. {
  968. // extend base class
  969. UpdateModule::crc( xfer );
  970. } // end crc
  971. // ------------------------------------------------------------------------------------------------
  972. /** Xfer method
  973. * Version Info:
  974. * 1: Initial version */
  975. // ------------------------------------------------------------------------------------------------
  976. void ProductionUpdate::xfer( Xfer *xfer )
  977. {
  978. // version
  979. XferVersion currentVersion = 1;
  980. XferVersion version = currentVersion;
  981. xfer->xferVersion( &version, currentVersion );
  982. // extend base class
  983. UpdateModule::xfer( xfer );
  984. // production queue count
  985. ProductionEntry *production;
  986. UnsignedShort productionCount = 0;
  987. for( production = m_productionQueue; production; production = production->m_next )
  988. productionCount++;
  989. xfer->xferUnsignedShort( &productionCount );
  990. // production queue data
  991. if( xfer->getXferMode() == XFER_SAVE )
  992. {
  993. AsciiString name;
  994. // write all queue data
  995. for( production = m_productionQueue; production; production = production->m_next )
  996. {
  997. // type
  998. xfer->xferUser( &production->m_type, sizeof( ProductionType ) );
  999. // thing/upgrade template name
  1000. if( production->m_type == PRODUCTION_UNIT )
  1001. name = production->m_objectToProduce->getName();
  1002. else
  1003. name = production->m_upgradeToResearch->getUpgradeName();
  1004. xfer->xferAsciiString( &name );
  1005. // production ID
  1006. xfer->xferUser( &production->m_productionID, sizeof( ProductionID ) );
  1007. // percent complete
  1008. xfer->xferReal( &production->m_percentComplete );
  1009. // frames under construction
  1010. xfer->xferInt( &production->m_framesUnderConstruction );
  1011. // production quantity
  1012. xfer->xferInt( &production->m_productionQuantityTotal );
  1013. // production quantity in progress
  1014. xfer->xferInt( &production->m_productionQuantityProduced );
  1015. // exit door
  1016. xfer->xferInt( (Int*)&production->m_exitDoor );
  1017. } // end for
  1018. } // end if, save
  1019. else
  1020. {
  1021. AsciiString name;
  1022. // the queue should be emtpy now
  1023. if( m_productionQueue != NULL )
  1024. {
  1025. DEBUG_CRASH(( "ProductionUpdate::xfer - m_productionQueue is not empty, but should be\n" ));
  1026. throw SC_INVALID_DATA;
  1027. } // end if
  1028. // read each element
  1029. for( UnsignedShort i = 0; i < productionCount; ++i )
  1030. {
  1031. // allocate new production entry
  1032. production = newInstance(ProductionEntry);
  1033. // tie to list at end
  1034. if( m_productionQueue == NULL )
  1035. m_productionQueue = production;
  1036. // make any existing tail pointer now point to us, and we point back to them
  1037. if( m_productionQueueTail )
  1038. {
  1039. m_productionQueueTail->m_next = production;
  1040. production->m_prev = m_productionQueueTail;
  1041. } // end if
  1042. // this production entry is now the new tail at the end of the list
  1043. m_productionQueueTail = production;
  1044. // type
  1045. xfer->xferUser( &production->m_type, sizeof( ProductionType ) );
  1046. // thing/upgrade template name
  1047. xfer->xferAsciiString( &name );
  1048. if( production->m_type == PRODUCTION_UNIT )
  1049. {
  1050. production->m_objectToProduce = TheThingFactory->findTemplate( name );
  1051. if( production->m_objectToProduce == NULL )
  1052. {
  1053. DEBUG_CRASH(( "ProductionUpdate::xfer - Cannot find template '%s'\n", name.str() ));
  1054. throw SC_INVALID_DATA;
  1055. } // end if
  1056. } // end if, unit production
  1057. else
  1058. {
  1059. production->m_upgradeToResearch = TheUpgradeCenter->findUpgrade( name );
  1060. if( production->m_upgradeToResearch == NULL )
  1061. {
  1062. DEBUG_CRASH(( "ProductionUpdate::xfer - Cannot find upgrade '%s'\n", name.str() ));
  1063. throw SC_INVALID_DATA;
  1064. } // end if
  1065. } // end else, upgrade production
  1066. // production ID
  1067. xfer->xferUser( &production->m_productionID, sizeof( ProductionID ) );
  1068. // percent complete
  1069. xfer->xferReal( &production->m_percentComplete );
  1070. // frames under construction
  1071. xfer->xferInt( &production->m_framesUnderConstruction );
  1072. // production quantity
  1073. xfer->xferInt( &production->m_productionQuantityTotal );
  1074. // production quantity in progress
  1075. xfer->xferInt( &production->m_productionQuantityProduced );
  1076. // exit door
  1077. xfer->xferInt( (Int*)&production->m_exitDoor );
  1078. } // end for, i
  1079. } // end else, load
  1080. // unique id
  1081. xfer->xferUser( &m_uniqueID, sizeof( ProductionID ) );
  1082. // production count
  1083. xfer->xferUnsignedInt( &m_productionCount );
  1084. // construction complete frame
  1085. xfer->xferUnsignedInt( &m_constructionCompleteFrame );
  1086. // door info
  1087. xfer->xferUser( &m_doors, sizeof( DoorInfo ) * DOOR_COUNT_MAX );
  1088. // clear flags
  1089. m_clearFlags.xfer( xfer );
  1090. // set flags
  1091. m_setFlags.xfer( xfer );
  1092. // flags dirty
  1093. xfer->xferBool( &m_flagsDirty );
  1094. } // end xfer
  1095. // ------------------------------------------------------------------------------------------------
  1096. /** Load post process */
  1097. // ------------------------------------------------------------------------------------------------
  1098. void ProductionUpdate::loadPostProcess( void )
  1099. {
  1100. // extend base class
  1101. UpdateModule::loadPostProcess();
  1102. } // end loadPostProcess