| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: ProductionUpdate.cpp /////////////////////////////////////////////////////////////////////
- // Author: Colin Day, March 2002
- // Desc: This module allows things to be "constructed" from a building
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/BitFlagsIO.h"
- #include "Common/BuildAssistant.h"
- #include "Common/CRCDebug.h"
- #include "Common/GameState.h"
- #include "Common/Player.h"
- #include "Common/Radar.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Upgrade.h"
- #include "Common/Xfer.h"
- #include "Common/XferCRC.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/Eva.h"
- #include "GameClient/GameText.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Module/CreateModule.h"
- #include "GameLogic/Module/ParkingPlaceBehavior.h"
- #include "GameLogic/Module/ProductionUpdate.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/ScriptEngine.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
- static const ModelConditionFlagType theOpeningFlags[DOOR_COUNT_MAX] =
- {
- MODELCONDITION_DOOR_1_OPENING,
- MODELCONDITION_DOOR_2_OPENING,
- MODELCONDITION_DOOR_3_OPENING,
- MODELCONDITION_DOOR_4_OPENING
- };
- static const ModelConditionFlagType theClosingFlags[DOOR_COUNT_MAX] =
- {
- MODELCONDITION_DOOR_1_CLOSING,
- MODELCONDITION_DOOR_2_CLOSING,
- MODELCONDITION_DOOR_3_CLOSING,
- MODELCONDITION_DOOR_4_CLOSING
- };
- static const ModelConditionFlagType theWaitingOpenFlags[DOOR_COUNT_MAX] =
- {
- MODELCONDITION_DOOR_1_WAITING_OPEN,
- MODELCONDITION_DOOR_2_WAITING_OPEN,
- MODELCONDITION_DOOR_3_WAITING_OPEN,
- MODELCONDITION_DOOR_4_WAITING_OPEN
- };
- static const ModelConditionFlagType theWaitingToCloseFlags[DOOR_COUNT_MAX] =
- {
- MODELCONDITION_DOOR_1_WAITING_TO_CLOSE,
- MODELCONDITION_DOOR_2_WAITING_TO_CLOSE,
- MODELCONDITION_DOOR_3_WAITING_TO_CLOSE,
- MODELCONDITION_DOOR_4_WAITING_TO_CLOSE
- };
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ProductionUpdateModuleData::ProductionUpdateModuleData( void )
- {
- // someday, might need separate times for each door. but not yet.
- m_numDoorAnimations = 0;
- m_doorOpeningTime = 0;
- m_doorWaitOpenTime = 0;
- m_doorClosingTime = 0;
- m_constructionCompleteDuration = 0;
- m_quantityModifiers.clear();
- m_maxQueueEntries = 9;
- m_disabledTypesToProcess = MAKE_DISABLED_MASK(DISABLED_HELD);
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void ProductionUpdateModuleData::parseAppendQuantityModifier( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- ProductionUpdateModuleData* data = (ProductionUpdateModuleData*)instance;
- const char* name = ini->getNextToken();
- const char* countStr = ini->getNextTokenOrNull();
- Int count = countStr ? INI::scanInt(countStr) : 1;
- QuantityModifier qm;
- qm.m_quantity = count;
- qm.m_templateName.set( name );
- data->m_quantityModifiers.push_back( qm );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /*static*/ void ProductionUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- UpdateModuleData::buildFieldParse( p );
- static const FieldParse dataFieldParse[] =
- {
- { "MaxQueueEntries", INI::parseInt, NULL, offsetof( ProductionUpdateModuleData, m_maxQueueEntries ) },
- { "NumDoorAnimations", INI::parseInt, NULL, offsetof( ProductionUpdateModuleData, m_numDoorAnimations ) },
- { "DoorOpeningTime", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_doorOpeningTime ) },
- { "DoorWaitOpenTime", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_doorWaitOpenTime ) },
- { "DoorCloseTime", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_doorClosingTime ) },
- { "ConstructionCompleteDuration", INI::parseDurationUnsignedInt, NULL, offsetof( ProductionUpdateModuleData, m_constructionCompleteDuration ) },
- { "QuantityModifier", parseAppendQuantityModifier, NULL, offsetof( ProductionUpdateModuleData, m_quantityModifiers ) },
- { "DisabledTypesToProcess", DisabledMaskType::parseFromINI, NULL, offsetof( ProductionUpdateModuleData, m_disabledTypesToProcess ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ProductionEntry::ProductionEntry( void )
- {
- m_type = PRODUCTION_INVALID;
- m_objectToProduce = NULL;
- m_upgradeToResearch = NULL;
- m_productionID = (ProductionID)1;
- m_percentComplete = 0.0f;
- m_framesUnderConstruction = 0;
- m_next = NULL;
- m_prev = NULL;
- //Added By Sadullah Nader
- //Initializations inserted
- m_productionQuantityProduced = 0;
- m_productionQuantityTotal = 0;
- //
- } // end ProductionEntry
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ProductionEntry::~ProductionEntry( void )
- {
- } // end ~ProductionEntry
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ProductionUpdate::ProductionUpdate( Thing *thing, const ModuleData* moduleData ) :
- UpdateModule( thing, moduleData )
- {
- m_productionQueue = NULL;
- m_productionQueueTail = NULL;
- m_productionCount = 0;
- m_uniqueID = (ProductionID)1;
- for (Int i = 0; i < DOOR_COUNT_MAX; ++i)
- {
- m_doors[i].m_doorOpenedFrame = 0;
- m_doors[i].m_doorWaitOpenFrame = 0;
- m_doors[i].m_doorClosedFrame = 0;
- m_doors[i].m_holdOpen = false;
- }
- m_constructionCompleteFrame = 0;
- m_clearFlags.clear();
- m_setFlags.clear();
- m_flagsDirty = FALSE;
- m_specialPowerConstructionCommandButton = NULL;
- } // end ProductionUpdate
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ProductionUpdate::~ProductionUpdate( void )
- {
- // destroy any queued productions
- ProductionEntry *production;
- while( m_productionQueue )
- {
- production = m_productionQueue;
- removeFromProductionQueue( production );
- } // end while
- } // end ~ProductionUpdate
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- CanMakeType ProductionUpdate::canQueueUpgrade( const UpgradeTemplate *upgrade ) const
- {
- if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
- return CANMAKE_QUEUE_FULL;
- return CANMAKE_OK;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- CanMakeType ProductionUpdate::canQueueCreateUnit( const ThingTemplate *unitType ) const
- {
- /// @todo srj -- this is horrible, but the "right" way to do it is to move
- // ProductionUpdate to be part of ParkingPlaceBehavior, which I don't currently
- // have time for...
- ParkingPlaceBehaviorInterface* pp = NULL;
- for (BehaviorModule** i = getObject()->getBehaviorModules(); *i; ++i)
- {
- if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
- {
- if (pp->shouldReserveDoorWhenQueued(unitType) && !pp->hasAvailableSpaceFor(unitType))
- return CANMAKE_PARKING_PLACES_FULL;
- }
- }
- if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
- return CANMAKE_QUEUE_FULL;
- return CANMAKE_OK;
- }
- //-------------------------------------------------------------------------------------------------
- /** Queue an upgrade to be produced */
- //-------------------------------------------------------------------------------------------------
- Bool ProductionUpdate::queueUpgrade( const UpgradeTemplate *upgrade )
- {
- // sanity
- if( upgrade == NULL )
- return FALSE;
- // get the player
- Player *player = getObject()->getControllingPlayer();
- // sanity check to make sure we can build this upgrade
- if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER &&
- TheUpgradeCenter->canAffordUpgrade( player, upgrade ) == FALSE )
- return FALSE;
- else if( upgrade->getUpgradeType() == UPGRADE_TYPE_OBJECT &&
- (getObject()->hasUpgrade( upgrade ) == TRUE ||
- getObject()->affectedByUpgrade( upgrade ) == FALSE) )
- return FALSE;
- // you cannot queue the production of an upgrade twice in this queue
- if( isUpgradeInQueue( upgrade ) == TRUE )
- return FALSE;
-
- // STOP cheaters by making sure they can actually build this
- if( !getObject()->canProduceUpgrade(upgrade) )
- return FALSE;
- //
- // you cannot queue a player upgrade production if you are producing one already somewhere else
- // (or that somewhere else could even possibly be here)
- //
- if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER &&
- (player->hasUpgradeComplete( upgrade ) || player->hasUpgradeInProduction( upgrade )) )
- return FALSE;
- if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
- {
- DEBUG_CRASH(("Production Queue is full... how did we get here?"));
- return FALSE;
- }
- // take the cost for the build away from the player
- Money *money = player->getMoney();
- money->withdraw( upgrade->calcCostToBuild( player ) );
- // allocate a new production entry
- ProductionEntry *production = newInstance(ProductionEntry);
- // assing production entry data
- production->m_type = PRODUCTION_UPGRADE;
- production->m_upgradeToResearch = upgrade;
- production->m_productionID = PRODUCTIONID_INVALID; // not needed for upgrades, you can only have one of
- // this type in the queue
- // tie to the end of the production queue
- addToProductionQueue( production );
- // add this upgrade as in progress in the player
- player->addUpgrade( upgrade, UPGRADE_STATUS_IN_PRODUCTION );
-
- return TRUE; // queued
- } // end queueUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Cancel an upgrade being produced here */
- //-------------------------------------------------------------------------------------------------
- void ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade )
- {
- // sanity
- if( upgrade == NULL )
- return;
- // get the player
- Player *player = getObject()->getControllingPlayer();
- // sanity, you can't cancel it if the player isn't actually building one
- if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER && player->hasUpgradeInProduction( upgrade ) == FALSE )
- return;
- //
- // find the production entry for this upgrade in the queue here, there can only be one
- // of this type in the queue
- //
- ProductionEntry *production;
- for( production = m_productionQueue; production; production = production->m_next )
- {
- if( production->m_type == PRODUCTION_UPGRADE &&
- production->m_upgradeToResearch == upgrade )
- break;
- } // end for
- // sanity, entry not found
- if( production == NULL )
- return;
- // refund money back to the player
- Money *money = player->getMoney();
- money->deposit( production->m_upgradeToResearch->calcCostToBuild( player ) );
- // remove this production from the queue
- removeFromProductionQueue( production );
- // delete production instance
- production->deleteInstance();
- //
- // remove the IN_PRODUCTION status of this upgrade from the player, object upgrades don't
- // have any other IN_PRODUCTION status other than their existence in the build queue
- //
- if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER )
- player->removeUpgrade( upgrade );
-
- } // end cancelUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Queue the prodcution of a unit. Returns TRUE if unit was added to queue, FALSE if it
- * was not */
- //-------------------------------------------------------------------------------------------------
- Bool ProductionUpdate::queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID )
- {
- const ProductionUpdateModuleData *data = getProductionUpdateModuleData();
- // if we can't create the unit do nothing
- if( TheBuildAssistant->canMakeUnit( getObject(), unitType ) != CANMAKE_OK )
- return FALSE;
- ExitDoorType exitDoor = DOOR_NONE_AVAILABLE;
- /// @todo srj -- this is horrible, but the "right" way to do it is to move
- // ProductionUpdate to be part of ParkingPlaceBehavior, which I don't currently
- // have time for...
- ParkingPlaceBehaviorInterface* pp = NULL;
- for (BehaviorModule** i = getObject()->getBehaviorModules(); *i; ++i)
- {
- if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
- {
- if (pp->shouldReserveDoorWhenQueued(unitType))
- {
- ExitInterface* exitInterface = getObject()->getObjectExitInterface();
- if (exitInterface)
- {
- exitDoor = exitInterface->reserveDoorForExit(unitType, NULL);
- }
- if (exitDoor == DOOR_NONE_AVAILABLE)
- {
- return FALSE;
- }
- break;
- }
- }
- }
- if (m_productionCount >= getProductionUpdateModuleData()->m_maxQueueEntries)
- {
- DEBUG_CRASH(("Production Queue is full... how did we get here?"));
- return FALSE;
- }
- // take the cost for the build away from the player
- Player *player = getObject()->getControllingPlayer();
- Money *money = player->getMoney();
- money->withdraw( unitType->calcCostToBuild( player ) );
- // allocate a new production entry
- ProductionEntry *production = newInstance(ProductionEntry);
- // Check to see if there is a quantity modifier entry. What this means is if we are building a particular unit
- // and it has a modifier, we build that number of objects instead of just one. A good example is the Chinese
- // barracks building redguards -- they are built 4 at a time, but the user or script only pays for one and
- // builds four for that price!
- production->m_productionQuantityTotal = 1;
- production->m_productionQuantityProduced = 0;
- for( std::vector<QuantityModifier>::const_iterator it = data->m_quantityModifiers.begin(); it != data->m_quantityModifiers.end(); ++it )
- {
- const ThingTemplate* productionTemplate = TheThingFactory->findTemplate( it->m_templateName );
- if( productionTemplate && productionTemplate->isEquivalentTo( unitType ) )
- {
- production->m_productionQuantityTotal = it->m_quantity;
- break;
- }
- }
-
- // assing production entry data
- production->m_type = PRODUCTION_UNIT;
- production->m_objectToProduce = unitType;
- production->m_productionID = productionID;
- production->m_exitDoor = exitDoor;
- // tie to the end of the production queue
- addToProductionQueue( production );
- return TRUE; // unit queued
- } // end queueMakeUnit
- //-------------------------------------------------------------------------------------------------
- /** Cancel the construction of the unit with the matching production ID */
- //-------------------------------------------------------------------------------------------------
- void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
- {
- // search for the production entry in our queue
- ProductionEntry *production;
- for( production = m_productionQueue; production; production = production->m_next )
- {
- // are we at the one we want get rid of it
- if( production->m_productionID == productionID )
- {
- // give the player the cost of the object back
- Player *player = getObject()->getControllingPlayer();
- Money *money = player->getMoney();
- money->deposit( production->m_objectToProduce->calcCostToBuild( player ) );
- // remove from queue list
- removeFromProductionQueue( production );
- // delete the production entry
- production->deleteInstance();
- return;
- } // end if
- } // end for
- } // end cancelUnitCreate
- //-------------------------------------------------------------------------------------------------
- /** Cancel all production of type unitType */
- //-------------------------------------------------------------------------------------------------
- void ProductionUpdate::cancelAllUnitsOfType( const ThingTemplate *unitType)
- {
- // search for the production entry in our queue
- ProductionEntry *production;
- for( production = m_productionQueue; production; )
- {
- // are we at the one we want get rid of it
- if( production->getProductionType() == PRODUCTION_UNIT &&
- production->getProductionObject() == unitType )
- {
- ProductionEntry *temp = production->m_next;
- // cancel the production
- cancelUnitCreate( production->getProductionID() );
-
- // advance
- production = temp;
- } // end if
- else
- {
- // advance
- production = production->m_next;
- } // end else
- } // end for
- } // end cancelAllUnitsOfType
- //-------------------------------------------------------------------------------------------------
- /** Update the door behavior */
- //-------------------------------------------------------------------------------------------------
- void ProductionUpdate::updateDoors()
- {
- const ProductionUpdateModuleData *d = getProductionUpdateModuleData();
- UnsignedInt now = TheGameLogic->getFrame();
- for (Int i = 0; i < DOOR_COUNT_MAX; ++i)
- {
- //
- // if we have an open door, see if enough time has passed for us to close it ... likewise
- // if we were closing a door we will clear the closing condition after an amount of time
- // has passed
- //
- if( m_doors[i].m_doorOpenedFrame )
- {
- if( now - m_doors[i].m_doorOpenedFrame > d->m_doorOpeningTime )
- {
-
- // set our frame markers for door states
- m_doors[i].m_doorOpenedFrame = 0;
- m_doors[i].m_doorWaitOpenFrame = now;
- // set the flags that will show the difference in the model
- m_clearFlags.set( theOpeningFlags[i], true );
- m_setFlags.set( theOpeningFlags[i], false );
- m_setFlags.set( theWaitingOpenFlags[i] );
- m_flagsDirty = TRUE;
- } // end if
- } // end if
- else if( m_doors[i].m_doorWaitOpenFrame )
- {
- if( now - m_doors[i].m_doorWaitOpenFrame > d->m_doorWaitOpenTime && !m_doors[i].m_holdOpen )
- {
- // set our frame marker for closing
- m_doors[i].m_doorWaitOpenFrame = 0;
- m_doors[i].m_doorClosedFrame = now;
- // set the flags that show the difference in the art
- m_clearFlags.set( theWaitingOpenFlags[i], true );
- m_setFlags.set( theWaitingOpenFlags[i], false );
- m_setFlags.set( theClosingFlags[i] );
- m_flagsDirty = TRUE;
- } // end if
- } // end if
- else if( m_doors[i].m_doorClosedFrame && !m_doors[i].m_holdOpen )
- {
- if( now - m_doors[i].m_doorClosedFrame > d->m_doorClosingTime )
- {
- // set our frame marker for the closed door frame
- m_doors[i].m_doorClosedFrame = 0;
- // set the flags that will show the difference in the model
- m_clearFlags.set( theClosingFlags[i], true );
- m_setFlags.set( theClosingFlags[i], false );
- m_flagsDirty = TRUE;
- } // end if
- } // end else if
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime ProductionUpdate::update( void )
- {
- /// @todo srj use SLEEPY_UPDATE here
- ProductionEntry *production = m_productionQueue;
- const ProductionUpdateModuleData *d = getProductionUpdateModuleData();
- UnsignedInt now = TheGameLogic->getFrame();
- Object *us = getObject();
- // update the door behaviors
- if( d->m_numDoorAnimations > 0 )
- updateDoors();
- //
- // if we're in the construction complete state ... we need to check frame
- // we're in it and get out of it when the time is up
- //
- if( m_constructionCompleteFrame )
- {
-
- if( now - m_constructionCompleteFrame > d->m_constructionCompleteDuration )
- {
- // set our frame marker to be out of construction complete state
- m_constructionCompleteFrame = 0;
- // set the flags that show the difference in the model
- m_clearFlags.set( MODELCONDITION_CONSTRUCTION_COMPLETE, true );
- m_setFlags.set( MODELCONDITION_CONSTRUCTION_COMPLETE, false );
- m_flagsDirty = TRUE;
- } // end if
- } // end if
- // if we have dirty bits that need to be set and cleared, do it here all at once
- if( m_flagsDirty == TRUE )
- {
- Drawable *draw = us->getDrawable();
- if( draw )
- draw->clearAndSetModelConditionFlags( m_clearFlags, m_setFlags );
- // clear the things we just set and turn off our dirty flag
- m_clearFlags.clear();
- m_setFlags.clear();
- m_flagsDirty = FALSE;
- } // end if
- // if nothing in the queue get outta here
- if( production == NULL )
- return UPDATE_SLEEP_NONE;
- //
- // if we've become OBJECT_STATUS_SOLD, halt all production ... leave things in the
- // queue because if the sell process completes we get money back for them, for now we
- // will be just frozen in time
- // Actually, there will be nothing in the queue since everything gets cancel/refunded
- // at the start of sell, but we still don't want to do anything here.
- //
- if( us->getStatusBits().test( OBJECT_STATUS_SOLD ) )
- return UPDATE_SLEEP_NONE;
- // get the player that is building this thing
- Player *player = us->getControllingPlayer();
- // sanity
- if( player == NULL )
- {
- // remove from queue list
- removeFromProductionQueue( production );
- // delete the production entry
- production->deleteInstance();
- return UPDATE_SLEEP_NONE;
- } // end if
- //
- // you can disallow types of units on the fly in scripts, so if there is something
- // in the queue that suddenly can't be build then get rid of it
- //
- if( production->getProductionType() == PRODUCTION_UNIT &&
- player->allowedToBuild( production->getProductionObject() ) == FALSE )
- {
-
- // Don't cancel dozers in the queue. jba.
- if (!production->getProductionObject()->isKindOf(KINDOF_DOZER))
- {
- cancelUnitCreate(production->getProductionID());
- return UPDATE_SLEEP_NONE;
-
- } // end if
- } // end if
- // increase the frames we've been under production for
- production->m_framesUnderConstruction++;
- // how many total logic frames does it take to produce this unit
- Int totalProductionFrames;
- if( production->m_type == PRODUCTION_UNIT )
- totalProductionFrames = production->m_objectToProduce->calcTimeToBuild( player );
- else
- totalProductionFrames = production->m_upgradeToResearch->calcTimeToBuild( player );
- // figure out our percent complete
- production->m_percentComplete = INT_TO_REAL( production->m_framesUnderConstruction ) /
- INT_TO_REAL( totalProductionFrames ) *
- 100.0f;
- // if we've reached 100% or more we're done, tada!
- if( production->m_percentComplete >= 100.0f )
- {
- // handle unit production items
- if( production->m_type == PRODUCTION_UNIT )
- {
- Object *creationBuilding = us;
- // create this unit
- //if I've got a ExitModule
- ExitInterface *exitInterface = creationBuilding->getObjectExitInterface();
- if( exitInterface )
- {
- //
- // only exit if the exit interface will let us ... if we can't we'll just keep this
- // unit in the production and keep "overbuilding" it, every frame we will check to see
- // if we can exit and then move onto production of the next one
- //
- Int numberToTry = production->getProductionQuantityRemaining();// this will change midloop, so it can't be the For clause
- for( int i = 0; i < numberToTry; i++ )
- {
- ExitDoorType exitDoor = production->getExitDoor();
- if (exitDoor == DOOR_NONE_AVAILABLE)
- {
- exitDoor = exitInterface->reserveDoorForExit(production->m_objectToProduce, NULL);
- production->setExitDoor(exitDoor);
- }
- if (exitDoor != DOOR_NONE_AVAILABLE)
- {
- // note, could be DOOR_NONE_NEEDED! so door could be null. (srj)
- DoorInfo* door = (exitDoor >= 0 && exitDoor < DOOR_COUNT_MAX) ? &m_doors[exitDoor] : NULL;
- //
- // if the producing structure has a door opening animation we will set the condition
- // bits to open the door (note that we must also take care to remove any condition
- // that had us previously closing a door)
- //
- const ProductionUpdateModuleData *d = getProductionUpdateModuleData();
- if( d->m_numDoorAnimations > 0 && door != NULL )
- {
- // if the door is closed, open it
- if( door->m_doorOpenedFrame == 0 && door->m_doorWaitOpenFrame == 0 && door->m_doorClosedFrame == 0 )
- {
- door->m_doorOpenedFrame = now;
- m_setFlags.set( theOpeningFlags[exitDoor] );
- m_flagsDirty = TRUE;
- } // end if
- // if the door is waiting-open, keep it there
- else if( door->m_doorWaitOpenFrame != 0 )
- {
- door->m_doorWaitOpenFrame = now;
- } // end else if
- // if the door is closing, for now, pop it to waiting open
- else if( door->m_doorClosedFrame != 0 )
- {
- door->m_doorWaitOpenFrame = now;
- m_clearFlags.set( theOpeningFlags[exitDoor], true );
- m_clearFlags.set( theClosingFlags[exitDoor], true );
- m_setFlags.set( theOpeningFlags[exitDoor], false );
- m_setFlags.set( theClosingFlags[exitDoor], false );
- m_setFlags.set( theWaitingOpenFlags[exitDoor] );
- m_flagsDirty = TRUE;
- } // end else
- } // end if, has door animation
- // we now go into the construction complete condition for a while
- if( m_constructionCompleteFrame == 0 )
- {
- m_constructionCompleteFrame = now;
- m_setFlags.set( MODELCONDITION_CONSTRUCTION_COMPLETE );
- m_flagsDirty = TRUE;
- } // end if
-
- //
- // make a new object, note that for production buildings that have door
- // animations we will not make the object until the door has been totally
- // opened
- //
- if( d->m_numDoorAnimations == 0 || door == NULL || door->m_doorWaitOpenFrame != 0 )
- {
- Object *newObj = TheThingFactory->newObject( production->m_objectToProduce,
- creationBuilding->getControllingPlayer()->getDefaultTeam() );
-
- newObj->setProducer(creationBuilding);
- // call the exit interface to do the rally point and position stuff
- exitInterface->exitObjectViaDoor( newObj, exitDoor );
- // since we successfully exited via this door, we should NOT call unreserveDoorForExit
- // when we toss this production entry. so set it back to an innocuous value.
- production->setExitDoor(DOOR_NONE_AVAILABLE);
- // Now, play the sound associated with the new object's production.
- AudioEventRTS voiceCreate = *newObj->getTemplate()->getVoiceCreated();
- voiceCreate.setObjectID(newObj->getID());
- TheAudio->addAudioEvent(&voiceCreate);
- // call the onUnitCreated for the player
- creationBuilding->getControllingPlayer()->onUnitCreated( creationBuilding, newObj );
- // onCreates have been called on newObj, and after that the owner was set,
- // so now is the time to call the game side of CreateModules
- for (BehaviorModule** m = newObj->getBehaviorModules(); *m; ++m)
- {
- CreateModuleInterface* create = (*m)->getCreate();
- if (!create)
- continue;
- create->onBuildComplete();
- }
- if( production->getProductionQuantity() == production->getProductionQuantityRemaining() )
- {
- //Call the voice created for the 1st object -- because it's possible to create multiple objects like redguards!
- AudioEventRTS sound = *newObj->getTemplate()->getPerUnitSound( "VoiceCreate" );
- sound.setObjectID( newObj->getID() );
- TheAudio->addAudioEvent( &sound );
- }
- creationBuilding->getControllingPlayer()->getAcademyStats()->recordProduction( newObj, creationBuilding );
- //We created one guy, but we may want to do more so we should stay in this node of production.
- // This is last so the voice check can easily check for "first" guy
- production->oneProductionSuccessful();
- } // end if, door open or no door animation ... make the object
- } // end, if we got a door reservation
- } //end of trying to exit all the things we were planning on attempting
- if( production->getProductionQuantityRemaining() == 0 )
- {
- // remove this production entry so we can go on to the next if we are totally finished
- removeFromProductionQueue( production );
-
- // delete the production entry
- production->deleteInstance();
- }
- } // end if we found an exit interface
- else
- {
- // there is no exit interface, this is an error
- DEBUG_ASSERTCRASH( 0, ("Cannot create '%s', there is no ExitUpdate interface defined for producer object '%s'\n",
- production->m_objectToProduce->getName().str(),
- creationBuilding->getTemplate()->getName().str()) );
- // remove this item from the production queue
- removeFromProductionQueue( production );
- // delete the production entry
- production->deleteInstance();
- } // end else
-
- } // end if, production unit
- else if( production->m_type == PRODUCTION_UPGRADE )
- {
- const UpgradeTemplate *upgrade = production->m_upgradeToResearch;
- // we finished an upgrade, lets add that money spent on it to the scorekeeper
- player->getScoreKeeper()->addMoneySpent(upgrade->calcCostToBuild(player));
-
- // notify the script engine
- TheScriptEngine->notifyOfCompletedUpgrade(
- us->getControllingPlayer()->getPlayerIndex(),
- upgrade->getUpgradeName(),
- us->getID());
- // print a message to the local player, if it wants one
- if( us->isLocallyControlled() && !upgrade->getDisplayNameLabel().isEmpty() )
- {
- UnicodeString msg;
- UnicodeString format = TheGameText->fetch( "UPGRADE:UpgradeComplete" );
- UnicodeString upgradeName = TheGameText->fetch( upgrade->getDisplayNameLabel().str() );
- msg.format( format.str(), upgradeName.str() );
- TheInGameUI->message( msg );
- // upgrades are a more rare event, play a nifty radar event thingie
- TheRadar->createEvent( us->getPosition(), RADAR_EVENT_UPGRADE );
-
- //Play the sound for the upgrade, because we just built it!
- AudioEventRTS sound = *upgrade->getResearchCompleteSound();
- if( TheAudio->isValidAudioEvent( &sound ) )
- {
- //We have a custom upgrade complete sound.
- sound.setObjectID( us->getID() );
- TheAudio->addAudioEvent( &sound );
- }
- else
- {
- //Use a generic EVA event.
- TheEva->setShouldPlay(EVA_UpgradeComplete);
- }
- //Play any available unit specific sound for upgrade.
- sound = *upgrade->getUnitSpecificSound();
- sound.setObjectID( us->getID() );
- TheAudio->addAudioEvent( &sound );
-
- } // end if
- // update the upgrade status in the player or give the upgrade to the object
- if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER )
- {
- player->addUpgrade( upgrade, UPGRADE_STATUS_COMPLETE );
- }
- else
- {
- us->giveUpgrade( upgrade );
- }
- player->getAcademyStats()->recordUpgrade( upgrade, FALSE );
-
- //Also mark the UI dirty -- incase object with upgrade cameo is selected.
- Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
- Object *selectedObject = draw ? draw->getObject() : NULL;
- if( selectedObject )
- {
- const ThingTemplate *thing = selectedObject->getTemplate();
- for( Int i = 0; i < MAX_UPGRADE_CAMEO_UPGRADES; i++ )
- {
- AsciiString upgradeName = thing->getUpgradeCameoName( i );
- const UpgradeTemplate *testUpgrade = TheUpgradeCenter->findUpgrade( upgradeName );
- if( testUpgrade == upgrade )
- {
- //Our selected object has the upgrade
- TheControlBar->markUIDirty();
- break;
- }
- }
- }
- // remove this production entry so we can go on to the next
- removeFromProductionQueue( production );
- // delete the production entry
- production->deleteInstance();
- } // end else, production upgrade
- } // end if, production is 100% complete
-
- return UPDATE_SLEEP_NONE;
- } // end update
- //-------------------------------------------------------------------------------------------------
- /** Add the production entry to the *END* of the production queue list */
- //-------------------------------------------------------------------------------------------------
- void ProductionUpdate::addToProductionQueue( ProductionEntry *production )
- {
- // check for empty list
- if( m_productionQueue == NULL )
- m_productionQueue = production;
- // make any existing tail pointer now point to us, and we point back to them
- if( m_productionQueueTail )
- {
- m_productionQueueTail->m_next = production;
- production->m_prev = m_productionQueueTail;
- } // end if
- // this production entry is now the new tail at the end of the list
- m_productionQueueTail = production;
- // we now have one more production item
- ++m_productionCount;
- // when something is in the queue ... we are in the actively constructing state
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- ModelConditionFlags condition = draw->getModelConditionFlags();
- if( condition.test( MODELCONDITION_ACTIVELY_CONSTRUCTING ) == FALSE )
- {
- m_setFlags.set( MODELCONDITION_ACTIVELY_CONSTRUCTING );
- m_flagsDirty = TRUE;
- } // end if
- } // end if
- } // end addToProductionQueue
- //-------------------------------------------------------------------------------------------------
- /** Remove the production entry from the production queue list */
- //-------------------------------------------------------------------------------------------------
- void ProductionUpdate::removeFromProductionQueue( ProductionEntry *production )
- {
- if (production->m_type == PRODUCTION_UNIT &&
- production->m_exitDoor != DOOR_NONE_AVAILABLE)
- {
- ExitInterface* exitInterface = getObject()->getObjectExitInterface();
- if (exitInterface)
- {
- exitInterface->unreserveDoorForExit(production->m_exitDoor);
- }
- }
- // detach prev pointer, keep head pointer to the whole queue in tact
- if( production->m_prev )
- production->m_prev->m_next = production->m_next;
- else
- m_productionQueue = production->m_next;
- // detach next pointer, keep tail poitner to the whole queue in tact
- if( production->m_next )
- production->m_next->m_prev = production->m_prev;
- else
- m_productionQueueTail = production->m_prev;
- // we now have one less production item
- --m_productionCount;
- // when we go back to zero things in our queue, we clear the constructing state
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- ModelConditionFlags condition = draw->getModelConditionFlags();
- if( m_productionCount == 0 && condition.test( MODELCONDITION_ACTIVELY_CONSTRUCTING ) == TRUE )
- {
- m_clearFlags.set( MODELCONDITION_ACTIVELY_CONSTRUCTING, true );
- m_setFlags.set( MODELCONDITION_ACTIVELY_CONSTRUCTING, false );
- m_flagsDirty = TRUE;
-
- } // end if
- } // end if
- /*
- // Debugging
- UnicodeString msg;
- if( production->getProductionType() == ProductionEntry::PRODUCTION_UNIT )
- {
- msg.format( L"Removed unit '%S'(%d)", production->getProductionObject()->getName().str(),
- production->getProductionID() );
- TheInGameUI->message( msg );
- }
- else
- {
- msg.format( L"Remove upgrade '%S'", production->getProductionUpgrade()->getName().str() );
- TheInGameUI->message( msg );
- }
- */
- } // end removeFromProductionQueue
- //-------------------------------------------------------------------------------------------------
- /** Is the upgrade already in the production queue. Note that you can only have one
- * production entry for any given upgrade in the queue */
- //-------------------------------------------------------------------------------------------------
- Bool ProductionUpdate::isUpgradeInQueue( const UpgradeTemplate *upgrade ) const
- {
- const ProductionEntry *production;
- for( production = firstProduction(); production; production = nextProduction( production ) )
- if( production->getProductionType() == PRODUCTION_UPGRADE &&
- production->getProductionUpgrade() == upgrade )
- return TRUE;
- return FALSE; // not in queue
- } // end isUpgradeInQueue
- // ------------------------------------------------------------------------------------------------
- /** count number of units with matching unit type in the production queue */
- // ------------------------------------------------------------------------------------------------
- UnsignedInt ProductionUpdate::countUnitTypeInQueue( const ThingTemplate *unitType ) const
- {
- UnsignedInt count = 0;
- const ProductionEntry *production;
- for( production = firstProduction(); production; production = nextProduction( production ) )
- if( production->getProductionType() == PRODUCTION_UNIT &&
- production->getProductionObject() == unitType )
- count++;
- return count;
- } // end countUnitTypeInQueue
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void ProductionUpdate::onDie( const DamageInfo *damageInfo )
- {
- // we need to cancel all of our production on death
- cancelAndRefundAllProduction();
- }
- // ------------------------------------------------------------------------------------------------
- /** Cancel each of the production items in the queue. By going through the actual cancel
- * methods, the cost of each of those items will be refunded to the player */
- // ------------------------------------------------------------------------------------------------
- void ProductionUpdate::cancelAndRefundAllProduction( void )
- {
- // Empirically, in release the code can loop forever. So we limit to 100 passes. jba. [8/31/2003]
- const Int productionLimit = 100;// With luck, we never queue up 100 units. [8/31/2003]
- Int i;
- for (i=0; i<productionLimit; i++)
- {
- // iterate through our production queue
- if( m_productionQueue )
- {
- if( m_productionQueue->getProductionType() == PRODUCTION_UNIT )
- cancelUnitCreate( m_productionQueue->getProductionID() );
- else if( m_productionQueue->getProductionType() == PRODUCTION_UPGRADE )
- cancelUpgrade( m_productionQueue->getProductionUpgrade() );
- else
- {
- // unknown production type
- DEBUG_CRASH(( "ProductionUpdate::cancelAndRefundAllProduction - Unknown production type '%d'\n", m_productionQueue->getProductionType() ));
- return;
- } // end else
- } // end if
- }
- } // end cancelAndRefundAllProduction
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void ProductionUpdate::setHoldDoorOpen(ExitDoorType exitDoor, Bool holdIt)
- {
- if (exitDoor >= DOOR_1 && exitDoor < DOOR_COUNT_MAX)
- {
- DoorInfo& door = m_doors[exitDoor];
- door.m_holdOpen = holdIt;
- if (holdIt && door.m_doorOpenedFrame == 0 && door.m_doorWaitOpenFrame == 0 && door.m_doorClosedFrame == 0)
- {
- door.m_doorOpenedFrame = TheGameLogic->getFrame();
- m_setFlags.set( theOpeningFlags[exitDoor] );
- m_flagsDirty = TRUE;
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Helper method to retrieve a production update interface from an object if one is present */
- // ------------------------------------------------------------------------------------------------
- /*static*/ ProductionUpdateInterface *ProductionUpdate::getProductionUpdateInterfaceFromObject( Object *obj )
- {
-
- // sanity
- if( obj == NULL )
- return NULL;
- BehaviorModule **bmi;
- ProductionUpdateInterface *pui = NULL;
- for( bmi = obj->getBehaviorModules(); *bmi; ++bmi )
- {
- pui = (*bmi)->getProductionUpdateInterface();
- if( pui )
- return pui;
- } // end for, bmi
- // interface not found
- return NULL;
- } // end getProductionUpdateInterfaceFromObject
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void ProductionUpdate::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void ProductionUpdate::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
- // production queue count
- ProductionEntry *production;
- UnsignedShort productionCount = 0;
- for( production = m_productionQueue; production; production = production->m_next )
- productionCount++;
- xfer->xferUnsignedShort( &productionCount );
-
- // production queue data
- if( xfer->getXferMode() == XFER_SAVE )
- {
- AsciiString name;
- // write all queue data
- for( production = m_productionQueue; production; production = production->m_next )
- {
- // type
- xfer->xferUser( &production->m_type, sizeof( ProductionType ) );
- // thing/upgrade template name
- if( production->m_type == PRODUCTION_UNIT )
- name = production->m_objectToProduce->getName();
- else
- name = production->m_upgradeToResearch->getUpgradeName();
- xfer->xferAsciiString( &name );
- // production ID
- xfer->xferUser( &production->m_productionID, sizeof( ProductionID ) );
- // percent complete
- xfer->xferReal( &production->m_percentComplete );
- // frames under construction
- xfer->xferInt( &production->m_framesUnderConstruction );
- // production quantity
- xfer->xferInt( &production->m_productionQuantityTotal );
- // production quantity in progress
- xfer->xferInt( &production->m_productionQuantityProduced );
- // exit door
- xfer->xferInt( (Int*)&production->m_exitDoor );
- } // end for
- } // end if, save
- else
- {
- AsciiString name;
- // the queue should be emtpy now
- if( m_productionQueue != NULL )
- {
-
- DEBUG_CRASH(( "ProductionUpdate::xfer - m_productionQueue is not empty, but should be\n" ));
- throw SC_INVALID_DATA;
- } // end if
- // read each element
- for( UnsignedShort i = 0; i < productionCount; ++i )
- {
- // allocate new production entry
- production = newInstance(ProductionEntry);
- // tie to list at end
- if( m_productionQueue == NULL )
- m_productionQueue = production;
- // make any existing tail pointer now point to us, and we point back to them
- if( m_productionQueueTail )
- {
- m_productionQueueTail->m_next = production;
- production->m_prev = m_productionQueueTail;
- } // end if
- // this production entry is now the new tail at the end of the list
- m_productionQueueTail = production;
- // type
- xfer->xferUser( &production->m_type, sizeof( ProductionType ) );
- // thing/upgrade template name
- xfer->xferAsciiString( &name );
- if( production->m_type == PRODUCTION_UNIT )
- {
- production->m_objectToProduce = TheThingFactory->findTemplate( name );
- if( production->m_objectToProduce == NULL )
- {
-
- DEBUG_CRASH(( "ProductionUpdate::xfer - Cannot find template '%s'\n", name.str() ));
- throw SC_INVALID_DATA;
- } // end if
- } // end if, unit production
- else
- {
- production->m_upgradeToResearch = TheUpgradeCenter->findUpgrade( name );
- if( production->m_upgradeToResearch == NULL )
- {
- DEBUG_CRASH(( "ProductionUpdate::xfer - Cannot find upgrade '%s'\n", name.str() ));
- throw SC_INVALID_DATA;
- } // end if
- } // end else, upgrade production
- // production ID
- xfer->xferUser( &production->m_productionID, sizeof( ProductionID ) );
- // percent complete
- xfer->xferReal( &production->m_percentComplete );
- // frames under construction
- xfer->xferInt( &production->m_framesUnderConstruction );
- // production quantity
- xfer->xferInt( &production->m_productionQuantityTotal );
- // production quantity in progress
- xfer->xferInt( &production->m_productionQuantityProduced );
- // exit door
- xfer->xferInt( (Int*)&production->m_exitDoor );
- } // end for, i
- } // end else, load
- // unique id
- xfer->xferUser( &m_uniqueID, sizeof( ProductionID ) );
-
- // production count
- xfer->xferUnsignedInt( &m_productionCount );
-
- // construction complete frame
- xfer->xferUnsignedInt( &m_constructionCompleteFrame );
-
- // door info
- xfer->xferUser( &m_doors, sizeof( DoorInfo ) * DOOR_COUNT_MAX );
- // clear flags
- m_clearFlags.xfer( xfer );
- // set flags
- m_setFlags.xfer( xfer );
- // flags dirty
- xfer->xferBool( &m_flagsDirty );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void ProductionUpdate::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|