public override void clearState() { this.formationStandingStartAnnounced = false; this.formationStandingPreStartReminderAnnounced = false; this.numUpdatesActionSame = 0; this.newFrozenOrderAction = FrozenOrderAction.None; this.newDriverToFollow = null; this.newFrozenOrderColumn = FrozenOrderColumn.None; this.currFrozenOrderAction = FrozenOrderAction.None; this.currDriverToFollow = null; this.currFrozenOrderColumn = FrozenOrderColumn.None; this.scrLastFCYLapLaneAnnounced = false; }
override protected void triggerInternal(GameStateData previousGameState, GameStateData currentGameState) { var cgs = currentGameState; var pgs = previousGameState; if (cgs.PitData.InPitlane || /*|| cgs.SessionData.SessionRunningTime < 10 */ GameStateData.onManualFormationLap || // We may want manual formation to phase of FrozenOrder. pgs == null) { return; // don't process if we're in the pits or just started a session } var cfod = cgs.FrozenOrderData; var pfod = pgs.FrozenOrderData; var cfodp = cgs.FrozenOrderData.Phase; if (cfodp == FrozenOrderPhase.None) { return; // Nothing to do. } var useAmericanTerms = GlobalBehaviourSettings.useAmericanTerms || GlobalBehaviourSettings.useOvalLogic; var useOvalLogic = GlobalBehaviourSettings.useOvalLogic; if (pfod.Phase == FrozenOrderPhase.None) { Console.WriteLine("Frozen Order: New Phase detected: " + cfod.Phase); int delay = Utilities.random.Next(0, 3); if (cfod.Phase == FrozenOrderPhase.Rolling) { audioPlayer.playMessage(new QueuedMessage(folderRollingStartReminder, delay + 4, secondsDelay: delay, abstractEvent: this, priority: 10)); } else if (cfod.Phase == FrozenOrderPhase.FormationStanding) { audioPlayer.playMessage(new QueuedMessage(folderStandingStartReminder, delay + 4, secondsDelay: delay, abstractEvent: this, priority: 10)); } // Clear previous state. this.clearState(); } // Because FO Action is distance dependent, it tends to fluctuate. // We need to detect when it stabilizes (values stay identical for ACTION_STABLE_THRESHOLD times). if (cfod.Action == pfod.Action && cfod.DriverToFollowRaw == pfod.DriverToFollowRaw && cfod.AssignedColumn == pfod.AssignedColumn) { ++this.numUpdatesActionSame; } else { this.newFrozenOrderAction = cfod.Action; this.newDriverToFollow = cfod.DriverToFollowRaw; this.newFrozenOrderColumn = cfod.AssignedColumn; this.numUpdatesActionSame = 0; } var isActionUpdateStable = this.numUpdatesActionSame >= FrozenOrderMonitor.ACTION_STABLE_THRESHOLD; // Detect if we should be following SC, as SC has no driver name. var shouldFollowSafetyCar = false; var driverToFollow = ""; if (cfodp == FrozenOrderPhase.Rolling || cfodp == FrozenOrderPhase.FullCourseYellow) { shouldFollowSafetyCar = (cfod.AssignedColumn == FrozenOrderColumn.None && cfod.AssignedPosition == 1) || // Single file order. (cfod.AssignedColumn != FrozenOrderColumn.None && cfod.AssignedPosition <= 2); // Double file (grid) order. driverToFollow = shouldFollowSafetyCar ? (useAmericanTerms ? folderThePaceCar : folderTheSafetyCar) : cfod.DriverToFollowRaw; } if (cfodp == FrozenOrderPhase.Rolling) { var prevDriverToFollow = this.currDriverToFollow; var prevFrozenOrderAction = this.currFrozenOrderAction; if (isActionUpdateStable && (this.currFrozenOrderAction != this.newFrozenOrderAction || this.currDriverToFollow != this.newDriverToFollow || this.currFrozenOrderColumn != this.newFrozenOrderColumn)) { this.currFrozenOrderAction = this.newFrozenOrderAction; this.currDriverToFollow = this.newDriverToFollow; this.currFrozenOrderColumn = this.newFrozenOrderColumn; // canReadDriverToFollow will be true if we're behind the safety car or we can read the driver's name: var canReadDriverToFollow = shouldFollowSafetyCar || AudioPlayer.canReadName(driverToFollow); var usableDriverNameToFollow = shouldFollowSafetyCar ? driverToFollow : DriverNameHelper.getUsableDriverName(driverToFollow); var validationData = new Dictionary <string, object>(); validationData.Add(FrozenOrderMonitor.validateMessageTypeKey, FrozenOrderMonitor.validateMessageTypeAction); validationData.Add(FrozenOrderMonitor.validationActionKey, cfod.Action); validationData.Add(FrozenOrderMonitor.validationAssignedPositionKey, cfod.AssignedPosition); validationData.Add(FrozenOrderMonitor.validationDriverToFollowKey, cfod.DriverToFollowRaw); if (this.newFrozenOrderAction == FrozenOrderAction.Follow && prevDriverToFollow != this.currDriverToFollow) // Don't announce Follow messages for the driver that we caught up to or allowed to pass. { if (canReadDriverToFollow) { // Follow messages are only meaningful if there's name to announce. int delay = Utilities.random.Next(3, 6); if (cfod.AssignedColumn == FrozenOrderColumn.None || Utilities.random.Next(1, 11) > 8) // Randomly, announce message without coulmn info. { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/follow_driver" : "frozen_order/follow_safety_car", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderFollow, usableDriverNameToFollow), abstractEvent: this, validationData: validationData, priority: 10)); } else { string columnName; if (useOvalLogic) { columnName = cfod.AssignedColumn == FrozenOrderColumn.Left ? folderInTheInsideColumn : folderInTheOutsideColumn; } else { columnName = cfod.AssignedColumn == FrozenOrderColumn.Left ? folderInTheLeftColumn : folderInTheRightColumn; } audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/follow_driver_in_col" : "frozen_order/follow_safecy_car_in_col", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderFollow, usableDriverNameToFollow, columnName), abstractEvent: this, validationData: validationData, priority: 10)); } } } else if (this.newFrozenOrderAction == FrozenOrderAction.AllowToPass) { // Follow messages are only meaningful if there's name to announce. int delay = Utilities.random.Next(1, 4); if (canReadDriverToFollow && Utilities.random.Next(1, 11) > 2) // Randomly, announce message without name. { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/allow_driver_to_pass" : "frozen_order/allow_safety_car_to_pass", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderAllow, usableDriverNameToFollow, folderToPass), abstractEvent: this, validationData: validationData, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage(folderYoureAheadOfAGuyYouShouldBeFollowing, delay + 6, secondsDelay: delay, abstractEvent: this, validationData: validationData, priority: 10)); } } else if (this.newFrozenOrderAction == FrozenOrderAction.CatchUp) { int delay = Utilities.random.Next(1, 4); if (canReadDriverToFollow && Utilities.random.Next(1, 11) > 2) // Randomly, announce message without name. { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/catch_up_to_driver" : "frozen_order/catch_up_to_safety_car", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderCatchUpTo, usableDriverNameToFollow), abstractEvent: this, validationData: validationData, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage(folderYouNeedToCatchUpToTheGuyAhead, delay + 6, secondsDelay: delay, abstractEvent: this, validationData: validationData, priority: 10)); } } else if (this.newFrozenOrderAction == FrozenOrderAction.StayInPole && prevFrozenOrderAction != FrozenOrderAction.MoveToPole) // No point in nagging user to stay in pole if we previously told them to move there. { int delay = Utilities.random.Next(0, 3); if (cfod.AssignedColumn == FrozenOrderColumn.None || Utilities.random.Next(1, 11) > 8) // Randomly, announce message without coulmn info. { audioPlayer.playMessage(new QueuedMessage("frozen_order/stay_in_pole", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderStayInPole), abstractEvent: this, validationData: validationData, priority: 10)); } else { string folderToPlay = null; if (useOvalLogic) { folderToPlay = cfod.AssignedColumn == FrozenOrderColumn.Left ? folderStayInPoleInInsideColumn : folderStayInPoleInOutsideColumn; } else { folderToPlay = cfod.AssignedColumn == FrozenOrderColumn.Left ? folderStayInPoleInLeftColumn : folderStayInPoleInRightColumn; } audioPlayer.playMessage(new QueuedMessage("frozen_order/stay_in_pole_in_column", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderToPlay), abstractEvent: this, validationData: validationData, priority: 10)); } } else if (this.newFrozenOrderAction == FrozenOrderAction.MoveToPole) { int delay = Utilities.random.Next(2, 5); if (cfod.AssignedColumn == FrozenOrderColumn.None) { audioPlayer.playMessage(new QueuedMessage("frozen_order/move_to_pole", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderMoveToPole), abstractEvent: this, validationData: validationData, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage("frozen_order/move_to_pole_row", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderMoveToPoleRow), abstractEvent: this, validationData: validationData, priority: 10)); } } } } else if (cfodp == FrozenOrderPhase.FullCourseYellow) { var prevDriverToFollow = this.currDriverToFollow; var prevFrozenOrderColumn = this.currFrozenOrderColumn; var announceSCRLastFCYLapLane = useAmericanTerms && currentGameState.StockCarRulesData.stockCarRulesEnabled && (currentGameState.FlagData.fcyPhase == FullCourseYellowPhase.LAST_LAP_NEXT || currentGameState.FlagData.fcyPhase == FullCourseYellowPhase.LAST_LAP_CURRENT); if (isActionUpdateStable && (this.currFrozenOrderAction != this.newFrozenOrderAction || this.currDriverToFollow != this.newDriverToFollow || this.currFrozenOrderColumn != this.newFrozenOrderColumn || announceSCRLastFCYLapLane && !this.scrLastFCYLapLaneAnnounced)) { this.currFrozenOrderAction = this.newFrozenOrderAction; this.currDriverToFollow = this.newDriverToFollow; this.currFrozenOrderColumn = this.newFrozenOrderColumn; this.scrLastFCYLapLaneAnnounced = announceSCRLastFCYLapLane; // canReadDriverToFollow will be true if we're behind the safety car or we can read the driver's name: var canReadDriverToFollow = shouldFollowSafetyCar || AudioPlayer.canReadName(driverToFollow); var usableDriverNameToFollow = shouldFollowSafetyCar ? driverToFollow : DriverNameHelper.getUsableDriverName(driverToFollow); var validationData = new Dictionary <string, object>(); validationData.Add(FrozenOrderMonitor.validateMessageTypeKey, FrozenOrderMonitor.validateMessageTypeAction); validationData.Add(FrozenOrderMonitor.validationActionKey, cfod.Action); validationData.Add(FrozenOrderMonitor.validationAssignedPositionKey, cfod.AssignedPosition); validationData.Add(FrozenOrderMonitor.validationDriverToFollowKey, cfod.DriverToFollowRaw); if (this.newFrozenOrderAction == FrozenOrderAction.Follow && ((prevDriverToFollow != this.currDriverToFollow) || // Don't announce Follow messages for the driver that we caught up to or allowed to pass. (prevFrozenOrderColumn != this.currFrozenOrderColumn && announceSCRLastFCYLapLane))) // But announce for SCR last FCY lap. { int delay = Utilities.random.Next(0, 3); if (canReadDriverToFollow) { if (announceSCRLastFCYLapLane && cfod.AssignedColumn == FrozenOrderColumn.Left) { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/follow_driver_in_left" : "frozen_order/follow_safety_car_in_left", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderFollow, usableDriverNameToFollow, useOvalLogic ? folderInTheInsideColumn : folderInTheLeftColumn), abstractEvent: this, validationData: validationData, priority: 10)); } else if (announceSCRLastFCYLapLane && cfod.AssignedColumn == FrozenOrderColumn.Right) { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/follow_driver_in_right" : "frozen_order/follow_safety_car_in_right", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderFollow, usableDriverNameToFollow, useOvalLogic ? folderInTheOutsideColumn : folderInTheRightColumn), abstractEvent: this, validationData: validationData, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/follow_driver" : "frozen_order/follow_safety_car", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderFollow, usableDriverNameToFollow), abstractEvent: this, validationData: validationData, priority: 10)); } } else if (announceSCRLastFCYLapLane && cfod.AssignedColumn == FrozenOrderColumn.Left) { audioPlayer.playMessage(new QueuedMessage(useOvalLogic ? folderLineUpInInsideColumn : folderLineUpInLeftColumn, delay + 6, secondsDelay: delay, abstractEvent: this, validationData: validationData, priority: 10)); } else if (announceSCRLastFCYLapLane && cfod.AssignedColumn == FrozenOrderColumn.Right) { audioPlayer.playMessage(new QueuedMessage(useOvalLogic ? folderLineUpInOutsideColumn : folderLineUpInRightColumn, delay + 6, secondsDelay: delay, abstractEvent: this, validationData: validationData, priority: 10)); } } else if (this.newFrozenOrderAction == FrozenOrderAction.AllowToPass) { int delay = Utilities.random.Next(1, 4); if (canReadDriverToFollow && Utilities.random.Next(1, 11) > 2) // Randomly, announce message without name. { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/allow_driver_to_pass" : "frozen_order/allow_safety_car_to_pass", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderAllow, usableDriverNameToFollow, folderToPass), abstractEvent: this, validationData: validationData, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage(folderYoureAheadOfAGuyYouShouldBeFollowing, delay + 6, secondsDelay: delay, abstractEvent: this, validationData: validationData, priority: 10)); } } else if (this.newFrozenOrderAction == FrozenOrderAction.CatchUp) { int delay = Utilities.random.Next(1, 4); if (canReadDriverToFollow && Utilities.random.Next(1, 11) > 2) // Randomly, announce message without name. { audioPlayer.playMessage(new QueuedMessage(!shouldFollowSafetyCar ? "frozen_order/catch_up_to_driver" : "frozen_order/catch_up_to_safety_car", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderCatchUpTo, usableDriverNameToFollow), abstractEvent: this, validationData: validationData, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage(folderYouNeedToCatchUpToTheGuyAhead, delay + 6, secondsDelay: delay, abstractEvent: this, validationData: validationData, priority: 10)); } } } } else if (cfodp == FrozenOrderPhase.FormationStanding) { string columnName = null; if (cfod.AssignedColumn != FrozenOrderColumn.None) { if (useOvalLogic) { columnName = cfod.AssignedColumn == FrozenOrderColumn.Left ? folderInTheInsideColumn : folderInTheOutsideColumn; } else { columnName = cfod.AssignedColumn == FrozenOrderColumn.Left ? folderInTheLeftColumn : folderInTheRightColumn; } } if (!this.formationStandingStartAnnounced && cgs.SessionData.SessionRunningTime > 10) { this.formationStandingStartAnnounced = true; var isStartingFromPole = cfod.AssignedPosition == 1; int delay = Utilities.random.Next(0, 3); if (isStartingFromPole) { if (columnName == null) { audioPlayer.playMessage(new QueuedMessage(folderWereStartingFromPole, 6, abstractEvent: this)); } else { audioPlayer.playMessage(new QueuedMessage("frozen_order/youre_starting_from_pole_in_column", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderWereStartingFromPole, columnName), abstractEvent: this, priority: 10)); } } else { if (columnName == null) { audioPlayer.playMessage(new QueuedMessage("frozen_order/youre_starting_from_pos", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderWeStartingFromPosition, cfod.AssignedPosition), abstractEvent: this, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage("frozen_order/youre_starting_from_pos_row_in_column", delay + 6, secondsDelay: delay, messageFragments: MessageContents(folderWeStartingFromPosition, cfod.AssignedPosition, folderRow, cfod.AssignedGridPosition, columnName), abstractEvent: this, priority: 10)); } } } if (!this.formationStandingPreStartReminderAnnounced && cgs.SessionData.SectorNumber == 3 && cgs.PositionAndMotionData.DistanceRoundTrack > (cgs.SessionData.TrackDefinition.trackLength - FrozenOrderMonitor.DIST_TO_START_TO_ANNOUNCE_POS_REMINDER)) { this.formationStandingPreStartReminderAnnounced = true; var isStartingFromPole = cfod.AssignedPosition == 1; int delay = Utilities.random.Next(0, 3); if (isStartingFromPole) { if (columnName == null) { audioPlayer.playMessage(new QueuedMessage("frozen_order/get_ready_starting_from_pole", delay + 6, secondsDelay: delay, messageFragments: MessageContents(LapCounter.folderGetReady, folderWereStartingFromPole), abstractEvent: this, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage("frozen_order/get_ready_starting_from_pole_in_column", delay + 6, secondsDelay: delay, messageFragments: MessageContents(LapCounter.folderGetReady, folderWereStartingFromPole, columnName), abstractEvent: this, priority: 10)); } } else { if (columnName == null) { audioPlayer.playMessage(new QueuedMessage("frozen_order/get_ready_youre_starting_from_pos", delay + 6, secondsDelay: delay, messageFragments: MessageContents(LapCounter.folderGetReady, folderWeStartingFromPosition, cfod.AssignedPosition), abstractEvent: this, priority: 10)); } else { audioPlayer.playMessage(new QueuedMessage("frozen_order/get_ready_youre_starting_from_pos_row_in_column", delay + 6, secondsDelay: delay, messageFragments: MessageContents(LapCounter.folderGetReady, folderWeStartingFromPosition, cfod.AssignedPosition, folderRow, cfod.AssignedGridPosition, columnName), abstractEvent: this, priority: 10)); } } } } // Announce SC speed. if (pfod.SafetyCarSpeed == -1.0f && cfod.SafetyCarSpeed != -1.0f) { // TODO: may also need to announce basic "SC in" message. var kmPerHour = cfod.SafetyCarSpeed * 3.6f; var messageFragments = new List <MessageFragment>(); if (useAmericanTerms) { messageFragments.Add(MessageFragment.Text(FrozenOrderMonitor.folderPaceCarSpeedIs)); var milesPerHour = kmPerHour * 0.621371f; messageFragments.Add(MessageFragment.Integer((int)Math.Round(milesPerHour), false)); messageFragments.Add(MessageFragment.Text(FrozenOrderMonitor.folderMilesPerHour)); } else { messageFragments.Add(MessageFragment.Text(FrozenOrderMonitor.folderSafetyCarSpeedIs)); messageFragments.Add(MessageFragment.Integer((int)Math.Round(kmPerHour), false)); messageFragments.Add(MessageFragment.Text(FrozenOrderMonitor.folderKilometresPerHour)); } int delay = Utilities.random.Next(10, 16); audioPlayer.playMessage(new QueuedMessage("frozen_order/pace_car_speed", delay + 6, secondsDelay: delay, messageFragments: messageFragments, abstractEvent: this, priority: 10)); } // Announce SC left. if (pfod.SafetyCarSpeed != -1.0f && cfod.SafetyCarSpeed == -1.0f) { if (useAmericanTerms) { audioPlayer.playMessage(new QueuedMessage(folderPaceCarJustLeft, 10, abstractEvent: this)); } else { audioPlayer.playMessage(new QueuedMessage(folderSafetyCarJustLeft, 10, abstractEvent: this)); } } // For fast rolling, do nothing for now. }