/* ** Command & Conquer Generals(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 . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: ParkingPlaceBehavior.cpp /////////////////////////////////////////////////////////////////////// // Author: Steven Johnson, June 2002 // Desc: /////////////////////////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/CRCDebug.h" #include "Common/Xfer.h" #include "Common/ThingTemplate.h" #include "GameClient/Drawable.h" #include "GameLogic/AI.h" #include "GameLogic/AIPathfind.h" #include "GameLogic/Module/AIUpdate.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Module/ParkingPlaceBehavior.h" #include "GameLogic/Module/ProductionUpdate.h" #include "GameLogic/Module/JetAIUpdate.h" #include "GameLogic/Object.h" #include "GameLogic/TerrainLogic.h" #include "Common/Team.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ParkingPlaceBehavior::ParkingPlaceBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData ) { m_gotInfo = false; //Added By Sadullah Nader //Initializations m_heliRallyPoint.zero(); // m_heliRallyPointExists = FALSE; m_nextHealFrame = FOREVER; setWakeFrame(getObject(), UPDATE_SLEEP_NONE); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ParkingPlaceBehavior::~ParkingPlaceBehavior( void ) { } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::buildInfo() { if (m_gotInfo) return; if (getObject()->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) || getObject()->testStatus(OBJECT_STATUS_SOLD)) return; const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData(); ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface(); m_spaces.reserve(d->m_numRows * d->m_numCols); Int door = 0; { ParkingPlaceInfo info; for (Int row = 0; row < d->m_numRows; ++row) { for (Int col = 0; col < d->m_numCols; ++col) { AsciiString tmp; Matrix3D mtx; tmp.format("Runway%dPark%dHan",col+1,row+1); getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_hangarStart, &mtx); info.m_hangarStartOrient = mtx.Get_Z_Rotation(); tmp.format("Runway%dParking%d",col+1,row+1); getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_location, &mtx); info.m_orientation = mtx.Get_Z_Rotation(); tmp.format("Runway%dPrep%d",col+1,row+1); getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_prep, NULL); info.m_runway = col; info.m_door = (ExitDoorType)door++; info.m_objectInSpace = INVALID_ID; info.m_reservedForExit = false; if (pu) pu->setHoldDoorOpen(info.m_door, false); m_spaces.push_back(info); } } } if (d->m_hasRunways) { RunwayInfo info; m_runways.reserve(d->m_numCols); for (Int col = 0; col < d->m_numCols; ++col) { AsciiString tmp; tmp.format("RunwayStart%d",col+1); getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_start, NULL); tmp.format("RunwayEnd%d",col+1); getObject()->getSingleLogicalBonePosition(tmp.str(), &info.m_end, NULL); info.m_inUseBy = INVALID_ID; info.m_nextInLineForTakeoff = INVALID_ID; info.m_wasInLine = false; m_runways.push_back(info); } } m_gotInfo = true; } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::purgeDead() { buildInfo(); ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface(); { for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace != INVALID_ID) { Object* obj = TheGameLogic->findObjectByID(it->m_objectInSpace); if (obj == NULL || obj->isEffectivelyDead()) { it->m_objectInSpace = INVALID_ID; it->m_reservedForExit = false; if (pu) pu->setHoldDoorOpen(it->m_door, false); } } } } { for (std::vector::iterator it = m_runways.begin(); it != m_runways.end(); ++it) { if (it->m_inUseBy != INVALID_ID) { Object* obj = TheGameLogic->findObjectByID(it->m_inUseBy); if (obj == NULL || obj->isEffectivelyDead()) { it->m_inUseBy = INVALID_ID; it->m_wasInLine = false; } } if (it->m_nextInLineForTakeoff != INVALID_ID) { Object* obj = TheGameLogic->findObjectByID(it->m_nextInLineForTakeoff); if (obj == NULL || obj->isEffectivelyDead()) { it->m_nextInLineForTakeoff = INVALID_ID; } } } } { Bool anythingPurged = false; for (std::list::iterator it = m_healing.begin(); it != m_healing.end(); /*++it*/) { if (it->m_gettingHealedID != INVALID_ID) { Object* objToHeal = TheGameLogic->findObjectByID(it->m_gettingHealedID); if (objToHeal == NULL || objToHeal->isEffectivelyDead()) { it = m_healing.erase(it); anythingPurged = true; } else { ++it; } } } if (anythingPurged) resetWakeFrame(); } } //------------------------------------------------------------------------------------------------- // note: called from client, so MUST NOT modify self in any way, or desyncs will occur Bool ParkingPlaceBehavior::hasReservedSpace(ObjectID id) const { if (!m_gotInfo) return false; if (id == INVALID_ID) // shouldn't call this way, but Weapon mistakenly does sometimes, so check for it return false; for (std::vector::const_iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace == id) return true; } return false; } //------------------------------------------------------------------------------------------------- ParkingPlaceBehavior::ParkingPlaceInfo* ParkingPlaceBehavior::findPPI(ObjectID id) { DEBUG_ASSERTCRASH(id != INVALID_ID, ("call findEmptyPPI instead")); if (!m_gotInfo || id == INVALID_ID) return NULL; for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace == id) return &(*it); } return NULL; } //------------------------------------------------------------------------------------------------- ParkingPlaceBehavior::ParkingPlaceInfo* ParkingPlaceBehavior::findEmptyPPI() { if (!m_gotInfo) return NULL; for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace == INVALID_ID && it->m_reservedForExit == false) return &(*it); } return NULL; } //------------------------------------------------------------------------------------------------- // note: called from client, so MUST NOT modify self in any way, or desyncs will occur Bool ParkingPlaceBehavior::shouldReserveDoorWhenQueued(const ThingTemplate* thing) const { if (thing->isKindOf(KINDOF_PRODUCED_AT_HELIPAD)) return false; return true; } //------------------------------------------------------------------------------------------------- // note: called from client, so MUST NOT modify self in any way, or desyncs will occur Bool ParkingPlaceBehavior::hasAvailableSpaceFor(const ThingTemplate* thing) const { if (!m_gotInfo) // degenerate case, shouldn't happen, but just in case... return false; if (thing->isKindOf(KINDOF_PRODUCED_AT_HELIPAD)) return true; for (std::vector::const_iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { ObjectID id = it->m_objectInSpace; // since this is const, and we can't purge the dead safely, just peek and see if we have a dead thing. if (id != INVALID_ID) { Object* obj = TheGameLogic->findObjectByID(id); if (obj == NULL || obj->isEffectivelyDead()) { id = INVALID_ID; } } if (id == INVALID_ID && it->m_reservedForExit == false) { return true; } } return false; } //------------------------------------------------------------------------------------------------- Bool ParkingPlaceBehavior::reserveSpace(ObjectID id, Real parkingOffset, ParkingPlaceBehaviorInterface::PPInfo* info) { buildInfo(); purgeDead(); const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData(); ParkingPlaceInfo* ppi = findPPI(id); if (ppi == NULL) { ppi = findEmptyPPI(); if (ppi == NULL) { DEBUG_CRASH(("No parking places!")); return false; // nothing available } } ppi->m_objectInSpace = id; ppi->m_reservedForExit = false; if (info) { const RunwayInfo& rr = m_runways[ppi->m_runway]; info->parkingSpace = d->m_parkInHangars ? ppi->m_hangarStart : ppi->m_location; if (parkingOffset != 0.0f) { info->parkingSpace.x += parkingOffset * Cos(ppi->m_orientation); info->parkingSpace.y += parkingOffset * Sin(ppi->m_orientation); } info->runwayPrep = ppi->m_prep; info->parkingOrientation = d->m_parkInHangars ? ppi->m_hangarStartOrient : ppi->m_orientation; info->runwayStart = rr.m_start; info->runwayEnd = rr.m_end; info->runwayApproach = rr.m_end; //const Real APPROACH_DIST = 0.5f; // no, too short, planes may not have enough space to drop altitude const Real APPROACH_DIST = 0.75f; info->runwayApproach.x += (rr.m_end.x - rr.m_start.x) * APPROACH_DIST; info->runwayApproach.y += (rr.m_end.y - rr.m_start.y) * APPROACH_DIST; info->runwayApproach.z = rr.m_end.z + d->m_approachHeight; info->hangarInternal = ppi->m_hangarStart; info->hangarInternalOrient = ppi->m_hangarStartOrient; for (std::vector::iterator it = m_runways.begin(); it != m_runways.end(); ++it) { if (it->m_inUseBy == id && it->m_wasInLine) { info->runwayStart = info->runwayPrep; } } } ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface(); if (pu) pu->setHoldDoorOpen(ppi->m_door, true); return true; } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::releaseSpace(ObjectID id) { buildInfo(); purgeDead(); ProductionUpdateInterface* pu = getObject()->getProductionUpdateInterface(); for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace == id) { it->m_objectInSpace = INVALID_ID; it->m_reservedForExit = false; if (pu) pu->setHoldDoorOpen(it->m_door, false); } } } //------------------------------------------------------------------------------------------------- ObjectID ParkingPlaceBehavior::getRunwayReservation(Int runway) { buildInfo(); purgeDead(); return m_runways[runway].m_inUseBy; } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::transferRunwayReservationToNextInLineForTakeoff(ObjectID id) { buildInfo(); purgeDead(); for (std::vector::iterator it = m_runways.begin(); it != m_runways.end(); ++it) { if (it->m_inUseBy == id && it->m_nextInLineForTakeoff != INVALID_ID) { it->m_inUseBy = it->m_nextInLineForTakeoff; it->m_wasInLine = true; it->m_nextInLineForTakeoff = INVALID_ID; } } } //------------------------------------------------------------------------------------------------- Bool ParkingPlaceBehavior::reserveRunway(ObjectID id, Bool forLanding) { buildInfo(); purgeDead(); Int runway = -1; for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace == id) { runway = it->m_runway; break; } } if (runway == -1) { DEBUG_CRASH(("only planes with reserved spaces can reserve runways")); return false; } RunwayInfo& info = m_runways[runway]; if (info.m_inUseBy == id) { return true; } else if (info.m_inUseBy == INVALID_ID) { info.m_inUseBy = id; if (info.m_nextInLineForTakeoff == id) { info.m_nextInLineForTakeoff = INVALID_ID; info.m_wasInLine = true; } else { info.m_wasInLine = false; } return true; } else if (!forLanding && info.m_nextInLineForTakeoff == INVALID_ID) { info.m_nextInLineForTakeoff = id; return false; // yes, that's right } return false; } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::releaseRunway(ObjectID id) { buildInfo(); purgeDead(); for (std::vector::iterator it = m_runways.begin(); it != m_runways.end(); ++it) { if (it->m_inUseBy == id) { it->m_inUseBy = INVALID_ID; it->m_wasInLine = false; } if (it->m_nextInLineForTakeoff == id) { it->m_nextInLineForTakeoff = INVALID_ID; } } } //------------------------------------------------------------------------------------------------- // don't really need to autoheal every frame.... const Int HEAL_RATE_FRAMES = LOGICFRAMES_PER_SECOND / 5; //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::resetWakeFrame() { if (m_healing.empty()) { m_nextHealFrame = FOREVER; } else { m_nextHealFrame = TheGameLogic->getFrame() + HEAL_RATE_FRAMES; } } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::setHealee(Object* healee, Bool add) { if (add) { for (std::list::const_iterator it = m_healing.begin(); it != m_healing.end(); ++it) { if (it->m_gettingHealedID == healee->getID()) return; } HealingInfo info; info.m_gettingHealedID = healee->getID(); info.m_healStartFrame = TheGameLogic->getFrame(); m_healing.push_back(info); resetWakeFrame(); } else { for (std::list::iterator it = m_healing.begin(); it != m_healing.end(); /*++it*/) { if (it->m_gettingHealedID == healee->getID()) { it = m_healing.erase(it); resetWakeFrame(); } else { ++it; } } } } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::defectAllParkedUnits(Team* newTeam, UnsignedInt detectionTime) { buildInfo(); purgeDead(); for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace != INVALID_ID) { Object* obj = TheGameLogic->findObjectByID(it->m_objectInSpace); if (obj == NULL || obj->isEffectivelyDead()) continue; // srj sez: evil. fix better someday. static NameKeyType jetKey = TheNameKeyGenerator->nameToKey("JetAIUpdate"); JetAIUpdate* ju = (JetAIUpdate *)obj->findUpdateModule(jetKey); Bool takeoffOrLanding = ju ? ju->friend_isTakeoffOrLandingInProgress() : false; if (obj->isAboveTerrain() && !takeoffOrLanding) { // if the new team is a different controlling player, this guys loses his space. if (newTeam->getControllingPlayer() != obj->getControllingPlayer()) { releaseSpace(obj->getID()); if (obj->getProducerID() == getObject()->getID()) obj->setProducer(NULL); } } else { obj->defect(newTeam, detectionTime); } } } purgeDead(); } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::killAllParkedUnits() { buildInfo(); purgeDead(); for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace != INVALID_ID) { Object* obj = TheGameLogic->findObjectByID(it->m_objectInSpace); if (obj == NULL || obj->isEffectivelyDead()) continue; // srj sez: evil. fix better someday. static NameKeyType jetKey = TheNameKeyGenerator->nameToKey("JetAIUpdate"); JetAIUpdate* ju = (JetAIUpdate *)obj->findUpdateModule(jetKey); Bool takeoffOrLanding = ju ? ju->friend_isTakeoffOrLandingInProgress() : false; if (obj->isAboveTerrain() && !takeoffOrLanding) continue; obj->kill(); } } purgeDead(); } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::onDie( const DamageInfo *damageInfo ) { killAllParkedUnits(); } //------------------------------------------------------------------------------------------------- UpdateSleepTime ParkingPlaceBehavior::update() { // alas, we need to keep the buildInfo and dead-purged stuff pretty much up to date, for // the client to be able to peek at. at this late date, the most expedient way is to ensure // our update is run every frame, and do this manually. the extra cost should be trivial, since // there are generally at most only a few airfields at any given time. buildInfo(); purgeDead(); UnsignedInt now = TheGameLogic->getFrame(); if (now >= m_nextHealFrame) { m_nextHealFrame = now + HEAL_RATE_FRAMES; const ParkingPlaceBehaviorModuleData* d = getParkingPlaceBehaviorModuleData(); for (std::list::iterator it = m_healing.begin(); it != m_healing.end(); /*++it*/) { if (it->m_gettingHealedID != INVALID_ID) { Object* objToHeal = TheGameLogic->findObjectByID(it->m_gettingHealedID); if (objToHeal == NULL || objToHeal->isEffectivelyDead()) { it = m_healing.erase(it); } else { DamageInfo healInfo; healInfo.in.m_damageType = DAMAGE_HEALING; healInfo.in.m_deathType = DEATH_NONE; healInfo.in.m_sourceID = getObject()->getID(); healInfo.in.m_amount = HEAL_RATE_FRAMES * d->m_healAmount * SECONDS_PER_LOGICFRAME_REAL; BodyModuleInterface *body = objToHeal->getBodyModule(); body->attemptHealing( &healInfo ); ++it; } } } } return UPDATE_SLEEP_NONE; } //------------------------------------------------------------------------------------------------- ExitDoorType ParkingPlaceBehavior::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject ) { buildInfo(); purgeDead(); if (objType->isKindOf(KINDOF_PRODUCED_AT_HELIPAD)) { return DOOR_NONE_NEEDED; } // note that this never allocates a per-unit parking space, even if specificObject is null. // this is by design and is assumed by exitObjectViaDoor. ParkingPlaceInfo* ppi = findEmptyPPI(); if (ppi) { ppi->m_objectInSpace = INVALID_ID; ppi->m_reservedForExit = true; return ppi->m_door; } return DOOR_NONE_AVAILABLE; } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::exitObjectViaDoor( Object *newObj, ExitDoorType exitDoor ) ///< Here is the thing I want you to exit { if (exitDoor != DOOR_NONE_NEEDED) { ParkingPlaceInfo* ppi = NULL; for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_objectInSpace == INVALID_ID && it->m_reservedForExit == TRUE && it->m_door == exitDoor) { ppi = &(*it); break; } } if (!ppi) { DEBUG_CRASH(("could not find the space. what?")); return; } ppi->m_objectInSpace = newObj->getID(); ppi->m_reservedForExit = false; } /// @todo srj -- this is evil. fix. static NameKeyType jetKey = TheNameKeyGenerator->nameToKey( "JetAIUpdate" ); JetAIUpdate* ju = (JetAIUpdate *)newObj->findUpdateModule( jetKey ); Real parkingOffset = ju ? ju->friend_getParkingOffset() : 0.0f; Bool producedAtHelipad = newObj->isKindOf(KINDOF_PRODUCED_AT_HELIPAD); PPInfo ppinfo; DUMPMATRIX3D(getObject()->getTransformMatrix()); DUMPCOORD3D(getObject()->getPosition()); if (producedAtHelipad) { CRCDEBUG_LOG(("Produced at helipad (door = %d)\n", exitDoor)); DEBUG_ASSERTCRASH(exitDoor == DOOR_NONE_NEEDED, ("Hmm, unlikely")); Matrix3D mtx; #ifdef DEBUG_CRASHING Bool boneOk = #endif getObject()->getSingleLogicalBonePosition("HeliPark01", &ppinfo.hangarInternal, &mtx); DEBUG_ASSERTCRASH(boneOk, ("Could not get bone!")); ppinfo.hangarInternalOrient = mtx.Get_Z_Rotation(); ppinfo.parkingSpace = ppinfo.hangarInternal; ppinfo.parkingOrientation = ppinfo.hangarInternalOrient; } else { CRCDEBUG_LOG(("Produced at hangar (door = %d)\n", exitDoor)); DEBUG_ASSERTCRASH(exitDoor != DOOR_NONE_NEEDED, ("Hmm, unlikely")); if (!reserveSpace(newObj->getID(), parkingOffset, &ppinfo)) //&loc, &orient, NULL, NULL, NULL, NULL, &hangarInternal, &hangOrient)) { DEBUG_CRASH(("no spaces available, how did we get here?")); ppinfo.parkingSpace = *getObject()->getPosition(); ppinfo.parkingOrientation = getObject()->getOrientation(); } } DUMPCOORD3D(&ppinfo.hangarInternal); DUMPREAL(ppinfo.hangarInternalOrient); DUMPCOORD3D(&ppinfo.parkingSpace); DUMPREAL(ppinfo.parkingOrientation); newObj->setPosition( &ppinfo.hangarInternal ); newObj->setOrientation( ppinfo.hangarInternalOrient ); TheAI->pathfinder()->addObjectToPathfindMap( newObj ); AIUpdateInterface *ai = newObj->getAIUpdateInterface(); if( ai ) { Bool movedToRallyPoint = FALSE; if( producedAtHelipad ) { const Coord3D *rallyPoint = getRallyPoint(); if( rallyPoint ) { ai->aiMoveToPosition( rallyPoint, CMD_FROM_AI ); movedToRallyPoint = TRUE; } } if( !movedToRallyPoint ) { std::vector exitPath; exitPath.push_back(ppinfo.parkingSpace); ai->aiFollowExitProductionPath( &exitPath, getObject(), CMD_FROM_AI ); } } } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::unreserveDoorForExit( ExitDoorType exitDoor ) { if (exitDoor != DOOR_NONE_NEEDED) { for (std::vector::iterator it = m_spaces.begin(); it != m_spaces.end(); ++it) { if (it->m_door == exitDoor) { //DEBUG_ASSERTCRASH(it->m_reservedForExit, ("ParkingPlaceBehavior::unreserveDoorForExit: door %d was not reserved\n",exitDoor)); it->m_objectInSpace = INVALID_ID; it->m_reservedForExit = false; return; } } DEBUG_CRASH(("ParkingPlaceBehavior::unreserveDoorForExit: door %d was not found\n",exitDoor)); } } //------------------------------------------------------------------------------------------------- void ParkingPlaceBehavior::setRallyPoint( const Coord3D *pos ) { m_heliRallyPointExists = TRUE; m_heliRallyPoint.set( pos ); // nothing } //------------------------------------------------------------------------------------------------- const Coord3D* ParkingPlaceBehavior::getRallyPoint( void ) const { if( m_heliRallyPointExists ) { return &m_heliRallyPoint; } return NULL; } //------------------------------------------------------------------------------------------------- //We only use this for the helipad -- therefore the exit position is the helipad creation point. //------------------------------------------------------------------------------------------------- Bool ParkingPlaceBehavior::getExitPosition( Coord3D& exitPosition ) const { Matrix3D mtx; return getObject()->getSingleLogicalBonePosition("HeliPark01", &exitPosition, &mtx ); } //------------------------------------------------------------------------------------------------- Bool ParkingPlaceBehavior::getNaturalRallyPoint( Coord3D& rallyPoint, Bool offset ) const { Matrix3D mtx; return getObject()->getSingleLogicalBonePosition("HeliPark01", &rallyPoint, &mtx ); } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void ParkingPlaceBehavior::crc( Xfer *xfer ) { // extend base class UpdateModule::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void ParkingPlaceBehavior::xfer( Xfer *xfer ) { Int i; // version const XferVersion currentVersion = 3; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class UpdateModule::xfer( xfer ); if( xfer->getXferMode() == XFER_LOAD ) { // first, build our info, so it won't be overwritten later. buildInfo(); } // spaces info count and data UnsignedByte spacesCount = m_spaces.size(); xfer->xferUnsignedByte( &spacesCount ); if( xfer->getXferMode() == XFER_SAVE ) { // save all elements std::vector< ParkingPlaceInfo >::iterator it; for( it = m_spaces.begin(); it != m_spaces.end(); ++it ) { // object in this space xfer->xferObjectID( &((*it).m_objectInSpace) ); // reserved for exit xfer->xferBool( &((*it).m_reservedForExit) ); } // end for, it } // end if, save else if( xfer->getXferMode() == XFER_LOAD ) { ObjectID objectID; Bool reservedForExit; // read all elements std::vector< ParkingPlaceInfo >::iterator it; it = m_spaces.begin(); for( i = 0; i < spacesCount; ++i ) { // read object id xfer->xferObjectID( &objectID ); // read reserved flag xfer->xferBool( &reservedForExit ); // store in vector if the vector does indeed still have room for this entry if( it != m_spaces.end() ) { (*it).m_objectInSpace = objectID; (*it).m_reservedForExit = reservedForExit; ++it; } // end if } // end for, i } // end else, load // runways cound and info UnsignedByte runwaysCount = m_runways.size(); xfer->xferUnsignedByte( &runwaysCount ); if( xfer->getXferMode() == XFER_SAVE ) { // save all elements std::vector< RunwayInfo >::iterator it; for( it = m_runways.begin(); it != m_runways.end(); ++it ) { // save object ID xfer->xferObjectID( &((*it).m_inUseBy) ); xfer->xferObjectID( &((*it).m_nextInLineForTakeoff) ); xfer->xferBool( &((*it).m_wasInLine) ); } // end for, it } // end if, save else if( xfer->getXferMode() == XFER_LOAD ) { // read all elements std::vector< RunwayInfo >::iterator it; it = m_runways.begin(); for( i = 0; i < runwaysCount; ++i ) { ObjectID inUseBy; ObjectID nextInLineForTakeoff; Bool wasInLine; // read object ID xfer->xferObjectID( &inUseBy ); xfer->xferObjectID( &nextInLineForTakeoff ); xfer->xferBool( &wasInLine ); // store in vector if the vector does indeed still have room for this entry if( it != m_runways.end() ) { (*it).m_inUseBy = inUseBy; (*it).m_nextInLineForTakeoff = nextInLineForTakeoff; (*it).m_wasInLine = wasInLine; ++it; } // end if } // end for, i } // end else, load // healees UnsignedByte healCount = m_healing.size(); xfer->xferUnsignedByte( &healCount ); if( xfer->getXferMode() == XFER_SAVE ) { // save all elements std::list< HealingInfo >::iterator it; for( it = m_healing.begin(); it != m_healing.end(); ++it ) { // save object ID xfer->xferObjectID( &((*it).m_gettingHealedID) ); xfer->xferUnsignedInt( &((*it).m_healStartFrame) ); } // end for, it } // end if, save else if( xfer->getXferMode() == XFER_LOAD ) { // read all elements m_healing.clear(); for( i = 0; i < healCount; ++i ) { HealingInfo info; // read object ID xfer->xferObjectID( &info.m_gettingHealedID ); xfer->xferUnsignedInt( &info.m_healStartFrame ); m_healing.push_back(info); } // end for, i } // end else, load if (version >= 2) { xfer->xferCoord3D(&m_heliRallyPoint); xfer->xferBool(&m_heliRallyPointExists); } if (version >= 3) { xfer->xferUnsignedInt(&m_nextHealFrame); } else { // old file .. just wake it up right away, and it will fall into the correct pattern if (xfer->getXferMode() == XFER_LOAD) { m_nextHealFrame = 0; } } } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void ParkingPlaceBehavior::loadPostProcess( void ) { // extend base class UpdateModule::loadPostProcess(); // no, this is bad.. it is NOT SAFE to call setWakeFrame from the xfer system. crap. (srj) // make sure we are awake... old save games let us sleep //setWakeFrame(getObject(), UPDATE_SLEEP_NONE); } // end loadPostProcess