From da95f4ebddcfa39dbcf7fdad6612b17a8b3b39f4 Mon Sep 17 00:00:00 2001 From: Dekokiyo Date: Fri, 31 Mar 2023 11:02:33 +0900 Subject: [PATCH 01/40] =?UTF-8?q?=E3=81=AA=E3=82=93=E3=81=8B=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DynamicLSPDFR/Common/Backup.cs | 19 +++++++++ DynamicLSPDFR/Extensions/MathExtensions.cs | 25 ----------- .../Extensions/RandomDrawExtensions.cs | 42 +++++++++++++++++++ 3 files changed, 61 insertions(+), 25 deletions(-) delete mode 100644 DynamicLSPDFR/Extensions/MathExtensions.cs create mode 100644 DynamicLSPDFR/Extensions/RandomDrawExtensions.cs diff --git a/DynamicLSPDFR/Common/Backup.cs b/DynamicLSPDFR/Common/Backup.cs index 7c34082..e3ee16b 100644 --- a/DynamicLSPDFR/Common/Backup.cs +++ b/DynamicLSPDFR/Common/Backup.cs @@ -2,6 +2,25 @@ namespace DynamicLSPDFR.Common; internal static class BackupCommon { + internal enum BackupResponseType : int + { + Code2, + Code3, + Pursuit, + Transport, + } + + internal enum BackupUnitType : int + { + LocalPatrol, + StatePatrol, + LocalSWAT, + NooseSWAT, + LocalAir, + NooseAir, + PoliceBike, + } + internal static (string, string)[] CopMaleIdleAnim = new[] { ("amb@world_human_cop_idles@male@base", "base"), diff --git a/DynamicLSPDFR/Extensions/MathExtensions.cs b/DynamicLSPDFR/Extensions/MathExtensions.cs deleted file mode 100644 index aee65c5..0000000 --- a/DynamicLSPDFR/Extensions/MathExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace DynamicLSPDFR.Extensions; - -internal static class MathExtensions -{ - internal static int GetVehicleFromChance(int[] chances) - { - var total = 0; - var table = new int[chances.Length]; - for (int i = 0; i < chances.Length; i++) - { - total += chances[i]; - table[i] = total; - } - - var rnd = RandomNumber.Instance.Next(1, total + 1); - for (int i = 0; i < table.Length; i++) - { - if (table[i] >= rnd) - { - return i; - } - } - return 0; - } -} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/RandomDrawExtensions.cs b/DynamicLSPDFR/Extensions/RandomDrawExtensions.cs new file mode 100644 index 0000000..b4d8cba --- /dev/null +++ b/DynamicLSPDFR/Extensions/RandomDrawExtensions.cs @@ -0,0 +1,42 @@ +namespace DynamicLSPDFR.Extensions; + +internal class RandomDraw +{ + private List num; + private int totalNum; + private List cumulativeNum; + + internal RandomDraw(List num) + { + this.num = num; + cumulativeNum = new(); + + for (var i = 0; i < this.num.Count; i++) + { + totalNum += this.num[i]; + cumulativeNum[i] = totalNum; + } + } + + internal int Choose() + { + int rnd = RandomNumber.Instance.Next(0, totalNum); + int min = 0; + int max = cumulativeNum.Count - 1; + + while (min < max) + { + int index = (min + max) / 2; + int point = cumulativeNum[index]; + + if (rnd > point) min = index + 1; + else + { + if (rnd >= (index > 0 ? cumulativeNum[index - 1] : 0)) return index; + max = index; + } + } + + return max; + } +} \ No newline at end of file From f20a68da47d7db75d8dd1b952d700f1a9f2b7e16 Mon Sep 17 00:00:00 2001 From: Dekokiyo Date: Fri, 31 Mar 2023 12:47:34 +0900 Subject: [PATCH 02/40] =?UTF-8?q?=E3=81=BE=E3=81=81=E3=81=8C=E3=82=93?= =?UTF-8?q?=E3=81=B0=EF=BD=92=E3=81=A8=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DynamicLSPDFR/Backup/BackupFileLoader.cs | 72 +++++++ DynamicLSPDFR/Backup/BackupMain.cs | 113 +++++++++++ DynamicLSPDFR/Backup/BackupManager.cs | 176 ++++++++++++++++++ DynamicLSPDFR/Backup/LocalPatrol.cs | 9 + .../Modules/BackupMenu/BackupMenu.cs | 0 5 files changed, 370 insertions(+) create mode 100644 DynamicLSPDFR/Backup/BackupFileLoader.cs create mode 100644 DynamicLSPDFR/Backup/BackupMain.cs create mode 100644 DynamicLSPDFR/Backup/BackupManager.cs create mode 100644 DynamicLSPDFR/Backup/LocalPatrol.cs create mode 100644 DynamicLSPDFR/Modules/BackupMenu/BackupMenu.cs diff --git a/DynamicLSPDFR/Backup/BackupFileLoader.cs b/DynamicLSPDFR/Backup/BackupFileLoader.cs new file mode 100644 index 0000000..a5f2bdb --- /dev/null +++ b/DynamicLSPDFR/Backup/BackupFileLoader.cs @@ -0,0 +1,72 @@ +namespace DynamicLSPDFR.Backup; + +internal static class BackupFileLoader +{ + internal const string DEFAULT_BACKUP_FILE = @"plugins/LSPDFR/DynamicLSPDFR/DefaultBackup.xml"; + internal const string CUSTOM_BACKUP_FILE = @"plugins/LSPDFR/DynamicLSPDFR/CustomBackup.xml"; + + internal static List commonVehiclesData = new(); + internal static List commonPedsData = new(); + internal static List commonHandGunsData = new(); + internal static List commonLongGunsData = new(); + internal static List commonComponentsData = new(); + + internal static void GenerateBackupFile() + { + } + + internal static void LoadDefaultLocalPatrol() + { + var defaultBackupFile = new XmlDocument(); + defaultBackupFile.Load(DEFAULT_BACKUP_FILE); + var LocalPatrolUnits = defaultBackupFile.SelectNodes("DynamicBackup/LocalPatrol/Unit"); + + foreach (XmlNode lpu in LocalPatrolUnits) + { + var county = ConvertExtensions.ConvertToBackupCounty(lpu.Attributes["county"].InnerText); + var vehicles = lpu.SelectNodes("Vehicles/Vehicle"); + var peds = lpu.SelectNodes("Peds/Ped"); + var handGuns = lpu.SelectNodes("Weapons/HandGuns/Weapon"); + var longGuns = lpu.SelectNodes("Weapons/LongGuns/Weapon"); + + foreach (XmlNode v in vehicles) + { + commonVehiclesData.Clear(); + commonVehiclesData.Add(new(v.InnerText, int.Parse(v.Attributes["chance"].InnerText ?? "100"), int.Parse(v.Attributes["paxCount"].InnerText ?? "1"))); + } + foreach (XmlNode p in peds) + { + commonPedsData.Clear(); + commonPedsData.Add(new(p.InnerText, int.Parse(p.Attributes["chance"].InnerText ?? "100"), ConvertExtensions.ConvertToBackupWeather(p.Attributes["weather"].InnerText ?? "sunny"), bool.Parse(p.Attributes["rndProp"].InnerText.ToLower() ?? "false"))); + } + foreach (XmlNode hg in handGuns) + { + commonHandGunsData.Clear(); + commonComponentsData.Clear(); + foreach (XmlNode cp in hg.SelectSingleNode("Components").SelectNodes("Component")) commonComponentsData.Add(cp.InnerText); + commonHandGunsData.Add(new(hg.SelectSingleNode("Hash").InnerText, int.Parse(hg.Attributes["chance"].InnerText), commonComponentsData)); + } + foreach (XmlNode lg in longGuns) + { + commonLongGunsData.Clear(); + commonComponentsData.Clear(); + foreach (XmlNode cp in lg.SelectSingleNode("Components").SelectNodes("Component")) commonComponentsData.Add(cp.InnerText); + commonLongGunsData.Add(new(lg.SelectSingleNode("Hash").InnerText, int.Parse(lg.Attributes["chance"].InnerText), commonComponentsData)); + } + + LocalPatrol.CountyDistance[county] = float.Parse(lpu.Attributes["distance"].InnerText); + LocalPatrol.CountyVehicles[county] = commonVehiclesData; + LocalPatrol.CountyPeds[county] = commonPedsData; + LocalPatrol.HandGuns[county] = commonHandGunsData; + LocalPatrol.LongGuns[county] = commonLongGunsData; + LocalPatrol.HasStunGun = bool.Parse(lpu.SelectSingleNode("Weapons").Attributes["hasStunGun"].InnerText ?? "false"); + LocalPatrol.HasNightStick = bool.Parse(lpu.SelectSingleNode("Weapons").Attributes["hasNightStick"].InnerText ?? "false"); + } + } + + internal static void Initialize() + { + GenerateBackupFile(); + LoadDefaultLocalPatrol(); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/BackupMain.cs b/DynamicLSPDFR/Backup/BackupMain.cs new file mode 100644 index 0000000..84913f7 --- /dev/null +++ b/DynamicLSPDFR/Backup/BackupMain.cs @@ -0,0 +1,113 @@ +using static DynamicLSPDFR.Common.BackupCommon; + +namespace DynamicLSPDFR.Backup; + +internal class BackupUnitBase +{ + internal static List units = new(); + + internal int id { get; set; } + internal Vehicle vehicle { get; set; } + internal Ped driver { get; set; } + internal Dictionary<(Model, int), int> vehicleData { get; set; } + internal Dictionary<(Model, int), int> driverData { get; set; } + internal Ped[] paxes { get; set; } + internal float distance { get; set; } + internal BackupResponseType responseType { get; set; } + internal BackupUnitType unitType { get; set; } + + private int VehicleElementId { get; set; } + private int DriverElementId { get; set; } + private List SpawnPoints { get; set; } + + internal bool hasStunGun { get; set; } + internal bool hasNightStick { get; set; } + internal bool hasFlashlight { get; set; } + internal Dictionary<(WeaponAsset, int), string[]> handGunsData { get; set; } + internal Dictionary<(WeaponAsset, int), string[]> longGunsData { get; set; } + + internal string TranslationKey { get; set; } + + internal BackupUnitBase() { } + internal BackupUnitBase(int id, Dictionary<(Model, int), int> vData, Dictionary<(Model, int), int> driverData, Ped[] paxes, float distance, BackupResponseType responseType, BackupUnitType unitType) + { + Init(id, vData, driverData, paxes, distance, responseType, unitType); + } + + private void Init(int id, Dictionary<(Model, int), int> vehicleData, Dictionary<(Model, int), int> driverData, Ped[] paxes, float distance, BackupResponseType responseType, BackupUnitType unitType) + { + this.id = id; + this.vehicleData = vehicleData; + this.driverData = driverData; + this.paxes = paxes; + this.distance = distance; + this.responseType = responseType; + this.unitType = unitType; + this.TranslationKey = new StringBuilder(Enum.GetName(typeof(BackupResponseType), responseType)).Append(Enum.GetName(typeof(BackupUnitType), unitType)).ToString(); + + if (this.vehicleData.Count is 0) return; + + // for (int i = 0; i < 10; i++) SpawnPoints.Add(World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(75f))); + List vPer = new(); + foreach (var v in this.vehicleData) vPer.Add(v.Key.Item2); + VehicleElementId = new RandomDraw(vPer).Choose(); + + this.vehicle = new(vehicleData.ElementAt(VehicleElementId).Key.Item1, World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(75f))); + this.vehicle.IsPersistent = true; + + List dPer = new(); + foreach (var d in this.driverData) dPer.Add(d.Key.Item2); + DriverElementId = new RandomDraw(dPer).Choose(); + + this.driver = new(driverData.ElementAt(DriverElementId).Key.Item1, Vector3.Zero, 0f); + this.driver.IsPersistent = true; + this.driver.BlockPermanentEvents = true; + this.driver.KeepTasks = true; + this.driver.WarpIntoVehicle(this.vehicle, -1); + + GameFiber.StartNew(() => + { + this.driver.Tasks.DriveToPosition(World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(10f)), 20f, VehicleDrivingFlags.DriveAroundObjects | VehicleDrivingFlags.DriveAroundPeds | VehicleDrivingFlags.DriveAroundVehicles | VehicleDrivingFlags.DriveBySight | VehicleDrivingFlags.StopAtDestination).WaitForCompletion(); + }, "[Backup] Init"); + + units.Add(this); + } + + internal virtual void GiveWeapon(Ped ped, Dictionary<(WeaponAsset, int), string[]> handGuns, Dictionary<(WeaponAsset, int), string[]> longGuns, bool hasStunGun, bool hasNightStick, bool hasFlashlight) + { + this.handGunsData = handGuns; + this.longGunsData = longGuns; + this.hasStunGun = hasStunGun; + this.hasNightStick = hasNightStick; + this.hasFlashlight = hasFlashlight; + + if (hasStunGun) ped.Inventory.GiveNewWeapon(WeaponHash.StunGun, 5000, false); + if (hasNightStick) ped.Inventory.GiveNewWeapon(WeaponHash.Nightstick, 5000, false); + if (hasFlashlight) ped.Inventory.GiveFlashlight(); + + foreach (var hg in handGuns) + { + if (hg.Key.Item2 > 100 || RandomNumber.Instance.Next(0, 101) <= hg.Key.Item2) + { + var wp = new WeaponAsset(hg.Key.Item1); + wp.Load(); + ped.Inventory.GiveNewWeapon(wp, 5000, false); + if (hg.Value.Length > 0) foreach (var comp in hg.Value) ped.Inventory.AddComponentToWeapon(wp, comp); + } + } + foreach (var lg in longGuns) + { + if (lg.Key.Item2 > 100 || RandomNumber.Instance.Next(0, 101) <= lg.Key.Item2) + { + var wp = new WeaponAsset(lg.Key.Item1); + wp.Load(); + ped.Inventory.GiveNewWeapon(wp, 5000, false); + if (lg.Value.Length > 0) foreach (var comp in lg.Value) ped.Inventory.AddComponentToWeapon(wp, comp); + } + } + } + + internal virtual void Dismiss() + { + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/BackupManager.cs b/DynamicLSPDFR/Backup/BackupManager.cs new file mode 100644 index 0000000..06729b8 --- /dev/null +++ b/DynamicLSPDFR/Backup/BackupManager.cs @@ -0,0 +1,176 @@ +namespace DynamicLSPDFR.Backup; + +internal enum BackupResponseType : int +{ + Code2, + Code3, + Pursuit, +} + +internal enum BackupUnitType : int +{ + // Police + LocalPatrol, + StatePatrol, + LocalSWAT, + NooseSWAT, + LocalFemalePatrol, + StateFemalePatrol, + LocalHighwayPatrol, + StateHighwayPatrol, + LocalFemaleHighwayPatrol, + StateFemaleHighwayPatrol, + BikePatrol, + BikeFemalePatrol, + + // FBI + FBILocal, + FBISWAT, + + // EMS + FireTruck, + Ambulance, + + // Air Support + LocalAir, + NooseAir, + AirAmbulance, + AirSWAT, + + // Service + + // Emergency + Panic, +} + +internal enum BackupUnitWeatherType : int +{ + Sunny = 0, + Rainy = 1, + Snowy = 2, +} + +internal enum BackupWorldZoneCounty : int +{ + LosSantos = 0, + LosSantosCounty = 1, + BlaineCounty = 2, + NorthYankton = 4, +} + +internal enum BackupStatus : int +{ + IsMoving, + Arrived, + Dismiss, + Unknown, +} + +internal abstract class BackupBase +{ + internal static List AllUnits = new(); + internal abstract void Update(); + internal abstract void Dismiss(); + + internal static void AllClear() + { + AllUnits = new(); + } +} + +internal static class BackupManager +{ + internal static BackupVehicleData GetRandomModel(this List vehicleDataList) + { + if (vehicleDataList.Count is 0) return null; + if (vehicleDataList.Count is 1) return vehicleDataList.FirstOrDefault(); + + int min = 0, max = 0, rnd = 0; + Dictionary<(int, int), BackupVehicleData> dic = new(); + foreach (var v in vehicleDataList) + { + max = v.chance; + dic.Add((min, max), v); + min = max; + } + rnd = RandomNumber.Instance.Next(max); + + foreach (var d in dic) if (d.Key.Item1 <= rnd && rnd < d.Key.Item2) return d.Value; + return dic.FirstOrDefault().Value; + } + + internal static Model GetRandomModel(this List pedDataList) + { + if (pedDataList.Count is 0) return null; + if (pedDataList.Count is 1) return pedDataList.FirstOrDefault().model; + + int min = 0, max = 0, rnd = 0; + Dictionary<(int, int), Model> dic = new(); + foreach (var v in pedDataList) + { + max = v.chance; + dic.Add((min, max), v.model); + min = max; + } + rnd = RandomNumber.Instance.Next(max); + + foreach (var d in dic) if (d.Key.Item1 <= rnd && rnd < d.Key.Item2) return d.Value; + return dic.FirstOrDefault().Value; + } + + internal static void StartUpdate() + { + GameFiber.StartNew(() => + { + while (true) + { + GameFiber.Yield(); + BackupBase.AllUnits.Do(x => x.Update()); + } + }, "[DynamicLSPDFR] Backup Unit Update"); + } +} + +internal class BackupVehicleData +{ + internal Model model { get; set; } + internal int chance { get; set; } + internal int paxCount { get; set; } + + internal BackupVehicleData(Model model, int chance, int paxCount) + { + this.model = model; + this.chance = chance; + this.paxCount = paxCount; + } +} + +internal class BackupPedData +{ + internal Model model { get; set; } + internal int chance { get; set; } + internal BackupUnitWeatherType weatherType { get; set; } + internal bool isRandomProp { get; set; } + + internal BackupPedData(Model model, int chance, BackupUnitWeatherType weatherType, bool isRandomProp) + { + this.model = model; + this.chance = chance; + this.weatherType = weatherType; + this.isRandomProp = isRandomProp; + } +} + +internal class BackupWeaponData +{ + internal string hash { get; set; } + internal int chance { get; set; } + internal List components { get; set; } + + internal BackupWeaponData(string hash, int chance, List components) + { + this.hash = hash; + this.chance = chance; + this.components = components; + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/LocalPatrol.cs b/DynamicLSPDFR/Backup/LocalPatrol.cs new file mode 100644 index 0000000..039ef50 --- /dev/null +++ b/DynamicLSPDFR/Backup/LocalPatrol.cs @@ -0,0 +1,9 @@ +namespace DynamicLSPDFR.Backup; + +internal class LocalPatrol +{ + internal LocalPatrol() + { + + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/BackupMenu/BackupMenu.cs b/DynamicLSPDFR/Modules/BackupMenu/BackupMenu.cs new file mode 100644 index 0000000..e69de29 From 625624396176c25633d30f7f64115dd017f137fd Mon Sep 17 00:00:00 2001 From: Dekokiyo Date: Fri, 31 Mar 2023 22:02:07 +0900 Subject: [PATCH 03/40] =?UTF-8?q?=E9=80=94=E4=B8=AD=E3=81=A0=E3=81=91?= =?UTF-8?q?=E3=81=A9=E5=A4=9A=E5=88=86=E3=81=86=E3=81=BE=E3=81=8F=E3=81=84?= =?UTF-8?q?=E3=81=8F=E3=81=93=E3=82=8C=E3=81=AA=E3=82=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DynamicLSPDFR/Backup/BackupFileLoader.cs | 8 +- DynamicLSPDFR/Backup/BackupMain.cs | 113 ------------ DynamicLSPDFR/Backup/BackupManager.cs | 140 ++++---------- DynamicLSPDFR/Backup/BackupUnit.cs | 174 ++++++++++++++++++ DynamicLSPDFR/Backup/LocalPatrol.cs | 9 - DynamicLSPDFR/Extensions/ConvertExtensions.cs | 114 ++++++------ .../Extensions/RandomDrawExtensions.cs | 42 ----- .../Extensions/WeightedExtensions.cs | 23 +++ DynamicLSPDFR/Main.cs | 4 + .../Modules/BackupMenu/BackupMenu.cs | 0 DynamicLSPDFR/Modules/KeyDownUpdate.cs | 4 + DynamicLSPDFR/Modules/Menu/BackupMenu.cs | 55 ++++++ .../{DynamicMenu => Menu}/ContextMenu.cs | 0 .../Resources/BackupFile/DefaultBackup.xml | 2 +- 14 files changed, 355 insertions(+), 333 deletions(-) delete mode 100644 DynamicLSPDFR/Backup/BackupMain.cs create mode 100644 DynamicLSPDFR/Backup/BackupUnit.cs delete mode 100644 DynamicLSPDFR/Backup/LocalPatrol.cs delete mode 100644 DynamicLSPDFR/Extensions/RandomDrawExtensions.cs create mode 100644 DynamicLSPDFR/Extensions/WeightedExtensions.cs delete mode 100644 DynamicLSPDFR/Modules/BackupMenu/BackupMenu.cs create mode 100644 DynamicLSPDFR/Modules/Menu/BackupMenu.cs rename DynamicLSPDFR/Modules/{DynamicMenu => Menu}/ContextMenu.cs (100%) diff --git a/DynamicLSPDFR/Backup/BackupFileLoader.cs b/DynamicLSPDFR/Backup/BackupFileLoader.cs index a5f2bdb..947576b 100644 --- a/DynamicLSPDFR/Backup/BackupFileLoader.cs +++ b/DynamicLSPDFR/Backup/BackupFileLoader.cs @@ -5,12 +5,6 @@ internal static class BackupFileLoader internal const string DEFAULT_BACKUP_FILE = @"plugins/LSPDFR/DynamicLSPDFR/DefaultBackup.xml"; internal const string CUSTOM_BACKUP_FILE = @"plugins/LSPDFR/DynamicLSPDFR/CustomBackup.xml"; - internal static List commonVehiclesData = new(); - internal static List commonPedsData = new(); - internal static List commonHandGunsData = new(); - internal static List commonLongGunsData = new(); - internal static List commonComponentsData = new(); - internal static void GenerateBackupFile() { } @@ -61,6 +55,8 @@ internal static void LoadDefaultLocalPatrol() LocalPatrol.LongGuns[county] = commonLongGunsData; LocalPatrol.HasStunGun = bool.Parse(lpu.SelectSingleNode("Weapons").Attributes["hasStunGun"].InnerText ?? "false"); LocalPatrol.HasNightStick = bool.Parse(lpu.SelectSingleNode("Weapons").Attributes["hasNightStick"].InnerText ?? "false"); + + } } diff --git a/DynamicLSPDFR/Backup/BackupMain.cs b/DynamicLSPDFR/Backup/BackupMain.cs deleted file mode 100644 index 84913f7..0000000 --- a/DynamicLSPDFR/Backup/BackupMain.cs +++ /dev/null @@ -1,113 +0,0 @@ -using static DynamicLSPDFR.Common.BackupCommon; - -namespace DynamicLSPDFR.Backup; - -internal class BackupUnitBase -{ - internal static List units = new(); - - internal int id { get; set; } - internal Vehicle vehicle { get; set; } - internal Ped driver { get; set; } - internal Dictionary<(Model, int), int> vehicleData { get; set; } - internal Dictionary<(Model, int), int> driverData { get; set; } - internal Ped[] paxes { get; set; } - internal float distance { get; set; } - internal BackupResponseType responseType { get; set; } - internal BackupUnitType unitType { get; set; } - - private int VehicleElementId { get; set; } - private int DriverElementId { get; set; } - private List SpawnPoints { get; set; } - - internal bool hasStunGun { get; set; } - internal bool hasNightStick { get; set; } - internal bool hasFlashlight { get; set; } - internal Dictionary<(WeaponAsset, int), string[]> handGunsData { get; set; } - internal Dictionary<(WeaponAsset, int), string[]> longGunsData { get; set; } - - internal string TranslationKey { get; set; } - - internal BackupUnitBase() { } - internal BackupUnitBase(int id, Dictionary<(Model, int), int> vData, Dictionary<(Model, int), int> driverData, Ped[] paxes, float distance, BackupResponseType responseType, BackupUnitType unitType) - { - Init(id, vData, driverData, paxes, distance, responseType, unitType); - } - - private void Init(int id, Dictionary<(Model, int), int> vehicleData, Dictionary<(Model, int), int> driverData, Ped[] paxes, float distance, BackupResponseType responseType, BackupUnitType unitType) - { - this.id = id; - this.vehicleData = vehicleData; - this.driverData = driverData; - this.paxes = paxes; - this.distance = distance; - this.responseType = responseType; - this.unitType = unitType; - this.TranslationKey = new StringBuilder(Enum.GetName(typeof(BackupResponseType), responseType)).Append(Enum.GetName(typeof(BackupUnitType), unitType)).ToString(); - - if (this.vehicleData.Count is 0) return; - - // for (int i = 0; i < 10; i++) SpawnPoints.Add(World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(75f))); - List vPer = new(); - foreach (var v in this.vehicleData) vPer.Add(v.Key.Item2); - VehicleElementId = new RandomDraw(vPer).Choose(); - - this.vehicle = new(vehicleData.ElementAt(VehicleElementId).Key.Item1, World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(75f))); - this.vehicle.IsPersistent = true; - - List dPer = new(); - foreach (var d in this.driverData) dPer.Add(d.Key.Item2); - DriverElementId = new RandomDraw(dPer).Choose(); - - this.driver = new(driverData.ElementAt(DriverElementId).Key.Item1, Vector3.Zero, 0f); - this.driver.IsPersistent = true; - this.driver.BlockPermanentEvents = true; - this.driver.KeepTasks = true; - this.driver.WarpIntoVehicle(this.vehicle, -1); - - GameFiber.StartNew(() => - { - this.driver.Tasks.DriveToPosition(World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(10f)), 20f, VehicleDrivingFlags.DriveAroundObjects | VehicleDrivingFlags.DriveAroundPeds | VehicleDrivingFlags.DriveAroundVehicles | VehicleDrivingFlags.DriveBySight | VehicleDrivingFlags.StopAtDestination).WaitForCompletion(); - }, "[Backup] Init"); - - units.Add(this); - } - - internal virtual void GiveWeapon(Ped ped, Dictionary<(WeaponAsset, int), string[]> handGuns, Dictionary<(WeaponAsset, int), string[]> longGuns, bool hasStunGun, bool hasNightStick, bool hasFlashlight) - { - this.handGunsData = handGuns; - this.longGunsData = longGuns; - this.hasStunGun = hasStunGun; - this.hasNightStick = hasNightStick; - this.hasFlashlight = hasFlashlight; - - if (hasStunGun) ped.Inventory.GiveNewWeapon(WeaponHash.StunGun, 5000, false); - if (hasNightStick) ped.Inventory.GiveNewWeapon(WeaponHash.Nightstick, 5000, false); - if (hasFlashlight) ped.Inventory.GiveFlashlight(); - - foreach (var hg in handGuns) - { - if (hg.Key.Item2 > 100 || RandomNumber.Instance.Next(0, 101) <= hg.Key.Item2) - { - var wp = new WeaponAsset(hg.Key.Item1); - wp.Load(); - ped.Inventory.GiveNewWeapon(wp, 5000, false); - if (hg.Value.Length > 0) foreach (var comp in hg.Value) ped.Inventory.AddComponentToWeapon(wp, comp); - } - } - foreach (var lg in longGuns) - { - if (lg.Key.Item2 > 100 || RandomNumber.Instance.Next(0, 101) <= lg.Key.Item2) - { - var wp = new WeaponAsset(lg.Key.Item1); - wp.Load(); - ped.Inventory.GiveNewWeapon(wp, 5000, false); - if (lg.Value.Length > 0) foreach (var comp in lg.Value) ped.Inventory.AddComponentToWeapon(wp, comp); - } - } - } - - internal virtual void Dismiss() - { - } -} \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/BackupManager.cs b/DynamicLSPDFR/Backup/BackupManager.cs index 06729b8..bb735bf 100644 --- a/DynamicLSPDFR/Backup/BackupManager.cs +++ b/DynamicLSPDFR/Backup/BackupManager.cs @@ -58,119 +58,49 @@ internal enum BackupWorldZoneCounty : int NorthYankton = 4, } -internal enum BackupStatus : int -{ - IsMoving, - Arrived, - Dismiss, - Unknown, -} - -internal abstract class BackupBase -{ - internal static List AllUnits = new(); - internal abstract void Update(); - internal abstract void Dismiss(); - - internal static void AllClear() - { - AllUnits = new(); - } -} - internal static class BackupManager { - internal static BackupVehicleData GetRandomModel(this List vehicleDataList) - { - if (vehicleDataList.Count is 0) return null; - if (vehicleDataList.Count is 1) return vehicleDataList.FirstOrDefault(); - - int min = 0, max = 0, rnd = 0; - Dictionary<(int, int), BackupVehicleData> dic = new(); - foreach (var v in vehicleDataList) - { - max = v.chance; - dic.Add((min, max), v); - min = max; - } - rnd = RandomNumber.Instance.Next(max); - - foreach (var d in dic) if (d.Key.Item1 <= rnd && rnd < d.Key.Item2) return d.Value; - return dic.FirstOrDefault().Value; - } - - internal static Model GetRandomModel(this List pedDataList) - { - if (pedDataList.Count is 0) return null; - if (pedDataList.Count is 1) return pedDataList.FirstOrDefault().model; - - int min = 0, max = 0, rnd = 0; - Dictionary<(int, int), Model> dic = new(); - foreach (var v in pedDataList) - { - max = v.chance; - dic.Add((min, max), v.model); - min = max; - } - rnd = RandomNumber.Instance.Next(max); - - foreach (var d in dic) if (d.Key.Item1 <= rnd && rnd < d.Key.Item2) return d.Value; - return dic.FirstOrDefault().Value; - } + internal static List allUnits = new(); + internal static Dictionary<(BackupResponseType, BackupUnitType), BackupUnitDatabase> unitDatabase = new(); - internal static void StartUpdate() + internal static void DismissAllUnit() { - GameFiber.StartNew(() => - { - while (true) - { - GameFiber.Yield(); - BackupBase.AllUnits.Do(x => x.Update()); - } - }, "[DynamicLSPDFR] Backup Unit Update"); + foreach (var unit in allUnits) unit.Dismiss(); } } -internal class BackupVehicleData +internal class BackupUnitDatabase { - internal Model model { get; set; } - internal int chance { get; set; } - internal int paxCount { get; set; } - - internal BackupVehicleData(Model model, int chance, int paxCount) - { - this.model = model; - this.chance = chance; - this.paxCount = paxCount; - } -} - -internal class BackupPedData -{ - internal Model model { get; set; } - internal int chance { get; set; } - internal BackupUnitWeatherType weatherType { get; set; } - internal bool isRandomProp { get; set; } - - internal BackupPedData(Model model, int chance, BackupUnitWeatherType weatherType, bool isRandomProp) - { - this.model = model; - this.chance = chance; - this.weatherType = weatherType; - this.isRandomProp = isRandomProp; - } -} - -internal class BackupWeaponData -{ - internal string hash { get; set; } - internal int chance { get; set; } - internal List components { get; set; } - - internal BackupWeaponData(string hash, int chance, List components) + internal string UnitName { get; set; } + + internal BackupResponseType responseType { get; set; } + internal BackupUnitType unitType { get; set; } + + internal Vehicle vehicle { get; set; } + internal Ped driver { get; set; } + internal List<(int, Model, int)> vehicleData { get; set; } + internal List<(int, Model, int)> driverData { get; set; } + // internal Ped[] paxes { get; set; } + internal float distance { get; set; } + + internal bool hasStunGun { get; set; } + internal bool hasNightStick { get; set; } + internal bool hasFlashlight { get; set; } + internal List<(int, WeaponAsset, List)> handGunsData { get; set; } + internal List<(int, WeaponAsset, List)> longGunsData { get; set; } + + internal BackupUnitDatabase(string UnitName, + Vehicle vehicle, + Ped[] peds, + List<(int, Model, int)> vehicleData, + List<(int, Model, int)>[] pedsData, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List<(int, WeaponAsset, List)> handGunsData, + List<(int, WeaponAsset, List)> longGunsData) { - this.hash = hash; - this.chance = chance; - this.components = components; + } } \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/BackupUnit.cs b/DynamicLSPDFR/Backup/BackupUnit.cs new file mode 100644 index 0000000..5c19c4c --- /dev/null +++ b/DynamicLSPDFR/Backup/BackupUnit.cs @@ -0,0 +1,174 @@ +using static DynamicLSPDFR.Common.BackupCommon; + +namespace DynamicLSPDFR.Backup; + +internal class BackupUnit +{ + internal Vehicle vehicle { get; set; } + internal Ped driver { get; set; } + internal List<(int, Model, int)> vehicleData { get; set; } + internal List<(int, Model, int)> driverData { get; set; } + // internal Ped[] paxes { get; set; } + internal float distance { get; set; } + internal BackupResponseType responseType { get; set; } + internal BackupUnitType unitType { get; set; } + + internal Model VehicleModel { get; set; } + internal Model DriverModel { get; set; } + internal List SpawnPoints { get; set; } + + internal bool hasStunGun { get; set; } + internal bool hasNightStick { get; set; } + internal bool hasFlashlight { get; set; } + internal List<(int, WeaponAsset, List)> handGunsData { get; set; } + internal List<(int, WeaponAsset, List)> longGunsData { get; set; } + internal string TranslationKey { get; set; } + internal bool IsDismissed = false; + + internal BackupUnit() { } + internal BackupUnit(List<(int, Model, int)> vehicleData, + List<(int, Model, int)> driverData, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List<(int, WeaponAsset, List)> handGunsData, + List<(int, WeaponAsset, List)> longGunsData, + BackupResponseType responseType, + BackupUnitType unitType) + { + Init(vehicleData, driverData, distance, hasStunGun, hasNightStick, hasFlashlight, handGunsData, longGunsData, responseType, unitType); + } + + internal static BackupUnit Backup(List<(int, Model, int)> vehicleData, + List<(int, Model, int)> driverData, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List<(int, WeaponAsset, List)> handGunsData, + List<(int, WeaponAsset, List)> longGunsData, + BackupResponseType responseType, + BackupUnitType unitType) + => new BackupUnit(vehicleData, driverData, distance, hasStunGun, hasNightStick, hasFlashlight, handGunsData, longGunsData, responseType, unitType); + + private void Init(List<(int, Model, int)> vehicleData, + List<(int, Model, int)> driverData, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List<(int, WeaponAsset, List)> handGunsData, + List<(int, WeaponAsset, List)> longGunsData, + BackupResponseType responseType, + BackupUnitType unitType) + { + GameFiber.StartNew(() => + { + this.vehicleData = vehicleData; + this.driverData = driverData; + this.distance = distance; + this.responseType = responseType; + this.unitType = unitType; + this.TranslationKey = new StringBuilder(Enum.GetName(typeof(BackupResponseType), responseType)).Append(Enum.GetName(typeof(BackupUnitType), unitType)).ToString(); + this.handGunsData = handGunsData; + this.longGunsData = longGunsData; + this.hasStunGun = hasStunGun; + this.hasNightStick = hasNightStick; + this.hasFlashlight = hasFlashlight; + + if (this.vehicleData.Count is 0) return; + + // for (int i = 0; i < 10; i++) SpawnPoints.Add(World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(75f))); + this.VehicleModel = WeightedExtensions.Weighted(this.vehicleData); + this.VehicleModel.LoadAndWait(); + this.vehicle = new(this.VehicleModel, World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(75f))); + this.vehicle.IsPersistent = true; + this.vehicle.IsSirenOn = true; + + this.DriverModel = WeightedExtensions.Weighted(this.driverData); + this.DriverModel.LoadAndWait(); + this.driver = new(this.DriverModel, Vector3.Zero, 0f); + this.driver.IsPersistent = true; + this.driver.BlockPermanentEvents = true; + this.driver.KeepTasks = true; + this.driver.WarpIntoVehicle(this.vehicle, -1); + + if (hasStunGun) driver.Inventory.GiveNewWeapon(WeaponHash.StunGun, 5000, false); + if (hasNightStick) driver.Inventory.GiveNewWeapon(WeaponHash.Nightstick, 5000, false); + if (hasFlashlight) driver.Inventory.GiveFlashlight(); + + foreach (var hg in this.handGunsData) + { + if (hg.Item1 > 100 || RandomNumber.Instance.Next(0, 101) <= hg.Item1) + { + var wp = new WeaponAsset(hg.Item2); + wp.Load(); + this.driver.Inventory.GiveNewWeapon(wp, 5000, false); + if (hg.Item3.Count > 0) foreach (var comp in hg.Item3) this.driver.Inventory.AddComponentToWeapon(wp, comp); + } + } + foreach (var lg in this.longGunsData) + { + if (lg.Item1 > 100 || RandomNumber.Instance.Next(0, 101) <= lg.Item1) + { + var wp = new WeaponAsset(lg.Item2); + wp.Load(); + this.driver.Inventory.GiveNewWeapon(wp, 5000, false); + if (lg.Item3.Count > 0) foreach (var comp in lg.Item3) this.driver.Inventory.AddComponentToWeapon(wp, comp); + } + } + + var toDrivePos = World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(20f)); + while (this.driver.DistanceTo(Game.LocalPlayer.Character.Position) > 20f) + { + if (this.IsDismissed || !this.driver.IsInVehicle(this.vehicle, true)) break; + var posCache = this.driver.Position; + this.driver.Tasks.DriveToPosition(toDrivePos, 10f, VehicleDrivingFlags.DriveAroundObjects | VehicleDrivingFlags.DriveAroundPeds | VehicleDrivingFlags.DriveAroundVehicles).WaitForCompletion(100000); + if (this.IsDismissed || !this.driver.IsInVehicle(this.vehicle, true)) break; + if (posCache.DistanceTo(this.driver.Position) < 3f) + { + this.driver.Tasks.Clear(); + if (this.IsDismissed || !this.driver.IsInVehicle(this.vehicle, true)) break; + var pos = Game.LocalPlayer.Character.Position.Around(RandomNumber.Instance.Next(15, 25)); + this.vehicle.Position = new(pos.X, pos.Y, (float)World.GetGroundZ(pos, true, true)); + if (this.vehicle.IsUpsideDown) this.vehicle.Rotation = new(0f, 0f, this.vehicle.Rotation.Yaw); + continue; + } + } + var toParkPos = World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(10f)); + while (this.driver.DistanceTo(Game.LocalPlayer.Character.Position) > 5f) + { + if (this.IsDismissed || !this.driver.IsInVehicle(this.vehicle, true)) break; + var posCache = this.driver.Position; + this.driver.Tasks.ParkVehicle(toParkPos, this.vehicle.Heading).WaitForCompletion(300); + if (this.IsDismissed || !this.driver.IsInVehicle(this.vehicle, true)) break; + if (posCache.DistanceTo(this.driver.Position) < 5f) + { + this.driver.Tasks.Clear(); + break; + } + } + this.vehicle.IsSirenSilent = true; + this.driver.Tasks.LeaveVehicle(LeaveVehicleFlags.None); + }, "[Backup] Init"); + + BackupManager.allUnits.Add(this); + } + + internal virtual void Dismiss() + { + this.driver.Tasks.Clear(); + this.vehicle.IsSirenSilent = true; + this.vehicle.IsSirenOn = false; + + this.IsDismissed = true; + + this.driver.Tasks.EnterVehicle(this.vehicle, 5000, -1, EnterVehicleFlags.AllowJacking); + if (this.driver.IsInVehicle(this.vehicle, true)) this.driver.Tasks.EnterVehicle(this.vehicle, 5000, -1, EnterVehicleFlags.AllowJacking | EnterVehicleFlags.EnterFromOppositeSide); + if (this.driver.IsInVehicle(this.vehicle, true)) this.driver.WarpIntoVehicle(this.vehicle, -1); + + this.vehicle.Dismiss(); + this.driver.Dismiss(); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/LocalPatrol.cs b/DynamicLSPDFR/Backup/LocalPatrol.cs deleted file mode 100644 index 039ef50..0000000 --- a/DynamicLSPDFR/Backup/LocalPatrol.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DynamicLSPDFR.Backup; - -internal class LocalPatrol -{ - internal LocalPatrol() - { - - } -} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/ConvertExtensions.cs b/DynamicLSPDFR/Extensions/ConvertExtensions.cs index 7631919..f5a013c 100644 --- a/DynamicLSPDFR/Extensions/ConvertExtensions.cs +++ b/DynamicLSPDFR/Extensions/ConvertExtensions.cs @@ -2,43 +2,43 @@ namespace DynamicLSPDFR.Extensions; internal static class ConvertExtensions { - // internal static BackupUnitWeatherType ConvertToBackupWeather(WeatherHash weather) - // { - // switch (weather) - // { - // default: - // case WeatherHash.EXTRA_SUNNY: - // case WeatherHash.CLEAR: - // case WeatherHash.NEUTRAL: - // case WeatherHash.SMOG: - // case WeatherHash.FOGGY: - // case WeatherHash.OVERCAST: - // case WeatherHash.CLOUDS: - // case WeatherHash.HALLOWEEN: - // return BackupUnitWeatherType.Sunny; - // case WeatherHash.CLEARING: - // case WeatherHash.RAIN: - // case WeatherHash.THUNDER: - // return BackupUnitWeatherType.Rainy; - // case WeatherHash.SNOW: - // case WeatherHash.SNOWLIGHT: - // case WeatherHash.BLIZZARD: - // case WeatherHash.XMAS: - // return BackupUnitWeatherType.Snowy; - // } - // } + internal static BackupUnitWeatherType ConvertToBackupWeather(WeatherHash weather) + { + switch (weather) + { + default: + case WeatherHash.EXTRA_SUNNY: + case WeatherHash.CLEAR: + case WeatherHash.NEUTRAL: + case WeatherHash.SMOG: + case WeatherHash.FOGGY: + case WeatherHash.OVERCAST: + case WeatherHash.CLOUDS: + case WeatherHash.HALLOWEEN: + return BackupUnitWeatherType.Sunny; + case WeatherHash.CLEARING: + case WeatherHash.RAIN: + case WeatherHash.THUNDER: + return BackupUnitWeatherType.Rainy; + case WeatherHash.SNOW: + case WeatherHash.SNOWLIGHT: + case WeatherHash.BLIZZARD: + case WeatherHash.XMAS: + return BackupUnitWeatherType.Snowy; + } + } - // internal static BackupUnitWeatherType ConvertToBackupWeather(string weather) - // { - // weather.ToUpper(); - // return weather switch - // { - // "SUNNY" => BackupUnitWeatherType.Sunny, - // "RAINY" => BackupUnitWeatherType.Rainy, - // "SNOWY" => BackupUnitWeatherType.Snowy, - // _ => BackupUnitWeatherType.Sunny, - // }; - // } + internal static BackupUnitWeatherType ConvertToBackupWeather(string weather) + { + weather.ToUpper(); + return weather switch + { + "SUNNY" => BackupUnitWeatherType.Sunny, + "RAINY" => BackupUnitWeatherType.Rainy, + "SNOWY" => BackupUnitWeatherType.Snowy, + _ => BackupUnitWeatherType.Sunny, + }; + } internal static EWorldZoneCounty ConvertToWorldZoneCounty(string county) { @@ -53,26 +53,26 @@ internal static EWorldZoneCounty ConvertToWorldZoneCounty(string county) }; } - // internal static BackupWorldZoneCounty ConvertToBackupCounty(string county) - // { - // county.ToUpper(); - // return county switch - // { - // "LOSSANTOS" => BackupWorldZoneCounty.LosSantos, - // "LOSSANTOSCOUNTY" => BackupWorldZoneCounty.LosSantosCounty, - // "BLAINECOUNTY" => BackupWorldZoneCounty.BlaineCounty, - // "NORTHYANKTON" => BackupWorldZoneCounty.NorthYankton, - // _ => BackupWorldZoneCounty.LosSantos, - // }; - // } + internal static BackupWorldZoneCounty ConvertToBackupCounty(string county) + { + county.ToUpper(); + return county switch + { + "LOSSANTOS" => BackupWorldZoneCounty.LosSantos, + "LOSSANTOSCOUNTY" => BackupWorldZoneCounty.LosSantosCounty, + "BLAINECOUNTY" => BackupWorldZoneCounty.BlaineCounty, + "NORTHYANKTON" => BackupWorldZoneCounty.NorthYankton, + _ => BackupWorldZoneCounty.LosSantos, + }; + } - // internal static BackupWorldZoneCounty ConvertToBackupCounty(EWorldZoneCounty county) => - // county switch - // { - // EWorldZoneCounty.LosSantos => BackupWorldZoneCounty.LosSantos, - // EWorldZoneCounty.LosSantosCounty => BackupWorldZoneCounty.LosSantosCounty, - // EWorldZoneCounty.BlaineCounty => BackupWorldZoneCounty.BlaineCounty, - // EWorldZoneCounty.NorthYankton => BackupWorldZoneCounty.NorthYankton, - // _ => BackupWorldZoneCounty.LosSantos, - // }; + internal static BackupWorldZoneCounty ConvertToBackupCounty(EWorldZoneCounty county) => + county switch + { + EWorldZoneCounty.LosSantos => BackupWorldZoneCounty.LosSantos, + EWorldZoneCounty.LosSantosCounty => BackupWorldZoneCounty.LosSantosCounty, + EWorldZoneCounty.BlaineCounty => BackupWorldZoneCounty.BlaineCounty, + EWorldZoneCounty.NorthYankton => BackupWorldZoneCounty.NorthYankton, + _ => BackupWorldZoneCounty.LosSantos, + }; } \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/RandomDrawExtensions.cs b/DynamicLSPDFR/Extensions/RandomDrawExtensions.cs deleted file mode 100644 index b4d8cba..0000000 --- a/DynamicLSPDFR/Extensions/RandomDrawExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace DynamicLSPDFR.Extensions; - -internal class RandomDraw -{ - private List num; - private int totalNum; - private List cumulativeNum; - - internal RandomDraw(List num) - { - this.num = num; - cumulativeNum = new(); - - for (var i = 0; i < this.num.Count; i++) - { - totalNum += this.num[i]; - cumulativeNum[i] = totalNum; - } - } - - internal int Choose() - { - int rnd = RandomNumber.Instance.Next(0, totalNum); - int min = 0; - int max = cumulativeNum.Count - 1; - - while (min < max) - { - int index = (min + max) / 2; - int point = cumulativeNum[index]; - - if (rnd > point) min = index + 1; - else - { - if (rnd >= (index > 0 ? cumulativeNum[index - 1] : 0)) return index; - max = index; - } - } - - return max; - } -} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/WeightedExtensions.cs b/DynamicLSPDFR/Extensions/WeightedExtensions.cs new file mode 100644 index 0000000..18fa1bb --- /dev/null +++ b/DynamicLSPDFR/Extensions/WeightedExtensions.cs @@ -0,0 +1,23 @@ +namespace DynamicLSPDFR.Extensions; + +internal static class WeightedExtensions +{ + internal static Model Weighted(List<(int, Model, int)> list) + { + int total = 0, pick = 0; + foreach (var l in list) total += l.Item1; + + int rnd = RandomNumber.Instance.Next(total); + for (int i = 0; i < list.Count; i++) + { + if (rnd < list[i].Item1) + { + pick = i; + break; + } + rnd -= list[i].Item1; + } + + return list[pick].Item2; + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Main.cs b/DynamicLSPDFR/Main.cs index 45a4950..faf8cb0 100644 --- a/DynamicLSPDFR/Main.cs +++ b/DynamicLSPDFR/Main.cs @@ -10,6 +10,7 @@ global using System.Text.RegularExpressions; global using System.Xml; +global using DynamicLSPDFR.Backup; global using DynamicLSPDFR.Callouts; global using DynamicLSPDFR.Common; global using DynamicLSPDFR.Debug; @@ -74,6 +75,7 @@ private static void OnDutyStateChanged(bool OnDuty) if (!OnDuty) { DynamicMenu.Reset(); + BackupMenu.Reset(); #if Dev ShowPlayerPosition.Remove(); #endif @@ -89,6 +91,7 @@ private static void OnDutyStateChanged(bool OnDuty) KeyDown.StartUpdate(); RandomNumber.SetInstance(ClientSettings.Random); DynamicMenu.Initialize(); + BackupMenu.Initialize(); // Overlay.Initialize(); GameFiber.StartNew(() => @@ -106,6 +109,7 @@ private static void OnDutyStateChanged(bool OnDuty) { GameFiber.Yield(); DynamicMenu.Process(); + BackupMenu.Process(); } }, "[DynamicLSPDFR] Menu Update Process"); diff --git a/DynamicLSPDFR/Modules/BackupMenu/BackupMenu.cs b/DynamicLSPDFR/Modules/BackupMenu/BackupMenu.cs deleted file mode 100644 index e69de29..0000000 diff --git a/DynamicLSPDFR/Modules/KeyDownUpdate.cs b/DynamicLSPDFR/Modules/KeyDownUpdate.cs index 94e1611..2079467 100644 --- a/DynamicLSPDFR/Modules/KeyDownUpdate.cs +++ b/DynamicLSPDFR/Modules/KeyDownUpdate.cs @@ -16,6 +16,10 @@ internal static void StartUpdate() internal static void KeyDownUpdate() { + if (KeyExtensions.IsKeysDown(Keys.U)) + { + BackupMenu.SwitchVisible(); + } if (KeyExtensions.IsKeysDown(ClientSettings.OpenContextMenuKey, ClientSettings.OpenContextMenuModifierKey)) { DynamicMenu.SwitchVisible(); diff --git a/DynamicLSPDFR/Modules/Menu/BackupMenu.cs b/DynamicLSPDFR/Modules/Menu/BackupMenu.cs new file mode 100644 index 0000000..af65a93 --- /dev/null +++ b/DynamicLSPDFR/Modules/Menu/BackupMenu.cs @@ -0,0 +1,55 @@ +namespace DynamicLSPDFR.Modules; + +internal static class BackupMenu +{ + // Pool + private static MenuPool Pool; + + // Context Menu + private static UIMenu BackupMenuMain; + private static UIMenuListScrollerItem ResponseUnitTypeItem; + + // Code2 + private static UIMenuItem LocalPatrolCode2; + + private static UIMenuItem AllDismiss; + + internal static void Initialize() + { + // Pool + Pool = new(); + + // Backup Menu + BackupMenuMain = new(Translation.GetString("BackupMenuTitle"), MenuCommon.PluginInfo); + ResponseUnitTypeItem = new(Translation.GetString("BackupResponseUnitType"), Translation.GetString("BackupResponseUnitTypeDesc"), new[] { "Code2", "Code3", "Pursuit" }); + + // Code2 + LocalPatrolCode2 = new(Translation.GetString("LocalPatrol"), Translation.GetString("LocalPatrol")); + LocalPatrolCode2.Activated += (m, i) => BackupUnit.Backup(); + + AllDismiss = new(Translation.GetString("AllDismissItem"), Translation.GetString("AllDismissItemDesc")); + AllDismiss.Activated += (m, i) => BackupManager.DismissAllUnit(); + + Pool.Add(BackupMenuMain); + BackupMenuMain.AddItems(ResponseUnitTypeItem, LocalPatrolCode2, AllDismiss); + + BackupMenuMain.MouseControlsEnabled = false; + BackupMenuMain.ControlDisablingEnabled = false; + } + + internal static void SwitchVisible() + { + if (BackupMenuMain.Visible) BackupMenuMain.Visible = false; + else BackupMenuMain.Visible = true; + } + + internal static void Process() + { + Pool.ProcessMenus(); + } + + internal static void Reset() + { + Pool.Clear(); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/DynamicMenu/ContextMenu.cs b/DynamicLSPDFR/Modules/Menu/ContextMenu.cs similarity index 100% rename from DynamicLSPDFR/Modules/DynamicMenu/ContextMenu.cs rename to DynamicLSPDFR/Modules/Menu/ContextMenu.cs diff --git a/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml b/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml index 66052e6..2dbcdc8 100644 --- a/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml +++ b/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml @@ -1,6 +1,6 @@ w98m$$TD~sASJBXr<~e;s7X~5T)DhL zSgEMqTKBf;cx#kwyF6P>G2FDSalSypsXl718wOiDIZsm;FkkcbiDSLj2iyR$QL1 z>0}&Y^CLEs)*&cuF`Y!OKt)JA`H}N)91IM`XWCj-PkEW_N!vfI&8?MfLuF}-SLzmn zhy9fMCXL5EEV<;H97Ky48ylY3Kc#oqqO|_qTwS@Tjmfd`dC_}ApZm9Xt`pp3jYmV> zBe30;=lT0 z)@ypm=Ql@J&-tBQaN(EVkD0$p3p)QEQxR56Ygr~gZwSQ$k)2$QCnM;6L`A2W$+J_2 zcV3?o8=oEBHF%}>G(iPjM51C}^=`Sjb$|Wz>eJ=@D>#<#4(zMzeU<7 zXnb^I(!#E5da`@(&noHnDeFq+lD;eYmNYdYfW=y8yO7t~MvNylCOIcgXjspBUO~ubul5GwY|| zxa7KLAFsn6I^j2ti7q7H!As3#?U@d*CTgX!B;uFvx5=OX`BeDj@{esrpDK1Wob5Pa zR1i*vuW(Ml*vJ(LWvMmYcVt}db+%`9w@JzUqTdDWmBcd3U7w9Nl)YQ)tG|^lD_Kyu zt-xLM>km;C(eg;yYR#ma=G>Fg!{)~pb{?PJ(nsF!&cHtd1Ou*Qz3;iX^Y$43;HTn5 zdZ}Zd_I!JKP3@nE0`B)WU!HyZ^|MOQvR~sQd5iwG{VlnaK8?FT6z$>i z_~iLmazuENt7fn$R~6|8FUuHL9j!{x(aN4oj;3hvX?m)$qFx4Gx&uG@5vjxo$M9VFQk83zc+n! z8I!vXjQ<#Zz+1zLb>7tvZMW7O|Ghq6ldJkh{#NlVEN?mApJozfqvs2U1>B1! zb)KERIg2@nJ=ijM&A|J8z0+B#me}UNU6Od#LdO|{NBisQ@qdgz2j^Npn?E&tp7Jg7 zXLeCdxuPz*JzTfXe%g;LtyzTYfBVQQG?Mn=DKhgzchi<(7MO3;q)QA!TKE-;6t1cl&WP`js+ zeUhALzoTpIC~N9f=TTK$9$5ad{7hA9-RPDGxm}xPBV26SYR(mb#$&2?hOg0YiJ#TC z)VsxNzQ=A+FJ52P3FNbLf~`{bRd%mQR#jWtzt}H7JMZ2v)~^qFLkdn7FD{L&Io6b; zcxmRimoXNKKl_JAX2emtaJv`u9MWrF#`>NUyLIbKi?0hC<2ON+MU&x2^_$wJ))8d` zimv1h`7!J#CqK2AQ8BQgul!%b4FaQa#R~#HMXgS7b={wSG;>4mTba``4tKksyeQ`1 zkfUDRxe?@FmbqGL`-ZxgW&ahc3%m-(<P7_3X~0Rj%!$7XQ^FWceJ6RMXFG1``Bpi0M<5Po6q9Vq}a^l z@>F;Ce(9EUkDlq>PIWfLUy7&>$o1&S&7{_0V+=m4U;ldy%eI21+pl=xfabMoDsYPV*~OzAcKn zg?rrlaWJh@PI6h=^o*IkhGrI~Q@fX>jO^4iw6|2ntt4--hN+jf%j&W#NBlWjoKv*9 z_|Wf%Wp}Hx8#=Y0R(YDj@wcwS$VA3G7K6Q()l5H&d?fK4pDa54Vf6{Qx$Q#Bx~AHu zD=iIe8Hy(DQ;Q$bA1&tQduC);_lBRQ;zqS1qqW+sJKiP;Ao`naPfF(j9tF&fkLjqIZ&uo@~#5JmSS{;X&>w z=6LiKd6V-d?uA{kbFfq#cXV^XnuXi|X}k;WSHyDrXw!CWquistZ&Pb6yLwAyc=_wH zmh!)<_SW5L?yeYati(%^4nbeP^AWCuf6~Mm0a=W`DOs9KzntK#jYqpLbMuIeCZB-S|M4fz{w61$3f zo~PupIDE!j>Tbf@zBc$107hfGv`Jr+}wXi;}^?~x8;kTm;nkYE#WewUIos~2^ zmD}xLkN@2dZ@QdMq(_?q7kDkRmzgqr4=I$0LluHx%m!PFh}&1^sZ zYT;g5$+9y~0I+oL)j=B8plB-`3g7Fs!?aQe4C@0y zBCE9bpV-oS<*TaiG)8vx(2uoBU6JT9wtzQYKodsrW7!+fe_a`NiOwPK+8kB$q4Z?& z@IqH%LveNKiJH=uVs(o3GBrrB*|#*jYr@Dbb=^C9IWwX%rglHnWkKBS@Z)}Ggk4y{ zE~@pKR^8FJP2Z$#tZJIjdP}}WGtqpO2&1GhFY%Xqyz=_t9qGN&D_^vL7sVJ$*#Wz% zXElf9_SW^yubSvh9nCH6`<06f6}Iv2IkaNV2uZfL&fhzvHsnC4Dx_=h5x)l>i@D3G zTwJ94*<4+eak zOxnn}A=2JRmpjq(y$7&P+n=eA)fX2J`$sM?9Yb05k*Pm7ix3cKIuI1 z7}`I=df(pRd2xrkoa;U@JuYKm`uQ|_azrOtsM33n-~bx!$~E8DPE?JS&+AB+)79&A zJ{GlOJSB#)jyp)Sz@v}!yL75o4-bpr8LJW5N93BHsG?dg)*q`PN;m)R^=Cwxp}JS& zADK+I)V`2X%$_Lm^NS7r6se58-)VAOkC<7JPlKC$)<~?J7V0l&p-HMa(w1NMsdD`9 z9r>NVhkfh&rTojn+|zl#N=((8S{`e;_yl^jI5qHKv_5%#kMyj81M7!A9i|_;bx=UR z*XcJq-;Ev8dQcd`9**uJKO>G~4YpC%YD<6XUF&Nb%RbgF zu$S6~z>a8*;l8$187s?cF0PNPrd6>2Y$>6YtSpHw{aQJvzH8fB^-61{dpcvX=#%fD zu=41p_|+-qF3DYXbiST^C!P_j4wDAp@rkeU;b-8uP^~y$RYe7exdZ{tXe$4redX_npn<5(P(MkHoJIUv= z&noZ7Udugxi*E4q*s+Wjq>bFk{fv-0ZUB#Y-fpy+tdA|-Ol*C!dVhz!`F!24%74qu zf0q6ERkpc$TGJKz1>+;vFfQNsSfn<&Z#tH>V`#;Qs*&Eqb%Xq~7~O6rbPEX+QK*@g z5AvXzsRc{Fj>^$JJNtC{6ZK15PSc+=Z-AkADIZLUJPCN8O436U7KW z6P)Vb!~29}g>Wrb$=<>8U_E7iX1!u};@Y`gc!#)O*nZ4z=w5es{EqRgYGUij+HHU4 z7Knc_z6E@}l-v5lv*>jBt-80eB4ey;JEL8=#0L*V!_P-Ajd>7#DRNWTl)!^NO36ij zKI`mh>FR74iu^iHWm8* z?qAWpv5Ru6jf`9ojSNdkr1Yrhn>%dwh=!3>LlXye?p4yIJm!EuQ@D_F*BmKlHa#mJ zQIPt*uj;MQrSFQRcR`9cBPNxN<0F68vLI5uMWud5BFd0+vdGV z%9q~v%8`!u$@fhN2oFSpJ_QN`e)=5p93sePymH6ek~KxG{c7Ko-z)OSJN={V`>r3i z@~#$*D!Wph+}vL|O&@CSN*+nC<8%mbOE!9>dmi$5ATAfAay866dNsNg{f*Yrf3UcG zhWL$ir{CD%)nU;Q45YXgNt;CEe}WCVaNp5c3!bK(`$y0 zhu=BBBwwEQPsv^W8s>Fs3Guh}xW2Edv?HbCTgNEHJ=IQ~w{eB}t!08`p!t)2t>&`A z-F~~ZTkEX0{&JgUm~}g)fg9y}FOr@zC0*3_?SP7bWdj!UR(AUnzbWjEhmF44A=MYP zG**B9J*M#T_rG%oe;b;c`h9xd!{XkRBOB((e;G#Oo|JIr5&kTXIPY}dOFkT*tx_*9 zvm{TPBjSp8NMxQ%d|vq<4UPyq93C7#KjgQ+&hruP8FIzmN26}3s(4#8@yF<|K3`J5 z_Wb@kZ`7aZ)mNJ@szw=W@XM43%s#x&0=oE>c!K1Hh%0!`eaY%dZ-9*ABIxt>M4K7= zxUu5(()$71!Ujj>#9oN&h#eIj5q83Fm&Z~zo3aD@V7LcHku{C08ur&Sn?|%uYx^Y6 zQD^IKn7&(H*}mJ~VJonBY>mAL3=Feuhiqrjbg!qZMH3hq z%%jXbbP>|wdT#G*h*p1W&urb&a=0x~Nik@M!Hkn0cf<2LU&%N;V9`+H;0uF9y*;~* zi8|)9iIwd*tMP1pQ$C?+RG#-Q=MVSK5Bc)qr=_8lyK1*KMz;M@c6U#CT=xD9atIYV5 zQaGq!RenbCqd&*09gX3N3?0SBB???afXtZ!c?G6>lT!i-_Wzt=q*2H+W^Z1r_=V5l zz<_Xhls%@Z)BL!)*i|uWB5k4X0x$V2keudyqi2(AuzX{xcBcGnTV(Tq#+MD<8?QHA zZJj9F1I`7DZn$x|d8T!_?W^s&eF+wUXE<&US6v7>i`t4#X4sgE*q=D-xR1G;xhBqP z&H^@-{Wq(UxsRE}e86zhf6<1cJE;@NuU-2c2km8+zf5cOA2kluJ4L;GU&qi6NymVW z-yJsjMdc|qrrmDLvuwc|UBT!z&SSCOR~>$>)7#|!X#>0MOMQ`88}Y*@nlsncZrCe} zXbh^ASNE(R(w3?(bDic$y;p~|MkzY=j(!n(%_o)noaAX7+cu|mKxuW+j>4GVFDr8E zQQ0p|m3c9yb|kxYI9tFUd=Kw~J;JU!yj}e%bLgp@Dq)1z8K2kwg96_9&GMP!HBs~r zx1D~5^4&QH``gJfZ_9eu85(61UU&Wlm{>T<` zez4h`fgC+&9B;JXr6|MmAMbmv=rJO>&82hxM$WkEE$(a3mbQpG5M4rkM48q6 zno=mb=)Wv-TB0jyUCN4tyjWV8BjA{Ht|V9_6c~iJ#KWXZeCLA?@Yt+~>cC0Te*AZ|PXya))Lm9zRP9ke)^9Km!R|Zn zkkgS{Xc{_zT14s(W0JtK+#uEcr{(GX=(EkcvGFcDwLjB~w@x(Dquz6zm)z4P*&^yC zxWcVvXR@r!FRbO91-$XX0pd^K4_xGVRALeg;mk+fuC2C(+R^gw;4mcDcdFCYxf%jn zY;C9II;99w2ELkKYNhJ5a*}ens;6eEUSxTW?;(F@?iZf&bo)gIFAWV0>lHFL@T1RE zPd~vx7LM#A&vPAhP{G?Q$3;YibGcha>JEg+Bgn6pLXxeTD8^0pk2WvcVB4Yp{}_CQ zCTGex0^S0il-G?rg`LbiPAfuEs2?CP@d}b5c1k0aiL_D;6e+or>jA#Py4F~wy`;?T zNRySc&1yT;eo5Qcj_A%AG{%eO=N504V;Is?0`%xfB0 zwxF2E8&gnMR@HDpGsUrjebQ%M*th8Ls8^vBf4b-ieVD7Wy~*5SS&I)LU1Q7FC$9C@7U zCz0vQAniiFv-*q1d7lcN6g@kBc*6SlM=^e(i1Z=%JUQLkp-OJw-@K%0eS52lYdP&a zP9MPA%AduX!uF>stOr%8&3`Im%6O%ciY;|xIxd+5$(uM0;x;co@1Gv;1)CY$T?JqX zn+EP_qv5;FkJLmr3%FkA{4V%E@%u;mLO6t-Pkl#>ww9U>8PbhYEvfbfq8GV4S^{qL zTg;6N3N3(o#XZ|G-&ShgU`#fgGTb!owG?5$oXaUBd;Pm#`m z73l{t(?uaCBJp$?^B$XE8<|^ZW5_{{p%#}ePBl#NPM)dQr=F>2Sr%ehM766oF#Pvi zGU5(?$^P56&o0Dy&JNNMRL}g$nJXw3_7sVP!})w}4YQ6GhWtkskS;*V)d!l5RIb&K z4V?o?zA(r*db>Sbp-wtM;3M#-7#)9)2N9!O22vp6PcLUuxkGrX`M&&co-gMWvy|qG zTqn;0zA6*Am~hA&s-ef?mFpqF!<(#h(>(a30}IRo{xKWqkL!?1&j*XXA2ABQZog|SG@mnVHx4&$G7dMH%xddTy9qzx zoZvQ+=2CK~D-mDl+RLE5qs7x}!Q4>@j`UN^ZOs16e1r;NYrwsG-b%KXT1Hs~ zHi~@;b`}5b2z1hblgonUrCrebVS>(RH>rYLMaiRvgW@Tfev{5-JfH_aVbg5{rE