1
Fork 0
mirror of https://github.com/thegeneralist01/Scene-Manager-DevRepo synced 2026-01-11 23:50:29 +01:00

Merge pull request #1 from Rich-Dunne/Testing

V2.0 Merge
This commit is contained in:
Rich 2020-10-16 13:03:02 -06:00 committed by GitHub
commit 9e20de9aff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 2732 additions and 1149 deletions

41
README.md Normal file
View file

@ -0,0 +1,41 @@
# Scene Manager
## INSTALLATION
Drag and drop the included GTA V folder into where your GTA V is installed. Everything is installed into the plugins folder.
## HOW-TO USE
### 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.
### 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. 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-To waypoints, and red are Stop waypoints.
5. The path will activate once you select "End path creation," and cars will automatically follow the path when they are close to the first waypoint.
6. After creating paths, you can delete them via the Path Menu.
### 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. Dismissing from current position should be used for vehicles which might be stuck, but are not controlled by Scene Manager
### 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.
### 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. The AI will not drive around barriers on their own. Barrier placement should be in conjunction with your paths.
### 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.
## CREDITS
* Author: Rich
* Additional credit: PNWParksFan for code assistance and extensive testing, Sereous, OJdoesIt, Vincentsgm, EchoWolf, FactionsBrutus

230
SceneManager/AITasking.cs Normal file
View file

@ -0,0 +1,230 @@
using Rage;
using System.Collections.Generic;
using System.Linq;
namespace SceneManager
{
// Driving styles https://gtaforums.com/topic/822314-guide-driving-styles/
// also https://vespura.com/fivem/drivingstyle/
class AITasking
{
internal static void AssignWaypointTasks(CollectedVehicle collectedVehicle, Path path, Waypoint currentWaypoint)
{
if (!VehicleAndDriverAreValid(collectedVehicle))
{
return;
}
collectedVehicle.Path = path;
collectedVehicle.CurrentWaypoint = currentWaypoint;
if (currentWaypoint != null && collectedVehicle.Directed)
{
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);
if(collectedVehicle != null)
{
collectedVehicle.Directed = false;
}
if (collectedVehicle.Vehicle)
{
Logger.Log($"{collectedVehicle.Vehicle.Model.Name} directed task is complete, directed is now false");
}
}
if (currentWaypoint.IsStopWaypoint)
{
StopVehicleAtWaypoint(currentWaypoint, collectedVehicle);
}
if(path?.Waypoints?.Count > 0 && currentWaypoint != path?.Waypoints?.Last())
{
DriveVehicleToNextWaypoint(collectedVehicle, path, currentWaypoint);
}
if (!VehicleAndDriverAreValid(collectedVehicle) || collectedVehicle.Directed)
{
return;
}
Logger.Log($"{collectedVehicle.Vehicle.Model.Name} all Path {path.Number} tasks complete.");
if(!collectedVehicle.Dismissed)
{
collectedVehicle.Dismiss();
}
void AssignTasksForDirectedDriver(float acceptedDistance)
{
//Logger.Log($"{collectedVehicle.Vehicle.Model.Name} distance to collection waypoint: {collectedVehicle.Vehicle.DistanceTo2D(currentWaypoint.Position)}");
Logger.Log($"{collectedVehicle.Vehicle.Model.Name} is driving to path {currentWaypoint.Path.Number} waypoint {currentWaypoint.Number} (directed)");
//Logger.Log($"{collectedVehicle.Vehicle.Model.Name} Dismissed: {collectedVehicle.Dismissed}, Directed: {collectedVehicle.Directed}");
collectedVehicle.Driver.Tasks.DriveToPosition(currentWaypoint.Position, currentWaypoint.Speed, (VehicleDrivingFlags)currentWaypoint.DrivingFlagType, acceptedDistance);
}
void LoopWhileDrivingToDirectedWaypoint(float acceptedDistance)
{
while (VehicleAndDriverAreValid(collectedVehicle) && !collectedVehicle.Dismissed && !collectedVehicle.SkipWaypoint && collectedVehicle.Vehicle.FrontPosition.DistanceTo2D(currentWaypoint.Position) > acceptedDistance)
{
//Logger.Log($"Looping while {collectedVehicle.Vehicle.Model.Name} drives to path {path.Number} waypoint {currentWaypoint.Number}");
GameFiber.Yield();
}
}
}
private static void DriveVehicleToNextWaypoint(CollectedVehicle collectedVehicle, Path path, Waypoint currentWaypoint)
{
if (!VehicleAndDriverAreValid(collectedVehicle) || currentWaypoint == null || currentWaypoint.Path == null)
{
Logger.Log($"Vehicle, driver, waypoint, or path is null.");
return;
}
var vehicle = collectedVehicle.Vehicle;
var driver = vehicle.Driver;
Logger.Log($"Preparing to run task loop for {collectedVehicle.Vehicle.Model.Name} on path {path.Number}");
//Logger.Log($"Current path: {collectedVehicle.Path.Number}, Current waypoint: {collectedVehicle.CurrentWaypoint.Number}");
for (int currentWaypointTask = currentWaypoint.Number; currentWaypointTask < path.Waypoints.Count; currentWaypointTask++)
{
//Logger.Log($"{collectedVehicle.Vehicle.Model.Name} in the task loop");
//Logger.Log($"Dismissed: {collectedVehicle.Dismissed}, Directed: {collectedVehicle.Directed}, StoppedAtWaypoint: {collectedVehicle.StoppedAtWaypoint}");
collectedVehicle.SkipWaypoint = false;
if (collectedVehicle == null || !collectedVehicle.Vehicle || collectedVehicle.Dismissed || collectedVehicle.Directed)
{
Logger.Log($"Vehicle dismissed, directed, or null, return");
return;
}
if(collectedVehicle.Driver == null || !collectedVehicle.Driver || !collectedVehicle.Vehicle.HasDriver || !collectedVehicle.Driver.IsAlive || collectedVehicle.Vehicle.Driver == Game.LocalPlayer.Character)
{
Logger.Log($"{vehicle.Model.Name} does not have a driver/driver is null or driver is dead.");
return;
}
if (path.Waypoints.ElementAtOrDefault(currentWaypointTask) != null && !collectedVehicle.StoppedAtWaypoint)
{
collectedVehicle.CurrentWaypoint = path.Waypoints[currentWaypointTask];
//Logger.Log($"{collectedVehicle.Vehicle.Model.Name} current waypoint: {collectedVehicle.CurrentWaypoint.Number}");
float acceptedDistance = GetAcceptedStoppingDistance(path.Waypoints, currentWaypointTask);
Logger.Log($"{vehicle.Model.Name} is driving to path {currentWaypoint.Path.Number} waypoint {path.Waypoints[currentWaypointTask].Number} (Stop: {currentWaypoint.IsStopWaypoint}, Driving flag: {currentWaypoint.DrivingFlagType})");
//Logger.Log($"{vehicle.Model.Name} driver is persistent: {driver.IsPersistent}");
driver.Tasks.DriveToPosition(path.Waypoints[currentWaypointTask].Position, path.Waypoints[currentWaypointTask].Speed, (VehicleDrivingFlags)path.Waypoints[currentWaypointTask].DrivingFlagType, acceptedDistance);
LoopWhileDrivingToWaypoint(currentWaypointTask, acceptedDistance);
if (!VehicleAndDriverAreValid(collectedVehicle))
{
return;
}
if (collectedVehicle.SkipWaypoint)
{
collectedVehicle.SkipWaypoint = false;
continue;
}
if (!collectedVehicle.Dismissed && !collectedVehicle.Directed && path.Waypoints.ElementAtOrDefault(currentWaypointTask) != null && path.Waypoints[currentWaypointTask].IsStopWaypoint)
{
StopVehicleAtWaypoint(path.Waypoints[currentWaypointTask], collectedVehicle);
}
if (!VehicleAndDriverAreValid(collectedVehicle) || collectedVehicle.Dismissed || collectedVehicle.Directed)
{
return;
}
driver.Tasks.PerformDrivingManeuver(collectedVehicle.Vehicle, VehicleManeuver.GoForwardWithCustomSteeringAngle, 3).WaitForCompletion();
// Do we need this?
if (driver)
{
driver.Tasks.Clear();
}
}
}
void LoopWhileDrivingToWaypoint(int nextWaypoint, float acceptedDistance)
{
while (VehicleAndDriverAreValid(collectedVehicle) && !collectedVehicle.Dismissed && !collectedVehicle.SkipWaypoint && !collectedVehicle.Directed && path.Waypoints.ElementAtOrDefault(nextWaypoint) != null && collectedVehicle.Vehicle.FrontPosition.DistanceTo2D(path.Waypoints[nextWaypoint].Position) > acceptedDistance)
{
//Logger.Log($"Looping while {vehicle.Model.Name} drives to waypoint.");
GameFiber.Sleep(100);
}
}
}
private static float GetAcceptedStoppingDistance(List<Waypoint> waypoints, int nextWaypoint)
{
float dist;
if(Settings.SpeedUnit == SpeedUnits.MPH)
{
dist = (MathHelper.ConvertMilesPerHourToKilometersPerHour(waypoints[nextWaypoint].Speed) * MathHelper.ConvertMilesPerHourToKilometersPerHour(waypoints[nextWaypoint].Speed)) / (250 * 0.8f);
}
else
{
dist = (waypoints[nextWaypoint].Speed * waypoints[nextWaypoint].Speed) / (250 * 0.8f);
}
var acceptedDistance = MathHelper.Clamp(dist, 2, 10);
//Logger.Log($"Accepted distance: {acceptedDistance}");
return acceptedDistance;
}
private static bool VehicleAndDriverAreValid(CollectedVehicle collectedVehicle)
{
if (collectedVehicle == null)
{
Logger.Log($"CollectedVehicle is null");
return false;
}
if (!collectedVehicle.Vehicle && !collectedVehicle.Dismissed)
{
Logger.Log($"Vehicle is null");
collectedVehicle.Dismiss();
return false;
}
if (collectedVehicle.Driver == null || !collectedVehicle.Driver || !collectedVehicle.Driver.IsAlive && !collectedVehicle.Dismissed)
{
collectedVehicle.Dismiss();
//Logger.Log($"{collectedVehicle.Vehicle.Model.Name} driver is null or dead");
return false;
}
return true;
}
private static void StopVehicleAtWaypoint(Waypoint currentWaypoint, CollectedVehicle collectedVehicle)
{
if (!VehicleAndDriverAreValid(collectedVehicle))
{
return;
}
var stoppingDistance = GetAcceptedStoppingDistance(currentWaypoint.Path.Waypoints, currentWaypoint.Path.Waypoints.IndexOf(currentWaypoint));
Logger.Log($"{collectedVehicle.Vehicle.Model.Name} stopping at path {currentWaypoint.Path.Number} waypoint.");
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(collectedVehicle.Vehicle, stoppingDistance, -1, true);
collectedVehicle.StoppedAtWaypoint = true;
while (currentWaypoint != null && VehicleAndDriverAreValid(collectedVehicle) && collectedVehicle.StoppedAtWaypoint && !collectedVehicle.Directed)
{
GameFiber.Yield();
}
if(collectedVehicle.Vehicle && collectedVehicle.Driver)
{
Logger.Log($"{collectedVehicle.Vehicle.Model.Name} releasing from stop waypoint.");
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(collectedVehicle.Vehicle, 0f, 1, true);
collectedVehicle.Driver.Tasks.CruiseWithVehicle(5f);
}
}
}
}

View file

@ -1,27 +0,0 @@
using Rage;
namespace SceneManager
{
public class ControlledVehicle
{
public Vehicle Vehicle;
public int Path;
public int TotalWaypoints;
public int CurrentWaypoint;
public bool TasksAssigned;
public bool DismissNow;
public bool StoppedAtWaypoint;
public bool Redirected;
public ControlledVehicle(Vehicle vehicle, int path, int totalWaypoints, int currentWaypoint, bool tasksAssigned, bool dismissNow, bool redirected)
{
Vehicle = vehicle;
Path = path;
TotalWaypoints = totalWaypoints;
CurrentWaypoint = currentWaypoint;
TasksAssigned = tasksAssigned;
DismissNow = dismissNow;
Redirected = redirected;
}
}
}

View file

@ -1,86 +1,81 @@
using System.Windows.Forms; using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Rage; using Rage;
[assembly: Rage.Attributes.Plugin("Scene Manager V1.7", Author = "Rich", Description = "Manage your scenes with custom AI traffic pathing and cone placement.")] [assembly: Rage.Attributes.Plugin("Scene Manager", Author = "Rich", Description = "Control your scenes with custom AI pathing and traffic barrier management.", PrefersSingleInstance = true)]
namespace SceneManager namespace SceneManager
{ {
public class EntryPoint public class EntryPoint
{ {
internal static class Settings internal static void Main()
{
internal static Keys ToggleKey = Keys.T;
internal static Keys ModifierKey = Keys.LShiftKey;
internal static ControllerButtons ToggleButton = ControllerButtons.Y;
internal static ControllerButtons ModifierButton = ControllerButtons.A;
internal static bool EnableHints = true;
internal static string id = Verification.passThrough(Verification.GetID());
internal static string PatronKey = null; // This cannot reference VerifyUser because the file can just be shared and it will always work. Must be manually set to each user's ID
internal static void LoadSettings()
{
Game.LogTrivial("Loading SceneManager.ini settings");
InitializationFile ini = new InitializationFile("Plugins/SceneManager.ini");
ini.Create();
PatronKey = ini.ReadString("Patreon","PatronKey", null);
ToggleKey = ini.ReadEnum("Keybindings", "ToggleKey", Keys.T);
ModifierKey = ini.ReadEnum("Keybindings", "ModifierKey", Keys.LShiftKey);
ToggleButton = ini.ReadEnum("Keybindings", "ToggleButton", ControllerButtons.A);
ModifierButton = ini.ReadEnum("Keybindings", "ModifierButton", ControllerButtons.DPadDown);
EnableHints = ini.ReadBoolean("Other Settings", "EnableHints", true);
}
}
public static void Main()
{ {
AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler;
Settings.LoadSettings(); Settings.LoadSettings();
Game.LogTrivial($"Scene Manager is ready."); GetAssemblyVersion();
MenuManager.InstantiateMenus();
// id is hardware ID and needs to match PatronKey, which is also hardware ID DisplayHintsToOpenMenu();
if (Settings.id == Settings.PatronKey)
{ GameFiber UserInputFiber = new GameFiber(() => GetUserInput.LoopForUserInput());
Game.LogTrivial($"Patron status verified."); UserInputFiber.Start();
Game.DisplayNotification($"~o~Scene Manager\n~g~[Patreon]~w~ Thanks for the support, enjoy your session!");
}
else
{
Game.LogTrivial($"Patron status not verified.");
Game.DisplayNotification($"~o~Scene Manager\n~y~[Patreon]~w~ Thanks for using my plugin! If you would like to gain access to benefits such as ~g~new features for this plugin~w~, ~g~early access to new plugins~w~, and ~g~custom plugins made just for you~w~, please consider supporting me on ~b~Patreon~w~. ~y~https://www.patreon.com/richdevs");
} }
if (Settings.EnableHints) private static void GetAssemblyVersion()
{
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.");
}
private static void DisplayHintsToOpenMenu()
{ {
if (Settings.ModifierKey == Keys.None && Settings.ModifierButton == ControllerButtons.None) if (Settings.ModifierKey == Keys.None && Settings.ModifierButton == ControllerButtons.None)
{ {
Game.DisplayNotification($"~o~Scene Manager\n~y~[Hint]~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ToggleButton} button ~w~while on foot"); 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");
} }
else if (Settings.ModifierKey == Keys.None) else if (Settings.ModifierKey == Keys.None)
{ {
Game.DisplayNotification($"~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 ~w~while on foot"); 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");
} }
else if (Settings.ModifierButton == ControllerButtons.None) else if (Settings.ModifierButton == ControllerButtons.None)
{ {
Game.DisplayNotification($"~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 ~w~while on foot"); 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");
} }
else else
{ {
Game.DisplayNotification($"~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 ~w~while on foot"); 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");
} }
} }
GameFiber TrafficMenuFiber = new GameFiber(() => TrafficMenu.CheckUserInput()); private static void MyTerminationHandler(object sender, EventArgs e)
TrafficMenuFiber.Start(); {
// Clean up cones
foreach (Barrier barrier in BarrierMenu.barriers.Where(b => b.Object))
{
barrier.Object.Delete();
}
if (BarrierMenu.shadowBarrier)
{
BarrierMenu.shadowBarrier.Delete();
}
// Clean up paths
for (int i = 0; i < PathMainMenu.paths.Count; i++)
{
PathMainMenu.DeletePath(PathMainMenu.paths[i], PathMainMenu.Delete.All);
}
// 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.");
} }
} }
} }
/*
* GameFiber.StartNew(delegate{
*
* });
*
* public static Vehicle[] GetNearbyVehicles2(Vector3 OriginPosition, int amount)
* {
* return (Vehicle[])(from x in World.GetAllVehicles() orderby x.DistanceTo(OriginPosition) select x).Take(amount).ToArray();
* }
*/

View file

@ -0,0 +1,67 @@
using Rage;
namespace SceneManager
{
class GetUserInput
{
internal static void LoopForUserInput()
{
while (true)
{
GetKeyboardInput();
GetControllerInput();
#if DEBUG
if (MenuManager.menuPool.IsAnyMenuOpen())
{
Game.DisplaySubtitle($"You are using a test build of Scene Manager. Please report any bugs/crashes in the Discord server.");
}
#endif
MenuManager.menuPool.ProcessMenus();
GameFiber.Yield();
}
}
private static void GetControllerInput()
{
if (Settings.ModifierButton == ControllerButtons.None)
{
if (Game.IsControllerButtonDown(Settings.ToggleButton) && AreMenusClosed())
{
MainMenu.mainMenu.Visible = !MainMenu.mainMenu.Visible;
}
}
else if (Game.IsControllerButtonDownRightNow(Settings.ModifierButton) && Game.IsControllerButtonDown(Settings.ToggleButton) && AreMenusClosed())
{
MainMenu.mainMenu.Visible = !MainMenu.mainMenu.Visible;
}
}
private static void GetKeyboardInput()
{
if (Settings.ModifierKey == System.Windows.Forms.Keys.None)
{
if (Game.IsKeyDown(Settings.ToggleKey) && AreMenusClosed())
{
MainMenu.mainMenu.Visible = !MainMenu.mainMenu.Visible;
}
}
else if (Game.IsKeyDownRightNow(Settings.ModifierKey) && Game.IsKeyDown(Settings.ToggleKey) && AreMenusClosed())
{
MainMenu.mainMenu.Visible = !MainMenu.mainMenu.Visible;
}
}
private static bool AreMenusClosed()
{
if(!BarrierMenu.barrierMenu.Visible && !PathMainMenu.pathMainMenu.Visible && !PathCreationMenu.pathCreationMenu.Visible && !EditPathMenu.editPathMenu.Visible && !EditWaypointMenu.editWaypointMenu.Visible && !SettingsMenu.settingsMenu.Visible)
{
return true;
}
else
{
return false;
}
}
}
}

17
SceneManager/Hints.cs Normal file
View file

@ -0,0 +1,17 @@
using Rage;
namespace SceneManager
{
class Hints
{
internal static bool Enabled { get; set; } = SettingsMenu.hints.Checked;
internal static void Display(string message)
{
if (Enabled)
{
Game.DisplayNotification($"{message}");
}
}
}
}

16
SceneManager/Logger.cs Normal file
View file

@ -0,0 +1,16 @@
using Rage;
namespace SceneManager
{
class Logger
{
internal static void Log(string message)
{
#if DEBUG
Game.LogTrivialDebug($"{message}");
#else
Game.LogTrivial($"{message}");
#endif
}
}
}

View file

@ -0,0 +1,302 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
class BarrierMenu
{
internal static UIMenu barrierMenu = new UIMenu("Scene Manager", "~o~Barrier Management");
internal static List<Barrier> barriers = new List<Barrier>();
private static UIMenuListScrollerItem<string> barrierList = new UIMenuListScrollerItem<string>("Spawn Barrier", "", Settings.barrierKeys);
private static UIMenuNumericScrollerItem<int> rotateBarrier = new UIMenuNumericScrollerItem<int>("Rotate Barrier", "", 0, 350, 10);
private static UIMenuListScrollerItem<string> removeBarrierOptions = new UIMenuListScrollerItem<string>("Remove Barrier", "", new[] { "Last Barrier", "Nearest Barrier", "All Barriers" });
private static UIMenuItem resetBarriers = new UIMenuItem("Reset Barriers", "Reset all spawned barriers to their original position and rotation");
internal static Rage.Object shadowBarrier;
internal static void InstantiateMenu()
{
barrierMenu.ParentMenu = MainMenu.mainMenu;
MenuManager.menuPool.Add(barrierMenu);
}
internal static void BuildBarrierMenu()
{
barrierMenu.AddItem(resetBarriers);
resetBarriers.ForeColor = Color.Gold;
resetBarriers.Enabled = false;
barrierMenu.AddItem(removeBarrierOptions, 0);
removeBarrierOptions.ForeColor = Color.Gold;
removeBarrierOptions.Enabled = false;
barrierMenu.AddItem(rotateBarrier, 0);
barrierMenu.AddItem(barrierList, 0);
barrierList.ForeColor = Color.Gold;
barrierMenu.OnItemSelect += BarrierMenu_OnItemSelected;
barrierMenu.OnScrollerChange += BarrierMenu_OnScrollerChange;
}
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.");
if (shadowBarrier)
shadowBarrier.Delete();
shadowBarrier = new Rage.Object(Settings.barrierValues[barrierList.Index], TracePlayerView(Settings.BarrierPlacementDistance, TraceFlags.IntersectWorld).HitPosition, rotateBarrier.Value);
if (!shadowBarrier)
{
barrierMenu.Close();
Game.DisplayNotification($"~o~Scene Manager\n~red~[Error]~w~ Something went wrong creating the shadow barrier. Please try again.");
return;
}
Rage.Native.NativeFunction.Natives.PLACE_OBJECT_ON_GROUND_PROPERLY(shadowBarrier);
shadowBarrier.IsGravityDisabled = true;
shadowBarrier.IsCollisionEnabled = false;
shadowBarrier.Opacity = 0.7f;
GameFiber ShadowConeLoopFiber = new GameFiber(() => LoopToDisplayShadowBarrier());
ShadowConeLoopFiber.Start();
void LoopToDisplayShadowBarrier()
{
while (barrierMenu.Visible && shadowBarrier)
{
if (barrierList.Selected || rotateBarrier.Selected)
{
shadowBarrier.IsVisible = true;
UpdateShadowBarrierPosition();
}
else
{
shadowBarrier.IsVisible = false;
}
GameFiber.Yield();
}
if (shadowBarrier)
shadowBarrier.Delete();
void UpdateShadowBarrierPosition()
{
DisableBarrierMenuOptionsIfShadowConeTooFar();
shadowBarrier.SetPositionWithSnap(TracePlayerView(Settings.BarrierPlacementDistance, TraceFlags.IntersectWorld).HitPosition);
void DisableBarrierMenuOptionsIfShadowConeTooFar()
{
if (shadowBarrier.Position.DistanceTo2D(Game.LocalPlayer.Character.Position) > Settings.BarrierPlacementDistance)
{
barrierList.Enabled = false;
rotateBarrier.Enabled = false;
}
else if (shadowBarrier.Position.DistanceTo2D(Game.LocalPlayer.Character.Position) <= Settings.BarrierPlacementDistance && barrierList.SelectedItem == "Flare")
{
barrierList.Enabled = true;
rotateBarrier.Enabled = false;
}
else
{
barrierList.Enabled = true;
rotateBarrier.Enabled = true;
}
}
}
}
//------------ CREDIT PNWPARKS FOR THESE FUNCTIONS ------------\\
// Implement Parks's 'Get Point Player is Looking At' script for better placement in 3rd person https://bitbucket.org/snippets/gtaparks/MeBKxX
HitResult TracePlayerView(float maxTraceDistance = 30f, TraceFlags flags = TraceFlags.IntersectWorld) => TracePlayerView2(out Vector3 v1, out Vector3 v2, maxTraceDistance, flags);
HitResult TracePlayerView2(out Vector3 start, out Vector3 end, float maxTraceDistance, TraceFlags flags)
{
Vector3 direction = GetPlayerLookingDirection(out start);
end = start + (maxTraceDistance * direction);
var barrierObjects = barriers.Where(b => b.Object).Select(b => b.Object).ToArray();
return World.TraceLine(start, end, flags, barrierObjects);
}
Vector3 GetPlayerLookingDirection(out Vector3 camPosition)
{
if (Camera.RenderingCamera)
{
camPosition = Camera.RenderingCamera.Position;
return Camera.RenderingCamera.Direction;
}
else
{
float pitch = Rage.Native.NativeFunction.Natives.GET_GAMEPLAY_CAM_RELATIVE_PITCH<float>();
float heading = Rage.Native.NativeFunction.Natives.GET_GAMEPLAY_CAM_RELATIVE_HEADING<float>();
camPosition = Rage.Native.NativeFunction.Natives.GET_GAMEPLAY_CAM_COORD<Vector3>();
return (Game.LocalPlayer.Character.Rotation + new Rotator(pitch, 0, heading)).ToVector().ToNormalized();
}
}
//------------ CREDIT PNWPARKS FOR THESE FUNCTIONS ------------\\
}
private static void BarrierMenu_OnScrollerChange(UIMenu sender, UIMenuScrollerItem scrollerItem, int oldIndex, int newIndex)
{
if (scrollerItem == barrierList)
{
CreateShadowBarrier(barrierMenu);
if(barrierList.SelectedItem == "Flare")
{
rotateBarrier.Enabled = false;
}
else
{
rotateBarrier.Enabled = true;
}
barrierMenu.Width = SetMenuWidth();
}
if (scrollerItem == rotateBarrier)
{
shadowBarrier.Heading = rotateBarrier.Value;
}
}
private static void BarrierMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == barrierList as UIMenuItem)
{
// Attach some invisible object to the cone which the AI try to drive around
// Barrier rotates with cone and becomes invisible similar to ASC when created
if(barrierList.SelectedItem == "Flare")
{
SpawnFlare();
}
else
{
SpawnBarrier();
}
}
if (selectedItem == removeBarrierOptions as UIMenuItem)
{
RemoveBarrier();
}
if (selectedItem == resetBarriers)
{
var currentBarriers = barriers.Where(b => b.Model.Name != "0xa2c44e80").ToList(); // 0xa2c44e80 is the flare weapon hash
foreach (Barrier barrier in currentBarriers)
{
var newBarrier = new Rage.Object(barrier.Model, barrier.Position, barrier.Rotation);
newBarrier.SetPositionWithSnap(barrier.Position);
Rage.Native.NativeFunction.Natives.SET_ENTITY_DYNAMIC(newBarrier, true);
newBarrier.IsPositionFrozen = false;
Rage.Native.NativeFunction.Natives.SET_DISABLE_FRAG_DAMAGE(newBarrier, true);
barriers.Add(new Barrier(newBarrier, newBarrier.Position, newBarrier.Heading));
if (barrier.Object)
{
barrier.Object.Delete();
}
barriers.Remove(barrier);
}
currentBarriers.Clear();
}
void SpawnBarrier()
{
var barrier = new Rage.Object(shadowBarrier.Model, shadowBarrier.Position, rotateBarrier.Value);
barrier.SetPositionWithSnap(shadowBarrier.Position);
Rage.Native.NativeFunction.Natives.SET_ENTITY_DYNAMIC(barrier, true);
barrier.IsPositionFrozen = false;
Rage.Native.NativeFunction.Natives.SET_DISABLE_FRAG_DAMAGE(barrier, true);
barriers.Add(new Barrier(barrier, barrier.Position, barrier.Heading));
removeBarrierOptions.Enabled = true;
resetBarriers.Enabled = true;
}
void SpawnFlare()
{
var flare = new Weapon("weapon_flare", shadowBarrier.Position, 1);
Rage.Native.NativeFunction.Natives.SET_ENTITY_DYNAMIC(flare, true);
GameFiber.StartNew(() =>
{
while (flare && flare.HeightAboveGround > 0.05f)
{
GameFiber.Yield();
}
GameFiber.Sleep(1000);
if (flare)
{
flare.IsPositionFrozen = true;
}
});
barriers.Add(new Barrier(flare, flare.Position, flare.Heading));
removeBarrierOptions.Enabled = true;
}
void RemoveBarrier()
{
switch (removeBarrierOptions.Index)
{
case 0:
barriers[barriers.Count - 1].Object.Delete();
barriers.RemoveAt(barriers.Count - 1);
break;
case 1:
var nearestBarrier = barriers.OrderBy(b => b.Object.DistanceTo2D(Game.LocalPlayer.Character)).FirstOrDefault();
if(nearestBarrier != null)
{
nearestBarrier.Object.Delete();
barriers.Remove(nearestBarrier);
}
break;
case 2:
foreach (Barrier b in barriers.Where(b => b.Object))
{
b.Object.Delete();
}
if (barriers.Count > 0)
{
barriers.Clear();
}
break;
}
removeBarrierOptions.Enabled = barriers.Count == 0 ? false : true;
resetBarriers.Enabled = barriers.Count == 0 ? false : true;
}
}
private static float SetMenuWidth()
{
float defaultWidth = UIMenu.DefaultWidth;
float width = barrierMenu.Width;
barrierList.TextStyle.Apply();
Rage.Native.NativeFunction.Natives.x54CE8AC98E120CAB("STRING"); // _BEGIN_TEXT_COMMAND_GET_WIDTH
Rage.Native.NativeFunction.Natives.ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(barrierList.SelectedItem);
float textWidth = Rage.Native.NativeFunction.Natives.x85F061DA64ED2F67<float>(true); // _END_TEXT_COMMAND_GET_WIDTH
float padding = 0.00390625f * 2; // typical padding used in RNUI
var selectedItemWidth = textWidth + padding;
if(selectedItemWidth <= 0.14f)
{
return defaultWidth;
}
else
{
return selectedItemWidth * 1.6f;
}
}
}
}

View file

@ -0,0 +1,66 @@
using System.Drawing;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
class EditPathMenu
{
internal static UIMenu editPathMenu = new UIMenu("Scene Manager", "~o~Edit Path");
private static UIMenuItem editPathWaypoints, deletePath;
internal static UIMenuCheckboxItem disablePath;
internal static void InstantiateMenu()
{
editPathMenu.ParentMenu = PathMainMenu.pathMainMenu;
MenuManager.menuPool.Add(editPathMenu);
}
internal static void BuildEditPathMenu()
{
editPathMenu.AddItem(disablePath = new UIMenuCheckboxItem("Disable Path", false));
editPathMenu.AddItem(editPathWaypoints = new UIMenuItem("Edit Waypoints"));
editPathWaypoints.ForeColor = Color.Gold;
editPathMenu.AddItem(deletePath = new UIMenuItem("Delete Path"));
deletePath.ForeColor = Color.Gold;
editPathMenu.RefreshIndex();
editPathMenu.OnItemSelect += EditPath_OnItemSelected;
editPathMenu.OnCheckboxChange += EditPath_OnCheckboxChange;
}
private static void EditPath_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
if (selectedItem == editPathWaypoints)
{
EditWaypointMenu.BuildEditWaypointMenu();
}
if (selectedItem == deletePath)
{
PathMainMenu.DeletePath(currentPath, PathMainMenu.Delete.Single);
}
}
private static void EditPath_OnCheckboxChange(UIMenu sender, UIMenuCheckboxItem checkboxItem, bool @checked)
{
if (checkboxItem == disablePath)
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
if (disablePath.Checked)
{
currentPath.DisablePath();
Logger.Log($"Path {currentPath.Number} disabled.");
}
else
{
currentPath.EnablePath();
Logger.Log($"Path {currentPath.Number} enabled.");
}
}
}
}
}

View file

@ -0,0 +1,291 @@
using System;
using System.Drawing;
using System.Linq;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
class EditWaypointMenu
{
private static VehicleDrivingFlags[] drivingFlags = new VehicleDrivingFlags[] { VehicleDrivingFlags.Normal, VehicleDrivingFlags.IgnorePathFinding, VehicleDrivingFlags.StopAtDestination };
private static string[] waypointTypes = new string[] { "Drive To (Normal)", "Drive To (Direct)", "Stop" };
internal static UIMenu editWaypointMenu = new UIMenu("Scene Manager", "~o~Edit Waypoint");
internal static UIMenuItem updateWaypoint = new UIMenuItem("Update Waypoint");
internal static UIMenuItem removeWaypoint = new UIMenuItem("Remove Waypoint");
internal static UIMenuItem addAsNewWaypoint = new UIMenuItem("Add as New Waypoint", "Adds a new waypoint to the end of the path with these settings");
internal static UIMenuNumericScrollerItem<int> editWaypoint;
internal static UIMenuListScrollerItem<string> changeWaypointType = new UIMenuListScrollerItem<string>("Waypoint Type", "", waypointTypes);
private static UIMenuNumericScrollerItem<int> changeWaypointSpeed;
internal static UIMenuCheckboxItem stopWaypointType;
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;
internal static UIMenuNumericScrollerItem<int> changeCollectorRadius = new UIMenuNumericScrollerItem<int>("Collection Radius", "The distance from this waypoint (in meters) vehicles will be collected", 1, 50, 1);
internal static UIMenuNumericScrollerItem<int> changeSpeedZoneRadius = new UIMenuNumericScrollerItem<int>("Speed Zone Radius", "The distance from this collector waypoint (in meters) non-collected vehicles will drive at this waypoint's speed", 5, 200, 5);
internal static UIMenuCheckboxItem updateWaypointPosition = new UIMenuCheckboxItem("Update Waypoint Position", false, "Updates the waypoint's position to the player's current position. You should turn this on if you're planning on adding this waypoint as a new waypoint.");
internal static void InstantiateMenu()
{
editWaypointMenu.ParentMenu = EditPathMenu.editPathMenu;
MenuManager.menuPool.Add(editWaypointMenu);
}
internal static void BuildEditWaypointMenu()
{
// Need to unsubscribe from these or else there will be duplicate firings if the user left the menu, then re-entered
ResetEventHandlerSubscriptions();
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Value-1];
//Logger.Log($"Current path: {currentPath.Number}");
editWaypoint = new UIMenuNumericScrollerItem<int>("Edit Waypoint", "", currentPath.Waypoints.First().Number, currentPath.Waypoints.Last().Number, 1);
editWaypointMenu.Clear();
editWaypointMenu.AddItem(editWaypoint);
editWaypoint.Index = 0;
var currentWaypoint = currentPath.Waypoints.Where(wp => wp.Number == editWaypoint.Value).FirstOrDefault();
//Logger.Log($"Current waypoint: {currentWaypoint.Number}, Driving flag: {currentWaypoint.DrivingFlag.ToString()}");
if(currentWaypoint != null)
{
editWaypointMenu.AddItem(collectorWaypoint = new UIMenuCheckboxItem("Collector", currentWaypoint.IsCollector, "If this waypoint will collect vehicles to follow the path"));
editWaypointMenu.AddItem(changeCollectorRadius);
changeCollectorRadius.Value = currentWaypoint.CollectorRadius != 0
? (int)currentWaypoint.CollectorRadius
: changeCollectorRadius.Minimum;
editWaypointMenu.AddItem(changeSpeedZoneRadius);
changeSpeedZoneRadius.Value = currentWaypoint.CollectorRadius != 0
? (int)currentWaypoint.SpeedZoneRadius
: changeSpeedZoneRadius.Minimum;
changeCollectorRadius.Enabled = collectorWaypoint.Checked ? true : false;
changeSpeedZoneRadius.Enabled = collectorWaypoint.Checked ? true : false;
editWaypointMenu.AddItem(stopWaypointType = new UIMenuCheckboxItem("Is this a Stop waypoint?", currentWaypoint.IsStopWaypoint, "If checked, vehicles will drive to this waypoint, then stop."));
editWaypointMenu.AddItem(directWaypointBehavior);
if(currentWaypoint.DrivingFlagType == DrivingFlagType.Direct)
{
directWaypointBehavior.Checked = true;
}
editWaypointMenu.AddItem(changeWaypointSpeed = new UIMenuNumericScrollerItem<int>("Waypoint Speed", $"How fast the AI will drive to the waypoint in ~b~{SettingsMenu.speedUnits.SelectedItem}", 5, 100, 5));
changeWaypointSpeed.Value = (int)MathHelper.ConvertMetersPerSecondToMilesPerHour(currentWaypoint.Speed);
editWaypointMenu.AddItem(updateWaypointPosition);
editWaypointMenu.AddItem(updateWaypoint);
updateWaypoint.ForeColor = Color.Gold;
editWaypointMenu.AddItem(removeWaypoint);
removeWaypoint.ForeColor = Color.Gold;
editWaypointMenu.AddItem(addAsNewWaypoint);
addAsNewWaypoint.ForeColor = Color.Gold;
EditPathMenu.editPathMenu.Visible = false;
editWaypointMenu.RefreshIndex();
editWaypointMenu.Visible = true;
}
void ResetEventHandlerSubscriptions()
{
editWaypointMenu.OnItemSelect -= EditWaypoint_OnItemSelected;
editWaypointMenu.OnCheckboxChange -= EditWaypoint_OnCheckboxChanged;
editWaypointMenu.OnScrollerChange -= EditWaypoint_OnScrollerChanged;
editWaypointMenu.OnScrollerChange += EditWaypoint_OnScrollerChanged;
editWaypointMenu.OnCheckboxChange += EditWaypoint_OnCheckboxChanged;
editWaypointMenu.OnItemSelect += EditWaypoint_OnItemSelected;
}
}
private static void EditWaypoint_OnScrollerChanged(UIMenu sender, UIMenuScrollerItem scrollerItem, int first, int last)
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
var currentWaypoint = currentPath.Waypoints[editWaypoint.Value - 1];
if (scrollerItem == editWaypoint)
{
//changeWaypointType.Index = Array.IndexOf(drivingFlags, currentWaypoint.DrivingFlag);
changeWaypointSpeed.Value = (int)MathHelper.ConvertMetersPerSecondToMilesPerHour(currentWaypoint.Speed);
stopWaypointType.Checked = currentWaypoint.IsStopWaypoint;
directWaypointBehavior.Checked = currentWaypoint.DrivingFlagType == DrivingFlagType.Direct ? true : false;
collectorWaypoint.Checked = currentWaypoint.IsCollector;
changeCollectorRadius.Enabled = collectorWaypoint.Checked ? true : false;
changeCollectorRadius.Value = (int)currentWaypoint.CollectorRadius;
changeSpeedZoneRadius.Enabled = collectorWaypoint.Checked ? true : false;
changeSpeedZoneRadius.Value = (int)currentWaypoint.SpeedZoneRadius;
updateWaypointPosition.Checked = false;
}
if (scrollerItem == changeCollectorRadius)
{
if (changeCollectorRadius.Value > changeSpeedZoneRadius.Value)
{
while(changeCollectorRadius.Value > changeSpeedZoneRadius.Value)
{
changeSpeedZoneRadius.ScrollToNextOption();
}
}
}
if (scrollerItem == changeSpeedZoneRadius)
{
if (changeSpeedZoneRadius.Value < changeCollectorRadius.Value)
{
changeCollectorRadius.Value = changeSpeedZoneRadius.Value;
}
}
}
private static void EditWaypoint_OnCheckboxChanged(UIMenu sender, UIMenuCheckboxItem checkboxItem, bool @checked)
{
if (checkboxItem == collectorWaypoint)
{
changeCollectorRadius.Enabled = collectorWaypoint.Checked ? true : false;
changeSpeedZoneRadius.Enabled = collectorWaypoint.Checked ? true : false;
}
}
private static void EditWaypoint_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
var currentWaypoint = currentPath.Waypoints[editWaypoint.Index];
DrivingFlagType drivingFlag = directWaypointBehavior.Checked ? DrivingFlagType.Direct : DrivingFlagType.Normal;
if (selectedItem == updateWaypoint)
{
if(currentPath.Waypoints.Count == 1)
{
currentWaypoint.UpdateWaypoint(currentWaypoint, drivingFlag, stopWaypointType.Checked, SetDriveSpeedForWaypoint(), true, changeCollectorRadius.Value, changeSpeedZoneRadius.Value, updateWaypointPosition.Checked);
}
else
{
currentWaypoint.UpdateWaypoint(currentWaypoint, drivingFlag, stopWaypointType.Checked, SetDriveSpeedForWaypoint(), collectorWaypoint.Checked, changeCollectorRadius.Value, changeSpeedZoneRadius.Value, updateWaypointPosition.Checked);
}
Logger.Log($"Path {currentPath.Number} Waypoint {currentWaypoint.Number} updated [Driving style: {drivingFlag} | Stop waypoint: {stopWaypointType.Checked} | Speed: {changeWaypointSpeed.Value} | Collector: {currentWaypoint.IsCollector}]");
updateWaypointPosition.Checked = false;
Game.DisplayNotification($"~o~Scene Manager\n~g~[Success]~w~ Waypoint {currentWaypoint.Number} updated.");
// Why am I rebuilding the menu??
//BuildEditWaypointMenu();
}
if (selectedItem == addAsNewWaypoint)
{
var pathIndex = PathMainMenu.paths.IndexOf(currentPath);
var newWaypointBlip = CreateNewWaypointBlip();
if (!currentPath.IsEnabled)
{
newWaypointBlip.Alpha = 0.5f;
}
if (collectorWaypoint.Checked)
{
currentPath.Waypoints.Add(new Waypoint(currentPath, currentPath.Waypoints.Last().Number + 1, Game.LocalPlayer.Character.Position, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, newWaypointBlip, true, changeCollectorRadius.Value, changeSpeedZoneRadius.Value));
}
else
{
currentPath.Waypoints.Add(new Waypoint(currentPath, currentPath.Waypoints.Last().Number + 1, Game.LocalPlayer.Character.Position, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, newWaypointBlip));
}
editWaypointMenu.RemoveItemAt(0);
editWaypoint = new UIMenuNumericScrollerItem<int>("Edit Waypoint", "", currentPath.Waypoints.First().Number, currentPath.Waypoints.Last().Number, 1);
editWaypointMenu.AddItem(editWaypoint, 0);
editWaypoint.Index = editWaypoint.OptionCount - 1;
editWaypointMenu.RefreshIndex();
updateWaypointPosition.Checked = false;
Logger.Log($"New waypoint (#{currentPath.Waypoints.Last().Number}) added.");
Blip CreateNewWaypointBlip()
{
var spriteNumericalEnum = pathIndex + 17; // 17 because the numerical value of these sprites are always 17 more than the path index
var blip = new Blip(Game.LocalPlayer.Character.Position)
{
Scale = 0.5f,
Sprite = (BlipSprite)spriteNumericalEnum
};
if (collectorWaypoint.Checked)
{
blip.Color = Color.Blue;
}
else if (stopWaypointType.Checked)
{
blip.Color = Color.Red;
}
else
{
blip.Color = Color.Green;
}
if (!SettingsMenu.mapBlips.Checked)
{
blip.Alpha = 0f;
}
return blip;
}
}
if (selectedItem == removeWaypoint)
{
if (currentPath.Waypoints.Count == 1)
{
Logger.Log($"Deleting the last waypoint from the path.");
PathMainMenu.DeletePath(currentPath, PathMainMenu.Delete.Single);
editWaypointMenu.Visible = false;
PathMainMenu.pathMainMenu.Visible = true;
}
else
{
currentWaypoint.Remove();
currentPath.Waypoints.Remove(currentWaypoint);
Logger.Log($"[Path {currentPath.Number}] Waypoint {currentWaypoint.Number} ({currentWaypoint.DrivingFlagType}) removed");
foreach (Waypoint wp in currentPath.Waypoints)
{
wp.Number = currentPath.Waypoints.IndexOf(wp) + 1;
//Logger.Log($"Waypoint at index {currentPath.Waypoints.IndexOf(wp)} is now waypoint #{wp.Number}");
}
editWaypointMenu.Clear();
BuildEditWaypointMenu();
if (currentPath.Waypoints.Count == 1)
{
Hints.Display($"~o~Scene Manager\n~y~[Hint]~w~ Your path's first waypoint ~b~must~w~ be a collector. If it's not, it will automatically be made into one.");
Logger.Log($"The path only has 1 waypoint left, this waypoint must be a collector.");
currentPath.Waypoints[0].UpdateWaypoint(currentWaypoint, drivingFlag, stopWaypointType.Checked, SetDriveSpeedForWaypoint(), true, changeCollectorRadius.Value, changeSpeedZoneRadius.Value, updateWaypointPosition.Checked);
collectorWaypoint.Checked = true;
changeCollectorRadius.Enabled = true;
changeSpeedZoneRadius.Enabled = true;
}
}
}
}
private static float SetDriveSpeedForWaypoint()
{
float convertedSpeed;
if (SettingsMenu.speedUnits.SelectedItem == SpeedUnits.MPH)
{
//Logger.Log($"Original speed: {waypointSpeeds[waypointSpeed.Index]}{SettingsMenu.speedUnits.SelectedItem}");
convertedSpeed = MathHelper.ConvertMilesPerHourToMetersPerSecond(changeWaypointSpeed.Value);
//Logger.Log($"Converted speed: {convertedSpeed}m/s");
}
else
{
//Logger.Log($"Original speed: {waypointSpeeds[waypointSpeed.Index]}{SettingsMenu.speedUnits.SelectedItem}");
convertedSpeed = MathHelper.ConvertKilometersPerHourToMetersPerSecond(changeWaypointSpeed.Value);
//Logger.Log($"Converted speed: {convertedSpeed}m/s");
}
return convertedSpeed;
}
}
}

View file

@ -0,0 +1,42 @@
using RAGENativeUI;
using RAGENativeUI.Elements;
using System.Drawing;
namespace SceneManager
{
class MainMenu
{
public static UIMenu mainMenu { get; private set; }
private static UIMenuItem navigateToPathMenu, navigateToBarrierMenu, navigateToSettingsMenu;
internal static void InstantiateMenu()
{
mainMenu = new UIMenu("Scene Manager", "~o~Main Menu");
MenuManager.menuPool.Add(mainMenu);
}
public static void BuildMainMenu()
{
mainMenu.AddItem(navigateToPathMenu = new UIMenuItem("Path Menu"));
navigateToPathMenu.ForeColor = Color.Gold;
mainMenu.BindMenuToItem(PathMainMenu.pathMainMenu, navigateToPathMenu);
mainMenu.AddItem(navigateToBarrierMenu = new UIMenuItem("Barrier Menu"));
navigateToBarrierMenu.ForeColor = Color.Gold;
mainMenu.BindMenuToItem(BarrierMenu.barrierMenu, navigateToBarrierMenu);
mainMenu.AddItem(navigateToSettingsMenu = new UIMenuItem("Settings"));
navigateToSettingsMenu.ForeColor = Color.Gold;
mainMenu.BindMenuToItem(SettingsMenu.settingsMenu, navigateToSettingsMenu);
mainMenu.RefreshIndex();
mainMenu.OnItemSelect += MainMenu_OnItemSelected;
}
private static void MainMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == navigateToBarrierMenu)
{
BarrierMenu.CreateShadowBarrier(BarrierMenu.barrierMenu);
}
}
}
}

View file

@ -0,0 +1,53 @@
using RAGENativeUI;
namespace SceneManager
{
public static class MenuManager
{
public static MenuPool menuPool = new MenuPool();
public static void InstantiateMenus()
{
MainMenu.InstantiateMenu();
SettingsMenu.InstantiateMenu();
PathMainMenu.InstantiateMenu();
PathCreationMenu.InstantiateMenu();
BarrierMenu.InstantiateMenu();
EditPathMenu.InstantiateMenu();
EditWaypointMenu.InstantiateMenu();
BuildMenus();
DefineMenuMouseSettings();
}
private static void DefineMenuMouseSettings()
{
foreach (UIMenu menu in menuPool)
{
menu.MouseControlsEnabled = false;
menu.AllowCameraMovement = true;
}
}
private static void BuildMenus()
{
MainMenu.BuildMainMenu();
SettingsMenu.BuildSettingsMenu();
PathMainMenu.BuildPathMenu();
PathCreationMenu.BuildPathCreationMenu();
EditPathMenu.BuildEditPathMenu();
BarrierMenu.BuildBarrierMenu();
}
private static void AddMenusToMenuPool()
{
menuPool.Add(MainMenu.mainMenu);
menuPool.Add(SettingsMenu.settingsMenu);
menuPool.Add(PathMainMenu.pathMainMenu);
menuPool.Add(BarrierMenu.barrierMenu);
menuPool.Add(PathCreationMenu.pathCreationMenu);
menuPool.Add(EditPathMenu.editPathMenu);
menuPool.Add(EditWaypointMenu.editWaypointMenu);
}
}
}

View file

@ -0,0 +1,277 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
class PathCreationMenu
{
private static VehicleDrivingFlags[] drivingFlags = new VehicleDrivingFlags[] { VehicleDrivingFlags.Normal, VehicleDrivingFlags.IgnorePathFinding, VehicleDrivingFlags.StopAtDestination }; // Implement custom driving flag for normal
private static string[] waypointTypes = new string[] { "Drive To (Normal)", "Drive To (Direct)", "Stop" };
internal static UIMenu pathCreationMenu = new UIMenu("Scene Manager", "~o~Path Creation Menu");
private static UIMenuItem trafficAddWaypoint = new UIMenuItem("Add waypoint"), trafficRemoveWaypoint = new UIMenuItem("Remove last waypoint"), trafficEndPath = new UIMenuItem("End path creation");
internal static UIMenuListScrollerItem<string> waypointType = new UIMenuListScrollerItem<string>("Waypoint Type", $"~b~Drive To (Normal): ~w~AI obeys traffic as much as possible{Environment.NewLine}~b~Drive To (Direct): ~w~AI ignores pathfinding rules{Environment.NewLine}~b~Stop: ~w~AI stops at the waypoint until dismissed", waypointTypes);
private static UIMenuNumericScrollerItem<int> 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 UIMenuNumericScrollerItem<int> collectorRadius = new UIMenuNumericScrollerItem<int>("Collection Radius", "The distance from this waypoint (in meters) vehicles will be collected", 1, 50, 1);
internal static UIMenuNumericScrollerItem<int> speedZoneRadius = new UIMenuNumericScrollerItem<int>("Speed Zone Radius", "The distance from this collector waypoint (in meters) non-collected vehicles will drive at this waypoint's speed", 5, 200, 5);
internal static void InstantiateMenu()
{
pathCreationMenu.ParentMenu = PathMainMenu.pathMainMenu;
MenuManager.menuPool.Add(pathCreationMenu);
}
internal static void BuildPathCreationMenu()
{
pathCreationMenu.AddItem(collectorWaypoint);
collectorWaypoint.Enabled = false;
collectorWaypoint.Checked = true;
pathCreationMenu.AddItem(collectorRadius);
collectorRadius.Index = 0;
collectorRadius.Enabled = true;
pathCreationMenu.AddItem(speedZoneRadius);
speedZoneRadius.Index = 0;
speedZoneRadius.Enabled = true;
pathCreationMenu.AddItem(stopWaypointType);
pathCreationMenu.AddItem(directWaypointBehavior);
pathCreationMenu.AddItem(waypointSpeed = new UIMenuNumericScrollerItem<int>("Waypoint Speed", $"How fast the AI will drive to this waypoint in ~b~{SettingsMenu.speedUnits.SelectedItem}", 5, 100, 5));
waypointSpeed.Index = 0;
pathCreationMenu.AddItem(trafficAddWaypoint);
trafficAddWaypoint.ForeColor = Color.Gold;
pathCreationMenu.AddItem(trafficRemoveWaypoint);
trafficRemoveWaypoint.ForeColor = Color.Gold;
trafficRemoveWaypoint.Enabled = false;
pathCreationMenu.AddItem(trafficEndPath);
trafficEndPath.ForeColor = Color.Gold;
trafficEndPath.Enabled = false;
pathCreationMenu.RefreshIndex();
pathCreationMenu.OnItemSelect += PathCreation_OnItemSelected;
pathCreationMenu.OnCheckboxChange += PathCreation_OnCheckboxChanged;
pathCreationMenu.OnScrollerChange += PathCreation_OnScrollerChanged;
}
private static void PathCreation_OnCheckboxChanged(UIMenu sender, UIMenuCheckboxItem checkboxItem, bool @checked)
{
if(checkboxItem == collectorWaypoint)
{
collectorRadius.Enabled = collectorWaypoint.Checked ? true : false;
speedZoneRadius.Enabled = collectorWaypoint.Checked ? true : false;
}
}
private static void PathCreation_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == trafficAddWaypoint)
{
var anyPathsExist = PathMainMenu.paths.Count > 0;
if (!anyPathsExist)
{
AddNewPathToPathsCollection(PathMainMenu.paths, 0);
}
else if(anyPathsExist && !PathMainMenu.paths.Any(p => p != null && p.State == State.Creating))
{
AddNewPathToPathsCollection(PathMainMenu.paths, PathMainMenu.paths.IndexOf(PathMainMenu.paths.Where(p => p.State == State.Finished).Last()) + 1);
}
var firstNonNullPath = PathMainMenu.paths.Where(p => p != null && p.State == State.Creating).First();
var pathIndex = PathMainMenu.paths.IndexOf(firstNonNullPath);
var pathNumber = firstNonNullPath.Number;
var waypointNumber = PathMainMenu.paths[pathIndex].Waypoints.Count + 1;
DrivingFlagType drivingFlag = directWaypointBehavior.Checked ? DrivingFlagType.Direct : DrivingFlagType.Normal;
if (collectorWaypoint.Checked)
{
PathMainMenu.paths[pathIndex].Waypoints.Add(new Waypoint(firstNonNullPath, waypointNumber, Game.LocalPlayer.Character.Position, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, CreateWaypointBlip(), true, collectorRadius.Value, speedZoneRadius.Value));
}
else
{
PathMainMenu.paths[pathIndex].Waypoints.Add(new Waypoint(firstNonNullPath, waypointNumber, Game.LocalPlayer.Character.Position, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, CreateWaypointBlip()));
}
Logger.Log($"Path {pathNumber} Waypoint {waypointNumber} added [Driving style: {drivingFlag} | Stop waypoint: {stopWaypointType.Checked} | Speed: {waypointSpeed.Value} | Collector: {collectorWaypoint.Checked}]");
ToggleTrafficEndPathMenuItem(pathIndex);
collectorWaypoint.Enabled = true;
trafficRemoveWaypoint.Enabled = true;
PathMainMenu.createNewPath.Text = $"Continue Creating Path {pathNumber}";
float SetDriveSpeedForWaypoint()
{
float convertedSpeed;
if (SettingsMenu.speedUnits.SelectedItem == SpeedUnits.MPH)
{
//Logger.Log($"Original speed: {waypointSpeeds[waypointSpeed.Index]}{SettingsMenu.speedUnits.SelectedItem}");
convertedSpeed = MathHelper.ConvertMilesPerHourToMetersPerSecond(waypointSpeed.Value);
//Logger.Log($"Converted speed: {convertedSpeed}m/s");
}
else
{
//Logger.Log($"Original speed: {waypointSpeeds[waypointSpeed.Index]}{SettingsMenu.speedUnits.SelectedItem}");
convertedSpeed = MathHelper.ConvertKilometersPerHourToMetersPerSecond(waypointSpeed.Value);
//Logger.Log($"Converted speed: {convertedSpeed}m/s");
}
return convertedSpeed;
}
Blip CreateWaypointBlip()
{
var spriteNumericalEnum = pathIndex + 17; // 17 because the numerical value of these sprites are always 17 more than the path index
var blip = new Blip(Game.LocalPlayer.Character.Position)
{
Scale = 0.5f,
Sprite = (BlipSprite)spriteNumericalEnum
};
if (collectorWaypoint.Checked)
{
blip.Color = Color.Blue;
}
else if (stopWaypointType.Checked)
{
blip.Color = Color.Red;
}
else
{
blip.Color = Color.Green;
}
if (!SettingsMenu.mapBlips.Checked)
{
blip.Alpha = 0f;
}
return blip;
}
}
if (selectedItem == trafficRemoveWaypoint)
{
// Loop through each path and find the first one which isn't finished, then delete the path's last waypoint and corresponding blip
for (int i = 0; i < PathMainMenu.paths.Count; i++)
{
if (PathMainMenu.paths.ElementAtOrDefault(i) != null && PathMainMenu.paths[i].State == State.Creating)
{
Logger.Log($"[Path {i + 1}] {PathMainMenu.paths[i].Waypoints.Last().DrivingFlagType} waypoint removed");
PathMainMenu.paths[i].Waypoints.Last().Blip.Delete();
PathMainMenu.paths[i].Waypoints.Last().RemoveSpeedZone();
if (PathMainMenu.paths[i].Waypoints.Last().CollectorRadiusBlip)
{
PathMainMenu.paths[i].Waypoints.Last().CollectorRadiusBlip.Delete();
}
PathMainMenu.paths[i].Waypoints.RemoveAt(PathMainMenu.paths[i].Waypoints.IndexOf(PathMainMenu.paths[i].Waypoints.Last()));
ToggleTrafficEndPathMenuItem(i);
// If the path has no waypoints, disable the menu option to remove a waypoint
if (PathMainMenu.paths[i].Waypoints.Count == 0)
{
collectorWaypoint.Checked = true;
collectorWaypoint.Enabled = false;
speedZoneRadius.Enabled = true;
collectorRadius.Enabled = true;
trafficRemoveWaypoint.Enabled = false;
trafficEndPath.Enabled = false;
}
}
}
}
if (selectedItem == trafficEndPath)
{
// Loop through each path and find the first one which isn't finished
for (int i = 0; i < PathMainMenu.paths.Count; i++)
{
var currentPath = PathMainMenu.paths[i];
if (PathMainMenu.paths.ElementAtOrDefault(i) != null && currentPath.State == State.Creating)
{
Logger.Log($"[Path Creation] Path {currentPath.Number} finished with {currentPath.Waypoints.Count} waypoints.");
Game.DisplayNotification($"~o~Scene Manager\n~g~[Success]~w~ Path {i + 1} complete.");
currentPath.State = State.Finished;
currentPath.IsEnabled = true;
currentPath.Number = i + 1;
foreach (Waypoint waypoint in PathMainMenu.paths[i].Waypoints)
{
GameFiber WaypointVehicleCollectorFiber = new GameFiber(() => VehicleCollector.StartCollectingAtWaypoint(PathMainMenu.paths, PathMainMenu.paths[i], waypoint));
WaypointVehicleCollectorFiber.Start();
}
PathMainMenu.createNewPath.Text = "Create New Path";
PathMainMenu.BuildPathMenu();
PathMainMenu.pathMainMenu.RefreshIndex();
pathCreationMenu.RefreshIndex();
collectorWaypoint.Enabled = false;
collectorWaypoint.Checked = true;
speedZoneRadius.Enabled = true;
collectorRadius.Enabled = true;
trafficEndPath.Enabled = false;
PathMainMenu.pathMainMenu.Visible = true;
break;
}
}
}
}
private static void PathCreation_OnScrollerChanged(UIMenu sender, UIMenuScrollerItem scrollerItem, int first, int last)
{
if (scrollerItem == collectorRadius)
{
if (collectorRadius.Value > speedZoneRadius.Value)
{
while(collectorRadius.Value > speedZoneRadius.Value)
{
speedZoneRadius.ScrollToNextOption();
}
}
}
if(scrollerItem == speedZoneRadius)
{
if(speedZoneRadius.Value < collectorRadius.Value)
{
collectorRadius.Value = speedZoneRadius.Value;
}
}
}
private static void ToggleTrafficEndPathMenuItem(int pathIndex)
{
if (PathMainMenu.paths[pathIndex].Waypoints.Count > 0)
{
trafficEndPath.Enabled = true;
}
else
{
trafficEndPath.Enabled = false;
}
}
internal static void AddNewPathToPathsCollection(List<Path> paths, int pathIndex)
{
var pathNum = pathIndex + 1;
Logger.Log($"Creating path {pathNum}");
Game.DisplayNotification($"~o~Scene Manager\n~y~[Creating]~w~ Path {pathNum} started.");
paths.Insert(pathIndex, new Path(pathNum, State.Creating));
trafficRemoveWaypoint.Enabled = false;
trafficEndPath.Enabled = false;
}
}
}

View file

@ -0,0 +1,371 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
public enum DismissOption
{
FromPath = 0,
FromWaypoint = 1,
FromWorld = 2,
FromPlayer = 3
}
static class PathMainMenu
{
internal static List<Path> paths = new List<Path>() { };
private static List<string> dismissOptions = new List<string>() { "From path", "From waypoint", "From world" };
internal static UIMenu pathMainMenu = new UIMenu("Scene Manager", "~o~Path Manager Main Menu");
internal static UIMenuItem createNewPath;
internal static UIMenuItem deleteAllPaths = new UIMenuItem("Delete All Paths");
internal static UIMenuNumericScrollerItem<int> editPath;
internal static UIMenuListScrollerItem<string> directOptions = new UIMenuListScrollerItem<string>("Direct driver to path's", "", new[] { "First waypoint", "Nearest waypoint" });
internal static UIMenuNumericScrollerItem<int> directDriver;
internal static UIMenuListScrollerItem<string> dismissDriver = new UIMenuListScrollerItem<string>("Dismiss nearest driver", $"~b~From path: ~w~AI will be released from the path{Environment.NewLine}~b~From waypoint: ~w~AI will skip their current waypoint task{Environment.NewLine}~b~From world: ~w~AI will be removed from the world.", dismissOptions);
internal static UIMenuCheckboxItem disableAllPaths = new UIMenuCheckboxItem("Disable All Paths", false);
internal enum Delete
{
Single,
All
}
internal static void InstantiateMenu()
{
pathMainMenu.ParentMenu = MainMenu.mainMenu;
MenuManager.menuPool.Add(pathMainMenu);
}
internal static void BuildPathMenu()
{
// Need to unsubscribe from events, else there will be duplicate firings if the user left the menu and re-entered
ResetEventHandlerSubscriptions();
MenuManager.menuPool.CloseAllMenus();
pathMainMenu.Clear();
pathMainMenu.AddItem(createNewPath = new UIMenuItem("Create New Path"));
createNewPath.ForeColor = Color.Gold;
pathMainMenu.AddItem(editPath = new UIMenuNumericScrollerItem<int>("Edit Path", "", 1, paths.Count, 1));
editPath.Index = 0;
editPath.ForeColor = Color.Gold;
pathMainMenu.AddItem(disableAllPaths);
disableAllPaths.Enabled = true;
pathMainMenu.AddItem(deleteAllPaths);
deleteAllPaths.Enabled = true;
deleteAllPaths.ForeColor = Color.Gold;
pathMainMenu.AddItem(directOptions);
pathMainMenu.AddItem(directDriver = new UIMenuNumericScrollerItem<int>("Direct nearest driver to path", "", 1, paths.Count, 1));
directDriver.ForeColor = Color.Gold;
directDriver.Enabled = true;
pathMainMenu.AddItem(dismissDriver);
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;
}
MenuManager.menuPool.RefreshIndex();
void ResetEventHandlerSubscriptions()
{
pathMainMenu.OnItemSelect -= PathMenu_OnItemSelected;
pathMainMenu.OnCheckboxChange -= PathMenu_OnCheckboxChange;
pathMainMenu.OnItemSelect += PathMenu_OnItemSelected;
pathMainMenu.OnCheckboxChange += PathMenu_OnCheckboxChange;
}
}
private static bool VehicleAndDriverValid(this Vehicle v)
{
if (v && v.HasDriver && v.Driver && v.Driver.IsAlive)
{
return true;
}
else
{
return false;
}
}
internal static void DeletePath(Path path, Delete pathsToDelete)
{
//Game.LogTrivial($"Preparing to delete path {path.Number}");
RemoveVehiclesFromPath();
RemoveBlipsAndYieldZones();
//Game.LogTrivial($"Clearing path waypoints");
path.Waypoints.Clear();
// Manipulating the menu to reflect specific paths being deleted
if (pathsToDelete == Delete.Single)
{
paths.Remove(path);
UpdatePathNumbers();
UpdatePathBlips();
BuildPathMenu();
pathMainMenu.Visible = true;
Game.LogTrivial($"Path {path.Number} deleted successfully.");
Game.DisplayNotification($"~o~Scene Manager\n~w~Path {path.Number} deleted.");
}
EditPathMenu.editPathMenu.Reset(true, true);
EditPathMenu.disablePath.Enabled = true;
void RemoveVehiclesFromPath()
{
//Game.LogTrivial($"Removing all vehicles on the path");
var pathVehicles = VehicleCollector.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)
{
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(cv.Vehicle, 1f, 1, true);
}
cv.StoppedAtWaypoint = false;
if (cv.Driver.GetAttachedBlip())
{
cv.Driver.GetAttachedBlip().Delete();
}
cv.Driver.Dismiss();
cv.Vehicle.IsSirenOn = false;
cv.Vehicle.IsSirenSilent = true;
cv.Vehicle.Dismiss();
//Game.LogTrivial($"{cv.vehicle.Model.Name} cleared from path {cv.path}");
VehicleCollector.collectedVehicles.Remove(cv);
}
}
void RemoveBlipsAndYieldZones()
{
//Game.LogTrivial($"Removing waypoint blips and yield zones.");
foreach (Waypoint waypoint in path.Waypoints)
{
if (waypoint.SpeedZone != 0)
{
waypoint.RemoveSpeedZone();
}
if (waypoint.Blip)
{
waypoint.Blip.Delete();
}
if (waypoint.CollectorRadiusBlip)
{
waypoint.CollectorRadiusBlip.Delete();
}
}
}
void UpdatePathBlips()
{
foreach (Path p in paths)
{
foreach (Waypoint waypoint in p.Waypoints)
{
var blipColor = waypoint.Blip.Color;
waypoint.Blip.Sprite = (BlipSprite)paths.IndexOf(p) + 17;
waypoint.Blip.Color = blipColor;
}
}
}
void UpdatePathNumbers()
{
for (int i = 0; i < paths.Count; i++)
{
paths[i].Number = i + 1;
}
}
}
private static void PathMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == createNewPath)
{
pathMainMenu.Visible = false;
PathCreationMenu.pathCreationMenu.Visible = true;
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($"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], Delete.All);
}
paths.Clear();
BuildPathMenu();
pathMainMenu.Visible = true;
Game.LogTrivial($"All paths deleted");
Game.DisplayNotification($"~o~Scene Manager\n~w~All paths deleted.");
}
if (selectedItem == directDriver)
{
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();
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();
// 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();
//Logger.Log($"Collected vehicle is {collectedVehicle.Vehicle.Model.Name}");
}
if (collectedVehicle == null)
{
return;
}
collectedVehicle.Directed = true;
collectedVehicle.Driver.Tasks.Clear();
//Logger.Log($"Collected vehicle properties: Dismissed [{collectedVehicle.Dismissed}], Directed [{collectedVehicle.Directed}], StopppedAtWaypoint [{collectedVehicle.StoppedAtWaypoint}]");
if (directOptions.SelectedItem == "First waypoint")
{
GameFiber.StartNew(() =>
{
AITasking.AssignWaypointTasks(collectedVehicle, path, firstWaypoint);
});
}
else
{
if (nearestWaypoint != null)
{
GameFiber.StartNew(() =>
{
AITasking.AssignWaypointTasks(collectedVehicle, path, nearestWaypoint);
});
}
}
}
}
if (selectedItem == dismissDriver)
{
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)
{
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();
}
}
}
}
}
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.");
}
}
}
private static void Draw3DWaypointOnPlayer()
{
GameFiber.StartNew(() =>
{
while (SettingsMenu.threeDWaypoints.Checked)
{
if (PathCreationMenu.pathCreationMenu.Visible)
{
if (PathCreationMenu.collectorWaypoint.Checked)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, (float)PathCreationMenu.collectorRadius.Value * 2, (float)PathCreationMenu.collectorRadius.Value * 2, 1f, 80, 130, 255, 80, false, false, 2, false, 0, 0, false);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, (float)PathCreationMenu.speedZoneRadius.Value * 2, (float)PathCreationMenu.speedZoneRadius.Value * 2, 1f, 255, 185, 80, 80, false, false, 2, false, 0, 0, false);
}
else if (PathCreationMenu.stopWaypointType.Checked)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, 1f, 255, 65, 65, 80, false, false, 2, false, 0, 0, false);
}
else
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, 1f, 65, 255, 65, 80, false, false, 2, false, 0, 0, false);
}
}
else
{
break;
}
GameFiber.Yield();
}
});
}
}
}

View file

@ -0,0 +1,90 @@
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
using System;
namespace SceneManager
{
class SettingsMenu
{
internal static UIMenu settingsMenu = new UIMenu("Scene Manager", "~o~Plugin Settings");
internal static UIMenuCheckboxItem threeDWaypoints = new UIMenuCheckboxItem("Enable 3D Waypoints", Settings.Enable3DWaypoints),
mapBlips = new UIMenuCheckboxItem("Enable Map Blips", Settings.EnableMapBlips),
hints = new UIMenuCheckboxItem("Enable Hints", Settings.EnableHints);
private static SpeedUnits[] speedArray = {SpeedUnits.MPH, SpeedUnits.KPH };
internal static UIMenuListScrollerItem<SpeedUnits> speedUnits = new UIMenuListScrollerItem<SpeedUnits>("Speed Unit of Measure", "", new[] { SpeedUnits.MPH, SpeedUnits.KPH });
internal static UIMenuItem saveSettings = new UIMenuItem("Save settings to .ini", "Updates the plugin's .ini file with the current settings so the next time the plugin is loaded, it will use these settings.");
internal static void InstantiateMenu()
{
settingsMenu.ParentMenu = MainMenu.mainMenu;
MenuManager.menuPool.Add(settingsMenu);
}
internal static void BuildSettingsMenu()
{
settingsMenu.AddItem(threeDWaypoints);
settingsMenu.AddItem(mapBlips);
settingsMenu.AddItem(hints);
settingsMenu.AddItem(speedUnits);
speedUnits.Index = Array.IndexOf(speedArray, Settings.SpeedUnit);
settingsMenu.AddItem(saveSettings);
saveSettings.ForeColor = System.Drawing.Color.Gold;
settingsMenu.OnCheckboxChange += SettingsMenu_OnCheckboxChange;
settingsMenu.OnScrollerChange += SettingsMenu_OnScrollerChange;
settingsMenu.OnItemSelect += SettingsMenu_OnItemSelected;
}
private static void SettingsMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if(selectedItem == saveSettings)
{
Settings.UpdateSettings(threeDWaypoints.Checked, mapBlips.Checked, hints.Checked, speedUnits.SelectedItem);
Game.DisplayHelp($"Settings saved");
}
}
private static void SettingsMenu_OnCheckboxChange(UIMenu sender, UIMenuCheckboxItem checkboxItem, bool @checked)
{
if (checkboxItem == mapBlips)
{
if (mapBlips.Checked)
{
foreach(Path path in PathMainMenu.paths)
{
foreach(Waypoint wp in path.Waypoints)
{
wp.EnableBlip();
}
}
}
else
{
foreach (Path path in PathMainMenu.paths)
{
foreach (Waypoint wp in path.Waypoints)
{
wp.DisableBlip();
}
}
}
}
if (checkboxItem == hints)
{
Hints.Enabled = hints.Checked ? true : false;
}
}
private static void SettingsMenu_OnScrollerChange(UIMenu sender, UIMenuScrollerItem scrollerItem, int oldIndex, int newIndex)
{
if (scrollerItem == speedUnits)
{
// Clear the menu and rebuild it to reflect the menu item text change
PathCreationMenu.pathCreationMenu.Clear();
PathCreationMenu.BuildPathCreationMenu();
}
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using Rage;
namespace SceneManager
{
class Barrier
{
internal Rage.Object Object { get; }
internal Model @Model{ get; }
internal Vector3 Position { get; }
internal float Rotation { get; }
internal Barrier(Rage.Object barrier, Vector3 barrierPosition, float barrierRotation)
{
Object = barrier;
@Model = barrier.Model;
Position = barrierPosition;
Rotation = barrierRotation;
}
}
}

View file

@ -0,0 +1,177 @@
using Rage;
using System.Linq;
namespace SceneManager
{
internal class CollectedVehicle
{
internal Ped Driver { get; set; }
internal Vehicle Vehicle { get; set; }
internal Path Path { get; set; }
internal Waypoint CurrentWaypoint { get; set; }
internal Waypoint NextWaypoint { get; private set; }
internal bool StoppedAtWaypoint { get; set; } = false;
internal bool Dismissed { get; set; } = false;
internal bool Directed { get; set; } = false;
internal bool SkipWaypoint { get; set; } = false;
internal bool ReadyForDirectTasks { get; set; } = true;
internal CollectedVehicle(Vehicle vehicle, Path path, Waypoint currentWaypoint)
{
Vehicle = vehicle;
Driver = Vehicle.Driver;
Path = path;
CurrentWaypoint = currentWaypoint;
SetPersistence();
}
internal CollectedVehicle(Vehicle vehicle, Path path)
{
Vehicle = vehicle;
Driver = vehicle.Driver;
Path = path;
SetPersistence();
}
internal void SetPersistence()
{
Vehicle.IsPersistent = true;
Driver.IsPersistent = true;
Driver.BlockPermanentEvents = true;
//Logger.Log($"{Vehicle.Model.Name} and driver are now persistent.");
}
internal void Dismiss(DismissOption dismissOption = DismissOption.FromPath)
{
if (!Vehicle || !Driver)
{
return;
}
if (dismissOption == DismissOption.FromWorld)
{
DismissFromWorld();
return;
}
if (dismissOption == DismissOption.FromPlayer)
{
Dismissed = true;
if (Driver)
{
Driver.Dismiss();
}
if (Vehicle)
{
Vehicle.Dismiss();
}
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
VehicleCollector.collectedVehicles.Remove(this);
return;
}
if(StoppedAtWaypoint)
{
//Logger.Log($"Unstucking {Vehicle.Model.Name}");
StoppedAtWaypoint = false;
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
Driver.Tasks.CruiseWithVehicle(5f);
}
Driver.Tasks.Clear();
if (dismissOption == DismissOption.FromWaypoint)
{
DismissFromWaypoint();
}
if (dismissOption == DismissOption.FromPath)
{
DismissFromPath();
}
void DismissFromWorld()
{
Game.LogTrivial($"Dismissed {Vehicle.Model.Name} from the world");
while (Vehicle.HasOccupants)
{
foreach (Ped occupant in Vehicle.Occupants)
{
occupant.Dismiss();
occupant.Delete();
}
GameFiber.Yield();
}
Vehicle.Delete();
}
void DismissFromWaypoint()
{
if (CurrentWaypoint == null || Path == null)
{
Logger.Log($"CurrentWaypoint or Path are null");
}
else if (CurrentWaypoint?.Number != Path?.Waypoints.Count)
{
Logger.Log($"Dismissed from waypoint.");
SkipWaypoint = true;
}
else if (CurrentWaypoint?.Number == Path?.Waypoints.Count)
{
DismissFromPath();
}
}
void DismissFromPath()
{
Logger.Log($"Dismissing from path");
Dismissed = true;
// Check if the vehicle is near any of the path's collector waypoints
GameFiber.StartNew(() =>
{
var nearestCollectorWaypoint = Path.Waypoints.Where(wp => wp.IsCollector).OrderBy(wp => Vehicle.DistanceTo2D(wp.Position)).FirstOrDefault();
if (nearestCollectorWaypoint != null)
{
// Enabling this will keep the menu, but the dismissed vehicle is immediately re - collected
while (nearestCollectorWaypoint != null && Vehicle && Vehicle.HasDriver && Driver && Driver.IsAlive && Vehicle.FrontPosition.DistanceTo2D(nearestCollectorWaypoint.Position) <= nearestCollectorWaypoint.CollectorRadius)
{
//Game.LogTrivial($"{Vehicle.Model.Name} is within 2x collector radius, cannot be fully dismissed yet.");
GameFiber.Yield();
}
}
else
{
Logger.Log($"Nearest collector is null");
}
if (!Vehicle || !Driver)
{
return;
}
if (!Directed)
{
VehicleCollector.collectedVehicles.Remove(this);
Logger.Log($"{Vehicle.Model.Name} dismissed successfully.");
if (Driver)
{
if (Driver.GetAttachedBlip())
{
Driver.GetAttachedBlip().Delete();
}
Driver.BlockPermanentEvents = false;
Driver.Dismiss();
}
if (Vehicle)
{
Vehicle.IsSirenOn = false;
Vehicle.IsSirenSilent = true;
Vehicle.Dismiss();
}
}
});
}
}
}
}

View file

@ -0,0 +1,105 @@
using Rage;
using System.Collections.Generic;
using System.Drawing;
namespace SceneManager
{
public class Path
{
internal int Number { get; set; }
internal bool IsEnabled { get; set; }
internal State State { get; set; }
internal List<Waypoint> Waypoints = new List<Waypoint>();
internal Path(int pathNum, State pathState)
{
Number = pathNum;
State = pathState;
DrawLinesBetweenWaypoints();
}
private void LowerWaypointBlipsOpacity()
{
foreach (Waypoint wp in Waypoints)
{
wp.Blip.Alpha = 0.5f;
if (wp.CollectorRadiusBlip)
{
wp.CollectorRadiusBlip.Alpha = 0.25f;
}
}
}
private void RestoreWaypointBlipsOpacity()
{
foreach (Waypoint wp in Waypoints)
{
if (wp.Blip)
{
wp.Blip.Alpha = 1.0f;
if (wp.CollectorRadiusBlip)
{
wp.CollectorRadiusBlip.Alpha = 0.5f;
}
}
}
}
internal void DisablePath()
{
IsEnabled = false;
foreach(Waypoint wp in Waypoints)
{
wp.RemoveSpeedZone();
}
if (SettingsMenu.mapBlips.Checked)
{
LowerWaypointBlipsOpacity();
}
}
internal void EnablePath()
{
IsEnabled = true;
foreach (Waypoint wp in Waypoints)
{
if (wp.IsCollector)
{
wp.AddSpeedZone();
}
}
if (SettingsMenu.mapBlips.Checked)
{
RestoreWaypointBlipsOpacity();
}
}
internal void DrawLinesBetweenWaypoints()
{
GameFiber.StartNew(() =>
{
while (SettingsMenu.threeDWaypoints.Checked)
{
if (MenuManager.menuPool.IsAnyMenuOpen())
{
for (int i = 0; i < Waypoints.Count; i++)
{
if (i != Waypoints.Count - 1)
{
if (Waypoints[i + 1].IsStopWaypoint)
{
Debug.DrawLine(Waypoints[i].Position, Waypoints[i + 1].Position, Color.Orange);
}
else
{
Debug.DrawLine(Waypoints[i].Position, Waypoints[i + 1].Position, Color.Green);
}
}
}
}
GameFiber.Yield();
}
});
}
}
}

View file

@ -0,0 +1,290 @@
using Rage;
using System.Drawing;
using System.Linq;
namespace SceneManager
{
public enum DrivingFlagType
{
Normal = 263075,
Direct = 17040259
}
public class Waypoint
{
internal Path Path { get; set; }
internal int Number { get; set; }
internal Vector3 Position { get; set; }
internal float Speed { get; set; }
internal DrivingFlagType DrivingFlagType { get; private set; }
internal bool IsStopWaypoint { get; set; }
internal Blip Blip { get; }
internal bool IsCollector { get; set; }
internal float CollectorRadius { get; set; }
internal Blip CollectorRadiusBlip { get; set; }
internal float SpeedZoneRadius { get; set; }
internal uint SpeedZone { get; set; }
internal bool EnableWaypointMarker { get; set; } = true;
internal bool EnableEditMarker { get; set; }
internal Waypoint(Path path, int waypointNum, Vector3 waypointPos, float speed, DrivingFlagType drivingFlag, bool stopWaypoint, Blip waypointBlip, bool collector = false, float collectorRadius = 1, float speedZoneRadius = 5)
{
Path = path;
Number = waypointNum;
Position = waypointPos;
Speed = speed;
DrivingFlagType = drivingFlag;
IsStopWaypoint = stopWaypoint;
Blip = waypointBlip;
IsCollector = collector;
CollectorRadius = collectorRadius;
SpeedZoneRadius = speedZoneRadius;
if (collector)
{
AddSpeedZone();
CollectorRadiusBlip = new Blip(waypointBlip.Position, collectorRadius)
{
Color = waypointBlip.Color,
};
if (SettingsMenu.mapBlips.Checked)
{
CollectorRadiusBlip.Alpha = 0.5f;
}
else
{
CollectorRadiusBlip.Alpha = 0f;
}
}
DrawWaypointMarker();
}
internal void UpdateWaypoint(Waypoint currentWaypoint, DrivingFlagType drivingFlag, bool stopWaypoint, float speed, bool collectorWaypointChecked, float collectorRadius, float speedZoneRadius, bool updateWaypointPositionChecked)
{
if(IsStopWaypoint != stopWaypoint)
{
UpdateIfStopWaypoint();
}
DrivingFlagType = drivingFlag;
UpdateWaypointSpeed(speed);
UpdateCollectorOptions();
if (updateWaypointPositionChecked)
{
UpdateWaypointPosition(Game.LocalPlayer.Character.Position);
}
void UpdateIfStopWaypoint()
{
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))
{
// Logger.Log($"Setting StoppedAtWaypoint to false for {cv.Vehicle.Model.Name}");
cv.Dismiss(DismissOption.FromWaypoint);
}
}
else if(stopWaypoint && !IsStopWaypoint)
{
Blip.Color = Color.Red;
}
IsStopWaypoint = stopWaypoint;
}
void UpdateWaypointSpeed(float newWaypointSpeed)
{
Speed = newWaypointSpeed;
}
void UpdateCollectorOptions()
{
if (collectorWaypointChecked)
{
IsCollector = true;
RemoveSpeedZone();
SpeedZone = World.AddSpeedZone(currentWaypoint.Position, SpeedZoneRadius, speed);
Blip.Color = Color.Blue;
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Alpha = 0.5f;
CollectorRadiusBlip.Scale = collectorRadius;
}
else
{
CollectorRadiusBlip = new Blip(Blip.Position, collectorRadius)
{
Color = Blip.Color,
Alpha = 0.5f
};
}
CollectorRadius = collectorRadius;
SpeedZoneRadius = speedZoneRadius;
}
else
{
IsCollector = false;
if (IsStopWaypoint)
{
Blip.Color = Color.Red;
}
else
{
Blip.Color = Color.Green;
}
RemoveSpeedZone();
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Delete();
}
}
}
void UpdateWaypointPosition(Vector3 newWaypointPosition)
{
Position = newWaypointPosition;
RemoveSpeedZone();
AddSpeedZone();
UpdateWaypointBlipPosition();
}
void UpdateWaypointBlipPosition()
{
if (Blip)
{
Blip.Position = Game.LocalPlayer.Character.Position;
}
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Position = Game.LocalPlayer.Character.Position;
}
}
}
internal void Remove()
{
if (Blip)
{
Blip.Delete();
}
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Delete();
}
RemoveSpeedZone();
}
internal void AddSpeedZone()
{
SpeedZone = World.AddSpeedZone(Position, SpeedZoneRadius, Speed);
}
internal void RemoveSpeedZone()
{
World.RemoveSpeedZone(SpeedZone);
}
internal void DrawWaypointMarker()
{
// This is called once when the waypoint is created
GameFiber.StartNew(() =>
{
while (true)
{
if(SettingsMenu.threeDWaypoints.Checked && EnableWaypointMarker && Path.Waypoints.Contains(this))
{
if (EditWaypointMenu.editWaypointMenu.Visible && PathMainMenu.editPath.Value == Path.Number && EditWaypointMenu.editWaypoint.Value == Number)
{
if (EditWaypointMenu.collectorWaypoint.Checked)
{
if (EditWaypointMenu.updateWaypointPosition.Checked)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, (float)EditWaypointMenu.changeCollectorRadius.Value * 2, (float)EditWaypointMenu.changeCollectorRadius.Value * 2, 1f, 80, 130, 255, 100, false, false, 2, false, 0, 0, false);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, (float)EditWaypointMenu.changeSpeedZoneRadius.Value * 2, (float)EditWaypointMenu.changeSpeedZoneRadius.Value * 2, 1f, 255, 185, 80, 100, false, false, 2, false, 0, 0, false);
}
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, (float)EditWaypointMenu.changeCollectorRadius.Value * 2, (float)EditWaypointMenu.changeCollectorRadius.Value * 2, 2f, 80, 130, 255, 100, false, false, 2, false, 0, 0, false);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, (float)EditWaypointMenu.changeSpeedZoneRadius.Value * 2, (float)EditWaypointMenu.changeSpeedZoneRadius.Value * 2, 2f, 255, 185, 80, 100, false, false, 2, false, 0, 0, false);
}
else if (EditWaypointMenu.stopWaypointType.Checked)
{
if (EditWaypointMenu.updateWaypointPosition.Checked)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, 1f, 255, 65, 65, 100, false, false, 2, false, 0, 0, false);
}
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, 2f, 255, 65, 65, 100, false, false, 2, false, 0, 0, false);
}
else
{
if (EditWaypointMenu.updateWaypointPosition.Checked)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, 1f, 65, 255, 65, 100, false, false, 2, false, 0, 0, false);
}
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, 2f, 65, 255, 65, 100, false, false, 2, false, 0, 0, false);
}
}
else if ((Path.State == State.Finished && MenuManager.menuPool.IsAnyMenuOpen()) || (Path.State == State.Creating && PathCreationMenu.pathCreationMenu.Visible))
{
float markerHeight = 1f;
if ((PathMainMenu.directDriver.Selected && PathMainMenu.directDriver.Value == Path.Number) || PathMainMenu.editPath.Selected && PathMainMenu.editPath.Value == Path.Number && (PathMainMenu.pathMainMenu.Visible || EditPathMenu.editPathMenu.Visible))
{
markerHeight = 2f;
}
if (IsCollector)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, CollectorRadius * 2, CollectorRadius * 2, markerHeight, 80, 130, 255, 100, false, false, 2, false, 0, 0, false);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, SpeedZoneRadius * 2, SpeedZoneRadius * 2, markerHeight, 255, 185, 80, 100, false, false, 2, false, 0, 0, false);
}
else if (IsStopWaypoint)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, markerHeight, 255, 65, 65, 100, false, false, 2, false, 0, 0, false);
}
else
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position.X, Position.Y, Position.Z - 1, 0, 0, 0, 0, 0, 0, 1f, 1f, markerHeight, 65, 255, 65, 100, false, false, 2, false, 0, 0, false);
}
}
}
GameFiber.Yield();
}
});
}
internal void EnableBlip()
{
if(!Path.IsEnabled)
{
if (Blip)
{
Blip.Alpha = 0.5f;
}
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Alpha = 0.25f;
}
}
else
{
if (Blip)
{
Blip.Alpha = 1.0f;
}
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Alpha = 0.5f;
}
}
}
internal void DisableBlip()
{
if (Blip)
{
Blip.Alpha = 0;
}
if (CollectorRadiusBlip)
{
CollectorRadiusBlip.Alpha = 0;
}
}
}
}

View file

@ -1,24 +0,0 @@
using System.Collections.Generic;
namespace SceneManager
{
public class PathData
{
public int PathNum;
public bool PathFinished;
public List<WaypointData> WaypointData = new List<WaypointData>() { };
public PathData(int pathNum, bool pathFinished, List<WaypointData> waypointData)
{
PathNum = pathNum;
PathFinished = pathFinished;
WaypointData = waypointData;
}
public PathData(int pathNum, bool pathFinished)
{
PathNum = pathNum;
PathFinished = pathFinished;
}
}
}

View file

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.0.0")] [assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("1.7.0.0")] [assembly: AssemblyFileVersion("2.0.0.0")]

View file

@ -38,7 +38,6 @@
<HintPath>D:\Program Files\Rockstar Games\Grand Theft Auto V\plugins\LSPDFR\RagePluginHookSDK.dll</HintPath> <HintPath>D:\Program Files\Rockstar Games\Grand Theft Auto V\plugins\LSPDFR\RagePluginHookSDK.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Collections.Immutable, Version=1.2.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Management" /> <Reference Include="System.Management" />
@ -51,14 +50,26 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ControlledVehicle.cs" /> <Compile Include="AITasking.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Object Classes\Barrier.cs" />
<Compile Include="Hints.cs" />
<Compile Include="Menus\BarrierMenu.cs" />
<Compile Include="Object Classes\CollectedVehicle.cs" />
<Compile Include="Menus\EditPathMenu.cs" />
<Compile Include="Menus\EditWaypointMenu.cs" />
<Compile Include="EntryPoint.cs" /> <Compile Include="EntryPoint.cs" />
<Compile Include="PathData.cs" /> <Compile Include="GetUserInput.cs" />
<Compile Include="Menus\MainMenu.cs" />
<Compile Include="Menus\MenuManager.cs" />
<Compile Include="Object Classes\Path.cs" />
<Compile Include="Menus\PathCreationMenu.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TrafficMenu.cs" /> <Compile Include="Menus\PathMainMenu.cs" />
<Compile Include="TrafficPathing.cs" /> <Compile Include="Menus\SettingsMenu.cs" />
<Compile Include="Verification.cs" /> <Compile Include="Settings.cs" />
<Compile Include="WaypointData.cs" /> <Compile Include="VehicleCollector.cs" />
<Compile Include="Object Classes\Waypoint.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

68
SceneManager/Settings.cs Normal file
View file

@ -0,0 +1,68 @@
using Rage;
using System.Collections.Generic;
using System.Windows.Forms;
namespace SceneManager
{
internal enum State
{
Uninitialized,
Creating,
Finished
}
internal enum SpeedUnits
{
MPH,
KPH
}
internal static class Settings
{
internal static readonly InitializationFile ini = new InitializationFile("Plugins/SceneManager.ini");
internal static Keys ToggleKey = Keys.T;
internal static Keys ModifierKey = Keys.LShiftKey;
internal static ControllerButtons ToggleButton = ControllerButtons.Y;
internal static ControllerButtons ModifierButton = ControllerButtons.A;
internal static bool Enable3DWaypoints = true;
internal static bool EnableMapBlips = true;
internal static bool EnableHints = true;
internal static SpeedUnits SpeedUnit = SpeedUnits.MPH;
internal static float BarrierPlacementDistance = 30f;
internal static List<string> barrierKeys = new List<string>();
internal static List<string> barrierValues = new List<string>();
internal static void LoadSettings()
{
Game.LogTrivial("Loading SceneManager.ini settings");
ini.Create();
ToggleKey = ini.ReadEnum("Keybindings", "ToggleKey", Keys.T);
ModifierKey = ini.ReadEnum("Keybindings", "ModifierKey", Keys.LShiftKey);
ToggleButton = ini.ReadEnum("Keybindings", "ToggleButton", ControllerButtons.A);
ModifierButton = ini.ReadEnum("Keybindings", "ModifierButton", ControllerButtons.DPadDown);
Enable3DWaypoints = ini.ReadBoolean("Other Settings", "Enable3DWaypoints", true);
EnableMapBlips = ini.ReadBoolean("Other Settings", "EnableMapBlips", true);
EnableHints = ini.ReadBoolean("Other Settings", "EnableHints", true);
SpeedUnit = ini.ReadEnum("Other Settings", "SpeedUnits", SpeedUnits.MPH);
BarrierPlacementDistance = ini.ReadInt32("Other Settings", "BarrierPlacementDistance", 30);
foreach(string key in ini.GetKeyNames("Barriers"))
{
barrierKeys.Add(key.Trim());
var m = new Model(ini.ReadString("Barriers", key));
if (m.IsValid)
barrierValues.Add(m.Name);
}
}
internal static void UpdateSettings(bool threeDWaypointsEnabled, bool mapBlipsEnabled, bool hintsEnabled, SpeedUnits unit)
{
ini.Write("Other Settings", "Enable3DWaypoints", threeDWaypointsEnabled);
ini.Write("Other Settings", "EnableMapBlips", mapBlipsEnabled);
ini.Write("Other Settings", "EnableHints", hintsEnabled);
ini.Write("Other Settings", "SpeedUnits", unit);
}
}
}

View file

@ -1,645 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
public static class TrafficMenu
{
private static MenuPool _menuPool;
private static UIMenu mainMenu, trafficMenu, coneMenu, pathCreationMenu;
private static UIMenuItem navigateToTrafficMenu, navigateToConeMenu, createNewPath, trafficAddWaypoint, trafficRemoveWaypoint, trafficEndPath, deleteAllPaths, addCone, removeLastCone, removeNearestCone, removeAllCones, dismissDriver;
private static UIMenuListItem deleteSinglePath, selectCone, waypointType, waypointSpeed, directDriver;
private static List<dynamic> pathsNum = new List<dynamic>() { };
private static List<PathData> paths = new List<PathData>() { };
private static List<Rage.Object> cones = new List<Rage.Object>() { };
private static List<dynamic> waypointSpeeds = new List<dynamic>() { 5, 10, 15, 20, 30, 40, 50, 60, 70 };
private static List<dynamic> waypointTypes = new List<dynamic>() { "Drive To", "Stop" };
private static VehicleDrivingFlags[] drivingFlags = new VehicleDrivingFlags[] { VehicleDrivingFlags.Normal, VehicleDrivingFlags.StopAtDestination };
private static List<dynamic> coneList = new List<dynamic>() { "Large Striped Cone", "Large Cone", "Medium Striped Cone", "Medium Cone", "Roadpole A", "Roadpole B" };
private static string[] coneObjectNames = new string[] { "prop_mp_cone_01", "prop_roadcone01c", "prop_mp_cone_02", "prop_mp_cone_03", "prop_roadpole_01a", "prop_roadpole_01b" };
private static Rage.Object shadowCone;
public static void CheckUserInput()
{
#pragma warning disable CS0618 // Type or member is obsolete, clear NUI squiggles in BuildMenu
AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler;
BuildMenu();
while (true)
{
// Keyboard
if (EntryPoint.Settings.ModifierKey == System.Windows.Forms.Keys.None)
{
if (Game.LocalPlayer.Character.IsOnFoot && Game.IsKeyDown(EntryPoint.Settings.ToggleKey) && !trafficMenu.Visible && !pathCreationMenu.Visible)
{
mainMenu.Visible = !mainMenu.Visible;
}
}
else if (Game.LocalPlayer.Character.IsOnFoot && Game.IsKeyDownRightNow(EntryPoint.Settings.ModifierKey) && Game.IsKeyDown(EntryPoint.Settings.ToggleKey) && !trafficMenu.Visible && !pathCreationMenu.Visible)
{
mainMenu.Visible = !mainMenu.Visible;
}
// Controller
if (EntryPoint.Settings.ModifierButton == ControllerButtons.None)
{
if (Game.LocalPlayer.Character.IsOnFoot && Game.IsControllerButtonDown(EntryPoint.Settings.ToggleButton) && !trafficMenu.Visible && !pathCreationMenu.Visible)
{
mainMenu.Visible = !mainMenu.Visible;
}
}
else if (Game.LocalPlayer.Character.IsOnFoot && Game.IsControllerButtonDownRightNow(EntryPoint.Settings.ModifierButton) && Game.IsControllerButtonDown(EntryPoint.Settings.ToggleButton) && !trafficMenu.Visible && !pathCreationMenu.Visible)
{
mainMenu.Visible = !mainMenu.Visible;
}
_menuPool.ProcessMenus();
GameFiber.Yield();
}
}
private static void BuildMenu()
{
_menuPool = new MenuPool();
// Instantiate menus
mainMenu = new UIMenu("Scene Manager", "");
trafficMenu = new UIMenu("Scene Manager", "~o~Traffic Menu");
trafficMenu.ParentMenu = mainMenu;
pathCreationMenu = new UIMenu("Scene Manager", "~o~Path Creation");
pathCreationMenu.ParentMenu = trafficMenu;
coneMenu = new UIMenu("Scene Manager", "~o~Cone Menu");
coneMenu.ParentMenu = mainMenu;
// Add menus to the pool
_menuPool.Add(mainMenu);
_menuPool.Add(trafficMenu);
_menuPool.Add(coneMenu);
_menuPool.Add(pathCreationMenu);
// Add menu items to main menu and navigate each item to a submenu
mainMenu.AddItem(navigateToTrafficMenu = new UIMenuItem("~o~Traffic Menu"));
mainMenu.BindMenuToItem(trafficMenu, navigateToTrafficMenu);
mainMenu.AddItem(navigateToConeMenu = new UIMenuItem("~o~Cone Menu"));
mainMenu.BindMenuToItem(coneMenu, navigateToConeMenu);
// Add menu items to trafficMenu
trafficMenu.AddItem(createNewPath = new UIMenuItem("Create New Path"));
trafficMenu.AddItem(deleteSinglePath = new UIMenuListItem("Delete Path", pathsNum, 0));
deleteSinglePath.Enabled = false;
trafficMenu.AddItem(deleteAllPaths = new UIMenuItem("Delete All Paths"));
deleteAllPaths.Enabled = false;
trafficMenu.AddItem(directDriver = new UIMenuListItem("Direct nearest driver to path", pathsNum, 0));
directDriver.Enabled = false;
trafficMenu.AddItem(dismissDriver = new UIMenuItem("Dismiss nearest driver"));
// Add menu items to pathCreationMenu
pathCreationMenu.AddItem(waypointType = new UIMenuListItem("Waypoint Type", waypointTypes, 0));
pathCreationMenu.AddItem(waypointSpeed = new UIMenuListItem("Waypoint Speed", waypointSpeeds, 0));
pathCreationMenu.AddItem(trafficAddWaypoint = new UIMenuItem("Add waypoint"));
pathCreationMenu.AddItem(trafficRemoveWaypoint = new UIMenuItem("Remove last waypoint"));
trafficRemoveWaypoint.Enabled = false;
pathCreationMenu.AddItem(trafficEndPath = new UIMenuItem("End path creation"));
// Add menu items to coneMenu
coneMenu.AddItem(selectCone = new UIMenuListItem("Select Cone", coneList, 0));
coneMenu.AddItem(addCone = new UIMenuItem("Add Cone"));
coneMenu.AddItem(removeLastCone = new UIMenuItem("Remove Last Cone"));
removeLastCone.Enabled = false;
coneMenu.AddItem(removeNearestCone = new UIMenuItem("Remove Nearest Cone"));
removeNearestCone.Enabled = false;
coneMenu.AddItem(removeAllCones = new UIMenuItem("Remove All Cones"));
removeAllCones.Enabled = false;
mainMenu.RefreshIndex();
trafficMenu.RefreshIndex();
pathCreationMenu.RefreshIndex();
coneMenu.RefreshIndex();
// Event handlers for when a menu item is selected
mainMenu.OnItemSelect += MainMenu_OnItemSelected;
trafficMenu.OnItemSelect += TrafficMenu_OnItemSelected;
pathCreationMenu.OnItemSelect += PathCreation_OnItemSelected;
coneMenu.OnListChange += ConeMenu_OnListChange;
coneMenu.OnItemSelect += ConeMenu_OnItemSelected;
// Disable mouse control for the menus
mainMenu.MouseControlsEnabled = false;
mainMenu.AllowCameraMovement = true;
trafficMenu.MouseControlsEnabled = false;
trafficMenu.AllowCameraMovement = true;
pathCreationMenu.MouseControlsEnabled = false;
pathCreationMenu.AllowCameraMovement = true;
coneMenu.MouseControlsEnabled = false;
coneMenu.AllowCameraMovement = true;
}
private static void RebuildTrafficMenu()
{
// The traffic menu has to be "refreshed" in some instances to show changes, so we do that here
_menuPool.CloseAllMenus();
trafficMenu.Clear();
trafficMenu.AddItem(createNewPath = new UIMenuItem("Create New Path"));
trafficMenu.AddItem(deleteSinglePath = new UIMenuListItem("Delete Path", pathsNum, 0));
trafficMenu.AddItem(deleteAllPaths = new UIMenuItem("Delete All Paths"));
trafficMenu.AddItem(directDriver = new UIMenuListItem("Direct nearest driver to path", pathsNum, 0));
trafficMenu.AddItem(dismissDriver = new UIMenuItem("Dismiss nearest driver"));
if (paths.Count == 8)
{
createNewPath.Enabled = false;
}
if (paths.Count == 0)
{
deleteSinglePath.Enabled = false;
deleteAllPaths.Enabled = false;
directDriver.Enabled = false;
}
_menuPool.RefreshIndex();
trafficMenu.Visible = true;
}
private static void DeletePath(PathData path, int index, UIMenuItem selectedItem)
{
// 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.WaypointData[0].Path}");
var matchingVehicle = TrafficPathing.ControlledVehicles.Where(cv => cv.Path == path.WaypointData[0].Path).ToList();
Game.LogTrivial($"Running foreach loop");
foreach (ControlledVehicle cv in matchingVehicle)
{
if (cv.Vehicle.Exists() && cv.Vehicle.IsValid() && cv.Vehicle.Driver.Exists() && cv.Vehicle.Driver.IsValid())
{
cv.DismissNow = true;
cv.Vehicle.Driver.Tasks.Clear();
cv.Vehicle.Driver.Dismiss();
//Game.LogTrivial($"{cv.vehicle.Model.Name} cleared from path {cv.path}");
}
}
Game.LogTrivial($"Remove all vehicles in the path");
TrafficPathing.ControlledVehicles.RemoveAll(cv => cv.Path == path.WaypointData[0].Path);
// Remove the speed zone so cars don't continue to be affected after the path is deleted
foreach (WaypointData wd in path.WaypointData)
{
if (wd == path.WaypointData[0])
{
World.RemoveSpeedZone(wd.YieldZone);
}
wd.WaypointBlip.Delete();
}
path.WaypointData.Clear();
// Manipulating the menu to reflect specific paths being deleted
if (selectedItem == deleteSinglePath)
{
Game.LogTrivial($"Path {path.PathNum} deleted.");
Game.DisplayNotification($"~o~Scene Manager\n~w~Path {path.PathNum} deleted.");
paths.RemoveAt(index);
//Game.LogTrivial("pathsNum count: " + pathsNum.Count);
//Game.LogTrivial("index: " + index);
pathsNum.RemoveAt(index);
RebuildTrafficMenu();
}
}
private static void TrafficMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == createNewPath)
{
trafficMenu.Visible = false;
pathCreationMenu.Visible = true;
// 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].PathFinished == false)
{
//Game.LogTrivial($"pathFinished: {paths[i].PathFinished}");
Game.LogTrivial($"Resuming path {i+1}");
Game.DisplayNotification($"~o~Scene Manager\n~y~[Creating]~w~ Resuming path {i+1}");
break;
}
else if (paths.ElementAtOrDefault(i) == null)
{
Game.LogTrivial($"Creating path {i+1}");
Game.DisplayNotification($"~o~Scene Manager\n~y~[Creating]~w~ Path {i+1} started.");
paths.Insert(i, new PathData(i+1,false));// { pathNum = i+1, pathFinished = false });
trafficRemoveWaypoint.Enabled = false;
break;
}
}
}
if (selectedItem == deleteSinglePath)
{
//Game.LogTrivial("pathsNum has " + pathsNum.Count + " items before deleting a path.");
//Game.LogTrivial("deletePath index is " + deletePath.Index);
//Game.LogTrivial("deletePath selectedPath is " + deletePath.IndexToItem(deletePath.Index));
DeletePath(paths[deleteSinglePath.Index], deleteSinglePath.Index, deleteSinglePath);
}
if (selectedItem == deleteAllPaths)
{
// Iterate through each item in paths and delete it
for(int i = 0; i < paths.Count; i++)
{
DeletePath(paths[i], i, deleteAllPaths);
}
pathsNum.Clear();
paths.Clear();
RebuildTrafficMenu();
Game.LogTrivial($"All paths deleted");
Game.DisplayNotification($"~o~Scene Manager\n~w~All paths deleted.");
}
if (selectedItem == directDriver)
{
// Sometimes GetNearbyVehicles will cause a crash for some reason, so keeping it in a try/catch will prevent that.
try
{
foreach (Vehicle v in Game.LocalPlayer.Character.GetNearbyVehicles(5))
{
if (v.Exists() && v.IsValid() && v.HasDriver && v.Driver.IsAlive)
{
// Check if there's a matching vehicle in ControlledVehicles. If so, check if it has tasks and proceed, else add it to the collection and assign tasks
var matchingVehicle = TrafficPathing.ControlledVehicles.Where(cv => cv.Vehicle == v).ToList();
if (matchingVehicle.ElementAtOrDefault(0) != null && matchingVehicle[0].TasksAssigned)
{
Game.LogTrivial($"[Direct Driver] {v.Model.Name} already in collection with tasks. Clearing tasks.");
v.Driver.Tasks.Clear();
matchingVehicle[0].Path = paths[directDriver.Index].WaypointData[0].Path;
matchingVehicle[0].TotalWaypoints = paths[directDriver.Index].WaypointData.Count;
matchingVehicle[0].CurrentWaypoint = 1;
matchingVehicle[0].DismissNow = true;
matchingVehicle[0].StoppedAtWaypoint = false;
matchingVehicle[0].Redirected = true;
GameFiber DirectTaskFiber = new GameFiber(() => TrafficPathing.DirectTask(matchingVehicle[0], paths[directDriver.Index].WaypointData));
DirectTaskFiber.Start();
}
else if(matchingVehicle.ElementAtOrDefault(0) != null && !matchingVehicle[0].TasksAssigned)
{
Game.LogTrivial($"[Direct Driver] {v.Model.Name} already in collection, but with no tasks.");
v.Driver.Tasks.Clear();
matchingVehicle[0].Path = paths[directDriver.Index].WaypointData[0].Path;
matchingVehicle[0].TotalWaypoints = paths[directDriver.Index].WaypointData.Count;
matchingVehicle[0].CurrentWaypoint = 1;
matchingVehicle[0].DismissNow = true;
matchingVehicle[0].StoppedAtWaypoint = false;
matchingVehicle[0].Redirected = true;
GameFiber DirectTaskFiber = new GameFiber(() => TrafficPathing.DirectTask(matchingVehicle[0], paths[directDriver.Index].WaypointData));
DirectTaskFiber.Start();
}
else
{
TrafficPathing.ControlledVehicles.Add(new ControlledVehicle(v, paths[directDriver.Index].WaypointData[0].Path, paths[directDriver.Index].WaypointData.Count, 1, false, false, true));
Game.LogTrivial($"[Direct Driver] {v.Model.Name} not in collection, adding to collection for path {paths[directDriver.Index].WaypointData[0].Path} with {paths[directDriver.Index].WaypointData.Count} waypoints");
GameFiber DirectTaskFiber = new GameFiber(() => TrafficPathing.DirectTask(TrafficPathing.ControlledVehicles.Last(), paths[directDriver.Index].WaypointData));
DirectTaskFiber.Start();
}
Game.LogTrivial($"Directed driver of {v.Model.Name} to path {paths[directDriver.Index].WaypointData[0].Path}.");
break;
}
}
}
catch
{
Game.LogTrivial($"No vehicles nearby");
}
}
if (selectedItem == dismissDriver)
{
// Check for nearby vehicles, and if the vehicle is being controlled, release it
GameFiber.StartNew(delegate {
foreach (Vehicle v in Game.LocalPlayer.Character.GetNearbyVehicles(5))
{
try
{
if (v.Exists() && v.IsValid() && v.HasDriver && v.Driver.IsAlive)
{
var matchingVehicle = TrafficPathing.ControlledVehicles.Where(cv => cv.Vehicle == v).ToList();
if (matchingVehicle.ElementAtOrDefault(0) != null && matchingVehicle[0].CurrentWaypoint < matchingVehicle[0].TotalWaypoints && !matchingVehicle[0].StoppedAtWaypoint)
{
matchingVehicle[0].DismissNow = true;
v.Driver.Tasks.Clear();
v.Driver.Dismiss();
Game.LogTrivial($"Dismissed driver of {v.Model.Name} from the path");
}
else if (matchingVehicle.ElementAtOrDefault(0) != null && matchingVehicle[0].CurrentWaypoint < matchingVehicle[0].TotalWaypoints)
{
matchingVehicle[0].StoppedAtWaypoint = false;
Game.LogTrivial($"Dismissed driver of {v.Model.Name} from waypoint {matchingVehicle[0].CurrentWaypoint}");
}
else if (matchingVehicle.ElementAtOrDefault(0) != null && matchingVehicle[0].CurrentWaypoint == matchingVehicle[0].TotalWaypoints)
{
matchingVehicle[0].StoppedAtWaypoint = false;
matchingVehicle[0].DismissNow = true;
v.Driver.Tasks.Clear();
v.Driver.Dismiss();
Game.LogTrivial($"Dismissed driver of {v.Model.Name} from final waypoint and ultimately the path");
}
else if (matchingVehicle.ElementAtOrDefault(0) != null)
{
matchingVehicle[0].DismissNow = true;
v.Driver.Tasks.Clear();
v.Driver.Dismiss();
Game.LogTrivial($"Dismissed driver of {v.Model.Name} from path {matchingVehicle[0].Path}");
}
else
{
v.Driver.Tasks.Clear();
v.Driver.Dismiss();
Game.LogTrivial($"Dismissed driver of {v.Model.Name} (was not in collection)");
}
break;
}
}
catch
{
Game.LogTrivial($"Something went wrong getting nearby vehicles to dismiss");
}
}
});
}
}
private static void PathCreation_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == trafficAddWaypoint)
{
// Loop through each path and find the first one which isn't finished
for (int i = 0; i < paths.Count; i++)
{
if (paths.ElementAtOrDefault(i) != null && !paths.ElementAtOrDefault(i).PathFinished)
{
// Create a waypoint blip and set the sprite based on the current path number
Blip blip = new Blip(Game.LocalPlayer.Character.Position);
blip.Scale = 0.5f;
switch (i)
{
case 0:
blip.Sprite = BlipSprite.Numbered1;
break;
case 1:
blip.Sprite = BlipSprite.Numbered2;
break;
case 2:
blip.Sprite = BlipSprite.Numbered3;
break;
case 3:
blip.Sprite = BlipSprite.Numbered4;
break;
case 4:
blip.Sprite = BlipSprite.Numbered5;
break;
case 5:
blip.Sprite = BlipSprite.Numbered6;
break;
case 6:
blip.Sprite = BlipSprite.Numbered7;
break;
case 7:
blip.Sprite = BlipSprite.Numbered8;
break;
}
// If it's the first waypoint, make the blip orange, else make it yellow
if (paths[i].WaypointData.Count == 0)
{
blip.Color = Color.Orange;
}
else
{
blip.Color = Color.Yellow;
}
// Add the waypoint data to the path
paths[i].WaypointData.Add(new WaypointData(i+1, Game.LocalPlayer.Character.Position, waypointSpeeds[waypointSpeed.Index], drivingFlags[waypointType.Index], blip));
Game.LogTrivial($"[Path {i+1}] {drivingFlags[waypointType.Index].ToString()} waypoint added");
}
}
// Refresh the trafficMenu after a waypoint is added in order to show Continue Creating Current Path instead of Create New Path
trafficRemoveWaypoint.Enabled = true;
trafficMenu.Clear();
trafficMenu.AddItem(createNewPath = new UIMenuItem("Continue Creating Current Path"));
trafficMenu.AddItem(deleteSinglePath = new UIMenuListItem("Delete Path", pathsNum, 0));
trafficMenu.AddItem(deleteAllPaths = new UIMenuItem("Delete All Paths"));
trafficMenu.AddItem(directDriver = new UIMenuListItem("Direct nearest driver to path", pathsNum, 0));
trafficMenu.AddItem(dismissDriver = new UIMenuItem("Dismiss nearest driver"));
if (pathsNum.Count == 8)
{
createNewPath.Enabled = false;
}
if (pathsNum.Count == 0)
{
deleteSinglePath.Enabled = false;
deleteAllPaths.Enabled = false;
directDriver.Enabled = false;
}
_menuPool.RefreshIndex();
}
if (selectedItem == trafficRemoveWaypoint)
{
// Loop through each path and find the first one which isn't finished, then delete the path's last waypoint and corresponding blip
for (int i = 0; i < paths.Count; i++)
{
if (paths.ElementAtOrDefault(i) != null && !paths[i].PathFinished)
{
Game.LogTrivial($"[Path {i+1}] {paths[i].WaypointData.Last().DrivingFlag.ToString()} waypoint removed");
paths[i].WaypointData.Last().WaypointBlip.Delete();
paths[i].WaypointData.RemoveAt(paths[i].WaypointData.IndexOf(paths[i].WaypointData.Last()));
// If the path has no waypoints, disable the menu option to remove a waypoint
if (paths[0].WaypointData.Count == 0)
{
trafficRemoveWaypoint.Enabled = false;
}
}
}
}
if (selectedItem == trafficEndPath)
{
// Loop through each path and find the first one which isn't finished
for (int i = 0; i < paths.Count; i++)
{
if (paths.ElementAtOrDefault(i) != null && !paths[i].PathFinished)
{
// If the path has one stop waypoint or at least two waypoints, finish the path and start the vehicle collector loop, else show user the error and delete any waypoints they made and clear the invalid path
if (paths[i].WaypointData.Count >= 2 || (paths[i].WaypointData.Count == 1 && paths[i].WaypointData[0].DrivingFlag == VehicleDrivingFlags.StopAtDestination))
{
Game.LogTrivial($"[Path Creation] Path {i+1} finished with {paths[i].WaypointData.Count} waypoints.");
Game.DisplayNotification($"~o~Scene Manager\n~g~[Success]~w~ Path {i+1} complete.");
paths[i].WaypointData.Last().WaypointBlip.Color = Color.OrangeRed;
paths[i].PathFinished = true;
paths[i].PathNum = i + 1;
pathsNum.Insert(i, paths[i].PathNum);
GameFiber InitialWaypointVehicleCollectorFiber = new GameFiber(() => TrafficPathing.InitialWaypointVehicleCollector(paths[i].WaypointData));
InitialWaypointVehicleCollectorFiber.Start();
break;
}
else
{
Game.LogTrivial($"[Path Error] A minimum of 2 waypoints is required.");
Game.DisplayNotification($"~o~Scene Manager\n~r~[Error]~w~ A minimum of 2 waypoints is required.");
foreach (WaypointData wd in paths[i].WaypointData)
{
wd.WaypointBlip.Delete();
}
paths[i].WaypointData.Clear();
paths.RemoveAt(i);
break;
}
}
}
// "Refresh" the menu to reflect the new path
RebuildTrafficMenu();
}
}
private static void ConeMenu_OnListChange(UIMenu sender, UIMenuListItem listItem, int index)
{
if (shadowCone.Exists())
{
shadowCone.IsVisible = false;
}
shadowCone = new Rage.Object(coneObjectNames[selectCone.Index], Game.LocalPlayer.Character.GetOffsetPosition(new Vector3(0f, 3f, -1f)));
shadowCone.Opacity = 70f;
shadowCone.IsCollisionEnabled = false;
}
private static void MainMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == navigateToConeMenu)
{
if (EntryPoint.Settings.EnableHints)
{
Game.DisplayNotification($"~o~Scene Manager\n~y~[Hint]~y~ ~w~It's easier to place cones in first-person view.");
}
shadowCone = new Rage.Object(coneObjectNames[selectCone.Index], Game.LocalPlayer.Character.GetOffsetPosition(new Vector3(0f, 3f, -1f)));
shadowCone.IsCollisionEnabled = false;
shadowCone.Opacity = 70f;
GameFiber ConeHoverFiber = new GameFiber(() => ConeHover(sender, selectedItem, index));
ConeHoverFiber.Start();
}
}
private static void ConeHover(UIMenu sender, UIMenuItem selectedItem, int index)
{
// Run a loop to show currently selected cone hovering in front of player
//Game.LogTrivial("Creating shadow cone");
while (coneMenu.Visible)
{
shadowCone.Position = Game.LocalPlayer.Character.GetOffsetPosition(new Vector3(0f, 3f, -1f));
GameFiber.Yield();
}
}
private static void ConeMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
Rage.Object cone;
if (selectedItem == addCone)
{
// Attach some invisible object to the cone which the AI try to drive around
// Barrier, new rotate option in menu, barrier rotates with cone and becomes invisible similar to ASC when created
cone = new Rage.Object(shadowCone.Model, shadowCone.Position);
cones.Add(cone);
}
if (selectedItem == removeLastCone)
{
//Game.LogTrivial($"cones count before deletion: {cones.Count}");
cones[cones.Count - 1].Delete();
cones.RemoveAt(cones.Count - 1);
//Game.LogTrivial($"cones count after deletion: {cones.Count}");
}
if (selectedItem == removeNearestCone)
{
cones = cones.OrderBy(o => o.DistanceTo(Game.LocalPlayer.Character)).ToList();
cones[0].Delete();
cones.RemoveAt(0);
//Game.LogTrivial($"cones count: {cones.Count}");
}
if (selectedItem == removeAllCones)
{
foreach (Rage.Object c in cones)
{
c.Delete();
}
if (cones.Count > 0)
{
cones.Clear();
}
//Game.LogTrivial($"cones count: {cones.Count}");
}
coneMenu.RemoveItemAt(2);
coneMenu.RemoveItemAt(2);
coneMenu.RemoveItemAt(2);
coneMenu.AddItem(removeLastCone = new UIMenuItem("Remove Last Cone"));
if (cones.Count == 0)
{
removeLastCone.Enabled = false;
}
coneMenu.AddItem(removeNearestCone = new UIMenuItem("Remove Nearest Cone"));
if (cones.Count == 0)
{
removeNearestCone.Enabled = false;
}
coneMenu.AddItem(removeAllCones = new UIMenuItem("Remove All Cones"));
if (cones.Count == 0)
{
removeAllCones.Enabled = false;
}
}
private static void MyTerminationHandler(object sender, EventArgs e)
{
// Clean up paths
for (int i = 0; i < paths.Count; i++)
{
DeletePath(paths[i], i, deleteAllPaths);
}
// Clean up cones
foreach (Rage.Object cone in cones)
{
if (cone.IsValid() && cone.Exists())
{
cone.Delete();
}
}
// Clear everything
cones.Clear();
TrafficPathing.ControlledVehicles.Clear();
Game.LogTrivial($"Scene Manager has been terminated.");
Game.DisplayNotification($"~o~Scene Manager\n~r~[Notice]~w~ The plugin has shut down.");
}
}
}

View file

@ -1,282 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Rage;
namespace SceneManager
{
public static class TrafficPathing
{
public static List<ControlledVehicle> ControlledVehicles = new List<ControlledVehicle> { };
public static void InitialWaypointVehicleCollector(List<WaypointData> waypointData)
{
waypointData[0].YieldZone = World.AddSpeedZone(waypointData[0].WaypointPos, 10f, waypointData[0].Speed);
// If there's a path with a single stop waypoint, run a loop to give all nearby AI the StopForVehicle driving flag so they don't just go around
if(waypointData.Count == 1)
{
GameFiber.StartNew(delegate
{
while (waypointData.ElementAtOrDefault(0) != null)
{
foreach (Vehicle v in GetNearbyVehicles(waypointData[0].WaypointPos, 50f))
{
if(v.Exists() && v.IsValid() && v.HasDriver && v.Driver.Exists() && v.Driver.IsValid())
{
SetDriveTaskDrivingFlags(v.Driver, EDrivingFlags.StopForVehicles);
}
}
GameFiber.Yield();
}
});
}
while (waypointData.ElementAtOrDefault(0) != null)
{
// Getting vehicles within 3f of waypoint
try
{
foreach (Vehicle v in GetNearbyVehicles(waypointData[0].WaypointPos, 3f))
{
// No protection for player if they drive into the waypoints
if(VehicleAndDriverValid(v) && v != Game.LocalPlayer.Character.CurrentVehicle && v.HasDriver && v.Driver.IsAlive && (v.IsCar || v.IsBike || v.IsBicycle || v.IsQuadBike))
{
// Check if there's an object in the list with a matching vehicle
var matchingVehicle = ControlledVehicles.Where(cv => cv.Vehicle == v).ToList();
// If there's a match, then check if the first match has tasksAssigned. If not, AssignTasks
if (matchingVehicle.ElementAtOrDefault(0) != null && !matchingVehicle[0].TasksAssigned && !matchingVehicle[0].DismissNow)
{
Game.LogTrivial($"[InitialWaypointVehicleCollector] {v.Model.Name} already in collection, but with no tasks. Assigning tasks.");
matchingVehicle[0].TasksAssigned = true;
GameFiber AssignTasksFiber = new GameFiber(() => AssignTasks(matchingVehicle[0], waypointData));
AssignTasksFiber.Start();
}
// Else if object doesn't exist, add to collection and AssignTasks
else if (matchingVehicle.ElementAtOrDefault(0) != null && matchingVehicle[0].TasksAssigned)
{
//Game.LogTrivial($"Vehicle already in collection with tasks. Do nothing.");
}
else
{
ControlledVehicles.Add(new ControlledVehicle(v, waypointData[0].Path, waypointData.Count, 1, true, false, false));
Game.LogTrivial($"Added {v.Model.Name} to collection from initial waypoint at path {waypointData[0].Path} with {waypointData.Count} waypoints");
GameFiber AssignTasksFiber = new GameFiber(() => AssignTasks(ControlledVehicles.Last(), waypointData));
AssignTasksFiber.Start();
}
}
GameFiber.Yield();
}
}
catch
{
Game.LogTrivial($"There was a problem getting vehicles near the start waypoint");
}
GameFiber.Yield();
}
}
public static void DirectTask(ControlledVehicle cv, List<WaypointData> waypointData)
{
cv.DismissNow = false;
if (VehicleAndDriverValid(cv))
{
cv.Vehicle.IsPersistent = true;
cv.Vehicle.Driver.BlockPermanentEvents = true;
cv.Vehicle.Driver.Tasks.Clear();
}
// Give vehicle task to initial waypoint of desired path, then run a loop to keep giving the task until they're close enough in case they try to wander away too early
cv.Vehicle.Driver.Tasks.DriveToPosition(waypointData[0].WaypointPos, waypointData[0].Speed, VehicleDrivingFlags.FollowTraffic, 1f);
while (waypointData.ElementAtOrDefault(0) != null && VehicleAndDriverValid(cv) && cv.Vehicle.DistanceTo(waypointData[0].WaypointPos) > 3f && !cv.DismissNow)
{
cv.Vehicle.Driver.Tasks.DriveToPosition(waypointData[0].WaypointPos, waypointData[0].Speed, VehicleDrivingFlags.FollowTraffic, 1f);
GameFiber.Sleep(500);
}
cv.Redirected = false;
Game.LogTrivial($"DirectTask loop over");
}
private static void AssignTasks(ControlledVehicle cv, List<WaypointData> waypointData)
{
if (VehicleAndDriverValid(cv))
{
cv.Vehicle.IsPersistent = true;
cv.Vehicle.Driver.BlockPermanentEvents = true;
cv.Vehicle.Driver.Tasks.Clear();
}
if (waypointData.Count == 1 && VehicleAndDriverValid(cv) && !cv.DismissNow)
{
AssignSingleWaypointTask(cv, waypointData);
}
else if(waypointData.Count > 1 && VehicleAndDriverValid(cv))
{
AssignMultiWaypointTasks(cv, waypointData);
}
while(!cv.DismissNow || cv.StoppedAtWaypoint)
{
GameFiber.Yield();
}
Game.LogTrivial($"AssignTasks exit");
}
private static void AssignSingleWaypointTask(ControlledVehicle cv, List<WaypointData> waypointData)
{
// Give driver a task to the single path waypoint. Run a loop with a condition checking for DismissNow for cases where the driver is dismissed or redirected
Game.LogTrivial($"Assigning task for single waypoint.");
cv.Vehicle.Driver.Tasks.DriveToPosition(waypointData[0].WaypointPos, waypointData[0].Speed, VehicleDrivingFlags.FollowTraffic, 1f);
while (waypointData.ElementAtOrDefault(0) != null && VehicleAndDriverValid(cv) && cv.Vehicle.DistanceTo(waypointData[0].WaypointPos) > 3f && !cv.DismissNow)
{
GameFiber.Sleep(1000);
}
Game.LogTrivial($"{cv.Vehicle.Model.Name} should be stopped at the waypoint.");
cv.Vehicle.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.GoForwardStraightBraking);//.WaitForCompletion();
cv.Vehicle.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.Wait);
cv.StoppedAtWaypoint = true;
}
private static void AssignMultiWaypointTasks(ControlledVehicle cv, List<WaypointData> waypointData)
{
// For each waypoint in the path, give driver a task to that waypoint
for (int i = 1; i < waypointData.Count; i++)
{
if (cv.DismissNow)
{
break;
}
Game.LogTrivial($"Assigning task to {cv.Vehicle.Model.Name} for waypoint {i+1} of {waypointData.Count}");
cv.CurrentWaypoint++;
//if (VehicleAndDriverValid(cv) && waypointData.ElementAtOrDefault(i) != null && i == waypointData.IndexOf(waypointData.Last()) && waypointData.Last().DrivingFlag == VehicleDrivingFlags.StopAtDestination)
if (VehicleAndDriverValid(cv) && waypointData.ElementAtOrDefault(i) != null && waypointData[i].DrivingFlag == VehicleDrivingFlags.StopAtDestination) // NEW
{
// Give driver a task to the waypoint. Run a loop with a condition checking for DismissNow for cases where the driver is dismissed or redirected
cv.Vehicle.Driver.Tasks.DriveToPosition(waypointData[i].WaypointPos, waypointData[i].Speed, VehicleDrivingFlags.FollowTraffic, 1f);
while (waypointData.ElementAtOrDefault(i) != null && cv.Vehicle.DistanceTo(waypointData[i].WaypointPos) > 3f && !cv.DismissNow)
{
GameFiber.Yield();
}
if (cv.DismissNow)
{
break;
}
if (waypointData.ElementAtOrDefault(i) != null)
{
Game.LogTrivial($"{cv.Vehicle.Model.Name} stopping at stop waypoint");
//Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(v, 3f, 1, false);
cv.Vehicle.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.GoForwardStraightBraking);
cv.StoppedAtWaypoint = true;
while(waypointData.ElementAtOrDefault(i) != null && cv.StoppedAtWaypoint && !cv.DismissNow)
{
GameFiber.Yield();
}
}
}
else if (VehicleAndDriverValid(cv) && waypointData.ElementAtOrDefault(i) != null && !cv.DismissNow)
{
cv.Vehicle.Driver.Tasks.DriveToPosition(waypointData[i].WaypointPos, waypointData[i].Speed, VehicleDrivingFlags.FollowTraffic, 1f).WaitForCompletion();
}
Game.LogTrivial($"{cv.Vehicle.Model.Name} waypoint {i+1} task complete");
}
if (cv.Redirected)
{
Game.LogTrivial($"{cv.Vehicle.Model.Name} was redirected, all old path tasks have been cleared.");
}
else
{
Game.LogTrivial($"{cv.Vehicle.Model.Name} all path {cv.Path} tasks complete.");
}
cv.TasksAssigned = false;
cv.DismissNow = true;
cv.StoppedAtWaypoint = false;
if (VehicleAndDriverValid(cv) && !cv.Redirected)
{
cv.Vehicle.Driver.Dismiss();
cv.Vehicle.Driver.Tasks.Clear();
//cv.Vehicle.IsPersistent = false;
cv.Vehicle.Driver.BlockPermanentEvents = false;
ControlledVehicles.Remove(cv);
}
else if(!VehicleAndDriverValid(cv))
{
Game.LogTrivial($"The vehicle is not valid after tasks completed.");
}
}
private static bool VehicleAndDriverValid(Vehicle v)
{
// Ensure everything is valid before we do stuff with them so there isn't a crash
if (v.Exists() && v.IsValid() && v.Driver.Exists() && v.Driver.IsValid())
{
return true;
}
else
{
return false;
}
}
private static bool VehicleAndDriverValid(ControlledVehicle cv)
{
// Ensure everything is valid before we do stuff with them so there isn't a crash
if(cv.Vehicle.Exists() && cv.Vehicle.IsValid() && cv.Vehicle.Driver.Exists() && cv.Vehicle.Driver.IsValid())
{
return true;
}
else
{
return false;
}
}
private static Vehicle[] GetNearbyVehicles(Vector3 OriginPosition, float radius)
{
return (from x in World.GetAllVehicles() where !x.IsTrailer && x.DistanceTo(OriginPosition) < radius select x).ToArray();
}
[Flags]
public enum EDrivingFlags
{
None = 0,
StopForVehicles = 1,
StopForPeds = 2,
AvoidEmptyVehicles = 8,
StopForTrafficLights = 128,
UseBlinkers = 256,
TakeShortestPath = 262144,
IgnoreRoads = 4194304,
AvoidHighways = 536870912,
Normal = StopForVehicles | StopForPeds | AvoidEmptyVehicles | StopForTrafficLights | UseBlinkers,
}
public static void SetDriveTaskDrivingFlags(this Ped ped, EDrivingFlags flags)
{
ulong SetDriveTaskDrivingFlagsHash = 0xDACE1BE37D88AF67;
Rage.Native.NativeFunction.CallByHash<int>(SetDriveTaskDrivingFlagsHash, ped, (int)flags);
}
}
}
/*
//while (waypointData.ElementAtOrDefault(0) != null && v.Exists() && v.IsValid() && v.Driver.Exists() && v.Driver.IsValid() && !cv.DismissNow)
//{
//if (waypointData.ElementAtOrDefault(0) == null || cv.DismissNow)
//{
//driver.Tasks.Clear();
//Game.LogTrivial($"{v.Model.Name} released from wait loop");
//break;
//}
//else if (waypointData.ElementAtOrDefault(0) != null && !cv.DismissNow && v.Driver.IsInVehicle(v, false))
//{
v.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.GoForwardStraightBraking);
//v.Driver.Tasks.PerformDrivingManeuver(VehicleManeuver.Wait);
//}
//GameFiber.Yield();
//}
//v.Driver.Tasks.Clear();
//Game.LogTrivial($"{v.Model.Name} is dismissed or the driver is not in the vehicle.");
*/

View file

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Rage;
namespace SceneManager
{
internal static class VehicleCollector
{
internal static List<CollectedVehicle> collectedVehicles = new List<CollectedVehicle>();
internal static void StartCollectingAtWaypoint(List<Path> 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<Path> 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;
}
}
}
}

View file

@ -1,68 +0,0 @@
using System.Management;
using System.Text;
using Rage;
namespace SceneManager
{
class Verification
{
// https://www.codingame.com/playgrounds/11117/simple-encryption-using-c-and-xor-technique
// Get hardware ID from user
// encrypt hardware ID
// show encrypted ID in log
// decrypt ID for hardware ID
// Give user their hardware ID for ini
// Pass hardware ID through encryption for a match
public static string GetID()
{
// Get processor ID
ManagementObjectCollection mbsList = null;
ManagementObjectSearcher mbs = new ManagementObjectSearcher("Select * From Win32_processor");
mbsList = mbs.Get();
string processorID = "";
foreach (ManagementObject mo in mbsList)
{
processorID = mo["ProcessorID"].ToString();
}
// Get HD ID
/*ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""c:""");
dsk.Get();
string hdID = dsk["VolumeSerialNumber"].ToString();*/
// Get MoBo ID
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_BaseBoard");
ManagementObjectCollection moc = mos.Get();
string motherBoardID = "";
foreach (ManagementObject mo in moc)
{
motherBoardID = (string)mo["SerialNumber"];
}
string uniqueID = processorID + motherBoardID;
string encrypted = passThrough(uniqueID);
//Game.LogTrivial($"Processor ID: {processorID}");
//Game.LogTrivial($"HD ID: {hdID}");
//Game.LogTrivial($"Motherboard ID: {motherBoardID}");
//Game.LogTrivial($"{uniqueID}");
Game.LogTrivial($"{encrypted}"); // Get this value from user's log as their key. When they put it in the .ini, it will go back through passThrough and match with their uniqueID
//Game.LogTrivial($"Decrypted: {passThrough(encrypted)}");
return encrypted;
}
public static string passThrough(string id)
{
StringBuilder szInputStringBuild = new StringBuilder(id);
StringBuilder szOutStringBuild = new StringBuilder(id.Length);
char Textch;
for (int iCount = 0; iCount < id.Length; iCount++)
{
Textch = szInputStringBuild[iCount];
Textch = (char)(Textch ^ 1);
szOutStringBuild.Append(Textch);
}
return szOutStringBuild.ToString();
}
}
}

View file

@ -1,33 +0,0 @@
using Rage;
namespace SceneManager
{
public class WaypointData
{
public int Path;
public Vector3 WaypointPos;
public float Speed;
public VehicleDrivingFlags DrivingFlag;
public Blip WaypointBlip;
public uint YieldZone;
public WaypointData(int path, Vector3 waypointPos, float speed, VehicleDrivingFlags drivingFlag, Blip waypointBlip, uint yieldZone)
{
Path = path;
WaypointPos = waypointPos;
Speed = speed;
DrivingFlag = drivingFlag;
WaypointBlip = waypointBlip;
YieldZone = yieldZone;
}
public WaypointData(int path, Vector3 waypointPos, float speed, VehicleDrivingFlags drivingFlag, Blip waypointBlip)
{
Path = path;
WaypointPos = waypointPos;
Speed = speed;
DrivingFlag = drivingFlag;
WaypointBlip = waypointBlip;
}
}
}