1
Fork 0
mirror of https://github.com/thegeneralist01/Scene-Manager-DevRepo synced 2026-03-09 18:30:06 +01:00

Renamed Object Classes folder to Objects

This commit is contained in:
Rich Dunne 2020-12-06 07:29:50 -07:00
parent a2b935659f
commit 8ae65246f8
5 changed files with 134 additions and 64 deletions

View file

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Rage;
using SceneManager.Utils;
namespace SceneManager.Objects
{
class Barrier
{
internal Rage.Object Object { get; }
internal Model @Model{ get; }
internal Vector3 Position { get; }
internal float Rotation { get; }
internal bool Invincible { get; }
internal bool Immobile { get; }
internal Barrier(Rage.Object barrier, Vector3 barrierPosition, float barrierRotation, bool invincible, bool immobile)
{
Object = barrier;
@Model = barrier.Model;
Position = barrierPosition;
Rotation = barrierRotation;
Invincible = invincible;
Immobile = immobile;
//AddBlocker();
}
private void AddBlocker()
{
var blocker = new Rage.Object("prop_barier_conc_01a", Position, Rotation);
blocker.AttachTo(Object, 0, new Vector3(0, 0, 0), new Rotator());
GameFiber.StartNew(() =>
{
while (Object)
{
GameFiber.Yield();
}
blocker.Delete();
});
}
internal void GoAround()
{
GameFiber.StartNew(() =>
{
var collected = new List<Vehicle>();
while (Object)
{
foreach (Vehicle v in World.GetAllVehicles())
{
if(v && v.IsEngineOn)
{
if(v.HasDriver && v.Driver && v.Driver.IsAlive)
{
if (!collected.Contains(v))
{
v.Driver.Tasks.Clear();
v.Driver.Tasks.CruiseWithVehicle(5f, (VehicleDrivingFlags)17039872);
v.Driver.KeepTasks = true;
collected.Add(v);
}
}
}
}
GameFiber.Sleep(1000);
}
});
}
}
}

View file

@ -0,0 +1,454 @@
using Rage;
using System.Collections.Generic;
using System.Linq;
using SceneManager.Utils;
namespace SceneManager.Objects
{
internal class CollectedVehicle
{
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; private set; } = false;
internal bool Dismissed { get; private set; } = false;
internal bool Directed { get; set; } = false;
internal bool SkipWaypoint { get; private set; } = false;
internal bool ReadyForDirectTasks { get; private set; } = true;
internal CollectedVehicle(Vehicle vehicle, Path path, Waypoint currentWaypoint)
{
Vehicle = vehicle;
Driver = Vehicle.Driver;
Path = path;
CurrentWaypoint = currentWaypoint;
SetPersistence();
}
internal CollectedVehicle(Vehicle vehicle, Path path)
{
Vehicle = vehicle;
Driver = vehicle.Driver;
Path = path;
SetPersistence();
}
private void SetPersistence()
{
Vehicle.IsPersistent = true;
Driver.IsPersistent = true;
Driver.BlockPermanentEvents = true;
}
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 (!Dismissed && !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)
{
Game.LogTrivial($"Waypoint position has changed, updating drive task.");
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()
{
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.CurrentVehicle || !Driver.IsAlive)
{
Game.LogTrivial($"Driver is null or dead or not in a vehicle");
Dismiss();
return false;
}
return true;
}
}
internal void Dismiss(DismissOption dismissOption = DismissOption.FromPath, Path newPath = null)
{
if (!Vehicle)
{
Game.LogTrivial($"Vehicle is null.");
return;
}
if (!Driver)
{
Game.LogTrivial($"Driver is null.");
return;
}
if (dismissOption == DismissOption.FromWorld)
{
DismissFromWorld();
return;
}
if (dismissOption == DismissOption.FromPlayer)
{
Dismissed = true;
//if (Driver)
//{
Driver.Dismiss();
//}
//if (Vehicle)
//{
Vehicle.Dismiss();
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Vehicle, 0f, 1, true);
//}
Path.CollectedVehicles.Remove(this);
return;
}
if(Driver.CurrentVehicle && StoppedAtWaypoint)
{
StoppedAtWaypoint = false;
Rage.Native.NativeFunction.Natives.x260BE8F09E326A20(Driver.LastVehicle, 0f, 1, true);
//if (Driver)
//{
Driver.Tasks.CruiseWithVehicle(5f);
//}
}
Driver.Tasks.Clear();
if (dismissOption == DismissOption.FromWaypoint)
{
DismissFromWaypoint();
}
if (dismissOption == DismissOption.FromPath)
{
DismissFromPath();
}
if(dismissOption == DismissOption.FromDirected)
{
DismissFromDirect();
}
void DismissFromWorld()
{
Game.LogTrivial($"Dismissed {Vehicle.Model.Name} [{Vehicle.Handle}] from the world");
while (Vehicle.HasOccupants)
{
foreach (Ped occupant in Vehicle.Occupants)
{
occupant.Dismiss();
occupant.Delete();
}
GameFiber.Yield();
}
Vehicle.Delete();
}
void DismissFromWaypoint()
{
if (CurrentWaypoint == null || Path == null)
{
Game.LogTrivial($"CurrentWaypoint or Path is null");
return;
}
if (CurrentWaypoint?.Number != Path?.Waypoints.Count)
{
Game.LogTrivial($"{Vehicle?.Model.Name} [{Vehicle?.Handle}] dismissed from waypoint.");
SkipWaypoint = true;
}
else if (CurrentWaypoint?.Number == Path?.Waypoints.Count)
{
DismissFromPath();
}
}
void DismissFromPath()
{
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)
{
Game.LogTrivial($"Nearest collector is null");
}
else
{
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);
Game.LogTrivial($"{Vehicle.Model.Name} [{Vehicle.Handle}] dismissed successfully.");
if (Driver)
{
if (Driver.GetAttachedBlip())
{
Driver.GetAttachedBlip().Delete();
}
Driver.BlockPermanentEvents = false;
Driver.Dismiss();
}
if (Vehicle)
{
Vehicle.Dismiss();
Vehicle.IsSirenOn = false;
Vehicle.IsSirenSilent = true;
}
}
}, "DismissFromPath Fiber");
}
void DismissFromDirect()
{
Dismissed = true;
Directed = true;
if (newPath != null)
{
newPath.CollectedVehicles.Add(this);
Path.CollectedVehicles.Remove(this);
}
Driver.Tasks.Clear();
}
}
}
}

View file

@ -0,0 +1,261 @@
using Rage;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Xml.Serialization;
using SceneManager.Utils;
using System.IO;
namespace SceneManager.Objects
{
internal class Path // Change this to Public for import/export
{
internal int Number { get; set; }
internal bool IsEnabled { get; set; }
internal State State { get; set; }
[XmlArray("Waypoints")]
[XmlArrayItem("Waypoint")]
public List<Waypoint> Waypoints { get; set; } = new List<Waypoint>();
internal List<CollectedVehicle> CollectedVehicles = new List<CollectedVehicle>();
private List<Vehicle> _blacklistedVehicles = new List<Vehicle>();
private Path() { }
internal Path(int pathNum, State pathState)
{
Number = pathNum;
State = pathState;
DrawLinesBetweenWaypoints();
}
internal void Save(string filename)
{
var GAME_DIRECTORY = Directory.GetCurrentDirectory();
var SAVED_PATHS_DIRECTORY = GAME_DIRECTORY + "/plugins/SceneManager/Saved Paths/";
if (!Directory.Exists(SAVED_PATHS_DIRECTORY))
{
Directory.CreateDirectory(SAVED_PATHS_DIRECTORY);
Game.LogTrivial($"New directory created at '/plugins/SceneManager/Saved Paths'");
}
PathXMLManager.SaveItemToXML(this, SAVED_PATHS_DIRECTORY + filename);
}
private void LowerWaypointBlipsOpacity()
{
foreach (Waypoint wp in Waypoints)
{
wp.Blip.Alpha = 0.5f;
if (wp.CollectorRadiusBlip)
{
wp.CollectorRadiusBlip.Alpha = 0.25f;
}
}
}
private void RestoreWaypointBlipsOpacity()
{
foreach (Waypoint wp in Waypoints)
{
if (wp.Blip)
{
wp.Blip.Alpha = 1.0f;
if (wp.CollectorRadiusBlip)
{
wp.CollectorRadiusBlip.Alpha = 0.5f;
}
}
}
}
internal void DisablePath()
{
IsEnabled = false;
foreach(Waypoint wp in Waypoints)
{
wp.RemoveSpeedZone();
}
if (SettingsMenu.mapBlips.Checked)
{
LowerWaypointBlipsOpacity();
}
}
internal void EnablePath()
{
IsEnabled = true;
foreach (Waypoint wp in Waypoints)
{
if (wp.IsCollector)
{
wp.AddSpeedZone();
}
}
if (SettingsMenu.mapBlips.Checked)
{
RestoreWaypointBlipsOpacity();
}
}
internal void DrawLinesBetweenWaypoints()
{
GameFiber.StartNew(() =>
{
while(true)
{
if (SettingsMenu.threeDWaypoints.Checked && (State == State.Finished && MenuManager.menuPool.IsAnyMenuOpen()) || (State == State.Creating && PathCreationMenu.pathCreationMenu.Visible))
{
for (int i = 0; i < Waypoints.Count; i++)
{
if (i != Waypoints.Count - 1)
{
if (Waypoints[i + 1].IsStopWaypoint)
{
Debug.DrawLine(Waypoints[i].Position, Waypoints[i + 1].Position, Color.Orange);
}
else
{
Debug.DrawLine(Waypoints[i].Position, Waypoints[i + 1].Position, Color.Green);
}
}
}
}
GameFiber.Yield();
}
});
}
internal void LoopForVehiclesToBeDismissed()
{
GameFiber.StartNew(() =>
{
while (PathMainMenu.paths.Contains(this))
{
//Logger.Log($"Dismissing unused vehicles for cleanup");
foreach (CollectedVehicle cv in CollectedVehicles.Where(cv => cv.Vehicle))
{
if (!cv.Vehicle.IsDriveable || cv.Vehicle.IsUpsideDown || !cv.Vehicle.HasDriver)
{
if (cv.Vehicle.HasDriver)
{
cv.Vehicle.Driver.Dismiss();
}
cv.Vehicle.Dismiss();
}
}
CollectedVehicles.RemoveAll(cv => !cv.Vehicle);
_blacklistedVehicles.RemoveAll(v => !v);
GameFiber.Sleep(60000);
}
});
}
internal void LoopWaypointCollection()
{
uint lastProcessTime = Game.GameTime; // Store the last time the full loop finished; this is a value in ms
int yieldAfterChecks = 50; // How many calculations to do before yielding
while (PathMainMenu.paths.Contains(this))
{
if (IsEnabled)
{
int checksDone = 0;
try
{
foreach (Waypoint waypoint in Waypoints)
{
if (waypoint != null & waypoint.IsCollector)
{
foreach (Vehicle v in World.GetAllVehicles())
{
if (VehicleIsNearWaypoint(v, waypoint) && VehicleIsValidForCollection(v))
{
CollectedVehicle newCollectedVehicle = AddVehicleToCollection(v);
GameFiber AssignTasksFiber = new GameFiber(() => newCollectedVehicle.AssignWaypointTasks(this, waypoint));
//GameFiber AssignTasksFiber = new GameFiber(() => AITasking.AssignWaypointTasks(newCollectedVehicle, this, waypoint));
AssignTasksFiber.Start();
}
checksDone++; // Increment the counter inside the vehicle loop
if (checksDone % yieldAfterChecks == 0)
{
GameFiber.Yield(); // Yield the game fiber after the specified number of vehicles have been checked
}
}
}
}
}
catch
{
//return;
}
}
GameFiber.Sleep((int)Math.Max(1, Game.GameTime - lastProcessTime)); // If the prior lines took more than a second to run, then you'll run again almost immediately, but if they ran fairly quickly, you can sleep the loop until the remainder of the time between checks has passed
lastProcessTime = Game.GameTime;
}
CollectedVehicle AddVehicleToCollection(Vehicle vehicle)
{
var collectedVehicle = new CollectedVehicle(vehicle, this);
CollectedVehicles.Add(collectedVehicle);
Game.LogTrivial($"Added {vehicle.Model.Name} to collection from path {Number} waypoint {1}.");
return collectedVehicle;
}
bool VehicleIsNearWaypoint(Vehicle v, Waypoint wp)
{
return v.FrontPosition.DistanceTo2D(wp.Position) <= wp.CollectorRadius && Math.Abs(wp.Position.Z - v.Position.Z) < 3;
}
bool VehicleIsValidForCollection(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) && !_blacklistedVehicles.Contains(v))
{
var vehicleCollectedOnAnotherPath = PathMainMenu.paths.Any(p => p.Number != Number && p.CollectedVehicles.Any(cv => cv.Vehicle == v));
if (vehicleCollectedOnAnotherPath)
{
return false;
}
if (v.HasDriver && v.Driver)
{
if(!v.Driver.IsAlive)
{
Game.LogTrivial($"Vehicle's driver is dead.");
_blacklistedVehicles.Add(v);
return false;
}
if (v.IsPoliceVehicle && !v.Driver.IsAmbient())
{
_blacklistedVehicles.Add(v);
return false;
}
}
if (!v.HasDriver)
{
v.CreateRandomDriver();
while (!v.HasDriver)
{
GameFiber.Yield();
}
if (v && v.Driver)
{
v.Driver.IsPersistent = true;
v.Driver.BlockPermanentEvents = true;
}
else
{
return false;
}
}
return true;
}
else
{
return false;
}
}
}
}
}

View file

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