/// <summary> /// Setup race mode by disabling traffic, clearing weather, and teleporting player to the 1st SectorCheckpoint. /// </summary> public void enterRaceMode() { // set the 2nd SectorCheckpoint as active (there must be at least 2 SectorCheckpoints to start race mode); draw the checkpoint activateRaceCheckpoint(1); // set weather to extra sunny; save current weather so it can be restored after exiting race mode weather = World.Weather; World.Weather = Weather.ExtraSunny; // teleport player to the starting checkpoint; set player orientation SectorCheckpoint start = markedSectorCheckpoints[0]; Game.Player.Character.CurrentVehicle.Position = start.position; Game.Player.Character.CurrentVehicle.Quaternion = start.quarternion; // freeze time Game.Player.CanControlCharacter = false; GTA.UI.Screen.ShowSubtitle("~y~Lap Timer: Ready..."); Script.Wait(freezeTime); Game.Player.CanControlCharacter = true; GTA.UI.Screen.ShowSubtitle("~g~Lap Timer: Go!"); // start the clock by getting the current GameTime lapStartTime = Game.GameTime; }
/// <summary> /// Redraw the blips and checkpoints of all saved SectorCheckpoints. Should only be used in placement mode. /// </summary> private void redrawAllSectorCheckpoints() { for (int i = 0; i < markedSectorCheckpoints.Count; i++) { // copy the instance of SectorCheckpoint and replace marker with a new instance returned by placeMarker SectorCheckpoint newCheckpoint = markedSectorCheckpoints[i]; newCheckpoint.marker = newCheckpoint.placeMarker(MarkerType.placement, markedSectorCheckpoints[i].number); markedSectorCheckpoints[i] = newCheckpoint; // assign new instance of SectorCheckpoint to the original index in the List } }
/// <summary> /// Mark player's current position, and create a blip & checkpoint. /// </summary> /// <returns>Instance of </returns> public SectorCheckpoint createSectorCheckpoint(bool verbose = true) { // instantiate empty SectorCheckpoint int checkpointNum = markedSectorCheckpoints.Count; SectorCheckpoint newCheckpoint = new SectorCheckpoint(checkpointNum); markedSectorCheckpoints.Add(newCheckpoint); return(newCheckpoint); }
/// <summary> /// Activate the provided SectorCheckpoint after deactivating the current active checkpoint. /// By activating, a marker will be placed at the checkpoint, and timer will run until player is in range of the checkpoint. /// If the index is out of bounds (>= no. of checkpoints), either end the race or reset the lap. /// </summary> /// <param name="idx">List index of SectorCheckpoint to activate in <c>markedSectorCheckpoints</c></param> /// <returns>The now-active SectorCheckpoint</returns> private SectorCheckpoint activateRaceCheckpoint(int idx) { // deactivate current active checkpoint's marker try { markedSectorCheckpoints[activeSector].hideMarker(); } catch { } // detect if index is out of expected range if (idx >= markedSectorCheckpoints.Count && lapRace) { //// if point-to-point race, then race is completed. Print time and exit race mode. //if (!lapRace) //{ // lapFinishedHandler(activeCheckpoint); // return activeCheckpoint; //} //// if lapped race, activate the 0th checkpoint //else //{ // idx = 0; //} idx = 0; } // set the new SectorCheckpoint as active (by index) activeSector = idx; activeCheckpoint = markedSectorCheckpoints[idx]; // determine if this is the final checkpoint based on the index bool isFinal = activeCheckpoint.GetHashCode() == finishCheckpoint.GetHashCode(); //idx == markedSectorCheckpoints.Count - 1 || idx == 0; // the marker placed should be different, depending on whether this checkpoint is final if (isFinal) { activeCheckpoint.marker = activeCheckpoint.placeMarker(MarkerType.raceFinish, idx); } // if not final checkpoint, place a checkpoint w/ an arrow pointing to the next checkpoint else { Vector3 nextChkptPosition = getNextCheckpoint(idx).position; activeCheckpoint.marker = activeCheckpoint.placeMarker(MarkerType.raceArrow, idx, nextChkptPosition); } return(activeCheckpoint); }
/// <summary> /// Import a race from a file on disk. The currently placed checkpoints will be overwritten. /// </summary> public void importRace(string path = null) { // clean up any existing race/checkpoints if (raceMode) { exitRaceMode(); } clearAllSectorCheckpoints(); // set placement mode active; make sure player is not in race mode (exit if need to) placementMode = true; // prompt user to enter the name of the file (with or without the file extension) to import from string name = path == null?GTA.Game.GetUserInput("custom_race") : path; try { // attempt to import from file ExportableRace race = RaceExporter.deserializeFromJson(name, path == null ? false : true); // repopulate List<SectorCheckpoint> using the imported race data lapRace = race.lapMode; for (int i = 0; i < race.checkpoints.Length; i++) { SimplifiedCheckpoint sc = race.checkpoints[i]; SectorCheckpoint chkpt = new SectorCheckpoint(sc.number, sc.position, sc.quarternion, false); markedSectorCheckpoints.Add(chkpt); } // inform user of successful load GTA.UI.Notification.Show("Lap Timer: successfully imported race!"); // with the race loaded & reconstructed, try to load timing sheet. make sure all hash codes match! int raceHash = GetHashCode(); ExportableTimingSheet timingSheet = TimingSheetExporter.deserializeFromJson(raceHash.ToString()); for (int i = 0; i < timingSheet.timingData.Length; i++) { markedSectorCheckpoints[i].setTimingDataFromSimplified(timingSheet.timingData[i]); } GTA.UI.Notification.Show("Lap Timer: successfully imported personal timing sheet for the imported race!"); } catch { } }
/// <summary> /// /// </summary> /// <param name="finalChkpt"><c>SectorCheckpoint</c> to extract timing summary from</param> /// <param name="lapRaceMode">if <c>true</c>, invoke exitRaceMode()</param> private void lapFinishedHandler(SectorCheckpoint finalChkpt, bool lapRaceMode = false) { // display on screen a summary of the race results GTA.UI.Screen.ShowSubtitle("Lap completed. ~n~" + finalChkpt.timing.getLatestTimingSummaryString(), 10000); // export timing sheet exportTimingSheet(); // exit race mode if point-to-point (i.e. non-lapped) race if (!lapRaceMode) { exitRaceMode(); } // otherwise, if lapped race, reset the timer else { lapStartTime = Game.GameTime; } }
/// <summary> /// Delete the last <c>SectorCheckpoint</c>. First delete its <c>Marker</c>, then remove the checkpoint from <c>markedSectorCheckpoints</c>. /// </summary> public void deleteLastSectorCheckpoint(bool verbose = true) { // if markedSectorCheckpoints is empty, do nothing if (markedSectorCheckpoints.Count <= 0) { return; } // get the last checkpoint in markedSectorCheckpoints SectorCheckpoint chkpt = markedSectorCheckpoints.Last(); int checkpointNum = chkpt.number; // delete its Marker (Blip + Checkpoint) from the World, if they are defined chkpt.hideMarker(); // remove the checkpoint from the list markedSectorCheckpoints.RemoveAt(markedSectorCheckpoints.Count - 1); // print output if verbose if (verbose) { GTA.UI.Screen.ShowSubtitle("Lap Timer: deleted checkpoint #" + checkpointNum); } }
private void loadLapTimeMenu(UIMenu sender) { // clear the menu sender.Clear(); // validate the race; if race is invalid if (!race.isValid) { return; } // get the last checkpoint in list of checkpoints SectorCheckpoint finalChkpt = race.finishCheckpoint; // iterate over each k-v in the final checkpoint's timing data var times = finalChkpt.timing.vehicleFastestTime.OrderBy(x => x.Value); foreach (KeyValuePair <string, int> entry in times) { sender.AddItem(new UIMenuItem(TimingData.msToReadable(entry.Value, false, true) + " - " + entry.Key)); } sender.RefreshIndex(); }