ProductionUpdate.cpp 46 KB

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