diff --git a/README.md b/README.md index 0d726d0..2a7d381 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,59 @@ -# Scene Manager - -## INSTALLATION -Drag and drop the contents from within the downloaded GTA V folder into where your GTA V is installed. -![How to drag and drop](https://i.imgur.com/nnxqgtn.jpg) - -## HOW TO USE SCENE MANAGER -### Using the Menus: -Menu options with gold colored text are selectable, which means when you select these menu items, something will happen (opening a new menu, adding a waypoint, placing a barrier, etc). Menu options with white colored text are interactable (can be scrolled through, for example), but nothing will happen if you try to select them. - -![Menus](https://i.imgur.com/dOCtdQN.jpg) - -### Creating Paths: -1. To create paths, open the Scene Manager menu by pressing Left Shift + T (default). Select "Path Menu," then "Create New Path." -2. In the Path Creation menu, you can specify different waypoint options, as well as add and remove waypoints. The settings for each waypoint are how the AI will drive to **that** waypoint from the **previous** waypoint. -3. Adding a waypoint will create a waypoint at your player's position. -4. The first waypoint of a path **must** be a collector waypoint. If you have 3D waypoints enabled, the blue marker is the collection radius and the orange marker is the speed zone radius. -5. As you add and remove waypoints, blips and world markers will appear and disappear if enabled in your settings. Blips are numbered to correspond with their path. Blips and markers are also colored to designate the following: Blue are collector waypoints, green are Drive waypoints, and red are Stop waypoints. -6. The path will activate once you select "End path creation," and cars will automatically follow the path when they are within the collection radius. -7. After creating paths, you can delete them via the Path Menu. - -![Creating a path](https://i.imgur.com/h52Y2SY.jpg) -![Finishg a path](https://i.imgur.com/MGGfGQB.jpg) - -### Other Path Menu Options -1. The "Direct nearest driver to path" option in the Path Manager Main Menu allows you to manually direct the nearest vehicle to a path of your choice. You may either direct a driver to the nearest path's first waypoint or the nearest waypoint in front of the driver. - -2. The "Dismiss nearest driver" option in the Path Manager Main Menu allows you to dismiss vehicles from their path, their current waypoint, or from their current position. Dismissing from a path will clear all driver tasks and they will no longer be controlled by Scene Manager. Dismissing from their current waypoint will either give the driver their next waypoint task, or dismiss them from the path if they were already going to the path's final waypoint. The Dismiss from World option will delete the vehicle and all occupants from the world. - -![Other path menu options](https://i.imgur.com/3tpiitR.jpg) - -### Editing Paths and Waypoints -1. In the Edit Path menu, you can disable and delete individual paths. When a path is disabled, it will not collect any new vehicles at collector waypoints. However, vehicles can still be manually directed to disabled paths. -2. In the Edit Waypoint menu, you can change the settings of each path's individual waypoints. -3. You can add new waypoints to the end of the path by selecting the Add as New Waypoint option in the Edit Waypoint menu. It is recommended to have the Update Waypoint Position option checked as you do this since it will create a 3D marker (if enabled in the settings) around your player so you can see the waypoint's settings as you change them. - -![Edit path waypoints](https://i.imgur.com/V8q5wvp.jpg) - -### Barrier Management -1. To place barriers, open the Scene Manager menu by pressing Left Shift + T (default). -2. In the Barrier Menu, you may scroll through different types of road barriers, as well as place and remove barriers. -3. While the barrier menu is open and Spawn Barrier or Rotate Barrier are highlighted, you will see a "shadow" barrier. The shadow barrier shows the barrier you currently have selected, its rotation, and the position it will be spawned at. -4. The spawn position is wherever you aim your mouse. If you aim your mouse too far away, the shadow barrier will disappear and you won't be able to spawn the barrier. You can change the maximum spawn distance in the plugin's .ini file. -5. The AI will not drive around barriers on their own. Barrier placement should be done in conjunction with your paths. - -![Barrier menu](https://i.imgur.com/kxMGhIF.jpg) -![Placing barriers](https://i.imgur.com/FanVlGP.jpg) - -### Other Notes -1. Paths and barriers will remain in the world after you die. Be sure to delete them when you're done! -2. The first waypoint of a path must be a collector waypoint. If you edit the only waypoint of a path to not be a collector, it will automatically be turned into a collector. -3. Some settings in Scene Manager's .ini file can be updated in-game using the Settings menu. Saving these settings in-game will update the .ini so you don't have to change the settings every time you load the plugin. -4. You can add custom barrier objects to the barrier menu by adding them to the Barriers section of the .ini file. A link is provided in the .ini file to find object model names. - -![Scene overview](https://i.imgur.com/lB2pdh6.jpg) - -## CREDITS -* Author: Rich -* Additional credit: PNWParksFan for code assistance and extensive testing, Sereous, OJdoesIt, Vincentsgm, EchoWolf, FactionsBrutus +# Scene Manager + +## INSTALLATION +Drag and drop the contents from within the downloaded GTA V folder into where your GTA V is installed. +![How to drag and drop](https://i.imgur.com/nnxqgtn.jpg) + +## HOW TO USE SCENE MANAGER +### Using the Menus: +Menu options with gold colored text are selectable, which means when you select these menu items, something will happen (opening a new menu, adding a waypoint, placing a barrier, etc). Menu options with white colored text are interactable (can be scrolled through, for example), but nothing will happen if you try to select them. + +![Menus](https://i.imgur.com/dOCtdQN.jpg) + +### Creating Paths: +1. To create paths, open the Scene Manager menu by pressing Left Shift + T (default). Select "Path Menu," then "Create New Path." +2. In the Path Creation menu, you can specify different waypoint options, as well as add and remove waypoints. The settings for each waypoint are how the AI will drive to **that** waypoint from the **previous** waypoint. +3. Adding a waypoint will create a waypoint at your player's position. +4. The first waypoint of a path **must** be a collector waypoint. If you have 3D waypoints enabled, the blue marker is the collection radius and the orange marker is the speed zone radius. +5. As you add and remove waypoints, blips and world markers will appear and disappear if enabled in your settings. Blips are numbered to correspond with their path. Blips and markers are also colored to designate the following: Blue are collector waypoints, green are Drive waypoints, and red are Stop waypoints. +6. The path will activate once you select "End path creation," and cars will automatically follow the path when they are within the collection radius. +7. After creating paths, you can delete them via the Path Menu. + +![Creating a path](https://i.imgur.com/h52Y2SY.jpg) +![Finishg a path](https://i.imgur.com/MGGfGQB.jpg) + +### Other Path Menu Options +1. The "Direct nearest driver to path" option in the Path Manager Main Menu allows you to manually direct the nearest vehicle to a path of your choice. You may either direct a driver to the nearest path's first waypoint or the nearest waypoint in front of the driver. + +2. The "Dismiss nearest driver" option in the Path Manager Main Menu allows you to dismiss vehicles from their path, their current waypoint, or from their current position. Dismissing from a path will clear all driver tasks and they will no longer be controlled by Scene Manager. Dismissing from their current waypoint will either give the driver their next waypoint task, or dismiss them from the path if they were already going to the path's final waypoint. The Dismiss from World option will delete the vehicle and all occupants from the world. + +![Other path menu options](https://i.imgur.com/3tpiitR.jpg) + +### Editing Paths and Waypoints +1. In the Edit Path menu, you can disable and delete individual paths. When a path is disabled, it will not collect any new vehicles at collector waypoints. However, vehicles can still be manually directed to disabled paths. +2. In the Edit Waypoint menu, you can change the settings of each path's individual waypoints. +3. You can add new waypoints to the end of the path by selecting the Add as New Waypoint option in the Edit Waypoint menu. It is recommended to have the Update Waypoint Position option checked as you do this since it will create a 3D marker (if enabled in the settings) around your player so you can see the waypoint's settings as you change them. + +![Edit path waypoints](https://i.imgur.com/V8q5wvp.jpg) + +### Barrier Management +1. To place barriers, open the Scene Manager menu by pressing Left Shift + T (default). +2. In the Barrier Menu, you may scroll through different types of road barriers, as well as place and remove barriers. +3. While the barrier menu is open and Spawn Barrier or Rotate Barrier are highlighted, you will see a "shadow" barrier. The shadow barrier shows the barrier you currently have selected, its rotation, and the position it will be spawned at. +4. The spawn position is wherever you aim your mouse. If you aim your mouse too far away, the shadow barrier will disappear and you won't be able to spawn the barrier. You can change the maximum spawn distance in the plugin's .ini file. +5. The AI will not drive around barriers on their own. Barrier placement should be done in conjunction with your paths. + +![Barrier menu](https://i.imgur.com/kxMGhIF.jpg) +![Placing barriers](https://i.imgur.com/FanVlGP.jpg) + +### Other Notes +1. Paths and barriers will remain in the world after you die. Be sure to delete them when you're done! +2. The first waypoint of a path must be a collector waypoint. If you edit the only waypoint of a path to not be a collector, it will automatically be turned into a collector. +3. Some settings in Scene Manager's .ini file can be updated in-game using the Settings menu. Saving these settings in-game will update the .ini so you don't have to change the settings every time you load the plugin. +4. You can add custom barrier objects to the barrier menu by adding them to the Barriers section of the .ini file. A link is provided in the .ini file to find object model names. + +![Scene overview](https://i.imgur.com/lB2pdh6.jpg) + +## CREDITS +* Author: Rich +* Additional credit: PNWParksFan for code assistance and extensive testing, Sereous, OJdoesIt, Vincentsgm, EchoWolf, FactionsBrutus \ No newline at end of file diff --git a/SceneManager/AITasking.cs b/SceneManager/AITasking.cs index 2790328..ea6b2d2 100644 --- a/SceneManager/AITasking.cs +++ b/SceneManager/AITasking.cs @@ -17,22 +17,23 @@ namespace SceneManager } collectedVehicle.Path = path; - collectedVehicle.CurrentWaypoint = currentWaypoint; + if(currentWaypoint != null) + { + collectedVehicle.CurrentWaypoint = currentWaypoint; + } + else + { + collectedVehicle.CurrentWaypoint = path.Waypoints[0]; + } if (currentWaypoint != null && collectedVehicle.Directed) { + collectedVehicle.Dismissed = false; float acceptedDistance = GetAcceptedStoppingDistance(path.Waypoints, path.Waypoints.IndexOf(currentWaypoint)); while (!collectedVehicle.ReadyForDirectTasks) { GameFiber.Yield(); } - if (collectedVehicle.StoppedAtWaypoint) - { - Logger.Log($"Unstucking {collectedVehicle.Vehicle.Model.Name}"); - collectedVehicle.StoppedAtWaypoint = false; - Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(collectedVehicle.Vehicle, 0f, 1, true); - collectedVehicle.Driver.Tasks.CruiseWithVehicle(5f); - } collectedVehicle.Driver.Tasks.Clear(); AssignTasksForDirectedDriver(acceptedDistance); LoopWhileDrivingToDirectedWaypoint(acceptedDistance); @@ -42,6 +43,7 @@ namespace SceneManager } if (collectedVehicle.Vehicle) { + collectedVehicle.Driver.Tasks.PerformDrivingManeuver(collectedVehicle.Vehicle, VehicleManeuver.GoForwardWithCustomSteeringAngle, 3).WaitForCompletion(); Logger.Log($"{collectedVehicle.Vehicle.Model.Name} directed task is complete, directed is now false"); } } diff --git a/SceneManager/EntryPoint.cs b/SceneManager/EntryPoint.cs index 3d69be8..c8ee0e8 100644 --- a/SceneManager/EntryPoint.cs +++ b/SceneManager/EntryPoint.cs @@ -1,4 +1,7 @@ using System; +using System.ComponentModel.Design.Serialization; +using System.Drawing.Text; +using System.IO; using System.Linq; using System.Reflection; using System.Windows.Forms; @@ -12,42 +15,72 @@ namespace SceneManager { internal static void Main() { - AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler; - Settings.LoadSettings(); - GetAssemblyVersion(); - MenuManager.InstantiateMenus(); + if (CheckRNUIVersion()) + { + AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler; + Settings.LoadSettings(); + GetAssemblyVersion(); + MenuManager.InstantiateMenus(); - DisplayHintsToOpenMenu(); + DisplayHintsToOpenMenu(); - GameFiber UserInputFiber = new GameFiber(() => GetUserInput.LoopForUserInput()); - UserInputFiber.Start(); + GameFiber UserInputFiber = new GameFiber(() => GetUserInput.LoopForUserInput()); + UserInputFiber.Start(); + } + else + { + Game.UnloadActivePlugin(); + return; + } + + void GetAssemblyVersion() + { + string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + Logger.Log($"Scene Manager V{version} is ready."); + } } - - private static void GetAssemblyVersion() + private static bool CheckRNUIVersion() { - Assembly assembly = Assembly.GetExecutingAssembly(); - System.Diagnostics.FileVersionInfo fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); - var version = fvi.FileVersion; - Logger.Log($"Scene Manager V{version} is ready."); + var directory = Directory.GetCurrentDirectory(); + var exists = File.Exists(directory + @"\RAGENativeUI.dll"); + if (!exists) + { + Logger.Log($"RNUI was not found in the user's GTA V directory."); + Game.DisplayNotification($"~o~Scene Manager ~r~[Error]\n~w~RAGENativeUI.dll was not found in your GTA V directory. Please install RAGENativeUI and try again."); + return false; + } + + var userVersion = Assembly.LoadFrom(directory + @"\RAGENativeUI.dll").GetName().Version; + Version requiredMinimumVersion = new Version("1.7.0.0"); + if(userVersion >= requiredMinimumVersion) + { + Logger.Log($"User's RNUI version: {userVersion}"); + return true; + } + else + { + Game.DisplayNotification($"~o~Scene Manager~r~[Error]\n~w~ Your RAGENativeUI.dll version is below 1.7, please update RAGENativeUI and try again."); + return false; + } + } - private static void DisplayHintsToOpenMenu() { if (Settings.ModifierKey == Keys.None && Settings.ModifierButton == ControllerButtons.None) { - Hints.Display($"~o~Scene Manager\n~y~[Hint]~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ToggleButton} button"); + Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ToggleButton} button"); } else if (Settings.ModifierKey == Keys.None) { - Hints.Display($"~o~Scene Manager\n~y~[Hint]~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons"); + Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons"); } else if (Settings.ModifierButton == ControllerButtons.None) { - Hints.Display($"~o~Scene Manager\n~y~[Hint]~w~ To open the menu, press ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} ~w~or the ~b~{Settings.ToggleButton} button"); + Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} ~w~or the ~b~{Settings.ToggleButton} button"); } else { - Hints.Display($"~o~Scene Manager\n~y~[Hint]~w~ To open the menu, press the ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} keys ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons"); + Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press the ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} keys ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons"); } } @@ -71,11 +104,10 @@ namespace SceneManager // Clear everything BarrierMenu.barriers.Clear(); - VehicleCollector.collectedVehicles.Clear(); PathMainMenu.paths.Clear(); Logger.Log($"Plugin has shut down."); - Game.DisplayNotification($"~o~Scene Manager\n~r~[Notice]~w~ The plugin has shut down."); + Game.DisplayNotification($"~o~Scene Manager ~r~[Terminated]\n~w~ The plugin has shut down."); } } } diff --git a/SceneManager/Menus/BarrierMenu.cs b/SceneManager/Menus/BarrierMenu.cs index 2a1b4ba..84a9477 100644 --- a/SceneManager/Menus/BarrierMenu.cs +++ b/SceneManager/Menus/BarrierMenu.cs @@ -44,7 +44,7 @@ namespace SceneManager internal static void CreateShadowBarrier(UIMenu barrierMenu) { - Hints.Display($"~o~Scene Manager\n~y~[Hint]~y~ ~w~The shadow cone will disappear if you aim too far away."); + Hints.Display($"~o~Scene Manager ~y~[Hint]\n~y~ ~w~The shadow cone will disappear if you aim too far away."); if (shadowBarrier) shadowBarrier.Delete(); @@ -53,7 +53,7 @@ namespace SceneManager if (!shadowBarrier) { barrierMenu.Close(); - Game.DisplayNotification($"~o~Scene Manager\n~red~[Error]~w~ Something went wrong creating the shadow barrier. Please try again."); + Game.DisplayNotification($"~o~Scene Manager ~red~[Error]\n~w~ Something went wrong creating the shadow barrier. Please try again."); return; } Rage.Native.NativeFunction.Natives.PLACE_OBJECT_ON_GROUND_PROPERLY(shadowBarrier); diff --git a/SceneManager/Menus/PathCreationMenu.cs b/SceneManager/Menus/PathCreationMenu.cs index a93f727..bf9553d 100644 --- a/SceneManager/Menus/PathCreationMenu.cs +++ b/SceneManager/Menus/PathCreationMenu.cs @@ -18,7 +18,7 @@ namespace SceneManager private static UIMenuNumericScrollerItem waypointSpeed; internal static UIMenuCheckboxItem stopWaypointType = new UIMenuCheckboxItem("Is this a Stop waypoint?", false, "If checked, vehicles will drive to this waypoint, then stop."); internal static UIMenuCheckboxItem directWaypointBehavior = new UIMenuCheckboxItem("Drive directly to waypoint?", false, "If checked, vehicles will ignore traffic rules and drive directly to this waypoint."); - internal static UIMenuCheckboxItem collectorWaypoint = new UIMenuCheckboxItem("Collector", true, "If chcked, this waypoint will collect vehicles to follow the path. Your path's first waypoint ~b~must~w~ be a collector."); + internal static UIMenuCheckboxItem collectorWaypoint = new UIMenuCheckboxItem("Collector", true, "If checked, this waypoint will collect vehicles to follow the path. Your path's first waypoint ~b~must~w~ be a collector."); internal static UIMenuNumericScrollerItem collectorRadius = new UIMenuNumericScrollerItem("Collection Radius", "The distance from this waypoint (in meters) vehicles will be collected", 1, 50, 1); internal static UIMenuNumericScrollerItem speedZoneRadius = new UIMenuNumericScrollerItem("Speed Zone Radius", "The distance from this collector waypoint (in meters) non-collected vehicles will drive at this waypoint's speed", 5, 200, 5); @@ -107,6 +107,7 @@ namespace SceneManager ToggleTrafficEndPathMenuItem(pathIndex); collectorWaypoint.Enabled = true; + collectorWaypoint.Checked = false; trafficRemoveWaypoint.Enabled = true; PathMainMenu.createNewPath.Text = $"Continue Creating Path {pathNumber}"; @@ -206,21 +207,32 @@ namespace SceneManager currentPath.State = State.Finished; currentPath.IsEnabled = true; currentPath.Number = i + 1; + currentPath.LoopForVehiclesToBeDismissed(); - foreach (Waypoint waypoint in PathMainMenu.paths[i].Waypoints) + GameFiber.StartNew(() => { - GameFiber WaypointVehicleCollectorFiber = new GameFiber(() => VehicleCollector.StartCollectingAtWaypoint(PathMainMenu.paths, PathMainMenu.paths[i], waypoint)); - WaypointVehicleCollectorFiber.Start(); - } + currentPath.LoopWaypointCollection(); + //foreach(Waypoint waypoint in PathMainMenu.paths[i].Waypoints) + //{ + // GameFiber WaypointVehicleCollectorFiber = new GameFiber(() => waypoint.CollectVehicles(PathMainMenu.paths)); + // WaypointVehicleCollectorFiber.Start(); + // GameFiber.Sleep(1000); + //} + }); PathMainMenu.createNewPath.Text = "Create New Path"; PathMainMenu.BuildPathMenu(); PathMainMenu.pathMainMenu.RefreshIndex(); pathCreationMenu.RefreshIndex(); + waypointSpeed.Index = 0; collectorWaypoint.Enabled = false; collectorWaypoint.Checked = true; speedZoneRadius.Enabled = true; + speedZoneRadius.Index = 0; collectorRadius.Enabled = true; + collectorRadius.Index = 0; + stopWaypointType.Checked = false; + directWaypointBehavior.Checked = false; trafficEndPath.Enabled = false; PathMainMenu.pathMainMenu.Visible = true; diff --git a/SceneManager/Menus/PathMainMenu.cs b/SceneManager/Menus/PathMainMenu.cs index bb7803d..5c41188 100644 --- a/SceneManager/Menus/PathMainMenu.cs +++ b/SceneManager/Menus/PathMainMenu.cs @@ -13,7 +13,8 @@ namespace SceneManager FromPath = 0, FromWaypoint = 1, FromWorld = 2, - FromPlayer = 3 + FromPlayer = 3, + FromDirected = 4 } static class PathMainMenu @@ -130,7 +131,7 @@ namespace SceneManager void RemoveVehiclesFromPath() { //Game.LogTrivial($"Removing all vehicles on the path"); - var pathVehicles = VehicleCollector.collectedVehicles.Where(cv => cv.Path.Number == path.Number).ToList(); + var pathVehicles = path.CollectedVehicles.Where(cv => cv.Path.Number == path.Number).ToList(); foreach (CollectedVehicle cv in pathVehicles.Where(cv => cv != null && cv.Vehicle && cv.Driver)) { if (cv.StoppedAtWaypoint) @@ -148,8 +149,9 @@ namespace SceneManager cv.Vehicle.Dismiss(); //Game.LogTrivial($"{cv.vehicle.Model.Name} cleared from path {cv.path}"); - VehicleCollector.collectedVehicles.Remove(cv); + path.CollectedVehicles.Remove(cv); } + path.CollectedVehicles.Clear(); } void RemoveBlipsAndYieldZones() @@ -227,7 +229,7 @@ namespace SceneManager { DeletePath(paths[i], Delete.All); } - + disableAllPaths.Checked = false; paths.Clear(); BuildPathMenu(); pathMainMenu.Visible = true; @@ -238,21 +240,48 @@ namespace SceneManager if (selectedItem == directDriver) { var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(16).Where(v => v != Game.LocalPlayer.Character.CurrentVehicle && v.VehicleAndDriverValid()).FirstOrDefault(); + var path = paths[directDriver.Index]; + var collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault(); + var waypoints = path.Waypoints; + var firstWaypoint = waypoints.First(); + var nearestWaypoint = waypoints.Where(wp => wp.Position.DistanceTo2D(nearbyVehicle.FrontPosition) < wp.Position.DistanceTo2D(nearbyVehicle.RearPosition)).OrderBy(wp => wp.Position.DistanceTo2D(nearbyVehicle)).FirstOrDefault(); if (nearbyVehicle) { - var collectedVehicle = VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault(); - var path = paths[directDriver.Index]; - var waypoints = path.Waypoints; - var firstWaypoint = waypoints.First(); - var nearestWaypoint = waypoints.Where(wp => wp.Position.DistanceTo2D(nearbyVehicle.FrontPosition) < wp.Position.DistanceTo2D(nearbyVehicle.RearPosition)).OrderBy(wp => wp.Position.DistanceTo2D(nearbyVehicle)).FirstOrDefault(); + var nearbyVehiclePath = paths.Where(p => p.CollectedVehicles.Any(v => v.Vehicle == nearbyVehicle)).FirstOrDefault(); + if(nearbyVehiclePath != null) + { + var nearbyCollectedVehicle = nearbyVehiclePath.CollectedVehicles.Where(v => v.Vehicle == nearbyVehicle).FirstOrDefault(); + if (nearbyCollectedVehicle != null) + { + nearbyCollectedVehicle.Dismiss(DismissOption.FromDirected, path); + if (directOptions.SelectedItem == "First waypoint") + { + GameFiber.StartNew(() => + { + AITasking.AssignWaypointTasks(nearbyCollectedVehicle, path, firstWaypoint); + }); + } + else + { + if (nearestWaypoint != null) + { + GameFiber.StartNew(() => + { + AITasking.AssignWaypointTasks(nearbyCollectedVehicle, path, nearestWaypoint); + }); + } + } + return; + } + } // The vehicle should only be added to the collection when it's not null AND if the selected item is First Waypoint OR if the selected item is nearestWaypoint AND nearestWaypoint is not null if (collectedVehicle == null && directOptions.SelectedItem == "First waypoint" || (directOptions.SelectedItem == "Nearest waypoint" && nearestWaypoint != null)) { Game.LogTrivial($"[Direct Driver] Adding {nearbyVehicle.Model.Name} to collection."); - VehicleCollector.collectedVehicles.Add(new CollectedVehicle(nearbyVehicle, path)); - collectedVehicle = VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault(); + path.CollectedVehicles.Add(new CollectedVehicle(nearbyVehicle, path)); + collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault(); //Logger.Log($"Collected vehicle is {collectedVehicle.Vehicle.Model.Name}"); } @@ -289,12 +318,7 @@ namespace SceneManager var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(16).Where(v => v != Game.LocalPlayer.Character.CurrentVehicle && v.VehicleAndDriverValid()).FirstOrDefault(); if (nearbyVehicle) { - var collectedVehicle = VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault(); - if(collectedVehicle != null) - { - collectedVehicle.Dismiss((DismissOption)dismissDriver.Index); - } - else if(dismissDriver.Index == (int)DismissOption.FromWorld) + if (!paths.Any() && dismissDriver.Index == (int)DismissOption.FromWorld) { Game.LogTrivial($"Dismissed {nearbyVehicle.Model.Name} from the world"); while (nearbyVehicle && nearbyVehicle.HasOccupants) @@ -309,6 +333,34 @@ namespace SceneManager { nearbyVehicle.Delete(); } + return; + } + + foreach(Path path in paths) + { + var collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault(); + if (collectedVehicle != null) + { + collectedVehicle.Dismiss((DismissOption)dismissDriver.Index); + break; + } + else if (dismissDriver.Index == (int)DismissOption.FromWorld) + { + Game.LogTrivial($"Dismissed {nearbyVehicle.Model.Name} from the world"); + while (nearbyVehicle && nearbyVehicle.HasOccupants) + { + foreach (Ped occupant in nearbyVehicle.Occupants) + { + occupant.Delete(); + } + GameFiber.Yield(); + } + if (nearbyVehicle) + { + nearbyVehicle.Delete(); + } + break; + } } } } diff --git a/SceneManager/Object Classes/CollectedVehicle.cs b/SceneManager/Object Classes/CollectedVehicle.cs index 19d9bf1..2638b1e 100644 --- a/SceneManager/Object Classes/CollectedVehicle.cs +++ b/SceneManager/Object Classes/CollectedVehicle.cs @@ -41,7 +41,7 @@ namespace SceneManager //Logger.Log($"{Vehicle.Model.Name} and driver are now persistent."); } - internal void Dismiss(DismissOption dismissOption = DismissOption.FromPath) + internal void Dismiss(DismissOption dismissOption = DismissOption.FromPath, Path newPath = null) { if (!Vehicle || !Driver) { @@ -66,7 +66,7 @@ namespace SceneManager Vehicle.Dismiss(); } Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true); - VehicleCollector.collectedVehicles.Remove(this); + Path.CollectedVehicles.Remove(this); return; } @@ -89,6 +89,11 @@ namespace SceneManager DismissFromPath(); } + if(dismissOption == DismissOption.FromDirected) + { + DismissFromDirect(); + } + void DismissFromWorld() { Game.LogTrivial($"Dismissed {Vehicle.Model.Name} from the world"); @@ -151,7 +156,7 @@ namespace SceneManager if (!Directed) { - VehicleCollector.collectedVehicles.Remove(this); + Path.CollectedVehicles.Remove(this); Logger.Log($"{Vehicle.Model.Name} dismissed successfully."); if (Driver) { @@ -172,6 +177,18 @@ namespace SceneManager }); } + + void DismissFromDirect() + { + Dismissed = true; + Directed = true; + if (newPath != null) + { + newPath.CollectedVehicles.Add(this); + Path.CollectedVehicles.Remove(this); + } + Driver.Tasks.Clear(); + } } } } diff --git a/SceneManager/Object Classes/Path.cs b/SceneManager/Object Classes/Path.cs index af3a743..e2e5bca 100644 --- a/SceneManager/Object Classes/Path.cs +++ b/SceneManager/Object Classes/Path.cs @@ -1,6 +1,8 @@ using Rage; +using System; using System.Collections.Generic; using System.Drawing; +using System.Linq; namespace SceneManager { @@ -10,6 +12,7 @@ namespace SceneManager internal bool IsEnabled { get; set; } internal State State { get; set; } internal List Waypoints = new List(); + internal List CollectedVehicles = new List(); internal Path(int pathNum, State pathState) { @@ -101,5 +104,125 @@ namespace SceneManager } }); } + + internal void LoopForVehiclesToBeDismissed() + { + GameFiber.StartNew(() => + { + while (PathMainMenu.paths.Contains(this)) + { + //Logger.Log($"Dismissing unused vehicles for cleanup"); + foreach (CollectedVehicle cv in CollectedVehicles.Where(cv => cv.Vehicle)) + { + if (!cv.Vehicle.IsDriveable || cv.Vehicle.IsUpsideDown || !cv.Vehicle.HasDriver) + { + if (cv.Vehicle.HasDriver) + { + cv.Vehicle.Driver.Dismiss(); + } + cv.Vehicle.Dismiss(); + } + } + + CollectedVehicles.RemoveAll(cv => !cv.Vehicle); + GameFiber.Sleep(60000); + } + }); + } + + internal void LoopWaypointCollection() + { + uint lastProcessTime = Game.GameTime; // Store the last time the full loop finished; this is a value in ms + int timeBetweenChecks = 1000; // How many ms to wait between outer loops + int yieldAfterChecks = 50; // How many calculations to do before yielding + while (PathMainMenu.paths.Contains(this)) + { + if (IsEnabled) + { + int checksDone = 0; + try + { + foreach (Waypoint waypoint in Waypoints) + { + if (waypoint != null & waypoint.IsCollector) + { + foreach (Vehicle v in World.GetAllVehicles()) + { + if (IsNearWaypoint(v, waypoint) && IsValidForCollection(v)) + { + CollectedVehicle newCollectedVehicle = AddVehicleToCollection(v); + GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, this, waypoint)); + AssignTasksFiber.Start(); + } + + checksDone++; // Increment the counter inside the vehicle loop + if (checksDone % yieldAfterChecks == 0) + { + GameFiber.Yield(); // Yield the game fiber after the specified number of vehicles have been checked + } + } + } + } + } + catch + { + //return; + } + } + GameFiber.Sleep((int)Math.Max(1, Game.GameTime - lastProcessTime)); // If the prior lines took more than a second to run, then you'll run again almost immediately, but if they ran fairly quickly, you can sleep the loop until the remainder of the time between checks has passed + lastProcessTime = Game.GameTime; + } + + CollectedVehicle AddVehicleToCollection(Vehicle vehicle) + { + var collectedVehicle = new CollectedVehicle(vehicle, this); + CollectedVehicles.Add(collectedVehicle); + Logger.Log($"Added {vehicle.Model.Name} to collection from path {Number} waypoint {1}."); + return collectedVehicle; + } + + bool IsNearWaypoint(Vehicle v, Waypoint wp) + { + return v.FrontPosition.DistanceTo2D(wp.Position) <= wp.CollectorRadius && Math.Abs(wp.Position.Z - v.Position.Z) < 3; + } + + bool IsValidForCollection(Vehicle v) + { + if (v && v != Game.LocalPlayer.Character.CurrentVehicle && v != Game.LocalPlayer.Character.LastVehicle && (v.IsCar || v.IsBike || v.IsBicycle || v.IsQuadBike) && !v.IsSirenOn && v.IsEngineOn && v.IsOnAllWheels && v.Speed > 1 && !CollectedVehicles.Any(cv => cv?.Vehicle == v)) + { + var vehicleCollectedOnAnotherPath = PathMainMenu.paths.Any(p => p.Number != Number && p.CollectedVehicles.Any(cv => cv.Vehicle == v)); + if (vehicleCollectedOnAnotherPath) + { + return false; + } + if (v.HasDriver && v.Driver && !v.Driver.IsAlive) + { + return false; + } + if (!v.HasDriver) + { + v.CreateRandomDriver(); + while (!v.HasDriver) + { + GameFiber.Yield(); + } + if (v && v.Driver) + { + v.Driver.IsPersistent = true; + v.Driver.BlockPermanentEvents = true; + } + else + { + return false; + } + } + return true; + } + else + { + return false; + } + } + } } } diff --git a/SceneManager/Object Classes/Waypoint.cs b/SceneManager/Object Classes/Waypoint.cs index 6c0cb13..92b4d30 100644 --- a/SceneManager/Object Classes/Waypoint.cs +++ b/SceneManager/Object Classes/Waypoint.cs @@ -1,4 +1,6 @@ using Rage; +using System; +using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -77,7 +79,7 @@ namespace SceneManager if (IsStopWaypoint && !stopWaypoint) { Blip.Color = Color.Green; - foreach(CollectedVehicle cv in VehicleCollector.collectedVehicles.Where(cv => cv.Vehicle && cv.Path == Path && cv.CurrentWaypoint == this && cv.StoppedAtWaypoint)) + foreach(CollectedVehicle cv in Path.CollectedVehicles.Where(cv => cv.Vehicle && cv.Path == Path && cv.CurrentWaypoint == this && cv.StoppedAtWaypoint)) { // Logger.Log($"Setting StoppedAtWaypoint to false for {cv.Vehicle.Model.Name}"); cv.Dismiss(DismissOption.FromWaypoint); @@ -286,5 +288,102 @@ namespace SceneManager CollectorRadiusBlip.Alpha = 0; } } + + internal void CollectVehicles(List paths) + { + var sleepInterval = 1000; + Logger.Log($"Starting collection loop on waypoint {Number}"); + while (paths.Contains(Path) && Path.Waypoints.Contains(this)) + { + if (Path.IsEnabled && IsCollector) + { + sleepInterval = 100; + LoopForNearbyValidVehicles(); + } + else + { + sleepInterval = 1000; + } + + var collectedVehiclePlayerIsIn = Path.CollectedVehicles.Where(x => x.Vehicle == Game.LocalPlayer.Character.CurrentVehicle).FirstOrDefault(); + if (collectedVehiclePlayerIsIn != null) + { + collectedVehiclePlayerIsIn.Dismiss(DismissOption.FromPlayer); + Logger.Log($"Dismissed a collected vehicle the player was in."); + } + GameFiber.Sleep(sleepInterval); + } + + void LoopForNearbyValidVehicles() + { + foreach (Vehicle vehicle in GetNearbyVehiclesForCollection(Position, CollectorRadius)) + { + if (!vehicle) + { + break; + } + + var collectedVehicle = Path.CollectedVehicles.Where(cv => cv.Vehicle == vehicle).FirstOrDefault(); + if (collectedVehicle == null) + { + CollectedVehicle newCollectedVehicle = AddVehicleToCollection(vehicle); + //Logger.Log($"Vehicle's front position distance to waypoint: {vehicle.FrontPosition.DistanceTo2D(waypoint.Position)}, collector radius: {waypoint.CollectorRadius}"); + GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, Path, this)); + AssignTasksFiber.Start(); + } + } + + Vehicle[] GetNearbyVehiclesForCollection(Vector3 collectorWaypointPosition, float collectorRadius) + { + return (from v in World.GetAllVehicles() where v.FrontPosition.DistanceTo2D(collectorWaypointPosition) <= collectorRadius && Math.Abs(collectorWaypointPosition.Z - v.Position.Z) < 3 && IsValidForCollection(v) select v).ToArray(); + } + } + + CollectedVehicle AddVehicleToCollection(Vehicle vehicle) + { + var collectedVehicle = new CollectedVehicle(vehicle, Path, this); + Path.CollectedVehicles.Add(collectedVehicle); + Logger.Log($"Added {vehicle.Model.Name} to collection from path {Path.Number} waypoint {this.Number}."); + return collectedVehicle; + } + + bool IsValidForCollection(Vehicle v) + { + if (v && v.Speed > 1 && v.IsOnAllWheels && v.IsEngineOn && v != Game.LocalPlayer.Character.CurrentVehicle && v != Game.LocalPlayer.Character.LastVehicle && (v.IsCar || v.IsBike || v.IsBicycle || v.IsQuadBike) && !v.IsSirenOn && !Path.CollectedVehicles.Any(cv => cv?.Vehicle == v)) + { + var vehicleCollectedOnAnotherPath = paths.Any(p => p.Number != Path.Number && p.CollectedVehicles.Any(cv => cv.Vehicle == v)); + if (vehicleCollectedOnAnotherPath) + { + return false; + } + if (v.HasDriver && v.Driver && !v.Driver.IsAlive) + { + return false; + } + if (!v.HasDriver) + { + v.CreateRandomDriver(); + while (!v.HasDriver) + { + GameFiber.Yield(); + } + if (v && v.Driver) + { + v.Driver.IsPersistent = true; + v.Driver.BlockPermanentEvents = true; + } + else + { + return false; + } + } + return true; + } + else + { + return false; + } + } + } } } diff --git a/SceneManager/Properties/AssemblyInfo.cs b/SceneManager/Properties/AssemblyInfo.cs index 44061cc..e2ce833 100644 --- a/SceneManager/Properties/AssemblyInfo.cs +++ b/SceneManager/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +[assembly: AssemblyVersion("2.0.1.0")] +[assembly: AssemblyFileVersion("2.0.1.0")] diff --git a/SceneManager/SceneManager.csproj b/SceneManager/SceneManager.csproj index 9c579ae..e629da2 100644 --- a/SceneManager/SceneManager.csproj +++ b/SceneManager/SceneManager.csproj @@ -51,11 +51,11 @@ + - @@ -68,7 +68,6 @@ - diff --git a/SceneManager/VehicleCollector.cs b/SceneManager/VehicleCollector.cs deleted file mode 100644 index 50893d1..0000000 --- a/SceneManager/VehicleCollector.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using Rage; - -namespace SceneManager -{ - internal static class VehicleCollector - { - internal static List collectedVehicles = new List(); - - internal static void StartCollectingAtWaypoint(List paths, Path path, Waypoint waypoint) - { - LoopForVehiclesToBeDismissed(paths, path); - - while (paths.Contains(path) && path.Waypoints.Contains(waypoint)) - { - if (path.IsEnabled && waypoint.IsCollector) - { - LoopForNearbyValidVehicles(path, waypoint); - } - - var collectedVehiclePlayerIsIn = collectedVehicles.Where(x => x.Vehicle == Game.LocalPlayer.Character.CurrentVehicle).FirstOrDefault(); - if (collectedVehiclePlayerIsIn != null) - { - collectedVehiclePlayerIsIn.Dismiss(DismissOption.FromPlayer); - Logger.Log($"Dismissed a collected vehicle the player was in."); - } - GameFiber.Sleep(100); - } - } - - private static void LoopForVehiclesToBeDismissed(List paths, Path path) - { - GameFiber.StartNew(() => - { - while (paths.Contains(path)) - { - //Logger.Log($"Dismissing unused vehicles for cleanup"); - foreach (CollectedVehicle cv in collectedVehicles.Where(cv => cv.Vehicle)) - { - if (!cv.Vehicle.IsDriveable || cv.Vehicle.IsUpsideDown || !cv.Vehicle.HasDriver) - { - if (cv.Vehicle.HasDriver) - { - cv.Vehicle.Driver.Dismiss(); - } - cv.Vehicle.Dismiss(); - } - } - - collectedVehicles.RemoveAll(cv => !cv.Vehicle); - GameFiber.Sleep(60000); - } - }); - } - - private static void LoopForNearbyValidVehicles(Path path, Waypoint waypoint) - { - foreach (Vehicle vehicle in GetNearbyVehiclesForCollection(waypoint.Position, waypoint.CollectorRadius)) - { - if (!vehicle) - { - break; - } - - //Logger.Log($"Vehicle: {vehicle.Model.Name}, Waypoint collector radius: {waypoint.CollectorRadius}, Distance to waypoint: {vehicle.DistanceTo2D(waypoint.Position)}"); - - var collectedVehicle = collectedVehicles.Where(cv => cv.Vehicle == vehicle).FirstOrDefault(); - if(collectedVehicle == null) - { - //SetVehicleAndDriverPersistence(vehicle); - CollectedVehicle newCollectedVehicle = AddVehicleToCollection(path, waypoint, vehicle); - //Logger.Log($"Vehicle's front position distance to waypoint: {vehicle.FrontPosition.DistanceTo2D(waypoint.Position)}, collector radius: {waypoint.CollectorRadius}"); - GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, path, waypoint)); - AssignTasksFiber.Start(); - } - } - - Vehicle[] GetNearbyVehiclesForCollection(Vector3 collectorWaypointPosition, float collectorRadius) - { - return (from v in World.GetAllVehicles() where v.FrontPosition.DistanceTo2D(collectorWaypointPosition) <= collectorRadius && Math.Abs(collectorWaypointPosition.Z - v.Position.Z) < 3 && v.IsValidForCollection() select v).ToArray(); - } - } - - private static CollectedVehicle AddVehicleToCollection(Path path, Waypoint waypoint, Vehicle v) - { - var collectedVehicle = new CollectedVehicle(v, path, waypoint); - collectedVehicles.Add(collectedVehicle); - Logger.Log($"Added {v.Model.Name} to collection from path {path.Number} waypoint {waypoint.Number}."); - return collectedVehicle; - } - - private static bool IsValidForCollection(this Vehicle v) - { - if(v && v.Speed > 1 && v.IsOnAllWheels && v.IsEngineOn && v != Game.LocalPlayer.Character.CurrentVehicle && v != Game.LocalPlayer.Character.LastVehicle && (v.IsCar || v.IsBike || v.IsBicycle || v.IsQuadBike) && !v.IsSirenOn && !collectedVehicles.Any(cv => cv?.Vehicle == v)) - { - if(v.HasDriver && v.Driver && !v.Driver.IsAlive) - { - return false; - } - if (!v.HasDriver) - { - v.CreateRandomDriver(); - while (!v.HasDriver) - { - GameFiber.Yield(); - } - if(v && v.Driver) - { - //var driverBlip = v.Driver.AttachBlip(); - //driverBlip.Color = Color.Green; - //driverBlip.Scale = 0.25f; - v.Driver.IsPersistent = true; - v.Driver.BlockPermanentEvents = true; - //Logger.Log($"A missing driver was created for {v.Model.Name}."); - } - else - { - return false; - } - } - return true; - } - else - { - return false; - } - } - } -}