using Rage; using RAGENativeUI.Elements; using SceneManager.Managers; using SceneManager.Paths; using SceneManager.Waypoints; using System; using System.Linq; namespace SceneManager.Utils { internal enum PedType { /// Any ped /// Any = 0, /// Cop peds /// Cop = 1, //Firefigher = 2, //EMS = 3 } /// A collection of potentially useful code snippets for GTA/LSPDFR development. /// internal static class Extensions { /// Determines if a ped can be considered ambient. Checks any type of ped by default. /// internal static bool IsAmbient(this Ped ped, PedType pedType = 0) { // Universal tasks (virtually all peds seem have this) var taskAmbientClips = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 38); // Universal on-foot tasks (virtually all ambient walking peds seem to have this) var taskComplexControlMovement = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 35); // Universal in-vehicle tasks (virtually all ambient driver peds seem to have this) var taskInVehicleBasic = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 150); var taskCarDriveWander = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 151); // On-foot ambient tasks var taskPolice = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 58); // From ambient cop (non-freemode) walking around var taskWanderingScenario = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 100); // From ambient cop walking around var taskUseScenario = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 118); // From ambient cop standing still var taskScriptedAnimation = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 134); // From UB ped waiting for interaction // In-vehicle controlled tasks var taskControlVehicle = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped, 169); // From backup unit driving to player // If ped relationship group does not contain "cop" then this extension doesn't apply if (pedType == PedType.Cop && !ped.RelationshipGroup.Name.ToLower().Contains("cop")) { //Game.LogTrivial($"Ped does not belong to a cop relationship group."); return false; } // Ped is in a vehicle if (ped.CurrentVehicle) { //Game.LogTrivial($"Ped is in a vehicle."); // Ped has a controlled driving task //if (taskControlVehicle) //{ // //Game.LogTrivial($"Ped has a controlled driving task. (non-ambient)"); // return false; //} // Ped has a wander driving task if (taskCarDriveWander) { //Game.LogTrivial($"Ped has a wander driving task. (ambient)"); return true; } // If the ped is in a vehicle but doesn't have a driving task, then it's a passenger. Check if the vehicle's driver has a driving wander task if (ped.CurrentVehicle.Driver && ped.CurrentVehicle.Driver != ped) { var driverHasWanderTask = Rage.Native.NativeFunction.Natives.GET_IS_TASK_ACTIVE(ped.CurrentVehicle.Driver, 151); if (driverHasWanderTask) { //Game.LogTrivial($"Ped is a passenger. Vehicle's driver has a wander driving task. (ambient)"); return true; } } } if (ped.IsOnFoot) { // Cop ped walking around or standing still if ((taskComplexControlMovement && taskWanderingScenario) || (taskAmbientClips && taskUseScenario)) { //Game.LogTrivial($"Ped is wandering around or standing still. (ambient)"); return true; } } // If nothing else returns true before now, then the ped is probably being controlled and doing something else //Game.LogTrivial($"Nothing else has returned true by this point. (non-ambient)"); return false; } /// Determines if a vehicle and driver are valid. /// internal static bool VehicleAndDriverValid(this Vehicle vehicle) { if (vehicle && vehicle.HasDriver && vehicle.Driver && vehicle.Driver.IsAlive) { return true; } else { return false; } } /// Determines if this vehicle is within the waypoint's collection range. /// internal static bool IsNearCollectorWaypoint(this Vehicle vehicle, Waypoint waypoint) { return vehicle.FrontPosition.DistanceTo2D(waypoint.Position) <= waypoint.CollectorRadius && Math.Abs(waypoint.Position.Z - vehicle.Position.Z) < 3; } internal static bool IsValidForPathCollection(this Vehicle vehicle, Path path) { if (!vehicle) { return false; } var vehicleCollectedOnAnotherPath = PathManager.Paths.Any(p => p != null && p.Number != path.Number && p.CollectedPeds.Any(cp => cp && cp.CurrentVehicle == vehicle)); if (vehicleCollectedOnAnotherPath) { return false; } if(path.BlacklistedVehicles.Contains(vehicle)) { return false; } if(vehicle == Game.LocalPlayer.Character.LastVehicle) { return false; } if (vehicle.Driver) { if (!vehicle.Driver.IsAlive) { Game.LogTrivial($"Vehicle's driver is dead."); path.BlacklistedVehicles.Add(vehicle); return false; } if (vehicle.IsPoliceVehicle && !vehicle.Driver.IsAmbient()) { Game.LogTrivial($"Vehicle is a non-ambient police vehicle."); path.BlacklistedVehicles.Add(vehicle); return false; } } if ((vehicle.IsCar || vehicle.IsBike || vehicle.IsBicycle || vehicle.IsQuadBike) && !vehicle.IsSirenOn && vehicle.IsEngineOn && vehicle.IsOnAllWheels && vehicle.Speed > 1 && !path.CollectedPeds.Any(cp => cp && cp.CurrentVehicle == vehicle)) { if (!vehicle.HasDriver) { vehicle.CreateRandomDriver(); GameFiber.Yield(); //while (vehicle && !vehicle.HasDriver) //{ // Game.LogTrivial($"Trying to create new driver"); // GameFiber.Yield(); //} if(!vehicle) { return false; } if(!vehicle.Driver) { Game.LogTrivial($"Unable to create a new driver"); vehicle.Delete(); return false; } Game.LogTrivial($"Vehicle has a new driver"); //vehicle.Driver.IsPersistent = true; vehicle.Driver.BlockPermanentEvents = true; } return true; } else { return false; } } internal static float GetTextWidth(this UIMenuItem menuItem) { menuItem.TextStyle.Apply(); Rage.Native.NativeFunction.Natives.x54CE8AC98E120CAB("STRING"); // _BEGIN_TEXT_COMMAND_GET_WIDTH Rage.Native.NativeFunction.Natives.ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(menuItem.Text); return Rage.Native.NativeFunction.Natives.x85F061DA64ED2F67(true); // _END_TEXT_COMMAND_GET_WIDTH } internal static float GetSelectedItemTextWidth(this UIMenuListScrollerItem scrollerItem) { if (scrollerItem.OptionCount == 0) { return 0; } scrollerItem.GetTextWidth(); scrollerItem.TextStyle.Apply(); Rage.Native.NativeFunction.Natives.x54CE8AC98E120CAB("STRING"); // _BEGIN_TEXT_COMMAND_GET_WIDTH Rage.Native.NativeFunction.Natives.ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(scrollerItem.SelectedItem); return Rage.Native.NativeFunction.Natives.x85F061DA64ED2F67(true); // _END_TEXT_COMMAND_GET_WIDTH } } }