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

Merge V2.1.1 release (#3)

* Mouse can now be used to fully navigate menus

* Added check for driver's current vehicle when releasing from Stop waypoint in case the ped is not in a vehicle.

* Lines are now only drawn between waypoint markers under the same conditions that waypoint markers are drawn

* Updated marker position to be player's mouse position

* Version update

* Added logic to update waypoint position during driving task if the waypoint position was changed before the driver arrived.

* Removed unused usings.  Refactored debug statements to use Game.LogTrivial instead of Logger.Log

* Removed class

* Removed Logger class

* Consolidated all custom enums to this class.  Added default waypoint settings from .ini

* Modified a hint message

* Fixed collector options not being enabled/disabled when Collector box is checked

* Refactored AITasking into CollectedVehicle.

* Updated reference to vehicle tasking based on AITasking refactor.  Fixed speed zone radius not updating correctly.

* Version update

* Refactored AITasking into CollectedVehicle

* Added check for CollectorRadius being more than SpeedZoneRadius.  Added debug messages when values are reset to default.

* Added hint message if player tries to edit waypoints while 3D waypoints are disabled

* Fixed a bug where a 3D waypoint marker would be drawn even if 3D waypoints were disabled

* Removed unnecessary property setting when a vehicle is being removed from a path.

* Added a check for if the driver loses their task and reassigns it.

* Fixed a bug where the 3D line between waypoints was still being drawn even though 3D waypoints were disabled in the settings menu.

* Updated version

* Added console command to show info about collected vehicles.

* Added ConsoleCommand class, removed AITasking class.

* Removed class after refactoring into CollectedVehicle

* Update README.md

* Update README.md

* Added ini setting for Advanced Barrier Options.  Added enum for TrafficLight state.  Added debug message for invalid barriers.

* Updated version

* Added MousePositionInWorld and RNUIMouseInputHandler classes

* Fixed a crash when a collected ped is arrested.

* Refactored to implement RNUIMouseInputHandler class.

* Removed unused method.

* Updated version

* Disabled deletion/creation of shadow barrier and re-enabled updating position based on mouse position.

* Added check for Driver's current vehicle in driving loop in case the Driver exited Vehicle at some point.  Adjusted some guard clause logic and log messages.

* Removed unused variable

* Renamed some methods to improve clarity

* Updated version.
This commit is contained in:
Rich 2020-11-26 07:41:22 -07:00 committed by GitHub
parent d73ae601e7
commit 8bc4de3528
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1818 additions and 1083 deletions

View file

@ -4,6 +4,9 @@
Drag and drop the contents from within the downloaded GTA V folder into where your GTA V is installed.
![How to drag and drop](https://i.imgur.com/nnxqgtn.jpg)
## GET SUPPORT AND REPORT PROBLEMS
For the fastest support, [join my Discord](https://discord.gg/cUQaTNQ) and ask your question in the **correct category/channel**. For slower support, [use this thread on the LSPDFR forums](https://www.lcpdfr.com/forums/topic/107730-richs-plugin-support-thread/).
## HOW TO USE SCENE MANAGER
### Using the Menus:
Menu options with gold colored text are selectable, which means when you select these menu items, something will happen (opening a new menu, adding a waypoint, placing a barrier, etc). Menu options with white colored text are interactable (can be scrolled through, for example), but nothing will happen if you try to select them.

View file

@ -1,232 +0,0 @@
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;
if(currentWaypoint != null)
{
collectedVehicle.CurrentWaypoint = currentWaypoint;
}
else
{
collectedVehicle.CurrentWaypoint = path.Waypoints[0];
}
if (currentWaypoint != null && collectedVehicle.Directed)
{
collectedVehicle.Dismissed = false;
float acceptedDistance = GetAcceptedStoppingDistance(path.Waypoints, path.Waypoints.IndexOf(currentWaypoint));
while (!collectedVehicle.ReadyForDirectTasks)
{
GameFiber.Yield();
}
collectedVehicle.Driver.Tasks.Clear();
AssignTasksForDirectedDriver(acceptedDistance);
LoopWhileDrivingToDirectedWaypoint(acceptedDistance);
if(collectedVehicle != null)
{
collectedVehicle.Directed = false;
}
if (collectedVehicle.Vehicle)
{
collectedVehicle.Driver.Tasks.PerformDrivingManeuver(collectedVehicle.Vehicle, VehicleManeuver.GoForwardWithCustomSteeringAngle, 3).WaitForCompletion();
Logger.Log($"{collectedVehicle.Vehicle.Model.Name} directed task is complete, directed is now false");
}
}
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

@ -0,0 +1,34 @@
using Rage;
using Rage.Attributes;
using Rage.ConsoleCommands.AutoCompleters;
using System.Linq;
namespace SceneManager
{
internal static class ConsoleCommands
{
[ConsoleCommand]
internal static void Command_ShowCollectedVehicleInfo([ConsoleCommandParameter(AutoCompleterType = typeof(ConsoleCommandAutoCompleterVehicle))] Vehicle vehicle)
{
foreach(Path path in PathMainMenu.paths)
{
var collectedVehicle = path.CollectedVehicles.Where(v => v.Vehicle == vehicle).FirstOrDefault();
if(collectedVehicle != null)
{
Game.LogTrivial($"Vehicle: {collectedVehicle.Vehicle.Model.Name} [{collectedVehicle.Vehicle.Handle}]");
Game.LogTrivial($"Driver handle: {collectedVehicle.Driver.Handle}");
Game.LogTrivial($"Path: {collectedVehicle.Path.Number}");
Game.LogTrivial($"Current waypoint: {collectedVehicle.CurrentWaypoint.Number}");
Game.LogTrivial($"StoppedAtWaypoint: {collectedVehicle.StoppedAtWaypoint}");
Game.LogTrivial($"SkipWaypoint: {collectedVehicle.SkipWaypoint}");
Game.LogTrivial($"ReadyForDirectTasks: {collectedVehicle.ReadyForDirectTasks}");
Game.LogTrivial($"Directed: {collectedVehicle.Directed}");
Game.LogTrivial($"Dismissed: {collectedVehicle.Dismissed}");
Game.LogTrivial($"Task status: {collectedVehicle.Driver.Tasks.CurrentTaskStatus}");
return;
}
}
Game.LogTrivial($"{vehicle.Model.Name} [{vehicle.Handle}] was not found collected by any path.");
}
}
}

View file

@ -1,6 +1,4 @@
using System;
using System.ComponentModel.Design.Serialization;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Reflection;
@ -36,7 +34,7 @@ namespace SceneManager
void GetAssemblyVersion()
{
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Logger.Log($"Scene Manager V{version} is ready.");
Game.LogTrivial($"Scene Manager V{version} is ready.");
}
}
private static bool CheckRNUIVersion()
@ -45,7 +43,7 @@ namespace SceneManager
var exists = File.Exists(directory + @"\RAGENativeUI.dll");
if (!exists)
{
Logger.Log($"RNUI was not found in the user's GTA V directory.");
Game.LogTrivial($"RNUI was not found in the user's GTA V directory.");
Game.DisplayNotification($"~o~Scene Manager ~r~[Error]\n~w~RAGENativeUI.dll was not found in your GTA V directory. Please install RAGENativeUI and try again.");
return false;
}
@ -54,7 +52,7 @@ namespace SceneManager
Version requiredMinimumVersion = new Version("1.7.0.0");
if(userVersion >= requiredMinimumVersion)
{
Logger.Log($"User's RNUI version: {userVersion}");
Game.LogTrivial($"User's RNUI version: {userVersion}");
return true;
}
else
@ -68,22 +66,21 @@ namespace SceneManager
{
if (Settings.ModifierKey == Keys.None && Settings.ModifierButton == ControllerButtons.None)
{
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ToggleButton} button");
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ToggleButton} button");
}
else if (Settings.ModifierKey == Keys.None)
{
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons");
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~To open the menu, press the ~b~{Settings.ToggleKey} key ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons");
}
else if (Settings.ModifierButton == ControllerButtons.None)
{
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} ~w~or the ~b~{Settings.ToggleButton} button");
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~To open the menu, press ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} ~w~or the ~b~{Settings.ToggleButton} button");
}
else
{
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~ To open the menu, press the ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} keys ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons");
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~To open the menu, press the ~b~{Settings.ModifierKey} ~w~+ ~b~{Settings.ToggleKey} keys ~w~or ~b~{Settings.ModifierButton} ~w~+ ~b~{Settings.ToggleButton} buttons");
}
}
private static void MyTerminationHandler(object sender, EventArgs e)
{
// Clean up cones
@ -102,12 +99,8 @@ namespace SceneManager
PathMainMenu.DeletePath(PathMainMenu.paths[i], PathMainMenu.Delete.All);
}
// Clear everything
BarrierMenu.barriers.Clear();
PathMainMenu.paths.Clear();
Logger.Log($"Plugin has shut down.");
Game.DisplayNotification($"~o~Scene Manager ~r~[Terminated]\n~w~ The plugin has shut down.");
Game.LogTrivial($"Plugin has shut down.");
Game.DisplayNotification($"~o~Scene Manager ~r~[Terminated]\n~w~The plugin has shut down.");
}
}
}

View file

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

View file

@ -9,214 +9,198 @@ namespace SceneManager
{
class BarrierMenu
{
private static List<TrafficLight> trafficLightList = new List<TrafficLight>() { TrafficLight.Green, TrafficLight.Red, TrafficLight.Yellow, TrafficLight.None };
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 UIMenuCheckboxItem invincible = new UIMenuCheckboxItem("Indestructible", false);
private static UIMenuNumericScrollerItem<int> barrierTexture = new UIMenuNumericScrollerItem<int>("Change Texture", "", 0, 15, 1);
private static UIMenuCheckboxItem setBarrierLights = new UIMenuCheckboxItem("Enable Barrier Lights", false);
private static UIMenuListScrollerItem<TrafficLight> setBarrierTrafficLight = new UIMenuListScrollerItem<TrafficLight>("Set Barrier Traffic Light", "", trafficLightList);
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 Object shadowBarrier;
internal static void InstantiateMenu()
{
barrierMenu.ParentMenu = MainMenu.mainMenu;
MenuManager.menuPool.Add(barrierMenu);
barrierMenu.OnItemSelect += BarrierMenu_OnItemSelected;
barrierMenu.OnScrollerChange += BarrierMenu_OnScrollerChanged;
barrierMenu.OnCheckboxChange += BarrierMenu_OnCheckboxChanged;
barrierMenu.OnMenuOpen += BarrierMenu_OnMenuOpen;
}
internal static void BuildBarrierMenu()
{
barrierMenu.AddItem(resetBarriers);
resetBarriers.ForeColor = Color.Gold;
resetBarriers.Enabled = false;
barrierMenu.AddItem(barrierList);
barrierList.ForeColor = Color.Gold;
barrierMenu.AddItem(removeBarrierOptions, 0);
barrierMenu.AddItem(rotateBarrier);
barrierMenu.AddItem(invincible);
if (Settings.EnableAdvancedBarricadeOptions)
{
barrierMenu.AddItem(barrierTexture);
barrierTexture.Index = 0;
barrierMenu.AddItem(setBarrierLights);
barrierMenu.AddItem(setBarrierTrafficLight);
setBarrierTrafficLight.Index = 3;
}
barrierMenu.AddItem(removeBarrierOptions);
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;
barrierMenu.AddItem(resetBarriers);
resetBarriers.ForeColor = Color.Gold;
resetBarriers.Enabled = false;
}
internal static void CreateShadowBarrier(UIMenu barrierMenu)
internal static void CreateShadowBarrier()
{
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~y~ ~w~The shadow cone will disappear if you aim too far away.");
if (shadowBarrier)
{
shadowBarrier.Delete();
}
shadowBarrier = new Rage.Object(Settings.barrierValues[barrierList.Index], TracePlayerView(Settings.BarrierPlacementDistance, TraceFlags.IntersectWorld).HitPosition, rotateBarrier.Value);
shadowBarrier = new Object(Settings.barrierValues[barrierList.Index], MousePositionInWorld.GetPosition, rotateBarrier.Value);
if (!shadowBarrier)
{
barrierMenu.Close();
Game.DisplayNotification($"~o~Scene Manager ~red~[Error]\n~w~ Something went wrong creating the shadow barrier. Please try again.");
Game.DisplayNotification($"~o~Scene Manager ~r~[Error]\n~w~Something went wrong creating the shadow barrier. Please try again.");
return;
}
Rage.Native.NativeFunction.Natives.SET_ENTITY_TRAFFICLIGHT_OVERRIDE(shadowBarrier, setBarrierTrafficLight.Index);
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()
// Start with lights off for Parks's objects
if (Settings.EnableAdvancedBarricadeOptions)
{
while (barrierMenu.Visible && shadowBarrier)
Rage.Native.NativeFunction.Natives.x971DA0055324D033(shadowBarrier, barrierTexture.Value);
SetBarrierLights();
}
}
private static void LoopToDisplayShadowBarrier()
{
while (barrierMenu.Visible)
{
if (barrierList.Selected || rotateBarrier.Selected || invincible.Selected || barrierTexture.Selected || setBarrierLights.Selected || setBarrierTrafficLight.Selected)
{
if (barrierList.Selected || rotateBarrier.Selected)
if (shadowBarrier)
{
shadowBarrier.IsVisible = true;
UpdateShadowBarrierPosition();
}
else if(MousePositionInWorld.GetPositionForBarrier.DistanceTo2D(Game.LocalPlayer.Character.Position) <= Settings.BarrierPlacementDistance)
{
CreateShadowBarrier();
}
}
else
{
if (shadowBarrier)
{
shadowBarrier.Delete();
}
}
GameFiber.Yield();
}
if (shadowBarrier)
{
shadowBarrier.Delete();
}
void UpdateShadowBarrierPosition()
{
DisableBarrierMenuOptionsIfShadowConeTooFar();
if (shadowBarrier)
{
// Delete and re-create for testing purposes.. Parks' stop light prop
//shadowBarrier.Delete();
//CreateShadowBarrier();
shadowBarrier.Heading = rotateBarrier.Value;
shadowBarrier.Position = MousePositionInWorld.GetPositionForBarrier;
Rage.Native.NativeFunction.Natives.PLACE_OBJECT_ON_GROUND_PROPERLY(shadowBarrier);
Rage.Native.NativeFunction.Natives.SET_ENTITY_TRAFFICLIGHT_OVERRIDE(shadowBarrier, setBarrierTrafficLight.Index);
}
void DisableBarrierMenuOptionsIfShadowConeTooFar()
{
if (!shadowBarrier && MousePositionInWorld.GetPositionForBarrier.DistanceTo2D(Game.LocalPlayer.Character.Position) <= Settings.BarrierPlacementDistance)
{
CreateShadowBarrier();
}
else if (shadowBarrier && shadowBarrier.Position.DistanceTo2D(Game.LocalPlayer.Character.Position) > Settings.BarrierPlacementDistance)
{
barrierList.Enabled = false;
rotateBarrier.Enabled = false;
shadowBarrier.Delete();
}
else if (shadowBarrier && shadowBarrier.Position.DistanceTo2D(Game.LocalPlayer.Character.Position) <= Settings.BarrierPlacementDistance && barrierList.SelectedItem == "Flare")
{
barrierList.Enabled = true;
rotateBarrier.Enabled = false;
}
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;
}
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)
private static void SpawnBarrier()
{
if (selectedItem == barrierList as UIMenuItem)
if(barrierList.SelectedItem == "Flare")
{
// 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();
}
SpawnFlare();
}
if (selectedItem == removeBarrierOptions as UIMenuItem)
else
{
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);
var barrier = new 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);
if (invincible.Checked)
{
Rage.Native.NativeFunction.Natives.SET_DISABLE_FRAG_DAMAGE(barrier, true);
if(barrier.Model.Name != "prop_barrier_wat_03a")
{
Rage.Native.NativeFunction.Natives.SET_DISABLE_BREAKING(barrier, true);
}
}
if (Settings.EnableAdvancedBarricadeOptions)
{
Rage.Native.NativeFunction.Natives.x971DA0055324D033(barrier, barrierTexture.Value);
if (setBarrierLights.Checked)
{
Rage.Native.NativeFunction.Natives.SET_ENTITY_LIGHTS(barrier, false);
}
else
{
Rage.Native.NativeFunction.Natives.SET_ENTITY_LIGHTS(barrier, true);
}
Rage.Native.NativeFunction.Natives.SET_ENTITY_TRAFFICLIGHT_OVERRIDE(barrier, setBarrierTrafficLight.Index);
barrier.IsPositionFrozen = true;
GameFiber.Sleep(50);
if (barrier)
{
barrier.IsPositionFrozen = false;
}
}
barriers.Add(new Barrier(barrier, barrier.Position, barrier.Heading));
removeBarrierOptions.Enabled = true;
resetBarriers.Enabled = true;
@ -225,8 +209,8 @@ namespace SceneManager
void SpawnFlare()
{
var flare = new Weapon("weapon_flare", shadowBarrier.Position, 1);
Rage.Native.NativeFunction.Natives.SET_ENTITY_DYNAMIC(flare, true);
GameFiber.Sleep(1);
GameFiber.StartNew(() =>
{
while (flare && flare.HeightAboveGround > 0.05f)
@ -243,44 +227,177 @@ namespace SceneManager
barriers.Add(new Barrier(flare, flare.Position, flare.Heading));
removeBarrierOptions.Enabled = true;
}
}
void RemoveBarrier()
internal static void RotateBarrier()
{
shadowBarrier.Heading = rotateBarrier.Value;
shadowBarrier.Position = MousePositionInWorld.GetPositionForBarrier;
Rage.Native.NativeFunction.Natives.PLACE_OBJECT_ON_GROUND_PROPERLY(shadowBarrier);
}
private static void RemoveBarrier()
{
switch (removeBarrierOptions.Index)
{
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;
}
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;
removeBarrierOptions.Enabled = barriers.Count == 0 ? false : true;
resetBarriers.Enabled = barriers.Count == 0 ? false : true;
}
private static void 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);
Rage.Native.NativeFunction.Natives.SET_ENTITY_TRAFFICLIGHT_OVERRIDE(newBarrier, setBarrierTrafficLight.Index);
newBarrier.IsPositionFrozen = true;
GameFiber.Sleep(50);
if (newBarrier)
{
newBarrier.IsPositionFrozen = false;
}
barriers.Add(new Barrier(newBarrier, newBarrier.Position, newBarrier.Heading));
if (barrier.Object)
{
barrier.Object.Delete();
}
barriers.Remove(barrier);
}
currentBarriers.Clear();
}
private static void SetBarrierLights()
{
if (setBarrierLights.Checked)
{
Rage.Native.NativeFunction.Natives.SET_ENTITY_LIGHTS(shadowBarrier, false);
}
else
{
Rage.Native.NativeFunction.Natives.SET_ENTITY_LIGHTS(shadowBarrier, true);
}
Rage.Native.NativeFunction.Natives.SET_ENTITY_TRAFFICLIGHT_OVERRIDE(shadowBarrier, setBarrierTrafficLight.Index);
}
private static void BarrierMenu_OnCheckboxChanged(UIMenu sender, UIMenuCheckboxItem checkbox, bool @checked)
{
if(checkbox == setBarrierLights)
{
SetBarrierLights();
}
}
private static float SetMenuWidth()
private static void BarrierMenu_OnScrollerChanged(UIMenu sender, UIMenuScrollerItem scrollerItem, int oldIndex, int newIndex)
{
if (scrollerItem == barrierList)
{
if (shadowBarrier)
{
shadowBarrier.Delete();
}
barrierTexture.Index = 0;
if(barrierList.SelectedItem == "Flare")
{
rotateBarrier.Enabled = false;
}
else
{
rotateBarrier.Enabled = true;
}
barrierMenu.Width = SetMenuWidth();
}
if (scrollerItem == barrierTexture)
{
Rage.Native.NativeFunction.Natives.x971DA0055324D033(shadowBarrier, barrierTexture.Value);
}
if (scrollerItem == setBarrierTrafficLight)
{
Rage.Native.NativeFunction.Natives.SET_ENTITY_TRAFFICLIGHT_OVERRIDE(shadowBarrier, setBarrierTrafficLight.Index);
}
if (scrollerItem == rotateBarrier)
{
RotateBarrier();
}
}
private static void BarrierMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == barrierList)
{
SpawnBarrier();
}
if (selectedItem == removeBarrierOptions)
{
RemoveBarrier();
}
if (selectedItem == resetBarriers)
{
ResetBarriers();
}
}
private static void BarrierMenu_OnMenuOpen(UIMenu menu)
{
var scrollerItems = new List<UIMenuScrollerItem> { barrierList, barrierTexture, setBarrierTrafficLight, rotateBarrier, removeBarrierOptions };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>() { { invincible, null }, {setBarrierLights, SetBarrierLights} };
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
{ barrierList, SpawnBarrier },
{ removeBarrierOptions, RemoveBarrier },
{ resetBarriers, ResetBarriers },
};
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~The shadow barrier will disappear if you aim too far away.");
CreateShadowBarrier();
GameFiber ShadowConeLoopFiber = new GameFiber(() => LoopToDisplayShadowBarrier());
ShadowConeLoopFiber.Start();
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
internal 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

View file

@ -1,4 +1,7 @@
using System.Drawing;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
@ -15,6 +18,9 @@ namespace SceneManager
{
editPathMenu.ParentMenu = PathMainMenu.pathMainMenu;
MenuManager.menuPool.Add(editPathMenu);
editPathMenu.OnItemSelect += EditPath_OnItemSelected;
editPathMenu.OnCheckboxChange += EditPath_OnCheckboxChange;
editPathMenu.OnMenuOpen += EditPath_OnMenuOpen;
}
internal static void BuildEditPathMenu()
@ -26,22 +32,48 @@ namespace SceneManager
deletePath.ForeColor = Color.Gold;
editPathMenu.RefreshIndex();
editPathMenu.OnItemSelect += EditPath_OnItemSelected;
editPathMenu.OnCheckboxChange += EditPath_OnCheckboxChange;
}
private static void EditPathWaypoints()
{
if (!SettingsMenu.threeDWaypoints.Checked)
{
Hints.Display($"~o~Scene Manager ~y~[Hint]\n~w~You have 3D waypoints disabled in your settings. It's recommended to enable 3D waypoints while working with waypoints.");
}
EditWaypointMenu.BuildEditWaypointMenu();
}
private static void DeletePath()
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
PathMainMenu.DeletePath(currentPath, PathMainMenu.Delete.Single);
}
private static void DisablePath()
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
if (disablePath.Checked)
{
currentPath.DisablePath();
Game.LogTrivial($"Path {currentPath.Number} disabled.");
}
else
{
currentPath.EnablePath();
Game.LogTrivial($"Path {currentPath.Number} enabled.");
}
}
private static void EditPath_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
if (selectedItem == editPathWaypoints)
{
EditWaypointMenu.BuildEditWaypointMenu();
EditPathWaypoints();
}
if (selectedItem == deletePath)
{
PathMainMenu.DeletePath(currentPath, PathMainMenu.Delete.Single);
DeletePath();
}
}
@ -49,18 +81,21 @@ namespace SceneManager
{
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.");
}
DisablePath();
}
}
private static void EditPath_OnMenuOpen(UIMenu menu)
{
var scrollerItems = new List<UIMenuScrollerItem> { };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>() { { disablePath, DisablePath } };
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
{ editPathWaypoints, EditPathWaypoints },
{ deletePath, DeletePath }
};
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Rage;
@ -9,35 +9,33 @@ 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 UIMenuCheckboxItem updateWaypointPosition = new UIMenuCheckboxItem("Update Waypoint Position", false, "Updates the waypoint's position to the player's chosen 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);
editWaypointMenu.OnScrollerChange += EditWaypoint_OnScrollerChanged;
editWaypointMenu.OnCheckboxChange += EditWaypoint_OnCheckboxChanged;
editWaypointMenu.OnItemSelect += EditWaypoint_OnItemSelected;
editWaypointMenu.OnMenuOpen += EditWaypoint_OnMenuOpen;
}
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();
@ -45,7 +43,6 @@ namespace SceneManager
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"));
@ -85,17 +82,140 @@ namespace SceneManager
editWaypointMenu.RefreshIndex();
editWaypointMenu.Visible = true;
}
}
void ResetEventHandlerSubscriptions()
private static void UpdateCollectorMenuOptionsStatus()
{
if (collectorWaypoint.Checked)
{
editWaypointMenu.OnItemSelect -= EditWaypoint_OnItemSelected;
editWaypointMenu.OnCheckboxChange -= EditWaypoint_OnCheckboxChanged;
editWaypointMenu.OnScrollerChange -= EditWaypoint_OnScrollerChanged;
changeCollectorRadius.Enabled = true;
changeSpeedZoneRadius.Enabled = true;
}
else
{
changeCollectorRadius.Enabled = false;
changeSpeedZoneRadius.Enabled = false;
}
}
editWaypointMenu.OnScrollerChange += EditWaypoint_OnScrollerChanged;
editWaypointMenu.OnCheckboxChange += EditWaypoint_OnCheckboxChanged;
editWaypointMenu.OnItemSelect += EditWaypoint_OnItemSelected;
private static void UpdateWaypoint()
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
var currentWaypoint = currentPath.Waypoints[editWaypoint.Index];
DrivingFlagType drivingFlag = directWaypointBehavior.Checked ? DrivingFlagType.Direct : DrivingFlagType.Normal;
if (currentPath.Waypoints.Count == 1)
{
currentWaypoint.UpdateWaypoint(currentWaypoint, MousePositionInWorld.GetPosition, drivingFlag, stopWaypointType.Checked, SetDriveSpeedForWaypoint(), true, changeCollectorRadius.Value, changeSpeedZoneRadius.Value, updateWaypointPosition.Checked);
}
else
{
currentWaypoint.UpdateWaypoint(currentWaypoint, MousePositionInWorld.GetPosition, drivingFlag, stopWaypointType.Checked, SetDriveSpeedForWaypoint(), collectorWaypoint.Checked, changeCollectorRadius.Value, changeSpeedZoneRadius.Value, updateWaypointPosition.Checked);
}
Game.LogTrivial($"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 ~g~[Success]~w~\nWaypoint {currentWaypoint.Number} updated.");
}
private static void RemoveWaypoint()
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
var currentWaypoint = currentPath.Waypoints[editWaypoint.Index];
DrivingFlagType drivingFlag = directWaypointBehavior.Checked ? DrivingFlagType.Direct : DrivingFlagType.Normal;
if (currentPath.Waypoints.Count == 1)
{
Game.LogTrivial($"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);
Game.LogTrivial($"[Path {currentPath.Number}] Waypoint {currentWaypoint.Number} ({currentWaypoint.DrivingFlagType}) removed");
foreach (Waypoint wp in currentPath.Waypoints)
{
wp.Number = currentPath.Waypoints.IndexOf(wp) + 1;
}
editWaypointMenu.Clear();
BuildEditWaypointMenu();
if (currentPath.Waypoints.Count == 1)
{
Hints.Display($"~o~Scene Manager ~y~[Hint]~w~\nYour path's first waypoint ~b~must~w~ be a collector. If it's not, it will automatically be made into one.");
Game.LogTrivial($"The path only has 1 waypoint left, this waypoint must be a collector.");
currentPath.Waypoints[0].UpdateWaypoint(currentWaypoint, MousePositionInWorld.GetPosition, drivingFlag, stopWaypointType.Checked, SetDriveSpeedForWaypoint(), true, changeCollectorRadius.Value, changeSpeedZoneRadius.Value, updateWaypointPosition.Checked);
collectorWaypoint.Checked = true;
changeCollectorRadius.Enabled = true;
changeSpeedZoneRadius.Enabled = true;
}
}
}
private static void AddAsNewWaypoint()
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
DrivingFlagType drivingFlag = directWaypointBehavior.Checked ? DrivingFlagType.Direct : DrivingFlagType.Normal;
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, MousePositionInWorld.GetPosition, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, newWaypointBlip, true, changeCollectorRadius.Value, changeSpeedZoneRadius.Value));
}
else
{
currentPath.Waypoints.Add(new Waypoint(currentPath, currentPath.Waypoints.Last().Number + 1, MousePositionInWorld.GetPosition, 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;
Game.LogTrivial($"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(MousePositionInWorld.GetPosition)
{
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;
}
}
@ -106,8 +226,6 @@ namespace SceneManager
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;
@ -156,133 +274,50 @@ namespace SceneManager
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();
UpdateWaypoint();
}
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;
}
AddAsNewWaypoint();
}
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;
}
}
RemoveWaypoint();
}
}
private static void EditWaypoint_OnMenuOpen(UIMenu menu)
{
var scrollerItems = new List<UIMenuScrollerItem> { editWaypoint, changeWaypointSpeed, changeCollectorRadius, changeSpeedZoneRadius };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>()
{
{ collectorWaypoint, UpdateCollectorMenuOptionsStatus },
{ stopWaypointType, null },
{ directWaypointBehavior, null },
{ updateWaypointPosition, null }
};
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
{ updateWaypoint, UpdateWaypoint },
{ removeWaypoint, RemoveWaypoint },
{ addAsNewWaypoint, AddAsNewWaypoint }
};
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
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

@ -1,6 +1,10 @@
using RAGENativeUI;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace SceneManager
{
@ -15,7 +19,7 @@ namespace SceneManager
MenuManager.menuPool.Add(mainMenu);
}
public static void BuildMainMenu()
internal static void BuildMainMenu()
{
mainMenu.AddItem(navigateToPathMenu = new UIMenuItem("Path Menu"));
navigateToPathMenu.ForeColor = Color.Gold;
@ -28,15 +32,36 @@ namespace SceneManager
mainMenu.BindMenuToItem(SettingsMenu.settingsMenu, navigateToSettingsMenu);
mainMenu.RefreshIndex();
mainMenu.OnItemSelect += MainMenu_OnItemSelected;
mainMenu.OnMenuOpen += MainMenu_OnMenuOpen;
}
private static void MainMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
private static void ShowPathMainMenu()
{
if (selectedItem == navigateToBarrierMenu)
PathMainMenu.pathMainMenu.Visible = true;
}
private static void ShowBarrierMenu()
{
BarrierMenu.barrierMenu.Visible = true;
}
private static void ShowSettingsMenu()
{
SettingsMenu.settingsMenu.Visible = true;
}
private static void MainMenu_OnMenuOpen(UIMenu menu)
{
var scrollerItems = new List<UIMenuScrollerItem> { };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>() { };
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
BarrierMenu.CreateShadowBarrier(BarrierMenu.barrierMenu);
}
{ navigateToPathMenu, ShowPathMainMenu },
{ navigateToBarrierMenu, ShowBarrierMenu },
{ navigateToSettingsMenu, ShowSettingsMenu }
};
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
}
}

View file

@ -34,20 +34,8 @@ namespace SceneManager
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

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Net.Configuration;
using System.Windows.Forms;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
@ -10,22 +12,24 @@ 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 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 checked, 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);
private static List<UIMenuItem> menuItems = new List<UIMenuItem> {collectorWaypoint, collectorRadius, speedZoneRadius, stopWaypointType, directWaypointBehavior, waypointSpeed, trafficAddWaypoint, trafficRemoveWaypoint, trafficEndPath };
internal static void InstantiateMenu()
{
pathCreationMenu.ParentMenu = PathMainMenu.pathMainMenu;
MenuManager.menuPool.Add(pathCreationMenu);
pathCreationMenu.OnItemSelect += PathCreation_OnItemSelected;
pathCreationMenu.OnCheckboxChange += PathCreation_OnCheckboxChanged;
pathCreationMenu.OnScrollerChange += PathCreation_OnScrollerChanged;
pathCreationMenu.OnMenuOpen += PathCreation_OnMenuOpen;
}
internal static void BuildPathCreationMenu()
@ -35,18 +39,20 @@ namespace SceneManager
collectorWaypoint.Checked = true;
pathCreationMenu.AddItem(collectorRadius);
collectorRadius.Index = 0;
collectorRadius.Index = Settings.CollectorRadius - 1;
collectorRadius.Enabled = true;
pathCreationMenu.AddItem(speedZoneRadius);
speedZoneRadius.Index = 0;
speedZoneRadius.Index = (Settings.SpeedZoneRadius / 5) - 1;
speedZoneRadius.Enabled = true;
pathCreationMenu.AddItem(stopWaypointType);
stopWaypointType.Checked = Settings.StopWaypoint;
pathCreationMenu.AddItem(directWaypointBehavior);
directWaypointBehavior.Checked = Settings.DirectDrivingBehavior;
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;
waypointSpeed.Index = (Settings.WaypointSpeed / 5) - 1;
pathCreationMenu.AddItem(trafficAddWaypoint);
trafficAddWaypoint.ForeColor = Color.Gold;
@ -60,206 +66,175 @@ namespace SceneManager
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)
private static void UpdateCollectorMenuOptionsStatus()
{
if(checkboxItem == collectorWaypoint)
if (collectorWaypoint.Checked)
{
collectorRadius.Enabled = collectorWaypoint.Checked ? true : false;
speedZoneRadius.Enabled = collectorWaypoint.Checked ? true : false;
collectorRadius.Enabled = true;
speedZoneRadius.Enabled = true;
}
else
{
collectorRadius.Enabled = false;
speedZoneRadius.Enabled = false;
}
}
private static void PathCreation_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == trafficAddWaypoint)
private static void AddNewWaypoint()
{
var anyPathsExist = PathMainMenu.paths.Count > 0;
var waypointPosition = MousePositionInWorld.GetPosition;
if (!anyPathsExist)
{
var anyPathsExist = PathMainMenu.paths.Count > 0;
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);
}
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;
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, waypointPosition, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, CreateWaypointBlip(), true, collectorRadius.Value, speedZoneRadius.Value));
}
else
{
PathMainMenu.paths[pathIndex].Waypoints.Add(new Waypoint(firstNonNullPath, waypointNumber, waypointPosition, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, CreateWaypointBlip()));
}
Game.LogTrivial($"Path {pathNumber} Waypoint {waypointNumber} added [Driving style: {drivingFlag} | Stop waypoint: {stopWaypointType.Checked} | Speed: {waypointSpeed.Value} | Collector: {collectorWaypoint.Checked}]");
if (collectorWaypoint.Checked)
ToggleTrafficEndPathMenuItem(pathIndex);
collectorWaypoint.Enabled = true;
collectorWaypoint.Checked = false;
if (collectorWaypoint.Checked)
{
collectorRadius.Enabled = true;
speedZoneRadius.Enabled = true;
}
else
{
collectorRadius.Enabled = false;
speedZoneRadius.Enabled = false;
}
trafficRemoveWaypoint.Enabled = true;
PathMainMenu.createNewPath.Text = $"Continue Creating Path {pathNumber}";
float SetDriveSpeedForWaypoint()
{
float convertedSpeed;
if (SettingsMenu.speedUnits.SelectedItem == SpeedUnits.MPH)
{
PathMainMenu.paths[pathIndex].Waypoints.Add(new Waypoint(firstNonNullPath, waypointNumber, Game.LocalPlayer.Character.Position, SetDriveSpeedForWaypoint(), drivingFlag, stopWaypointType.Checked, CreateWaypointBlip(), true, collectorRadius.Value, speedZoneRadius.Value));
//Logger.Log($"Original speed: {waypointSpeeds[waypointSpeed.Index]}{SettingsMenu.speedUnits.SelectedItem}");
convertedSpeed = MathHelper.ConvertMilesPerHourToMetersPerSecond(waypointSpeed.Value);
//Logger.Log($"Converted speed: {convertedSpeed}m/s");
}
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;
collectorWaypoint.Checked = false;
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;
//Logger.Log($"Original speed: {waypointSpeeds[waypointSpeed.Index]}{SettingsMenu.speedUnits.SelectedItem}");
convertedSpeed = MathHelper.ConvertKilometersPerHourToMetersPerSecond(waypointSpeed.Value);
//Logger.Log($"Converted speed: {convertedSpeed}m/s");
}
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;
}
return convertedSpeed;
}
if (selectedItem == trafficRemoveWaypoint)
Blip CreateWaypointBlip()
{
// 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++)
var spriteNumericalEnum = pathIndex + 17; // 17 because the numerical value of these sprites are always 17 more than the path index
var blip = new Blip(waypointPosition)
{
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();
Scale = 0.5f,
Sprite = (BlipSprite)spriteNumericalEnum
};
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 (collectorWaypoint.Checked)
{
blip.Color = Color.Blue;
}
}
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++)
else if (stopWaypointType.Checked)
{
var currentPath = PathMainMenu.paths[i];
if (PathMainMenu.paths.ElementAtOrDefault(i) != null && currentPath.State == State.Creating)
blip.Color = Color.Red;
}
else
{
blip.Color = Color.Green;
}
if (!SettingsMenu.mapBlips.Checked)
{
blip.Alpha = 0f;
}
return blip;
}
}
private static void RemoveWaypoint()
{
for (int i = 0; i < PathMainMenu.paths.Count; i++)
{
if (PathMainMenu.paths.ElementAtOrDefault(i) != null && PathMainMenu.paths[i].State == State.Creating)
{
Game.LogTrivial($"[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)
{
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;
currentPath.LoopForVehiclesToBeDismissed();
PathMainMenu.paths[i].Waypoints.Last().CollectorRadiusBlip.Delete();
}
PathMainMenu.paths[i].Waypoints.RemoveAt(PathMainMenu.paths[i].Waypoints.IndexOf(PathMainMenu.paths[i].Waypoints.Last()));
GameFiber.StartNew(() =>
{
currentPath.LoopWaypointCollection();
//foreach(Waypoint waypoint in PathMainMenu.paths[i].Waypoints)
//{
// GameFiber WaypointVehicleCollectorFiber = new GameFiber(() => waypoint.CollectVehicles(PathMainMenu.paths));
// WaypointVehicleCollectorFiber.Start();
// GameFiber.Sleep(1000);
//}
});
ToggleTrafficEndPathMenuItem(i);
PathMainMenu.createNewPath.Text = "Create New Path";
PathMainMenu.BuildPathMenu();
PathMainMenu.pathMainMenu.RefreshIndex();
pathCreationMenu.RefreshIndex();
waypointSpeed.Index = 0;
collectorWaypoint.Enabled = false;
// 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;
speedZoneRadius.Index = 0;
collectorRadius.Enabled = true;
collectorRadius.Index = 0;
stopWaypointType.Checked = false;
directWaypointBehavior.Checked = false;
trafficRemoveWaypoint.Enabled = false;
trafficEndPath.Enabled = false;
PathMainMenu.pathMainMenu.Visible = true;
break;
}
}
}
}
private static void PathCreation_OnScrollerChanged(UIMenu sender, UIMenuScrollerItem scrollerItem, int first, int last)
private static void EndPath()
{
if (scrollerItem == collectorRadius)
for (int i = 0; i < PathMainMenu.paths.Count; i++)
{
if (collectorRadius.Value > speedZoneRadius.Value)
var currentPath = PathMainMenu.paths[i];
if (PathMainMenu.paths.ElementAtOrDefault(i) != null && currentPath.State == State.Creating)
{
while(collectorRadius.Value > speedZoneRadius.Value)
{
speedZoneRadius.ScrollToNextOption();
}
}
}
Game.LogTrivial($"[Path Creation] Path {currentPath.Number} finished with {currentPath.Waypoints.Count} waypoints.");
Game.DisplayNotification($"~o~Scene Manager ~g~[Success]\n~w~Path {i + 1} complete.");
currentPath.State = State.Finished;
currentPath.IsEnabled = true;
currentPath.Number = i + 1;
currentPath.LoopForVehiclesToBeDismissed();
if(scrollerItem == speedZoneRadius)
{
if(speedZoneRadius.Value < collectorRadius.Value)
{
collectorRadius.Value = speedZoneRadius.Value;
GameFiber.StartNew(() =>
{
currentPath.LoopWaypointCollection();
});
PathMainMenu.createNewPath.Text = "Create New Path";
PathMainMenu.BuildPathMenu();
PathMainMenu.pathMainMenu.RefreshIndex();
pathCreationMenu.Clear();
PathMainMenu.pathMainMenu.Visible = true;
break;
}
}
}
@ -276,14 +251,82 @@ namespace SceneManager
}
}
internal static void AddNewPathToPathsCollection(List<Path> paths, int pathIndex)
private static void AddNewPathToPathsCollection(List<Path> paths, int pathIndex)
{
var pathNum = pathIndex + 1;
Logger.Log($"Creating path {pathNum}");
Game.LogTrivial($"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;
}
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)
{
AddNewWaypoint();
}
if (selectedItem == trafficRemoveWaypoint)
{
RemoveWaypoint();
}
if (selectedItem == trafficEndPath)
{
EndPath();
}
}
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 PathCreation_OnMenuOpen(UIMenu menu)
{
var scrollerItems = new List<UIMenuScrollerItem> { collectorRadius, speedZoneRadius, waypointSpeed };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>()
{
{ collectorWaypoint, UpdateCollectorMenuOptionsStatus},
{ stopWaypointType, null},
{ directWaypointBehavior, null}
};
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
{ trafficAddWaypoint, AddNewWaypoint },
{ trafficRemoveWaypoint, RemoveWaypoint },
{ trafficEndPath, EndPath }
};
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
}
}

View file

@ -2,25 +2,20 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
namespace SceneManager
{
public enum DismissOption
{
FromPath = 0,
FromWaypoint = 1,
FromWorld = 2,
FromPlayer = 3,
FromDirected = 4
}
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" };
private static string[] dismissOptions = new string[] { "From path", "From waypoint", "From world" };
//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;
@ -41,13 +36,13 @@ namespace SceneManager
{
pathMainMenu.ParentMenu = MainMenu.mainMenu;
MenuManager.menuPool.Add(pathMainMenu);
pathMainMenu.OnItemSelect += PathMenu_OnItemSelected;
pathMainMenu.OnCheckboxChange += PathMenu_OnCheckboxChange;
pathMainMenu.OnMenuOpen += PathMenu_OnMenuOpen;
}
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();
@ -81,14 +76,6 @@ namespace SceneManager
}
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)
@ -103,6 +90,72 @@ namespace SceneManager
}
}
private static void GoToEditPathMenu()
{
pathMainMenu.Visible = false;
EditPathMenu.editPathMenu.Visible = true;
}
private static void GoToPathCreationMenu()
{
if (createNewPath.Text.Contains("Continue"))
{
pathMainMenu.Visible = false;
PathCreationMenu.pathCreationMenu.Visible = true;
}
else
{
PathCreationMenu.pathCreationMenu.Clear();
PathCreationMenu.BuildPathCreationMenu();
pathMainMenu.Visible = false;
PathCreationMenu.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].State == State.Creating)
{
Game.DisplayNotification($"~o~Scene Manager~y~[Creating]\n~w~Resuming path {paths[i].Number}");
break;
}
}
}
}
private static void 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 DeleteAllPaths()
{
for (int i = 0; i < paths.Count; i++)
{
DeletePath(paths[i], Delete.All);
}
disableAllPaths.Checked = false;
paths.Clear();
BuildPathMenu();
pathMainMenu.Visible = true;
Game.LogTrivial($"All paths deleted");
Game.DisplayNotification($"~o~Scene Manager\n~w~All paths deleted.");
}
internal static void DeletePath(Path path, Delete pathsToDelete)
{
//Game.LogTrivial($"Preparing to delete path {path.Number}");
@ -138,7 +191,7 @@ namespace SceneManager
{
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(cv.Vehicle, 1f, 1, true);
}
cv.StoppedAtWaypoint = false;
//cv.StoppedAtWaypoint = false;
if (cv.Driver.GetAttachedBlip())
{
cv.Driver.GetAttachedBlip().Delete();
@ -196,129 +249,118 @@ namespace SceneManager
}
}
private static void PathMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
private static void DirectDriver()
{
if (selectedItem == createNewPath)
{
pathMainMenu.Visible = false;
PathCreationMenu.pathCreationMenu.Visible = true;
Draw3DWaypointOnPlayer();
var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(16).Where(v => v != Game.LocalPlayer.Character.CurrentVehicle && v.VehicleAndDriverValid()).FirstOrDefault();
var path = paths[directDriver.Index];
var collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault();
var waypoints = path.Waypoints;
var firstWaypoint = waypoints.First();
var nearestWaypoint = waypoints.Where(wp => wp.Position.DistanceTo2D(nearbyVehicle.FrontPosition) < wp.Position.DistanceTo2D(nearbyVehicle.RearPosition)).OrderBy(wp => wp.Position.DistanceTo2D(nearbyVehicle)).FirstOrDefault();
// 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 (nearbyVehicle)
{
var nearbyVehiclePath = paths.Where(p => p.CollectedVehicles.Any(v => v.Vehicle == nearbyVehicle)).FirstOrDefault();
if (nearbyVehiclePath != null)
{
if (paths.ElementAtOrDefault(i) != null && paths[i].State == State.Creating)
var nearbyCollectedVehicle = nearbyVehiclePath.CollectedVehicles.Where(v => v.Vehicle == nearbyVehicle).FirstOrDefault();
if (nearbyCollectedVehicle != null)
{
//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);
}
disableAllPaths.Checked = false;
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();
var path = paths[directDriver.Index];
var collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault();
var waypoints = path.Waypoints;
var firstWaypoint = waypoints.First();
var nearestWaypoint = waypoints.Where(wp => wp.Position.DistanceTo2D(nearbyVehicle.FrontPosition) < wp.Position.DistanceTo2D(nearbyVehicle.RearPosition)).OrderBy(wp => wp.Position.DistanceTo2D(nearbyVehicle)).FirstOrDefault();
if (nearbyVehicle)
{
var nearbyVehiclePath = paths.Where(p => p.CollectedVehicles.Any(v => v.Vehicle == nearbyVehicle)).FirstOrDefault();
if(nearbyVehiclePath != null)
{
var nearbyCollectedVehicle = nearbyVehiclePath.CollectedVehicles.Where(v => v.Vehicle == nearbyVehicle).FirstOrDefault();
if (nearbyCollectedVehicle != null)
{
nearbyCollectedVehicle.Dismiss(DismissOption.FromDirected, path);
if (directOptions.SelectedItem == "First waypoint")
{
GameFiber.StartNew(() =>
{
AITasking.AssignWaypointTasks(nearbyCollectedVehicle, path, firstWaypoint);
});
}
else
{
if (nearestWaypoint != null)
{
GameFiber.StartNew(() =>
{
AITasking.AssignWaypointTasks(nearbyCollectedVehicle, path, nearestWaypoint);
});
}
}
return;
}
}
// The vehicle should only be added to the collection when it's not null AND if the selected item is First Waypoint OR if the selected item is nearestWaypoint AND nearestWaypoint is not null
if (collectedVehicle == null && directOptions.SelectedItem == "First waypoint" || (directOptions.SelectedItem == "Nearest waypoint" && nearestWaypoint != null))
{
Game.LogTrivial($"[Direct Driver] Adding {nearbyVehicle.Model.Name} to collection.");
path.CollectedVehicles.Add(new CollectedVehicle(nearbyVehicle, path));
collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault();
//Logger.Log($"Collected vehicle is {collectedVehicle.Vehicle.Model.Name}");
}
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)
nearbyCollectedVehicle.Dismiss(DismissOption.FromDirected, path);
if (directOptions.SelectedItem == "First waypoint")
{
GameFiber.StartNew(() =>
{
AITasking.AssignWaypointTasks(collectedVehicle, path, nearestWaypoint);
nearbyCollectedVehicle.AssignWaypointTasks(path, firstWaypoint);
//AITasking.AssignWaypointTasks(nearbyCollectedVehicle, path, firstWaypoint);
});
}
else
{
if (nearestWaypoint != null)
{
GameFiber.StartNew(() =>
{
nearbyCollectedVehicle.AssignWaypointTasks(path, nearestWaypoint);
//AITasking.AssignWaypointTasks(nearbyCollectedVehicle, path, nearestWaypoint);
});
}
}
return;
}
}
// The vehicle should only be added to the collection when it's not null AND if the selected item is First Waypoint OR if the selected item is nearestWaypoint AND nearestWaypoint is not null
if (collectedVehicle == null && directOptions.SelectedItem == "First waypoint" || (directOptions.SelectedItem == "Nearest waypoint" && nearestWaypoint != null))
{
Game.LogTrivial($"[Direct Driver] Adding {nearbyVehicle.Model.Name} to collection.");
path.CollectedVehicles.Add(new CollectedVehicle(nearbyVehicle, path));
collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault();
//Logger.Log($"Collected vehicle is {collectedVehicle.Vehicle.Model.Name}");
}
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(() =>
{
collectedVehicle.AssignWaypointTasks(path, firstWaypoint);
//AITasking.AssignWaypointTasks(collectedVehicle, path, firstWaypoint);
});
}
else
{
if (nearestWaypoint != null)
{
GameFiber.StartNew(() =>
{
collectedVehicle.AssignWaypointTasks(path, nearestWaypoint);
//AITasking.AssignWaypointTasks(collectedVehicle, path, nearestWaypoint);
});
}
}
}
}
if (selectedItem == dismissDriver)
private static void DismissDriver()
{
var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(16).Where(v => v != Game.LocalPlayer.Character.CurrentVehicle && v.VehicleAndDriverValid()).FirstOrDefault();
if (nearbyVehicle)
{
var nearbyVehicle = Game.LocalPlayer.Character.GetNearbyVehicles(16).Where(v => v != Game.LocalPlayer.Character.CurrentVehicle && v.VehicleAndDriverValid()).FirstOrDefault();
if (nearbyVehicle)
if (!paths.Any() && dismissDriver.Index == (int)DismissOption.FromWorld)
{
if (!paths.Any() && dismissDriver.Index == (int)DismissOption.FromWorld)
Game.LogTrivial($"Dismissed {nearbyVehicle.Model.Name} from the world");
while (nearbyVehicle && nearbyVehicle.HasOccupants)
{
foreach (Ped occupant in nearbyVehicle.Occupants)
{
occupant.Delete();
}
GameFiber.Yield();
}
if (nearbyVehicle)
{
nearbyVehicle.Delete();
}
return;
}
foreach (Path path in paths)
{
var collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault();
if (collectedVehicle != null)
{
collectedVehicle.Dismiss((DismissOption)dismissDriver.Index);
break;
}
else if (dismissDriver.Index == (int)DismissOption.FromWorld)
{
Game.LogTrivial($"Dismissed {nearbyVehicle.Model.Name} from the world");
while (nearbyVehicle && nearbyVehicle.HasOccupants)
@ -333,91 +375,67 @@ namespace SceneManager
{
nearbyVehicle.Delete();
}
return;
}
foreach(Path path in paths)
{
var collectedVehicle = path.CollectedVehicles.Where(cv => cv.Vehicle == nearbyVehicle).FirstOrDefault();
if (collectedVehicle != null)
{
collectedVehicle.Dismiss((DismissOption)dismissDriver.Index);
break;
}
else if (dismissDriver.Index == (int)DismissOption.FromWorld)
{
Game.LogTrivial($"Dismissed {nearbyVehicle.Model.Name} from the world");
while (nearbyVehicle && nearbyVehicle.HasOccupants)
{
foreach (Ped occupant in nearbyVehicle.Occupants)
{
occupant.Delete();
}
GameFiber.Yield();
}
if (nearbyVehicle)
{
nearbyVehicle.Delete();
}
break;
}
break;
}
}
}
}
private static void PathMenu_OnItemSelected(UIMenu sender, UIMenuItem selectedItem, int index)
{
if (selectedItem == createNewPath)
{
GoToPathCreationMenu();
}
if (selectedItem == editPath)
{
pathMainMenu.Visible = false;
EditPathMenu.editPathMenu.Visible = true;
}
if (selectedItem == deleteAllPaths)
{
DeleteAllPaths();
}
if (selectedItem == directDriver)
{
DirectDriver();
}
if (selectedItem == dismissDriver)
{
DismissDriver();
}
}
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.");
}
DisableAllPaths();
}
}
private static void Draw3DWaypointOnPlayer()
private static void PathMenu_OnMenuOpen(UIMenu menu)
{
GameFiber.StartNew(() =>
var scrollerItems = new List<UIMenuScrollerItem> { directOptions, directDriver, dismissDriver, editPath };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>()
{
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();
}
});
{ disableAllPaths, DisableAllPaths }
};
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
{ createNewPath, GoToPathCreationMenu },
{ editPath, GoToEditPathMenu },
{ deleteAllPaths, DeleteAllPaths },
{ directDriver, DirectDriver },
{ dismissDriver, DismissDriver }
};
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
}
}

View file

@ -2,6 +2,9 @@
using RAGENativeUI;
using RAGENativeUI.Elements;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace SceneManager
{
@ -19,6 +22,10 @@ namespace SceneManager
{
settingsMenu.ParentMenu = MainMenu.mainMenu;
MenuManager.menuPool.Add(settingsMenu);
settingsMenu.OnCheckboxChange += SettingsMenu_OnCheckboxChange;
settingsMenu.OnScrollerChange += SettingsMenu_OnScrollerChange;
settingsMenu.OnItemSelect += SettingsMenu_OnItemSelected;
settingsMenu.OnMenuOpen += SettingsMenu_OnMenuOpen;
}
internal static void BuildSettingsMenu()
@ -30,18 +37,49 @@ namespace SceneManager
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 ToggleMapBlips()
{
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();
}
}
}
}
private static void ToggleHints()
{
Hints.Enabled = hints.Checked ? true : false;
}
private static void ToggleSettings()
{
Settings.UpdateSettings(threeDWaypoints.Checked, mapBlips.Checked, hints.Checked, speedUnits.SelectedItem);
Game.DisplayHelp($"Scene Manager settings saved");
}
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");
Game.DisplayHelp($"Scene Manager settings saved");
}
}
@ -49,26 +87,7 @@ namespace SceneManager
{
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();
}
}
}
ToggleMapBlips();
}
if (checkboxItem == hints)
@ -86,5 +105,22 @@ namespace SceneManager
PathCreationMenu.BuildPathCreationMenu();
}
}
private static void SettingsMenu_OnMenuOpen(UIMenu menu)
{
var scrollerItems = new List<UIMenuScrollerItem> { speedUnits };
var checkboxItems = new Dictionary<UIMenuCheckboxItem, RNUIMouseInputHandler.Function>()
{
{ threeDWaypoints, null},
{ mapBlips, ToggleMapBlips},
{ hints, ToggleHints}
};
var selectItems = new Dictionary<UIMenuItem, RNUIMouseInputHandler.Function>()
{
{ saveSettings, ToggleSettings }
};
RNUIMouseInputHandler.Initialize(menu, scrollerItems, checkboxItems, selectItems);
}
}
}

View file

@ -0,0 +1,42 @@
using Rage;
using RAGENativeUI.Elements;
namespace SceneManager
{
internal static class MousePositionInWorld
{
internal static Vector3 GetPosition { get { return GetMousePositionInWorld(); } }
internal static Vector3 GetPositionForBarrier { get { return GetMousePositionInWorld(Settings.BarrierPlacementDistance); } }
private static Vector3 GetMousePositionInWorld(float maxDistance = 100f)
{
HitResult TracePlayerView(float maxTraceDistance = 100f, 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);
return World.TraceLine(start, end, flags);
}
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();
}
}
return TracePlayerView(maxDistance, TraceFlags.IntersectWorld).HitPosition;
}
}
}

View file

@ -1,20 +1,21 @@
using Rage;
using System.Collections.Generic;
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 Ped Driver { get; private set; }
internal Vehicle Vehicle { get; private set; }
internal Path Path { get; private set; }
internal Waypoint CurrentWaypoint { get; private set; }
internal Waypoint NextWaypoint { get; private set; }
internal bool StoppedAtWaypoint { get; set; } = false;
internal bool Dismissed { get; set; } = false;
internal bool StoppedAtWaypoint { get; private set; } = false;
internal bool Dismissed { get; private set; } = false;
internal bool Directed { get; set; } = false;
internal bool SkipWaypoint { get; set; } = false;
internal bool ReadyForDirectTasks { get; set; } = true;
internal bool SkipWaypoint { get; private set; } = false;
internal bool ReadyForDirectTasks { get; private set; } = true;
internal CollectedVehicle(Vehicle vehicle, Path path, Waypoint currentWaypoint)
{
@ -33,12 +34,264 @@ namespace SceneManager
SetPersistence();
}
internal void SetPersistence()
private void SetPersistence()
{
Vehicle.IsPersistent = true;
Driver.IsPersistent = true;
Driver.BlockPermanentEvents = true;
//Logger.Log($"{Vehicle.Model.Name} and driver are now persistent.");
}
internal void AssignWaypointTasks(Path path, Waypoint currentWaypoint)
{
// Driving styles https://gtaforums.com/topic/822314-guide-driving-styles/
// also https://vespura.com/fivem/drivingstyle/
if (!VehicleAndDriverAreValid())
{
return;
}
AssignPathAndCurrentWaypoint();
AssignDirectedTask();
if (currentWaypoint.IsStopWaypoint)
{
StopAtWaypoint();
}
if (path?.Waypoints?.Count > 0 && currentWaypoint != path?.Waypoints?.Last())
{
DriveToNextWaypoint();
}
if (!VehicleAndDriverAreValid() || Directed)
{
return;
}
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] all Path {path.Number} tasks complete.");
if (!Dismissed)
{
Dismiss();
}
void AssignPathAndCurrentWaypoint()
{
Path = path;
if (currentWaypoint != null)
{
CurrentWaypoint = currentWaypoint;
}
else
{
CurrentWaypoint = path.Waypoints[0];
}
}
void AssignDirectedTask()
{
if (currentWaypoint != null && Directed)
{
Dismissed = false;
while (!ReadyForDirectTasks)
{
GameFiber.Yield();
}
if (!VehicleAndDriverAreValid())
{
return;
}
Driver.Tasks.Clear();
DriveToDirectedWaypoint();
}
}
void DriveToDirectedWaypoint()
{
Dismissed = false;
while (!ReadyForDirectTasks)
{
GameFiber.Yield();
}
Driver.Tasks.Clear();
AssignTasksForDirectedDriver();
void AssignTasksForDirectedDriver()
{
float acceptedDistance = GetAcceptedStoppingDistance(Path.Waypoints, Path.Waypoints.IndexOf(currentWaypoint));
Vector3 oldPosition = currentWaypoint.Position;
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] is driving to path {currentWaypoint.Path.Number} waypoint {currentWaypoint.Number} (directed)");
Driver.Tasks.DriveToPosition(currentWaypoint.Position, currentWaypoint.Speed, (VehicleDrivingFlags)currentWaypoint.DrivingFlagType, acceptedDistance);
LoopWhileDrivingToDirectedWaypoint();
void LoopWhileDrivingToDirectedWaypoint()
{
while (VehicleAndDriverAreValid() && !Dismissed && !SkipWaypoint && Vehicle.FrontPosition.DistanceTo2D(oldPosition) > acceptedDistance)
{
if (oldPosition != currentWaypoint.Position)
{
oldPosition = currentWaypoint.Position;
}
GameFiber.Yield();
}
if (Vehicle)
{
Driver.Tasks.PerformDrivingManeuver(Vehicle, VehicleManeuver.GoForwardWithCustomSteeringAngle, 3).WaitForCompletion();
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] directed task is complete, directed is now false");
}
Directed = false;
}
}
}
void DriveToNextWaypoint()
{
if (!VehicleAndDriverAreValid() || CurrentWaypoint == null || Path == null)
{
Game.LogTrivial($"Vehicle, driver, waypoint, or path is null.");
return;
}
Game.LogTrivial($"Preparing to run task loop for {Vehicle.Model.Name} [{Vehicle.Handle}] on path {Path.Number}");
for (int currentWaypointTask = CurrentWaypoint.Number; currentWaypointTask < Path.Waypoints.Count; currentWaypointTask++)
{
var oldPosition = Path.Waypoints[currentWaypointTask].Position;
SkipWaypoint = false;
if (this == null || !Vehicle || Dismissed || Directed)
{
Game.LogTrivial($"Vehicle dismissed, directed, or null, return");
return;
}
if (Driver == null || !Driver || !Vehicle.HasDriver || !Driver.IsAlive || Vehicle.Driver == Game.LocalPlayer.Character)
{
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] does not have a driver/driver is null or driver is dead.");
return;
}
if (Path.Waypoints.ElementAtOrDefault(currentWaypointTask) != null && !StoppedAtWaypoint)
{
CurrentWaypoint = Path.Waypoints[currentWaypointTask];
float acceptedDistance = GetAcceptedStoppingDistance(Path.Waypoints, currentWaypointTask);
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] is driving to path {Path.Number} waypoint {Path.Waypoints[currentWaypointTask].Number} (Stop: {CurrentWaypoint.IsStopWaypoint}, Driving flag: {CurrentWaypoint.DrivingFlagType})");
Driver.Tasks.DriveToPosition(Path.Waypoints[currentWaypointTask].Position, Path.Waypoints[currentWaypointTask].Speed, (VehicleDrivingFlags)Path.Waypoints[currentWaypointTask].DrivingFlagType, acceptedDistance);
LoopWhileDrivingToWaypoint(currentWaypointTask, acceptedDistance, oldPosition);
if (!VehicleAndDriverAreValid())
{
return;
}
if (SkipWaypoint)
{
SkipWaypoint = false;
continue;
}
if (!Dismissed && !Directed && Path.Waypoints.ElementAtOrDefault(currentWaypointTask) != null && Path.Waypoints[currentWaypointTask].IsStopWaypoint)
{
StopAtWaypoint();
}
if (!VehicleAndDriverAreValid() || Dismissed || Directed)
{
return;
}
Driver.Tasks.PerformDrivingManeuver(Vehicle, VehicleManeuver.GoForwardWithCustomSteeringAngle, 3).WaitForCompletion();
}
}
void LoopWhileDrivingToWaypoint(int currentWaypointTask, float acceptedDistance, Vector3 oldPosition)
{
while (VehicleAndDriverAreValid() && !Dismissed && !SkipWaypoint && !Directed && Path.Waypoints.ElementAtOrDefault(currentWaypointTask) != null && Vehicle.FrontPosition.DistanceTo2D(Path.Waypoints[currentWaypointTask].Position) > acceptedDistance)
{
if (oldPosition != Path.Waypoints[currentWaypointTask].Position)
{
oldPosition = Path.Waypoints[currentWaypointTask].Position;
Driver.Tasks.DriveToPosition(Path.Waypoints[currentWaypointTask].Position, Path.Waypoints[currentWaypointTask].Speed, (VehicleDrivingFlags)Path.Waypoints[currentWaypointTask].DrivingFlagType, acceptedDistance);
}
if(Driver.Tasks.CurrentTaskStatus == TaskStatus.NoTask)
{
//Game.DisplayNotification($"~o~Scene Manager ~r~[Error]\n{Vehicle.Model.Name} [{Vehicle.Handle}] driver [{Driver.Handle}] has no task. Reassiging current waypoint task.");
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] driver [{Driver.Handle}] has no task. Reassiging current waypoint task.");
if (Driver.CurrentVehicle)
{
Driver.Tasks.DriveToPosition(Path.Waypoints[currentWaypointTask].Position, Path.Waypoints[currentWaypointTask].Speed, (VehicleDrivingFlags)Path.Waypoints[currentWaypointTask].DrivingFlagType, acceptedDistance);
}
else
{
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] driver [{Driver.Handle}] is not in a vehicle. Exiting loop.");
return;
}
}
GameFiber.Sleep(100);
}
}
}
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);
return acceptedDistance;
}
void StopAtWaypoint()
{
if (!VehicleAndDriverAreValid())
{
return;
}
var stoppingDistance = GetAcceptedStoppingDistance(currentWaypoint.Path.Waypoints, currentWaypoint.Path.Waypoints.IndexOf(currentWaypoint));
Game.LogTrivial($"{Vehicle.Model.Name} stopping at path {currentWaypoint.Path.Number} waypoint.");
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, stoppingDistance, -1, true);
StoppedAtWaypoint = true;
while (currentWaypoint != null && VehicleAndDriverAreValid() && StoppedAtWaypoint && !Directed)
{
GameFiber.Yield();
}
if (Driver && Driver.CurrentVehicle)
{
Game.LogTrivial($"{Vehicle.Model.Name} releasing from stop waypoint.");
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
Driver.Tasks.CruiseWithVehicle(5f);
}
}
bool VehicleAndDriverAreValid()
{
if (this == null)
{
Game.LogTrivial($"CollectedVehicle is null");
return false;
}
if (!Vehicle && !Dismissed)
{
Game.LogTrivial($"Vehicle is null");
Dismiss();
return false;
}
if (!Driver || !Driver.IsAlive)
{
Dismiss();
return false;
}
return true;
}
}
internal void Dismiss(DismissOption dismissOption = DismissOption.FromPath, Path newPath = null)
@ -64,18 +317,20 @@ namespace SceneManager
if (Vehicle)
{
Vehicle.Dismiss();
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
}
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
Path.CollectedVehicles.Remove(this);
return;
}
if(StoppedAtWaypoint)
if(Vehicle && StoppedAtWaypoint)
{
//Logger.Log($"Unstucking {Vehicle.Model.Name}");
StoppedAtWaypoint = false;
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
Driver.Tasks.CruiseWithVehicle(5f);
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Driver.CurrentVehicle, 0f, 1, true);
if (Driver?.CurrentVehicle)
{
Driver.Tasks.CruiseWithVehicle(5f);
}
}
Driver.Tasks.Clear();
@ -96,7 +351,7 @@ namespace SceneManager
void DismissFromWorld()
{
Game.LogTrivial($"Dismissed {Vehicle.Model.Name} from the world");
Game.LogTrivial($"Dismissed {Vehicle.Model.Name} [{Vehicle.Handle}] from the world");
while (Vehicle.HasOccupants)
{
foreach (Ped occupant in Vehicle.Occupants)
@ -113,11 +368,13 @@ namespace SceneManager
{
if (CurrentWaypoint == null || Path == null)
{
Logger.Log($"CurrentWaypoint or Path are null");
Game.LogTrivial($"CurrentWaypoint or Path is null");
return;
}
else if (CurrentWaypoint?.Number != Path?.Waypoints.Count)
if (CurrentWaypoint?.Number != Path?.Waypoints.Count)
{
Logger.Log($"Dismissed from waypoint.");
Game.LogTrivial($"{Vehicle?.Model.Name} [{Vehicle?.Handle}] dismissed from waypoint.");
SkipWaypoint = true;
}
else if (CurrentWaypoint?.Number == Path?.Waypoints.Count)
@ -128,36 +385,35 @@ namespace SceneManager
void DismissFromPath()
{
Logger.Log($"Dismissing from path");
Game.LogTrivial($"Dismissing {Vehicle?.Model.Name} [{Vehicle?.Handle}] 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)
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();
}
Game.LogTrivial($"Nearest collector is null");
}
else
{
Logger.Log($"Nearest collector is null");
while (nearestCollectorWaypoint != null && Vehicle && Vehicle.HasDriver && Driver && Driver.IsAlive && Vehicle.FrontPosition.DistanceTo2D(nearestCollectorWaypoint.Position) <= nearestCollectorWaypoint.CollectorRadius)
{
GameFiber.Yield();
}
}
if (!Vehicle || !Driver)
{
Game.LogTrivial($"Vehicle or driver is null");
return;
}
if (!Directed)
{
Path.CollectedVehicles.Remove(this);
Logger.Log($"{Vehicle.Model.Name} dismissed successfully.");
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] dismissed successfully.");
if (Driver)
{
if (Driver.GetAttachedBlip())
@ -169,12 +425,12 @@ namespace SceneManager
}
if (Vehicle)
{
Vehicle.Dismiss();
Vehicle.IsSirenOn = false;
Vehicle.IsSirenSilent = true;
Vehicle.Dismiss();
}
}
});
}, "DismissFromPath Fiber");
}

View file

@ -81,9 +81,9 @@ namespace SceneManager
{
GameFiber.StartNew(() =>
{
while (SettingsMenu.threeDWaypoints.Checked)
while(true)
{
if (MenuManager.menuPool.IsAnyMenuOpen())
if (SettingsMenu.threeDWaypoints.Checked && (State == State.Finished && MenuManager.menuPool.IsAnyMenuOpen()) || (State == State.Creating && PathCreationMenu.pathCreationMenu.Visible))
{
for (int i = 0; i < Waypoints.Count; i++)
{
@ -133,7 +133,6 @@ namespace SceneManager
internal void LoopWaypointCollection()
{
uint lastProcessTime = Game.GameTime; // Store the last time the full loop finished; this is a value in ms
int timeBetweenChecks = 1000; // How many ms to wait between outer loops
int yieldAfterChecks = 50; // How many calculations to do before yielding
while (PathMainMenu.paths.Contains(this))
{
@ -148,10 +147,11 @@ namespace SceneManager
{
foreach (Vehicle v in World.GetAllVehicles())
{
if (IsNearWaypoint(v, waypoint) && IsValidForCollection(v))
if (VehicleIsNearWaypoint(v, waypoint) && VehicleIsValidForCollection(v))
{
CollectedVehicle newCollectedVehicle = AddVehicleToCollection(v);
GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, this, waypoint));
GameFiber AssignTasksFiber = new GameFiber(() => newCollectedVehicle.AssignWaypointTasks(this, waypoint));
//GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, this, waypoint));
AssignTasksFiber.Start();
}
@ -177,18 +177,18 @@ namespace SceneManager
{
var collectedVehicle = new CollectedVehicle(vehicle, this);
CollectedVehicles.Add(collectedVehicle);
Logger.Log($"Added {vehicle.Model.Name} to collection from path {Number} waypoint {1}.");
Game.LogTrivial($"Added {vehicle.Model.Name} to collection from path {Number} waypoint {1}.");
return collectedVehicle;
}
bool IsNearWaypoint(Vehicle v, Waypoint wp)
bool VehicleIsNearWaypoint(Vehicle v, Waypoint wp)
{
return v.FrontPosition.DistanceTo2D(wp.Position) <= wp.CollectorRadius && Math.Abs(wp.Position.Z - v.Position.Z) < 3;
}
bool IsValidForCollection(Vehicle v)
bool VehicleIsValidForCollection(Vehicle v)
{
if (v && v != Game.LocalPlayer.Character.CurrentVehicle && v != Game.LocalPlayer.Character.LastVehicle && (v.IsCar || v.IsBike || v.IsBicycle || v.IsQuadBike) && !v.IsSirenOn && v.IsEngineOn && v.IsOnAllWheels && v.Speed > 1 && !CollectedVehicles.Any(cv => cv?.Vehicle == v))
if (v && v != Game.LocalPlayer.Character.LastVehicle && (v.IsCar || v.IsBike || v.IsBicycle || v.IsQuadBike) && !v.IsSirenOn && v.IsEngineOn && v.IsOnAllWheels && v.Speed > 1 && !CollectedVehicles.Any(cv => cv?.Vehicle == v))
{
var vehicleCollectedOnAnotherPath = PathMainMenu.paths.Any(p => p.Number != Number && p.CollectedVehicles.Any(cv => cv.Vehicle == v));
if (vehicleCollectedOnAnotherPath)

View file

@ -6,12 +6,6 @@ using System.Linq;
namespace SceneManager
{
public enum DrivingFlagType
{
Normal = 263075,
Direct = 17040259
}
public class Waypoint
{
internal Path Path { get; set; }
@ -60,7 +54,7 @@ namespace SceneManager
DrawWaypointMarker();
}
internal void UpdateWaypoint(Waypoint currentWaypoint, DrivingFlagType drivingFlag, bool stopWaypoint, float speed, bool collectorWaypointChecked, float collectorRadius, float speedZoneRadius, bool updateWaypointPositionChecked)
internal void UpdateWaypoint(Waypoint currentWaypoint, Vector3 newWaypointPosition, DrivingFlagType drivingFlag, bool stopWaypoint, float speed, bool collectorWaypointChecked, float collectorRadius, float speedZoneRadius, bool updateWaypointPositionChecked)
{
if(IsStopWaypoint != stopWaypoint)
{
@ -71,7 +65,7 @@ namespace SceneManager
UpdateCollectorOptions();
if (updateWaypointPositionChecked)
{
UpdateWaypointPosition(Game.LocalPlayer.Character.Position);
UpdateWaypointPosition();
}
void UpdateIfStopWaypoint()
@ -103,7 +97,7 @@ namespace SceneManager
{
IsCollector = true;
RemoveSpeedZone();
SpeedZone = World.AddSpeedZone(currentWaypoint.Position, SpeedZoneRadius, speed);
SpeedZone = World.AddSpeedZone(currentWaypoint.Position, speedZoneRadius, speed);
Blip.Color = Color.Blue;
if (CollectorRadiusBlip)
{
@ -140,7 +134,7 @@ namespace SceneManager
}
}
void UpdateWaypointPosition(Vector3 newWaypointPosition)
void UpdateWaypointPosition()
{
Position = newWaypointPosition;
RemoveSpeedZone();
@ -199,27 +193,27 @@ namespace SceneManager
{
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, GetMousePositionInWorld(), 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, GetMousePositionInWorld(), 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);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position, 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, 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, GetMousePositionInWorld(), 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);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position, 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, GetMousePositionInWorld(), 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);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position, 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))
@ -231,16 +225,16 @@ namespace SceneManager
}
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);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position, 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, 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);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position, 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);
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, Position, 0, 0, 0, 0, 0, 0, 1f, 1f, markerHeight, 65, 255, 65, 100, false, false, 2, false, 0, 0, false);
}
}
}
@ -292,7 +286,7 @@ namespace SceneManager
internal void CollectVehicles(List<Path> paths)
{
var sleepInterval = 1000;
Logger.Log($"Starting collection loop on waypoint {Number}");
Game.LogTrivial($"Starting collection loop on waypoint {Number}");
while (paths.Contains(Path) && Path.Waypoints.Contains(this))
{
if (Path.IsEnabled && IsCollector)
@ -309,7 +303,7 @@ namespace SceneManager
if (collectedVehiclePlayerIsIn != null)
{
collectedVehiclePlayerIsIn.Dismiss(DismissOption.FromPlayer);
Logger.Log($"Dismissed a collected vehicle the player was in.");
Game.LogTrivial($"Dismissed a collected vehicle the player was in.");
}
GameFiber.Sleep(sleepInterval);
}
@ -327,8 +321,8 @@ namespace SceneManager
if (collectedVehicle == null)
{
CollectedVehicle newCollectedVehicle = AddVehicleToCollection(vehicle);
//Logger.Log($"Vehicle's front position distance to waypoint: {vehicle.FrontPosition.DistanceTo2D(waypoint.Position)}, collector radius: {waypoint.CollectorRadius}");
GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, Path, this));
GameFiber AssignTasksFiber = new GameFiber(() => newCollectedVehicle.AssignWaypointTasks(Path, this));
//GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, Path, this));
AssignTasksFiber.Start();
}
}
@ -343,7 +337,7 @@ namespace SceneManager
{
var collectedVehicle = new CollectedVehicle(vehicle, Path, this);
Path.CollectedVehicles.Add(collectedVehicle);
Logger.Log($"Added {vehicle.Model.Name} to collection from path {Path.Number} waypoint {this.Number}.");
Game.LogTrivial($"Added {vehicle.Model.Name} to collection from path {Path.Number} waypoint {this.Number}.");
return collectedVehicle;
}
@ -385,5 +379,36 @@ namespace SceneManager
}
}
}
private static Vector3 GetMousePositionInWorld()
{
HitResult TracePlayerView(float maxTraceDistance = 100f, 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);
return World.TraceLine(start, end, flags);
}
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();
}
}
return TracePlayerView(100f, TraceFlags.IntersectWorld).HitPosition;
}
}
}

View file

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -32,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.1.0")]
[assembly: AssemblyFileVersion("2.0.1.0")]
[assembly: AssemblyVersion("2.1.1.0")]
[assembly: AssemblyFileVersion("2.1.1.0")]

View file

@ -0,0 +1,253 @@
using Rage;
using RAGENativeUI;
using RAGENativeUI.Elements;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace SceneManager
{
internal class RNUIMouseInputHandler
{
internal delegate void Function();
internal static void Initialize(UIMenu menu, List<UIMenuScrollerItem> scrollerItems, Dictionary<UIMenuCheckboxItem, Function> checkboxItems, Dictionary<UIMenuItem, Function> selectItems)
{
GameFiber.StartNew(() =>
{
while (menu.Visible)
{
var selectedScroller = menu.MenuItems.Where(x => scrollerItems.Contains(x) && x.Selected && x.Enabled).FirstOrDefault();
if (selectedScroller != null)
{
OnWheelScroll(menu, selectedScroller, scrollerItems);
}
if (Game.IsKeyDown(Keys.LButton))
{
var selectedItem = menu.MenuItems.Where(x => x.Enabled && x.Selected).FirstOrDefault();
if (selectedItem != null)
{
//Game.LogTrivial($"selectedItem: {selectedItem.Text}");
//Game.LogTrivial($"scrollerItems contains: {scrollerItems.Contains(selectedItem)}");
if (selectItems.ContainsKey(selectedItem))
{
OnMenuItemClicked(selectItems);
}
else if (!scrollerItems.Contains(selectedItem) && checkboxItems.ContainsKey((UIMenuCheckboxItem)selectedItem))
{
OnCheckboxItemClicked(checkboxItems);
}
}
if(menu.SubtitleText == "~o~Main Menu")
{
menu.Visible = false;
}
}
if (menu.SubtitleText.Contains("Path Creation Menu"))
{
DrawWaypointMarker();
}
GameFiber.Yield();
}
});
}
internal static void OnCheckboxItemClicked(Dictionary<UIMenuCheckboxItem, Function> checkboxItems)
{
var checkedItem = checkboxItems.Keys.Where(x => x.Selected && x.Enabled).FirstOrDefault();
if(checkedItem != null)
{
checkedItem.Checked = !checkedItem.Checked;
if(checkboxItems.TryGetValue(checkedItem, out Function func))
{
func?.Invoke();
}
}
}
internal static void OnMenuItemClicked(Dictionary<UIMenuItem, Function> selectItems)
{
var selectedItem = selectItems.Keys.Where(x => x.Selected && x.Enabled).FirstOrDefault();
//Game.LogTrivial($"selectedItem: {selectedItem?.Text}");
if (selectedItem != null)
{
if (selectItems.TryGetValue(selectedItem, out Function func))
{
func?.Invoke();
}
}
}
internal static void OnWheelScroll(UIMenu menu, UIMenuItem selectedScroller, List<UIMenuScrollerItem> scrollerItems)
{
var menuScrollingDisabled = false;
var menuItems = menu.MenuItems.Where(x => x != selectedScroller);
while (Game.IsShiftKeyDownRightNow)
{
menu.ResetKey(Common.MenuControls.Up);
menu.ResetKey(Common.MenuControls.Down);
menuScrollingDisabled = true;
ScrollMenuItem();
if (menu.SubtitleText.Contains("Path Creation Menu") || menu.SubtitleText.Contains("Edit Waypoint"))
{
CompareScrollerValues();
}
if(menu.SubtitleText.Contains("Path Creation Menu"))
{
DrawWaypointMarker();
}
GameFiber.Yield();
}
if (menuScrollingDisabled)
{
menuScrollingDisabled = false;
menu.SetKey(Common.MenuControls.Up, GameControl.CursorScrollUp);
menu.SetKey(Common.MenuControls.Up, GameControl.CellphoneUp);
menu.SetKey(Common.MenuControls.Down, GameControl.CursorScrollDown);
menu.SetKey(Common.MenuControls.Down, GameControl.CellphoneDown);
}
void ScrollMenuItem()
{
//Game.LogTrivial($"Selected scroller: {selectedScroller.Text}");
if (Game.GetMouseWheelDelta() > 0)
{
foreach (var item in scrollerItems)
{
if (item == selectedScroller)
{
//Game.LogTrivial($"item text: {item.Text}");
item.ScrollToNextOption();
if (menu.SubtitleText.ToLower().Contains("barrier"))
{
HandleBarrierMenuItems(item);
}
if(item.Text == "Edit Waypoint")
{
UpdateEditWaypointMenuItems();
}
}
}
}
else if (Game.GetMouseWheelDelta() < 0)
{
foreach (var item in scrollerItems)
{
if (item == selectedScroller)
{
item.ScrollToPreviousOption();
if (menu.SubtitleText.ToLower().Contains("barrier"))
{
HandleBarrierMenuItems(item);
}
if (item.Text == "Edit Waypoint")
{
UpdateEditWaypointMenuItems();
}
}
}
}
void HandleBarrierMenuItems(UIMenuItem item)
{
if (item.Text == "Spawn Barrier")
{
if (BarrierMenu.shadowBarrier)
{
BarrierMenu.shadowBarrier.Delete();
}
var changeTextureItem = scrollerItems.Where(x => x.Text == "Change Texture").FirstOrDefault().Index = 0;
var listScrollerItem = (UIMenuListScrollerItem<string>)item;
if (listScrollerItem.SelectedItem == "Flare")
{
scrollerItems.Where(x => x.Text == "Rotate Barrier").FirstOrDefault().Enabled = false;
}
else
{
scrollerItems.Where(x => x.Text == "Rotate Barrier").FirstOrDefault().Enabled = true;
}
menu.Width = BarrierMenu.SetMenuWidth();
}
else if (item.Text == "Rotate Barrier")
{
BarrierMenu.RotateBarrier();
}
else if(item.Text == "Change Texture")
{
var numericScrollerItem = (UIMenuNumericScrollerItem<int>)item;
Rage.Native.NativeFunction.Natives.x971DA0055324D033(BarrierMenu.shadowBarrier, numericScrollerItem.Value);
}
}
void UpdateEditWaypointMenuItems()
{
var currentPath = PathMainMenu.paths[PathMainMenu.editPath.Index];
var editWaypoint = (UIMenuNumericScrollerItem<int>)menu.MenuItems.Where(x => x.Text == "Edit Waypoint").FirstOrDefault();
var collectorWaypoint = (UIMenuCheckboxItem)menu.MenuItems.Where(x => x.Text == "Collector").FirstOrDefault();
var changeCollectorRadius = (UIMenuNumericScrollerItem<int>)menu.MenuItems.Where(x => x.Text == "Collection Radius").FirstOrDefault();
var changeSpeedZoneRadius = (UIMenuNumericScrollerItem<int>)menu.MenuItems.Where(x => x.Text == "Speed Zone Radius").FirstOrDefault();
var stopWaypointType = (UIMenuCheckboxItem)menu.MenuItems.Where(x => x.Text == "Is this a Stop waypoint?").FirstOrDefault();
var directWaypointBehavior = (UIMenuCheckboxItem)menu.MenuItems.Where(x => x.Text == "Drive directly to waypoint?").FirstOrDefault();
var changeWaypointSpeed = (UIMenuNumericScrollerItem<int>)menu.MenuItems.Where(x => x.Text == "Waypoint Speed").FirstOrDefault();
var updateWaypointPosition = (UIMenuCheckboxItem)menu.MenuItems.Where(x => x.Text == "Update Waypoint Position").FirstOrDefault();
var currentWaypoint = currentPath.Waypoints[editWaypoint.Value - 1];
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;
}
}
void CompareScrollerValues()
{
var collectorRadius = (UIMenuNumericScrollerItem<int>)scrollerItems.Where(x => x.Text == "Collection Radius").FirstOrDefault();
var speedZoneRadius = (UIMenuNumericScrollerItem<int>)scrollerItems.Where(x => x.Text == "Speed Zone Radius").FirstOrDefault();
if (selectedScroller.Text == "Collection Radius" || selectedScroller.Text == "Speed Zone Radius")
{
if (selectedScroller == collectorRadius && collectorRadius.Value > speedZoneRadius.Value)
{
while (collectorRadius.Value > speedZoneRadius.Value)
{
speedZoneRadius.ScrollToNextOption();
}
}
if (selectedScroller == speedZoneRadius && speedZoneRadius.Value < collectorRadius.Value)
{
collectorRadius.Value = speedZoneRadius.Value;
}
}
}
}
private static void DrawWaypointMarker()
{
var waypointPosition = MousePositionInWorld.GetPosition;
if (SettingsMenu.threeDWaypoints.Checked && PathCreationMenu.collectorWaypoint.Checked)
{
Rage.Native.NativeFunction.Natives.DRAW_MARKER(1, waypointPosition, 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, waypointPosition, 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, waypointPosition, 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, waypointPosition, 0, 0, 0, 0, 0, 0, 1f, 1f, 1f, 65, 255, 65, 80, false, false, 2, false, 0, 0, false);
}
}
}
}

View file

@ -50,9 +50,9 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AITasking.cs" />
<Compile Include="ConsoleCommands.cs" />
<Compile Include="MousePositionInWorld.cs" />
<Compile Include="Object Classes\CollectedVehicle.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Object Classes\Barrier.cs" />
<Compile Include="Hints.cs" />
<Compile Include="Menus\BarrierMenu.cs" />
@ -67,6 +67,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Menus\PathMainMenu.cs" />
<Compile Include="Menus\SettingsMenu.cs" />
<Compile Include="RNUIMouseInputHandler.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Object Classes\Waypoint.cs" />
</ItemGroup>

View file

@ -17,19 +17,55 @@ namespace SceneManager
KPH
}
internal enum DrivingFlagType
{
Normal = 263075,
Direct = 17040259
}
internal enum DismissOption
{
FromPath = 0,
FromWaypoint = 1,
FromWorld = 2,
FromPlayer = 3,
FromDirected = 4
}
internal enum TrafficLight
{
Green = 0,
Red = 1,
Yellow = 2,
None = 3
}
internal static class Settings
{
internal static readonly InitializationFile ini = new InitializationFile("Plugins/SceneManager.ini");
// Keybindings
internal static Keys ToggleKey = Keys.T;
internal static Keys ModifierKey = Keys.LShiftKey;
internal static ControllerButtons ToggleButton = ControllerButtons.Y;
internal static ControllerButtons ModifierButton = ControllerButtons.A;
// Plugin Settings
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 bool EnableAdvancedBarricadeOptions = false;
// Default Waypoint Settings
internal static int CollectorRadius = 1;
internal static int SpeedZoneRadius = 5;
internal static bool StopWaypoint = false;
internal static bool DirectDrivingBehavior = false;
internal static int WaypointSpeed = 5;
// Barriers
internal static List<string> barrierKeys = new List<string>();
internal static List<string> barrierValues = new List<string>();
@ -38,22 +74,66 @@ namespace SceneManager
Game.LogTrivial("Loading SceneManager.ini settings");
ini.Create();
// Keybindings
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);
// Plugin Settings
Enable3DWaypoints = ini.ReadBoolean("Plugin Settings", "Enable3DWaypoints", true);
EnableMapBlips = ini.ReadBoolean("Plugin Settings", "EnableMapBlips", true);
EnableHints = ini.ReadBoolean("Plugin Settings", "EnableHints", true);
SpeedUnit = ini.ReadEnum("Plugin Settings", "SpeedUnits", SpeedUnits.MPH);
BarrierPlacementDistance = ini.ReadInt32("Plugin Settings", "BarrierPlacementDistance", 30);
EnableAdvancedBarricadeOptions = ini.ReadBoolean("Plugin Settings", "EnableAdvancedBarricadeOptions", false);
// Default Waypoint Settings
CollectorRadius = ini.ReadInt32("Default Waypoint Settings", "CollectorRadius", 1);
SpeedZoneRadius = ini.ReadInt32("Default Waypoint Settings", "SpeedZoneRadius", 5);
StopWaypoint = ini.ReadBoolean("Default Waypoint Settings", "StopWaypoint", false);
DirectDrivingBehavior = ini.ReadBoolean("Default Waypoint Settings", "DirectDrivingBehavior", false);
WaypointSpeed = ini.ReadInt32("Default Waypoint Settings", "WaypointSpeed", 5);
CheckForValidWaypointSettings();
// Barriers
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);
}
else
{
Game.LogTrivial($"{m.Name} is not valid.");
}
}
void CheckForValidWaypointSettings()
{
if(CollectorRadius > 50 || CollectorRadius < 1)
{
CollectorRadius = 1;
Game.LogTrivial($"Invalid value for CollectorRadius in user settings, resetting to default.");
}
if(SpeedZoneRadius > 200 || SpeedZoneRadius < 5)
{
SpeedZoneRadius = 5;
Game.LogTrivial($"Invalid value for SpeedZoneRadius in user settings, resetting to default.");
}
if (CollectorRadius > SpeedZoneRadius)
{
CollectorRadius = 1;
SpeedZoneRadius = 5;
Game.LogTrivial($"CollectorRadius is greater than SpeedZoneRadius in user settings, resetting to defaults.");
}
if (WaypointSpeed > 100 || WaypointSpeed < 5)
{
WaypointSpeed = 5;
Game.LogTrivial($"Invalid value for WaypointSpeed in user settings, resetting to default.");
}
}
}