///////////////////////////////////////////////////////////////////////////////// // FILE : train.h // PURPOSE : Those big metal cars on fixed paths that everybody loves. // AUTHOR : Obbe, Adam Croston // CREATED : 21-8-00 ///////////////////////////////////////////////////////////////////////////////// #ifndef _TRAIN_H_ #define _TRAIN_H_ #include "grcore/debugdraw.h" #include "Network\Cloud\CloudManager.h" #include "network\objects\entities\netObjTrain.h" #include "renderer\hierarchyIds.h" #include "scene\RegdRefTypes.h" #include "timecycle\TimeCycleConfig.h" #include "vehicles\door.h" #include "vehicles\vehicle.h" #include "vehicles\automobile.h" class CCarDoor; #define MAX_TRAIN_DOORS (6) #define MAX_ON_DISK_TRAIN_TRACKS_PER_LEVEL (12) #define MAX_DLC_TRAIN_TRACKS_PER_LEVEL (15) #define MAX_ON_DISK_TRAIN_CONFIGS (15) #define MAX_DLC_TRAIN_CONFIGS (15) #define MAX_TRAIN_TRACKS_PER_LEVEL MAX_ON_DISK_TRAIN_TRACKS_PER_LEVEL+MAX_DLC_TRAIN_TRACKS_PER_LEVEL // This structure contains the required information for a single node // on a train track. class CTrainTrackNode { public: enum eStationType { ST_NONE, // No station ST_LEFT, // Station with platform on left side of track ST_RIGHT // Station with platform on right side of track }; enum { TUNNEL_MASK = (1<<2) }; CTrainTrackNode() : m_nStation(ST_NONE), m_isTunnel(false) { } void SetCoorsX(float Coor) { m_fCoorX = Coor; } void SetCoorsY(float Coor) { m_fCoorY = Coor; } void SetCoorsZ(float Coor) { m_fCoorZ = Coor; } // Packed into 14 bits void SetCoors(const Vector3 &vCoor) { m_fCoorX = vCoor.x; m_fCoorY = vCoor.y; m_fCoorZ = vCoor.z; } float GetCoorsX() const { return m_fCoorX; } float GetCoorsY() const { return m_fCoorY; } float GetCoorsZ() const { return m_fCoorZ; } Vector3 GetCoors() const { return Vector3(m_fCoorX, m_fCoorY, m_fCoorZ); } Vector2 GetCoorsXY() const { return Vector2(m_fCoorX, m_fCoorY); } Vec3V GetCoorsV() const { return Vec3V(m_fCoorX, m_fCoorY, m_fCoorZ); } // JB: Why are these functions multiplying/dividing by 3?? void SetLengthFromStart(const float fLength) { m_fLengthFromStart = fLength; } float GetLengthFromStart() const { return m_fLengthFromStart; } void SetStationType(eStationType nType) { m_nStation = nType; } eStationType GetStationType() const { return m_nStation; } void SetIsTunnel(bool bIsTunnel) { m_isTunnel = bIsTunnel; } bool GetIsTunnel() const { return(m_isTunnel); } protected: float m_fCoorX, m_fCoorY, m_fCoorZ; float m_fLengthFromStart; // In meters u8 m_isTunnel : 1; public: eStationType m_nStation : 2; // 0 = no station. 1 = station with platform on left side of track. 2 = station with platform on right side of track. }; #if !__FINAL class CTrainCachedNode { public: CTrainTrackNode * pNode; s32 iTrack; s32 iNode; }; #endif typedef bool (*ForAllTrackNodesCB) (s32 iTrackIndex, s32 iNodeIndex, void * data); #define MAX_NUM_STATIONS (20) // The maximum number of stations we can have for a particular track. class CTrainTrack { public: CTrainTrack(); enum PlatformSide { None, Left, Right, Both }; u32 m_trainConfigGroupNameHash; // Is this track a cable-car? bool m_isEnabled; bool m_isLooped; bool m_stopAtStations; bool m_MPstopAtStations; u32 m_maxSpeed; u32 m_breakDist; s32 m_numNodes; // The number of nodes on this track CTrainTrackNode* m_pNodes; // Pointer to the actual track nodes float m_totalLengthIfLooped; // In meters (total length linear is just GetLengthFromStart() of final node). int m_iLinkedTrackIndex; bool m_bInitiallySwitchedOff; // Tracks default as off, and have to be enabled by script scrThreadId m_iTurnedOffByScriptThreadID; // If non-zero then this track has been disabled by the specified script #if __BANK char m_TurnedOffByScriptName[64]; #endif u32 m_iLastTrainSpawnTimeMS; // The last time that a train was spawned on this track s32 m_iScriptTrainSpawnFreqMS; // Lower bound frequency for trains to be spawned on this track, or -1 if none scrThreadId m_iSpawnFreqThreadID; // ID of the thread which modified this track's spawn frequency u32 m_iLastTrainReportingTimeMS; // The last time this train reported in bool m_bTrainActive; bool m_bTrainProcessNetworkCreation; bool m_bTrainNetworkCreationApproval; s32 m_numStations; // The number of stations for each track type. Vector3 m_aStationCoors[MAX_NUM_STATIONS]; // The coordinates of the stations s32 m_aStationNodes[MAX_NUM_STATIONS]; // The node Id for each station, so that I can search quickly float m_aStationDistFromStart[MAX_NUM_STATIONS]; // How far along the tracks are the stations located. s8 m_aStationSide[MAX_NUM_STATIONS]; // Which side the platform is on (see the PlatformSide enum) inline CTrainTrackNode * GetNode(const s32 iNode) { Assert(iNode >= 0 && iNode < m_numNodes); return &m_pNodes[iNode]; } inline bool IsSwitchedOff() { return (m_bInitiallySwitchedOff || (m_iTurnedOffByScriptThreadID != THREAD_INVALID)); } float GetTotalTrackLength() { if(m_isLooped) { const float totalTrackLengthIfLooped = m_totalLengthIfLooped; return totalTrackLengthIfLooped; } else { const float totalTrackLengthLinear = m_pNodes[m_numNodes - 1].GetLengthFromStart(); return totalTrackLengthLinear; } } static int GetTrackIndex(CTrainTrack * pTrack); #if __DEV char* m_trainConfigGroupName; #endif }; extern CTrainTrack gTrainTracks[MAX_TRAIN_TRACKS_PER_LEVEL]; // for audio namespace rage { struct TrainStation; } class CTrainCloudListener : public CloudListener { public: virtual void OnCloudEvent(const CloudEvent* pEvent); }; class CTrain : public CVehicle { public: enum TrainState { TS_Moving, TS_ArrivingAtStation, TS_OpeningDoors, TS_WaitingAtStation, TS_ClosingDoors, TS_LeavingStation, TS_Destroyed }; enum PassengersState { PS_None, PS_GetOff, PS_WaitingForGetOff, PS_GetOn, PS_WaitingForGetOn, }; enum SideFlags { SF_Left = BIT0, SF_Right = BIT1, }; enum ReleaseTrainFlags { RTF_KeepOldSpeed = BIT0 }; public: CTrain* GetEngine() { return m_pEngine; } const CTrain* GetEngine() const { return m_pEngine; } void SetEngine(CTrain* engine, bool bValidate = true) { #if ENABLE_NETWORK_LOGGING if(NetworkInterface::IsGameInProgress()) { if(engine) { if(GetNetworkObject()) NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_SET_ENGINE", "%s", GetLogName()); else NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_SET_ENGINE", "0x%p", this); if(m_pEngine) { NetworkInterface::GetObjectManagerLog().WriteDataValue("Previous Engine", "0x%p", m_pEngine.Get()); } NetworkInterface::GetObjectManagerLog().WriteDataValue("New Engine", "0x%p", engine); } else { if(GetNetworkObject()) NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_REMOVE_ENGINE", "%s", GetLogName()); else NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_REMOVE_ENGINE", "0x%p", this); if(m_pEngine) NetworkInterface::GetObjectManagerLog().WriteDataValue("Previous Engine", "0x%p", m_pEngine.Get()); } } #endif // ENABLE_NETWORK_LOGGING m_pEngine = engine; if (bValidate) ValidateLinkedLists( m_pEngine ); } CTrain* GetLinkedToBackward() const { if(m_pLinkedToBackward) { bool validPtr = !sysThreadType::IsUpdateThread() || CVehicle::GetPool()->IsValidPtr( m_pLinkedToBackward); bool inheritsFromTrain = m_pLinkedToBackward->InheritsFromTrain(); bool linkedForwardIsNotThis = m_pLinkedToBackward->m_pLinkedToForward == this; bool notEngine = !m_pLinkedToBackward->IsEngine(); bool linkedBackIsNotThis = m_pLinkedToBackward != this; #if ENABLE_NETWORK_LOGGING if(NetworkInterface::IsGameInProgress()) { NETWORK_QUITF(validPtr, "Corrupted train car detected %s(0x%p), the train car linked to forward is %s(0x%p)", m_pLinkedToBackward->GetDebugName(), m_pLinkedToBackward.Get(), GetDebugName(), this); NETWORK_QUITF(inheritsFromTrain, "m_pLinkedToBackward does not inherit from train! Corrupted train car detected %s(0x%p), the train car linked to forward is %s(0x%p)", m_pLinkedToBackward->GetDebugName(), m_pLinkedToBackward.Get(), GetDebugName(), this); NETWORK_QUITF(linkedForwardIsNotThis, "m_pLinkedToBackward's linked to forward is not 'this'! Corrupted train car detected %s(0x%p), the train car linked to forward is %s(0x%p)", m_pLinkedToBackward->GetDebugName(), m_pLinkedToBackward.Get(), GetDebugName(), this); NETWORK_QUITF(notEngine, "m_pLinkedToBackward is an engine! Corrupted train car detected %s(0x%p), the train car linked to forward is %s(0x%p)", m_pLinkedToBackward->GetDebugName(), m_pLinkedToBackward.Get(), GetDebugName(), this); NETWORK_QUITF(linkedBackIsNotThis, "m_pLinkedToBackward is 'this'! Corrupted train car detected %s(0x%p), the train car linked to forward is %s(0x%p)", m_pLinkedToBackward->GetDebugName(), m_pLinkedToBackward.Get(), GetDebugName(), this); } #endif // ENABLE_NETWORK_LOGGING if(validPtr && inheritsFromTrain && linkedForwardIsNotThis && notEngine && linkedBackIsNotThis) { return m_pLinkedToBackward; } } return NULL; } CTrain* GetLinkedToForward() const { if(m_pLinkedToForward) { bool validPtr = !sysThreadType::IsUpdateThread() || CVehicle::GetPool()->IsValidPtr( m_pLinkedToForward ); bool inheritsFromTrain = m_pLinkedToForward->InheritsFromTrain(); bool linkedBackwardIsNotThis = m_pLinkedToForward->m_pLinkedToBackward == this; bool notEngine = !IsEngine(); bool linkedForwardIsNotThis = m_pLinkedToForward != this; #if ENABLE_NETWORK_LOGGING if(NetworkInterface::IsGameInProgress()) { NETWORK_QUITF(validPtr, "Corrupted train car detected %s(0x%p), the train car linked to backward is %s(0x%p)", m_pLinkedToForward->GetDebugName(), m_pLinkedToForward.Get(), GetDebugName(), this); NETWORK_QUITF(inheritsFromTrain, "m_pLinkedToForward does not inherit from train! Corrupted train car detected %s(0x%p), the train car linked to backward is %s(0x%p)", m_pLinkedToForward->GetDebugName(), m_pLinkedToForward.Get(), GetDebugName(), this); NETWORK_QUITF(linkedBackwardIsNotThis, "m_pLinkedToForward's linked to forward is not 'this'! Corrupted train car detected %s(0x%p), the train car linked to backward is %s(0x%p)", m_pLinkedToForward->GetDebugName(), m_pLinkedToForward.Get(), GetDebugName(), this); NETWORK_QUITF(notEngine, "m_pLinkedToForward is an engine! Corrupted train car detected %s(0x%p), the train car linked to backward is %s(0x%p)", m_pLinkedToForward->GetDebugName(), m_pLinkedToForward.Get(), GetDebugName(), this); NETWORK_QUITF(linkedForwardIsNotThis, "m_pLinkedToForward is 'this'! Corrupted train car detected %s(0x%p), the train car linked to backward is %s(0x%p)", m_pLinkedToForward->GetDebugName(), m_pLinkedToForward.Get(), GetDebugName(), this); } #endif // ENABLE_NETWORK_LOGGING if(validPtr && inheritsFromTrain && linkedBackwardIsNotThis && notEngine && linkedForwardIsNotThis) { return m_pLinkedToForward; } } return NULL; } void SetLinkedToBackward( CTrain* linked, bool bValidate = true) { #if ENABLE_NETWORK_LOGGING if(NetworkInterface::IsGameInProgress()) { if(linked) { if(GetNetworkObject()) NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_SET_LINK_BACKWARD", "%s", GetLogName()); else NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_SET_LINK_BACKWARD", "0x%p", this); if(m_pLinkedToBackward) { NetworkInterface::GetObjectManagerLog().WriteDataValue("Previous Linked Backward", "0x%p", m_pLinkedToBackward.Get()); } NetworkInterface::GetObjectManagerLog().WriteDataValue("New Linked Backward", "0x%p", linked); } else { if(GetNetworkObject()) NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_REMOVE_LINK_BACKWARD", "%s", GetLogName()); else NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_REMOVE_LINK_BACKWARD", "0x%p", this); if(m_pLinkedToBackward) NetworkInterface::GetObjectManagerLog().WriteDataValue("Previous Linked Backward", "0x%p", m_pLinkedToBackward.Get()); } } #endif // ENABLE_NETWORK_LOGGING m_pLinkedToBackward = linked; if (bValidate) ValidateLinkedLists( m_pLinkedToBackward ); } void SetLinkedToForward( CTrain* linked, bool bValidate = true) { #if ENABLE_NETWORK_LOGGING if(NetworkInterface::IsGameInProgress()) { if(linked) { if(GetNetworkObject()) NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_SET_LINK_FORWARD", "%s", GetLogName()); else NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_SET_LINK_FORWARD", "0x%p", this); if(m_pLinkedToForward) { NetworkInterface::GetObjectManagerLog().WriteDataValue("Previous Linked Forward", "0x%p", m_pLinkedToForward.Get()); } NetworkInterface::GetObjectManagerLog().WriteDataValue("New Linked Forward", "0x%p", linked); } else { if(GetNetworkObject()) NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_REMOVE_LINK_FORWARD", "%s", GetLogName()); else NetworkLogUtils::WriteLogEvent(CNetwork::GetObjectManager().GetLog(), "TRAIN_REMOVE_LINK_FORWARD", "0x%p", this); if(m_pLinkedToForward) { NetworkInterface::GetObjectManagerLog().WriteDataValue("Previous Linked Forward", "0x%p", m_pLinkedToForward.Get()); } } } #endif // ENABLE_NETWORK_LOGGING m_pLinkedToForward = linked; if (bValidate) ValidateLinkedLists( m_pLinkedToForward ); } public: bool CanGetOff() const; bool CanGetOn() const; bool CanGetOnOrOff() const; bool IsMoving() const; bool AreDoorsMoving() const; void RemoveAllOccupants(); bool SetUpDriverOnEngine(bool bUseExistingNodes); public: // Static members static void Init(unsigned initMode); static void Shutdown(unsigned shutdownMode); static void SetTunablesValues(); static void UpdateVisualDataSettings(const CVisualSettings& visualSettings); #if __BANK static void InitWidgets(bkBank& bank); #endif static s32 GetTrainConfigIndex(u32 nameHash); static void LoadTrainConfigs(const char *pFileName); static void TrainConfigsPostLoad(); static void LoadTrackXml(const char *pXmlFilename); static void TrainTracksPostLoad(); static void ProcessTrainNetworkPending(int trackindex); static void DoTrainGenerationAndRemoval(); static bool FindTrackGenerationPos(const int iTrackIndex, const Vector3 & vOrigin, const float fMinCreationDist, const float fMaxCreationDist, Vector3 & vOut_CreationPos, int & iOut_PrevNodeIndex, float & fOut_CreationTVal); static void RenderTrainDebug(); static void SetTrackActive(int trackindex, bool trainactive); static bool GetTrackActive(int trackindex); static bool DetermineHostApproval(int trackindex); static void ReceivedHostApprovalData(int trackindex, bool trainapproval); // The functions the level designers use to control trains. static void DisableRandomTrains(bool bDisable); static void CreateTrain(s8 trackIndex, s32 trackNodeForEngine, bool bDirection, u8 trainConfigIndex, CTrain** ppEngine_out = NULL, CTrain** ppLastCarriage_out = NULL, scrThreadId iMissionScriptId = THREAD_INVALID); static void RemoveMissionTrains(scrThreadId iCurrentThreadId); static void ReleaseMissionTrains(scrThreadId iCurrentThreadId, const u32 iReleaseTrainFlags=0); static void ReleaseOneMissionTrain(GtaThread& iCurrentThread, CTrain* pEngine, const u32 iReleaseTrainFlags=0); static void RemoveOneMissionTrain(GtaThread& iCurrentThreadId, CTrain* pEngine); static void RemoveTrainsFromArea(const Vector3 &Centre, float Radius); static void RemoveAllTrains(); static void RestoreScriptModificationsToTracks(const scrThreadId iThreadId); // called from mission cleanup static void SwitchTrainTrackFromScript(const s32 iTrackIndex, const bool bSwitchOn, const scrThreadId iThreadId); static void SetTrainTrackSpawnFrequencyFromScript(const s32 iTrackIndex, const s32 iSpawnFreqMS, const scrThreadId iThreadId); static void SetCarriageSpeed(CTrain* pCarriage, float NewSpeed); static float GetCarriageSpeed(CTrain* pCarriage); static void SetCarriageCruiseSpeed(CTrain* pCarriage, float NewSpeed); static float GetCarriageCruiseSpeed(CTrain* pCarriage); static CTrain* FindEngine(const CTrain* pCarriage); static CTrain* FindCaboose(const CTrain* pCarriage); static CTrain* FindCarriage(const CTrain* pEngine, u8 CarriageNumber); static s32 FindClosestNodeOnTrack(const Vector3 &pos, s32 trackIndex, const float * fIn_ThisHeading=NULL, float * fOut_Distance=NULL, float * fOut_Heading=NULL, Vector3 * vOut_Position=NULL); static s32 FindClosestNodeOnTrack(const Vector3 &pos, s32 trackIndex, s32 startNode, f32 maxDistance, u32& numNodesSearched); static s32 FindClosestTrackNode(const Vector3& pos, s8& trackIndex_out); static CTrain* FindNearestTrainCarriage(const Vector3& Coors, bool bOnlyEngine); static void SetNewTrainPosition(CTrain* pEngine, const Vector3& NewCoors); static void SkipToNextAllowedStation(CTrain* pCarriage); static CTrainTrack * GetTrainTrack(const s32 iTrackIndex); static s32 GetTrackIndexFromNode(CTrainTrackNode * pNode); static CTrain* IsTrainApproachingNode(CTrainTrackNode * pNode); // Useful queries. static bool IsLoopedTrack(s8 trackIndex); static bool StopsAtStations(s8 trackIndex); static void FixupTrackNode(s8 trackIndex, s32& nodeIndex_inOut); static bool FixupTrackPos(s8 trackIndex, float& trackPos_inOut); static void CalcWorldPosAndForward(Vector3& worldPos_out, Vector3& forward_out, s8 trackIndex, float trackPosFront, bool bDirectionTrackForward, s32 initialNodeToSearchFrom, float carriageLength, bool carriagesAreSuspended, bool flipModelDir, bool& bIsTunnel); static s32 FindCurrentNode(s8 trackIndex, float trackPos, bool bDirectionTrackForward); static s32 FindCurrentNodeFromInitialNode(s8 trackIndex, float trackPos, bool bDirectionTrackForward, s32 initialNodeToSearchFrom); static bool GetWorldPosFromTrackNode(Vector3& worldPos, s8 trackIndex, s32 trackNode); static void GetWorldPosFromTrackPos(Vector3& worldPos_out, s32& currentNode, s32& nextNode, s8 trackIndex, float trackPos, float preferredSmoothLength, bool& bIsTunnel); static bool GetWorldPosFromTrackSegmentPos(Vector3& worldPos_out, float segmentPos, s32 PreviousNode, s32 Node, s32 NextNode, const CTrainTrackNode *pTrackNodes, float PreferredSmoothLength); static bool sm_bDisplayTrainAndTrackDebug; #if __BANK static bool sm_bProcessWithPhysics; #endif static bool sm_bDisplayTrainAndTrackDebugOnVMap; static bool sm_bDisableRandomTrains; static float sm_stationRadius; static float ms_fCloseStationDistEps; // Used for linking tracks with adjacent stations, if "link_tracks_with_adjacent_stations" is set in config static float sm_speedAtWhichConsideredStopped; static float sm_maxDeceleration; static float sm_maxAcceleration; static bool sm_swingCarriages; static bool sm_swingCarriagesSideToSide; static float sm_swingSideToSideAngMax; static float sm_swingSideToSideRate; static float sm_swingSideToSideNoiseFrequency; static float sm_swingSideToSideNoiseStrength; static bool sm_swingCarriagesFrontToBack; static float sm_swingFrontToBackAngMax; static float sm_swingFrontToBackAngDeltaMaxPerSec; enum GenTrainStatus { TGEN_NOTRAINPENDING, TGEN_TRAINPENDING, TGEN_TRAINNETWORKPENDING }; static GenTrainStatus sm_genTrain_status; static s8 sm_genTrain_trackIndex; static unsigned sm_networkTimeCreationRequest; #if __BANK static void PerformDebugTeleportEngineCB(); static void PerformDebugTeleportCB(); static void PerformDebugTeleportBaseCB(bool bTeleportToEngine = false); static void PerformDebugTeleportIntoSeatCB(); static void PerformDebugTeleportToRoof(); #endif #if !__FINAL static void CullTrackNodesToArea(const Vector3 & vMin, const Vector3 & vMax); static void ForAllTrackNodesIntersectingArea(const Vector3 & vMin, const Vector3 & vMax, ForAllTrackNodesCB callbackFunction, void * pData); static bool CollisionTriangleIntersectsTrainTracks(Vector3 * pTriPts); #endif public: NETWORK_OBJECT_TYPE_DECL( CNetObjTrain, NET_OBJ_TYPE_TRAIN ); // CTrain() is protected... see below... ~CTrain(); virtual void SetModelId(fwModelId modelId); virtual int InitPhys(); virtual void InitDoors(); // virtual door stuff virtual void InitCompositeBound(); virtual void PlayCarHorn(bool bOneShot, u32 hornTypeHash); virtual bool ShouldDoFullUpdate(); virtual void UpdateLODAndFadeState(const Vector3& worldPosNew); virtual void DoProcessControl(bool fullUpdate, float fFullUpdateTimeStep); bool ProcessTrainControl(float fTimeStep); // physics virtual ePhysicsResult ProcessPhysics(float fTimeStep, bool bCanPostpone, int nTimeSlice); bool UpdatePosition(float fTimeStep); void ProcessControlInputs(CPed *UNUSED_PARAM(pPlayerPed)){}; bool ProcessDoors(float fTargetRatio); void ProcessPassengers(); void ProcessVfx(); virtual ePrerenderStatus PreRender(const bool bIsVisibleInMainViewport = true); virtual void PreRender2(const bool bIsVisibleInMainViewport = true); float PopulateTrainDist() const; void PopulateCarriage(s32& maxPedsToCreateForTrain_inOut); bool IsLocalPlayerSeatedInThisCarriage() const; bool IsLocalPlayerSeatedInAnyCarriage() const; bool IsLocalPlayerSeatedOnThisEnginesTrain() const; bool IsLocalPlayerStandingInThisTrain() const; bool DoesLocalPlayerDisableAmbientTrainStops() const; virtual const Vector3& GetVelocity() const; virtual const Vector3& GetAngVelocity() const; virtual Mat34V_Out GetMatrixPrePhysicsUpdate() const; inline u32 GetCurrentNode() { return m_currentNode; } inline u32 GetPreviousNode() { return m_oldCurrentNode; } s32 StopAtStationDurationNormal() const; s32 StopAtStationDurationPlayer() const; s32 StopAtStationDurationPlayerWithGang() const; bool AnnouncesStations() const; bool DoorsGiveWarningBeep() const; bool CarriagesAreSuspended() const; bool CarriagesSwing() const; bool IsEngine() const; float GetCarriageVerticalOffsetFromTrack() const; int GetNumCarriages() const; #if __ASSERT bool AllowedToActivate() const { return m_trackIndex == -1 || m_nTrainFlags.bCreatedAsScriptVehicle; } #endif // __ASSERT virtual void BlowUpCar( CEntity *pCulprit, bool bInACutscene = false, bool bAddExplosion = true, bool bNetCall = false, u32 weaponHash = WEAPONTYPE_EXPLOSION, bool bDelayedExplosion = false); // Flag this the train/driver has been threaten and should respond accordingly void SetThreatened(); // DEBUG!! -AC, These are here just for audio support. int GetCurrentStation() const; int GetNextStation() const; const char* GetStationName(int stationId) const; int GetTimeTilNextStation() const; // END DEBUG!! void SignalTrainPadShake(u32 iTimeToSecondaryShake = 0); void ScanForEntitiesBlockingProgress(float & fInOut_dirForwardSpeedDesired); bool IsEntityBlockingProgress(CEntity * pEntity, const Vector3 & vTrainFrontPos, const float fScanDist, float & fOut_DistToEntity); void FindNextStationPositionInDirection(bool bDir, float PosOnTrack, float *pResultPosOnTrack, s32 *pStation) const; void SetTrainStopsForStations(bool bStops); void SetTrainIsStoppedAtStation(); void SetTrainToLeaveStation(); bool FindNearestStationSide(s8 trackIndex, float PositionOnTrack) const; void GetStationRelativeInfo(s32& currentStation_out, s32& nextStation_out, float& travelDistToNextStation_out, float& maxSpeedToApproachStation_out, int & iPlatformSideForNextStation_out) const; void SetStationSideForEngineAndCarriages(int iSides); // 0:left, 1:right, 2:both void SetTrackPosFromWorldPos(s32 currentNode = -1, s32 nextNode = -1); bool CanPedGetOnTrain(const CPed& rPed) const; bool AddPassenger(CPed* pPed); void ClearInvalidPassengers(); u32 GetEmptyPassengerSlots() const; bool HasPassengers() const; bool RemovePassenger(CPed* pPed); bool HasNetworkClonedPassenger() const; inline bool GetIsMissionTrain() const { return m_iMissionScriptId!=THREAD_INVALID || (NetworkInterface::IsGameInProgress() && GetScriptThatCreatedMe() != nullptr); } inline scrThreadId GetMissionScriptId() const { return m_iMissionScriptId; } int GetTrackIndex() const { return m_trackIndex; } TrainState GetTrainState() const { return m_nTrainState; } void InitiateTrainReportOnJoin(u8 playerIndex); void SetDoorsForcedOpen(bool bValue) { m_doorsForcedOpen = bValue; } const bool GetDoorsForcedOpen() { return m_doorsForcedOpen; } bool IsDoorBlocked(const CCarDoor& rDoor) const; struct CTrainFlags { u8 bAtStation:1; u8 bEngine:1; // The one that controls the speed etc. u8 bCaboose:1; // The one with the tail lights // u8 bMissionTrain:1; u8 bDirectionTrackForward:1; u8 bStopForStations:1; // Is this a passenger train that stops at stations? u8 bIsForcedToSlowDown:1; u8 bHasPassengerCarriages:1; u8 bIsCarriageTurningRight:1; u8 iStationPlatformSides:2; // 0:left (along positive 'A' vector), 1:right, 2:both u8 bRenderAsDerailed:1; u8 bStartedOpeningDoors:1; u8 bStartedClosingDoors:1; u8 bDisableNextStationAnnouncements : 1; u8 bCreatedAsScriptVehicle : 1; // don't treat this like a train, it is just a script vehicle u8 bIsCutsceneControlled : 1; u8 bIsSyncedSceneControlled : 1; u8 bAllowRemovalByPopulation : 1; }; enum UpdateLODState { FADING_IN, NORMAL, FADING_OUT, SIMPLIFIED }; UpdateLODState m_updateLODState; u32 m_updateLODFadeStartTimeMS; float m_updateLODCurrentAlpha; Matrix34 m_inactiveLastMatrix; Vector3 m_inactiveMoveSpeed; Vector3 m_inactiveTurnSpeed; float m_desiredCruiseSpeed; //decided to put this here rather then in a vehicle task, as this is all the vehicle task would store. float m_trackForwardSpeed; float m_trackForwardAccPortion; float m_frontBackSwingAngPrev; float m_trackPosFront; float m_trackSteepness; // The angle of the track with the horizon. Used by the cablecar to place the hanger thingy. float m_wheelSquealIntensity; u32 m_currentNode; // We can speed stuff up by not always looking from 0 u32 m_oldCurrentNode; // Old position data private: RegdTrain m_pEngine; // Pointer to the carriage that is at the head of the train. RegdTrain m_pLinkedToForward; // Pointer to the carriage we're linked to in the forward direction. RegdTrain m_pLinkedToBackward; // Pointer to the carriage we're linked to in the rear direction. public: s32 m_currentStation; s32 m_nextStation; float m_travelDistToNextStation; CTrainFlags m_nTrainFlags; s8 m_trackIndex; // What track are we on? s8 m_trainConfigIndex; s8 m_trainConfigCarriageIndex; float m_trackDistFromEngineFrontForCarriageFront; u32 m_TimeSliceLastProcessed; u32 m_iTimeOfSecondaryPadShake; static const int sMaxPassengers = 8; atFixedArray m_aPassengers; TrainState m_nTrainState; float m_fTimeInTrainState; PassengersState m_nPassengersState; s32 m_iLastTimeScannedForVehicles; RegdVeh m_pVehicleBlockingProgress; s32 m_iLastTimeScannedForPeds; RegdPed m_pPedBlockingProgress; u32 m_iLastHornTime; u32 m_iTimeWaitingForBlockage; static const float sm_fMinSecondsToConsiderBeingThreatened; float m_fTimeSinceThreatened; u32 m_requestInteriorRescanTime; bool m_bMetroTrain; bool m_bArchetypeSet; // Script ID which owns this train // This replaces the previous 'm_nTrainFlags.bMissionTrain' scrThreadId m_iMissionScriptId; protected: // This is PROTECTED as only CVEHICLEFACTORY is allowed to call this - make sure you use CVehicleFactory! // DONT make this public! CTrain(const eEntityOwnedBy ownedBy, u32 popType); friend class CVehicleFactory; #if __ASSERT u32 m_uFrameCreated; // keep track of the frame we were created on, for assert purposes #endif public: bool FindClosestBoardingPoint(Vec3V_In vPos, CBoardingPoint& rBoardingPoint) const; void ChangeTrainState(TrainState nTrainState); static void ValidateLinkedLists( CTrain* pCarriage ); private: void CorrectLocalPlayerInsideTrain(); bool WasProcessedThisTimeSlice() const; void SetWasProcessedThisTimeSlice(); bool ArePassengersGettingOff() const; bool ArePassengersGettingOn() const; bool GetFlipModelDir() const; fwFlags8 GetSideFlagsClosestToPlatform() const; void MakeNearbyPedsGetOn(); void MakePassengersGetOff(); #if !__FINAL const char* GetTrainStateString(); const char* GetPassengerStateString(); const int GetFullTrainPassengerCount(); #endif private: void DoInteriorLights(); static bool CheckTrainSpawnTrackInView(s32 generationNode, bool bDirection, u8 trainConfigIndex, const Vector3& vCamCenter); // static void LoadTrack(char *pFileName, char *pParamString0, char *pParamString1, char *pParamString2, char *pParamString3, char *pParamString4); static void ReadAndInterpretTrackFile(const char * pFileName, CTrainTrack & track, TrainStation ** stations); static void PostProcessTrack(CTrainTrack & track, const float fMaxSpacingBetweenNodes=50.0f); static void InitLoadConfig(); #if DEBUG_DRAW static void SceneUpdateDebugDisplayTrainAndTrack(fwEntity &entity, void *userData); #endif // DEBUG_DRAW private: static CTrainCloudListener* msp_trainCloudListener; //Need to make door array private to make sure that doors are accessed by function by referencing either the //eDoors enum or the hierarchyId enum. We need to get the door from a function so that we can readily //switch from left-hand drive right-hand drive. CCarDoor m_aTrainDoors[MAX_TRAIN_DOORS]; bool m_doorsForcedOpen; public: static void DumpTrainInformation(); static u32 ms_iTrainNetworkHearbeatInterval; static bool ms_bEnableTrainsInMP; static bool ms_bEnableTrainPassengersInMP; static bool ms_bEnableTrainLinkLoopForceCrash; #if __BANK static bool sm_bDebugTrain; static bool ms_bForceSpawn; #endif }; #endif // _TRAIN_H_