/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: RailroadBehavior.h //////////////////////////////////////////////////////////////// // Author: Mark Lorenzen, September 2002 // Desc: Railroad Train AI /////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef __RAILROADGUIDE_AI_UPDATE_H_ #define __RAILROADGUIDE_AI_UPDATE_H_ // USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// #include "GameLogic/Module/AIUpdate.h" #include "GameLogic/Module/PhysicsUpdate.h" // FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// class Waypoint; typedef std::vector TemplateNameList; // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ class RailroadBehaviorModuleData : public PhysicsBehaviorModuleData { public: RailroadBehaviorModuleData( void ); static void buildFieldParse( MultiIniFieldParse &p ) { PhysicsBehaviorModuleData::buildFieldParse( p ); static const FieldParse dataFieldParse[] = { { "PathPrefixName", INI::parseAsciiString, NULL, offsetof( RailroadBehaviorModuleData, m_pathPrefixName ) }, { "CrashFXTemplateName", INI::parseAsciiString, NULL, offsetof( RailroadBehaviorModuleData, m_CrashFXTemplateName ) }, { "IsLocomotive", INI::parseBool, NULL, offsetof( RailroadBehaviorModuleData, m_isLocomotive ) }, { "CarriageTemplateName", INI::parseAsciiStringVectorAppend, NULL, offsetof(RailroadBehaviorModuleData, m_carriageTemplateNameData) }, { "BigMetalBounceSound", INI::parseAudioEventRTS, NULL, offsetof( RailroadBehaviorModuleData, m_bigMetalImpactDefaultSound) }, { "SmallMetalBounceSound", INI::parseAudioEventRTS, NULL, offsetof( RailroadBehaviorModuleData, m_smallMetalImpactDefaultSound) }, { "MeatyBounceSound", INI::parseAudioEventRTS, NULL, offsetof( RailroadBehaviorModuleData, m_meatyImpactDefaultSound) }, { "RunningGarrisonSpeedMax", INI::parseReal, NULL, offsetof( RailroadBehaviorModuleData, m_runningGarrisonSpeedMax) }, { "KillSpeedMin", INI::parseReal, NULL, offsetof( RailroadBehaviorModuleData, m_killSpeedMin) }, { "SpeedMax", INI::parseReal, NULL, offsetof( RailroadBehaviorModuleData, m_speedMax) }, { "Acceleration", INI::parseReal, NULL, offsetof( RailroadBehaviorModuleData, m_acceleration) }, { "Braking", INI::parseReal, NULL, offsetof( RailroadBehaviorModuleData, m_braking) }, { "WaitAtStationTime", INI::parseDurationUnsignedInt, NULL, offsetof( RailroadBehaviorModuleData, m_waitAtStationTime) }, { "RunningSound", INI::parseAudioEventRTS, NULL, offsetof( RailroadBehaviorModuleData, m_runningSound) }, { "ClicketyClackSound", INI::parseAudioEventRTS, NULL, offsetof( RailroadBehaviorModuleData, m_clicketyClackSound) }, { "WhistleSound", INI::parseAudioEventRTS, NULL, offsetof( RailroadBehaviorModuleData, m_whistleSound) }, { "Friction", INI::parseReal, NULL, offsetof( RailroadBehaviorModuleData, m_friction) }, { 0, 0, 0, 0 } }; p.add( dataFieldParse ); } // end buildFieldParse TemplateNameList m_carriageTemplateNameData; AsciiString m_pathPrefixName; ///< prefix to use for waypont start and end points we'll look for AsciiString m_CrashFXTemplateName; Bool m_isLocomotive; Real m_runningGarrisonSpeedMax; Real m_killSpeedMin; Real m_speedMax; Real m_acceleration; Real m_braking; Real m_friction; UnsignedInt m_waitAtStationTime; AudioEventRTS m_runningSound; AudioEventRTS m_clicketyClackSound; AudioEventRTS m_bigMetalImpactDefaultSound; AudioEventRTS m_smallMetalImpactDefaultSound; AudioEventRTS m_meatyImpactDefaultSound; AudioEventRTS m_whistleSound; }; //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- struct TrackPoint { TrackPoint( void ) { clear(); }; void clear( void ) { m_position.set(0,0,0); m_distanceFromPrev = 0; m_distanceFromFirst = 0; m_handle = 0xfacade; m_isFirstPoint = FALSE; m_isLastPoint = FALSE; m_isTunnelOrBridge = FALSE; m_isStation = FALSE; m_isDisembark = FALSE; m_isPingPong = FALSE; }; const Int getHandle( void ) { return m_handle; }; Coord3D m_position; Real m_distanceFromPrev; Real m_distanceFromFirst; Int m_handle; Bool m_isFirstPoint; Bool m_isLastPoint; Bool m_isTunnelOrBridge; Bool m_isStation; Bool m_isDisembark; Bool m_isPingPong; }; typedef std::list TrackPointList; struct TrainTrack { TrainTrack( void ) // a constructor 4 u { clear(); incReference(); }; void clear( void ) { m_pointList.clear(); m_isLooping = FALSE; m_isValid = FALSE; m_refCount = 0; m_length = 0.0f; }; Bool m_isLooping; Bool m_isValid; Real m_length; void incReference(); Bool releaseReference(); // To protect the track form ever going out of sync between cars on the same train... // I restrict write access to the first referencer, before a second one is added (the locomotive) TrackPointList* getWritablePointList( void ) { return m_refCount == 1 ? &m_pointList : NULL; }; const TrackPointList* getPointList( void ) { return &m_pointList; }; private: TrackPointList m_pointList; UnsignedInt m_refCount; }; //------------------------------------------------------------------------------------------------- class RailroadBehavior : public PhysicsBehavior { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( RailroadBehavior, "RailroadBehavior" ) MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( RailroadBehavior, RailroadBehaviorModuleData ) public: RailroadBehavior( Thing *thing, const ModuleData *moduleData ); // virtual destructor prototype provided by memory pool declaration static Int getInterfaceMask() { return PhysicsBehavior::getInterfaceMask(); } struct PullInfo { Real m_direction; ///< 1 = forward, -1 = backward Real speed; Real trackDistance; Coord3D towHitchPosition; Int m_mostRecentSpecialPointHandle; UnsignedInt previousWaypoint; UnsignedInt currentWaypoint; void xferPullInfo( Xfer *xfer ); }; typedef std::vector TemplateNameVector; typedef TemplateNameVector::const_iterator TemplateNameIterator;; //UpdateModule methods // virtual SleepyUpdatePhase getUpdatePhase() const { return PHASE_FINAL; } // PhysicsBehavior methods virtual void onCollide( Object *other, const Coord3D *loc, const Coord3D *normal ); virtual Bool isRailroad() const ; virtual UpdateSleepTime update( void ); // TRAINY METHODS void getPulled( PullInfo *info ); void destroyTheWholeTrainNow( void ); void hitchNewCarriagebyTemplate( ObjectID parentID, const TemplateNameVector& list, TemplateNameIterator& iter, TrainTrack *trackPointList ); void hitchNewCarriagebyProximity( ObjectID parentID, TrainTrack *trackPointList ); void disembark( void ); Bool hasEverBeenHitched( void ) { return m_hasEverBeenHitched; }; void setHeld( Bool held ) {m_held = held;}; void makeAWallOutOfThisTrain( Bool on ); protected: //definitions enum ConductorState { APPLY_BRAKES, WAIT_AT_STATION, ACCELERATE, COAST }; enum StationTask { DO_NOTHING, DISEMBARK }; // our methods void updatePositionTrackDistance( PullInfo *pullerInfo, PullInfo *myInfo ); void loadTrackData( void ); void createCarriages( void ); void FindPosByPathDistance( Coord3D *pos, const Real dist, const Real length, Bool setState = FALSE ); void playImpactSound(Object *victim, const Coord3D *impactPosition); TemplateNameIterator m_carriageTemplateNameIterator; StationTask m_nextStationTask; ObjectID m_trailerID; ///< the ID of the object I am directly pulling PullInfo conductorPullInfo; PullInfo m_pullInfo; AudioEventRTS m_whistleSound; AudioEventRTS m_clicketyClackSound; AudioEventRTS m_runningSound; AudioHandle m_runningSoundHandle; TrainTrack *m_track; Int m_currentPointHandle; Int m_waitAtStationTimer; //Flags Bool m_carriagesCreated; ///< TRUE once we have made all the cars in the train Bool m_hasEverBeenHitched; /// has somebody ever hitched me? Remains true, even after puller dies. Bool m_trackDataLoaded; ///< have I TRIED to load track data, yet? I only try once! Bool m_waitingInWings; /// I have not entered the real track yet, so leave me alone Bool m_endOfLine; /// I have reached the end of a non looping track Bool m_isLocomotive; ///< Am I a locomotive, Bool m_isLeadCarraige; ///< Am the carraige in front, Int m_wantsToBeLeadCarraige; ///< Am the carraige in front, Bool m_disembark; ///< If I wait at a station, I should also evacuate everybody when I get theres Bool m_inTunnel; ///< Am I in a tunnel, so I wil not snap to ground height, until the next waypoint, // i.e. do I provide the movement and scheduling AI for m_trailerID // And therefore for his and his and his.......... Bool m_held; ///< This will prevent a loco from departing a station ConductorState m_conductorState; WaypointID m_anchorWaypointID; }; #endif // end __RAILROAD_GUIDE_AI_UPDATE_H_