protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { audioPlayer.queueClip(new QueuedMessage(folderTest, 0, this)); testDriverNames(); /*audioPlayer.queueClip(new QueuedMessage("conditionsAirAndTrackIncreasing1", MessageContents (ConditionsMonitor.folderAirAndTrackTempIncreasing, ConditionsMonitor.folderAirTempIsNow, QueuedMessage.folderNameNumbersStub + 26, ConditionsMonitor.folderTrackTempIsNow, QueuedMessage.folderNameNumbersStub + 32, ConditionsMonitor.folderCelsius), 0, this)); audioPlayer.queueClip(new QueuedMessage("Fuel/estimate", MessageContents( Fuel.folderWeEstimate, QueuedMessage.folderNameNumbersStub + 12, Fuel.folderMinutesRemaining), 0, this)); audioPlayer.queueClip(new QueuedMessage("laptime", MessageContents(LapTimes.folderLapTimeIntro, TimeSpan.FromSeconds(60 + (random.NextDouble() * 60))), 0, this)); audioPlayer.queueClip(new QueuedMessage("yesBoxAfter", MessageContents(MandatoryPitStops.folderMandatoryPitStopsYesStopAfter, QueuedMessage.folderNameNumbersStub + 10, MandatoryPitStops.folderMandatoryPitStopsMinutes), 0, null)); audioPlayer.queueClip(new QueuedMessage("laps_on_current_tyres", MessageContents(TyreMonitor.folderLapsOnCurrentTyresIntro, QueuedMessage.folderNameNumbersStub + 5, TyreMonitor.folderLapsOnCurrentTyresOutro), 0, this));*/ }
public override bool isMessageStillValid(string eventSubType, GameStateData currentGameState, Dictionary<String, Object> validationData) { if (validationData != null && validationData.ContainsKey(validationDriverAheadKey)) { String expectedOpponentName = (String)validationData[validationDriverAheadKey]; OpponentData opponentInFront = currentGameState.SessionData.Position > 1 ? currentGameState.getOpponentAtPosition(currentGameState.SessionData.Position - 1, false) : null; String actualOpponentName = opponentInFront == null ? null : opponentInFront.DriverRawName; if (actualOpponentName != expectedOpponentName) { if (actualOpponentName != null && expectedOpponentName != null) { Console.WriteLine("new car in front message for opponent " + expectedOpponentName + " no longer valid - driver in front is now " + actualOpponentName); } return false; } } return true; }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { if (currentGameState.SessionData.Flag == FlagEnum.BLACK) { if (currentGameState.Now > lastBlackFlagTime.Add(timeBetweenBlackFlagMessages)) { lastBlackFlagTime = currentGameState.Now; audioPlayer.queueClip(new QueuedMessage(folderBlackFlag, 0, this)); } } else if (!currentGameState.PitData.InPitlane && currentGameState.SessionData.Flag == FlagEnum.BLUE) { if (currentGameState.Now > lastBlueFlagTime.Add(timeBetweenBlueFlagMessages)) { lastBlueFlagTime = currentGameState.Now; audioPlayer.queueClip(new QueuedMessage(folderBlueFlag, 0, this)); } } else if (!currentGameState.PitData.InPitlane && currentGameState.SessionData.Flag == FlagEnum.YELLOW) { if (currentGameState.Now > lastYellowFlagTime.Add(timeBetweenYellowFlagMessages)) { lastYellowFlagTime = currentGameState.Now; audioPlayer.queueClip(new QueuedMessage(folderYellowFlag, 0, this)); } } else if (currentGameState.SessionData.Flag == FlagEnum.WHITE) { if (currentGameState.Now > lastWhiteFlagTime.Add(timeBetweenWhiteFlagMessages)) { lastWhiteFlagTime = currentGameState.Now; audioPlayer.queueClip(new QueuedMessage(folderWhiteFlag, 0, this)); } } else if (!currentGameState.PitData.InPitlane && currentGameState.SessionData.Flag == FlagEnum.DOUBLE_YELLOW) { if (currentGameState.Now > lastYellowFlagTime.Add(timeBetweenYellowFlagMessages)) { lastYellowFlagTime = currentGameState.Now; audioPlayer.queueClip(new QueuedMessage(folderDoubleYellowFlag, 0, this)); } } }
public override bool isMessageStillValid(String eventSubType, GameStateData currentGameState, Dictionary<String, Object> validationData) { // when a new penalty is given we queue a 'three laps left to serve' message for 20 seconds in the future. // If, 20 seconds later, the player has started a new lap, this message is no longer valid so shouldn't be played if (eventSubType == folderThreeLapsToServe) { Console.WriteLine("checking penalty validity, pen lap = " + penaltyLap + ", completed =" + lapsCompleted); return hasOutstandingPenalty && lapsCompleted == penaltyLap && currentGameState.SessionData.SessionPhase != SessionPhase.Finished; } else if (eventSubType == folderCutTrackInRace) { return !hasOutstandingPenalty && currentGameState.SessionData.SessionPhase != SessionPhase.Finished; } else if(eventSubType == folderCutTrackPracticeOrQual || eventSubType == folderLapDeleted) { return currentGameState.SessionData.SessionPhase != SessionPhase.Finished; } else { return hasOutstandingPenalty && currentGameState.SessionData.SessionPhase != SessionPhase.Finished; } }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { Boolean checkPushToGain = currentGameState.SessionData.SessionRunningTime - currentGameState.SessionData.GameTimeAtLastPositionFrontChange < minTimeToBeInThisPosition; Boolean checkPushToHold = currentGameState.SessionData.SessionRunningTime - currentGameState.SessionData.GameTimeAtLastPositionBehindChange < minTimeToBeInThisPosition; if (currentGameState.SessionData.SessionType == SessionType.Race) { if ((checkPushToGain || checkPushToHold) && !playedNearEndTimePush && currentGameState.SessionData.SessionNumberOfLaps <= 0 && currentGameState.SessionData.SessionTimeRemaining < 4 * 60 && currentGameState.SessionData.SessionTimeRemaining > 2 * 60) { // estimate the number of remaining laps - be optimistic... int numLapsLeft = (int)Math.Ceiling((double)currentGameState.SessionData.SessionTimeRemaining / (double)currentGameState.SessionData.PlayerLapTimeSessionBest); if (currentGameState.carClass.carClassEnum == CarData.CarClassEnum.DTM_2015) { numLapsLeft = numLapsLeft + 1; } playedNearEndTimePush = checkGaps(currentGameState, numLapsLeft, checkPushToGain, checkPushToHold); } else if ((checkPushToGain || checkPushToHold) && !playedNearEndLapsPush && currentGameState.SessionData.SessionNumberOfLaps > 0 && currentGameState.SessionData.SessionNumberOfLaps - currentGameState.SessionData.CompletedLaps <= 4) { playedNearEndLapsPush = checkGaps(currentGameState, currentGameState.SessionData.SessionNumberOfLaps - currentGameState.SessionData.CompletedLaps, checkPushToGain, checkPushToHold); } } if (currentGameState.PitData.IsAtPitExit && currentGameState.PositionAndMotionData.CarSpeed > 5) { // we've just been handed control back after a pitstop if (isOpponentApproachingPitExit(currentGameState)) { // we've exited into clean air audioPlayer.queueClip(new QueuedMessage(folderTrafficBehindExitingPits, 0, this)); } else { audioPlayer.queueClip(new QueuedMessage(folderPushExitingPits, 0, this)); } } }
public Boolean isMessageStillValid(String eventSubType, GameStateData currentGameState) { return this.abstractEvent == null || this.abstractEvent.isMessageStillValid(eventSubType, currentGameState, validationData); }
private LastLapRating getLastLapRating(GameStateData currentGameState, float[] bestLapDataForOpponents) { if (currentGameState.SessionData.PreviousLapWasValid && currentGameState.SessionData.LapTimePrevious > 0) { float closeThreshold = currentGameState.SessionData.LapTimePrevious * goodLapPercent / 100; float matchingRacePaceThreshold = currentGameState.SessionData.LapTimePrevious * matchingRacePacePercent / 100; if (currentGameState.SessionData.OverallSessionBestLapTime == currentGameState.SessionData.LapTimePrevious) { return LastLapRating.BEST_OVERALL; } else if (currentGameState.SessionData.PlayerClassSessionBestLapTime == currentGameState.SessionData.LapTimePrevious) { return LastLapRating.BEST_IN_CLASS; } else if (currentGameState.SessionData.SessionType == SessionType.Race && bestLapDataForOpponents[0] > 0 && bestLapDataForOpponents[0] >= currentGameState.SessionData.LapTimePrevious) { return LastLapRating.SETTING_CURRENT_PACE; } else if (currentGameState.SessionData.SessionType == SessionType.Race && bestLapDataForOpponents[0] > 0 && bestLapDataForOpponents[0] > currentGameState.SessionData.LapTimePrevious - closeThreshold) { return LastLapRating.CLOSE_TO_CURRENT_PACE; } else if (currentGameState.SessionData.LapTimePrevious == currentGameState.SessionData.PlayerLapTimeSessionBest) { if (currentGameState.SessionData.OpponentsLapTimeSessionBestOverall > currentGameState.SessionData.LapTimePrevious - closeThreshold) { return LastLapRating.PERSONAL_BEST_CLOSE_TO_OVERALL_LEADER; } else if (currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass > currentGameState.SessionData.LapTimePrevious - closeThreshold) { return LastLapRating.PERSONAL_BEST_CLOSE_TO_CLASS_LEADER; } else if (currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass > 0 || currentGameState.SessionData.OpponentsLapTimeSessionBestOverall > 0) { return LastLapRating.PERSONAL_BEST_STILL_SLOW; } } else if (currentGameState.SessionData.OpponentsLapTimeSessionBestOverall >= currentGameState.SessionData.LapTimePrevious - closeThreshold) { return LastLapRating.CLOSE_TO_OVERALL_LEADER; } else if (currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass >= currentGameState.SessionData.LapTimePrevious - closeThreshold) { return LastLapRating.CLOSE_TO_CLASS_LEADER; } else if (currentGameState.SessionData.PlayerLapTimeSessionBest >= currentGameState.SessionData.LapTimePrevious - closeThreshold) { return LastLapRating.CLOSE_TO_PERSONAL_BEST; } else if (currentGameState.SessionData.PlayerLapTimeSessionBest > 0) { return LastLapRating.MEH; } } return LastLapRating.NO_DATA; }
private void displayNewSessionInfo(GameStateData currentGameState) { Console.WriteLine("New session details..."); Console.WriteLine("SessionType " + currentGameState.SessionData.SessionType); Console.WriteLine("EventIndex " + currentGameState.SessionData.EventIndex); Console.WriteLine("SessionIteration " + currentGameState.SessionData.SessionIteration); String trackName = currentGameState.SessionData.TrackDefinition == null ? "unknown" : currentGameState.SessionData.TrackDefinition.name; Console.WriteLine("TrackName " + trackName); }
public override bool isMessageStillValid(string eventSubType, GameStateData currentGameState, Dictionary<String, Object> validationData) { return true; }
private void playPreLightsMessage(GameStateData currentGameState, int maxNumberToPlay) { playedPreLightsMessage = true; CrewChiefV3.GameState.Conditions.ConditionsSample currentConditions = currentGameState.Conditions.getMostRecentConditions(); List<QueuedMessage> possibleMessages = new List<QueuedMessage>(); if (currentConditions != null) { possibleMessages.Add(new QueuedMessage("trackTemp", MessageContents(ConditionsMonitor.folderTrackTempIs, QueuedMessage.folderNameNumbersStub + Math.Round(currentConditions.TrackTemperature), ConditionsMonitor.folderCelsius, ConditionsMonitor.folderAirTempIs, QueuedMessage.folderNameNumbersStub + Math.Round(currentConditions.AmbientTemperature), ConditionsMonitor.folderCelsius), 0, null)); } if (currentGameState.PitData.HasMandatoryPitStop) { if (currentGameState.SessionData.SessionHasFixedTime) { possibleMessages.Add(new QueuedMessage("pit_window_time", MessageContents(MandatoryPitStops.folderMandatoryPitStopsPitWindowOpensAfter, QueuedMessage.folderNameNumbersStub + currentGameState.PitData.PitWindowStart, MandatoryPitStops.folderMandatoryPitStopsMinutes), 0, this)); } else { possibleMessages.Add(new QueuedMessage("pit_window_lap", MessageContents(MandatoryPitStops.folderMandatoryPitStopsPitWindowOpensOnLap, QueuedMessage.folderNameNumbersStub + currentGameState.PitData.PitWindowStart), 0, this)); } } if (currentGameState.SessionData.Position == 1) { possibleMessages.Add(new QueuedMessage(Position.folderPole, 0, this)); } else { Console.WriteLine("pre-start message for P " + currentGameState.SessionData.Position); possibleMessages.Add(new QueuedMessage(Position.folderStub + currentGameState.SessionData.Position, 0, this)); } // now pick a random selection if (possibleMessages.Count > 0) { int played = 0; var shuffled = possibleMessages.OrderBy(item => rand.Next()); foreach (var message in shuffled) { played++; if (played > maxNumberToPlay) { break; } audioPlayer.queueClip(message); } } // TODO: in the countdown / pre-lights phase, we don't know how long the race is going to be so we can't use the 'get on with it' messages :( }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { if (!playedPreLightsMessage && currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.SessionPhase == SessionPhase.Gridwalk && (playPreLightsInRaceroom || CrewChief.gameDefinition.gameEnum != GameEnum.RACE_ROOM)) { playPreLightsMessage(currentGameState, 3); playedPreLightsMessage = true; purgePreLightsMessages = true; } // TODO: in R3E online there's a GridWalk phase before the Countdown. In PCars they're combined. Add some messages to this phase. // R3E's gridWalk phase isn't useable here - the data during this phase are bollocks if (!playedGetReady && currentGameState.SessionData.SessionType == SessionType.Race && (currentGameState.SessionData.SessionPhase == SessionPhase.Countdown || (currentGameState.SessionData.SessionPhase == SessionPhase.Formation && CrewChief.gameDefinition.gameEnum == GameEnum.RACE_ROOM))) { // If we've not yet played the pre-lights messages, just play one of them here, but not for RaceRoom as the lights will already have started if (!playedPreLightsMessage && CrewChief.gameDefinition.gameEnum != GameEnum.RACE_ROOM) { playedPreLightsMessage = true; playPreLightsMessage(currentGameState, 2); purgePreLightsMessages = false; } if (purgePreLightsMessages) { audioPlayer.purgeQueues(); } audioPlayer.queueClip(new QueuedMessage(folderGetReady, 0, this)); playedGetReady = true; } /*if (!playedGreenGreenGreen && previousGameState != null && currentGameState.SessionData.SessionType == SessionType.Race && (currentGameState.SessionData.SessionPhase == SessionPhase.Green && (previousGameState.SessionData.SessionPhase == SessionPhase.Formation || previousGameState.SessionData.SessionPhase == SessionPhase.Countdown))) {*/ if (previousGameState != null && enableGreenLightMessages && currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.SessionPhase == SessionPhase.Green && (previousGameState.SessionData.SessionPhase == SessionPhase.Formation || previousGameState.SessionData.SessionPhase == SessionPhase.Countdown)) { audioPlayer.removeQueuedClip(folderGetReady); audioPlayer.playClipImmediately(new QueuedMessage(folderGreenGreenGreen, 0, this), false); audioPlayer.closeChannel(); audioPlayer.disablePearlsOfWisdom = false; } if (currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.IsNewLap && currentGameState.SessionData.CompletedLaps > 0) { // a new lap has been started in race mode int position = currentGameState.SessionData.Position; if (currentGameState.SessionData.CompletedLaps == currentGameState.SessionData.SessionNumberOfLaps - 1) { if (position == 1) { audioPlayer.queueClip(new QueuedMessage(folderLastLapLeading, 0, this)); } else if (position < 4) { audioPlayer.queueClip(new QueuedMessage(folderLastLapTopThree, 0, this)); } else if (position >= 4) { audioPlayer.queueClip(new QueuedMessage(folderLastLap, 0, this), PearlsOfWisdom.PearlType.NEUTRAL, 0.5); } else if (position >= 10) { audioPlayer.queueClip(new QueuedMessage(folderLastLap, 0, this), PearlsOfWisdom.PearlType.BAD, 0.5); } else { Console.WriteLine("1 lap left but position is < 1"); } } else if (currentGameState.SessionData.CompletedLaps == currentGameState.SessionData.SessionNumberOfLaps - 2) { if (position == 1) { audioPlayer.queueClip(new QueuedMessage(folderTwoLeftLeading, 0, this)); } else if (position < 4) { audioPlayer.queueClip(new QueuedMessage(folderTwoLeftTopThree, 0, this)); } else if (position >= 4) { audioPlayer.queueClip(new QueuedMessage(folderTwoLeft, 0, this), PearlsOfWisdom.PearlType.NEUTRAL, 0.5); } else if (position >= 10) { audioPlayer.queueClip(new QueuedMessage(folderTwoLeft, 0, this), PearlsOfWisdom.PearlType.BAD, 0.5); } else { Console.WriteLine("2 laps left but position is < 1"); } // 2 laps left, so prevent any further pearls of wisdom being added } } }
public GameStateData mapToGameStateData(Object memoryMappedFileStruct, GameStateData previousGameState) { pCarsAPIStruct shared = ((CrewChiefV3.PCars.PCarsSharedMemoryReader.PCarsStructWrapper)memoryMappedFileStruct).data; long ticks = ((CrewChiefV3.PCars.PCarsSharedMemoryReader.PCarsStructWrapper)memoryMappedFileStruct).ticksWhenRead; // game state is 3 for paused, 5 for replay. No idea what 4 is... if (shared.mGameState == (uint)eGameState.GAME_FRONT_END || (shared.mGameState == (uint)eGameState.GAME_INGAME_PAUSED && !System.Diagnostics.Debugger.IsAttached) || (shared.mGameState == (uint)eGameState.GAME_INGAME_PAUSED) || shared.mGameState == (uint)eGameState.GAME_VIEWING_REPLAY || shared.mGameState == (uint)eGameState.GAME_EXITED) { // don't ignore the paused game updates if we're in debug mode return previousGameState; } GameStateData currentGameState = new GameStateData(ticks); if (shared.mNumParticipants < 1 || shared.mTrackLength <= 0) { // Unusable data in the block // TODO: is this check sufficient? return null; } Tuple<int, pCarsAPIParticipantStruct> playerData = getPlayerDataStruct(shared.mParticipantData, shared.mViewedParticipantIndex); if (getPlayerByName && playerSteamId != shared.mParticipantData[shared.mViewedParticipantIndex].mName) { return null; } int playerDataIndex = playerData.Item1; pCarsAPIParticipantStruct viewedParticipant = playerData.Item2; NameValidator.validateName(viewedParticipant.mName); currentGameState.SessionData.CompletedLaps = (int)viewedParticipant.mLapsCompleted; currentGameState.SessionData.SectorNumber = (int)viewedParticipant.mCurrentSector; currentGameState.SessionData.Position = (int)viewedParticipant.mRacePosition; currentGameState.SessionData.UnFilteredPosition = (int)viewedParticipant.mRacePosition; currentGameState.SessionData.IsNewSector = previousGameState == null || viewedParticipant.mCurrentSector != previousGameState.SessionData.SectorNumber; // When in the pit lane, mCurrentLapDistance gets set to 0 when crossing the start line and *remains at 0* until some distance into the lap (about 300 metres) currentGameState.PositionAndMotionData.DistanceRoundTrack = viewedParticipant.mCurrentLapDistance; // previous session data to check if we've started an new session SessionPhase lastSessionPhase = SessionPhase.Unavailable; SessionType lastSessionType = SessionType.Unavailable; float lastSessionRunningTime = 0; int lastSessionLapsCompleted = 0; TrackDefinition lastSessionTrack = null; Boolean lastSessionHasFixedTime = false; int lastSessionNumberOfLaps = 0; float lastSessionRunTime = 0; float lastSessionTimeRemaining = 0; if (previousGameState != null) { lastSessionPhase = previousGameState.SessionData.SessionPhase; lastSessionType = previousGameState.SessionData.SessionType; lastSessionRunningTime = previousGameState.SessionData.SessionRunningTime; lastSessionHasFixedTime = previousGameState.SessionData.SessionHasFixedTime; lastSessionTrack = previousGameState.SessionData.TrackDefinition; lastSessionLapsCompleted = previousGameState.SessionData.CompletedLaps; lastSessionNumberOfLaps = previousGameState.SessionData.SessionNumberOfLaps; lastSessionRunTime = previousGameState.SessionData.SessionRunTime; lastSessionTimeRemaining = previousGameState.SessionData.SessionTimeRemaining; currentGameState.carClass = previousGameState.carClass; } if (currentGameState.carClass.carClassEnum == CarData.CarClassEnum.UNKNOWN_RACE) { CarData.CarClass newClass = CarData.getCarClassForPCarsClassName(shared.mCarClassName); if (newClass.carClassEnum != currentGameState.carClass.carClassEnum) { currentGameState.carClass = newClass; Console.WriteLine("Player is using car class " + currentGameState.carClass.carClassEnum + " (class name " + shared.mCarClassName + ")"); brakeTempThresholdsForPlayersCar = CarData.getBrakeTempThresholds(currentGameState.carClass, shared.mCarName); // no tyre data in the block so get the default tyre types for this car defaultTyreTypeForPlayersCar = CarData.getDefaultTyreType(currentGameState.carClass, shared.mCarName); } } // current session data currentGameState.SessionData.SessionType = mapToSessionType(shared); Boolean leaderHasFinished = previousGameState != null && previousGameState.SessionData.LeaderHasFinishedRace; currentGameState.SessionData.LeaderHasFinishedRace = leaderHasFinished; currentGameState.SessionData.IsDisqualified = shared.mRaceState == (int)eRaceState.RACESTATE_DISQUALIFIED; currentGameState.SessionData.SessionPhase = mapToSessionPhase(currentGameState.SessionData.SessionType, shared.mSessionState, shared.mRaceState, shared.mNumParticipants, leaderHasFinished, lastSessionPhase, lastSessionTimeRemaining, lastSessionRunTime); float sessionTimeRemaining = -1; int numberOfLapsInSession = (int)shared.mLapsInEvent; if (shared.mEventTimeRemaining > 0) { currentGameState.SessionData.SessionHasFixedTime = true; sessionTimeRemaining = shared.mEventTimeRemaining; } currentGameState.SessionData.TrackDefinition = TrackData.getTrackDefinition(shared.mTrackLocation + ":" + shared.mTrackVariation, shared.mTrackLength); // Console.WriteLine(lastSessionPhase + ", " + currentGameState.SessionData.SessionPhase + "; " + lastSessionType + ", " + currentGameState.SessionData.SessionType); // now check if this is a new session... Boolean raceRestarted = currentGameState.SessionData.SessionType == SessionType.Race && lastSessionPhase == SessionPhase.Green && currentGameState.SessionData.SessionPhase == SessionPhase.Countdown; if (raceRestarted || (currentGameState.SessionData.SessionType != SessionType.Unavailable && (lastSessionType != currentGameState.SessionData.SessionType || lastSessionHasFixedTime != currentGameState.SessionData.SessionHasFixedTime || lastSessionTrack == null || lastSessionTrack.name != currentGameState.SessionData.TrackDefinition.name || lastSessionLapsCompleted > currentGameState.SessionData.CompletedLaps || (numberOfLapsInSession > 0 && lastSessionNumberOfLaps > 0 && lastSessionNumberOfLaps != numberOfLapsInSession) || (sessionTimeRemaining > 0 && sessionTimeRemaining > lastSessionRunTime)))) { Console.WriteLine("New session, trigger..."); if (raceRestarted) { Console.WriteLine("Race restarted (green -> countdown)"); } if (lastSessionType != currentGameState.SessionData.SessionType) { Console.WriteLine("lastSessionType = " + lastSessionType + " currentGameState.SessionData.SessionType = " + currentGameState.SessionData.SessionType); } else if (lastSessionHasFixedTime != currentGameState.SessionData.SessionHasFixedTime) { Console.WriteLine("lastSessionHasFixedTime = " + lastSessionHasFixedTime + " currentGameState.SessionData.SessionHasFixedTime = " + currentGameState.SessionData.SessionHasFixedTime); } else if (lastSessionTrack != currentGameState.SessionData.TrackDefinition) { String lastTrackName = lastSessionTrack == null ? "unknown" : lastSessionTrack.name; String currentTrackName = currentGameState.SessionData.TrackDefinition == null ? "unknown" : currentGameState.SessionData.TrackDefinition.name; Console.WriteLine("lastSessionTrack = " + lastTrackName + " currentGameState.SessionData.Track = " + currentTrackName); } else if (lastSessionLapsCompleted > currentGameState.SessionData.CompletedLaps) { Console.WriteLine("lastSessionLapsCompleted = " + lastSessionLapsCompleted + " currentGameState.SessionData.CompletedLaps = " + currentGameState.SessionData.CompletedLaps); } else if (lastSessionNumberOfLaps != numberOfLapsInSession) { Console.WriteLine("lastSessionNumberOfLaps = " + lastSessionNumberOfLaps + " numberOfLapsInSession = "+ numberOfLapsInSession); } else if (sessionTimeRemaining > 0 && sessionTimeRemaining > lastSessionRunTime) { Console.WriteLine("sessionTimeRemaining = " + sessionTimeRemaining + " lastSessionRunTime = " + lastSessionRunTime); } currentGameState.SessionData.IsNewSession = true; currentGameState.SessionData.SessionNumberOfLaps = numberOfLapsInSession; currentGameState.SessionData.LeaderHasFinishedRace = false; currentGameState.SessionData.SessionStartTime = currentGameState.Now; currentGameState.SessionData.SessionStartPosition = (int)viewedParticipant.mRacePosition; if (currentGameState.SessionData.SessionHasFixedTime) { currentGameState.SessionData.SessionRunTime = sessionTimeRemaining; currentGameState.SessionData.SessionTimeRemaining = sessionTimeRemaining; if (currentGameState.SessionData.SessionRunTime == 0) { Console.WriteLine("Setting session run time to 0"); } Console.WriteLine("Time in this new session = " + sessionTimeRemaining); } currentGameState.SessionData.DriverRawName = viewedParticipant.mName; currentGameState.PitData.IsRefuellingAllowed = true; for (int i=0; i < shared.mParticipantData.Length; i++) { pCarsAPIParticipantStruct participantStruct = shared.mParticipantData[i]; if (i != playerDataIndex && participantStruct.mIsActive && participantStruct.mName != null && participantStruct.mName.Length > 0) { if (!currentGameState.OpponentData.ContainsKey(participantStruct.mName)) { currentGameState.OpponentData.Add(participantStruct.mName, createOpponentData(participantStruct, false)); } } } currentGameState.carClass = CarData.getCarClassForPCarsClassName(shared.mCarClassName); Console.WriteLine("Player is using car class " + currentGameState.carClass.carClassEnum + " (class name " + shared.mCarClassName + ")"); brakeTempThresholdsForPlayersCar = CarData.getBrakeTempThresholds(currentGameState.carClass, shared.mCarName); // no tyre data in the block so get the default tyre types for this car defaultTyreTypeForPlayersCar = CarData.getDefaultTyreType(currentGameState.carClass, shared.mCarName); } else { Boolean justGoneGreen = false; if (lastSessionPhase != currentGameState.SessionData.SessionPhase) { if (currentGameState.SessionData.SessionPhase == SessionPhase.Green) { justGoneGreen = true; // just gone green, so get the session data if (currentGameState.SessionData.SessionType == SessionType.Race) { if (currentGameState.SessionData.SessionHasFixedTime) { currentGameState.SessionData.SessionRunTime = sessionTimeRemaining; currentGameState.SessionData.SessionTimeRemaining = sessionTimeRemaining; if (currentGameState.SessionData.SessionRunTime == 0) { Console.WriteLine("Setting session run time to 0"); } } currentGameState.SessionData.SessionStartTime = currentGameState.Now; currentGameState.SessionData.SessionNumberOfLaps = numberOfLapsInSession; currentGameState.SessionData.SessionStartPosition = (int)viewedParticipant.mRacePosition; } currentGameState.SessionData.LeaderHasFinishedRace = false; currentGameState.SessionData.NumCarsAtStartOfSession = shared.mNumParticipants; currentGameState.SessionData.TrackDefinition = TrackData.getTrackDefinition(shared.mTrackLocation + ":" + shared.mTrackVariation, shared.mTrackLength); currentGameState.carClass = CarData.getCarClassForPCarsClassName(shared.mCarClassName); Console.WriteLine("Player is using car class " + currentGameState.carClass.carClassEnum + " (class name " + shared.mCarClassName + ")"); brakeTempThresholdsForPlayersCar = CarData.getBrakeTempThresholds(currentGameState.carClass, shared.mCarName); // no tyre data in the block so get the default tyre types for this car defaultTyreTypeForPlayersCar = CarData.getDefaultTyreType(currentGameState.carClass, shared.mCarName); if (previousGameState != null) { currentGameState.OpponentData = previousGameState.OpponentData; currentGameState.PitData.IsRefuellingAllowed = previousGameState.PitData.IsRefuellingAllowed; if (currentGameState.SessionData.SessionType != SessionType.Race) { currentGameState.SessionData.SessionStartTime = previousGameState.SessionData.SessionStartTime; currentGameState.SessionData.SessionRunTime = previousGameState.SessionData.SessionRunTime; currentGameState.SessionData.SessionTimeRemaining = previousGameState.SessionData.SessionTimeRemaining; currentGameState.SessionData.SessionNumberOfLaps = previousGameState.SessionData.SessionNumberOfLaps; } } Console.WriteLine("Just gone green, session details..."); Console.WriteLine("SessionType " + currentGameState.SessionData.SessionType); Console.WriteLine("SessionPhase " + currentGameState.SessionData.SessionPhase); if (previousGameState != null) { Console.WriteLine("previous SessionPhase " + previousGameState.SessionData.SessionPhase); } Console.WriteLine("EventIndex " + currentGameState.SessionData.EventIndex); Console.WriteLine("SessionIteration " + currentGameState.SessionData.SessionIteration); Console.WriteLine("HasMandatoryPitStop " + currentGameState.PitData.HasMandatoryPitStop); Console.WriteLine("PitWindowStart " + currentGameState.PitData.PitWindowStart); Console.WriteLine("PitWindowEnd " + currentGameState.PitData.PitWindowEnd); Console.WriteLine("NumCarsAtStartOfSession " + currentGameState.SessionData.NumCarsAtStartOfSession); Console.WriteLine("SessionNumberOfLaps " + currentGameState.SessionData.SessionNumberOfLaps); Console.WriteLine("SessionRunTime " + currentGameState.SessionData.SessionRunTime); Console.WriteLine("SessionStartPosition " + currentGameState.SessionData.SessionStartPosition); Console.WriteLine("SessionStartTime " + currentGameState.SessionData.SessionStartTime); String trackName = currentGameState.SessionData.TrackDefinition == null ? "unknown" : currentGameState.SessionData.TrackDefinition.name; Console.WriteLine("TrackName " + trackName); } } // copy persistent data from the previous game state // if (!justGoneGreen && previousGameState != null) { currentGameState.SessionData.SessionStartTime = previousGameState.SessionData.SessionStartTime; currentGameState.SessionData.SessionRunTime = previousGameState.SessionData.SessionRunTime; currentGameState.SessionData.SessionNumberOfLaps = previousGameState.SessionData.SessionNumberOfLaps; currentGameState.SessionData.SessionStartPosition = previousGameState.SessionData.SessionStartPosition; currentGameState.SessionData.NumCarsAtStartOfSession = previousGameState.SessionData.NumCarsAtStartOfSession; currentGameState.SessionData.TrackDefinition = previousGameState.SessionData.TrackDefinition; currentGameState.SessionData.EventIndex = previousGameState.SessionData.EventIndex; currentGameState.SessionData.SessionIteration = previousGameState.SessionData.SessionIteration; currentGameState.OpponentData = previousGameState.OpponentData; currentGameState.PitData.PitWindowStart = previousGameState.PitData.PitWindowStart; currentGameState.PitData.PitWindowEnd = previousGameState.PitData.PitWindowEnd; currentGameState.PitData.HasMandatoryPitStop = previousGameState.PitData.HasMandatoryPitStop; currentGameState.PitData.HasMandatoryDriverChange = previousGameState.PitData.HasMandatoryDriverChange; currentGameState.PitData.HasMandatoryTyreChange = previousGameState.PitData.HasMandatoryTyreChange; currentGameState.PitData.MandatoryTyreChangeRequiredTyreType = previousGameState.PitData.MandatoryTyreChangeRequiredTyreType; currentGameState.PitData.IsRefuellingAllowed = previousGameState.PitData.IsRefuellingAllowed; currentGameState.PitData.MaxPermittedDistanceOnCurrentTyre = previousGameState.PitData.MaxPermittedDistanceOnCurrentTyre; currentGameState.PitData.MinPermittedDistanceOnCurrentTyre = previousGameState.PitData.MinPermittedDistanceOnCurrentTyre; currentGameState.PitData.OnInLap = previousGameState.PitData.OnInLap; currentGameState.PitData.OnOutLap = previousGameState.PitData.OnOutLap; // the other properties of PitData are updated each tick, and shouldn't be copied over here. Nasty... currentGameState.SessionData.SessionTimesAtEndOfSectors = previousGameState.SessionData.SessionTimesAtEndOfSectors; currentGameState.PenaltiesData.CutTrackWarnings = previousGameState.PenaltiesData.CutTrackWarnings; currentGameState.SessionData.formattedPlayerLapTimes = previousGameState.SessionData.formattedPlayerLapTimes; currentGameState.SessionData.PlayerLapTimeSessionBest = previousGameState.SessionData.PlayerLapTimeSessionBest; currentGameState.SessionData.OpponentsLapTimeSessionBestOverall = previousGameState.SessionData.OpponentsLapTimeSessionBestOverall; currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass = previousGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass; currentGameState.SessionData.OverallSessionBestLapTime = previousGameState.SessionData.OverallSessionBestLapTime; currentGameState.SessionData.PlayerClassSessionBestLapTime = previousGameState.SessionData.PlayerClassSessionBestLapTime; currentGameState.SessionData.GameTimeAtLastPositionFrontChange = previousGameState.SessionData.GameTimeAtLastPositionFrontChange; currentGameState.SessionData.GameTimeAtLastPositionBehindChange = previousGameState.SessionData.GameTimeAtLastPositionBehindChange; currentGameState.SessionData.LastSector1Time = previousGameState.SessionData.LastSector1Time; currentGameState.SessionData.LastSector2Time = previousGameState.SessionData.LastSector2Time; currentGameState.SessionData.LastSector3Time = previousGameState.SessionData.LastSector3Time; currentGameState.SessionData.PlayerBestSector1Time = previousGameState.SessionData.PlayerBestSector1Time; currentGameState.SessionData.PlayerBestSector2Time = previousGameState.SessionData.PlayerBestSector2Time; currentGameState.SessionData.PlayerBestSector3Time = previousGameState.SessionData.PlayerBestSector3Time; currentGameState.SessionData.PlayerBestLapSector1Time = previousGameState.SessionData.PlayerBestLapSector1Time; currentGameState.SessionData.PlayerBestLapSector2Time = previousGameState.SessionData.PlayerBestLapSector2Time; currentGameState.SessionData.PlayerBestLapSector3Time = previousGameState.SessionData.PlayerBestLapSector3Time; currentGameState.Conditions = previousGameState.Conditions; } } //------------------- Variable session data --------------------------- if (currentGameState.SessionData.SessionHasFixedTime) { currentGameState.SessionData.SessionRunningTime = currentGameState.SessionData.SessionRunTime - shared.mEventTimeRemaining; currentGameState.SessionData.SessionTimeRemaining = shared.mEventTimeRemaining; } else { currentGameState.SessionData.SessionRunningTime = (float)(currentGameState.Now - currentGameState.SessionData.SessionStartTime).TotalSeconds; } if (currentGameState.SessionData.IsNewSector) { if (currentGameState.SessionData.SectorNumber == 1) { currentGameState.SessionData.LapTimePreviousEstimateForInvalidLap = currentGameState.SessionData.SessionRunningTime - currentGameState.SessionData.SessionTimesAtEndOfSectors[3]; currentGameState.SessionData.SessionTimesAtEndOfSectors[3] = currentGameState.SessionData.SessionRunningTime; currentGameState.SessionData.LastSector3Time = shared.mCurrentSector3Time; if (currentGameState.SessionData.LastSector3Time > 0 && (currentGameState.SessionData.PlayerBestSector3Time == -1 || currentGameState.SessionData.LastSector3Time < currentGameState.SessionData.PlayerBestSector3Time)) { currentGameState.SessionData.PlayerBestSector3Time = currentGameState.SessionData.LastSector3Time; } if (shared.mLastLapTime > 0 && (currentGameState.SessionData.PlayerLapTimeSessionBest == -1 || shared.mLastLapTime <= currentGameState.SessionData.PlayerLapTimeSessionBest)) { currentGameState.SessionData.PlayerBestLapSector1Time = currentGameState.SessionData.LastSector1Time; currentGameState.SessionData.PlayerBestLapSector2Time = currentGameState.SessionData.LastSector2Time; currentGameState.SessionData.PlayerBestLapSector3Time = currentGameState.SessionData.LastSector3Time; } } else if (currentGameState.SessionData.SectorNumber == 2) { currentGameState.SessionData.SessionTimesAtEndOfSectors[1] = currentGameState.SessionData.SessionRunningTime; currentGameState.SessionData.LastSector1Time = shared.mCurrentSector1Time; if (currentGameState.SessionData.LastSector1Time > 0 && (currentGameState.SessionData.PlayerBestSector1Time == -1 || currentGameState.SessionData.LastSector1Time < currentGameState.SessionData.PlayerBestSector1Time)) { currentGameState.SessionData.PlayerBestSector1Time = currentGameState.SessionData.LastSector1Time; } } if (currentGameState.SessionData.SectorNumber == 3) { currentGameState.SessionData.SessionTimesAtEndOfSectors[2] = currentGameState.SessionData.SessionRunningTime; currentGameState.SessionData.LastSector2Time = shared.mCurrentSector2Time; if (currentGameState.SessionData.LastSector2Time > 0 && (currentGameState.SessionData.PlayerBestSector2Time == -1 || currentGameState.SessionData.LastSector2Time < currentGameState.SessionData.PlayerBestSector2Time)) { currentGameState.SessionData.PlayerBestSector2Time = currentGameState.SessionData.LastSector2Time; } } } currentGameState.SessionData.Flag = mapToFlagEnum(shared.mHighestFlagColour); currentGameState.SessionData.NumCars = shared.mNumParticipants; currentGameState.SessionData.CurrentLapIsValid = !shared.mLapInvalidated; currentGameState.SessionData.IsNewLap = previousGameState == null || currentGameState.SessionData.CompletedLaps == previousGameState.SessionData.CompletedLaps + 1 || ((shared.mSessionState == (int)eSessionState.SESSION_PRACTICE || shared.mSessionState == (int)eSessionState.SESSION_QUALIFY || shared.mSessionState == (int)eSessionState.SESSION_TEST || shared.mSessionState == (int)eSessionState.SESSION_TIME_ATTACK) && previousGameState.SessionData.LapTimeCurrent == -1 && shared.mCurrentTime > 0); currentGameState.SessionData.IsRacingSameCarBehind = previousGameState != null && previousGameState.getOpponentKeyBehind(false) == currentGameState.getOpponentKeyBehind(false); currentGameState.SessionData.IsRacingSameCarInFront = previousGameState != null && previousGameState.getOpponentKeyInFront(false) == currentGameState.getOpponentKeyInFront(false); if (!currentGameState.SessionData.IsRacingSameCarInFront) { currentGameState.SessionData.GameTimeAtLastPositionFrontChange = currentGameState.SessionData.SessionRunningTime; } if (!currentGameState.SessionData.IsRacingSameCarBehind) { currentGameState.SessionData.GameTimeAtLastPositionBehindChange = currentGameState.SessionData.SessionRunningTime; } currentGameState.SessionData.LapTimeCurrent = shared.mCurrentTime; currentGameState.SessionData.TimeDeltaBehind = shared.mSplitTimeBehind; currentGameState.SessionData.TimeDeltaFront = shared.mSplitTimeAhead; // NOTE: the shared.mSessionFastestLapTime is JUST FOR THE PLAYER so the code below is not going to work: // currentGameState.SessionData.SessionFastestLapTimeFromGame = shared.mSessionFastestLapTime; // currentGameState.SessionData.SessionFastestLapTimeFromGamePlayerClass = shared.mSessionFastestLapTime; for (int i = 0; i < shared.mParticipantData.Length; i++) { if (i != playerDataIndex) { pCarsAPIParticipantStruct participantStruct = shared.mParticipantData[i]; if (participantStruct.mName != null && currentGameState.OpponentData.ContainsKey(participantStruct.mName)) { OpponentData currentOpponentData = currentGameState.OpponentData[participantStruct.mName]; if (currentOpponentData.IsActive && participantStruct.mIsActive) { if (previousGameState != null) { int previousOpponentSectorNumber = 1; int previousOpponentCompletedLaps = 0; int previousOpponentPosition = 0; Boolean previousOpponentIsEnteringPits = false; Boolean previousOpponentIsExitingPits = false; float[] previousOpponentWorldPosition = new float[] { 0, 0, 0 }; float previousOpponentSpeed = 0; OpponentData previousOpponentData = null; if (previousGameState.OpponentData.ContainsKey(participantStruct.mName)) { previousOpponentData = previousGameState.OpponentData[participantStruct.mName]; previousOpponentSectorNumber = previousOpponentData.CurrentSectorNumber; previousOpponentCompletedLaps = previousOpponentData.CompletedLaps; previousOpponentPosition = previousOpponentData.Position; previousOpponentIsEnteringPits = previousOpponentData.isEnteringPits(); previousOpponentIsExitingPits = previousOpponentData.isExitingPits(); previousOpponentWorldPosition = previousOpponentData.WorldPosition; previousOpponentSpeed = previousOpponentData.Speed; } int currentOpponentRacePosition = (int)participantStruct.mRacePosition; int currentOpponentLapsCompleted = (int)participantStruct.mLapsCompleted; int currentOpponentSector = (int)participantStruct.mCurrentSector; if (currentOpponentSector == 0) { currentOpponentSector = previousOpponentSectorNumber; } float currentOpponentLapDistance = participantStruct.mCurrentLapDistance; if (currentOpponentRacePosition == 1 && (currentGameState.SessionData.SessionNumberOfLaps > 0 && currentGameState.SessionData.SessionNumberOfLaps == currentOpponentLapsCompleted) || (currentGameState.SessionData.SessionRunTime > 0 && currentGameState.SessionData.SessionTimeRemaining < 1 && previousOpponentCompletedLaps < currentOpponentLapsCompleted)) { currentGameState.SessionData.LeaderHasFinishedRace = true; } if (currentOpponentRacePosition == 1 && previousOpponentPosition > 1) { currentGameState.SessionData.HasLeadChanged = true; } int opponentPositionAtSector3 = previousOpponentPosition; Boolean isEnteringPits = false; Boolean isLeavingPits = false; if (attemptPitDetection) { if (previousOpponentData != null && currentGameState.SessionData.SessionRunningTime > 30) { if (currentOpponentSector == 3) { if (previousOpponentSectorNumber == 2) { opponentPositionAtSector3 = currentOpponentRacePosition; } else if (!previousOpponentIsEnteringPits) { isEnteringPits = currentGameState.SessionData.TrackDefinition != null && currentGameState.SessionData.TrackDefinition.isAtPitEntry(participantStruct.mWorldPosition[0], participantStruct.mWorldPosition[2]); } else { isEnteringPits = previousOpponentIsEnteringPits; } } else if (currentOpponentSector == 1 && !previousOpponentIsExitingPits) { isLeavingPits = currentGameState.SessionData.TrackDefinition != null && currentGameState.SessionData.TrackDefinition.isAtPitExit(participantStruct.mWorldPosition[0], participantStruct.mWorldPosition[2]); } } if (isEnteringPits && !previousOpponentIsEnteringPits) { if (opponentPositionAtSector3 == 1) { Console.WriteLine("leader pitting, pos at sector 3 = " + opponentPositionAtSector3 + " current pos = " + currentOpponentRacePosition); currentGameState.PitData.LeaderIsPitting = true; currentGameState.PitData.OpponentForLeaderPitting = currentOpponentData; } if (currentGameState.SessionData.Position > 2 && opponentPositionAtSector3 == currentGameState.SessionData.Position - 1) { Console.WriteLine("car in front pitting, pos at sector 3 = " + opponentPositionAtSector3 + " current pos = " + currentOpponentRacePosition); currentGameState.PitData.CarInFrontIsPitting = true; currentGameState.PitData.OpponentForCarAheadPitting = currentOpponentData; } if (!currentGameState.isLast() && opponentPositionAtSector3 == currentGameState.SessionData.Position + 1) { Console.WriteLine("car behind pitting, pos at sector 3 = " + opponentPositionAtSector3 + " current pos = " + currentOpponentRacePosition); currentGameState.PitData.CarBehindIsPitting = true; currentGameState.PitData.OpponentForCarBehindPitting = currentOpponentData; } } } float secondsSinceLastUpdate = (float)new TimeSpan(currentGameState.Ticks - previousGameState.Ticks).TotalSeconds; upateOpponentData(currentOpponentData, currentOpponentRacePosition, currentOpponentLapsCompleted, currentOpponentSector, isEnteringPits || isLeavingPits, currentGameState.SessionData.SessionRunningTime, secondsSinceLastUpdate, new float[] { participantStruct.mWorldPosition[0], participantStruct.mWorldPosition[2] }, previousOpponentWorldPosition, previousOpponentSpeed, shared.mWorldFastestLapTime, participantStruct.mCurrentLapDistance, shared.mRainDensity == 1, shared.mAmbientTemperature, shared.mTrackTemperature); if (currentOpponentData.IsNewLap && currentOpponentData.CurrentBestLapTime > 0) { // the car class is always Unknown for PCars - it's not in the opponent data if (currentGameState.SessionData.OpponentsLapTimeSessionBestOverall == -1 || currentOpponentData.CurrentBestLapTime < currentGameState.SessionData.OpponentsLapTimeSessionBestOverall) { currentGameState.SessionData.OpponentsLapTimeSessionBestOverall = currentOpponentData.CurrentBestLapTime; currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass = currentOpponentData.CurrentBestLapTime; if (currentGameState.SessionData.OverallSessionBestLapTime == -1 || currentGameState.SessionData.OverallSessionBestLapTime > currentOpponentData.CurrentBestLapTime) { currentGameState.SessionData.OverallSessionBestLapTime = currentOpponentData.CurrentBestLapTime; currentGameState.SessionData.PlayerClassSessionBestLapTime = currentOpponentData.CurrentBestLapTime; } } } } } else { currentOpponentData.IsActive = false; } } else { if (participantStruct.mIsActive && participantStruct.mName != null && participantStruct.mName.Length > 0) { Console.WriteLine("Creating opponent for name " + participantStruct.mName); currentGameState.OpponentData.Add(participantStruct.mName, createOpponentData(participantStruct, true)); } } } } currentGameState.SessionData.LapTimePrevious = shared.mLastLapTime; if (currentGameState.SessionData.IsNewLap) { currentGameState.SessionData.PreviousLapWasValid = previousGameState != null && previousGameState.SessionData.CurrentLapIsValid; currentGameState.SessionData.formattedPlayerLapTimes.Add(TimeSpan.FromSeconds(shared.mLastLapTime).ToString(@"mm\:ss\.fff")); } else if (previousGameState != null) { currentGameState.SessionData.PreviousLapWasValid = previousGameState.SessionData.PreviousLapWasValid; } if (currentGameState.SessionData.IsNewLap && currentGameState.SessionData.PreviousLapWasValid && currentGameState.SessionData.LapTimePrevious > 0) { if (currentGameState.SessionData.PlayerLapTimeSessionBest == -1 || currentGameState.SessionData.LapTimePrevious < currentGameState.SessionData.PlayerLapTimeSessionBest) { currentGameState.SessionData.PlayerLapTimeSessionBest = currentGameState.SessionData.LapTimePrevious; if (currentGameState.SessionData.OverallSessionBestLapTime == -1 || currentGameState.SessionData.LapTimePrevious < currentGameState.SessionData.OverallSessionBestLapTime) { currentGameState.SessionData.OverallSessionBestLapTime = currentGameState.SessionData.LapTimePrevious; } if (currentGameState.SessionData.PlayerClassSessionBestLapTime == -1 || currentGameState.SessionData.LapTimePrevious < currentGameState.SessionData.PlayerClassSessionBestLapTime) { currentGameState.SessionData.PlayerClassSessionBestLapTime = currentGameState.SessionData.LapTimePrevious; } } } currentGameState.PitData.InPitlane = shared.mPitMode == (int)ePitMode.PIT_MODE_DRIVING_INTO_PITS || shared.mPitMode == (int)ePitMode.PIT_MODE_IN_PIT || shared.mPitMode == (int)ePitMode.PIT_MODE_DRIVING_OUT_OF_PITS || shared.mPitMode == (int)ePitMode.PIT_MODE_IN_GARAGE; if (currentGameState.PitData.InPitlane) { // should we just use the sector number to check this? if (shared.mPitMode == (int)ePitMode.PIT_MODE_DRIVING_INTO_PITS) { currentGameState.PitData.OnInLap = true; currentGameState.PitData.OnOutLap = false; } else if (shared.mPitMode == (int)ePitMode.PIT_MODE_DRIVING_OUT_OF_PITS || shared.mPitMode == (int)ePitMode.PIT_MODE_IN_GARAGE) { currentGameState.PitData.OnInLap = false; currentGameState.PitData.OnOutLap = true; } } else if (currentGameState.SessionData.IsNewLap) { currentGameState.PitData.OnInLap = false; currentGameState.PitData.OnOutLap = false; } currentGameState.PitData.IsAtPitExit = previousGameState != null && currentGameState.PitData.OnOutLap && previousGameState.PitData.InPitlane && !currentGameState.PitData.InPitlane; currentGameState.PitData.HasRequestedPitStop = shared.mPitSchedule == (int)ePitSchedule.PIT_SCHEDULE_STANDARD; if (previousGameState != null && previousGameState.PitData.HasRequestedPitStop) { Console.WriteLine("Has requested pitstop"); } if (currentGameState.SessionData.SessionType == SessionType.Race && shared.mEnforcedPitStopLap > 0) { currentGameState.PitData.HasMandatoryPitStop = true; currentGameState.PitData.PitWindowStart = (int) shared.mEnforcedPitStopLap; currentGameState.PitData.PitWindow = mapToPitWindow(currentGameState, shared.mPitSchedule, shared.mPitMode); currentGameState.PitData.IsMakingMandatoryPitStop = (currentGameState.PitData.PitWindow == PitWindow.Open || currentGameState.PitData.PitWindow == PitWindow.StopInProgress) && (currentGameState.PitData.OnInLap || currentGameState.PitData.OnOutLap); } currentGameState.CarDamageData.DamageEnabled = true; // no way to tell if it's disabled from the shared memory currentGameState.CarDamageData.OverallAeroDamage = mapToAeroDamageLevel(shared.mAeroDamage); currentGameState.CarDamageData.OverallEngineDamage = mapToEngineDamageLevel(shared.mEngineDamage); currentGameState.CarDamageData.OverallTransmissionDamage = DamageLevel.NONE; currentGameState.CarDamageData.SuspensionDamageStatus = CornerData.getCornerData(suspensionDamageThresholds, shared.mSuspensionDamage[0], shared.mSuspensionDamage[1], shared.mSuspensionDamage[2], shared.mSuspensionDamage[3]); currentGameState.CarDamageData.BrakeDamageStatus = CornerData.getCornerData(brakeDamageThresholds, shared.mBrakeDamage[0], shared.mBrakeDamage[1], shared.mBrakeDamage[2], shared.mBrakeDamage[3]); currentGameState.EngineData.EngineOilPressure = shared.mOilPressureKPa; // todo: units conversion currentGameState.EngineData.EngineOilTemp = shared.mOilTempCelsius; currentGameState.EngineData.EngineWaterTemp = shared.mWaterTempCelsius; currentGameState.EngineData.EngineRpm = shared.mRPM; currentGameState.EngineData.MaxEngineRpm = shared.mMaxRPM; currentGameState.EngineData.MinutesIntoSessionBeforeMonitoring = 2; currentGameState.FuelData.FuelCapacity = shared.mFuelCapacity; currentGameState.FuelData.FuelLeft = currentGameState.FuelData.FuelCapacity * shared.mFuelLevel; currentGameState.FuelData.FuelPressure = shared.mFuelPressureKPa; currentGameState.FuelData.FuelUseActive = true; // no way to tell if it's disabled currentGameState.PenaltiesData.HasDriveThrough = shared.mPitSchedule == (int)ePitSchedule.PIT_SCHEDULE_DRIVE_THROUGH; currentGameState.PenaltiesData.HasStopAndGo = shared.mPitSchedule == (int)ePitSchedule.PIT_SCHEDULE_STOP_GO; currentGameState.PositionAndMotionData.CarSpeed = shared.mSpeed; //------------------------ Tyre data ----------------------- currentGameState.TyreData.HasMatchedTyreTypes = true; currentGameState.TyreData.TireWearActive = true; currentGameState.TyreData.LeftFrontAttached = (shared.mTyreFlags[0] & 1) == 1; currentGameState.TyreData.RightFrontAttached = (shared.mTyreFlags[1] & 1) == 1; currentGameState.TyreData.LeftRearAttached = (shared.mTyreFlags[2] & 1) == 1; currentGameState.TyreData.RightRearAttached = (shared.mTyreFlags[3] & 1) == 1; currentGameState.TyreData.FrontLeft_CenterTemp = shared.mTyreTreadTemp[0] - 273; currentGameState.TyreData.FrontLeft_LeftTemp = shared.mTyreTreadTemp[0] - 273; currentGameState.TyreData.FrontLeft_RightTemp = shared.mTyreTreadTemp[0] - 273; currentGameState.TyreData.FrontLeftTyreType = defaultTyreTypeForPlayersCar; currentGameState.TyreData.FrontLeftPressure = -1; // not in the block currentGameState.TyreData.FrontLeftPercentWear = Math.Min(100, shared.mTyreWear[0] * 100 / wornOutTyreWearLevel); if (currentGameState.SessionData.IsNewLap) { currentGameState.TyreData.PeakFrontLeftTemperatureForLap = currentGameState.TyreData.FrontLeft_CenterTemp; } else if (previousGameState == null || currentGameState.TyreData.FrontLeft_CenterTemp > previousGameState.TyreData.PeakFrontLeftTemperatureForLap) { currentGameState.TyreData.PeakFrontLeftTemperatureForLap = currentGameState.TyreData.FrontLeft_CenterTemp; } currentGameState.TyreData.FrontRight_CenterTemp = shared.mTyreTreadTemp[1] - 273; currentGameState.TyreData.FrontRight_LeftTemp = shared.mTyreTreadTemp[1] - 273; currentGameState.TyreData.FrontRight_RightTemp = shared.mTyreTreadTemp[1] - 273; currentGameState.TyreData.FrontRightTyreType = defaultTyreTypeForPlayersCar; currentGameState.TyreData.FrontRightPressure = -1; // not in the block currentGameState.TyreData.FrontRightPercentWear = Math.Min(100, shared.mTyreWear[1] * 100 / wornOutTyreWearLevel); if (currentGameState.SessionData.IsNewLap) { currentGameState.TyreData.PeakFrontRightTemperatureForLap = currentGameState.TyreData.FrontRight_CenterTemp; } else if (previousGameState == null || currentGameState.TyreData.FrontRight_CenterTemp > previousGameState.TyreData.PeakFrontRightTemperatureForLap) { currentGameState.TyreData.PeakFrontRightTemperatureForLap = currentGameState.TyreData.FrontRight_CenterTemp; } currentGameState.TyreData.RearLeft_CenterTemp = shared.mTyreTreadTemp[2] - 273; currentGameState.TyreData.RearLeft_LeftTemp = shared.mTyreTreadTemp[2] - 273; currentGameState.TyreData.RearLeft_RightTemp = shared.mTyreTreadTemp[2] - 273; currentGameState.TyreData.RearLeftTyreType = defaultTyreTypeForPlayersCar; currentGameState.TyreData.RearLeftPressure = -1; // not in the block currentGameState.TyreData.RearLeftPercentWear = Math.Min(100, shared.mTyreWear[2] * 100 / wornOutTyreWearLevel); if (currentGameState.SessionData.IsNewLap) { currentGameState.TyreData.PeakRearLeftTemperatureForLap = currentGameState.TyreData.RearLeft_CenterTemp; } else if (previousGameState == null || currentGameState.TyreData.RearLeft_CenterTemp > previousGameState.TyreData.PeakRearLeftTemperatureForLap) { currentGameState.TyreData.PeakRearLeftTemperatureForLap = currentGameState.TyreData.RearLeft_CenterTemp; } currentGameState.TyreData.RearRight_CenterTemp = shared.mTyreTreadTemp[3] - 273; currentGameState.TyreData.RearRight_LeftTemp = shared.mTyreTreadTemp[3] - 273; currentGameState.TyreData.RearRight_RightTemp = shared.mTyreTreadTemp[3] - 273; currentGameState.TyreData.RearRightTyreType = defaultTyreTypeForPlayersCar; currentGameState.TyreData.RearRightPressure = -1; // not in the block currentGameState.TyreData.RearRightPercentWear = Math.Min(100, shared.mTyreWear[3] * 100 / wornOutTyreWearLevel); if (currentGameState.SessionData.IsNewLap) { currentGameState.TyreData.PeakRearRightTemperatureForLap = currentGameState.TyreData.RearRight_CenterTemp; } else if (previousGameState == null || currentGameState.TyreData.RearRight_CenterTemp > previousGameState.TyreData.PeakRearRightTemperatureForLap) { currentGameState.TyreData.PeakRearRightTemperatureForLap = currentGameState.TyreData.RearRight_CenterTemp; } currentGameState.TyreData.TyreConditionStatus = CornerData.getCornerData(tyreWearThresholds, currentGameState.TyreData.FrontLeftPercentWear, currentGameState.TyreData.FrontRightPercentWear, currentGameState.TyreData.RearLeftPercentWear, currentGameState.TyreData.RearRightPercentWear); currentGameState.TyreData.TyreTempStatus = CornerData.getCornerData(CarData.tyreTempThresholds[defaultTyreTypeForPlayersCar], currentGameState.TyreData.PeakFrontLeftTemperatureForLap, currentGameState.TyreData.PeakFrontRightTemperatureForLap, currentGameState.TyreData.PeakRearLeftTemperatureForLap, currentGameState.TyreData.PeakRearRightTemperatureForLap); currentGameState.TyreData.BrakeTempStatus = CornerData.getCornerData(brakeTempThresholdsForPlayersCar, shared.mBrakeTempCelsius[0], shared.mBrakeTempCelsius[1], shared.mBrakeTempCelsius[2], shared.mBrakeTempCelsius[3]); currentGameState.TyreData.LeftFrontBrakeTemp = shared.mBrakeTempCelsius[0]; currentGameState.TyreData.RightFrontBrakeTemp = shared.mBrakeTempCelsius[1]; currentGameState.TyreData.LeftRearBrakeTemp = shared.mBrakeTempCelsius[2]; currentGameState.TyreData.RightRearBrakeTemp = shared.mBrakeTempCelsius[0]; // improvised cut track warnings... if (incrementCutTrackCountWhenLeavingRacingSurface) { currentGameState.PenaltiesData.IsOffRacingSurface = !racingSurfaces.Contains(shared.mTerrain[0]) && !racingSurfaces.Contains(shared.mTerrain[1]) && !racingSurfaces.Contains(shared.mTerrain[2]) && !racingSurfaces.Contains(shared.mTerrain[3]); if (previousGameState != null && previousGameState.PenaltiesData.IsOffRacingSurface && currentGameState.PenaltiesData.IsOffRacingSurface) { currentGameState.PenaltiesData.CutTrackWarnings = previousGameState.PenaltiesData.CutTrackWarnings + 1; } } if (!currentGameState.PitData.OnOutLap && previousGameState != null && previousGameState.SessionData.CurrentLapIsValid && !currentGameState.SessionData.CurrentLapIsValid && !(shared.mSessionState == (int)eSessionState.SESSION_RACE && shared.mRaceState == (int)eRaceState.RACESTATE_NOT_STARTED)) { currentGameState.PenaltiesData.CutTrackWarnings = previousGameState.PenaltiesData.CutTrackWarnings + 1; } // Tyre slip speed seems to peak at about 30 with big lock or wheelspin (in Sauber Merc). It's noisy as hell and is frequently bouncing around // in single figures, with the noise varying between cars. // tyreRPS is much cleaner but we don't know the diameter of the tyre so can't compare it (accurately) to the car's speed if (shared.mSpeed > 7) { float minRotatingSpeed = 2 * (float)Math.PI * shared.mSpeed / currentGameState.carClass.maxTyreCircumference; // I think the tyreRPS is actually radians per second... currentGameState.TyreData.LeftFrontIsLocked = Math.Abs(shared.mTyreRPS[0]) < minRotatingSpeed; currentGameState.TyreData.RightFrontIsLocked = Math.Abs(shared.mTyreRPS[1]) < minRotatingSpeed; currentGameState.TyreData.LeftRearIsLocked = Math.Abs(shared.mTyreRPS[2]) < minRotatingSpeed; currentGameState.TyreData.RightRearIsLocked = Math.Abs(shared.mTyreRPS[3]) < minRotatingSpeed; float maxRotatingSpeed = 2 * (float)Math.PI * shared.mSpeed / currentGameState.carClass.minTyreCircumference; currentGameState.TyreData.LeftFrontIsSpinning = Math.Abs(shared.mTyreRPS[0]) > maxRotatingSpeed; currentGameState.TyreData.RightFrontIsSpinning = Math.Abs(shared.mTyreRPS[1]) > maxRotatingSpeed; currentGameState.TyreData.LeftRearIsSpinning = Math.Abs(shared.mTyreRPS[2]) > maxRotatingSpeed; currentGameState.TyreData.RightRearIsSpinning = Math.Abs(shared.mTyreRPS[3]) > maxRotatingSpeed; } if (currentGameState.Conditions.timeOfMostRecentSample.Add(ConditionsMonitor.ConditionsSampleFrequency) < currentGameState.Now) { currentGameState.Conditions.addSample(currentGameState.Now, currentGameState.SessionData.CompletedLaps, currentGameState.SessionData.SectorNumber, shared.mAmbientTemperature, shared.mTrackTemperature, shared.mRainDensity, shared.mWindSpeed, shared.mWindDirectionX, shared.mWindDirectionY, shared.mCloudBrightness); } return currentGameState; }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { addExtraLapForDTM2015 = currentGameState.carClass.carClassEnum == CarData.CarClassEnum.DTM_2015; leaderHasFinishedRace = currentGameState.SessionData.LeaderHasFinishedRace; timeLeft = currentGameState.SessionData.SessionTimeRemaining; if (currentGameState.SessionData.SessionNumberOfLaps > 0) { lapsLeft = currentGameState.SessionData.SessionNumberOfLaps - currentGameState.SessionData.CompletedLaps; sessionLengthIsTime = false; } else { sessionLengthIsTime = true; } if (sessionLengthIsTime) { if (addExtraLapForDTM2015 && gotHalfTime && timeLeft <= 0 && currentGameState.SessionData.IsNewLap) { startedDTM2015ExtraLap = true; } if (!gotHalfTime) { Console.WriteLine("Session time remaining = " + timeLeft); halfTime = timeLeft / 2; gotHalfTime = true; if (currentGameState.FuelData.FuelUseActive) { // don't allow the half way message to play if fuel use is active - there's already one in there playedHalfWayHome = true; } } PearlsOfWisdom.PearlType pearlType = PearlsOfWisdom.PearlType.NONE; if (currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.CompletedLaps > 1) { pearlType = PearlsOfWisdom.PearlType.NEUTRAL; if (currentGameState.SessionData.Position < 4) { pearlType = PearlsOfWisdom.PearlType.GOOD; } else if (currentGameState.SessionData.Position > 10) { pearlType = PearlsOfWisdom.PearlType.BAD; } } // this event only works if we're leading because we don't know when the leader // crosses the line :( // TODO: the above is no longer true - rework this if (currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.IsNewLap && currentGameState.SessionData.SessionRunningTime > 60 && !playedLastLap && currentGameState.SessionData.Position == 1 && ((!addExtraLapForDTM2015 && timeLeft > 0 && timeLeft < currentGameState.SessionData.PlayerLapTimeSessionBest) || (addExtraLapForDTM2015 && timeLeft <= 0))) { playedLastLap = true; played2mins = true; played5mins = true; played10mins = true; played15mins = true; played20mins = true; playedHalfWayHome = true; if (currentGameState.SessionData.Position == 1) { // don't add a pearl here - the audio clip already contains encouragement audioPlayer.queueClip(new QueuedMessage(folderLastLapLeading, 0, this), pearlType, 0); } else if (currentGameState.SessionData.Position < 4) { // don't add a pearl here - the audio clip already contains encouragement audioPlayer.queueClip(new QueuedMessage(folderLastLapPodium, 0, this), pearlType, 0); } else { audioPlayer.queueClip(new QueuedMessage(folderLastLap, 0, this), pearlType, 0.7); } } if (currentGameState.SessionData.SessionRunningTime > 60 && timeLeft / 60 < 3 && timeLeft / 60 > 2.9) { // disable pearls for the last part of the race audioPlayer.disablePearlsOfWisdom = true; } if (currentGameState.SessionData.SessionRunningTime > 60 && !played2mins && timeLeft / 60 < 2 && timeLeft / 60 > 1.9) { played2mins = true; played5mins = true; played10mins = true; played15mins = true; played20mins = true; playedHalfWayHome = true; audioPlayer.suspendPearlsOfWisdom(); audioPlayer.queueClip(new QueuedMessage(folder2mins, 0, this)); } if (currentGameState.SessionData.SessionRunningTime > 60 && !played5mins && timeLeft / 60 < 5 && timeLeft / 60 > 4.9) { played5mins = true; played10mins = true; played15mins = true; played20mins = true; playedHalfWayHome = true; if (currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.Position == 1) { // don't add a pearl here - the audio clip already contains encouragement audioPlayer.queueClip(new QueuedMessage(folder5minsLeading, 0, this), pearlType, 0); } else if (currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.Position < 4) { // don't add a pearl here - the audio clip already contains encouragement audioPlayer.queueClip(new QueuedMessage(folder5minsPodium, 0, this), pearlType, 0); } else { audioPlayer.queueClip(new QueuedMessage(folder5mins, 0, this), pearlType, 0.7); } } if (currentGameState.SessionData.SessionRunningTime > 60 && !played10mins && timeLeft / 60 < 10 && timeLeft / 60 > 9.9) { played10mins = true; played15mins = true; played20mins = true; audioPlayer.queueClip(new QueuedMessage(folder10mins, 0, this), pearlType, 0.7); } if (currentGameState.SessionData.SessionRunningTime > 60 && !played15mins && timeLeft / 60 < 15 && timeLeft / 60 > 14.9) { played15mins = true; played20mins = true; audioPlayer.queueClip(new QueuedMessage(folder15mins, 0, this), pearlType, 0.7); } if (currentGameState.SessionData.SessionRunningTime > 60 && !played20mins && timeLeft / 60 < 20 && timeLeft / 60 > 19.9) { played20mins = true; audioPlayer.queueClip(new QueuedMessage(folder20mins, 0, this), pearlType, 0.7); } else if (currentGameState.SessionData.SessionType == SessionType.Race && currentGameState.SessionData.SessionRunningTime > 60 && !playedHalfWayHome && timeLeft > 0 && timeLeft < halfTime) { // this one sounds weird in practice and qual sessions, so skip it playedHalfWayHome = true; audioPlayer.queueClip(new QueuedMessage(folderHalfWayHome, 0, this), pearlType, 0.7); } } }
// this is called on each 'tick' - the event subtype should // place its logic in here including calls to audioPlayer.queueClip protected abstract void triggerInternal(GameStateData previousGameState, GameStateData currentGameState);
private PitWindow mapToPitWindow(GameStateData currentGameState, uint pitSchedule, uint pitMode) { if (currentGameState.PitData.PitWindowStart > 0) { if ((currentGameState.SessionData.SessionNumberOfLaps > 0 && currentGameState.SessionData.CompletedLaps < currentGameState.PitData.PitWindowStart - 1) || (currentGameState.SessionData.SessionRunTime > 0 && currentGameState.SessionData.SessionRunningTime < currentGameState.PitData.PitWindowStart)) { return PitWindow.Closed; } else if ((currentGameState.SessionData.SessionNumberOfLaps > 0 && currentGameState.SessionData.CompletedLaps >= currentGameState.PitData.PitWindowStart - 1) || (currentGameState.SessionData.SessionRunTime > 0 && currentGameState.SessionData.SessionRunningTime >= currentGameState.PitData.PitWindowStart)) { if (currentGameState.PitData.PitWindow == PitWindow.Completed || (currentGameState.PitData.PitWindow == PitWindow.StopInProgress && pitMode == (uint)ePitMode.PIT_MODE_DRIVING_OUT_OF_PITS)) { return PitWindow.Completed; } else if (pitSchedule == (uint)ePitSchedule.PIT_SCHEDULE_STANDARD && (pitMode == (uint)ePitMode.PIT_MODE_DRIVING_INTO_PITS || pitMode == (uint)ePitMode.PIT_MODE_IN_PIT)) { return PitWindow.StopInProgress; } else { return PitWindow.Open; } } } return PitWindow.Unavailable; }
public void trigger(GameStateData previousGameState, GameStateData currentGameState) { // common checks here? triggerInternal(previousGameState, currentGameState); }
// generally the event subclass can just return true for this, but when a clip is played with // a non-zero delay it may be necessary to re-check that the clip is still valid against the current // state public virtual Boolean isMessageStillValid(String eventSubType, GameStateData currentGameState, Dictionary<String, Object> validationData) { return currentGameState != null && isApplicableForCurrentSessionAndPhase(currentGameState.SessionData.SessionType, currentGameState.SessionData.SessionPhase); }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { if (engineData == null) { clearState(); } if (maxSafeWaterTemp == 0) { maxSafeWaterTemp = currentGameState.carClass.maxSafeWaterTemp; } if (maxSafeOilTemp == 0) { maxSafeOilTemp = currentGameState.carClass.maxSafeOilTemp; } if (currentGameState.SessionData.SessionRunningTime > 60 * currentGameState.EngineData.MinutesIntoSessionBeforeMonitoring) { engineData.addSample(currentGameState.EngineData.EngineOilTemp, currentGameState.EngineData.EngineWaterTemp, currentGameState.EngineData.EngineOilPressure); if (currentGameState.SessionData.SessionRunningTime > gameTimeAtLastStatusCheck + statusMonitorWindowLength) { EngineStatus currentEngineStatus = engineData.getEngineStatusFromAverage(maxSafeWaterTemp, maxSafeOilTemp); if (currentEngineStatus != lastStatusMessage) { switch (currentEngineStatus) { case EngineStatus.ALL_CLEAR: lastStatusMessage = currentEngineStatus; audioPlayer.queueClip(new QueuedMessage(folderAllClear, 0, this)); break; case EngineStatus.HOT_OIL: // don't play this if the last message was about hot oil *and* water - wait for 'all clear' if (lastStatusMessage != EngineStatus.HOT_OIL_AND_WATER) { lastStatusMessage = currentEngineStatus; audioPlayer.queueClip(new QueuedMessage(folderHotOil, 0, this)); } break; case EngineStatus.HOT_WATER: // don't play this if the last message was about hot oil *and* water - wait for 'all clear' if (lastStatusMessage != EngineStatus.HOT_OIL_AND_WATER) { lastStatusMessage = currentEngineStatus; audioPlayer.queueClip(new QueuedMessage(folderHotWater, 0, this)); } break; case EngineStatus.HOT_OIL_AND_WATER: lastStatusMessage = currentEngineStatus; audioPlayer.queueClip(new QueuedMessage(folderHotOilAndWater, 0, this)); break; } } gameTimeAtLastStatusCheck = currentGameState.SessionData.SessionRunningTime; engineData = new EngineData(); } } }
public override void clearState() { lapTimesWindow = new List<float>(lapTimesWindowSize); lastConsistencyUpdate = 0; lastConsistencyMessage = ConsistencyResult.NOT_APPLICABLE; lapIsValid = true; lastLapRating = LastLapRating.NO_DATA; deltaPlayerBestToSessionBestInClass = TimeSpan.MaxValue; deltaPlayerBestToSessionBestOverall = TimeSpan.MaxValue; deltaPlayerLastToSessionBestInClass = TimeSpan.MaxValue; deltaPlayerLastToSessionBestOverall = TimeSpan.MaxValue; lastLapTime = 0; bestLapTime = 0; currentPosition = -1; currentGameState = null; isHotLapping = false; lastGapToSecondWhenLeadingPracOrQual = TimeSpan.Zero; }
private float getOpponentBestLap(int opponentPosition, int lapsToCheck, GameStateData gameState) { OpponentData opponent = gameState.getOpponentAtPosition(opponentPosition, false); if (opponent == null || opponent.OpponentLapData.Count < lapsToCheck) { return -1; } float bestLap = -1; for (int i = opponent.OpponentLapData.Count - 1; i >= opponent.OpponentLapData.Count - lapsToCheck; i--) { if (bestLap == -1 || bestLap > opponent.OpponentLapData[i].LapTime) { bestLap = opponent.OpponentLapData[i].LapTime; } } return bestLap; }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { sessionType = currentGameState.SessionData.SessionType; this.currentGameState = currentGameState; if (currentGameState.SessionData.IsNewLap) { if (currentGameState.SessionData.LapTimePrevious > 0) { if (currentGameState.OpponentData.Count > 0) { if (currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass > 0) { deltaPlayerLastToSessionBestInClass = TimeSpan.FromSeconds( currentGameState.SessionData.LapTimePrevious - currentGameState.SessionData.OpponentsLapTimeSessionBestPlayerClass); } if (currentGameState.SessionData.OpponentsLapTimeSessionBestOverall > 0) { deltaPlayerLastToSessionBestOverall = TimeSpan.FromSeconds( currentGameState.SessionData.LapTimePrevious - currentGameState.SessionData.OpponentsLapTimeSessionBestOverall); } } else if (currentGameState.SessionData.PlayerLapTimeSessionBest > 0) { deltaPlayerLastToSessionBestOverall = TimeSpan.FromSeconds(currentGameState.SessionData.LapTimePrevious - currentGameState.SessionData.PlayerLapTimeSessionBest); deltaPlayerLastToSessionBestInClass = deltaPlayerLastToSessionBestOverall; } if (currentGameState.SessionData.LapTimePrevious <= currentGameState.SessionData.PlayerLapTimeSessionBest) { deltaPlayerBestToSessionBestInClass = deltaPlayerLastToSessionBestInClass; deltaPlayerBestToSessionBestOverall = deltaPlayerLastToSessionBestOverall; } } else { // the last lap was invalid so the delta is undefined deltaPlayerLastToSessionBestInClass = TimeSpan.MaxValue; deltaPlayerLastToSessionBestOverall = TimeSpan.MaxValue; } } currentPosition = currentGameState.SessionData.Position; // check the current lap is still valid if (lapIsValid && currentGameState.SessionData.CompletedLaps > 0 && !currentGameState.SessionData.IsNewLap && !currentGameState.SessionData.CurrentLapIsValid) { lapIsValid = false; } if (currentGameState.SessionData.IsNewLap) { lastLapTime = currentGameState.SessionData.LapTimePrevious; if (lastLapTime > 0 && lapIsValid) { if (bestLapTime == 0 || lastLapTime < bestLapTime) { bestLapTime = lastLapTime; } } } float[] lapAndSectorsComparisonData = new float[] { -1, -1, -1, -1 }; if (currentGameState.SessionData.IsNewSector) { isHotLapping = currentGameState.SessionData.SessionType == SessionType.HotLap || (currentGameState.OpponentData.Count == 0 || ( currentGameState.OpponentData.Count == 1 && currentGameState.OpponentData.First().Value.DriverRawName == currentGameState.SessionData.DriverRawName)); if (isHotLapping) { lapAndSectorsComparisonData[1] = currentGameState.SessionData.PlayerBestLapSector1Time; lapAndSectorsComparisonData[2] = currentGameState.SessionData.PlayerBestLapSector2Time; lapAndSectorsComparisonData[3] = currentGameState.SessionData.PlayerBestLapSector3Time; } else { if (currentGameState.SessionData.SessionType == SessionType.Race) { lapAndSectorsComparisonData = currentGameState.getTimeAndSectorsForBestOpponentLapInWindow(paceCheckLapsWindowForRace, currentGameState.carClass.carClassEnum); } else if (currentGameState.SessionData.SessionType == SessionType.Qualify || currentGameState.SessionData.SessionType == SessionType.Practice) { lapAndSectorsComparisonData = currentGameState.getTimeAndSectorsForBestOpponentLapInWindow(-1, currentGameState.carClass.carClassEnum); } } } if (!currentGameState.PitData.OnInLap && !currentGameState.PitData.OnOutLap) { Boolean sectorsReportedForLap = false; if (currentGameState.SessionData.IsNewLap && ((currentGameState.SessionData.SessionType == SessionType.HotLap && currentGameState.SessionData.CompletedLaps > 0) || currentGameState.SessionData.CompletedLaps > 1)) { if (lapTimesWindow == null) { lapTimesWindow = new List<float>(lapTimesWindowSize); } lastLapRating = getLastLapRating(currentGameState, lapAndSectorsComparisonData); if (currentGameState.SessionData.PreviousLapWasValid) { lapTimesWindow.Insert(0, currentGameState.SessionData.LapTimePrevious); if (lapIsValid) { Boolean playedLapTime = false; if (isHotLapping) { // always play the laptime in hotlap mode audioPlayer.queueClip(new QueuedMessage("laptime", MessageContents(folderLapTimeIntro, TimeSpan.FromSeconds(currentGameState.SessionData.LapTimePrevious)), 0, this)); playedLapTime = true; } else if (((currentGameState.SessionData.SessionType == SessionType.Qualify || currentGameState.SessionData.SessionType == SessionType.Practice) && frequencyOfPlayerQualAndPracLapTimeReports > random.NextDouble() * 10) || (currentGameState.SessionData.SessionType == SessionType.Race && frequencyOfPlayerRaceLapTimeReports > random.NextDouble() * 10)) { // usually play it in practice / qual mode, occasionally play it in race mode QueuedMessage gapFillerLapTime = new QueuedMessage("laptime", MessageContents(folderLapTimeIntro, TimeSpan.FromSeconds(currentGameState.SessionData.LapTimePrevious)), 0, this); if (currentGameState.SessionData.SessionType == SessionType.Race) { gapFillerLapTime.maxPermittedQueueLengthForMessage = maxQueueLengthForRaceLapTimeReports; } audioPlayer.queueClip(gapFillerLapTime); playedLapTime = true; } if (currentGameState.SessionData.SessionType == SessionType.Qualify || currentGameState.SessionData.SessionType == SessionType.Practice || currentGameState.SessionData.SessionType == SessionType.HotLap) { if (currentGameState.SessionData.SessionType == SessionType.HotLap || currentGameState.OpponentData.Count == 0) { if (lastLapRating == LastLapRating.BEST_IN_CLASS || deltaPlayerLastToSessionBestOverall <= TimeSpan.Zero) { audioPlayer.queueClip(new QueuedMessage(folderPersonalBest, 0, this)); } else if (deltaPlayerLastToSessionBestOverall < TimeSpan.FromMilliseconds(50)) { audioPlayer.queueClip(new QueuedMessage(folderLessThanATenthOffThePace, 0, this)); } else if (deltaPlayerLastToSessionBestOverall < TimeSpan.MaxValue) { audioPlayer.queueClip(new QueuedMessage("lapTimeNotRaceGap", MessageContents(folderGapIntro, deltaPlayerLastToSessionBestOverall, folderGapOutroOffPace), 0, this)); } if (practiceAndQualSectorReportsLapEnd && frequencyOfPracticeAndQualSectorDeltaReports > random.NextDouble() * 10) { List<MessageFragment> sectorMessageFragments = getSectorDeltaMessages(SectorReportOption.COMBINED, currentGameState.SessionData.LastSector1Time, lapAndSectorsComparisonData[1], currentGameState.SessionData.LastSector2Time, lapAndSectorsComparisonData[2], currentGameState.SessionData.LastSector3Time, lapAndSectorsComparisonData[3], true); if (sectorMessageFragments.Count > 0) { audioPlayer.queueClip(new QueuedMessage("sectorsHotLap", sectorMessageFragments, 0, this)); sectorsReportedForLap = true; } } } // need to be careful with the rating here as it's based on the known opponent laps, and we may have joined the session part way through else if (currentGameState.SessionData.Position == 1) { Boolean newGapToSecond = false; if (previousGameState != null && previousGameState.SessionData.Position > 1) { newGapToSecond = true; if (currentGameState.SessionData.SessionType == SessionType.Qualify) { audioPlayer.queueClip(new QueuedMessage(Position.folderPole, 0, this)); } else if (currentGameState.SessionData.SessionType == SessionType.Practice) { audioPlayer.queueClip(new QueuedMessage(Position.folderStub + 1, 0, this)); } } else if (deltaPlayerLastToSessionBestOverall < lastGapToSecondWhenLeadingPracOrQual) { newGapToSecond = true; lastGapToSecondWhenLeadingPracOrQual = deltaPlayerLastToSessionBestOverall; } if (newGapToSecond) { lastGapToSecondWhenLeadingPracOrQual = deltaPlayerLastToSessionBestOverall; TimeSpan gapBehind = deltaPlayerLastToSessionBestOverall.Negate(); // only play qual / prac deltas for Raceroom as the PCars data is inaccurate for sessions joined part way through if ((!disablePCarspracAndQualPoleDeltaReports || CrewChief.gameDefinition.gameEnum == GameDefinition.raceRoom.gameEnum) && ((gapBehind.Seconds > 0 || gapBehind.Milliseconds > 50) && gapBehind.Seconds < 60)) { // delay this a bit... audioPlayer.queueClip(new QueuedMessage("lapTimeNotRaceGap", MessageContents(folderGapIntro, gapBehind, folderQuickerThanSecondPlace), random.Next(0, 20), this)); } } } else { if (lastLapRating == LastLapRating.PERSONAL_BEST_STILL_SLOW || lastLapRating == LastLapRating.PERSONAL_BEST_CLOSE_TO_CLASS_LEADER || lastLapRating == LastLapRating.PERSONAL_BEST_CLOSE_TO_OVERALL_LEADER) { audioPlayer.queueClip(new QueuedMessage(folderPersonalBest, 0, this)); } // don't read this message if the rounded time gap is 0.0 seconds or it's more than 59 seconds // only play qual / prac deltas for Raceroom as the PCars data is inaccurate for sessions joined part way through if ((!disablePCarspracAndQualPoleDeltaReports || CrewChief.gameDefinition.gameEnum == GameDefinition.raceRoom.gameEnum) && (deltaPlayerLastToSessionBestInClass.Seconds > 0 || deltaPlayerLastToSessionBestInClass.Milliseconds > 50) && deltaPlayerLastToSessionBestInClass.Seconds < 60) { // delay this a bit... audioPlayer.queueClip(new QueuedMessage("lapTimeNotRaceGap", MessageContents(folderGapIntro, deltaPlayerLastToSessionBestInClass, folderGapOutroOffPace), random.Next(0, 20), this)); } if (practiceAndQualSectorReportsLapEnd && frequencyOfPracticeAndQualSectorDeltaReports > random.NextDouble() * 10) { List<MessageFragment> sectorMessageFragments = getSectorDeltaMessages(SectorReportOption.COMBINED, currentGameState.SessionData.LastSector1Time, lapAndSectorsComparisonData[1], currentGameState.SessionData.LastSector2Time, lapAndSectorsComparisonData[2], currentGameState.SessionData.LastSector3Time, lapAndSectorsComparisonData[3], true); if (sectorMessageFragments.Count > 0) { audioPlayer.queueClip(new QueuedMessage("sectorsQual", sectorMessageFragments, 0, this)); sectorsReportedForLap = true; } } } } else if (currentGameState.SessionData.SessionType == SessionType.Race) { Boolean playedLapMessage = false; if (frequencyOfPlayerRaceLapTimeReports > random.NextDouble() * 10) { float pearlLikelihood = 0.8f; switch (lastLapRating) { case LastLapRating.BEST_OVERALL: playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderBestLapInRace, 0, this), PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); break; case LastLapRating.BEST_IN_CLASS: playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderBestLapInRaceForClass, 0, this), PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); break; case LastLapRating.SETTING_CURRENT_PACE: playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderSettingCurrentRacePace, 0, this), PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); break; case LastLapRating.CLOSE_TO_CURRENT_PACE: // don't keep playing this one if (random.NextDouble() < 0.5) { playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderMatchingCurrentRacePace, 0, this), PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); } break; case LastLapRating.PERSONAL_BEST_CLOSE_TO_OVERALL_LEADER: case LastLapRating.PERSONAL_BEST_CLOSE_TO_CLASS_LEADER: playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderGoodLap, 0, this), PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); break; case LastLapRating.PERSONAL_BEST_STILL_SLOW: playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderPersonalBest, 0, this), PearlsOfWisdom.PearlType.NEUTRAL, pearlLikelihood); break; case LastLapRating.CLOSE_TO_OVERALL_LEADER: case LastLapRating.CLOSE_TO_CLASS_LEADER: // this is an OK lap but not a PB. We only want to say "decent lap" occasionally here if (random.NextDouble() < 0.2) { playedLapMessage = true; audioPlayer.queueClip(new QueuedMessage(folderGoodLap, 0, this), PearlsOfWisdom.PearlType.NEUTRAL, pearlLikelihood); } break; default: break; } } if (raceSectorReportsAtLapEnd && frequencyOfRaceSectorDeltaReports > random.NextDouble() * 10) { double r = random.NextDouble(); SectorReportOption reportOption = SectorReportOption.COMBINED; if (playedLapTime && playedLapMessage) { // if we've already played a laptime and lap rating, use the short sector message. reportOption = SectorReportOption.WORST_ONLY; } else if (r > 0.6 || ((playedLapTime || playedLapMessage) && r > 0.3)) { // if we've played one of these, usually use the abbrieviated version. If we've played neither, sometimes use the abbrieviated version reportOption = SectorReportOption.BEST_AND_WORST; } List<MessageFragment> sectorMessageFragments = getSectorDeltaMessages(reportOption, currentGameState.SessionData.LastSector1Time, lapAndSectorsComparisonData[1], currentGameState.SessionData.LastSector2Time, lapAndSectorsComparisonData[2], currentGameState.SessionData.LastSector3Time, lapAndSectorsComparisonData[3], false); if (sectorMessageFragments.Count > 0) { QueuedMessage message = new QueuedMessage("sectorsRace", sectorMessageFragments, 0, this); message.maxPermittedQueueLengthForMessage = maxQueueLengthForRaceSectorDeltaReports; audioPlayer.queueClip(message); sectorsReportedForLap = true; } } // play the consistency message if we've not played the good lap message, or sometimes // play them both Boolean playConsistencyMessage = !playedLapMessage || random.NextDouble() < 0.25; if (playConsistencyMessage && currentGameState.SessionData.CompletedLaps >= lastConsistencyUpdate + lapTimesWindowSize && lapTimesWindow.Count >= lapTimesWindowSize) { ConsistencyResult consistency = checkAgainstPreviousLaps(); if (consistency == ConsistencyResult.CONSISTENT) { lastConsistencyUpdate = currentGameState.SessionData.CompletedLaps; audioPlayer.queueClip(new QueuedMessage(folderConsistentTimes, random.Next(0, 20), this)); } else if (consistency == ConsistencyResult.IMPROVING) { lastConsistencyUpdate = currentGameState.SessionData.CompletedLaps; audioPlayer.queueClip(new QueuedMessage(folderImprovingTimes, random.Next(0, 20), this)); } else if (consistency == ConsistencyResult.WORSENING) { // don't play the worsening message if the lap rating is good if (lastLapRating == LastLapRating.BEST_IN_CLASS || lastLapRating == LastLapRating.BEST_OVERALL || lastLapRating == LastLapRating.SETTING_CURRENT_PACE || lastLapRating == LastLapRating.CLOSE_TO_CURRENT_PACE) { Console.WriteLine("Skipping 'worsening' laptimes message - inconsistent with lap rating"); } else { lastConsistencyUpdate = currentGameState.SessionData.CompletedLaps; audioPlayer.queueClip(new QueuedMessage(folderWorseningTimes, random.Next(0, 20), this)); } } } } } } lapIsValid = true; } // report sector delta at the completion of a sector? if (!sectorsReportedForLap && currentGameState.SessionData.IsNewSector && ((currentGameState.SessionData.SessionType == SessionType.Race && raceSectorReportsAtEachSector) || (currentGameState.SessionData.SessionType != SessionType.Race && practiceAndQualSectorReportsAtEachSector))) { double r = random.NextDouble() * 10; Boolean canPlayForRace = frequencyOfRaceSectorDeltaReports > r; Boolean canPlayForPracAndQual = frequencyOfPracticeAndQualSectorDeltaReports > r; if ((currentGameState.SessionData.SessionType == SessionType.Race && canPlayForRace) || ((currentGameState.SessionData.SessionType == SessionType.Practice || currentGameState.SessionData.SessionType == SessionType.Qualify || currentGameState.SessionData.SessionType == SessionType.HotLap) && canPlayForPracAndQual)) { float playerSector = -1; float comparisonSector = -1; SectorSet sectorEnum = SectorSet.NONE; switch (currentGameState.SessionData.SectorNumber) { case 1: playerSector = currentGameState.SessionData.LastSector3Time; comparisonSector = lapAndSectorsComparisonData[3]; sectorEnum = SectorSet.THREE; break; case 2: playerSector = currentGameState.SessionData.LastSector1Time; comparisonSector = lapAndSectorsComparisonData[1]; sectorEnum = SectorSet.ONE; break; case 3: playerSector = currentGameState.SessionData.LastSector2Time; comparisonSector = lapAndSectorsComparisonData[2]; sectorEnum = SectorSet.TWO; break; } if (playerSector > 0 && comparisonSector > 0) { String folder = getFolderForSectorCombination(getEnumForSectorDelta(playerSector - comparisonSector, currentGameState.SessionData.SessionType != SessionType.Race), sectorEnum); if (folder != null) { audioPlayer.queueClip(new QueuedMessage(folder, random.Next(2, 4), this)); } } } } } }
private Boolean isOpponentApproachingPitExit(GameStateData currentGameState) { float distanceStartCheckPoint; float distanceEndCheckPoint; // hack for PCars - the distanceRoundTrack will be zero until we enter turn one after leaving the pits. Stupid... if (currentGameState.PositionAndMotionData.DistanceRoundTrack == 0) { distanceStartCheckPoint = 0; distanceEndCheckPoint = maxSeparationForPitExitWarning - minSeparationForPitExitWarning; } else { distanceStartCheckPoint = currentGameState.PositionAndMotionData.DistanceRoundTrack - maxSeparationForPitExitWarning; distanceEndCheckPoint = currentGameState.PositionAndMotionData.DistanceRoundTrack - minSeparationForPitExitWarning; } Boolean startCheckPointIsInSector1 = true; // here we assume the end check point will be in sector 1 (after the s/f line) if (distanceStartCheckPoint < 0) { startCheckPointIsInSector1 = false; distanceStartCheckPoint = currentGameState.SessionData.TrackDefinition.trackLength + distanceStartCheckPoint; } foreach (KeyValuePair<Object, OpponentData> opponent in currentGameState.OpponentData) { if (opponent.Value.OpponentLapData.Count > 0 && !opponent.Value.isEnteringPits() && !opponent.Value.isExitingPits() && ((startCheckPointIsInSector1 && opponent.Value.DistanceRoundTrack > distanceStartCheckPoint && opponent.Value.DistanceRoundTrack < distanceEndCheckPoint) || (!startCheckPointIsInSector1 && (opponent.Value.DistanceRoundTrack > distanceStartCheckPoint || opponent.Value.DistanceRoundTrack < distanceEndCheckPoint)))) { return true; } } return false; }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { if (currentGameState.CarDamageData.DamageEnabled) { aeroDamage = currentGameState.CarDamageData.OverallAeroDamage; trannyDamage = currentGameState.CarDamageData.OverallTransmissionDamage; engineDamage = currentGameState.CarDamageData.OverallEngineDamage; if (enableBrakeDamageMessages) { if (currentGameState.CarDamageData.BrakeDamageStatus.hasValueAtLevel(DamageLevel.DESTROYED)) { maxBrakeDamage = DamageLevel.DESTROYED; } else if (currentGameState.CarDamageData.BrakeDamageStatus.hasValueAtLevel(DamageLevel.MAJOR)) { maxBrakeDamage = DamageLevel.MAJOR; } else if (currentGameState.CarDamageData.BrakeDamageStatus.hasValueAtLevel(DamageLevel.MINOR)) { maxBrakeDamage = DamageLevel.MINOR; } else if (currentGameState.CarDamageData.BrakeDamageStatus.hasValueAtLevel(DamageLevel.TRIVIAL)) { maxBrakeDamage = DamageLevel.TRIVIAL; } } if (enableSuspensionDamageMessages) { if (currentGameState.CarDamageData.SuspensionDamageStatus.hasValueAtLevel(DamageLevel.DESTROYED)) { maxSuspensionDamage = DamageLevel.DESTROYED; } else if (currentGameState.CarDamageData.SuspensionDamageStatus.hasValueAtLevel(DamageLevel.MAJOR)) { maxSuspensionDamage = DamageLevel.MAJOR; } else if (currentGameState.CarDamageData.SuspensionDamageStatus.hasValueAtLevel(DamageLevel.MINOR)) { maxSuspensionDamage = DamageLevel.MINOR; } else if (currentGameState.CarDamageData.SuspensionDamageStatus.hasValueAtLevel(DamageLevel.TRIVIAL)) { maxSuspensionDamage = DamageLevel.TRIVIAL; } isMissingWheel = !currentGameState.PitData.InPitlane && (!currentGameState.TyreData.LeftFrontAttached || !currentGameState.TyreData.RightFrontAttached || !currentGameState.TyreData.LeftRearAttached || !currentGameState.TyreData.RightRearAttached); } if (engineDamage < getLastReportedDamageLevel(Component.ENGINE)) { resetReportedDamage(Component.ENGINE, engineDamage); } if (trannyDamage < getLastReportedDamageLevel(Component.TRANNY)) { resetReportedDamage(Component.TRANNY, trannyDamage); } if (maxSuspensionDamage < getLastReportedDamageLevel(Component.SUSPENSION)) { resetReportedDamage(Component.SUSPENSION, maxSuspensionDamage); } if (maxBrakeDamage < getLastReportedDamageLevel(Component.BRAKES)) { resetReportedDamage(Component.BRAKES, maxBrakeDamage); } if (aeroDamage < getLastReportedDamageLevel(Component.AERO)) { resetReportedDamage(Component.AERO, aeroDamage); } minDamageToReport = (DamageLevel)Math.Max((int)engineDamage, Math.Max((int)trannyDamage, Math.Max((int)maxSuspensionDamage, Math.Max((int)maxBrakeDamage, (int) aeroDamage)))); Tuple<Component, DamageLevel> worstUnreportedDamage = getWorstUnreportedDamage(); if (worstUnreportedDamage != null && worstUnreportedDamage.Item2 >= minDamageToReport) { if (damageToReportNext == null || worstUnreportedDamage.Item1 != damageToReportNext.Item1 || worstUnreportedDamage.Item2 != damageToReportNext.Item2) { timeWhenDamageLastChanged = currentGameState.Now; damageToReportNext = worstUnreportedDamage; } else if (timeWhenDamageLastChanged.Add(timeToWaitForDamageToSettle) < currentGameState.Now) { Console.WriteLine("reporting ..."); Console.WriteLine(damageToReportNext.Item1 + ", " + damageToReportNext.Item2); if (reportedDamagesLevels.ContainsKey(damageToReportNext.Item1)) { reportedDamagesLevels[damageToReportNext.Item1] = damageToReportNext.Item2; } else { reportedDamagesLevels.Add(damageToReportNext.Item1, damageToReportNext.Item2); } if (enableDamageMessages) { playDamageToReport(); } } } } }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { if (currentGameState.SessionData.IsNewLap) { timeLeftFrontIsLockedForLap = 0; timeRightFrontIsLockedForLap = 0; timeLeftRearIsLockedForLap = 0; timeRightRearIsLockedForLap = 0; timeLeftFrontIsSpinningForLap = 0; timeRightFrontIsSpinningForLap = 0; timeLeftRearIsSpinningForLap = 0; timeRightRearIsSpinningForLap = 0; if (warnedOnLockingForLap) { totalLockupThresholdForNextLap = totalLockupThresholdForNextLap + 1; } else { totalLockupThresholdForNextLap = initialTotalLapLockupThreshold; } if (warnedOnWheelspinForLap) { totalWheelspinThresholdForNextLap = totalWheelspinThresholdForNextLap + 1; } else { totalWheelspinThresholdForNextLap = initialTotalWheelspinThreshold; } warnedOnLockingForLap = false; warnedOnWheelspinForLap = false; } if (previousGameState != null && currentGameState.Ticks > previousGameState.Ticks) { addLockingAndSpinningData(currentGameState.TyreData, previousGameState.Ticks, currentGameState.Ticks); } if (currentGameState.Now > nextLockingAndSpinningCheck) { if (enableBrakeLockWarnings) { checkLocking(); } if (enableWheelSpinWarnings) { checkWheelSpinning(); } nextLockingAndSpinningCheck = currentGameState.Now.Add(lockingAndSpinningCheckInterval); } if (currentGameState.TyreData.TireWearActive) { leftFrontWearPercent = currentGameState.TyreData.FrontLeftPercentWear; leftRearWearPercent = currentGameState.TyreData.RearLeftPercentWear; rightFrontWearPercent = currentGameState.TyreData.FrontRightPercentWear; rightRearWearPercent = currentGameState.TyreData.RearRightPercentWear; currentTyreConditionStatus = currentGameState.TyreData.TyreConditionStatus; currentTyreTempStatus = currentGameState.TyreData.TyreTempStatus; if (isBrakeTempPeakForLap(currentGameState.TyreData.LeftFrontBrakeTemp, currentGameState.TyreData.RightFrontBrakeTemp, currentGameState.TyreData.LeftRearBrakeTemp, currentGameState.TyreData.RightRearBrakeTemp)) { currentBrakeTempStatus = currentGameState.TyreData.BrakeTempStatus; } completedLaps = currentGameState.SessionData.CompletedLaps; lapsInSession = currentGameState.SessionData.SessionNumberOfLaps; timeInSession = currentGameState.SessionData.SessionRunTime; timeElapsed = currentGameState.SessionData.SessionRunningTime; if (currentGameState.PitData.InPitlane && !currentGameState.SessionData.LeaderHasFinishedRace) { if (currentGameState.SessionData.SessionType == SessionType.Race && enableTyreWearWarnings && !reportedTyreWearForCurrentPitEntry) { //reportCurrentTyreConditionStatus(false, true); // sounds shit... reportedTyreWearForCurrentPitEntry = true; } } else { reportedTyreWearForCurrentPitEntry = false; } if (currentGameState.SessionData.IsNewLap && !currentGameState.PitData.InPitlane && enableTyreWearWarnings && !currentGameState.SessionData.LeaderHasFinishedRace) { reportCurrentTyreConditionStatus(false, false); } if (!currentGameState.PitData.InPitlane && !reportedEstimatedTimeLeft && enableTyreWearWarnings && !currentGameState.SessionData.LeaderHasFinishedRace) { reportEstimatedTyreLife(33, false); } // if the tyre wear has actually decreased, reset the 'reportdEstimatedTyreWear flag - assume this means the tyres have been changed if (previousGameState != null && (currentGameState.TyreData.FrontLeftPercentWear < previousGameState.TyreData.FrontLeftPercentWear || currentGameState.TyreData.FrontRightPercentWear < previousGameState.TyreData.FrontRightPercentWear || currentGameState.TyreData.RearRightPercentWear < previousGameState.TyreData.RearRightPercentWear || currentGameState.TyreData.RearLeftPercentWear < previousGameState.TyreData.RearLeftPercentWear)) { reportedEstimatedTimeLeft = true; } if (enableTyreTempWarnings && !currentGameState.SessionData.LeaderHasFinishedRace && !currentGameState.PitData.InPitlane && currentGameState.SessionData.CompletedLaps >= lapsIntoSessionBeforeTempMessage && currentGameState.SessionData.IsNewLap) { reportCurrentTyreTempStatus(false); } if (!currentGameState.SessionData.LeaderHasFinishedRace && ((checkBrakesAtSector == 1 && currentGameState.SessionData.IsNewLap) || ((currentGameState.SessionData.IsNewSector && currentGameState.SessionData.SectorNumber == checkBrakesAtSector)))) { if (!currentGameState.PitData.InPitlane && currentGameState.SessionData.CompletedLaps >= lapsIntoSessionBeforeTempMessage) { if (enableBrakeTempWarnings) { reportCurrentBrakeTempStatus(false); } } peakBrakeTempForLap = 0; } } }
public override bool isMessageStillValid(String eventSubType, GameStateData currentGameState, Dictionary<String, Object> validationData) { return applicableSessionPhases.Contains(currentGameState.SessionData.SessionPhase); }
private Boolean checkGaps(GameStateData currentGameState, int numLapsLeft, Boolean checkPushToGain, Boolean checkPushToHold) { if (checkPushToGain && currentGameState.SessionData.Position > 1) { float opponentInFrontBestLap = getOpponentBestLap(currentGameState.SessionData.Position - 1, lapsToCountBackForOpponentBest, currentGameState); if (opponentInFrontBestLap > 0 && (opponentInFrontBestLap - currentGameState.SessionData.PlayerLapTimeSessionBest) * numLapsLeft > currentGameState.SessionData.TimeDeltaFront) { // going flat out, we're going to catch the guy ahead us before the end if (currentGameState.SessionData.Position == 2) { audioPlayer.queueClip(new QueuedMessage(folderPushToGetWin, 0, this)); } else if (currentGameState.SessionData.Position == 3) { audioPlayer.queueClip(new QueuedMessage(folderPushToGetSecond, 0, this)); } else if (currentGameState.SessionData.Position == 4) { audioPlayer.queueClip(new QueuedMessage(folderPushToGetThird, 0, this)); } else { audioPlayer.queueClip(new QueuedMessage(folderPushToImprove, 0, this)); } return true; } } if (checkPushToHold && !currentGameState.isLast()) { float opponentBehindBestLap = getOpponentBestLap(currentGameState.SessionData.Position + 1, lapsToCountBackForOpponentBest, currentGameState); if (opponentBehindBestLap > 0 && (currentGameState.SessionData.PlayerLapTimeSessionBest - opponentBehindBestLap) * numLapsLeft > currentGameState.SessionData.TimeDeltaBehind) { // even with us going flat out, the guy behind is going to catch us before the end Console.WriteLine("might lose this position. Player best lap = " + currentGameState.SessionData.PlayerLapTimeSessionBest + " laps left = " + numLapsLeft + " opponent best lap = " + opponentBehindBestLap + " time delta = " + currentGameState.SessionData.TimeDeltaBehind); audioPlayer.queueClip(new QueuedMessage(folderPushToHoldPosition, 0, this)); return true; } } return false; }
public Boolean Run(String filenameToRun, int interval, Boolean dumpToFile) { loadDataFromFile = false; audioPlayer.mute = false; if (filenameToRun != null && System.Diagnostics.Debugger.IsAttached) { loadDataFromFile = true; spotterEnabled = false; if (interval > 0) { _timeInterval = TimeSpan.FromMilliseconds(interval); audioPlayer.mute = false; } else { _timeInterval = TimeSpan.Zero; audioPlayer.mute = true; } dumpToFile = false; } gameStateMapper = GameStateReaderFactory.getInstance().getGameStateMapper(gameDefinition); gameStateMapper.setSpeechRecogniser(speechRecogniser); gameDataReader = GameStateReaderFactory.getInstance().getGameStateReader(gameDefinition); gameDataReader.dumpToFile = System.Diagnostics.Debugger.IsAttached && dumpToFile; if (gameDefinition.spotterName != null) { spotter = (Spotter)Activator.CreateInstance(Type.GetType(gameDefinition.spotterName), audioPlayer, spotterEnabled); } else { Console.WriteLine("No spotter defined for game " + gameDefinition.friendlyName); spotter = null; } running = true; DateTime nextRunTime = DateTime.Now; if (!audioPlayer.initialised) { Console.WriteLine("Failed to initialise audio player"); return false; } audioPlayer.startMonitor(); Boolean attemptedToRunGame = false; int threadSleepTime = ((int)_timeInterval.Milliseconds / 10) + 1; Console.WriteLine("Polling for shared data every " + _timeInterval.Milliseconds + "ms, pausing " + threadSleepTime + "ms between invocations"); Boolean sessionFinished = false; while (running) { DateTime now = DateTime.Now; if (now > nextRunTime) { nextRunTime = DateTime.Now.Add(_timeInterval); if (!loadDataFromFile) { if (gameDefinition.processName == null || Utilities.IsGameRunning(gameDefinition.processName)) { if (!mapped) { mapped = gameDataReader.Initialise(); } } else if (UserSettings.GetUserSettings().getBoolean(gameDefinition.gameStartEnabledProperty) && !attemptedToRunGame) { Utilities.runGame(UserSettings.GetUserSettings().getString(gameDefinition.gameStartCommandProperty), UserSettings.GetUserSettings().getString(gameDefinition.gameStartCommandOptionsProperty)); attemptedToRunGame = true; } } if (loadDataFromFile || mapped) { stateCleared = false; Object rawGameData; if (loadDataFromFile) { rawGameData = gameDataReader.ReadGameDataFromFile(filenameToRun); if (rawGameData == null) { Console.WriteLine("Reached the end of the data file, sleeping to clear queued messages"); Thread.Sleep(5000); audioPlayer.purgeQueues(); running = false; continue; } } else { rawGameData = gameDataReader.ReadGameData(false); } gameStateMapper.versionCheck(rawGameData); GameStateData nextGameState = null; try { nextGameState = gameStateMapper.mapToGameStateData(rawGameData, currentGameState); } catch (Exception e) { Console.WriteLine("Error mapping game data: " + e.StackTrace); } if (nextGameState != null) { previousGameState = currentGameState; currentGameState = nextGameState; if (!sessionFinished && currentGameState.SessionData.SessionPhase == SessionPhase.Finished && previousGameState != null) { audioPlayer.purgeQueues(); if (displaySessionLapTimes) { Console.WriteLine("Session lap times:"); Console.WriteLine(String.Join(";", currentGameState.SessionData.formattedPlayerLapTimes)); } sessionEndMessages.trigger(previousGameState.SessionData.SessionRunningTime, previousGameState.SessionData.SessionType, currentGameState.SessionData.SessionPhase, previousGameState.SessionData.Position, previousGameState.SessionData.NumCarsAtStartOfSession, previousGameState.SessionData.CompletedLaps, previousGameState.SessionData.IsDisqualified); audioPlayer.closeChannel(); sessionFinished = true; audioPlayer.disablePearlsOfWisdom = false; if (loadDataFromFile) { Thread.Sleep(2000); } } float prevTime = previousGameState == null ? 0 : previousGameState.SessionData.SessionRunningTime; if (currentGameState.SessionData.IsNewSession) { audioPlayer.disablePearlsOfWisdom = false; displayNewSessionInfo(currentGameState); sessionFinished = false; if (!stateCleared) { Console.WriteLine("Clearing game state..."); audioPlayer.purgeQueues(); audioPlayer.closeChannel(); foreach (KeyValuePair<String, AbstractEvent> entry in eventsList) { entry.Value.clearState(); } faultingEvents.Clear(); faultingEventsCount.Clear(); stateCleared = true; } if (enableDriverNames) { List<String> rawDriverNames = currentGameState.getRawDriverNames(); if (currentGameState.SessionData.DriverRawName != null && currentGameState.SessionData.DriverRawName.Length > 0 && !rawDriverNames.Contains(currentGameState.SessionData.DriverRawName)) { rawDriverNames.Add(currentGameState.SessionData.DriverRawName); } if (rawDriverNames.Count > 0) { List<String> usableDriverNames = DriverNameHelper.getUsableDriverNames(rawDriverNames, audioPlayer.soundFilesPath); if (speechRecogniser != null && speechRecogniser.initialised) { speechRecogniser.addOpponentSpeechRecognition(usableDriverNames, enableDriverNames); } audioPlayer.cacheDriverNames(usableDriverNames); } } } else if (!sessionFinished && previousGameState != null && (currentGameState.SessionData.SessionRunningTime > previousGameState.SessionData.SessionRunningTime || (previousGameState != null && previousGameState.SessionData.SessionPhase != currentGameState.SessionData.SessionPhase))) { if (spotter != null) { spotter.unpause(); } if (currentGameState.SessionData.IsNewLap) { currentGameState.display(); } stateCleared = false; foreach (KeyValuePair<String, AbstractEvent> entry in eventsList) { if (entry.Value.isApplicableForCurrentSessionAndPhase(currentGameState.SessionData.SessionType, currentGameState.SessionData.SessionPhase)) { triggerEvent(entry.Key, entry.Value, previousGameState, currentGameState); } } if (spotter != null && spotterEnabled && !spotterIsRunning && !loadDataFromFile) { Console.WriteLine("********** starting spotter***********"); spotter.clearState(); startSpotterThread(); } else if (spotterIsRunning && !spotterEnabled) { runSpotterThread = false; } } else if (spotter != null) { spotter.pause(); } } } } else { Thread.Sleep(threadSleepTime); continue; } } foreach (KeyValuePair<String, AbstractEvent> entry in eventsList) { entry.Value.clearState(); } if (spotter != null) { spotter.clearState(); } stateCleared = true; currentGameState = null; previousGameState = null; sessionFinished = false; audioPlayer.stopMonitor(); audioPlayer.disablePearlsOfWisdom = false; if (gameDataReader != null && gameDataReader.dumpToFile) { gameDataReader.DumpRawGameData(); } gameDataReader.stop(); return true; }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { // DRS: if (drsMessagesEnabled && currentGameState.OvertakingAids.DrsEnabled) { if (trackDistanceToCheckDRSGapFrontAt == -1 && currentGameState.SessionData.TrackDefinition != null) { trackDistanceToCheckDRSGapFrontAt = currentGameState.SessionData.TrackDefinition.trackLength / 2; } if (currentGameState.SessionData.IsNewLap) { if (drsAvailableOnThisLap && !hasUsedDrsOnThisLap) { audioPlayer.queueClip(new QueuedMessage("missed_available_drs", MessageContents(folderDontForgetDRS), 0, this)); } drsAvailableOnThisLap = currentGameState.OvertakingAids.DrsAvailable; hasUsedDrsOnThisLap = false; playedGetCloserForDRSOnThisLap = false; playedOpponentHasDRSOnThisLap = false; } if (currentGameState.OvertakingAids.DrsEngaged) { hasUsedDrsOnThisLap = true; } if (!hasUsedDrsOnThisLap && !drsAvailableOnThisLap && !playedGetCloserForDRSOnThisLap && currentGameState.PositionAndMotionData.DistanceRoundTrack > trackDistanceToCheckDRSGapFrontAt) { if (currentGameState.SessionData.TimeDeltaFront < 1.3 + currentGameState.OvertakingAids.DrsRange && currentGameState.SessionData.TimeDeltaFront >= 0.6 + currentGameState.OvertakingAids.DrsRange) { audioPlayer.queueClip(new QueuedMessage("drs_a_second_out_of_range", MessageContents(folderASecondOffDRSRange), 0, this)); playedGetCloserForDRSOnThisLap = true; } else if (currentGameState.SessionData.TimeDeltaFront < 0.6 + currentGameState.OvertakingAids.DrsRange && currentGameState.SessionData.TimeDeltaFront >= 0.1 + currentGameState.OvertakingAids.DrsRange) { audioPlayer.queueClip(new QueuedMessage("drs_a_few_tenths_out_of_range", MessageContents(folderAFewTenthsOffDRSRange), 0, this)); playedGetCloserForDRSOnThisLap = true; } } if (!playedOpponentHasDRSOnThisLap && currentGameState.SessionData.TimeDeltaBehind <= currentGameState.OvertakingAids.DrsRange && currentGameState.SessionData.LapTimeCurrent > currentGameState.SessionData.TimeDeltaBehind && currentGameState.SessionData.LapTimeCurrent < currentGameState.SessionData.TimeDeltaBehind + 1) { audioPlayer.queueClip(new QueuedMessage("opponent_has_drs", MessageContents(folderGuyBehindHasDRS), 0, this)); playedOpponentHasDRSOnThisLap = true; } } // push to pass if (ptpMessagesEnabled && previousGameState != null) { if (previousGameState.OvertakingAids.PushToPassEngaged && !currentGameState.OvertakingAids.PushToPassEngaged && currentGameState.OvertakingAids.PushToPassActivationsRemaining == 0) { audioPlayer.queueClip(new QueuedMessage("no_push_to_pass_remaining", MessageContents(folderNoActivationsRemaining), 0, this)); pushToPassActivationsRemaining = 0; } else if (previousGameState.OvertakingAids.PushToPassWaitTimeLeft > 0 && currentGameState.OvertakingAids.PushToPassWaitTimeLeft == 0) { if (currentGameState.OvertakingAids.PushToPassActivationsRemaining == 1) { audioPlayer.queueClip(new QueuedMessage("one_push_to_pass_remaining", MessageContents( folderPushToPassNowAvailable, folderOneActivationRemaining), 0, this)); pushToPassActivationsRemaining = 1; } else { audioPlayer.queueClip(new QueuedMessage("one_push_to_pass_remaining", MessageContents(folderPushToPassNowAvailable, QueuedMessage.folderNameNumbersStub + currentGameState.OvertakingAids.PushToPassActivationsRemaining, folderActivationsRemaining), 0, this)); pushToPassActivationsRemaining = currentGameState.OvertakingAids.PushToPassActivationsRemaining; } } } }
private void triggerEvent(String eventName, AbstractEvent abstractEvent, GameStateData previousGameState, GameStateData currentGameState) { try { abstractEvent.trigger(previousGameState, currentGameState); } catch (Exception e) { if (faultingEventsCount.ContainsKey(eventName)) { faultingEventsCount[eventName]++; if (faultingEventsCount[eventName] > 5) { Console.WriteLine("Event " + eventName + " has failed > 5 times in this session"); } } if (!faultingEvents.ContainsKey(eventName)) { Console.WriteLine("Event " + eventName + " threw exception " + e.Message); Console.WriteLine("This is the first time this event has failed in this session"); faultingEvents.Add(eventName, e.Message); faultingEventsCount.Add(eventName, 1); } else if (faultingEvents[eventName] != e.Message) { Console.WriteLine("Event " + eventName + " threw a different exception: " + e.Message); faultingEvents[eventName] = e.Message; } } }
protected override void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { if (currentGameState.SessionData.SessionType == SessionType.Race && previousGameState != null && currentGameState.PenaltiesData.HasDriveThrough || currentGameState.PenaltiesData.HasStopAndGo || currentGameState.PenaltiesData.HasTimeDeduction) { if (currentGameState.PenaltiesData.HasDriveThrough && !previousGameState.PenaltiesData.HasDriveThrough) { lapsCompleted = currentGameState.SessionData.CompletedLaps; // this is a new penalty audioPlayer.queueClip(new QueuedMessage(folderNewPenaltyDriveThrough, 0, this)); // queue a '3 laps to serve penalty' message - this might not get played audioPlayer.queueClip(new QueuedMessage(folderThreeLapsToServe, 20, this)); // we don't already have a penalty if (penaltyLap == -1 || !hasOutstandingPenalty) { penaltyLap = currentGameState.SessionData.CompletedLaps; } hasOutstandingPenalty = true; hasHadAPenalty = true; } else if (currentGameState.PenaltiesData.HasStopAndGo && !previousGameState.PenaltiesData.HasStopAndGo) { lapsCompleted = currentGameState.SessionData.CompletedLaps; // this is a new penalty audioPlayer.queueClip(new QueuedMessage(folderNewPenaltyStopGo, 0, this)); // queue a '3 laps to serve penalty' message - this might not get played audioPlayer.queueClip(new QueuedMessage(folderThreeLapsToServe, 20, this)); // we don't already have a penalty if (penaltyLap == -1 || !hasOutstandingPenalty) { penaltyLap = currentGameState.SessionData.CompletedLaps; } hasOutstandingPenalty = true; hasHadAPenalty = true; } else if (currentGameState.PitData.InPitlane && currentGameState.PitData.OnOutLap && !playedNotServedPenalty && (currentGameState.PenaltiesData.HasStopAndGo || currentGameState.PenaltiesData.HasDriveThrough)) { // we've exited the pits but there's still an outstanding penalty audioPlayer.queueClip(new QueuedMessage(folderPenaltyNotServed, 3, this)); playedNotServedPenalty = true; } else if (currentGameState.SessionData.IsNewLap && (currentGameState.PenaltiesData.HasStopAndGo || currentGameState.PenaltiesData.HasDriveThrough)) { // TODO: variable number of laps to serve penalty... lapsCompleted = currentGameState.SessionData.CompletedLaps; if (lapsCompleted - penaltyLap == 3 && !currentGameState.PitData.InPitlane) { // run out of laps, an not in the pitlane audioPlayer.queueClip(new QueuedMessage(folderDisqualified, 5, this)); } else if (lapsCompleted - penaltyLap == 2 && currentGameState.PenaltiesData.HasDriveThrough) { audioPlayer.queueClip(new QueuedMessage(folderOneLapToServeDriveThrough, pitstopDelay, this)); } else if (lapsCompleted - penaltyLap == 2 && currentGameState.PenaltiesData.HasStopAndGo) { audioPlayer.queueClip(new QueuedMessage(folderOneLapToServeStopGo, pitstopDelay, this)); } else if (lapsCompleted - penaltyLap == 1) { audioPlayer.queueClip(new QueuedMessage(folderTwoLapsToServe, pitstopDelay, this)); } } else if (!playedPitNow && currentGameState.SessionData.SectorNumber == 3 && currentGameState.PenaltiesData.HasStopAndGo && lapsCompleted - penaltyLap == 2) { playedPitNow = true; audioPlayer.queueClip(new QueuedMessage(folderPitNowStopGo, 6, this)); } else if (!playedPitNow && currentGameState.SessionData.SectorNumber == 3 && currentGameState.PenaltiesData.HasDriveThrough && lapsCompleted - penaltyLap == 2) { playedPitNow = true; audioPlayer.queueClip(new QueuedMessage(folderPitNowDriveThrough, 6, this)); } else if (!playedTimePenaltyMessage && currentGameState.PenaltiesData.HasTimeDeduction) { playedTimePenaltyMessage = true; audioPlayer.queueClip(new QueuedMessage(folderTimePenalty, 0, this)); } } else if (currentGameState.PositionAndMotionData.CarSpeed > 1 && playCutTrackWarnings && currentGameState.SessionData.SessionType != SessionType.Race && !currentGameState.SessionData.CurrentLapIsValid && previousGameState != null && previousGameState.SessionData.CurrentLapIsValid) { cutTrackWarningsCount = currentGameState.PenaltiesData.CutTrackWarnings; // don't warn about cut track if the AI is driving if (currentGameState.ControlData.ControlType != ControlType.AI && lastCutTrackWarningTime.Add(cutTrackWarningFrequency) < currentGameState.Now) { lastCutTrackWarningTime = currentGameState.Now; audioPlayer.queueClip(new QueuedMessage(folderLapDeleted, 2, this)); clearPenaltyState(); } } else if (currentGameState.PositionAndMotionData.CarSpeed > 1 && playCutTrackWarnings && currentGameState.PenaltiesData.CutTrackWarnings > cutTrackWarningsCount) { cutTrackWarningsCount = currentGameState.PenaltiesData.CutTrackWarnings; if (currentGameState.ControlData.ControlType != ControlType.AI && lastCutTrackWarningTime.Add(cutTrackWarningFrequency) < currentGameState.Now) { lastCutTrackWarningTime = currentGameState.Now; if (currentGameState.SessionData.SessionType == SessionType.Race) { audioPlayer.queueClip(new QueuedMessage(folderCutTrackInRace, 2, this)); } else { audioPlayer.queueClip(new QueuedMessage(folderCutTrackPracticeOrQual, 2, this)); } clearPenaltyState(); } } else { clearPenaltyState(); } if (currentGameState.SessionData.SessionType == SessionType.Race && previousGameState != null && ((previousGameState.PenaltiesData.HasStopAndGo && !currentGameState.PenaltiesData.HasStopAndGo) || (previousGameState.PenaltiesData.HasDriveThrough && !currentGameState.PenaltiesData.HasDriveThrough))) { audioPlayer.queueClip(new QueuedMessage(folderPenaltyServed, 0, null)); } }