| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602 |
- /*
- ** 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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: RailroadBehavior.cpp //////////////////////////////////////////////////////////////
- // Author: Mark Lorenzen, Sept 2002
- // Desc: The railroad track following railroad carriage/locomotive logic module
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h"
- #include "Common/Player.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/RailroadGuideAIUpdate.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/DemoTrapUpdate.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/Statistics.h"
- ///#define RAILROAD_DESYNC_TEST
- #ifdef RAILROAD_DESYNC_TEST
- #include "winbase.h"
- #endif
- // TYPES //////////////////////////////////////////////////////////////////////////////////////////
- static const Int INVALID_PATH = -1;
- #define NORMAL_VEL_Z 0.25f
- #define NORMAL_MASS 50.0f
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- //#pragma message("NO COMMENTS, PLEASE ABOUT THIS PRAGMA BEING LEFT ON... IT IS FOR A SPECIAL TEST...")
- //#pragma message("IF QA EVER REPRODUCES A DESYNC (BELIEVED FIXED) I'LL NEED TO DEBUG WHILE PLAYING AGAINST")
- //#pragma message("THAT MACHINE WITHOUT QUITTING TO START A DEBUGGABLE VERSION...")
- //#pragma message("THIS WILL BE CORRECTED IN GOOD TIME. JUST TRUST ME, -ML")
- #endif
- #define FRAMES_UNPULLED_LONG_ENOUGH_TO_UNHITCH (2)
- void TrainTrack::incReference()
- {
- ++m_refCount;
- }
- Bool TrainTrack::releaseReference()
- {
- return ( --m_refCount == 0 );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- RailroadBehaviorModuleData::RailroadBehaviorModuleData( void )
- {
- m_carriageTemplateNameData.clear();
- m_pathPrefixName.clear();
- m_CrashFXTemplateName.clear();
-
- m_isLocomotive = FALSE;
- m_runningGarrisonSpeedMax = 1.0f;
- m_killSpeedMin = 1.0f;
- m_speedMax = 4;
- m_acceleration = 1.01f;
- m_braking = 0.99f;
- m_friction = 0.97f;
- m_waitAtStationTime = 150;
- } // end RailroadBehaviorModuleData
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- RailroadBehavior::RailroadBehavior( Thing *thing, const ModuleData *moduleData )
- : PhysicsBehavior( thing, moduleData )
- {
- const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
- m_carriageTemplateNameIterator = 0;
- m_nextStationTask = DO_NOTHING;
- m_trailerID = INVALID_ID;
- conductorPullInfo.speed = 0;
- conductorPullInfo.m_direction = 1.0f;
- conductorPullInfo.m_mostRecentSpecialPointHandle = 0xfacade;
- conductorPullInfo.towHitchPosition.set(0,0,0);
- conductorPullInfo.trackDistance = 0;
- conductorPullInfo.currentWaypoint = 0xfacade;
- conductorPullInfo.previousWaypoint = 0xfacade;
- m_pullInfo.speed = 0.0f;
- m_pullInfo.m_direction = 1.0f;
- m_pullInfo.m_mostRecentSpecialPointHandle = 0xfacade;
- m_pullInfo.towHitchPosition.set(0,0,0);
- m_pullInfo.trackDistance = 0.0f;
- m_pullInfo.currentWaypoint = 0xfacade;
- m_pullInfo.previousWaypoint = 0xfacade;
- #ifdef RAILROAD_DESYNC_TEST
- _LARGE_INTEGER pc;
- QueryPerformanceCounter( &pc ); // absolutely, positively random every call!
- Real random = 100000.0f / (Real)pc.LowPart;
- conductorPullInfo.m_direction = random;
- m_pullInfo.m_direction = random;
- #endif
- m_runningSound = modData->m_runningSound;
- m_clicketyClackSound = modData->m_clicketyClackSound;
- m_whistleSound = modData->m_whistleSound;
- m_runningSound.setObjectID( getObject()->getID() );
- m_whistleSound.setObjectID( getObject()->getID() ) ;
- m_clicketyClackSound.setObjectID( getObject()->getID() ) ;
- m_track = NULL;
- m_currentPointHandle = 0xfacade;
- m_waitAtStationTimer = 0;
-
- m_anchorWaypointID = INVALID_WAYPOINT_ID;
- m_carriagesCreated = FALSE;
- m_hasEverBeenHitched = FALSE;
- m_trackDataLoaded = FALSE;
- m_waitingInWings = TRUE;
- m_endOfLine = FALSE;
- m_isLocomotive = modData->m_isLocomotive;
- m_isLeadCarraige = m_isLocomotive; // for now, I am the lead, only if I am the locomotive
- m_wantsToBeLeadCarraige = FALSE;
- m_disembark = FALSE;
- m_inTunnel = FALSE;
- m_conductorState = m_isLocomotive ? ACCELERATE : COAST;
- } // end RailroadBehavior
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- RailroadBehavior::~RailroadBehavior( void )
- {
- TheAudio->removeAudioEvent( m_runningSound.getPlayingHandle() );// no more chugchug when I'm dead
- if( m_track != NULL )
- {
- if (m_track->releaseReference())
- delete m_track;
- m_track = NULL;
- }
- } // end ~RailroadBehavior
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool RailroadBehavior::isRailroad() const
- {
- if ( ! m_track )
- return FALSE;// I haven't even started yet!
- if (m_waitingInWings)
- return FALSE;
- if (m_endOfLine)
- return FALSE;
- if (m_isLeadCarraige)
- return TRUE;
- if (m_trailerID==INVALID_ID)
- return TRUE;
- //Explanation: I return that I am a railroad only if I am at one of the ends, otherwise I refuse to cooperate
- return FALSE;
- }
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void RailroadBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
- {
- Object *obj = getObject();
- if ( ! obj)
- return; //sanity
- if ( ! other)
- return; //sanity
- if ( ! loc)
- return; //sanity
- if ( ! normal)
- return; //sanity
- if (m_waitingInWings)
- return ;
- if (m_endOfLine)
- return ;
- for (BehaviorModule** m = other->getBehaviorModules(); *m; ++m)
- {
- CollideModuleInterface* collide = (*m)->getCollide();
- if (!collide)
- continue;
- if( collide->isRailroad())
- {
- if ( m_isLocomotive )//yikes! I just rear ended a stranded train! Oh well...
- {
- other->kill();
- }
- else if ( m_isLeadCarraige ) //yikes! I just coasted into some other carriage
- {
- other->kill();
- obj->kill();//and I am dead, too
- }
-
- return ;//// its a train, folks
- }
- }
- if (other->isKindOf( KINDOF_STRUCTURE ) )// now be careful here, we ignore buildings, except for hostile, nasty ones that can blow us up
- {
- //If it is a civilian building like a tunnel or a train station, let it be
- //but if it is a faction building, kill it!
- if (other->isKindOf( KINDOF_FS_POWER ) ||
- other->isKindOf( KINDOF_FS_FACTORY ) ||
- other->isKindOf( KINDOF_FS_BASE_DEFENSE ) ||
- other->isKindOf( KINDOF_FS_TECHNOLOGY ) ||
- other->isKindOf( KINDOF_REBUILD_HOLE ) )
- {
- playImpactSound(other, other->getPosition());
- other->kill();
- return;
- }
- static NameKeyType key_DemoTrapUpdate = NAMEKEY("DemoTrapUpdate");
- DemoTrapUpdate *dtu = (DemoTrapUpdate*)other->findUpdateModule(key_DemoTrapUpdate);
- if( dtu )
- {
- if( ! BitTest( other-> getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) )
- obj->kill(); // it can only detonate on me if it is ready
- playImpactSound(other, other->getPosition());
- other->kill();
- return;
- }
- }
-
- PhysicsBehavior *theirPhys = other->getPhysics();
- if( ! theirPhys )
- return;
- // maybe we don't want to trample this unit?
- const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
- if ( m_conductorState == WAIT_AT_STATION && (m_pullInfo.speed < modData->m_runningGarrisonSpeedMax) ) // they can grab on safely
- {
- AIUpdateInterface *ai = other->getAI();
- if (ai && ai->getEnterTarget() == obj) // other intends to garrison me.
- {
- return;
- }
- }
- if (other->getContainedBy() == getObject() )//I dont mash my own sweet passengers
- return;
- //if ( other->getModelConditionFlags().test( MODELCONDITION_RUBBLE ) == TRUE )
- // return; // I don't mess with rubble
- Bool victimIsInfantry = other->isKindOf( KINDOF_INFANTRY );
- const Coord3D *myDir = obj->getUnitDirectionVector2D();
- const Coord3D *myLoc = obj->getPosition();
- const Coord3D *theirLoc = other->getPosition();
- //---------------------------
- // if we made it this far, it is something we dont want to share space with
- Coord3D dlt;
- dlt.x = theirLoc->x - myLoc->x;
- dlt.y = theirLoc->y - myLoc->y;
- dlt.z = theirLoc->z - myLoc->z;
- //Alert all the players of recent disaster
- if ( ! m_whistleSound.isCurrentlyPlaying())
- m_whistleSound.setPlayingHandle(TheAudio->addAudioEvent( &m_whistleSound ));
- Real dist = (Real)sqrtf( dlt.x*dlt.x + dlt.y*dlt.y + dlt.z*dlt.z);
- Real usRadius = obj->getGeometryInfo().getMajorRadius();
- Real themRadius = other->getGeometryInfo().getMajorRadius();
- Real overlap = ((usRadius + themRadius) - dist) + 1;// the plus 1 makes them go just outside of me.
- dlt.normalize();
- if ( ! victimIsInfantry )
- {
- overlap/= 4;
- }
- dlt.scale( overlap);
- Coord3D newPos;
- newPos.x = theirLoc->x + dlt.x;
- newPos.y = theirLoc->y + dlt.y;
- newPos.z = theirLoc->z + dlt.z;
- other->setPosition( &newPos );
- AIUpdateInterface *ai = other->getAI();
- if ( ai )
- ai->aiIdle( CMD_FROM_AI );// this eliminates yadda by telling them to stop driving into me
- //---------------------------
- if ( m_conductorState == WAIT_AT_STATION || (m_conductorState == COAST && m_pullInfo.speed < modData->m_runningGarrisonSpeedMax))
- {
- ContainModuleInterface * contain = obj->getContain();
- if ( contain )
- {
- if (contain->isValidContainerFor( other, FALSE ))
- {
- return; // let them out
- }
- }
- else if ( victimIsInfantry )
- {
- return;//let those trying to board pass through unhindered
- }
- }
- //figure out the relative slope between them and me
- Coord3D delta = *theirLoc;
- delta.sub( myLoc );
- delta.normalize();
- Real dot = delta.x * myDir->x + delta.y * myDir->y + delta.z * myDir->z;
- if (other->isEffectivelyDead())
- {// we just run over debris, instead of shoving it around
- delta.scale( MIN(0.3f,m_pullInfo.speed * 0.66f) );
- }
- else
- {
- delta.scale( MIN(1.4f,m_pullInfo.speed * 0.66f) );// the faster I go, the harder I slam!
- //Absolute death to be hit by a train, no survival
- if ( m_pullInfo.speed >= modData->m_killSpeedMin ) // they can grab on safely
- {
- other->kill();
- theirPhys->setPitchRate(GameLogicRandomValueReal(-0.03f, 0.03f));
- theirPhys->setRollRate(GameLogicRandomValueReal(-0.03f, 0.03f));
- }
- else
- {
- playImpactSound(other, loc); //Play a bounce sound
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_CRUSH;
- damageInfo.in.m_deathType = DEATH_CRUSHED;
- damageInfo.in.m_sourceID = getObject()->getID();
- damageInfo.in.m_amount = m_pullInfo.speed * 10; // hurt!
- other->attemptDamage( &damageInfo );
- }
- }
- Coord3D heft = *theirLoc;
- heft.z = MAX(heft.z, TheTerrainLogic->getGroundHeight(heft.x, heft.y) + 2); // lift them off the ground
- other->setPosition(&heft);
- delta.z = GameLogicRandomValueReal(0.05f, m_pullInfo.speed/10); // for some fake heft
- delta.scale(dot);
- // This is a special check so that it wont hurl infantry clear across the map!
- if( ! ( victimIsInfantry && theirPhys->getVelocityMagnitude() > 5.0f ) )
- theirPhys->addVelocityTo( &delta );
- theirPhys->setAllowToFall( TRUE );
- theirPhys->setAllowBouncing( TRUE );
- theirPhys->setAllowAirborneFriction( TRUE );
- const Coord3D up = {0,0,1};
- Coord3D cross;
- myDir->crossProduct( myDir, &up, &cross );
- delta.normalize();
- Real deviationCOG = cross.x * delta.x + cross.y * delta.y + cross.z * delta.z;
- if (dot > 0)
- theirPhys->setYawRate( deviationCOG * -0.06f * m_pullInfo.speed);
-
- } // end RailroadBehavior:: on collide
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::playImpactSound(Object *victim, const Coord3D *impactPosition)
- {
- AudioEventRTS impact;
- //AudioEventRTS *pImpact = &impact;
- PhysicsBehavior *theirPhys = victim->getPhysics();
- Bool hasBounceSound = FALSE;
- if ( theirPhys )
- {
- const AudioEventRTS* bsound = theirPhys->getBounceSound();
- if (bsound)
- {
- impact = *bsound;//copy theirs
- hasBounceSound = TRUE;
- }
- }
- if ( ! hasBounceSound )
- {
- const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
- if (victim->isKindOf( KINDOF_INFANTRY ))
- {
- impact = modData->m_meatyImpactDefaultSound;//copy
- }
- else if (victim->isKindOf( KINDOF_HUGE_VEHICLE ) || victim->isKindOf( KINDOF_STRUCTURE ))
- {
- impact = modData->m_bigMetalImpactDefaultSound;//copy
- }
- else if (victim->isKindOf( KINDOF_VEHICLE ) )
- {
- impact = modData->m_smallMetalImpactDefaultSound;//copy
- }
- }
- if (impact.getEventName().isEmpty())
- return;
- Real vel = NORMAL_VEL_Z; //the max
- Real mass = NORMAL_MASS; //the max
- impact.setPosition(impactPosition);
- if ( theirPhys )
- {
- vel += fabs(theirPhys->getVelocity()->length());
- mass += fabs(theirPhys->getMass());
- vel /= 2;
- mass /= 2;//average of him and me
- impact.setPosition(victim->getPosition());
- }
- vel = MIN(NORMAL_VEL_Z, MAX(0, vel));
- mass = MIN(NORMAL_MASS, MAX(0, mass));
- Real volAdjust = NormalizeToRange(MuLaw(vel, NORMAL_VEL_Z, 500), -1, 1, 0.25, 1.0);
- volAdjust *= NormalizeToRange(MuLaw(mass, NORMAL_MASS, 500), -1, 1, 0.25, 1.0);
- impact.setVolume( volAdjust );
- // This sound really should only be played at the position where the collision occurs.
- // Therefore, set the player index of the sound to be the player index of the victim object.
- impact.setPlayerIndex( victim->getControllingPlayer()->getPlayerIndex() );
- TheAudio->addAudioEvent( &impact );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::loadTrackData( void )
- {
- if ( m_track != NULL )
- return;// lets do this only once!
-
- //First we scan the map for the nearest waypoint
- Object *obj = getObject();
- const Coord3D *myPos = obj->getPosition();
- Coord3D delta;
- Real closestDistance = 99999.9f;
- // m_anchorWaypointID COULD HAVE BEEN RECORDED IN XFER, WHICH MEANS I LOADED MY TRACK DATA IN A PRIOR LIFE,
- // SO LETS JUST RE_INIT THE TRACK BASED ON THAT POINT, AUTOMAGICALLY
- Waypoint *anchorWaypoint = NULL;
- if ( m_anchorWaypointID == INVALID_WAYPOINT_ID )
- {
- Waypoint *anyWaypoint = TheTerrainLogic->getFirstWaypoint();
- while ( anyWaypoint )
- {
- //measure the distance from me to the waypoint
- delta.x = myPos->x - anyWaypoint->getLocation()->x;
- delta.y = myPos->y - anyWaypoint->getLocation()->y;
- delta.z = myPos->z - anyWaypoint->getLocation()->z;
- if (closestDistance > delta.length() )
- {
- closestDistance = delta.length();
- anchorWaypoint = anyWaypoint;
- m_anchorWaypointID = anchorWaypoint->getID();// save for XFER, later
- }
- anyWaypoint = anyWaypoint->getNext();
- }
- }
- else
- {
- anchorWaypoint = TheTerrainLogic->getWaypointByID( m_anchorWaypointID );
- }
- DEBUG_ASSERTCRASH( anchorWaypoint, ( "Railroad... couldn't find a waypoint close enough to be the anchor.\n You should put your train engine right on a waypoint,\n and make sure the path forms a valid track.") );
- if ( ! anchorWaypoint )
- return;
- m_track = NEW( TrainTrack );// this constructor inc's the refcount to 1
-
- // From now until the next carriage is added, this track is writable using getWritablePointList();
- // This method will return NULL when refcount is 2 or more
- // getPointList returns the list as const to any caller
- // each carriage must increment the reference when this pointer is passed to it,
- // any subsequent carriage (destructor) will delete this memory here if it releases refcount to zero
- Coord3D fromPos, toPos, fromToDelta;
- m_track->m_length = 0.0f;
- Waypoint *scanner = anchorWaypoint;
- Real distFromTo = 0.0f;
- //Let's start buliding our own track data from the waypoint data we find
- TrackPointList* track = m_track->getWritablePointList();
- TrackPoint trackPoint; // local workspace
- if ( scanner && track)
- {
- trackPoint.m_distanceFromPrev = 0;
- trackPoint.m_distanceFromFirst = 0;
- trackPoint.m_isFirstPoint = TRUE;
- trackPoint.m_isLastPoint = FALSE;
- trackPoint.m_isTunnelOrBridge = scanner->getName().endsWith("Tunnel");
- trackPoint.m_isStation = scanner->getName().endsWith("Station");
- trackPoint.m_isDisembark = scanner->getName().endsWith("Disembark");
- trackPoint.m_isPingPong = FALSE;
- trackPoint.m_position.set( scanner->getLocation() );
- trackPoint.m_handle = scanner->getID();
- track->push_back( trackPoint );
- }
- while ( scanner )
- {
- trackPoint.clear();// clean up the local workspace to make debugging more fun
- if ( scanner->getNumLinks() )
- {
- Waypoint *anotherWaypoint = scanner->getLink( 0 );
- // if scanner's link is valid, we'll add it to the track. now
- if ( anotherWaypoint != NULL )
- {
- //measure the track while we are at it
- fromPos = *scanner->getLocation();
- toPos = *anotherWaypoint->getLocation();
- fromToDelta.x = fromPos.x - toPos.x;
- fromToDelta.y = fromPos.y - toPos.y;
- fromToDelta.z = fromPos.z - toPos.z;
- distFromTo = fromToDelta.length();
- m_track->m_length += distFromTo;
- trackPoint.m_distanceFromPrev = distFromTo;
- trackPoint.m_distanceFromFirst = m_track->m_length;
- trackPoint.m_isFirstPoint = FALSE;
- trackPoint.m_isLastPoint = anotherWaypoint->getLink( 0 ) == NULL;
- trackPoint.m_isTunnelOrBridge = anotherWaypoint->getName().endsWith("Tunnel");
- trackPoint.m_isStation = anotherWaypoint->getName().endsWith("Station");
- trackPoint.m_isPingPong = scanner->getName().endsWith("PingPong");
- trackPoint.m_isDisembark = scanner->getName().endsWith("Disembark");
- trackPoint.m_position.set( anotherWaypoint->getLocation() );
- trackPoint.m_handle = scanner->getID();
- track->push_back( trackPoint );
- }
- scanner = anotherWaypoint;
- }
- else
- break; // since we have reached a non-linked waypoint
- if ( scanner == anchorWaypoint )
- {
- m_track->m_isLooping = TRUE;
- break; // it must be a looping track. Cool.
- }
- }
- } // end loadTrackData
- // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- UpdateSleepTime RailroadBehavior::update( void )
- {
- // load the waypoint data if not loaded
- if( m_trackDataLoaded == FALSE && m_isLocomotive )
- {
- loadTrackData();
- if ( m_track )
- createCarriages();
- m_trackDataLoaded = TRUE;
- }
- if ( ! m_track )
- {
- return UPDATE_SLEEP_NONE;
- }
- //Object *us = getObject();
- const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
- if ( m_isLocomotive )
- {
- if ( m_conductorState == APPLY_BRAKES )
- {
- conductorPullInfo.speed *= modData->m_braking;
- if (fabs(conductorPullInfo.speed) < 0.01f)
- {
- conductorPullInfo.speed = 0;
- ///////////////////////////////////////( &m_hissySteamSound );
- m_waitAtStationTimer = modData->m_waitAtStationTime;
- m_conductorState = WAIT_AT_STATION;
- if ( m_disembark )
- {
- disembark();
- m_disembark = FALSE;
- }
- }
- }
- else if ( m_conductorState == WAIT_AT_STATION)
- {
- --m_waitAtStationTimer;
- if ( m_waitAtStationTimer <= 0)
- {
- m_conductorState = ACCELERATE;
- conductorPullInfo.speed = 0.05f * conductorPullInfo.m_direction;
- m_runningSound.setPlayingHandle(TheAudio->addAudioEvent( &m_runningSound ));
- }
- else if ( m_waitAtStationTimer == (modData->m_waitAtStationTime/4) )
- {
- m_whistleSound.setPlayingHandle(TheAudio->addAudioEvent( &m_whistleSound ));
- }
- }
- else if ( m_conductorState == ACCELERATE )
- {
- conductorPullInfo.speed += 0.02f * conductorPullInfo.m_direction; // push start multiplier
- conductorPullInfo.speed *= modData->m_acceleration;
- if ( conductorPullInfo.speed > modData->m_speedMax)
- {
- conductorPullInfo.speed = modData->m_speedMax;
- }
- else if ( conductorPullInfo.speed < -modData->m_speedMax)
- {
- conductorPullInfo.speed = -modData->m_speedMax;
- }
-
- if ( ! m_runningSound.isCurrentlyPlaying() )
- m_runningSound.setPlayingHandle(TheAudio->addAudioEvent( &m_runningSound ));
- }
- }
- if ( m_wantsToBeLeadCarraige > FRAMES_UNPULLED_LONG_ENOUGH_TO_UNHITCH )//if this flag survived until now, I have lost my puller
- {
- m_isLeadCarraige = TRUE;
- }
- if ( m_isLeadCarraige )
- {
-
- if ( m_conductorState == COAST )
- {
- conductorPullInfo.speed *= modData->m_friction;
- TheAudio->removeAudioEvent( m_runningSound.getPlayingHandle() );
- }
-
- conductorPullInfo.trackDistance += conductorPullInfo.speed ;
- // only normalize track position for a looping track, otherwise, let train exit by exceeding tracklength
- while ( (conductorPullInfo.trackDistance > m_track->m_length) && m_track->m_isLooping)
- conductorPullInfo.trackDistance -= m_track->m_length;
- while ( (conductorPullInfo.trackDistance < 0.0f ) && m_track->m_isLooping)
- conductorPullInfo.trackDistance += m_track->m_length;
- FindPosByPathDistance( &conductorPullInfo.towHitchPosition,
- conductorPullInfo.trackDistance,
- m_track->m_length);
-
- //let the conductor pull "me" while reseting my info, then...
- updatePositionTrackDistance( &conductorPullInfo, &m_pullInfo);
- //get pointer to my trailer
- Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
- if ( trailer )
- {
- //call his pull trailer with my info;
- static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
- RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
- if( RGUpdate )
- {
- RGUpdate->getPulled( &m_pullInfo );
- }
- }
- else
- {
- m_trailerID = INVALID_ID;// so I will forget about my trailer and designate myself as the caboose
- if ( m_endOfLine )
- TheGameLogic->destroyObject( getObject() );
- }
- }
- else if ( m_wantsToBeLeadCarraige <= FRAMES_UNPULLED_LONG_ENOUGH_TO_UNHITCH )// if I am not the lead carriage
- {
- m_wantsToBeLeadCarraige ++; // like every young carriage, I aspire to be the lead carriage some day
- // unless getpulled() set this false, I will be on the next update! Joy!
- }
- Drawable *draw = getObject()->getDrawable();
- if (draw)
- {
- if ( ! m_track->m_isLooping )
- {
- draw->setDrawableHidden( m_waitingInWings || m_endOfLine );
- }
- // TURN OFF SMOKE EFFECTS IF WE ARE IN A TUNNEL
- const Coord3D *drawPos = draw->getPosition();
- if (drawPos->z < TheTerrainLogic->getGroundHeight(drawPos->x, drawPos->y) - 3.0f )
- draw->setModelConditionState(MODELCONDITION_OVER_WATER);
- else
- draw->clearModelConditionState(MODELCONDITION_OVER_WATER);
-
- }
- return UPDATE_SLEEP_NONE;
- } // end update
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void RailroadBehavior::disembark(void)
- {
- ContainModuleInterface *contain = getObject()->getContain();
- if (contain)
- {
- contain->orderAllPassengersToExit( CMD_FROM_AI );
- }
-
- Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
- if ( trailer )
- {
- static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
- RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
- if( RGUpdate )
- {
- RGUpdate->disembark();
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- class PartitionFilterIsValidCarriage : public PartitionFilter
- {
- private:
- Object* m_obj;
- const RailroadBehaviorModuleData* m_data;
- public:
- PartitionFilterIsValidCarriage(Object* obj, const RailroadBehaviorModuleData* data) : m_obj(obj), m_data(data) { }
-
- #if defined(_DEBUG) || defined(_INTERNAL)
- virtual const char* debugGetName() { return "PartitionFilterIsValidCarriage"; }
- #endif
- virtual Bool allow(Object *objOther)
- {
- // must exist!
- if ( m_obj == NULL || objOther == NULL)
- return FALSE;
- //must not be me!
- if (m_obj == objOther)
- return FALSE;
- // must have railroady goodness
- static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
- RailroadBehavior *rb = (RailroadBehavior*)objOther->findUpdateModule(key_rb);
- if( ! rb )
- return FALSE;
- if ( rb->hasEverBeenHitched() )// two of us can't pull you
- return FALSE;
- // must be our ally (well, maybe)
- if (m_obj->getRelationship(objOther) != ALLIES)
- return FALSE;
- // guess it's carriage-worthy!
- return TRUE;
- }
- };
- void RailroadBehavior::createCarriages( void )
- {
- if ( ! m_isLocomotive )
- {
- DEBUG_ASSERTCRASH(m_isLocomotive, ("a not locomotive attempted to create carriages"));
- return;
- }
- const RailroadBehaviorModuleData* md = getRailroadBehaviorModuleData();
- Object *self = getObject();
- //First we'll see if the map artist put some cars down on the track for us to find
- Real maxRadius = self->getGeometryInfo().getMajorRadius() * 2.0f;
- Coord3D myHitchLoc = *self->getPosition();
- Coord3D hitchOffset = *self->getUnitDirectionVector2D();//copy that
- hitchOffset.scale ( - maxRadius );// negative, since I want the back, not the front
- myHitchLoc.add( & hitchOffset );
- PartitionFilterIsValidCarriage pfivc(self, md);
- PartitionFilter *filters[] = { &pfivc, 0 };
- Object* xferCarriage = NULL;
- Object *closeCarriage = NULL;
- Object *firstCarriage = NULL;
- if ( m_trailerID != INVALID_ID )
- {
- xferCarriage = TheGameLogic->findObjectByID( m_trailerID );
- }
- if (xferCarriage != NULL)
- closeCarriage = xferCarriage;
- else
- closeCarriage = ThePartitionManager->getClosestObject( &myHitchLoc, maxRadius, FROM_CENTER_2D, filters);
- TemplateNameList list = md->m_carriageTemplateNameData;
- TemplateNameIterator iter = list.begin();
- if ( iter != list.end() )
- {
- const ThingTemplate* temp = TheThingFactory->findTemplate( *iter );
- if (temp)
- {
- if ( closeCarriage )
- firstCarriage = closeCarriage;
- else // or else let's use the defualt template list prvided in the INI
- {
- firstCarriage = TheThingFactory->newObject( temp, self->getTeam() );
- DEBUG_LOG(("%s Added a carriage, %s \n", self->getTemplate()->getName().str(),firstCarriage->getTemplate()->getName().str()));
- }
-
- if ( firstCarriage )
- {
- firstCarriage->setProducer(self);
- m_trailerID = firstCarriage->getID();
-
- static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
- RailroadBehavior *rb = (RailroadBehavior*)firstCarriage->findUpdateModule(key_rb);
- if( rb )
- {
- if ( closeCarriage )
- rb->hitchNewCarriagebyProximity( self->getID(), m_track );
- else
- rb->hitchNewCarriagebyTemplate( self->getID(), list, ++iter, m_track );// ! increment iter here!
- }
- else
- {
- DEBUG_ASSERTCRASH( rb,
- ("%s is attempting to hitch carriage, %s without a RailroadBehavior... \nwhat kind of nutty conductor are you? ",
- self->getTemplate()->getName().str(),
- firstCarriage->getTemplate()->getName().str() ) );
- }
- }
- }
- }
- m_carriagesCreated = TRUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::hitchNewCarriagebyTemplate( ObjectID locoID, const TemplateNameVector& list, TemplateNameIterator& iter, TrainTrack *track)
- {
- if ( m_isLocomotive )
- {
- DEBUG_ASSERTCRASH(m_isLocomotive, ("You can not hitch a locomotive in mid train, dude."));
- return;
- }
- Object *locomotive = TheGameLogic->findObjectByID( locoID );
- if ( !locomotive )// sanity
- return;
- m_track = track; //ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
- m_track->incReference();//ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
- m_hasEverBeenHitched = TRUE;// so no other trains try to hitch me onto them
- //Okay that's me, now for the next guy
- //---------------------------------------
- Object *newCarriage = NULL;
- if ( iter != list.end() )// this test is bogus
- {
- const ThingTemplate* temp = TheThingFactory->findTemplate( *iter );
- if (temp)
- {
- newCarriage = TheThingFactory->newObject( temp, locomotive->getTeam() ); // just a little worried about this...
- newCarriage->setProducer(locomotive);
- m_trailerID = newCarriage->getID();
- static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
- RailroadBehavior *rb = (RailroadBehavior*)newCarriage->findUpdateModule(key_rb);
- if( rb )
- {
- rb->hitchNewCarriagebyTemplate( locoID, list, ++iter, m_track );
- }
- else
- {
- DEBUG_ASSERTCRASH( rb,
- ("%s could not hitch a %s without a RailroadBehavior... \nwhat kind of nutty conductor are you? \nThe next carriage would have been a %s.",
- locomotive->getTemplate()->getName().str(),
- newCarriage->getTemplate()->getName().str(),
- *iter
- ) );
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::hitchNewCarriagebyProximity( ObjectID locoID, TrainTrack *track)
- {
- if ( m_isLocomotive )
- {
- DEBUG_ASSERTCRASH(m_isLocomotive, ("You can not hitch a locomotive in mid train, dude."));
- return;
- }
- Object *locomotive = TheGameLogic->findObjectByID( locoID );
- if ( !locomotive )// sanity
- return;
- m_track = track; //ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
- m_track->incReference();//ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
- m_hasEverBeenHitched = TRUE;// so no other trains try to hitch me onto them
- //Okay that's me, now for the next guy
- //---------------------------------------
- //First we'll see if the map artist put some cars down on the track for us to find
- const RailroadBehaviorModuleData* md = getRailroadBehaviorModuleData();
- Object *self = getObject();
- Real maxRadius = self->getGeometryInfo().getMajorRadius() * 2.0f;
- Coord3D myHitchLoc = *self->getPosition();
- Coord3D hitchOffset = *self->getUnitDirectionVector2D();//copy that
- hitchOffset.scale ( - maxRadius );// negative, since I want the back, not the front
- myHitchLoc.add( & hitchOffset );
- PartitionFilterIsValidCarriage pfivc(self, md);
- PartitionFilter *filters[] = { &pfivc, 0 };
- Object* xferCarriage = NULL;
- Object *closeCarriage = NULL;
-
- if ( m_trailerID != INVALID_ID )
- {
- xferCarriage = TheGameLogic->findObjectByID( m_trailerID );
- }
- if (xferCarriage != NULL)
- closeCarriage = xferCarriage;
- else
- closeCarriage = ThePartitionManager->getClosestObject( &myHitchLoc, maxRadius, FROM_CENTER_2D, filters);
-
- if ( closeCarriage )
- {
- closeCarriage->setProducer(self);
- m_trailerID = closeCarriage->getID();
-
- static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
- RailroadBehavior *rb = (RailroadBehavior*)closeCarriage->findUpdateModule(key_rb);
- if( rb )
- rb->hitchNewCarriagebyProximity( self->getID(), m_track );
- else
- {
- DEBUG_ASSERTCRASH( rb,
- ("%s is attempting to hitch carriage, %s without a RailroadBehavior... \nwhat kind of nutty conductor are you? ",
- self->getTemplate()->getName().str(),
- closeCarriage->getTemplate()->getName().str() ) );
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::getPulled( PullInfo *info )
- {
- //ENFORCE MY STATUS AS A PULLEE, NOT A PULLER, and update my position, speed etc.
- m_wantsToBeLeadCarraige = 0;
- if ( ! m_track )
- {
- return;
- }
- conductorPullInfo = *info;//copy my puller to my conductor, so that if I ever get detached
- //( meaning that if this function stops getting called ), I will take over as the lead carriage!
- //Wheeeee!!
- info->previousWaypoint = info->currentWaypoint;
- updatePositionTrackDistance( info, &m_pullInfo );
-
- //get pointer to my trailer
- Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
- if ( trailer )
- {
- //call his pull trailer with MY info;
- static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
- RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
- if( RGUpdate )
- {
- RGUpdate->getPulled( &m_pullInfo ); //< this is MY info, not the one passed to me
- }
- }
- else
- {
- m_trailerID = INVALID_ID;// so I will forget about my trailer and designate myself as the caboose
- if ( m_endOfLine )
- TheGameLogic->destroyObject( getObject() );
- }
- }
- // ------------------------------------------------------------------------------------------------
- void alignToTerrain( Real angle, const Coord3D& pos, const Coord3D& normal, Matrix3D& mtx)
- {
- Coord3D x, y, z;
- z = normal;
- x.x = Cos( angle );
- x.y = Sin( angle );
- x.z = 0.0f;
- if (z.z != 0.0f)
- {
- x.z = -(x.x*z.x + x.y*z.y) / z.z;
- x.normalize();
- }
- DEBUG_ASSERTCRASH(fabs(x.x*z.x + x.y*z.y + x.z*z.z)<0.0001,("dot is not zero\n"));
- // now computing the y vector is trivial.
- y.crossProduct( &z, &x, &y );
- y.normalize();
- mtx.Set( x.x, y.x, z.x, pos.x,
- x.y, y.y, z.y, pos.y,
- x.z, y.z, z.z, pos.z );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::updatePositionTrackDistance( PullInfo *pullerInfo, PullInfo *myInfo )
- {
- if ( ! m_track )
- {
- return;
- }
- Object *obj = BehaviorModule::getObject();
- if ( ! obj )
- return; //sanity
- // IF THIS HAS BEEN CALLED, AM AM GETTING PULLED BY SOMETHING,
- // my conductor, or another car
- Real hitchRadius = obj->getGeometryInfo().getMajorRadius();
- myInfo->trackDistance = pullerInfo->trackDistance - (hitchRadius * 2);
- myInfo->speed = pullerInfo->speed;// YES, if I am getting pulled, I must obey puller
- myInfo->m_direction = pullerInfo->m_direction;// YES, if I am getting pulled, I must obey puller
- // pullerInfo->previousWaypoint = pullerInfo->currentWaypoint;// to feed edge test in update
- // I THINK THE TURN_TO POINT SHOULD BE A BONE ON THE PREVIOUS CAR!!!!!!
- FindPosByPathDistance( &myInfo->towHitchPosition,
- myInfo->trackDistance, // the look ahead
- m_track->m_length);
- Coord3D carPosition;
- FindPosByPathDistance( &carPosition,
- myInfo->trackDistance,
- m_track->m_length, TRUE);
- Coord3D turnPos = *obj->getPosition();
- if (!m_inTunnel)
- turnPos.z = TheTerrainLogic->getGroundHeight( turnPos.x, turnPos.y );
- const Coord3D* dir = obj->getUnitDirectionVector2D();
- turnPos.x += dir->x * -hitchRadius;
- turnPos.y += dir->y * -hitchRadius;
- Coord3D trackPosDelta;
- trackPosDelta.x = carPosition.x - turnPos.x;
- trackPosDelta.y = carPosition.y - turnPos.y;
- trackPosDelta.z = 0;
- Real dx = pullerInfo->towHitchPosition.x - turnPos.x;
- Real dy = pullerInfo->towHitchPosition.y - turnPos.y;
- Real desiredAngle = atan2(dy, dx);
- Real relAngle = stdAngleDiff(desiredAngle, obj->getTransformMatrix()->Get_Z_Rotation());
- Matrix3D mtx;
- Matrix3D tmp(1);
- tmp.Translate(turnPos.x, turnPos.y, 0);
- tmp.Translate(trackPosDelta.x, trackPosDelta.y, 0);
- tmp.In_Place_Pre_Rotate_Z(relAngle );
- tmp.Translate(-turnPos.x, -turnPos.y, 0);
- mtx.mul(tmp, *obj->getTransformMatrix());
- //enforce ground elevation
- Coord3D normal ;
- Real enforceElevation = TheTerrainLogic->getGroundHeight( turnPos.x, turnPos.y, &normal );
-
- obj->setTransformMatrix(&mtx);
- if (!m_inTunnel)
- obj->setPositionZ( enforceElevation );
- obj->handlePartitionCellMaintenance();
- }
- //---------------------------------------------------------------------------------
- //---------------------------------------------------------------------------------
- void RailroadBehavior::destroyTheWholeTrainNow( void )
- {
- TheGameLogic->destroyObject( getObject());
- Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
- if ( trailer )
- {
- static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
- RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
- if( RGUpdate )
- {
- RGUpdate->destroyTheWholeTrainNow(); //< this is MY info, not the one passed to me
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::FindPosByPathDistance( Coord3D *pos, const Real dist, const Real length, Bool setState )
- {
-
- if ( ! m_track )
- return;
- m_waitingInWings = FALSE;
- Real actualDistance = dist;// WRAP TO NORMALIZE
- if ( m_track->m_isLooping )
- {
- while ( actualDistance < 0.0f )
- {
- actualDistance += length;
- }
- while ( actualDistance > length )
- {
- actualDistance -= length;
- }
- }
- else
- {
- if ( dist < 0 )
- {
- actualDistance = 0;
- m_waitingInWings = TRUE;
- }
- else if ( dist >= length )
- {
- actualDistance = length;
- m_endOfLine = TRUE;
- }
- actualDistance = MAX(MIN(length,dist),0); // CLAMP TO NORMALIZE
- }
- pos->set( 0.0f, 0.0f, 0.0f );
- const TrackPointList *pointList = m_track->getPointList();//read-only
- if ( ! pointList )
- return;
- std::list<TrackPoint>::const_iterator pointIter = pointList->begin();
- while ( pointIter != pointList->end() )
- {
- const TrackPoint *thisPoint = &(*pointIter);
- ++pointIter;// next pointIter in this list, so then...
- const TrackPoint *nextPoint = &(*pointIter);
- if (thisPoint && thisPoint->m_distanceFromFirst < actualDistance)// I am after this point, and
- {
- Coord3D thisPointPos = thisPoint->m_position;
-
- if (nextPoint && nextPoint->m_distanceFromFirst > actualDistance)
- {
- //I am between this point and the next
- const Int handleFound = ((TrackPoint*)thisPoint)->getHandle();
- Bool edge = (m_currentPointHandle != handleFound);
- if ( setState )
- {
- m_inTunnel = thisPoint->m_isTunnelOrBridge;
- if ( m_isLocomotive )// since only locomotives care about such things
- {
- // THE LOCOMOTIVE SNIFFS THE TRACK TO SMELL FOR THE NEXT STATION AND TUNNEL
- if ( edge )
- {
- m_currentPointHandle = handleFound;
- if ( thisPoint->m_isStation )
- {
- m_conductorState = APPLY_BRAKES;
- m_disembark = FALSE;
- TheAudio->removeAudioEvent(m_runningSound.getPlayingHandle());
- }
- else if ( thisPoint->m_isDisembark )
- {
- m_conductorState = APPLY_BRAKES;
- m_disembark = TRUE;
- TheAudio->removeAudioEvent(m_runningSound.getPlayingHandle());
- }
- else if ( thisPoint->m_isPingPong && conductorPullInfo.m_mostRecentSpecialPointHandle != handleFound )
- {
- conductorPullInfo.m_mostRecentSpecialPointHandle = handleFound;
- m_conductorState = APPLY_BRAKES;
- m_disembark = FALSE;
- TheAudio->removeAudioEvent(m_runningSound.getPlayingHandle());
- conductorPullInfo.m_direction = -conductorPullInfo.m_direction;
- }
- }
- }
- if ( edge && ! m_inTunnel )
- {//play my clickety clack sound, `cause I just rode over a join IN the tracks
- TheAudio->addAudioEvent( &m_clicketyClackSound );
- m_clicketyClackSound.setPosition( getObject()->getPosition() );
- m_clicketyClackSound.setVolume( (Real)conductorPullInfo.speed / 10.0f );//assumed max speed
- }
- }
- Real difference = actualDistance - thisPoint->m_distanceFromFirst;
- Coord3D delta = nextPoint->m_position;
- delta.sub( &thisPointPos );
- delta.normalize();
- delta.scale( difference );
- thisPointPos.add( &delta );
- *pos = thisPointPos;//copy out
- return;
- }
- else
- *pos = thisPointPos;//copy out
- }
- }
- //DEBUG_ASSERTCRASH(FALSE,("Railroad could not find a position on the path!"));
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version
- * 2: Added... like, everything.
- **/
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // PLEASE NOTE:
- // m_track and trackDataLoaded are indeed NOT xferred,
- // since these are inited afresh within the update module,
- if ( version >= 2 )
- {
- // extend base class
- PhysicsBehavior::xfer( xfer );
-
- //StationTask m_nextStationTask;
- xfer->xferUser( &m_nextStationTask, sizeof( StationTask ) );
-
- //ObjectID m_trailerID; ///< the ID of the object I am directly pulling
- xfer->xferObjectID( &m_trailerID );
-
- //Int m_currentPointHandle;
- xfer->xferInt( &m_currentPointHandle );
-
- //Int m_waitAtStationTimer;
- xfer->xferInt( &m_waitAtStationTimer );
-
- //Bool m_carriagesCreated; ///< TRUE once we have made all the cars in the train
- xfer->xferBool( &m_carriagesCreated );
-
- //Bool m_hasEverBeenHitched; /// has somebody ever hitched me? Remains true, even after puller dies.
- xfer->xferBool( &m_hasEverBeenHitched );
-
- //Bool m_waitingInWings; /// I have not entered the real track yet, so leave me alone
- xfer->xferBool( &m_waitingInWings );
-
- //Bool m_endOfLine; /// I have reached the end of a non looping track
- xfer->xferBool( &m_endOfLine );
-
- //Bool m_isLocomotive; ///< Am I a locomotive,
- xfer->xferBool( &m_isLocomotive );
-
- //Bool m_isLeadCarraige; ///< Am the carraige in front,
- xfer->xferBool( &m_isLeadCarraige );
-
- //Int m_wantsToBeLeadCarraige; ///< Am the carraige in front,
- xfer->xferInt( &m_wantsToBeLeadCarraige );
-
- //Bool m_disembark; ///< If I wait at a station, I should also evacuate everybody when I get theres
- xfer->xferBool( &m_disembark );
-
- //Bool m_inTunnel; ///< Am I in a tunnel, so I wil not snap to ground height, until the next waypoint,
- xfer->xferBool( &m_inTunnel );
-
- //ConductorState m_conductorState;
- xfer->xferUser( &m_conductorState, sizeof( ConductorState ) );
- xfer->xferUser( &m_anchorWaypointID, sizeof( WaypointID ) );
- //PullInfo m_pullInfo;
- m_pullInfo.xferPullInfo( xfer );
- //PullInfo conductorPullInfo;
- conductorPullInfo.xferPullInfo( xfer );
- }
- } // end xfer
- void RailroadBehavior::PullInfo::xferPullInfo( Xfer *xfer )
- {
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferReal( &m_direction);
- xfer->xferReal( &speed);
- xfer->xferReal( &trackDistance);
- xfer->xferCoord3D( &towHitchPosition );
- xfer->xferInt( &m_mostRecentSpecialPointHandle );
- xfer->xferUnsignedInt( &previousWaypoint );
- xfer->xferUnsignedInt( ¤tWaypoint );
- }
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void RailroadBehavior::loadPostProcess( void )
- {
- // extend base class
- PhysicsBehavior::loadPostProcess();
- //Bool m_trackDataLoaded; ///< have I TRIED to load track data, yet? I only try once!
- m_trackDataLoaded = FALSE;
- const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
- m_runningSound = modData->m_runningSound;
- m_clicketyClackSound = modData->m_clicketyClackSound;
- m_whistleSound = modData->m_whistleSound;
- m_runningSound.setObjectID( getObject()->getID() );
- m_whistleSound.setObjectID( getObject()->getID() ) ;
- m_clicketyClackSound.setObjectID( getObject()->getID() ) ;
- } // end loadPostProcess
|