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)); } } } } } }
public override void clearState() { lapTimesWindow = new List<float>(lapTimesWindowSize); lastConsistencyUpdate = 0; lastConsistencyMessage = ConsistencyResult.NOT_APPLICABLE; lapIsValid = true; lastLapRating = LastLapRating.NO_DATA; sessionBestLapTimeDeltaToLeader = TimeSpan.MaxValue; currentLapTimeDeltaToLeadersBest = TimeSpan.MaxValue; lastLapTime = 0; isInSlowerClass = false; currentPosition = -1; }
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; }
protected override void triggerInternal(Data.Shared lastState, Data.Shared currentState) { if (currentState.LapTimeBest > 0) { sessionBestLapTimeDeltaToLeader = TimeSpan.FromSeconds(currentState.LapTimeBest - getLapTimeBestForClassLeader(currentState)); } else { sessionBestLapTimeDeltaToLeader = TimeSpan.MaxValue; } if (currentState.LapTimePrevious > 0) { currentLapTimeDeltaToLeadersBest = TimeSpan.FromSeconds(currentState.LapTimePrevious - getLapTimeBestForClassLeader(currentState)); } else { // the last lap was invalid so the delta is undefined currentLapTimeDeltaToLeadersBest = TimeSpan.MaxValue; } currentPosition = currentState.Position; // in race sessions (race only) the LapTimePrevious isn't set to -1 if that lap was invalid, so // we need to record that it's invalid while we're actually on the lap if (CommonData.isSessionRunning && lapIsValid && currentState.CompletedLaps > 0 && !CommonData.isNewLap && currentState.LapTimeCurrent == -1) { lapIsValid = false; } if (CommonData.isSessionRunning && CommonData.isNewLap) { lastLapTime = currentState.LapTimePrevious; } if (CommonData.isSessionRunning && CommonData.isNewLap && !CommonData.isInLap && !CommonData.isOutLap && ((CommonData.isHotLapping && currentState.CompletedLaps > 0) || currentState.CompletedLaps > 1)) { if (lapTimesWindow == null) { lapTimesWindow = new List<float>(lapTimesWindowSize); } // this might be NO_DATA lastLapRating = getLastLapRating(currentState); if (currentState.LapTimePrevious > 0) { lapTimesWindow.Insert(0, currentState.LapTimePrevious); if (lapIsValid) { // queue the actual laptime as a 'gap filler' - this is only played if the // queue would otherwise be empty if (enableLapTimeMessages && readLapTimes && !CommonData.isHotLapping) { QueuedMessage gapFillerLapTime = new QueuedMessage(folderLapTimeIntro, null, TimeSpan.FromSeconds(currentState.LapTimePrevious), 0, this); gapFillerLapTime.gapFiller = true; audioPlayer.queueClip(QueuedMessage.compoundMessageIdentifier + "laptime", gapFillerLapTime); } if (enableLapTimeMessages && currentState.SessionType == (int)Constant.Session.Qualify || currentState.SessionType == (int)Constant.Session.Practice) { if (CommonData.isHotLapping) { // special case for hot lapping - read best lap message and the laptime audioPlayer.queueClip(QueuedMessage.compoundMessageIdentifier + "laptime", new QueuedMessage(folderLapTimeIntro, null, TimeSpan.FromSeconds(currentState.LapTimePrevious), 0, this)); if (lastLapRating == LastLapRating.BEST_IN_CLASS || currentLapTimeDeltaToLeadersBest <= TimeSpan.Zero) { audioPlayer.queueClip(folderPersonalBest, 0, this); } else if (currentLapTimeDeltaToLeadersBest < TimeSpan.FromMilliseconds(50)) { audioPlayer.queueClip(folderLessThanATenthOffThePace, 0, this); } else if (currentLapTimeDeltaToLeadersBest < TimeSpan.MaxValue) { audioPlayer.queueClip(QueuedMessage.compoundMessageIdentifier + "_lapTimeNotRaceGap", new QueuedMessage(folderGapIntro, folderGapOutroOffPace, currentLapTimeDeltaToLeadersBest, 0, this)); } } else if (lastLapRating == LastLapRating.BEST_IN_CLASS) { audioPlayer.queueClip(folderFastestInClass, 0, this); if (sessionBestLapTimeDeltaToLeader < TimeSpan.Zero) { TimeSpan gapBehind = sessionBestLapTimeDeltaToLeader.Negate(); if ((gapBehind.Seconds > 0 || gapBehind.Milliseconds > 50) && gapBehind.Seconds < 60) { // delay this a bit... audioPlayer.queueClip(QueuedMessage.compoundMessageIdentifier + "_lapTimeNotRaceGap", new QueuedMessage(folderGapIntro, folderQuickerThanSecondPlace, gapBehind, random.Next(0, 20), this)); } } } else if (lastLapRating == LastLapRating.BEST_OVERALL) { if (currentState.SessionType == (int)Constant.Session.Qualify) { audioPlayer.queueClip(Position.folderPole, 0, this); } else if (currentState.SessionType == (int)Constant.Session.Practice) { audioPlayer.queueClip(Position.folderStub + currentState.Position, 0, this); } if (sessionBestLapTimeDeltaToLeader < TimeSpan.Zero) { TimeSpan gapBehind = sessionBestLapTimeDeltaToLeader.Negate(); if ((gapBehind.Seconds > 0 || gapBehind.Milliseconds > 50) && gapBehind.Seconds < 60) { // delay this a bit... audioPlayer.queueClip(QueuedMessage.compoundMessageIdentifier + "_lapTimeNotRaceGap", new QueuedMessage(folderGapIntro, folderQuickerThanSecondPlace, gapBehind, 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(folderPersonalBest, 0, this); } if (getLapTimeBestForClassLeader(currentState) > 0) { // don't read this message if the rounded time gap is 0.0 seconds or it's more than 59 seconds if ((sessionBestLapTimeDeltaToLeader.Seconds > 0 || sessionBestLapTimeDeltaToLeader.Milliseconds > 50) && sessionBestLapTimeDeltaToLeader.Seconds < 60) { // delay this a bit... audioPlayer.queueClip(QueuedMessage.compoundMessageIdentifier + "_lapTimeNotRaceGap", new QueuedMessage(folderGapIntro, folderGapOutroOffPace, sessionBestLapTimeDeltaToLeader, random.Next(0, 20), this)); } } } } else if (enableLapTimeMessages && CommonData.isRaceRunning) { Boolean playedLapMessage = false; float pearlLikelihood = 0.8f; switch (lastLapRating) { case LastLapRating.BEST_OVERALL: playedLapMessage = true; audioPlayer.queueClip(folderBestLapInRace, 0, this, PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); break; case LastLapRating.BEST_IN_CLASS: playedLapMessage = true; audioPlayer.queueClip(folderBestLapInRaceForClass, 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(folderGoodLap, 0, this, PearlsOfWisdom.PearlType.GOOD, pearlLikelihood); break; case LastLapRating.PERSONAL_BEST_STILL_SLOW: playedLapMessage = true; audioPlayer.queueClip(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.8) { playedLapMessage = true; audioPlayer.queueClip(folderGoodLap, 0, this, PearlsOfWisdom.PearlType.NEUTRAL, pearlLikelihood); } break; default: break; } // 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 && currentState.CompletedLaps >= lastConsistencyUpdate + lapTimesWindowSize && lapTimesWindow.Count >= lapTimesWindowSize) { ConsistencyResult consistency = checkAgainstPreviousLaps(); if (consistency == ConsistencyResult.CONSISTENT) { lastConsistencyUpdate = currentState.CompletedLaps; audioPlayer.queueClip(folderConsistentTimes, 0, this); } else if (consistency == ConsistencyResult.IMPROVING) { lastConsistencyUpdate = currentState.CompletedLaps; audioPlayer.queueClip(folderImprovingTimes, 0, this); } if (consistency == ConsistencyResult.WORSENING) { lastConsistencyUpdate = currentState.CompletedLaps; audioPlayer.queueClip(folderWorseningTimes, 0, this); } } } } } lapIsValid = true; } }