using System.Collections.Generic; using System.Drawing; using System.Linq; using Rage; using RAGENativeUI; using RAGENativeUI.Elements; namespace SceneManager { static class PathMainMenu { private static List paths = new List() { }; public static UIMenu pathMainMenu { get; private set; } public static UIMenuItem createNewPath { get; private set; } public static UIMenuItem deleteAllPaths; public static UIMenuNumericScrollerItem editPath = new UIMenuNumericScrollerItem("Edit Path", "", 1, paths.Count, 1); public static UIMenuListScrollerItem directOptions { get; private set; } public static UIMenuNumericScrollerItem directDriver = new UIMenuNumericScrollerItem("Direct nearest driver to path", "", 1, paths.Count, 1); public static UIMenuListScrollerItem dismissDriver { get; private set; } public static UIMenuCheckboxItem disableAllPaths { get; private set; } //private static List pathsNum = new List(); private static List dismissOptions = new List() { "From path", "From waypoint", "From position" }; public enum Delete { Single, All } internal static void InstantiateMenu() { pathMainMenu = new UIMenu("Scene Menu", "~o~Path Manager Main Menu"); pathMainMenu.ParentMenu = MainMenu.mainMenu; MenuManager.menuPool.Add(pathMainMenu); } public static void BuildPathMenu() { // New stuff to mitigate Rebuild method pathMainMenu.OnItemSelect -= PathMenu_OnItemSelected; pathMainMenu.OnCheckboxChange -= PathMenu_OnCheckboxChange; MenuManager.menuPool.CloseAllMenus(); pathMainMenu.Clear(); pathMainMenu.AddItem(createNewPath = new UIMenuItem("Create New Path")); createNewPath.ForeColor = Color.Gold; pathMainMenu.AddItem(editPath = new UIMenuNumericScrollerItem("Edit Path", "", 1, paths.Count, 1)); //pathMainMenu.AddItem(editPath = new UIMenuListScrollerItem("Edit Path", "", pathsNum)); editPath.ForeColor = Color.Gold; pathMainMenu.AddItem(disableAllPaths = new UIMenuCheckboxItem("Disable All Paths", false)); pathMainMenu.AddItem(deleteAllPaths = new UIMenuItem("Delete All Paths")); deleteAllPaths.ForeColor = Color.Gold; pathMainMenu.AddItem(directOptions = new UIMenuListScrollerItem("Direct driver to path's", "", new[] { "First waypoint", "Nearest waypoint" })); pathMainMenu.AddItem(directDriver = new UIMenuNumericScrollerItem("Direct nearest driver to path", "", 1, paths.Count, 1)); //pathMainMenu.AddItem(directDriver = new UIMenuListScrollerItem("Direct nearest driver to path", "", pathsNum)); directDriver.ForeColor = Color.Gold; pathMainMenu.AddItem(dismissDriver = new UIMenuListScrollerItem("Dismiss nearest driver", "", dismissOptions)); dismissDriver.ForeColor = Color.Gold; if (paths.Count == 8) { createNewPath.Enabled = false; } if (paths.Count == 0) { editPath.Enabled = false; deleteAllPaths.Enabled = false; disableAllPaths.Enabled = false; directDriver.Enabled = false; } pathMainMenu.RefreshIndex(); pathMainMenu.OnItemSelect += PathMenu_OnItemSelected; pathMainMenu.OnCheckboxChange += PathMenu_OnCheckboxChange; // New stuff to mitigate Rebuild method MenuManager.menuPool.RefreshIndex(); } public static ref List GetPaths() { return ref paths; } private static bool VehicleAndDriverValid(this Vehicle v) { if (v && v.HasDriver && v.Driver && v.Driver.IsAlive) { return true; } else { return false; } } private static bool IsInCollectedVehicles(this Vehicle vehicle) { if (vehicle && VehicleCollector.collectedVehicles.Any(cv => cv.Vehicle == vehicle)) { Game.LogTrivial($"{vehicle.Model.Name} was found in the collection."); return true; } else { Game.LogTrivial($"{vehicle.Model.Name} was not found in the collection."); return false; } } public static void DeletePath(Path path, int index, Delete pathsToDelete) { // Before deleting a path, we need to dismiss any vehicles controlled by that path and remove the vehicles from ControlledVehicles //Game.LogTrivial($"Deleting path {index+1}"); Game.LogTrivial($"Deleting path {path.Number}"); var pathVehicles = VehicleCollector.collectedVehicles.Where(cv => cv.Path == path.Number).ToList(); Game.LogTrivial($"Removing all vehicles on the path"); foreach (CollectedVehicle cv in pathVehicles.Where(cv => cv.Vehicle && cv.Vehicle.Driver)) { cv.SetDismissNow(true); cv.Vehicle.Driver.Tasks.Clear(); cv.Vehicle.Driver.Dismiss(); cv.Vehicle.Driver.IsPersistent = false; cv.Vehicle.Dismiss(); cv.Vehicle.IsPersistent = false; //Game.LogTrivial($"{cv.vehicle.Model.Name} cleared from path {cv.path}"); VehicleCollector.collectedVehicles.Remove(cv); } // Remove the speed zone so cars don't continue to be affected after the path is deleted Game.LogTrivial($"Removing yield zone and waypoint blips"); foreach (Waypoint waypoint in path.Waypoints) { if (waypoint.SpeedZone != 0) { waypoint.RemoveSpeedZone(); } if (waypoint.Blip) { waypoint.Blip.Delete(); } if (waypoint.CollectorRadiusBlip) { waypoint.CollectorRadiusBlip.Delete(); } } Game.LogTrivial($"Clearing path.WaypointData"); path.Waypoints.Clear(); // Manipulating the menu to reflect specific paths being deleted if (pathsToDelete == Delete.Single) { paths.RemoveAt(index); BuildPathMenu(); pathMainMenu.Visible = true; Game.LogTrivial($"Path {path.Number} deleted."); Game.DisplayNotification($"~o~Scene Manager\n~w~Path {path.Number} deleted."); } EditPathMenu.editPathMenu.Reset(true, true); EditPathMenu.disablePath.Enabled = true; } private static void PathMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index) { if (selectedItem == createNewPath) { pathMainMenu.Visible = false; PathCreationMenu.pathCreationMenu.Visible = true; DebugGraphics.Draw3DWaypointOnPlayer(); // For each element in paths, determine if the element exists but is not finished yet, or if it doesn't exist, create it. for (int i = 0; i <= paths.Count; i++) { if (paths.ElementAtOrDefault(i) != null && paths[i].State == State.Creating) { //Game.LogTrivial($"pathFinished: {paths[i].PathFinished}"); Game.LogTrivial($"Resuming path {paths[i].Number}"); Game.DisplayNotification($"~o~Scene Manager\n~y~[Creating]~w~ Resuming path {paths[i].Number}"); break; } } } if (selectedItem == editPath) { pathMainMenu.Visible = false; EditPathMenu.editPathMenu.Visible = true; } if (selectedItem == deleteAllPaths) { // Iterate through each item in paths and delete it for (int i = 0; i < paths.Count; i++) { DeletePath(paths[i], i, Delete.All); } foreach (Path path in paths) { foreach(Waypoint waypoint in path.Waypoints.Where(wp => wp.SpeedZone != 0)) { waypoint.RemoveSpeedZone(); } path.Waypoints.Clear(); } paths.Clear(); BuildPathMenu(); pathMainMenu.Visible = true; Game.LogTrivial($"All paths deleted"); Game.DisplayNotification($"~o~Scene Manager\n~w~All paths deleted."); } // This needs big refactor if (selectedItem == directDriver) { var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(1).Where(v => v.VehicleAndDriverValid()).SingleOrDefault(); var firstWaypoint = paths[directDriver.Index].Waypoints.First(); var pathNum = paths[directDriver.Index].Waypoints[0].Path; var totalPathWaypoints = paths[directDriver.Index].Waypoints.Count; if (nearbyVehicle) { var nearestWaypoint = paths[directDriver.Index].Waypoints.Where(wp => wp.Position.DistanceTo2D(nearbyVehicle.FrontPosition) < wp.Position.DistanceTo2D(nearbyVehicle.RearPosition)).OrderBy(wp => wp.Position.DistanceTo2D(nearbyVehicle)).ToArray(); VehicleCollector.SetVehicleAndDriverPersistence(nearbyVehicle); if (nearbyVehicle.IsInCollectedVehicles()) { Game.LogTrivial($"[Direct Driver] {nearbyVehicle.Model.Name} already in collection. Clearing tasks."); nearbyVehicle.Driver.Tasks.Clear(); //VehicleCollector.collectedVehicles[nearbyVehicle].AssignPropertiesFromDirectedTask(pathNum, totalPathWaypoints, 1, tasksAssigned: false, dismiss: true, stoppedAtWaypoint: false); if (directOptions.SelectedItem == "First waypoint") { GameFiber.StartNew(() => { nearbyVehicle.Driver.Tasks.DriveToPosition(firstWaypoint.Position, firstWaypoint.Speed, (VehicleDrivingFlags)263043, 2f).WaitForCompletion(); }); } else { GameFiber.StartNew(() => { nearbyVehicle.Driver.Tasks.DriveToPosition(nearestWaypoint[0].Position, nearestWaypoint[0].Speed, (VehicleDrivingFlags)263043, 2f).WaitForCompletion(); }); } } else { VehicleCollector.collectedVehicles.Add(new CollectedVehicle(nearbyVehicle, nearbyVehicle.LicensePlate, paths[directDriver.Index].Waypoints[0].Path, paths[directDriver.Index].Waypoints.Count, 1, false, false)); var collectedVehicle = VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle) as CollectedVehicle; Game.LogTrivial($"[Direct Driver] {nearbyVehicle.Model.Name} not in collection, adding to collection for path {paths[directDriver.Index].Number} with {paths[directDriver.Index].Waypoints.Count} waypoints"); if (directOptions.SelectedItem == "First waypoint") { GameFiber.StartNew(() => { nearbyVehicle.Driver.Tasks.DriveToPosition(firstWaypoint.Position, firstWaypoint.Speed, (VehicleDrivingFlags)263043, 2f).WaitForCompletion(); for (int nextWaypoint = firstWaypoint.Number; nextWaypoint < paths[directDriver.Index].Waypoints.Count; nextWaypoint++) { if (paths[directDriver.Index].Waypoints.ElementAtOrDefault(nextWaypoint) == null) { Game.LogTrivial($"Waypoint is null"); break; } Game.LogTrivial($"{nearbyVehicle.Model.Name} is driving to waypoint {paths[directDriver.Index].Waypoints[nextWaypoint].Number}"); if (paths[directDriver.Index].Waypoints.ElementAtOrDefault(nextWaypoint) != null && !collectedVehicle.StoppedAtWaypoint) { nearbyVehicle.Driver.Tasks.DriveToPosition(paths[directDriver.Index].Waypoints[nextWaypoint].Position, paths[directDriver.Index].Waypoints[nextWaypoint].Speed, (VehicleDrivingFlags)263043, 2f).WaitForCompletion(); } if (paths[directDriver.Index].Waypoints.ElementAtOrDefault(nextWaypoint) != null && paths[directDriver.Index].Waypoints[nextWaypoint].DrivingFlag == VehicleDrivingFlags.StopAtDestination) { Game.LogTrivial($"{nearbyVehicle.Model.Name} stopping at waypoint."); nearbyVehicle.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.GoForwardStraightBraking); collectedVehicle.SetStoppedAtWaypoint(true); } } Game.LogTrivial($"{nearbyVehicle.Model.Name} all tasks complete."); AITasking.DismissDriver(collectedVehicle); }); } else { GameFiber.StartNew(() => { nearbyVehicle.Driver.Tasks.DriveToPosition(nearestWaypoint[0].Position, nearestWaypoint[0].Speed, (VehicleDrivingFlags)263043, 2f).WaitForCompletion(); for (int nextWaypoint = nearestWaypoint[0].Number; nextWaypoint < paths[directDriver.Index].Waypoints.Count; nextWaypoint++) { if (paths[directDriver.Index].Waypoints.ElementAtOrDefault(nextWaypoint) == null) { Game.LogTrivial($"Waypoint is null"); break; } Game.LogTrivial($"{nearbyVehicle.Model.Name} is driving to waypoint {paths[directDriver.Index].Waypoints[nextWaypoint].Number}"); if (paths[directDriver.Index].Waypoints.ElementAtOrDefault(nextWaypoint) != null && !collectedVehicle.StoppedAtWaypoint) { nearbyVehicle.Driver.Tasks.DriveToPosition(paths[directDriver.Index].Waypoints[nextWaypoint].Position, paths[directDriver.Index].Waypoints[nextWaypoint].Speed, (VehicleDrivingFlags)263043, 2f).WaitForCompletion(); } if (paths[directDriver.Index].Waypoints.ElementAtOrDefault(nextWaypoint) != null && paths[directDriver.Index].Waypoints[nextWaypoint].DrivingFlag == VehicleDrivingFlags.StopAtDestination) { Game.LogTrivial($"{nearbyVehicle.Model.Name} stopping at waypoint."); nearbyVehicle.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.GoForwardStraightBraking); collectedVehicle.SetStoppedAtWaypoint(true); } } Game.LogTrivial($"{nearbyVehicle.Model.Name} all tasks complete."); AITasking.DismissDriver(collectedVehicle); }); } } } } if (selectedItem == dismissDriver) { var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(1).Where(v => v != Game.LocalPlayer.Character.CurrentVehicle && v.VehicleAndDriverValid()).SingleOrDefault(); if (nearbyVehicle) { switch (dismissDriver.Index) { case 0: Game.LogTrivial($"Dismiss from path"); if (nearbyVehicle.IsInCollectedVehicles()) { var collectedVehicle = VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).First(); if (collectedVehicle != null) { collectedVehicle.SetDismissNow(true); collectedVehicle.SetStoppedAtWaypoint(false); collectedVehicle.Vehicle.Driver.Tasks.Clear(); collectedVehicle.Vehicle.Driver.Dismiss(); collectedVehicle.Vehicle.IsSirenOn = false; Game.LogTrivial($"Dismissed driver of {collectedVehicle.Vehicle.Model.Name} from path {collectedVehicle.Path}"); } } else { goto case 2; } break; case 1: Game.LogTrivial($"Dismiss from waypoint"); if (nearbyVehicle.IsInCollectedVehicles()) { var collectedVehicle = VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).First(); if (collectedVehicle != null) { collectedVehicle.SetStoppedAtWaypoint(false); collectedVehicle.Vehicle.Driver.Tasks.Clear(); collectedVehicle.Vehicle.Driver.Dismiss(); collectedVehicle.Vehicle.IsSirenOn = false; if (collectedVehicle.CurrentWaypoint == collectedVehicle.TotalWaypoints && !collectedVehicle.StoppedAtWaypoint) { collectedVehicle.SetDismissNow(true); Game.LogTrivial($"Dismissed driver of {collectedVehicle.Vehicle.Model.Name} from final waypoint and ultimately the path"); } else { Game.LogTrivial($"Dismissed driver of {collectedVehicle.Vehicle.Model.Name} from waypoint {collectedVehicle.CurrentWaypoint}"); } } } else { goto case 2; } break; case 2: Game.LogTrivial($"Dismiss from position"); if (nearbyVehicle.IsInCollectedVehicles()) { nearbyVehicle.Driver.Tasks.Clear(); nearbyVehicle.Driver.Dismiss(); nearbyVehicle.IsSirenOn = false; Game.LogTrivial($"Dismissed driver of {nearbyVehicle.Model.Name} (in collection)"); } else { nearbyVehicle.Driver.Tasks.Clear(); nearbyVehicle.Driver.Dismiss(); nearbyVehicle.IsSirenOn = false; Game.LogTrivial($"Dismissed driver of {nearbyVehicle.Model.Name} (was not in collection)"); } break; default: Game.LogTrivial($"dismissDriver index was unexpected"); break; } } else { Game.LogTrivial($"There are no vehicles nearby matching the requirements."); } } } private static void PathMenu_OnCheckboxChange(UIMenu sender, UIMenuCheckboxItem checkboxItem, bool @checked) { if (checkboxItem == disableAllPaths) { if (disableAllPaths.Checked) { foreach (Path path in paths) { path.DisablePath(); } Game.LogTrivial($"All paths disabled."); } else { foreach (Path path in paths) { path.EnablePath(); } Game.LogTrivial($"All paths enabled."); } } } } }