RailroadGuideAIUpdate.cpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: RailroadBehavior.cpp //////////////////////////////////////////////////////////////
  24. // Author: Mark Lorenzen, Sept 2002
  25. // Desc: The railroad track following railroad carriage/locomotive logic module
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h"
  28. #include "Common/Player.h"
  29. #include "Common/ThingFactory.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "GameLogic/Locomotor.h"
  32. #include "GameLogic/Module/RailroadGuideAIUpdate.h"
  33. #include "GameLogic/Object.h"
  34. #include "GameLogic/PartitionManager.h"
  35. #include "GameLogic/Module/ContainModule.h"
  36. #include "GameLogic/Module/DemoTrapUpdate.h"
  37. #include "GameLogic/AI.h"
  38. #include "GameLogic/AIPathfind.h"
  39. #include "GameClient/Drawable.h"
  40. #include "GameClient/Statistics.h"
  41. ///#define RAILROAD_DESYNC_TEST
  42. #ifdef RAILROAD_DESYNC_TEST
  43. #include "winbase.h"
  44. #endif
  45. // TYPES //////////////////////////////////////////////////////////////////////////////////////////
  46. static const Int INVALID_PATH = -1;
  47. #define NORMAL_VEL_Z 0.25f
  48. #define NORMAL_MASS 50.0f
  49. #ifdef _INTERNAL
  50. // for occasional debugging...
  51. //#pragma optimize("", off)
  52. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  53. #endif
  54. #define FRAMES_UNPULLED_LONG_ENOUGH_TO_UNHITCH (2)
  55. void TrainTrack::incReference()
  56. {
  57. ++m_refCount;
  58. }
  59. Bool TrainTrack::releaseReference()
  60. {
  61. return ( --m_refCount == 0 );
  62. }
  63. // ------------------------------------------------------------------------------------------------
  64. // ------------------------------------------------------------------------------------------------
  65. RailroadBehaviorModuleData::RailroadBehaviorModuleData( void )
  66. {
  67. m_carriageTemplateNameData.clear();
  68. m_pathPrefixName.clear();
  69. m_CrashFXTemplateName.clear();
  70. m_isLocomotive = FALSE;
  71. m_runningGarrisonSpeedMax = 1.0f;
  72. m_killSpeedMin = 1.0f;
  73. m_speedMax = 4;
  74. m_acceleration = 1.01f;
  75. m_braking = 0.99f;
  76. m_friction = 0.97f;
  77. m_waitAtStationTime = 150;
  78. } // end RailroadBehaviorModuleData
  79. //-------------------------------------------------------------------------------------------------
  80. //-------------------------------------------------------------------------------------------------
  81. RailroadBehavior::RailroadBehavior( Thing *thing, const ModuleData *moduleData )
  82. : PhysicsBehavior( thing, moduleData )
  83. {
  84. const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
  85. m_carriageTemplateNameIterator = 0;
  86. m_nextStationTask = DO_NOTHING;
  87. m_trailerID = INVALID_ID;
  88. conductorPullInfo.speed = 0;
  89. conductorPullInfo.m_direction = 1.0f;
  90. conductorPullInfo.m_mostRecentSpecialPointHandle = 0xfacade;
  91. conductorPullInfo.towHitchPosition.set(0,0,0);
  92. conductorPullInfo.trackDistance = 0;
  93. conductorPullInfo.currentWaypoint = 0xfacade;
  94. conductorPullInfo.previousWaypoint = 0xfacade;
  95. m_pullInfo.speed = 0.0f;
  96. m_pullInfo.m_direction = 1.0f;
  97. m_pullInfo.m_mostRecentSpecialPointHandle = 0xfacade;
  98. m_pullInfo.towHitchPosition.set(0,0,0);
  99. m_pullInfo.trackDistance = 0.0f;
  100. m_pullInfo.currentWaypoint = 0xfacade;
  101. m_pullInfo.previousWaypoint = 0xfacade;
  102. #ifdef RAILROAD_DESYNC_TEST
  103. _LARGE_INTEGER pc;
  104. QueryPerformanceCounter( &pc ); // absolutely, positively random every call!
  105. Real random = 100000.0f / (Real)pc.LowPart;
  106. conductorPullInfo.m_direction = random;
  107. m_pullInfo.m_direction = random;
  108. #endif
  109. m_runningSound = modData->m_runningSound;
  110. m_clicketyClackSound = modData->m_clicketyClackSound;
  111. m_whistleSound = modData->m_whistleSound;
  112. m_runningSound.setObjectID( getObject()->getID() );
  113. m_whistleSound.setObjectID( getObject()->getID() ) ;
  114. m_clicketyClackSound.setObjectID( getObject()->getID() ) ;
  115. m_track = NULL;
  116. m_currentPointHandle = 0xfacade;
  117. m_waitAtStationTimer = 0;
  118. m_anchorWaypointID = INVALID_WAYPOINT_ID;
  119. m_carriagesCreated = FALSE;
  120. m_hasEverBeenHitched = FALSE;
  121. m_trackDataLoaded = FALSE;
  122. m_waitingInWings = TRUE;
  123. m_endOfLine = FALSE;
  124. m_isLocomotive = modData->m_isLocomotive;
  125. m_isLeadCarraige = m_isLocomotive; // for now, I am the lead, only if I am the locomotive
  126. m_wantsToBeLeadCarraige = FALSE;
  127. m_disembark = FALSE;
  128. m_inTunnel = FALSE;
  129. m_held = FALSE;
  130. m_conductorState = m_isLocomotive ? ACCELERATE : COAST;
  131. } // end RailroadBehavior
  132. //-------------------------------------------------------------------------------------------------
  133. //-------------------------------------------------------------------------------------------------
  134. RailroadBehavior::~RailroadBehavior( void )
  135. {
  136. TheAudio->removeAudioEvent( m_runningSound.getPlayingHandle() );// no more chugchug when I'm dead
  137. if( m_track != NULL )
  138. {
  139. if (m_track->releaseReference())
  140. delete m_track;
  141. m_track = NULL;
  142. }
  143. } // end ~RailroadBehavior
  144. // ------------------------------------------------------------------------------------------------
  145. // ------------------------------------------------------------------------------------------------
  146. Bool RailroadBehavior::isRailroad() const
  147. {
  148. if ( ! m_track )
  149. return FALSE;// I haven't even started yet!
  150. if (m_waitingInWings)
  151. return FALSE;
  152. if (m_endOfLine)
  153. return FALSE;
  154. if (m_isLeadCarraige)
  155. return TRUE;
  156. if (m_trailerID==INVALID_ID)
  157. return TRUE;
  158. //Explanation: I return that I am a railroad only if I am at one of the ends, otherwise I refuse to cooperate
  159. return FALSE;
  160. }
  161. // ------------------------------------------------------------------------------------------------
  162. //-------------------------------------------------------------------------------------------------
  163. void RailroadBehavior::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
  164. {
  165. Object *obj = getObject();
  166. if ( ! obj)
  167. return; //sanity
  168. if ( ! other)
  169. return; //sanity
  170. if ( ! loc)
  171. return; //sanity
  172. if ( ! normal)
  173. return; //sanity
  174. if (m_waitingInWings)
  175. return ;
  176. if (m_endOfLine)
  177. return ;
  178. for (BehaviorModule** m = other->getBehaviorModules(); *m; ++m)
  179. {
  180. CollideModuleInterface* collide = (*m)->getCollide();
  181. if (!collide)
  182. continue;
  183. if( collide->isRailroad())
  184. {
  185. if ( m_isLocomotive )//yikes! I just rear ended a stranded train! Oh well...
  186. {
  187. other->kill();
  188. }
  189. else if ( m_isLeadCarraige ) //yikes! I just coasted into some other carriage
  190. {
  191. other->kill();
  192. obj->kill();//and I am dead, too
  193. }
  194. return ;//// its a train, folks
  195. }
  196. }
  197. if (other->isKindOf( KINDOF_STRUCTURE ) )// now be careful here, we ignore buildings, except for hostile, nasty ones that can blow us up
  198. {
  199. //If it is a civilian building like a tunnel or a train station, let it be
  200. //but if it is a faction building, kill it!
  201. if (other->isKindOf( KINDOF_FS_POWER ) ||
  202. other->isKindOf( KINDOF_FS_FACTORY ) ||
  203. other->isKindOf( KINDOF_FS_BASE_DEFENSE ) ||
  204. other->isKindOf( KINDOF_FS_TECHNOLOGY ) ||
  205. other->isKindOf( KINDOF_REBUILD_HOLE ) )
  206. {
  207. playImpactSound(other, other->getPosition());
  208. other->kill();
  209. return;
  210. }
  211. static NameKeyType key_DemoTrapUpdate = NAMEKEY("DemoTrapUpdate");
  212. DemoTrapUpdate *dtu = (DemoTrapUpdate*)other->findUpdateModule(key_DemoTrapUpdate);
  213. if( dtu )
  214. {
  215. if( !other->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  216. obj->kill(); // it can only detonate on me if it is ready
  217. playImpactSound(other, other->getPosition());
  218. other->kill();
  219. return;
  220. }
  221. }
  222. PhysicsBehavior *theirPhys = other->getPhysics();
  223. if( ! theirPhys )
  224. return;
  225. // maybe we don't want to trample this unit?
  226. const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
  227. if ( m_conductorState == WAIT_AT_STATION && (m_pullInfo.speed < modData->m_runningGarrisonSpeedMax) ) // they can grab on safely
  228. {
  229. AIUpdateInterface *ai = other->getAI();
  230. if (ai && ai->getEnterTarget() == obj) // other intends to garrison me.
  231. {
  232. return;
  233. }
  234. }
  235. if (other->getContainedBy() == getObject() )//I dont mash my own sweet passengers
  236. return;
  237. //if ( other->getModelConditionFlags().test( MODELCONDITION_RUBBLE ) == TRUE )
  238. // return; // I don't mess with rubble
  239. Bool victimIsInfantry = other->isKindOf( KINDOF_INFANTRY );
  240. const Coord3D *myDir = obj->getUnitDirectionVector2D();
  241. const Coord3D *myLoc = obj->getPosition();
  242. const Coord3D *theirLoc = other->getPosition();
  243. //---------------------------
  244. // if we made it this far, it is something we dont want to share space with
  245. Coord3D dlt;
  246. dlt.x = theirLoc->x - myLoc->x;
  247. dlt.y = theirLoc->y - myLoc->y;
  248. dlt.z = theirLoc->z - myLoc->z;
  249. //Alert all the players of recent disaster
  250. if ( ! m_whistleSound.isCurrentlyPlaying())
  251. m_whistleSound.setPlayingHandle(TheAudio->addAudioEvent( &m_whistleSound ));
  252. Real dist = (Real)sqrtf( dlt.x*dlt.x + dlt.y*dlt.y + dlt.z*dlt.z);
  253. Real usRadius = obj->getGeometryInfo().getMajorRadius();
  254. Real themRadius = other->getGeometryInfo().getMajorRadius();
  255. Real overlap = ((usRadius + themRadius) - dist) + 1;// the plus 1 makes them go just outside of me.
  256. dlt.normalize();
  257. if ( ! victimIsInfantry )
  258. {
  259. overlap/= 4;
  260. dlt.scale( overlap);
  261. Coord3D newPos;
  262. newPos.x = theirLoc->x + dlt.x;
  263. newPos.y = theirLoc->y + dlt.y;
  264. newPos.z = theirLoc->z + dlt.z;
  265. other->setPosition( &newPos );
  266. }
  267. if ( m_conductorState == WAIT_AT_STATION || (m_conductorState == COAST && m_pullInfo.speed < modData->m_runningGarrisonSpeedMax) || !m_isLocomotive )
  268. {
  269. // AIUpdateInterface *ai = other->getAI();
  270. // if ( ai )
  271. // ai->aiIdle( CMD_FROM_AI );// this eliminates yadda by telling them to stop driving into me
  272. return;//let those trying to board pass through unhindered
  273. }
  274. //figure out the relative slope between them and me
  275. Coord3D delta = *theirLoc;
  276. delta.sub( myLoc );
  277. delta.normalize();
  278. Real dot = delta.x * myDir->x + delta.y * myDir->y + delta.z * myDir->z;
  279. if (other->isEffectivelyDead())
  280. {// we just run over debris, instead of shoving it around
  281. delta.scale( MIN(0.3f,m_pullInfo.speed * 0.66f) );
  282. }
  283. else
  284. {
  285. delta.scale( MIN(1.4f,m_pullInfo.speed * 0.66f) );// the faster I go, the harder I slam!
  286. //Absolute death to be hit by a train, no survival
  287. if ( m_pullInfo.speed >= modData->m_killSpeedMin ) // they can grab on safely
  288. {
  289. other->kill();
  290. theirPhys->setPitchRate(GameLogicRandomValueReal(-0.03f, 0.03f));
  291. theirPhys->setRollRate(GameLogicRandomValueReal(-0.03f, 0.03f));
  292. }
  293. else
  294. {
  295. playImpactSound(other, loc); //Play a bounce sound
  296. DamageInfo damageInfo;
  297. damageInfo.in.m_damageType = DAMAGE_CRUSH;
  298. damageInfo.in.m_deathType = DEATH_CRUSHED;
  299. damageInfo.in.m_sourceID = getObject()->getID();
  300. damageInfo.in.m_amount = m_pullInfo.speed * 10; // hurt!
  301. other->attemptDamage( &damageInfo );
  302. }
  303. }
  304. Coord3D heft = *theirLoc;
  305. heft.z = MAX(heft.z, TheTerrainLogic->getGroundHeight(heft.x, heft.y) + 2); // lift them off the ground
  306. other->setPosition(&heft);
  307. delta.z = GameLogicRandomValueReal(0.05f, m_pullInfo.speed/10); // for some fake heft
  308. delta.scale(dot);
  309. // This is a special check so that it wont hurl infantry clear across the map!
  310. if( ! ( victimIsInfantry && theirPhys->getVelocityMagnitude() > 5.0f ) )
  311. theirPhys->addVelocityTo( &delta );
  312. theirPhys->setAllowToFall( TRUE );
  313. theirPhys->setAllowBouncing( TRUE );
  314. theirPhys->setAllowAirborneFriction( TRUE );
  315. const Coord3D up = {0,0,1};
  316. Coord3D cross;
  317. myDir->crossProduct( myDir, &up, &cross );
  318. delta.normalize();
  319. Real deviationCOG = cross.x * delta.x + cross.y * delta.y + cross.z * delta.z;
  320. if (dot > 0)
  321. theirPhys->setYawRate( deviationCOG * -0.06f * m_pullInfo.speed);
  322. } // end RailroadBehavior:: on collide
  323. // ------------------------------------------------------------------------------------------------
  324. // ------------------------------------------------------------------------------------------------
  325. void RailroadBehavior::playImpactSound(Object *victim, const Coord3D *impactPosition)
  326. {
  327. AudioEventRTS impact;
  328. //AudioEventRTS *pImpact = &impact;
  329. PhysicsBehavior *theirPhys = victim->getPhysics();
  330. Bool hasBounceSound = FALSE;
  331. if ( theirPhys )
  332. {
  333. const AudioEventRTS* bsound = theirPhys->getBounceSound();
  334. if (bsound)
  335. {
  336. impact = *bsound;//copy theirs
  337. hasBounceSound = TRUE;
  338. }
  339. }
  340. if ( ! hasBounceSound )
  341. {
  342. const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
  343. if (victim->isKindOf( KINDOF_INFANTRY ))
  344. {
  345. impact = modData->m_meatyImpactDefaultSound;//copy
  346. }
  347. else if (victim->isKindOf( KINDOF_HUGE_VEHICLE ) || victim->isKindOf( KINDOF_STRUCTURE ))
  348. {
  349. impact = modData->m_bigMetalImpactDefaultSound;//copy
  350. }
  351. else if (victim->isKindOf( KINDOF_VEHICLE ) )
  352. {
  353. impact = modData->m_smallMetalImpactDefaultSound;//copy
  354. }
  355. }
  356. if (impact.getEventName().isEmpty())
  357. return;
  358. Real vel = NORMAL_VEL_Z; //the max
  359. Real mass = NORMAL_MASS; //the max
  360. impact.setPosition(impactPosition);
  361. if ( theirPhys )
  362. {
  363. vel += fabs(theirPhys->getVelocity()->length());
  364. mass += fabs(theirPhys->getMass());
  365. vel /= 2;
  366. mass /= 2;//average of him and me
  367. impact.setPosition(victim->getPosition());
  368. }
  369. vel = MIN(NORMAL_VEL_Z, MAX(0, vel));
  370. mass = MIN(NORMAL_MASS, MAX(0, mass));
  371. Real volAdjust = NormalizeToRange(MuLaw(vel, NORMAL_VEL_Z, 500), -1, 1, 0.25, 1.0);
  372. volAdjust *= NormalizeToRange(MuLaw(mass, NORMAL_MASS, 500), -1, 1, 0.25, 1.0);
  373. impact.setVolume( volAdjust );
  374. // This sound really should only be played at the position where the collision occurs.
  375. // Therefore, set the player index of the sound to be the player index of the victim object.
  376. impact.setPlayerIndex( victim->getControllingPlayer()->getPlayerIndex() );
  377. TheAudio->addAudioEvent( &impact );
  378. }
  379. // ------------------------------------------------------------------------------------------------
  380. // ------------------------------------------------------------------------------------------------
  381. void RailroadBehavior::loadTrackData( void )
  382. {
  383. if ( m_track != NULL )
  384. return;// lets do this only once!
  385. //First we scan the map for the nearest waypoint
  386. Object *obj = getObject();
  387. const Coord3D *myPos = obj->getPosition();
  388. Coord3D delta;
  389. Real closestDistance = 99999.9f;
  390. // m_anchorWaypointID COULD HAVE BEEN RECORDED IN XFER, WHICH MEANS I LOADED MY TRACK DATA IN A PRIOR LIFE,
  391. // SO LETS JUST RE_INIT THE TRACK BASED ON THAT POINT, AUTOMAGICALLY
  392. Waypoint *anchorWaypoint = NULL;
  393. if ( m_anchorWaypointID == INVALID_WAYPOINT_ID )
  394. {
  395. Waypoint *anyWaypoint = TheTerrainLogic->getFirstWaypoint();
  396. while ( anyWaypoint )
  397. {
  398. //measure the distance from me to the waypoint
  399. delta.x = myPos->x - anyWaypoint->getLocation()->x;
  400. delta.y = myPos->y - anyWaypoint->getLocation()->y;
  401. delta.z = myPos->z - anyWaypoint->getLocation()->z;
  402. if (closestDistance > delta.length() )
  403. {
  404. closestDistance = delta.length();
  405. anchorWaypoint = anyWaypoint;
  406. m_anchorWaypointID = anchorWaypoint->getID();// save for XFER, later
  407. }
  408. anyWaypoint = anyWaypoint->getNext();
  409. }
  410. }
  411. else
  412. {
  413. anchorWaypoint = TheTerrainLogic->getWaypointByID( m_anchorWaypointID );
  414. }
  415. 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.") );
  416. if ( ! anchorWaypoint )
  417. return;
  418. m_track = NEW( TrainTrack );// this constructor inc's the refcount to 1
  419. // From now until the next carriage is added, this track is writable using getWritablePointList();
  420. // This method will return NULL when refcount is 2 or more
  421. // getPointList returns the list as const to any caller
  422. // each carriage must increment the reference when this pointer is passed to it,
  423. // any subsequent carriage (destructor) will delete this memory here if it releases refcount to zero
  424. Coord3D fromPos, toPos, fromToDelta;
  425. m_track->m_length = 0.0f;
  426. Waypoint *scanner = anchorWaypoint;
  427. Real distFromTo = 0.0f;
  428. //Let's start buliding our own track data from the waypoint data we find
  429. TrackPointList* track = m_track->getWritablePointList();
  430. TrackPoint trackPoint; // local workspace
  431. if ( scanner && track)
  432. {
  433. trackPoint.m_distanceFromPrev = 0;
  434. trackPoint.m_distanceFromFirst = 0;
  435. trackPoint.m_isFirstPoint = TRUE;
  436. trackPoint.m_isLastPoint = FALSE;
  437. trackPoint.m_isTunnelOrBridge = scanner->getName().endsWith("Tunnel");
  438. trackPoint.m_isStation = scanner->getName().endsWith("Station");
  439. trackPoint.m_isDisembark = scanner->getName().endsWith("Disembark");
  440. trackPoint.m_isPingPong = FALSE;
  441. trackPoint.m_position.set( scanner->getLocation() );
  442. trackPoint.m_handle = scanner->getID();
  443. track->push_back( trackPoint );
  444. }
  445. while ( scanner )
  446. {
  447. trackPoint.clear();// clean up the local workspace to make debugging more fun
  448. if ( scanner->getNumLinks() )
  449. {
  450. Waypoint *anotherWaypoint = scanner->getLink( 0 );
  451. // if scanner's link is valid, we'll add it to the track. now
  452. if ( anotherWaypoint != NULL )
  453. {
  454. //measure the track while we are at it
  455. fromPos = *scanner->getLocation();
  456. toPos = *anotherWaypoint->getLocation();
  457. fromToDelta.x = fromPos.x - toPos.x;
  458. fromToDelta.y = fromPos.y - toPos.y;
  459. fromToDelta.z = fromPos.z - toPos.z;
  460. distFromTo = fromToDelta.length();
  461. m_track->m_length += distFromTo;
  462. trackPoint.m_distanceFromPrev = distFromTo;
  463. trackPoint.m_distanceFromFirst = m_track->m_length;
  464. trackPoint.m_isFirstPoint = FALSE;
  465. trackPoint.m_isLastPoint = anotherWaypoint->getLink( 0 ) == NULL;
  466. trackPoint.m_isTunnelOrBridge = anotherWaypoint->getName().endsWith("Tunnel");
  467. trackPoint.m_isStation = anotherWaypoint->getName().endsWith("Station");
  468. trackPoint.m_isPingPong = scanner->getName().endsWith("PingPong");
  469. trackPoint.m_isDisembark = scanner->getName().endsWith("Disembark");
  470. trackPoint.m_position.set( anotherWaypoint->getLocation() );
  471. trackPoint.m_handle = scanner->getID();
  472. track->push_back( trackPoint );
  473. }
  474. scanner = anotherWaypoint;
  475. }
  476. else
  477. break; // since we have reached a non-linked waypoint
  478. if ( scanner == anchorWaypoint )
  479. {
  480. m_track->m_isLooping = TRUE;
  481. break; // it must be a looping track. Cool.
  482. }
  483. }
  484. } // end loadTrackData
  485. void RailroadBehavior::makeAWallOutOfThisTrain( Bool on )
  486. {
  487. if ( on == TRUE )
  488. TheAI->pathfinder()->createAWallFromMyFootprint( getObject() ); // Temporarily treat this object as an obstacle.
  489. else
  490. TheAI->pathfinder()->removeWallFromMyFootprint( getObject() ); // Undo createAWallFromMyFootprint.
  491. if ( m_trailerID != INVALID_ID )
  492. {
  493. Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
  494. if ( trailer )
  495. {
  496. static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
  497. RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
  498. if( RGUpdate )
  499. {
  500. RGUpdate->makeAWallOutOfThisTrain( on ); // recursive down the train
  501. }
  502. }
  503. }
  504. }
  505. // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  506. UpdateSleepTime RailroadBehavior::update( void )
  507. {
  508. // load the waypoint data if not loaded
  509. if( m_trackDataLoaded == FALSE && m_isLocomotive )
  510. {
  511. loadTrackData();
  512. if ( m_track )
  513. createCarriages();
  514. m_trackDataLoaded = TRUE;
  515. }
  516. if ( ! m_track )
  517. {
  518. return UPDATE_SLEEP_NONE;
  519. }
  520. //Object *us = getObject();
  521. const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
  522. if ( m_isLocomotive )
  523. {
  524. if ( m_conductorState == APPLY_BRAKES )
  525. {
  526. conductorPullInfo.speed *= modData->m_braking;
  527. if (fabs(conductorPullInfo.speed) < 0.1f)
  528. {
  529. conductorPullInfo.speed = 0;
  530. ///////////////////////////////////////( &m_hissySteamSound );
  531. m_waitAtStationTimer = modData->m_waitAtStationTime;
  532. m_conductorState = WAIT_AT_STATION;
  533. makeAWallOutOfThisTrain( TRUE );
  534. if ( m_disembark )
  535. {
  536. disembark();
  537. m_disembark = FALSE;
  538. }
  539. }
  540. }
  541. else if ( m_conductorState == WAIT_AT_STATION)
  542. {
  543. --m_waitAtStationTimer;
  544. if ( m_waitAtStationTimer <= 0 && (!m_held) )
  545. {
  546. m_conductorState = ACCELERATE;
  547. conductorPullInfo.speed = 0.05f * conductorPullInfo.m_direction;
  548. m_runningSound.setPlayingHandle(TheAudio->addAudioEvent( &m_runningSound ));
  549. makeAWallOutOfThisTrain( FALSE );
  550. }
  551. else if ( m_waitAtStationTimer == (modData->m_waitAtStationTime/4) )
  552. {
  553. m_whistleSound.setPlayingHandle(TheAudio->addAudioEvent( &m_whistleSound ));
  554. }
  555. }
  556. else if ( m_conductorState == ACCELERATE )
  557. {
  558. conductorPullInfo.speed += 0.02f * conductorPullInfo.m_direction; // push start multiplier
  559. conductorPullInfo.speed *= modData->m_acceleration;
  560. if ( conductorPullInfo.speed > modData->m_speedMax)
  561. {
  562. conductorPullInfo.speed = modData->m_speedMax;
  563. }
  564. else if ( conductorPullInfo.speed < -modData->m_speedMax)
  565. {
  566. conductorPullInfo.speed = -modData->m_speedMax;
  567. }
  568. if ( ! m_runningSound.isCurrentlyPlaying() )
  569. m_runningSound.setPlayingHandle(TheAudio->addAudioEvent( &m_runningSound ));
  570. }
  571. }
  572. if ( m_wantsToBeLeadCarraige > FRAMES_UNPULLED_LONG_ENOUGH_TO_UNHITCH )//if this flag survived until now, I have lost my puller
  573. {
  574. m_isLeadCarraige = TRUE;
  575. }
  576. if ( m_isLeadCarraige )
  577. {
  578. if ( m_conductorState == COAST )
  579. {
  580. conductorPullInfo.speed *= modData->m_friction;
  581. TheAudio->removeAudioEvent( m_runningSound.getPlayingHandle() );
  582. }
  583. conductorPullInfo.trackDistance += conductorPullInfo.speed ;
  584. // only normalize track position for a looping track, otherwise, let train exit by exceeding tracklength
  585. while ( (conductorPullInfo.trackDistance > m_track->m_length) && m_track->m_isLooping)
  586. conductorPullInfo.trackDistance -= m_track->m_length;
  587. while ( (conductorPullInfo.trackDistance < 0.0f ) && m_track->m_isLooping)
  588. conductorPullInfo.trackDistance += m_track->m_length;
  589. FindPosByPathDistance( &conductorPullInfo.towHitchPosition,
  590. conductorPullInfo.trackDistance,
  591. m_track->m_length);
  592. //let the conductor pull "me" while reseting my info, then...
  593. updatePositionTrackDistance( &conductorPullInfo, &m_pullInfo);
  594. //get pointer to my trailer
  595. Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
  596. if ( trailer )
  597. {
  598. //call his pull trailer with my info;
  599. static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
  600. RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
  601. if( RGUpdate )
  602. {
  603. RGUpdate->getPulled( &m_pullInfo );
  604. }
  605. }
  606. else
  607. {
  608. m_trailerID = INVALID_ID;// so I will forget about my trailer and designate myself as the caboose
  609. if ( m_endOfLine )
  610. TheGameLogic->destroyObject( getObject() );
  611. }
  612. }
  613. else if ( m_wantsToBeLeadCarraige <= FRAMES_UNPULLED_LONG_ENOUGH_TO_UNHITCH )// if I am not the lead carriage
  614. {
  615. m_wantsToBeLeadCarraige ++; // like every young carriage, I aspire to be the lead carriage some day
  616. // unless getpulled() set this false, I will be on the next update! Joy!
  617. }
  618. Drawable *draw = getObject()->getDrawable();
  619. if (draw)
  620. {
  621. if ( ! m_track->m_isLooping )
  622. {
  623. draw->setDrawableHidden( m_waitingInWings || m_endOfLine );
  624. }
  625. // TURN OFF SMOKE EFFECTS IF WE ARE IN A TUNNEL
  626. const Coord3D *drawPos = draw->getPosition();
  627. if (drawPos->z < TheTerrainLogic->getGroundHeight(drawPos->x, drawPos->y) - 3.0f )
  628. draw->setModelConditionState(MODELCONDITION_OVER_WATER);
  629. else
  630. draw->clearModelConditionState(MODELCONDITION_OVER_WATER);
  631. }
  632. return UPDATE_SLEEP_NONE;
  633. } // end update
  634. // ------------------------------------------------------------------------------------------------
  635. //-------------------------------------------------------------------------------------------------
  636. void RailroadBehavior::disembark(void)
  637. {
  638. ContainModuleInterface *contain = getObject()->getContain();
  639. if (contain)
  640. {
  641. contain->orderAllPassengersToExit( CMD_FROM_AI, FALSE );
  642. }
  643. Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
  644. if ( trailer )
  645. {
  646. static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
  647. RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
  648. if( RGUpdate )
  649. {
  650. RGUpdate->disembark();
  651. }
  652. }
  653. }
  654. ///////////////////////////////////////////////////////////////////////////////////////////////////
  655. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  656. ///////////////////////////////////////////////////////////////////////////////////////////////////
  657. class PartitionFilterIsValidCarriage : public PartitionFilter
  658. {
  659. private:
  660. Object* m_obj;
  661. const RailroadBehaviorModuleData* m_data;
  662. public:
  663. PartitionFilterIsValidCarriage(Object* obj, const RailroadBehaviorModuleData* data) : m_obj(obj), m_data(data) { }
  664. #if defined(_DEBUG) || defined(_INTERNAL)
  665. virtual const char* debugGetName() { return "PartitionFilterIsValidCarriage"; }
  666. #endif
  667. virtual Bool allow(Object *objOther)
  668. {
  669. // must exist!
  670. if ( m_obj == NULL || objOther == NULL)
  671. return FALSE;
  672. //must not be me!
  673. if (m_obj == objOther)
  674. return FALSE;
  675. // must have railroady goodness
  676. static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
  677. RailroadBehavior *rb = (RailroadBehavior*)objOther->findUpdateModule(key_rb);
  678. if( ! rb )
  679. return FALSE;
  680. if ( rb->hasEverBeenHitched() )// two of us can't pull you
  681. return FALSE;
  682. // must be our ally (well, maybe)
  683. if (m_obj->getRelationship(objOther) != ALLIES)
  684. return FALSE;
  685. // guess it's carriage-worthy!
  686. return TRUE;
  687. }
  688. };
  689. void RailroadBehavior::createCarriages( void )
  690. {
  691. if ( ! m_isLocomotive )
  692. {
  693. DEBUG_ASSERTCRASH(m_isLocomotive, ("a not locomotive attempted to create carriages"));
  694. return;
  695. }
  696. const RailroadBehaviorModuleData* md = getRailroadBehaviorModuleData();
  697. Object *self = getObject();
  698. //First we'll see if the map artist put some cars down on the track for us to find
  699. Real maxRadius = self->getGeometryInfo().getMajorRadius() * 2.0f;
  700. Coord3D myHitchLoc = *self->getPosition();
  701. Coord3D hitchOffset = *self->getUnitDirectionVector2D();//copy that
  702. hitchOffset.scale ( - maxRadius );// negative, since I want the back, not the front
  703. myHitchLoc.add( & hitchOffset );
  704. PartitionFilterIsValidCarriage pfivc(self, md);
  705. PartitionFilter *filters[] = { &pfivc, 0 };
  706. Object* xferCarriage = NULL;
  707. Object *closeCarriage = NULL;
  708. Object *firstCarriage = NULL;
  709. if ( m_trailerID != INVALID_ID )
  710. {
  711. xferCarriage = TheGameLogic->findObjectByID( m_trailerID );
  712. }
  713. if (xferCarriage != NULL)
  714. closeCarriage = xferCarriage;
  715. else
  716. closeCarriage = ThePartitionManager->getClosestObject( &myHitchLoc, maxRadius, FROM_CENTER_2D, filters);
  717. TemplateNameList list = md->m_carriageTemplateNameData;
  718. TemplateNameIterator iter = list.begin();
  719. if ( iter != list.end() )
  720. {
  721. const ThingTemplate* temp = TheThingFactory->findTemplate( *iter );
  722. if (temp)
  723. {
  724. if ( closeCarriage )
  725. firstCarriage = closeCarriage;
  726. else // or else let's use the defualt template list prvided in the INI
  727. {
  728. firstCarriage = TheThingFactory->newObject( temp, self->getTeam() );
  729. DEBUG_LOG(("%s Added a carriage, %s \n", self->getTemplate()->getName().str(),firstCarriage->getTemplate()->getName().str()));
  730. }
  731. if ( firstCarriage )
  732. {
  733. firstCarriage->setProducer(self);
  734. m_trailerID = firstCarriage->getID();
  735. static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
  736. RailroadBehavior *rb = (RailroadBehavior*)firstCarriage->findUpdateModule(key_rb);
  737. if( rb )
  738. {
  739. if ( closeCarriage )
  740. rb->hitchNewCarriagebyProximity( self->getID(), m_track );
  741. else
  742. rb->hitchNewCarriagebyTemplate( self->getID(), list, ++iter, m_track );// ! increment iter here!
  743. }
  744. else
  745. {
  746. DEBUG_ASSERTCRASH( rb,
  747. ("%s is attempting to hitch carriage, %s without a RailroadBehavior... \nwhat kind of nutty conductor are you? ",
  748. self->getTemplate()->getName().str(),
  749. firstCarriage->getTemplate()->getName().str() ) );
  750. }
  751. }
  752. }
  753. }
  754. m_carriagesCreated = TRUE;
  755. }
  756. // ------------------------------------------------------------------------------------------------
  757. // ------------------------------------------------------------------------------------------------
  758. void RailroadBehavior::hitchNewCarriagebyTemplate( ObjectID locoID, const TemplateNameVector& list, TemplateNameIterator& iter, TrainTrack *track)
  759. {
  760. if ( m_isLocomotive )
  761. {
  762. DEBUG_ASSERTCRASH(m_isLocomotive, ("You can not hitch a locomotive in mid train, dude."));
  763. return;
  764. }
  765. Object *locomotive = TheGameLogic->findObjectByID( locoID );
  766. if ( !locomotive )// sanity
  767. return;
  768. m_track = track; //ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
  769. m_track->incReference();//ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
  770. m_hasEverBeenHitched = TRUE;// so no other trains try to hitch me onto them
  771. //Okay that's me, now for the next guy
  772. //---------------------------------------
  773. Object *newCarriage = NULL;
  774. if ( iter != list.end() )// this test is bogus
  775. {
  776. const ThingTemplate* temp = TheThingFactory->findTemplate( *iter );
  777. if (temp)
  778. {
  779. newCarriage = TheThingFactory->newObject( temp, locomotive->getTeam() ); // just a little worried about this...
  780. newCarriage->setProducer(locomotive);
  781. m_trailerID = newCarriage->getID();
  782. static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
  783. RailroadBehavior *rb = (RailroadBehavior*)newCarriage->findUpdateModule(key_rb);
  784. if( rb )
  785. {
  786. rb->hitchNewCarriagebyTemplate( locoID, list, ++iter, m_track );
  787. }
  788. else
  789. {
  790. DEBUG_ASSERTCRASH( rb,
  791. ("%s could not hitch a %s without a RailroadBehavior... \nwhat kind of nutty conductor are you? \nThe next carriage would have been a %s.",
  792. locomotive->getTemplate()->getName().str(),
  793. newCarriage->getTemplate()->getName().str(),
  794. *iter
  795. ) );
  796. }
  797. }
  798. }
  799. }
  800. // ------------------------------------------------------------------------------------------------
  801. // ------------------------------------------------------------------------------------------------
  802. void RailroadBehavior::hitchNewCarriagebyProximity( ObjectID locoID, TrainTrack *track)
  803. {
  804. if ( m_isLocomotive )
  805. {
  806. DEBUG_ASSERTCRASH(m_isLocomotive, ("You can not hitch a locomotive in mid train, dude."));
  807. return;
  808. }
  809. Object *locomotive = TheGameLogic->findObjectByID( locoID );
  810. if ( !locomotive )// sanity
  811. return;
  812. m_track = track; //ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
  813. m_track->incReference();//ALWAYS INCREFERENCE WHENEVER YOU INHERIT THIS POINTER
  814. m_hasEverBeenHitched = TRUE;// so no other trains try to hitch me onto them
  815. //Okay that's me, now for the next guy
  816. //---------------------------------------
  817. //First we'll see if the map artist put some cars down on the track for us to find
  818. const RailroadBehaviorModuleData* md = getRailroadBehaviorModuleData();
  819. Object *self = getObject();
  820. Real maxRadius = self->getGeometryInfo().getMajorRadius() * 2.0f;
  821. Coord3D myHitchLoc = *self->getPosition();
  822. Coord3D hitchOffset = *self->getUnitDirectionVector2D();//copy that
  823. hitchOffset.scale ( - maxRadius );// negative, since I want the back, not the front
  824. myHitchLoc.add( & hitchOffset );
  825. PartitionFilterIsValidCarriage pfivc(self, md);
  826. PartitionFilter *filters[] = { &pfivc, 0 };
  827. Object* xferCarriage = NULL;
  828. Object *closeCarriage = NULL;
  829. if ( m_trailerID != INVALID_ID )
  830. {
  831. xferCarriage = TheGameLogic->findObjectByID( m_trailerID );
  832. }
  833. if (xferCarriage != NULL)
  834. closeCarriage = xferCarriage;
  835. else
  836. closeCarriage = ThePartitionManager->getClosestObject( &myHitchLoc, maxRadius, FROM_CENTER_2D, filters);
  837. if ( closeCarriage )
  838. {
  839. closeCarriage->setProducer(self);
  840. m_trailerID = closeCarriage->getID();
  841. static NameKeyType key_rb = NAMEKEY("RailroadBehavior");
  842. RailroadBehavior *rb = (RailroadBehavior*)closeCarriage->findUpdateModule(key_rb);
  843. if( rb )
  844. rb->hitchNewCarriagebyProximity( self->getID(), m_track );
  845. else
  846. {
  847. DEBUG_ASSERTCRASH( rb,
  848. ("%s is attempting to hitch carriage, %s without a RailroadBehavior... \nwhat kind of nutty conductor are you? ",
  849. self->getTemplate()->getName().str(),
  850. closeCarriage->getTemplate()->getName().str() ) );
  851. }
  852. }
  853. }
  854. // ------------------------------------------------------------------------------------------------
  855. // ------------------------------------------------------------------------------------------------
  856. void RailroadBehavior::getPulled( PullInfo *info )
  857. {
  858. //ENFORCE MY STATUS AS A PULLEE, NOT A PULLER, and update my position, speed etc.
  859. m_wantsToBeLeadCarraige = 0;
  860. if ( ! m_track )
  861. {
  862. return;
  863. }
  864. conductorPullInfo = *info;//copy my puller to my conductor, so that if I ever get detached
  865. //( meaning that if this function stops getting called ), I will take over as the lead carriage!
  866. //Wheeeee!!
  867. info->previousWaypoint = info->currentWaypoint;
  868. updatePositionTrackDistance( info, &m_pullInfo );
  869. //get pointer to my trailer
  870. Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
  871. if ( trailer )
  872. {
  873. //call his pull trailer with MY info;
  874. static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
  875. RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
  876. if( RGUpdate )
  877. {
  878. RGUpdate->getPulled( &m_pullInfo ); //< this is MY info, not the one passed to me
  879. }
  880. }
  881. else
  882. {
  883. m_trailerID = INVALID_ID;// so I will forget about my trailer and designate myself as the caboose
  884. if ( m_endOfLine )
  885. TheGameLogic->destroyObject( getObject() );
  886. }
  887. }
  888. // ------------------------------------------------------------------------------------------------
  889. void alignToTerrain( Real angle, const Coord3D& pos, const Coord3D& normal, Matrix3D& mtx)
  890. {
  891. Coord3D x, y, z;
  892. z = normal;
  893. x.x = Cos( angle );
  894. x.y = Sin( angle );
  895. x.z = 0.0f;
  896. if (z.z != 0.0f)
  897. {
  898. x.z = -(x.x*z.x + x.y*z.y) / z.z;
  899. x.normalize();
  900. }
  901. DEBUG_ASSERTCRASH(fabs(x.x*z.x + x.y*z.y + x.z*z.z)<0.0001,("dot is not zero\n"));
  902. // now computing the y vector is trivial.
  903. y.crossProduct( &z, &x, &y );
  904. y.normalize();
  905. mtx.Set( x.x, y.x, z.x, pos.x,
  906. x.y, y.y, z.y, pos.y,
  907. x.z, y.z, z.z, pos.z );
  908. }
  909. // ------------------------------------------------------------------------------------------------
  910. // ------------------------------------------------------------------------------------------------
  911. void RailroadBehavior::updatePositionTrackDistance( PullInfo *pullerInfo, PullInfo *myInfo )
  912. {
  913. if ( ! m_track )
  914. {
  915. return;
  916. }
  917. Object *obj = BehaviorModule::getObject();
  918. if ( ! obj )
  919. return; //sanity
  920. // IF THIS HAS BEEN CALLED, AM AM GETTING PULLED BY SOMETHING,
  921. // my conductor, or another car
  922. Real hitchRadius = obj->getGeometryInfo().getMajorRadius();
  923. myInfo->trackDistance = pullerInfo->trackDistance - (hitchRadius * 2);
  924. myInfo->speed = pullerInfo->speed;// YES, if I am getting pulled, I must obey puller
  925. myInfo->m_direction = pullerInfo->m_direction;// YES, if I am getting pulled, I must obey puller
  926. // pullerInfo->previousWaypoint = pullerInfo->currentWaypoint;// to feed edge test in update
  927. // I THINK THE TURN_TO POINT SHOULD BE A BONE ON THE PREVIOUS CAR!!!!!!
  928. FindPosByPathDistance( &myInfo->towHitchPosition,
  929. myInfo->trackDistance, // the look ahead
  930. m_track->m_length);
  931. Coord3D carPosition;
  932. FindPosByPathDistance( &carPosition,
  933. myInfo->trackDistance,
  934. m_track->m_length, TRUE);
  935. Coord3D turnPos = *obj->getPosition();
  936. if (!m_inTunnel)
  937. turnPos.z = TheTerrainLogic->getGroundHeight( turnPos.x, turnPos.y );
  938. const Coord3D* dir = obj->getUnitDirectionVector2D();
  939. turnPos.x += dir->x * -hitchRadius;
  940. turnPos.y += dir->y * -hitchRadius;
  941. Coord3D trackPosDelta;
  942. trackPosDelta.x = carPosition.x - turnPos.x;
  943. trackPosDelta.y = carPosition.y - turnPos.y;
  944. trackPosDelta.z = 0;
  945. Real dx = pullerInfo->towHitchPosition.x - turnPos.x;
  946. Real dy = pullerInfo->towHitchPosition.y - turnPos.y;
  947. Real desiredAngle = atan2(dy, dx);
  948. Real relAngle = stdAngleDiff(desiredAngle, obj->getTransformMatrix()->Get_Z_Rotation());
  949. Matrix3D mtx;
  950. Matrix3D tmp(1);
  951. tmp.Translate(turnPos.x, turnPos.y, 0);
  952. tmp.Translate(trackPosDelta.x, trackPosDelta.y, 0);
  953. tmp.In_Place_Pre_Rotate_Z(relAngle );
  954. tmp.Translate(-turnPos.x, -turnPos.y, 0);
  955. mtx.mul(tmp, *obj->getTransformMatrix());
  956. //enforce ground elevation
  957. Coord3D normal ;
  958. Real enforceElevation = TheTerrainLogic->getGroundHeight( turnPos.x, turnPos.y, &normal );
  959. obj->setTransformMatrix(&mtx);
  960. if (!m_inTunnel)
  961. obj->setPositionZ( enforceElevation );
  962. obj->handlePartitionCellMaintenance();
  963. }
  964. //---------------------------------------------------------------------------------
  965. //---------------------------------------------------------------------------------
  966. void RailroadBehavior::destroyTheWholeTrainNow( void )
  967. {
  968. TheGameLogic->destroyObject( getObject());
  969. Object *trailer = TheGameLogic->findObjectByID( m_trailerID );
  970. if ( trailer )
  971. {
  972. static NameKeyType key_RGUpdate = NAMEKEY("RailroadBehavior");
  973. RailroadBehavior *RGUpdate = (RailroadBehavior*)trailer->findUpdateModule(key_RGUpdate);
  974. if( RGUpdate )
  975. {
  976. RGUpdate->destroyTheWholeTrainNow(); //< this is MY info, not the one passed to me
  977. }
  978. }
  979. }
  980. // ------------------------------------------------------------------------------------------------
  981. // ------------------------------------------------------------------------------------------------
  982. void RailroadBehavior::FindPosByPathDistance( Coord3D *pos, const Real dist, const Real length, Bool setState )
  983. {
  984. if ( ! m_track )
  985. return;
  986. m_waitingInWings = FALSE;
  987. Real actualDistance = dist;// WRAP TO NORMALIZE
  988. if ( m_track->m_isLooping )
  989. {
  990. while ( actualDistance < 0.0f )
  991. {
  992. actualDistance += length;
  993. }
  994. while ( actualDistance > length )
  995. {
  996. actualDistance -= length;
  997. }
  998. }
  999. else
  1000. {
  1001. if ( dist < 0 )
  1002. {
  1003. actualDistance = 0;
  1004. m_waitingInWings = TRUE;
  1005. }
  1006. else if ( dist >= length )
  1007. {
  1008. actualDistance = length;
  1009. m_endOfLine = TRUE;
  1010. }
  1011. actualDistance = MAX(MIN(length,dist),0); // CLAMP TO NORMALIZE
  1012. }
  1013. pos->set( 0.0f, 0.0f, 0.0f );
  1014. const TrackPointList *pointList = m_track->getPointList();//read-only
  1015. if ( ! pointList )
  1016. return;
  1017. std::list<TrackPoint>::const_iterator pointIter = pointList->begin();
  1018. while ( pointIter != pointList->end() )
  1019. {
  1020. const TrackPoint *thisPoint = &(*pointIter);
  1021. ++pointIter;// next pointIter in this list, so then...
  1022. const TrackPoint *nextPoint = &(*pointIter);
  1023. if (thisPoint && thisPoint->m_distanceFromFirst < actualDistance)// I am after this point, and
  1024. {
  1025. Coord3D thisPointPos = thisPoint->m_position;
  1026. if (nextPoint && nextPoint->m_distanceFromFirst > actualDistance)
  1027. {
  1028. //I am between this point and the next
  1029. const Int handleFound = ((TrackPoint*)thisPoint)->getHandle();
  1030. Bool edge = (m_currentPointHandle != handleFound);
  1031. if ( setState )
  1032. {
  1033. m_inTunnel = thisPoint->m_isTunnelOrBridge;
  1034. if ( m_isLocomotive )// since only locomotives care about such things
  1035. {
  1036. // THE LOCOMOTIVE SNIFFS THE TRACK TO SMELL FOR THE NEXT STATION AND TUNNEL
  1037. if ( edge )
  1038. {
  1039. m_currentPointHandle = handleFound;
  1040. if ( thisPoint->m_isStation )
  1041. {
  1042. m_conductorState = APPLY_BRAKES;
  1043. m_disembark = FALSE;
  1044. TheAudio->removeAudioEvent(m_runningSound.getPlayingHandle());
  1045. }
  1046. else if ( thisPoint->m_isDisembark )
  1047. {
  1048. m_conductorState = APPLY_BRAKES;
  1049. m_disembark = TRUE;
  1050. TheAudio->removeAudioEvent(m_runningSound.getPlayingHandle());
  1051. }
  1052. else if ( thisPoint->m_isPingPong && conductorPullInfo.m_mostRecentSpecialPointHandle != handleFound )
  1053. {
  1054. conductorPullInfo.m_mostRecentSpecialPointHandle = handleFound;
  1055. m_conductorState = APPLY_BRAKES;
  1056. m_disembark = FALSE;
  1057. TheAudio->removeAudioEvent(m_runningSound.getPlayingHandle());
  1058. conductorPullInfo.m_direction = -conductorPullInfo.m_direction;
  1059. }
  1060. }
  1061. }
  1062. if ( edge && ! m_inTunnel )
  1063. {//play my clickety clack sound, `cause I just rode over a join IN the tracks
  1064. TheAudio->addAudioEvent( &m_clicketyClackSound );
  1065. m_clicketyClackSound.setPosition( getObject()->getPosition() );
  1066. m_clicketyClackSound.setVolume( (Real)conductorPullInfo.speed / 10.0f );//assumed max speed
  1067. }
  1068. }
  1069. Real difference = actualDistance - thisPoint->m_distanceFromFirst;
  1070. Coord3D delta = nextPoint->m_position;
  1071. delta.sub( &thisPointPos );
  1072. delta.normalize();
  1073. delta.scale( difference );
  1074. thisPointPos.add( &delta );
  1075. *pos = thisPointPos;//copy out
  1076. return;
  1077. }
  1078. else
  1079. *pos = thisPointPos;//copy out
  1080. }
  1081. }
  1082. //DEBUG_ASSERTCRASH(FALSE,("Railroad could not find a position on the path!"));
  1083. }
  1084. // ------------------------------------------------------------------------------------------------
  1085. /** CRC */
  1086. // ------------------------------------------------------------------------------------------------
  1087. void RailroadBehavior::crc( Xfer *xfer )
  1088. {
  1089. // extend base class
  1090. UpdateModule::crc( xfer );
  1091. } // end crc
  1092. // ------------------------------------------------------------------------------------------------
  1093. /** Xfer method
  1094. * Version Info:
  1095. * 1: Initial version
  1096. * 2: Added... like, everything.
  1097. * 3: m_held script driven flag for hanging out
  1098. **/
  1099. // ------------------------------------------------------------------------------------------------
  1100. void RailroadBehavior::xfer( Xfer *xfer )
  1101. {
  1102. // version
  1103. XferVersion currentVersion = 3;
  1104. XferVersion version = currentVersion;
  1105. xfer->xferVersion( &version, currentVersion );
  1106. // PLEASE NOTE:
  1107. // m_track and trackDataLoaded are indeed NOT xferred,
  1108. // since these are inited afresh within the update module,
  1109. if ( version >= 2 )
  1110. {
  1111. // extend base class
  1112. PhysicsBehavior::xfer( xfer );
  1113. //StationTask m_nextStationTask;
  1114. xfer->xferUser( &m_nextStationTask, sizeof( StationTask ) );
  1115. //ObjectID m_trailerID; ///< the ID of the object I am directly pulling
  1116. xfer->xferObjectID( &m_trailerID );
  1117. //Int m_currentPointHandle;
  1118. xfer->xferInt( &m_currentPointHandle );
  1119. //Int m_waitAtStationTimer;
  1120. xfer->xferInt( &m_waitAtStationTimer );
  1121. //Bool m_carriagesCreated; ///< TRUE once we have made all the cars in the train
  1122. xfer->xferBool( &m_carriagesCreated );
  1123. //Bool m_hasEverBeenHitched; /// has somebody ever hitched me? Remains true, even after puller dies.
  1124. xfer->xferBool( &m_hasEverBeenHitched );
  1125. //Bool m_waitingInWings; /// I have not entered the real track yet, so leave me alone
  1126. xfer->xferBool( &m_waitingInWings );
  1127. //Bool m_endOfLine; /// I have reached the end of a non looping track
  1128. xfer->xferBool( &m_endOfLine );
  1129. //Bool m_isLocomotive; ///< Am I a locomotive,
  1130. xfer->xferBool( &m_isLocomotive );
  1131. //Bool m_isLeadCarraige; ///< Am the carraige in front,
  1132. xfer->xferBool( &m_isLeadCarraige );
  1133. //Int m_wantsToBeLeadCarraige; ///< Am the carraige in front,
  1134. xfer->xferInt( &m_wantsToBeLeadCarraige );
  1135. //Bool m_disembark; ///< If I wait at a station, I should also evacuate everybody when I get theres
  1136. xfer->xferBool( &m_disembark );
  1137. //Bool m_inTunnel; ///< Am I in a tunnel, so I wil not snap to ground height, until the next waypoint,
  1138. xfer->xferBool( &m_inTunnel );
  1139. //ConductorState m_conductorState;
  1140. xfer->xferUser( &m_conductorState, sizeof( ConductorState ) );
  1141. xfer->xferUser( &m_anchorWaypointID, sizeof( WaypointID ) );
  1142. //PullInfo m_pullInfo;
  1143. m_pullInfo.xferPullInfo( xfer );
  1144. //PullInfo conductorPullInfo;
  1145. conductorPullInfo.xferPullInfo( xfer );
  1146. }
  1147. if( version >= 3 )
  1148. {
  1149. xfer->xferBool( &m_held );
  1150. }
  1151. } // end xfer
  1152. void RailroadBehavior::PullInfo::xferPullInfo( Xfer *xfer )
  1153. {
  1154. XferVersion currentVersion = 1;
  1155. XferVersion version = currentVersion;
  1156. xfer->xferVersion( &version, currentVersion );
  1157. xfer->xferReal( &m_direction);
  1158. xfer->xferReal( &speed);
  1159. xfer->xferReal( &trackDistance);
  1160. xfer->xferCoord3D( &towHitchPosition );
  1161. xfer->xferInt( &m_mostRecentSpecialPointHandle );
  1162. xfer->xferUnsignedInt( &previousWaypoint );
  1163. xfer->xferUnsignedInt( &currentWaypoint );
  1164. }
  1165. // ------------------------------------------------------------------------------------------------
  1166. /** Load post process */
  1167. // ------------------------------------------------------------------------------------------------
  1168. void RailroadBehavior::loadPostProcess( void )
  1169. {
  1170. // extend base class
  1171. PhysicsBehavior::loadPostProcess();
  1172. //Bool m_trackDataLoaded; ///< have I TRIED to load track data, yet? I only try once!
  1173. m_trackDataLoaded = FALSE;
  1174. const RailroadBehaviorModuleData *modData = getRailroadBehaviorModuleData();
  1175. m_runningSound = modData->m_runningSound;
  1176. m_clicketyClackSound = modData->m_clicketyClackSound;
  1177. m_whistleSound = modData->m_whistleSound;
  1178. m_runningSound.setObjectID( getObject()->getID() );
  1179. m_whistleSound.setObjectID( getObject()->getID() ) ;
  1180. m_clicketyClackSound.setObjectID( getObject()->getID() ) ;
  1181. } // end loadPostProcess