diff --git a/.vscode/settings.json b/.vscode/settings.json index 3bc23c0..55cdb36 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,20 +7,30 @@ "Ballas", "ballasout", "BLAINECOUNTY", + "BODHI", "Callout", "Callouts", "CARBINERIFLE", "Cayo", + "chicold", + "Decryptor", "Dekokiyo", "DOUBLEACTION", "Driveable", + "Encryptor", "famca", "famdnf", "famfor", "FBISWAT", + "FLSH", + "Gusenberg", "Hawick", "HEAVYPISTOL", + "intupperphotography", + "Jakowski", + "Lazlow", "LCPDFR", + "lestercrest", "LOSSANTOS", "LOSSANTOSCOUNTY", "lossantospolicedept", @@ -28,18 +38,23 @@ "LSSD", "lxml", "Mantain", + "nervousron", "NORTHYANKTON", "openpyxl", "Paleto", "Perico", "Perro", + "POLICEOLD", "POLICET", "Puerta", + "Ragdoll", "roadcone", "Rockstar", "SNIPERRIFLE", "Snowlight", + "tendtodead", "Tongva", + "UNSTREAMED", "Vinewood", "voyeurizing", "Yankton" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 06922f5..3fdc100 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -22,9 +22,19 @@ } }, { - "label": "Release-Zip", + "label": "Dev-Confused", + "type": "shell", + "command": "powershell -ExecutionPolicy Bypass .\\DevelopBuild-Confused.ps1", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Release-Confused", "type": "shell", - "command": "powershell -ExecutionPolicy Bypass .\\ReleaseZip.ps1", + "command": "powershell -ExecutionPolicy Bypass .\\ReleaseBuild-Confused.ps1", "problemMatcher": [], "group": { "kind": "build", @@ -32,14 +42,14 @@ } }, { - "label": "Format", + "label": "Release-Zip", "type": "shell", - "command": "dotnet format DynamicLSPDFR/DynamicLSPDFR.csproj", + "command": "powershell -ExecutionPolicy Bypass .\\ReleaseZip-Confused.ps1", "problemMatcher": [], "group": { "kind": "build", - "isDefault": false + "isDefault": true } - } + }, ] } \ No newline at end of file diff --git a/Archive/ManualSirenLevel.cs b/Archive/ManualSirenLevel.cs new file mode 100644 index 0000000..b3c5c92 --- /dev/null +++ b/Archive/ManualSirenLevel.cs @@ -0,0 +1,46 @@ +namespace DynamicLSPDFR.Debug; + +internal static class ManualSiren +{ + internal static int Level = 0; + + internal static void Initialize() + { + GameFiber.StartNew(() => + { + while (true) + { + GameFiber.Yield(); + if (Level > 0) + { + Level--; + GameFiber.Wait(100); + } + } + }); + + GameFiber.StartNew(Update); + } + + internal static void Update() + { + while (true) + { + GameFiber.Yield(); + Game.DisplayHelp(Level.ToString()); + } + } + + internal static void AddValue() + { + GameFiber.StartNew(() => + { + GameFiber.Yield(); + if (Level < 100) + { + Level++; + GameFiber.Wait(100); + } + }); + } +} \ No newline at end of file diff --git "a/Archive/\346\227\247\343\203\220\343\203\203\343\202\257\343\202\242\343\203\203\343\203\227\351\201\213\350\273\242\343\202\267\343\202\271\343\203\206\343\203\240.cs" "b/Archive/\346\227\247\343\203\220\343\203\203\343\202\257\343\202\242\343\203\203\343\203\227\351\201\213\350\273\242\343\202\267\343\202\271\343\203\206\343\203\240.cs" new file mode 100644 index 0000000..7caeefe --- /dev/null +++ "b/Archive/\346\227\247\343\203\220\343\203\203\343\202\257\343\202\242\343\203\203\343\203\227\351\201\213\350\273\242\343\202\267\343\202\271\343\203\206\343\203\240.cs" @@ -0,0 +1,27 @@ +if (this.driver && this.driver.IsValid()) +{ + while (this.driver.DistanceTo(Game.LocalPlayer.Character.Position) > 22.5f) + { + try + { + if (this.IsDismissed || !this.driver.IsInVehicle(this.vehicle, true)) break; + var posCache = this.driver.Position; + this.driver.Tasks.DriveToPosition(World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(RandomNumber.Instance.Next(25, 30))), 10f, VehicleDrivingFlags.DriveAroundObjects | VehicleDrivingFlags.DriveAroundPeds | VehicleDrivingFlags.DriveAroundVehicles).WaitForCompletion(); + 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; + } + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 110); + Logger.Error(e.ToString()); + } + } +} \ No newline at end of file diff --git a/DevelopBuild-Confused.ps1 b/DevelopBuild-Confused.ps1 new file mode 100644 index 0000000..5f69224 --- /dev/null +++ b/DevelopBuild-Confused.ps1 @@ -0,0 +1,15 @@ +Write-Host "-------- Build as the Develop Edition --------" -ForegroundColor DarkMagenta +Write-Host "-------- Comfused Enabled --------" -ForegroundColor Green +Start-Sleep -Milliseconds 250 +.\PowerShell\Dev.ps1 +Write-Host "-------- Build Completed --------" -ForegroundColor Green +Write-Host "-------- Start Obfuscation --------" -ForegroundColor Blue +Start-Sleep -Milliseconds 250 +.\PowerShell\Obfuscation.ps1 +Write-Host "-------- Obfuscation Completed --------" -ForegroundColor Green +Write-Host "-------- Copy Files to GTA5 --------" -ForegroundColor Blue +Start-Sleep -Milliseconds 250 +.\PowerShell\CopyFiles-Confuse.ps1 +Start-Sleep -Milliseconds 250 +Write-Host "-------- Copy Completed --------" -ForegroundColor Green +.\PowerShell\Completed.ps1 \ No newline at end of file diff --git a/DevelopBuild.ps1 b/DevelopBuild.ps1 index 931d770..6f2d110 100644 --- a/DevelopBuild.ps1 +++ b/DevelopBuild.ps1 @@ -1,11 +1,9 @@ Write-Host "-------- Build as the Develop Edition --------" -ForegroundColor DarkMagenta +Write-Host "-------- Comfused Disabled --------" -ForegroundColor Red Start-Sleep -Milliseconds 250 .\PowerShell\Dev.ps1 Write-Host "-------- Build Completed --------" -ForegroundColor Green -Write-Host "-------- Start Obfuscation --------" -ForegroundColor Blue -Start-Sleep -Milliseconds 250 -.\PowerShell\Obfuscation.ps1 -Write-Host "-------- Obfuscation Completed --------" -ForegroundColor Green +Write-Host "-------- Skip Obfuscation --------" -ForegroundColor Blue Write-Host "-------- Copy Files to GTA5 --------" -ForegroundColor Blue Start-Sleep -Milliseconds 250 .\PowerShell\CopyFiles.ps1 diff --git a/Document/Animations.txt b/Document/Animations.txt index 26a3e8d..a38c9de 100644 --- a/Document/Animations.txt +++ b/Document/Animations.txt @@ -38,5 +38,17 @@ amb@world_human_cop_idles@female@idle_enter - idle_intro 運ぶときの抱えるアニメーション的な +anim@heists@box_carry +- idle + +無線 +random@arrests +- generic_radio_enter +- generic_radio_chatter +- generic_radio_exit anim@heists@box_carry@ -- idle \ No newline at end of file +- idle + +写真 +amb@world_human_paparazzi@male@base +- base \ No newline at end of file diff --git "a/Document/Readme/LSPDFR\343\202\263\343\203\237\343\203\245\343\203\213\343\203\206\343\202\243\351\205\215\345\270\203\347\254\254\344\270\211\345\274\276\347\224\250Readme.txt" "b/Document/Readme/LSPDFR\343\202\263\343\203\237\343\203\245\343\203\213\343\203\206\343\202\243\351\205\215\345\270\203\347\254\254\344\270\211\345\274\276\347\224\250Readme.txt" new file mode 100644 index 0000000..f91b738 --- /dev/null +++ "b/Document/Readme/LSPDFR\343\202\263\343\203\237\343\203\245\343\203\213\343\203\206\343\202\243\351\205\215\345\270\203\347\254\254\344\270\211\345\274\276\347\224\250Readme.txt" @@ -0,0 +1,60 @@ +===================================================================================== +                                    + このプラグインはベータ版です。バグ検証はしておりますが、万が一重大なバグが発生して、 + プレイヤーに対し損害が発生したとしても、開発者は一切の責任を負いません。     + また、バグが発生した場合は、バグ報告のご協力をお願い致します。         + バグ報告先は、ロケット霊夢様の快諾を得て、LSPDFRコミュニティ(JP)内の専用スレッドへ + ご送信ください。開発者が直接対応させていただきます。 +                                    +===================================================================================== + +Copyright 2023 DekoKiyo + +LSPDFRコミュニティ(JP)配布用 DynamicLSPDFR 開発版Ver.0.3.0.0をDLして頂きありがとうございます。 +導入前に以下の注意をお読みください。 + +【注意】 +1. このファイルはDekoKiyo(以下開発者)の意思により配布しています。このサーバーへ参加しているかを問わず、他人に二次配布は行わないでください。 +2. このファイルは著作権法に基づきソースコードは公開されていません。 + 逆コンパイルなどでソースコードを不正に入手する行為は認められていません。 + 不正流出防止のためにファイルの暗号化はしていますが、 + 万が一、ソースコードの不正流出またはそれに類する行為が確認された場合は、このサーバーでの配布を停止します。 + 発覚したらブチギレだぞヽ(`Д´#)ノ ムキー!! +3. 質問chや僕のDMにパスワード関連の問い合わせはしないでください。 + したらちゃぶ台投げるぞ(ノ`Д´)ノ彡┻━┻ + +【説明】 +DynamicLSPDFRはLCPDFR.comにて公開されているLSPDFR用プラグインです。 +特徴として、多言語対応、独自の乱数生成アルゴリズムを搭載しています。 +また、このデータではコールアウトを12個追加します。(完成度が高いとは言っていない。) + +このデータはVer.0.3.0.0となっていますが、もしかしたら正式リリース1.0.0.0になるかもしれないです。 +結構頑張って作ってるので使ってくれるだけでも嬉しいです(*´∀`) +ってなわけなんですが、一つ注意です。上記の通りバグを含んでいる可能性があります。 +万が一これを導入してLSPDFRがクラッシュした場合はLSPDFRコミュニティ(JP)内の専用スレッドを用意しているのでそちらにバグ報告をお願い致します。 +何をしたらクラッシュしたのかとログファイル(GTA5.exeがあるディレクトリの中にあるRAGEPluginHook.log)を添付してくださいますと非常に助かります。 + +【追加機能】 +既存の機能については公式Wikiを御覧ください→ (https://github.com/Dekokiyo/DynamicLSPDFR/wiki) +・コールアウト「銀行強盗」を追加 +・コールアウト「ひき逃げ」を追加 +・コールアウト「危険人物を発見」を追加 + +正式版はこちらでDL可能です。 +https://www.lcpdfr.com/downloads/gta5mods/scripts/43022-dynamic-lspdfr/ + +開発者SNS +Twitter: https://twitter.com/DekoKiyomori +GitHub: https://github.com/DekoKiyo + + + + + + + + + + + +パスワード: 3DLS417 \ No newline at end of file diff --git "a/Document/\343\202\250\343\203\251\343\203\274\343\202\263\343\203\274\343\203\211.txt" "b/Document/\343\202\250\343\203\251\343\203\274\343\202\263\343\203\274\343\203\211.txt" new file mode 100644 index 0000000..79448d5 --- /dev/null +++ "b/Document/\343\202\250\343\203\251\343\203\274\343\202\263\343\203\274\343\203\211.txt" @@ -0,0 +1,68 @@ +[DynamicLSPDFR] +30 +交通切符のXmlの読み込みに失敗 + +31 +DefaultBackup.xmlの読み込みに失敗 + +50 +コールアウトの開始時の初期化に失敗 + +51 +コールアウトの開始時、Pedの生成に失敗 + +52 +コールアウトの開始時、Vehicleの生成に失敗 + +53 +コールアウトの開始時、Blipの生成に失敗 + +60 +コールアウトの進行中、エラーが発生 + +[DynamicLSPDFR-Backup] +100 +無線アニメーション処理でのエラー + +101 +車両スポーン位置選定でのエラー + +102 +車両モデル選定処理でのエラー +運転手モデル選定処理でのエラー + +103 +車両生成時のエラー + +104 +運転手生成時のエラー + +105 +スタンガン、警棒、フラッシュライトのいずれかの付与処理でのエラー + +106 +ハンドガンの付与処理でのエラー + +107 +ロングガンの付与処理でのエラー + +108 +応援車両にBlipを付与する際のエラー + +109 +欠番 + +110 +応援車両移動中のエラー + +111 +応援車両駐車中のエラー + +112 +応援降車処理中のエラー + +115 +応援解散時のエラー + +116 +応援削除のエラー \ No newline at end of file diff --git "a/Document/\343\202\250\343\203\251\343\203\274\343\202\263\343\203\274\343\203\211\344\270\200\350\246\247.txt" "b/Document/\343\202\250\343\203\251\343\203\274\343\202\263\343\203\274\343\203\211\344\270\200\350\246\247.txt" deleted file mode 100644 index ccaf6b4..0000000 --- "a/Document/\343\202\250\343\203\251\343\203\274\343\202\263\343\203\274\343\203\211\344\270\200\350\246\247.txt" +++ /dev/null @@ -1,5 +0,0 @@ -50: コールアウトの開始時の初期化に失敗 -51: コールアウトの開始時、Pedの生成に失敗 -52: コールアウトの開始時、Vehicleの生成に失敗 -53: コールアウトの開始時、Blipの生成に失敗 -60: コールアウトの進行中、エラーが発生 \ No newline at end of file diff --git "a/Document/\350\250\255\345\256\232\343\203\225\343\202\241\343\202\244\343\203\253\344\272\210\345\202\231\350\273\215.txt" "b/Document/\350\250\255\345\256\232\343\203\225\343\202\241\343\202\244\343\203\253\344\272\210\345\202\231\350\273\215.txt" new file mode 100644 index 0000000..714fd9d --- /dev/null +++ "b/Document/\350\250\255\345\256\232\343\203\225\343\202\241\343\202\244\343\203\253\344\272\210\345\202\231\350\273\215.txt" @@ -0,0 +1,10 @@ +; Open Traffic Tickets Menu Key +OpenTrafficTicketsMenuKey = U +OpenTrafficTicketsMenuKey-Modifier = LControlKey + +[TrafficTickets] +; Limit points of seize suspect vehicle +LimitOfSeize = 10 +Unit = $ +; If it's true, the unit is displayed in right. +RightUnitPosition = false \ No newline at end of file diff --git a/DynamicLSPDFR.sln b/DynamicLSPDFR.sln index d2849b9..000098f 100644 --- a/DynamicLSPDFR.sln +++ b/DynamicLSPDFR.sln @@ -18,4 +18,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {61480014-3AFE-4A09-BBD4-194D66D6392E} + EndGlobalSection EndGlobal diff --git a/DynamicLSPDFR/Backup/BackupManager.cs b/DynamicLSPDFR/Backup/BackupManager.cs new file mode 100644 index 0000000..696c10f --- /dev/null +++ b/DynamicLSPDFR/Backup/BackupManager.cs @@ -0,0 +1,202 @@ +namespace DynamicLSPDFR.Backup; + +#nullable enable +internal static class BackupManager +{ + internal static List AllUnits = new(); + internal static Dictionary AllBackupUnitData = new(); + internal static int BackupCount = 0; + + internal static List TempV = new() { new(100, "POLICE", 1) }; + internal static List TempP = new() { new(100, "POLICE", false) }; + + internal static ZoneCounty county = ZoneCounty.LosSantos; + internal static float distance = 150f; + internal static List vehicleData = new(); + internal static List pedData = new(); + internal static bool hasStunGun = true; + internal static bool hasNightStick = true; + internal static bool hasFlashlight = true; + internal static List handGunsData = new(); + internal static List longGunsData = new(); + + internal static Dictionary UnitNodes = new() + { + { UnitType.LocalPatrol, "DynamicBackup/LocalPatrol/Unit" }, + }; + + internal static void GenerateBackupFile() { } + + internal static void LoadDefaultLocalPatrol() + { + var defaultBackupFile = new XmlDocument(); + if (File.Exists(DEFAULT_BACKUP_FILE)) defaultBackupFile.Load(DEFAULT_BACKUP_FILE); + else + { + Logger.ErrorCode("DynamicLSPDFR", 31); + Logger.Error("There is no DefaultBackup.xml File in the direction."); + } + BackupManager.AllBackupUnitData[UnitType.LocalPatrol] = LoadFile(defaultBackupFile.SelectNodes(UnitNodes[UnitType.LocalPatrol])); + } + + internal static BackupData LoadFile(XmlNodeList nodePath) + { + foreach (XmlNode n in nodePath) + { + county = ConvertExtensions.ConvertToBackupCounty(n.Attributes["county"].InnerText ?? "LosSantos"); + var vehicles = n.SelectNodes("Vehicles/Vehicle"); + var peds = n.SelectNodes("Peds/Ped"); + var handGuns = n.SelectNodes("Weapons/HandGuns/Weapon"); + var longGuns = n.SelectNodes("Weapons/LongGuns/Weapon"); + + foreach (XmlNode v in vehicles) + { + if (v.InnerText is not null) + vehicleData.Add(new(int.Parse(v.Attributes["chance"].InnerText ?? "100"), + v.InnerText, + int.Parse(v.Attributes["paxCount"].InnerText ?? "1"))); + } + foreach (XmlNode p in peds) + { + if (p.InnerText is not null) + pedData.Add(new(int.Parse(p.Attributes["chance"].InnerText ?? "100"), + p.InnerText, + bool.Parse(p.Attributes["rndProp"].InnerText.ToLower() ?? "false"))); + } + foreach (XmlNode hg in handGuns) + { + List hgComponents = new(); + if (hg.SelectSingleNode("Components").SelectNodes("Component") is not null) + foreach (XmlNode cp in hg.SelectSingleNode("Components").SelectNodes("Component")) + if (cp.InnerText is not null) + hgComponents.Add(cp.InnerText); + if (hg.SelectSingleNode("Hash").InnerText is not null && hgComponents is not null) + handGunsData.Add(new(int.Parse(hg.Attributes["chance"].InnerText ?? "100"), + hg.SelectSingleNode("Hash").InnerText, + hgComponents)); + } + foreach (XmlNode lg in longGuns) + { + List lgComponents = new(); + if (lg.SelectSingleNode("Components").SelectNodes("Component") is not null) + foreach (XmlNode cp in lg.SelectSingleNode("Components").SelectNodes("Component")) + if (cp.InnerText is not null) + lgComponents.Add(cp.InnerText); + if (lg.SelectSingleNode("Hash").InnerText is not null && lgComponents is not null) + longGunsData.Add(new(int.Parse(lg.Attributes["chance"].InnerText ?? "100"), + lg.SelectSingleNode("Hash").InnerText, + lgComponents)); + } + + distance = float.Parse(n.Attributes["distance"].InnerText ?? "150"); + hasStunGun = bool.Parse(n.SelectSingleNode("Weapons").Attributes["hasStunGun"].InnerText ?? "false"); + hasNightStick = bool.Parse(n.SelectSingleNode("Weapons").Attributes["hasNightStick"].InnerText ?? "false"); + hasFlashlight = bool.Parse(n.SelectSingleNode("Weapons").Attributes["hasFlashLight"].InnerText ?? "false"); + } + + BackupDataBase dataBase = new(distance, vehicleData, pedData, hasStunGun, hasNightStick, hasFlashlight, handGunsData, longGunsData); + return new(county, dataBase); + } + + internal static void Initialize() + { + GenerateBackupFile(); + LoadDefaultLocalPatrol(); + } + + internal static void DismissAllUnit() + { + foreach (var unit in AllUnits) unit.Dismiss(); + } + + internal static void DeleteAllUnit() + { + foreach (var unit in AllUnits) unit.Delete(); + } +} + +internal class BackupData +{ + internal ZoneCounty county { get; set; } + internal BackupDataBase? data { get; set; } + + internal BackupData(ZoneCounty county, BackupDataBase? data) + { + this.county = county; + this.data = data; + } +} + +internal class ChanceVehicle +{ + internal int? chance { get; set; } + internal Model? model { get; set; } + internal int? pax { get; set; } + + internal ChanceVehicle(int? chance, Model? model, int? pax) + { + this.chance = chance; + this.model = model; + this.pax = pax; + } +} + +internal class ChancePed +{ + internal int? chance { get; set; } + internal Model? model { get; set; } + internal bool? randomProp { get; set; } + + internal ChancePed(int? chance, Model? model, bool? randomProp) + { + this.chance = chance; + this.model = model; + this.randomProp = randomProp; + } +} + +internal class ChanceWeapon +{ + internal int? chance { get; set; } + internal WeaponAsset? weapon { get; set; } + internal List? components { get; set; } + + internal ChanceWeapon(int? chance, WeaponAsset? weapon, List? components) + { + this.chance = chance; + this.weapon = weapon; + this.components = components; + } +} + +internal class BackupDataBase +{ + internal float? distance { get; set; } + internal List? vehicleDataList { get; set; } + internal List? pedDataList { get; set; } + internal bool? hasStunGun { get; set; } + internal bool? hasNightStick { get; set; } + internal bool? hasFlashlight { get; set; } + internal List? handGunsData { get; set; } + internal List? longGunsData { get; set; } + + internal BackupDataBase(float? distance, + List? vehicleDataList, + List? pedDataList, + bool? hasStunGun, + bool? hasNightStick, + bool? hasFlashlight, + List? handGunsData, + List? longGunsData) + { + this.vehicleDataList = vehicleDataList; + this.pedDataList = pedDataList; + this.distance = distance; + this.hasStunGun = hasStunGun; + this.hasNightStick = hasNightStick; + this.hasFlashlight = hasFlashlight; + this.handGunsData = handGunsData; + this.longGunsData = longGunsData; + } +} +#nullable disable \ No newline at end of file diff --git a/DynamicLSPDFR/Backup/BackupUnit.cs b/DynamicLSPDFR/Backup/BackupUnit.cs new file mode 100644 index 0000000..f300bc5 --- /dev/null +++ b/DynamicLSPDFR/Backup/BackupUnit.cs @@ -0,0 +1,429 @@ +namespace DynamicLSPDFR.Backup; + +internal class BackupUnit +{ + internal Vehicle vehicle { get; set; } + internal List vehicleDataList { get; set; } + internal List pedDataList { get; set; } + internal float distance { get; set; } + internal ResponseType responseType { get; set; } + internal UnitType unitType { get; set; } + internal bool hasStunGun { get; set; } + internal bool hasNightStick { get; set; } + internal bool hasFlashlight { get; set; } + internal List handGunsData { get; set; } + internal List longGunsData { get; set; } + + private Model VehicleModel { get; set; } + private Model DriverModel { get; set; } + internal ChanceVehicle vehicleData { get; set; } + internal ChancePed pedData { get; set; } + private string TranslationKey { get; set; } + private Ped driver { get; set; } + private List paxes { get; set; } = new(); + private Blip vehicleBlip; + private Blip driverBlip; + private List paxBlips = new(); + private bool IsDismissed = false; + + internal BackupUnit() { } + internal BackupUnit(List vehicleDataList, + List pedDataList, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List handGunsData, + List longGunsData, + ResponseType responseType, + UnitType unitType) + { + Init(vehicleDataList, pedDataList, distance, hasStunGun, hasNightStick, hasFlashlight, handGunsData, longGunsData, responseType, unitType); + } + internal static BackupUnit Backup(List vehicleDataList, + List pedDataList, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List handGunsData, + List longGunsData, + ResponseType responseType, + UnitType unitType) + => new BackupUnit(vehicleDataList, pedDataList, distance, hasStunGun, hasNightStick, hasFlashlight, handGunsData, longGunsData, responseType, unitType); + internal static BackupUnit Backup(BackupData database, ResponseType responseType, UnitType unitType) + => new BackupUnit(database.data.vehicleDataList, + database.data.pedDataList, + (float)database.data.distance, + (bool)database.data.hasStunGun, + (bool)database.data.hasNightStick, + (bool)database.data.hasFlashlight, + database.data.handGunsData, + database.data.longGunsData, + responseType, + unitType); + + private void Init(List vehicleDataList, + List pedDataList, + float distance, + bool hasStunGun, + bool hasNightStick, + bool hasFlashlight, + List handGunsData, + List longGunsData, + ResponseType responseType, + UnitType unitType) + { + GameFiber.StartNew(() => + { + this.vehicleDataList = vehicleDataList; + this.pedDataList = pedDataList; + this.distance = distance; + this.responseType = responseType; + this.unitType = unitType; + this.TranslationKey = new StringBuilder(Enum.GetName(typeof(ResponseType), responseType)).Append(Enum.GetName(typeof(UnitType), unitType)).ToString(); + this.handGunsData = handGunsData; + this.longGunsData = longGunsData; + this.hasStunGun = hasStunGun; + this.hasNightStick = hasNightStick; + this.hasFlashlight = hasFlashlight; + + if (this.vehicleDataList.Count is 0) return; + + try + { + GameFiber.StartNew(() => + { + Functions.PlayPlayerRadioAction(EPoliceRadioAction.Shoulder, 5000); + Functions.PlayScannerAudioUsingPosition("ASSISTANCE_REQUIRED IN_OR_ON_POSITION", Game.LocalPlayer.Character.Position); + }, "[DynamicLSPDFR-Backup] Radio"); + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 100); + Logger.Error(e.ToString()); + } + + try + { + List posList = new(); + for (int i = 0; i < 10;) + { + var pos = World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(this.distance / 1.5f, this.distance / 1f)); + if (Math.Abs(Game.LocalPlayer.Character.Position.Z - pos.Z) > 5f) continue; + posList.Add(pos); + i++; + } + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 101); + Logger.Error(e.ToString()); + } + + try + { + this.vehicleData = WeightedExtensions.Weighted(this.vehicleDataList); + this.pedData = WeightedExtensions.Weighted(this.pedDataList); + this.VehicleModel = (Model)this.vehicleData.model; + this.DriverModel = (Model)this.pedData.model; + this.VehicleModel.Load(); + this.DriverModel.Load(); + GameFiber.Sleep(RandomNumber.Instance.Next(3000, 4500)); + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 102); + Logger.Error(e.ToString()); + } + + try + { + float Heading; + bool UseSpecialID = true; + Vector3 SpawnPoint; + float travelDistance; + int waitCount = 0; + var pos = Game.LocalPlayer.Character.Position; + while (true) + { + VehicleExtensions.GetVehicleSpawnPoint(pos, out SpawnPoint, out Heading, UseSpecialID); + travelDistance = NativeFunction.Natives.CALCULATE_TRAVEL_DISTANCE_BETWEEN_POINTS(SpawnPoint.X, SpawnPoint.Y, SpawnPoint.Z, pos.X, pos.Y, pos.Z); + waitCount++; + if (Vector3.Distance(pos, SpawnPoint) > Main.CoronerSpawnDistance - 15f) + { + if (travelDistance < (Main.CoronerSpawnDistance * 4.5f)) + { + Vector3 directionFromVehicleToPed1 = (pos - SpawnPoint); + directionFromVehicleToPed1.Normalize(); + if (Math.Abs(MathHelper.NormalizeHeading(Heading) - MathHelper.NormalizeHeading(MathHelper.ConvertDirectionToHeading(directionFromVehicleToPed1))) < 150f) break; + } + } + if (waitCount >= 400) UseSpecialID = false; + else if (waitCount == 600) VehicleExtensions.ShowUnitTeleportHelp(); + else if (waitCount >= 600 && KeyExtensions.IsKeysDown(Keys.Y)) + { + SpawnPoint = pos.Around(18f); + break; + } + GameFiber.Yield(); + } + VehicleExtensions.GetVehicleSpawnPoint(pos, out SpawnPoint, out Heading, false); + this.vehicle = new(this.VehicleModel, SpawnPoint, Heading); + this.vehicle.IsPersistent = true; + if (this.vehicle.HasSiren) this.vehicle.IsSirenOn = true; + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 103); + Logger.Error(e.ToString()); + } + + try + { + this.driver = new(this.DriverModel, Vector3.Zero, 0f); + if (this.driver && this.driver.IsValid()) + { + this.driver.IsPersistent = true; + this.driver.BlockPermanentEvents = true; + this.driver.KeepTasks = true; + this.driver.WarpIntoVehicle(this.vehicle, -1); + Functions.SetPedAsCop(this.driver); + } + if (this.vehicleData.pax > 0) + { + for (int i = 0; i < this.vehicleData.pax; i++) + { + this.paxes[i] = new(this.DriverModel, Vector3.Zero, 0f); + this.paxes[i].IsPersistent = true; + this.paxes[i].BlockPermanentEvents = true; + this.paxes[i].KeepTasks = true; + this.paxes[i].WarpIntoVehicle(this.vehicle, i); + Functions.SetPedAsCop(this.paxes[i]); + } + } + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 104); + Logger.Error(e.ToString()); + } + + try + { + if (this.hasStunGun && this.driver && this.driver.IsValid()) this.driver.Inventory.GiveNewWeapon(WeaponHash.StunGun, 5000, false); + if (this.hasNightStick && this.driver && this.driver.IsValid()) this.driver.Inventory.GiveNewWeapon(WeaponHash.Nightstick, 5000, false); + if (this.hasFlashlight && this.driver && this.driver.IsValid()) this.driver.Inventory.GiveFlashlight(); + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 105); + Logger.Error(e.ToString()); + } + + foreach (var hg in this.handGunsData) + { + if (hg.chance > 100 || RandomNumber.Instance.Next(0, 101) <= hg.chance) + { + try + { + var wp = new WeaponAsset((uint)hg.weapon); + wp.Load(); + if (this.driver && this.driver.IsValid()) + { + this.driver.Inventory.GiveNewWeapon(wp, 5000, false); + if (hg.components.Count > 0) foreach (var comp in hg.components) this.driver.Inventory.AddComponentToWeapon(wp, comp); + } + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 106); + Logger.Error(e.ToString()); + } + } + } + foreach (var lg in this.longGunsData) + { + if (lg.chance > 100 || RandomNumber.Instance.Next(0, 101) <= lg.chance) + { + try + { + var wp = new WeaponAsset((uint)lg.weapon); + wp.Load(); + if (this.driver && this.driver.IsValid()) + { + this.driver.Inventory.GiveNewWeapon(wp, 5000, false); + if (lg.components.Count > 0) foreach (var comp in lg.components) this.driver.Inventory.AddComponentToWeapon(wp, comp); + } + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 107); + Logger.Error(e.ToString()); + } + } + } + + try + { + if (this.vehicle && this.vehicle.IsValid()) this.vehicleBlip = this.vehicle.AttachBlip(); + if (this.vehicleBlip && this.vehicleBlip.IsValid()) + { + this.vehicleBlip.Color = Color.FromArgb(101, 185, 231); + this.vehicleBlip.Scale = 1f; + this.vehicleBlip.Name = Translation.GetString("BackupVehicle"); + } + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 108); + Logger.Error(e.ToString()); + } + + switch (responseType) + { + default: + case ResponseType.Code2: + VehicleExtensions.DriveToPosition(driver, vehicle, Game.LocalPlayer.Character.Position); + try + { + if (!this.IsDismissed) + { + if (this.vehicleBlip && this.vehicleBlip.IsValid()) this.vehicleBlip.Delete(); + if (this.driver && this.driver.IsValid()) this.driverBlip = this.driver.AttachBlip(); + if (this.driverBlip && this.driverBlip.IsValid()) + { + this.driverBlip.Sprite = BlipSprite.GangPolicePartner; + this.driverBlip.Scale = 0.75f; + this.driverBlip.Color = Color.FromArgb(101, 185, 231); + this.driverBlip.Name = Translation.GetString("BackupOfficer"); + } + if (this.vehicleData.pax > 0) + { + for (int i = 0; i < this.vehicleData.pax; i++) + { + this.paxBlips[i].Sprite = BlipSprite.GangPolicePartner; + this.paxBlips[i].Scale = 0.75f; + this.paxBlips[i].Color = Color.FromArgb(101, 185, 231); + this.paxBlips[i].Name = Translation.GetString("BackupOfficer"); + } + } + if (this.driver && this.driver.IsValid()) this.driver.Tasks.LeaveVehicle(LeaveVehicleFlags.None); + foreach (var pax in this.paxes) if (pax && pax.IsValid()) pax.Tasks.LeaveVehicle(LeaveVehicleFlags.None); + } + if (this.vehicle && this.vehicle.IsValid() && this.vehicle.HasSiren && this.vehicle.IsSirenOn && !this.vehicle.IsSirenSilent) + this.vehicle.IsSirenSilent = true; + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 112); + Logger.Error(e.ToString()); + } + break; + case ResponseType.Code3: + VehicleExtensions.DriveToPosition(driver, vehicle, Game.LocalPlayer.Character.Position); + try + { + if (!this.IsDismissed) + { + if (this.vehicleBlip && this.vehicleBlip.IsValid()) this.vehicleBlip.Delete(); + if (this.driver && this.driver.IsValid()) this.driverBlip = this.driver.AttachBlip(); + if (this.driverBlip && this.driverBlip.IsValid()) + { + this.driverBlip.Sprite = BlipSprite.GangPolicePartner; + this.driverBlip.Color = Color.FromArgb(101, 185, 231); + this.driverBlip.Scale = 0.75f; + this.driverBlip.Name = Translation.GetString("BackupOfficer"); + } + if (this.driver && this.driver.IsValid()) this.driver.Tasks.LeaveVehicle(LeaveVehicleFlags.None); + } + if (this.vehicle && this.vehicle.IsValid()) this.vehicle.IsSirenSilent = true; + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 112); + Logger.Error(e.ToString()); + } + break; + case ResponseType.Pursuit: + NativeExtensions.Drive(vehicle, 10f); + Functions.AddCopToPursuit(Functions.GetCurrentPullover(), driver); + foreach (var pax in paxes) Functions.AddCopToPursuit(Functions.GetCurrentPullover(), pax); + break; + } + + GameFiber.Wait(3000); + GameFiber.StartNew(() => + { + while (true) + { + GameFiber.Yield(); + if (IsDismissed) break; + if (driver && driver.IsValid()) driver.Tasks.GoToOffsetFromEntity(Game.LocalPlayer.Character, 10000, 5f, 5, Game.LocalPlayer.Character.Speed).WaitForCompletion(); + } + }, "[DynamicLSPDFR-Backup] Supporting Player"); + }, "[DynamicLSPDFR-Backup] Initialize"); + + BackupManager.AllUnits.Add(this); + } + + internal virtual void Dismiss() + { + // try + // { + // this.driver.Tasks.Clear(); + // } + // catch { } + GameFiber.StartNew(() => + { + try + { + this.IsDismissed = true; + + if (this.vehicleBlip && this.vehicleBlip.IsValid()) this.vehicleBlip.Delete(); + if (this.driverBlip && this.driverBlip.IsValid()) this.driverBlip.Delete(); + foreach (var paxBlip in this.paxBlips) if (paxBlip && paxBlip.IsValid()) paxBlip.Delete(); + + if (this.driver && this.driver.IsValid() && this.driver.IsAlive) this.driver.Tasks.EnterVehicle(this.vehicle, 5000, -1, EnterVehicleFlags.AllowJacking).WaitForCompletion(3000); + if (!this.driver.IsInVehicle(this.vehicle, true) && this.driver && this.driver.IsValid() && this.driver.IsAlive) this.driver.Tasks.EnterVehicle(this.vehicle, 5000, -1, EnterVehicleFlags.AllowJacking | EnterVehicleFlags.EnterFromOppositeSide).WaitForCompletion(10000); + if (!this.driver.IsInVehicle(this.vehicle, true) && this.driver && this.driver.IsValid() && this.driver.IsAlive) this.driver.WarpIntoVehicle(this.vehicle, -1); + + if (this.vehicle && this.vehicle.IsValid()) + { + this.vehicle.IsSirenSilent = true; + if (this.vehicle.HasSiren && this.vehicle.IsSirenOn) this.vehicle.IsSirenOn = false; + } + + if (this.vehicle && this.vehicle.IsValid()) this.vehicle.Dismiss(); + if (this.driver && this.driver.IsValid()) this.driver.Dismiss(); + foreach (var pax in this.paxes) if (pax && pax.IsValid()) pax.Dismiss(); + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 115); + Logger.Error(e.ToString()); + } + }, "[DynamicLSPDFR-Backup] Dismiss"); + } + + internal void Delete() + { + GameFiber.StartNew(() => + { + try + { + if (this.vehicleBlip && this.vehicleBlip.IsValid()) this.vehicleBlip.Delete(); + if (this.driverBlip && this.driverBlip.IsValid()) this.driverBlip.Delete(); + foreach (var paxBlip in this.paxBlips) if (paxBlip && paxBlip.IsValid()) paxBlip.Delete(); + if (this.vehicle && this.vehicle.IsValid()) this.vehicle.Delete(); + if (this.driver && this.driver.IsValid()) this.driver.Delete(); + foreach (var pax in this.paxes) if (pax && pax.IsValid()) pax.Delete(); + } + catch (Exception e) + { + Logger.ErrorCode("DynamicLSPDFR-Backup", 116); + Logger.Error(e.ToString()); + } + }, "[DynamicLSPDFR-Backup] Dismiss"); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Callouts/CalloutList.cs b/DynamicLSPDFR/Callouts/CalloutList.cs index 405a33f..e14e9d9 100644 --- a/DynamicLSPDFR/Callouts/CalloutList.cs +++ b/DynamicLSPDFR/Callouts/CalloutList.cs @@ -15,6 +15,9 @@ internal static class CalloutList { typeof(SushiTerrorism), "SushiTerrorism" }, { typeof(StolenMoneyTruck), "StolenMoneyTruck" }, { typeof(StreetFight), "StreetFight" }, + { typeof(BankRobbery), "BankRobbery" }, + { typeof(HitAndRun), "HitAndRun" }, + { typeof(FoundDangerousPerson), "FoundDangerousPerson" }, // { typeof(PacificBankHeist), "PacificBankHeist" }, }; diff --git a/DynamicLSPDFR/Callouts/FoundDangerousPerson/FoundDangerousPerson.cs b/DynamicLSPDFR/Callouts/FoundDangerousPerson/FoundDangerousPerson.cs new file mode 100644 index 0000000..c24a7dc --- /dev/null +++ b/DynamicLSPDFR/Callouts/FoundDangerousPerson/FoundDangerousPerson.cs @@ -0,0 +1,184 @@ +namespace DynamicLSPDFR.Callouts; + +[CalloutInfo("FoundDangerousPerson", CalloutProbability.Low)] +internal class FoundDangerousPerson : CalloutBase +{ + private Dictionary suspects = new(); + private Vehicle vehicle; + private LHandle pursuit; + private Vector3 SpawnPoint = World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(RandomNumber.Instance.Next(500, 1200))); + private FoundDangerousPersonData.Team member; + private WeaponHash[] driverWeapons = new[] { WeaponHash.AdvancedRifle, WeaponHash.AssaultRifle, WeaponHash.CarbineRifle, WeaponHash.Smg, WeaponHash.AssaultSMG }; + private WeaponHash[] paxWeapons = new[] { WeaponHash.Pistol, WeaponHash.APPistol, WeaponHash.MicroSMG, WeaponHash.Smg, WeaponHash.AssaultSMG }; + private bool PursuitBegan = false, go = false; + private int sno = RandomNumber.Instance.Next(2); + + public override bool OnBeforeCalloutDisplayed() + { + CalloutMessage = Translation.GetString(CalloutList.AllCallouts[typeof(FoundDangerousPerson)]); + CalloutPosition = SpawnPoint; + ShowCalloutAreaBlipBeforeAccepting(SpawnPoint, 100f); + Functions.PlayScannerAudioUsingPosition("ATTENTION_ALL_UNITS WE_HAVE DL_CRIME_SUSPECT_RESISTING_ARREST_01 IN_OR_ON_POSITION UNITS_RESPOND_CODE_99", SpawnPoint); + return base.OnBeforeCalloutDisplayed(); + } + + public override bool OnCalloutAccepted() + { + Logger.Info("[DynamicLSPDFR] Callout \"Found Dangerous Person\" was started."); + + MessageExtensions.SendDispatchMessage(Translation.GetString("FoundDangerousPersonDesc"), Translation.GetString(CalloutList.AllCallouts[typeof(FoundDangerousPerson)])); + + try + { + member = (FoundDangerousPersonData.Team)RandomNumber.Instance.Next((int)FoundDangerousPersonData.Team.None); + + var vModel = new Model(FoundDangerousPersonData.Data[member].Item1); + vModel.LoadAndWait(); + + vehicle = new(vModel, SpawnPoint); + if (vehicle && vehicle.IsValid()) + { + vehicle.IsPersistent = true; + } + for (int i = 0; i < FoundDangerousPersonData.Data[member].Item2.Count; i++) + { + var model = FoundDangerousPersonData.Data[member].Item2[i]; + var p = new Ped(model, Vector3.Zero, 0f); + if (p && p.IsValid()) + { + switch (model) + { + case "player_zero": + Functions.GetPersonaForPed(p).Forename = "Michael"; + Functions.GetPersonaForPed(p).Surname = "De Santa"; + Functions.GetPersonaForPed(p).Birthday = new(1965, 2, 19); + break; + case "player_one": + Functions.GetPersonaForPed(p).Forename = "Franklin"; + Functions.GetPersonaForPed(p).Surname = "Clinton"; + Functions.GetPersonaForPed(p).Birthday = new(1988, 4, 23); + break; + case "player_two": + Functions.GetPersonaForPed(p).Forename = "Trevor"; + Functions.GetPersonaForPed(p).Surname = "Phillips"; + Functions.GetPersonaForPed(p).Birthday = new(1968, 11, 14); + break; + case "ig_nervousron": + Functions.GetPersonaForPed(p).Forename = "Ron"; + Functions.GetPersonaForPed(p).Surname = "Jakowski"; + Functions.GetPersonaForPed(p).Birthday = new(1964, 6, 4); + break; + case "ig_wade": + Functions.GetPersonaForPed(p).Forename = "Wade"; + Functions.GetPersonaForPed(p).Surname = "Hebert"; + break; + case "ig_lestercrest": + Functions.GetPersonaForPed(p).Forename = "Lester"; + Functions.GetPersonaForPed(p).Surname = "Crest"; + break; + } + p.IsPersistent = true; + p.BlockPermanentEvents = true; + if (i is 0) + { + p.Inventory.GiveNewWeapon(driverWeapons[RandomNumber.Instance.Next(driverWeapons.Length)], 5000, true); + p.WarpIntoVehicle(vehicle, -1); + } + else + { + p.Inventory.GiveNewWeapon(paxWeapons[RandomNumber.Instance.Next(paxWeapons.Length)], 5000, true); + p.WarpIntoVehicle(vehicle, i - 1); + } + var b = p.AttachBlip(); + b.Scale = 0.75f; + b.Color = Color.Red; + + suspects.Add(p, b); + if (suspects.ElementAt(0).Value && suspects.ElementAt(0).Value.IsValid()) suspects.ElementAt(0).Value.EnableRoute(Color.Red); + } + } + go = true; + } + catch (Exception e) + { + Logger.ErrorCode("Found Dangerous Person", 50); + Logger.Error(e.ToString()); + this.End(); + } + + return base.OnCalloutAccepted(); + } + + public override void Process() + { + GameFiber.StartNew(() => + { + try + { + GameFiber.Yield(); + if (go && !PursuitBegan && Game.LocalPlayer.Character.Position.DistanceTo(suspects.ElementAt(0).Key) < 30f) + { + suspects.ElementAt(0).Value.EnableRoute(Color.Red); + switch (sno) + { + default: + case 0: + pursuit = Functions.CreatePursuit(); + if (pursuit is not null) + { + foreach (var p in suspects.Keys) if (p && p.IsValid()) Functions.AddPedToPursuit(pursuit, p); + foreach (var b in suspects.Values) if (b && b.IsValid()) b.Delete(); + Functions.SetPursuitIsActiveForPlayer(pursuit, true); + } + PursuitBegan = true; + break; + case 1: + new RelationshipGroup("SUSPECTS"); + new RelationshipGroup("COP"); + Game.SetRelationshipBetweenRelationshipGroups("SUSPECTS", "COP", Relationship.Hate); + Game.LocalPlayer.Character.RelationshipGroup = "COP"; + foreach (var p in suspects.Keys) + { + p.RelationshipGroup = "SUSPECTS"; + p.Tasks.FightAgainstClosestHatedTarget(5000f); + } + PursuitBegan = true; + break; + } + } + } + catch (Exception e) + { + Logger.ErrorCode("Found Dangerous Person", 60); + Logger.Error(e.ToString()); + this.End(); + } + }); + if (Game.LocalPlayer.Character.IsDead || PedExtensions.EveryoneAreDeadOrArrested(suspects.Keys.ToList())) End(); + base.Process(); + } + + public override void End() + { + Functions.PlayScannerAudio("ATTENTION_ALL_UNITS DL_CODE4 DL_NO_FURTHER_UNITS_REQUIRED"); + MessageExtensions.SendDispatchMessage(Translation.GetString("Code4"), Translation.GetString(CalloutList.AllCallouts[typeof(FoundDangerousPerson)])); + foreach (var p in suspects.Keys) if (p && p.IsValid()) p.Dismiss(); + foreach (var b in suspects.Values) if (b && b.IsValid()) b.Delete(); + if (vehicle && vehicle.IsValid()) vehicle.Dismiss(); + if (pursuit is not null) Functions.ForceEndPursuit(pursuit); + base.End(); + } + + private void CheckAndDeleteBlip(bool CheckPed = false) + { + switch (CheckPed) + { + case true: + foreach (var b in suspects) if (b.Value && b.Value.IsValid() && b.Key.IsDeadOrArrested()) b.Value.Delete(); + break; + case false: + foreach (var b in suspects) if (b.Value && b.Value.IsValid()) b.Value.Delete(); + break; + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Callouts/FoundDangerousPerson/SuspectData.cs b/DynamicLSPDFR/Callouts/FoundDangerousPerson/SuspectData.cs new file mode 100644 index 0000000..d18bdb6 --- /dev/null +++ b/DynamicLSPDFR/Callouts/FoundDangerousPerson/SuspectData.cs @@ -0,0 +1,23 @@ +namespace DynamicLSPDFR.Callouts; + +internal static class FoundDangerousPersonData +{ + internal enum Team : int + { + Michael, + Franklin, + Trevor, + Lester, + Everyone, + None, + } + + internal static Dictionary)> Data = new() + { + { Team.Michael, ("TAILGATER", new() { "player_zero" } ) }, + { Team.Franklin, ("BUFFALO", new() { "player_one" } ) }, + { Team.Trevor, ("BODHI2", new() { "player_two", "ig_nervousron", "ig_wade" } ) }, + { Team.Lester, ("TAILGATER", new() { "ig_lestercrest" } ) }, + { Team.Everyone, ("TAILGATER", new() { "player_zero", "ig_lestercrest", "player_two", "player_one" } ) }, + }; +} \ No newline at end of file diff --git a/DynamicLSPDFR/Callouts/GangShootout/GangShootout.cs b/DynamicLSPDFR/Callouts/GangShootout/GangShootout.cs index 1d80e62..2acc827 100644 --- a/DynamicLSPDFR/Callouts/GangShootout/GangShootout.cs +++ b/DynamicLSPDFR/Callouts/GangShootout/GangShootout.cs @@ -191,8 +191,79 @@ public override void Process() new RelationshipGroup("GANG_TEAM2"); Game.SetRelationshipBetweenRelationshipGroups("GANG_TEAM1", "GANG_TEAM2", Relationship.Hate); Game.SetRelationshipBetweenRelationshipGroups("GANG_TEAM2", "GANG_TEAM1", Relationship.Hate); - PedExtensions.SetTeamAndFight("GANG_TEAM1", GangTeam1A, GangTeam1B, GangTeam1C, GangTeam1D, GangTeam1E, GangTeam1F, GangTeam1G); - PedExtensions.SetTeamAndFight("GANG_TEAM2", GangTeam2A, GangTeam2B, GangTeam2C, GangTeam2D, GangTeam2E, GangTeam2F, GangTeam2G); + + if (GangTeam1A && GangTeam1A.IsValid()) + { + GangTeam1A.RelationshipGroup = "GANG_TEAM1"; + GangTeam1A.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1B && GangTeam1B.IsValid()) + { + GangTeam1B.RelationshipGroup = "GANG_TEAM1"; + GangTeam1B.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1C && GangTeam1C.IsValid()) + { + GangTeam1C.RelationshipGroup = "GANG_TEAM1"; + GangTeam1C.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1D && GangTeam1D.IsValid()) + { + GangTeam1D.RelationshipGroup = "GANG_TEAM1"; + GangTeam1D.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1E && GangTeam1E.IsValid()) + { + GangTeam1E.RelationshipGroup = "GANG_TEAM1"; + GangTeam1E.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1F && GangTeam1F.IsValid()) + { + GangTeam1F.RelationshipGroup = "GANG_TEAM1"; + GangTeam1F.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1G && GangTeam1G.IsValid()) + { + GangTeam1G.RelationshipGroup = "GANG_TEAM1"; + GangTeam1G.Tasks.FightAgainstClosestHatedTarget(5000f); + } + + if (GangTeam2A && GangTeam2A.IsValid()) + { + GangTeam2A.RelationshipGroup = "GANG_TEAM2"; + GangTeam2A.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam2B && GangTeam2B.IsValid()) + { + GangTeam2B.RelationshipGroup = "GANG_TEAM2"; + GangTeam2B.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam2C && GangTeam2C.IsValid()) + { + GangTeam2C.RelationshipGroup = "GANG_TEAM2"; + GangTeam2C.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1D && GangTeam2D.IsValid()) + { + GangTeam2D.RelationshipGroup = "GANG_TEAM2"; + GangTeam2D.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam1E && GangTeam2E.IsValid()) + { + GangTeam2E.RelationshipGroup = "GANG_TEAM2"; + GangTeam2E.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam2F && GangTeam2F.IsValid()) + { + GangTeam2F.RelationshipGroup = "GANG_TEAM2"; + GangTeam2F.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (GangTeam2G && GangTeam2G.IsValid()) + { + GangTeam2G.RelationshipGroup = "GANG_TEAM2"; + GangTeam2G.Tasks.FightAgainstClosestHatedTarget(5000f); + } + HasBeginFighting = true; GameFiber.Sleep(5000); diff --git a/DynamicLSPDFR/Callouts/HitAndRun/HitAndRun.cs b/DynamicLSPDFR/Callouts/HitAndRun/HitAndRun.cs new file mode 100644 index 0000000..e0ea7e4 --- /dev/null +++ b/DynamicLSPDFR/Callouts/HitAndRun/HitAndRun.cs @@ -0,0 +1,311 @@ +namespace DynamicLSPDFR.Callouts; + +[CalloutInfo("HitAndRun", CalloutProbability.Medium)] +internal class HitAndRun : CalloutBase +{ + private enum Status : int + { + Accepted, + Arrived, + Searching, + Pursuit, + } + + private Ped victim, caller, suspect; + private Vehicle suspectV; + private Blip victimAreaB, victimB, callerB, suspectB; + private LHandle pursuit; + private Vector3 SpawnPoint = World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(RandomNumber.Instance.Next(500, 750))); + private Status status = Status.Accepted; + private bool blipAttached = false; + + private List<(string, string)> conversations = new() + { + (Translation.GetString("Officer"), Translation.GetString("HitAndRunTalk1")), + (Translation.GetString("Caller"), Translation.GetString("HitAndRunTalk2")), + (Translation.GetString("Officer"), Translation.GetString("HitAndRunTalk3")), + (Translation.GetString("Caller"), Translation.GetString("HitAndRunTalk4")), + (Translation.GetString("Officer"), Translation.GetString("HitAndRunTalk5")), + }; + + public override bool OnBeforeCalloutDisplayed() + { + CalloutMessage = Translation.GetString(CalloutList.AllCallouts[typeof(HitAndRun)]); + CalloutPosition = SpawnPoint; + ShowCalloutAreaBlipBeforeAccepting(SpawnPoint, 30f); + Functions.PlayScannerAudioUsingPosition("WE_HAVE DL_CRIME_HIT_AND_RUN IN_OR_ON_POSITION UNITS_RESPOND_CODE_03", SpawnPoint); + return base.OnBeforeCalloutDisplayed(); + } + + public override bool OnCalloutAccepted() + { + Logger.Info("[DynamicLSPDFR] Callout \"Hit And Run\" was started."); + + MessageExtensions.SendDispatchMessage(Translation.GetString("HitAndRunDesc"), Translation.GetString(CalloutList.AllCallouts[typeof(HitAndRun)])); + + try + { + victim = new(SpawnPoint); + caller = new(SpawnPoint.Around2D(2f)); + if (victim && victim.IsValid()) + { + victim.IsPersistent = true; + victim.BlockPermanentEvents = true; + victim.Kill(); + } + if (caller && caller.IsValid()) + { + caller.IsPersistent = true; + caller.BlockPermanentEvents = true; + caller.Face(victim.Position); + } + } + catch (Exception e) + { + Logger.ErrorCode("Hit And Run", 51); + Logger.Error(e.ToString()); + this.End(); + } + + try + { + suspectV = new(x => x.IsCar, World.GetNextPositionOnStreet(SpawnPoint.Around(RandomNumber.Instance.Next(500, 800)))); + if (suspectV && suspectV.IsValid()) + { + suspectV.IsPersistent = true; + NativeExtensions.Drive(suspectV, 20f); + + suspect = suspectV.CreateRandomDriver(); + if (suspect && suspect.IsValid()) + { + suspect.IsPersistent = true; + suspect.BlockPermanentEvents = true; + suspect.KeepTasks = true; + suspect.Tasks.CruiseWithVehicle(30f, VehicleDrivingFlags.FollowTraffic | VehicleDrivingFlags.YieldToCrossingPedestrians | VehicleDrivingFlags.RespectIntersections); + } + } + } + catch (Exception e) + { + Logger.ErrorCode("Hit And Run", 52); + Logger.Error(e.ToString()); + this.End(); + } + + try + { + victimAreaB = new(SpawnPoint.Around(RandomNumber.Instance.Next(2, 10)), 80f); + if (victimAreaB && victimAreaB.IsValid()) + { + victimAreaB.Alpha = 0.5f; + victimAreaB.Color = Color.Yellow; + victimAreaB.Name = Translation.GetString("SearchArea"); + victimAreaB.EnableRoute(Color.Yellow); + } + } + catch (Exception e) + { + Logger.ErrorCode("Hit And Run", 53); + Logger.Error(e.ToString()); + this.End(); + } + + GameFiber.StartNew(this.Running, "[DynamicLSPDFR] Hit And Run"); + + GameFiber.StartNew(() => + { + while (true) + { + GameFiber.Yield(); + if (status is Status.Searching) + { + if (suspectB && suspectB.IsValid() && suspect && suspect.IsValid()) + { + MessageExtensions.SendDispatchMessage(string.Format(Translation.GetString("SuspectLocation"), Functions.GetZoneAtPosition(suspect.Position).RealAreaName, Translation.GetString(HeadingExtensions.GetPedDirection(suspect).ToString())), ""); + suspectB.Position = suspect.Position.Around(0f, 50f); + Functions.PlayScannerAudioUsingPosition("SUSPECT_LAST_SEEN IN_OR_ON_POSITION", suspect.Position); + } + GameFiber.Wait(20000); + } + } + }, "[DynamicLSPDFR] Hit And Run Area Update"); + + return base.OnCalloutAccepted(); + } + + private void Running() + { + while (status is Status.Accepted) + { + GameFiber.Yield(); + if (Game.LocalPlayer.Character.Position.DistanceTo(caller) < 15f) + { + Game.DisplayHelp(Translation.GetString("TalkToCaller"), true); + status = Status.Arrived; + } + } + + while (status is Status.Arrived) + { + try + { + if (victimAreaB && victimAreaB.IsValid()) victimAreaB.Delete(); + + if (!blipAttached) + { + victimB = victim.AttachBlip(); + if (victimB && victimB.IsValid()) + { + victimB.Color = Color.Red; + victimB.Scale = 0.75f; + victimB.Alpha = 1f; + victimB.Name = Translation.GetString("Victim"); + } + + callerB = caller.AttachBlip(); + if (callerB && callerB.IsValid()) + { + callerB.Color = Color.Green; + callerB.Scale = 0.75f; + callerB.Name = Translation.GetString("Caller"); + } + blipAttached = true; + } + } + catch (Exception e) + { + Logger.ErrorCode("Hit And Run", 53); + Logger.Error(e.ToString()); + this.End(); + } + + try + { + GameFiber.Yield(); + if (Vector3.Distance(Game.LocalPlayer.Character.Position, caller.Position) < 5f) + { + Game.DisplayHelp($"Press ~{Keys.Y.GetInstructionalId()}~ to talk."); + if (KeyExtensions.IsKeysDown(Keys.Y)) + { + if (caller && caller.IsValid()) caller.Tasks.ClearImmediately(); + break; + } + } + else Game.HideHelp(); + } + catch (Exception e) + { + Logger.ErrorCode("Hit And Run", 60); + Logger.Error(e.ToString()); + this.End(); + } + } + DialogExtensions.Dialog(conversations, caller); + + if (victim && victim.IsValid()) victim.Dismiss(); + if (caller && caller.IsValid()) caller.Dismiss(); + if (callerB && callerB.IsValid()) callerB.Delete(); + if (suspectB && suspectB.IsValid()) suspectB.Delete(); + MessageExtensions.SendDispatchMessage(Translation.GetString("SuspectReport"), ""); + GameFiber.Wait(5000); + MessageExtensions.SendDispatchMessage(Translation.GetString("SuspectSearching"), ""); + GameFiber.Wait(12000); + Functions.PlayScannerAudioUsingPosition("SUSPECT_LAST_SEEN IN_OR_ON_POSITION", suspect.Position); + MessageExtensions.SendDispatchMessage(string.Format(Translation.GetString("SuspectLocation"), Functions.GetZoneAtPosition(suspect.Position).RealAreaName, Translation.GetString(HeadingExtensions.GetPedDirection(suspect).ToString())), ""); + suspectB = new(suspect.Position.Around(0f, 100f), 150f); + if (suspectB && suspectB) + { + suspectB.Alpha = 0.5f; + suspectB.Color = Color.Red; + suspectB.Name = Translation.GetString("SuspectArea"); + suspectB.EnableRoute(Color.Red); + } + status = Status.Searching; + } + + public override void OnCalloutNotAccepted() + { + if (victim && victim.IsValid()) victim.Delete(); + if (caller && caller.IsValid()) caller.Delete(); + if (suspect && suspect.IsValid()) suspect.Delete(); + if (suspectV && suspectV.IsValid()) suspectV.Delete(); + if (victimB && victimB.IsValid()) victimB.Delete(); + if (victimAreaB && victimAreaB.IsValid()) victimAreaB.Delete(); + if (callerB && callerB.IsValid()) callerB.Delete(); + if (suspectB && suspectB.IsValid()) suspectB.Delete(); + base.OnCalloutNotAccepted(); + } + + public override void Process() + { + GameFiber.StartNew(() => + { + if (status is Status.Searching) + { + if (suspect.DistanceTo(Game.LocalPlayer.Character.Position) < 50f) + { + if (suspectB && suspectB.IsValid()) + { + suspectB.Delete(); + suspectB = suspect.AttachBlip(); + suspectB.IsFriendly = false; + } + + if (suspect.DistanceTo(Game.LocalPlayer.Character.Position) < 15f) + { + if (suspectB && suspectB.IsValid()) suspectB.Delete(); + pursuit = Functions.CreatePursuit(); + if (suspect && suspect.IsValid()) + { + Functions.AddPedToPursuit(pursuit, suspect); + Functions.SetPursuitIsActiveForPlayer(pursuit, true); + } + status = Status.Pursuit; + } + } + } + if (CompleteEndCondition()) End(); + CheckAndDeleteBlip(true); + }, "[DynamicLSPDFR] Hit And Run"); + base.Process(); + } + + public override void End() + { + Functions.PlayScannerAudio("ATTENTION_ALL_UNITS DL_CODE4 DL_NO_FURTHER_UNITS_REQUIRED"); + MessageExtensions.SendDispatchMessage(Translation.GetString("Code4"), Translation.GetString(CalloutList.AllCallouts[typeof(HitAndRun)])); + if (victim && victim.IsValid()) victim.Dismiss(); + if (caller && caller.IsValid()) caller.Dismiss(); + if (suspect && suspect.IsValid()) suspect.Dismiss(); + if (suspectV && suspectV.IsValid()) suspectV.Dismiss(); + if (victimB && victimB.IsValid()) victimB.Delete(); + if (victimAreaB && victimAreaB.IsValid()) victimAreaB.Delete(); + if (callerB && callerB.IsValid()) callerB.Delete(); + if (suspectB && suspectB.IsValid()) suspectB.Delete(); + + base.End(); + } + + private bool CompleteEndCondition() + { + if (Game.LocalPlayer.Character.IsDead || PedExtensions.EveryoneAreDeadOrArrested(suspect)) return true; + return false; + } + + private void CheckAndDeleteBlip(bool CheckPed = false) + { + switch (CheckPed) + { + case true: + if (victimB && victimB.IsValid() && (victim.IsDeadOrArrested())) victimB.Delete(); + if (callerB && callerB.IsValid() && (caller.IsDeadOrArrested())) callerB.Delete(); + if (suspectB && suspectB.IsValid() && (suspect.IsDeadOrArrested())) suspectB.Delete(); + break; + case false: + if (victimB && victimB.IsValid()) victimB.Delete(); + if (callerB && callerB.IsValid()) callerB.Delete(); + if (suspectB && suspectB.IsValid()) suspectB.Delete(); + break; + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Callouts/Not Completed/Murder.cs b/DynamicLSPDFR/Callouts/Not Completed/Murder.cs index a797dec..65917e8 100644 --- a/DynamicLSPDFR/Callouts/Not Completed/Murder.cs +++ b/DynamicLSPDFR/Callouts/Not Completed/Murder.cs @@ -9,7 +9,7 @@ // private Ped murder, victim, cop1, cop2, cop3, cop4, cop5, cop6, cop7, cop8; // private Vehicle copV1, copV2, copV3, copV4; -// private Rage.Object clipBoard; +// private Object clipBoard; // private enum Location // { diff --git a/DynamicLSPDFR/Callouts/PanicButton/PanicButton.cs b/DynamicLSPDFR/Callouts/Not Completed/PanicButton/PanicButton.cs similarity index 100% rename from DynamicLSPDFR/Callouts/PanicButton/PanicButton.cs rename to DynamicLSPDFR/Callouts/Not Completed/PanicButton/PanicButton.cs diff --git a/DynamicLSPDFR/Callouts/TrafficStopBackup/Locations.cs b/DynamicLSPDFR/Callouts/Not Completed/TrafficStopBackup/Locations.cs similarity index 100% rename from DynamicLSPDFR/Callouts/TrafficStopBackup/Locations.cs rename to DynamicLSPDFR/Callouts/Not Completed/TrafficStopBackup/Locations.cs diff --git a/DynamicLSPDFR/Callouts/TrafficStopBackup/TrafficStopBackup.cs b/DynamicLSPDFR/Callouts/Not Completed/TrafficStopBackup/TrafficStopBackup.cs similarity index 100% rename from DynamicLSPDFR/Callouts/TrafficStopBackup/TrafficStopBackup.cs rename to DynamicLSPDFR/Callouts/Not Completed/TrafficStopBackup/TrafficStopBackup.cs diff --git a/DynamicLSPDFR/Callouts/Pursuit/StolenMoneyTruck.cs b/DynamicLSPDFR/Callouts/Pursuit/StolenMoneyTruck.cs index d3d8c4a..4f687cf 100644 --- a/DynamicLSPDFR/Callouts/Pursuit/StolenMoneyTruck.cs +++ b/DynamicLSPDFR/Callouts/Pursuit/StolenMoneyTruck.cs @@ -14,7 +14,7 @@ public override bool OnBeforeCalloutDisplayed() CalloutMessage = Translation.GetString(TranslateKey); CalloutPosition = SpawnPoint; ShowCalloutAreaBlipBeforeAccepting(SpawnPoint, 50f); - Functions.PlayScannerAudioUsingPosition("ATTENTION_ALL_UNITS WE_HAVE DL_CRIME_STOLEN_VEHICLE_01 IN_OR_ON_POSITION IN_OR_ON_POSITION UNITS_RESPOND_CODE_99", SpawnPoint); + Functions.PlayScannerAudioUsingPosition("ATTENTION_ALL_UNITS WE_HAVE DL_CRIME_STOLEN_VEHICLE_01 IN_OR_ON_POSITION UNITS_RESPOND_CODE_99", SpawnPoint); return base.OnBeforeCalloutDisplayed(); } diff --git a/DynamicLSPDFR/Callouts/Robbery/Bank/BankRobbery.cs b/DynamicLSPDFR/Callouts/Robbery/Bank/BankRobbery.cs new file mode 100644 index 0000000..5af8a10 --- /dev/null +++ b/DynamicLSPDFR/Callouts/Robbery/Bank/BankRobbery.cs @@ -0,0 +1,282 @@ +namespace DynamicLSPDFR.Callouts; + +[CalloutInfo("BankRobbery", CalloutProbability.Low)] +internal class BankRobbery : CalloutBase +{ + private Ped Robber1, Robber2, Robber3, Robber4, Robber5; + private Blip Bank, Robber1B, Robber2B, Robber3B, Robber4B, Robber5B; + private Vehicle robV; + private static BankRobberyLocations.Locations loc = LocationExtensions.GetNearestPosition(BankRobberyLocations.SpawnPoints); + private Vector3 SpawnPoint = BankRobberyLocations.SpawnPoints[loc]; + private bool Arrived = false; + private WeaponHash[] Weapons = new[] { WeaponHash.CarbineRifle, WeaponHash.AssaultRifle, WeaponHash.AdvancedRifle, WeaponHash.AssaultSMG, WeaponHash.Smg }; + private string[] RobberVehicles = new[] { "baller", "baller2", "boxville2", "burrito3", "burrito4", "burrito5", "pony", "pony2", "rancherxl", "rumpo", "rumpo2", "rocoto", "seminole", "speedo", "minivan" }; + + public override bool OnBeforeCalloutDisplayed() + { + CalloutMessage = Translation.GetString(CalloutList.AllCallouts[typeof(BankRobbery)]); + CalloutPosition = SpawnPoint; + ShowCalloutAreaBlipBeforeAccepting(SpawnPoint, 50f); + Functions.PlayScannerAudioUsingPosition("WE_HAVE DL_CRIME_BANK_ROBBERY IN_OR_ON_POSITION UNITS_RESPOND_CODE_99", SpawnPoint); + return base.OnBeforeCalloutDisplayed(); + } + + public override bool OnCalloutAccepted() + { + Logger.Info("[DynamicLSPDFR] Callout \"Bank Robbery\" was started."); + + MessageExtensions.SendDispatchMessage(Translation.GetString("BankRobberyDesc"), Translation.GetString(CalloutList.AllCallouts[typeof(BankRobbery)])); + + try + { + Robber1 = new("g_m_m_chicold_01", BankRobberyLocations.Robbery1[loc], 0f); + Robber2 = new("g_m_m_chicold_01", BankRobberyLocations.Robbery2[loc], 0f); + Robber3 = new("g_m_m_chicold_01", BankRobberyLocations.Robbery3[loc], 0f); + Robber4 = new("g_m_m_chicold_01", BankRobberyLocations.Robbery4[loc], 0f); + Robber5 = new("g_m_m_chicold_01", BankRobberyLocations.Robbery5[loc], 0f); + if (Robber1 && Robber1.IsValid()) + { + Robber1.IsPersistent = true; + Robber1.BlockPermanentEvents = true; + Robber1.MaxHealth = 500; + Robber1.Health = 500; + Robber1.Armor = 500; + Robber1.SetPedSuffersCriticalHits(false); + Robber1.Inventory.GiveNewWeapon(Weapons[RandomNumber.Instance.Next(Weapons.Length)], 5000, true); + Robber1B = Robber1.AttachBlip(); + if (Robber1B && Robber1B.IsValid()) + { + Robber1B.IsFriendly = false; + Robber1B.Scale = 0.75f; + Robber1B.Name = Translation.GetString("Robber"); + } + } + if (Robber2 && Robber2.IsValid()) + { + Robber2.IsPersistent = true; + Robber2.BlockPermanentEvents = true; + Robber2.MaxHealth = 500; + Robber2.Health = 500; + Robber2.Armor = 500; + Robber2.SetPedSuffersCriticalHits(false); + Robber2.Inventory.GiveNewWeapon(Weapons[RandomNumber.Instance.Next(Weapons.Length)], 5000, true); + Robber2B = Robber2.AttachBlip(); + if (Robber2B && Robber2B.IsValid()) + { + Robber2B.IsFriendly = false; + Robber2B.Scale = 0.75f; + Robber2B.Name = Translation.GetString("Robber"); + } + } + if (Robber3 && Robber3.IsValid()) + { + Robber3.IsPersistent = true; + Robber3.BlockPermanentEvents = true; + Robber3.MaxHealth = 500; + Robber3.Health = 500; + Robber3.Armor = 700; + Robber3.SetPedSuffersCriticalHits(false); + Robber3.Inventory.GiveNewWeapon(Weapons[RandomNumber.Instance.Next(Weapons.Length)], 5000, true); + Robber3B = Robber3.AttachBlip(); + if (Robber3B && Robber3B.IsValid()) + { + Robber3B.IsFriendly = false; + Robber3B.Scale = 0.75f; + Robber3B.Name = Translation.GetString("Robber"); + } + } + if (Robber4 && Robber4.IsValid()) + { + Robber4.IsPersistent = true; + Robber4.BlockPermanentEvents = true; + Robber4.MaxHealth = 500; + Robber4.Health = 500; + Robber4.Armor = 800; + Robber4.SetPedSuffersCriticalHits(false); + Robber4.Inventory.GiveNewWeapon(Weapons[RandomNumber.Instance.Next(Weapons.Length)], 5000, true); + Robber4B = Robber4.AttachBlip(); + if (Robber4B && Robber4B.IsValid()) + { + Robber4B.IsFriendly = false; + Robber4B.Scale = 0.75f; + Robber4B.Name = Translation.GetString("Robber"); + } + } + if (Robber5 && Robber5.IsValid()) + { + Robber5.IsPersistent = true; + Robber5.BlockPermanentEvents = true; + Robber5.MaxHealth = 500; + Robber5.Health = 500; + Robber5.Armor = 1000; + Robber5.SetPedSuffersCriticalHits(false); + Robber5.Inventory.GiveNewWeapon(Weapons[RandomNumber.Instance.Next(Weapons.Length)], 5000, true); + Robber5B = Robber5.AttachBlip(); + if (Robber5B && Robber5B.IsValid()) + { + Robber5B.IsFriendly = false; + Robber5B.Scale = 0.75f; + Robber5B.Name = Translation.GetString("Robber"); + } + } + } + catch (Exception e) + { + Logger.ErrorCode("Bank Robbery", 51); + Logger.Error(e.ToString()); + this.End(); + } + + try + { + robV = new(RobberVehicles[RandomNumber.Instance.Next(RobberVehicles.Length)], BankRobberyLocations.RobberVehicle[loc].Item1, BankRobberyLocations.RobberVehicle[loc].Item2); + if (robV && robV.IsValid()) + { + robV.IsSirenOn = true; + robV.IsSirenSilent = true; + robV.IsPersistent = true; + if (RandomNumber.Instance.Next(2) is 0) robV.IsStolen = true; + if (Main.Language is Languages.Japanese) robV.LicensePlate = "114514"; + } + } + catch (Exception e) + { + Logger.ErrorCode("Bank Robbery", 52); + Logger.Error(e.ToString()); + this.End(); + } + + try + { + Bank = new(SpawnPoint, 90f); + if (Bank && Bank.IsValid()) + { + Bank.Alpha = 0.5f; + Bank.Color = Color.Red; + Bank.Name = Translation.GetString("RobbedBank"); + Bank.EnableRoute(Color.Red); + } + } + catch (Exception e) + { + Logger.ErrorCode("Bank Robbery", 53); + Logger.Error(e.ToString()); + this.End(); + } + + return base.OnCalloutAccepted(); + } + + public override void OnCalloutNotAccepted() + { + if (Robber1 && Robber1.IsValid()) Robber1.Delete(); + if (Robber2 && Robber2.IsValid()) Robber2.Delete(); + if (Robber3 && Robber3.IsValid()) Robber3.Delete(); + if (Robber4 && Robber4.IsValid()) Robber4.Delete(); + if (Robber5 && Robber5.IsValid()) Robber5.Delete(); + if (Bank && Bank.IsValid()) Bank.Delete(); + if (Robber1B && Robber1B.IsValid()) Robber1B.Delete(); + if (Robber2B && Robber2B.IsValid()) Robber2B.Delete(); + if (Robber3B && Robber3B.IsValid()) Robber3B.Delete(); + if (Robber4B && Robber4B.IsValid()) Robber4B.Delete(); + if (Robber5B && Robber5B.IsValid()) Robber5B.Delete(); + base.OnCalloutNotAccepted(); + } + + public override void Process() + { + GameFiber.StartNew(() => + { + if (robV.DistanceTo(Game.LocalPlayer.Character.Position) < 30f && !Arrived) + { + if (Bank && Bank.IsValid()) + { + Bank.DisableRoute(); + Bank.Delete(); + } + + GameFiber.Wait(3000); + new RelationshipGroup("ROBBERY"); + new RelationshipGroup("COP"); + Game.SetRelationshipBetweenRelationshipGroups("ROBBERY", "COP", Relationship.Hate); + Game.LocalPlayer.Character.RelationshipGroup = "COP"; + if (Robber1 && Robber1.IsValid()) + { + Robber1.RelationshipGroup = "ROBBERY"; + Robber1.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (Robber2 && Robber2.IsValid()) + { + Robber2.RelationshipGroup = "ROBBERY"; + Robber2.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (Robber3 && Robber3.IsValid()) + { + Robber3.RelationshipGroup = "ROBBERY"; + Robber3.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (Robber4 && Robber4.IsValid()) + { + Robber4.RelationshipGroup = "ROBBERY"; + Robber4.Tasks.FightAgainstClosestHatedTarget(5000f); + } + if (Robber5 && Robber5.IsValid()) + { + Robber5.RelationshipGroup = "ROBBERY"; + Robber5.Tasks.FightAgainstClosestHatedTarget(5000f); + } + Arrived = true; + } + if (CompleteEndCondition()) End(); + CheckAndDeleteBlip(true); + + }, "[DynamicLSPDFR] Bank Robbery"); + base.Process(); + } + + public override void End() + { + Functions.PlayScannerAudio("ATTENTION_ALL_UNITS DL_CODE4 DL_NO_FURTHER_UNITS_REQUIRED"); + MessageExtensions.SendDispatchMessage(Translation.GetString("Code4"), Translation.GetString(CalloutList.AllCallouts[typeof(BankRobbery)])); + if (Robber1 && Robber1.IsValid()) Robber1.Dismiss(); + if (Robber2 && Robber2.IsValid()) Robber2.Dismiss(); + if (Robber3 && Robber3.IsValid()) Robber3.Dismiss(); + if (Robber4 && Robber4.IsValid()) Robber4.Dismiss(); + if (Robber5 && Robber5.IsValid()) Robber5.Dismiss(); + if (Bank && Bank.IsValid()) Bank.Delete(); + if (Robber1B && Robber1B.IsValid()) Robber1B.Delete(); + if (Robber2B && Robber2B.IsValid()) Robber2B.Delete(); + if (Robber3B && Robber3B.IsValid()) Robber3B.Delete(); + if (Robber4B && Robber4B.IsValid()) Robber4B.Delete(); + if (Robber5B && Robber5B.IsValid()) Robber5B.Delete(); + + base.End(); + } + + private bool CompleteEndCondition() + { + if (Game.LocalPlayer.Character.IsDead || PedExtensions.EveryoneAreDeadOrArrested(Robber1, Robber2, Robber3, Robber4, Robber5)) return true; + return false; + } + + private void CheckAndDeleteBlip(bool CheckPed = false) + { + switch (CheckPed) + { + case true: + if (Robber1B && Robber1B.IsValid() && (Robber1.IsDeadOrArrested())) Robber1B.Delete(); + if (Robber2B && Robber2B.IsValid() && (Robber2.IsDeadOrArrested())) Robber2B.Delete(); + if (Robber3B && Robber3B.IsValid() && (Robber3.IsDeadOrArrested())) Robber3B.Delete(); + if (Robber4B && Robber4B.IsValid() && (Robber4.IsDeadOrArrested())) Robber4B.Delete(); + if (Robber5B && Robber5B.IsValid() && (Robber5.IsDeadOrArrested())) Robber5B.Delete(); + break; + case false: + if (Robber1B && Robber1B.IsValid()) Robber1B.Delete(); + if (Robber2B && Robber2B.IsValid()) Robber2B.Delete(); + if (Robber3B && Robber3B.IsValid()) Robber3B.Delete(); + if (Robber4B && Robber4B.IsValid()) Robber4B.Delete(); + if (Robber5B && Robber5B.IsValid()) Robber5B.Delete(); + break; + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Callouts/Robbery/Bank/Locations.cs b/DynamicLSPDFR/Callouts/Robbery/Bank/Locations.cs new file mode 100644 index 0000000..5c5a0f5 --- /dev/null +++ b/DynamicLSPDFR/Callouts/Robbery/Bank/Locations.cs @@ -0,0 +1,60 @@ +namespace DynamicLSPDFR.Callouts; + +internal static class BankRobberyLocations +{ + internal enum Locations : int + { + PillboxHill, + Burton, + Paleto, + } + + internal static Dictionary SpawnPoints = new() + { + { Locations.PillboxHill, new(146.9374f, -1044.931f, 30f) }, + { Locations.Burton, new(-353.9496f, -53.21059f, 50f) }, + { Locations.Paleto, new(-113.0431f, 6469.044f, 30f) }, + }; + + internal static Dictionary Robbery1 = new() + { + { Locations.PillboxHill, new(147.2159f, -1045.003f, 39.369f) }, + { Locations.Burton, new(-353.9496f, -53.21059f, 49.037f) }, + { Locations.Paleto, new(-113.0431f, 6469.044f, 31.627f) }, + }; + + internal static Dictionary Robbery2 = new() + { + { Locations.PillboxHill, new(145.715f, -1044.981f, 29.378f) }, + { Locations.Burton, new(-355.0357f, -54.30582f, 49.047f) }, + { Locations.Paleto, new(-101.5976f, 6463.146f, 31.627f) }, + }; + + internal static Dictionary Robbery3 = new() + { + { Locations.PillboxHill, new(144.3407f, -1043.922f, 29.368f) }, + { Locations.Burton, new(-356.4601f, -53.16462f, 49.037f) }, + { Locations.Paleto, new(-104.261f, 6471.767f, 31.627f) }, + }; + + internal static Dictionary Robbery4 = new() + { + { Locations.PillboxHill, new(142.5871f, -1043.835f, 29.368f) }, + { Locations.Burton, new(-358.6454f, -53.25273f, 49.037f) }, + { Locations.Paleto, new(-107.161f, 6474.427f, 31.627f) }, + }; + + internal static Dictionary Robbery5 = new() + { + { Locations.PillboxHill, new(144.9723f, -1036.951f, 29.369f) }, + { Locations.Burton, new(-355.4163f, -47.41251f, 49.037f) }, + { Locations.Paleto, new(-103.4466f, 6477.631f, 31.627f) }, + }; + + internal static Dictionary RobberVehicle = new() + { + { Locations.PillboxHill, (new(147.9261f, -1028.209f, 28.977f), -109.0291f) }, + { Locations.Burton, (new(-341.7926f, -30.77651f, 47.282f), -109.2123f) }, + { Locations.Paleto, (new(-115.7394f, 6457.531f, 31.216f), 44.26528f) }, + }; +} \ No newline at end of file diff --git a/DynamicLSPDFR/Callouts/Robbery/Locations.cs b/DynamicLSPDFR/Callouts/Robbery/Store/Locations.cs similarity index 100% rename from DynamicLSPDFR/Callouts/Robbery/Locations.cs rename to DynamicLSPDFR/Callouts/Robbery/Store/Locations.cs diff --git a/DynamicLSPDFR/Callouts/Robbery/StoreRobbery.cs b/DynamicLSPDFR/Callouts/Robbery/Store/StoreRobbery.cs similarity index 98% rename from DynamicLSPDFR/Callouts/Robbery/StoreRobbery.cs rename to DynamicLSPDFR/Callouts/Robbery/Store/StoreRobbery.cs index c447d26..b1ed35d 100644 --- a/DynamicLSPDFR/Callouts/Robbery/StoreRobbery.cs +++ b/DynamicLSPDFR/Callouts/Robbery/Store/StoreRobbery.cs @@ -100,7 +100,7 @@ public override void End() { Functions.PlayScannerAudio("ATTENTION_ALL_UNITS DL_CODE4 DL_NO_FURTHER_UNITS_REQUIRED"); MessageExtensions.SendDispatchMessage(Translation.GetString("Code4"), Translation.GetString(TranslateKey)); - if (suspect) suspect.Detach(); + if (suspect) suspect.Dismiss(); if (blip) blip.Delete(); base.End(); } diff --git a/DynamicLSPDFR/Callouts/StreetFight/StreetFight.cs b/DynamicLSPDFR/Callouts/StreetFight/StreetFight.cs index 8077f5c..8ac7cd1 100644 --- a/DynamicLSPDFR/Callouts/StreetFight/StreetFight.cs +++ b/DynamicLSPDFR/Callouts/StreetFight/StreetFight.cs @@ -83,8 +83,16 @@ public override void Process() new RelationshipGroup("CRIMINAL2"); Game.SetRelationshipBetweenRelationshipGroups("CRIMINAL1", "CRIMINAL2", Relationship.Hate); Game.SetRelationshipBetweenRelationshipGroups("CRIMINAL2", "CRIMINAL1", Relationship.Hate); - PedExtensions.SetTeamAndFight("CRIMINAL1", suspect1); - PedExtensions.SetTeamAndFight("CRIMINAL2", suspect2); + if (suspect1 && suspect1.IsValid()) + { + suspect1.RelationshipGroup = "CRIMINAL1"; + suspect1.Tasks.FightAgainstClosestHatedTarget(500f); + } + if (suspect2 && suspect2.IsValid()) + { + suspect2.RelationshipGroup = "CRIMINAL2"; + suspect2.Tasks.FightAgainstClosestHatedTarget(500f); + } susB1 = suspect1.AttachBlip(); susB2 = suspect2.AttachBlip(); diff --git a/DynamicLSPDFR/Callouts/WantedCriminalFound/WantedCriminalFound.cs b/DynamicLSPDFR/Callouts/WantedCriminalFound/WantedCriminalFound.cs index c2dfe88..03dc48f 100644 --- a/DynamicLSPDFR/Callouts/WantedCriminalFound/WantedCriminalFound.cs +++ b/DynamicLSPDFR/Callouts/WantedCriminalFound/WantedCriminalFound.cs @@ -109,7 +109,7 @@ public override void End() { Functions.PlayScannerAudio("ATTENTION_ALL_UNITS DL_CODE4 DL_NO_FURTHER_UNITS_REQUIRED"); MessageExtensions.SendDispatchMessage(Translation.GetString("Code4"), Translation.GetString(TranslateKey)); - if (criminal) criminal.Detach(); + if (criminal) criminal.Dismiss(); if (blip) blip.Delete(); base.End(); } diff --git a/DynamicLSPDFR/Common/Backup.cs b/DynamicLSPDFR/Common/Backup.cs index 7c34082..0cea5e2 100644 --- a/DynamicLSPDFR/Common/Backup.cs +++ b/DynamicLSPDFR/Common/Backup.cs @@ -2,6 +2,67 @@ namespace DynamicLSPDFR.Common; internal static class BackupCommon { + internal const string DEFAULT_BACKUP_FILE = @"plugins/LSPDFR/DynamicLSPDFR/DefaultBackup.xml"; + internal const string CUSTOM_BACKUP_FILE = @"plugins/LSPDFR/DynamicLSPDFR/CustomBackup.xml"; + + internal enum ResponseType : int + { + Code2, + Code3, + Pursuit, + } + + internal enum UnitType : int + { + // Police + LocalPatrol = 0, + StatePatrol = 1, + LocalFemalePatrol = 2, + StateFemalePatrol = 3, + LocalHighwayPatrol = 4, + StateHighwayPatrol = 5, + LocalFemaleHighwayPatrol = 6, + StateFemaleHighwayPatrol = 7, + BikePatrol = 8, + BikeFemalePatrol = 9, + LocalSWAT = 10, + NooseSWAT = 11, + + // FBI + FBILocal = 12, + FBISWAT = 13, + + // EMS + FireTruck = 50, + Ambulance = 51, + + // Air Support + LocalAir = 100, + NooseAir = 101, + AirAmbulance = 102, + AirSWAT = 103, + + // Service + + // Emergency + Panic = 200, + } + + internal enum WeatherType : int + { + Sunny = 0, + Rainy = 1, + Snowy = 2, + } + + internal enum ZoneCounty : int + { + LosSantos = 0, + LosSantosCounty = 1, + BlaineCounty = 2, + NorthYankton = 4, + } + internal static (string, string)[] CopMaleIdleAnim = new[] { ("amb@world_human_cop_idles@male@base", "base"), diff --git a/DynamicLSPDFR/Common/Callouts.cs b/DynamicLSPDFR/Common/Callouts.cs new file mode 100644 index 0000000..e1c1d48 --- /dev/null +++ b/DynamicLSPDFR/Common/Callouts.cs @@ -0,0 +1,7 @@ +namespace DynamicLSPDFR.Common; + +internal static class CalloutsCommon +{ + internal static string LicenseNumber = ""; + internal static Color VehicleColor = Color.Empty; +} \ No newline at end of file diff --git a/DynamicLSPDFR/Common/Menu.cs b/DynamicLSPDFR/Common/Menu.cs index 704a828..09b40f7 100644 --- a/DynamicLSPDFR/Common/Menu.cs +++ b/DynamicLSPDFR/Common/Menu.cs @@ -3,4 +3,6 @@ namespace DynamicLSPDFR.Common; internal static class MenuCommon { internal static readonly string PluginInfo = new StringBuilder(RSFormat.Bold).Append(RSFormat.ColorBlue).Append("DynamicLSPDFR").Append(RSFormat.Default).Append(RSFormat.Bold).Append(" Version.").Append(RSFormat.ColorOrange).Append(Main.PLUGIN_VERSION_STRING).Append(RSFormat.Default).ToString(); + internal const string BANNER_PATH = "DynamicLSPDFR.Resources.Images.Banner.png"; + internal static string GetStatusText(Languages lang) => new StringBuilder(Translation.GetString("LanguageMenuDesc")).Append("\n").Append(string.Format(Translation.GetString("TranslateStatus"), Translation.GetTranslateStatus(lang))).ToString(); } \ No newline at end of file diff --git a/DynamicLSPDFR/DynamicLSPDFR.ini b/DynamicLSPDFR/DynamicLSPDFR.ini index cd016c4..1ff8140 100644 --- a/DynamicLSPDFR/DynamicLSPDFR.ini +++ b/DynamicLSPDFR/DynamicLSPDFR.ini @@ -15,6 +15,12 @@ OfficerName = Officer StationName = [Keys] -; Open Context Menu key OpenContextMenuKey = O -OpenContextMenuKey-Modifier = None \ No newline at end of file +OpenContextMenuKey-Modifier = None + +UnitsTeleportToNearbyPlayerKey = U +UnitsTeleportToNearbyPlayerKey-Modifier = LControlKey + +[UnitsSetting] +; You should set value between 80 to 300 +CoronerSpawnDistance = 120 \ No newline at end of file diff --git a/DynamicLSPDFR/DynamicLSPDFRException.cs b/DynamicLSPDFR/DynamicLSPDFRException.cs new file mode 100644 index 0000000..2d1890b --- /dev/null +++ b/DynamicLSPDFR/DynamicLSPDFRException.cs @@ -0,0 +1,8 @@ +namespace DynamicLSPDFR; + +internal class DynamicLSPDFRException : Exception +{ + internal DynamicLSPDFRException() : base() { } + internal DynamicLSPDFRException(string message) : base() { } + internal DynamicLSPDFRException(string message, Exception innerException) : base() { } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Enums.cs b/DynamicLSPDFR/Enums.cs index a14a1ea..8b13cec 100644 --- a/DynamicLSPDFR/Enums.cs +++ b/DynamicLSPDFR/Enums.cs @@ -1,5 +1,117 @@ namespace DynamicLSPDFR; +internal enum CauseOfDeathHash : uint +{ + ElectricFence = 0x92BD4EBB, + WaterCannon = 0xCC34325E, + FlyingOutOfWindscreen = 0x7FC7D7, + RunOverByCar = 0xA36D413E, + Fall = 0xCDC174B0, + Animal = 0xF9FBAEBE, + AirstrikeRocket = 0x13579279, + Bleeding = 0x8B7333FB, + BarbedWire = 0x48E7B178, + Drownding = 0xFF58C4FB, + DrowndingInVehicle = 0x736F5990, + Explosion = 0x2024F4E8, + Exhaustion = 0x364A29EC, + Fire = 0xDF8E89EB, + HelicopterCrash = 0x145F1012, + + Dagger = 0x92A27487, + Bat = 0x958A4A8F, + Bottle = 0xF9E6AA4B, + Crowbar = 0x84BD7BFD, + Unarmed = 0xA2719263, + Flashlight = 0x8BB05FD7, + GolfClub = 0x440E4788, + Hammer = 0x4E875F73, + Hatchet = 0xF9DCBF2D, + Knuckle = 0xD8DF3C3C, + Knife = 0x99B507EA, + Machete = 0xDD5DF8D9, + Switchblade = 0xDFE37640, + Nightstick = 0x678B81B1, + Wrench = 0x19044EE0, + Battleaxe = 0xCD274149, + PoolCue = 0x94117305, + StoneHatchet = 0x3813FC08, + Pistol = 0x1B06D571, + Pistol_mk2 = 0xBFE256D4, + CombatPistol = 0x5EF9FEC4, + APPistol = 0x22D8FE39, + StunGun = 0x3656C8C1, + Pistol50 = 0x99AEEB3B, + SnsPistol = 0xBFD21232, + SnsPistol_mk2 = 0x88374054, + HeavyPistol = 0xD205520E, + VintagePistol = 0x83839C4, + FlareGun = 0x47757124, + MarksmanPistol = 0xDC4DB296, + Revolver = 0xC1B3C3D1, + Revolver_mk2 = 0xCB96392F, + Doubleaction = 0x97EA20B8, + RayPistol = 0xAF3696A1, + CeramicPistol = 0x2B5EF5EC, + NavyRevolver = 0x917F6C8C, + MicroSmg = 0x13532244, + Smg = 0x2BE6766B, + Smg_mk2 = 0x78A97CD0, + AssaultSmg = 0xEFE7E2DF, + CombatPdw = 0xA3D4D34, + MachinePistol = 0xDB1AA450, + MiniSmg = 0xBD248B55, + RayCarbine = 0x476BF155, + PumpShotgun = 0x1D073A89, + PumpShotgun_mk2 = 0x555AF99A, + SawnOffShotgun = 0x7846A318, + AssaultShotgun = 0xE284C527, + BullpupShotgun = 0x9D61E50F, + Musket = 0xA89CB99E, + HeavyShotgun = 0x3AABBBAA, + DbShotgun = 0xEF951FBB, + AutoShotgun = 0x12E82D3D, + AssaultRifle = 0xBFEFFF6D, + AssaultRifle_mk2 = 0x394F415C, + Carbinerifle = 0x83BF0278, + Carbinerifle_mk2 = 0xFAD1F1C9, + AdvancedRifle = 0xAF113F99, + SpecialCarbine = 0xC0A3098D, + SpecialCarbine_mk2 = 0x969C3D67, + BullpupRifle = 0x7F229F94, + BullpupRifle_mk2 = 0x84D6FAFD, + CompactRifle = 0x624FE830, + Mg = 0x9D07F764, + CombatMg = 0x7FD62962, + CombatMg_mk2 = 0xDBBD7280, + Gusenberg = 0x61012683, + SniperRifle = 0x5FC3C11, + HeavySniper = 0xC472FE2, + HeavySniper_mk2 = 0xA914799, + MarksmanRifle = 0xC734385A, + MarksmanRifle_mk2 = 0x6A6C02E0, + Rpg = 0xB1CA77B1, + GrenadeLauncher = 0xA284510B, + MiniGun = 0x42BF8A85, + Firework = 0x7F7497E5, + RailGun = 0x6D544C99, + HomingLauncher = 0x63AB0442, + CompactLauncher = 0x781FE4A, + RayMiniGun = 0xB62D1F67, + Grenade = 0x93E220BD, + BzGas = 0xA0973D5E, + SmokeGrenade = 0xFDBC8A50, + Flare = 0x497FACC3, + Molotov = 0x24B17070, + StickyBomb = 0x2C3731D9, + ProxMine = 0xAB564B93, + Snowball = 0x787F0BB, + PipeBomb = 0xBA45E8B8, + PetrolCan = 0x34A67B97, + FireExtinguisher = 0x60EC506, + HazardCan = 0xBA536372, +} + internal enum WeatherHash : uint { EXTRA_SUNNY = 0x97AA0A79, diff --git a/DynamicLSPDFR/Extensions/ConvertExtensions.cs b/DynamicLSPDFR/Extensions/ConvertExtensions.cs index 7631919..e7aca6b 100644 --- a/DynamicLSPDFR/Extensions/ConvertExtensions.cs +++ b/DynamicLSPDFR/Extensions/ConvertExtensions.cs @@ -2,43 +2,95 @@ 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; - // } - // } +#nullable enable + internal static List? ConvertToChanceModel(XElement unit) + { + var rList = new List(); + if (unit.Element("Vehicles").Elements("Vehicle") is not null) + foreach (var e in unit.Element("Vehicles").Elements("Vehicle")) + rList.Add(new((int)e.Attribute("chance"), e.Value, (int)e.Attribute("paxCount"))); + else rList.Add(new(100, "POLICE", 1)); + return rList; + } + + internal static List? ConvertToChancePed(XElement unit) + { + var rList = new List(); + if (unit.Element("Peds").Elements("Ped") is not null) + foreach (var e in unit.Element("Peds").Elements("Ped")) + rList.Add(new((int)e.Attribute("chance"), e.Value, (bool)e.Attribute("rndProp"))); + else rList.Add(new(100, "POLICE", false)); + return rList; + } + + internal static List? ConvertToChanceWeapon(XElement unit, bool isLong) + { + var rList = new List(); + if (unit.Element("Weapons").Element("HandGuns").Elements("Weapon") is not null) + switch (isLong) + { + default: + case false: + foreach (var e in unit.Element("Weapons").Element("HandGuns").Elements("Weapon")) + rList.Add(new((int)e.Attribute("chance"), e.Element("Hash").Value, ConvertToComponentsList(e))); + break; + case true: + foreach (var e in unit.Element("Weapons").Element("LongGuns").Elements("Weapon")) + rList.Add(new((int)e.Attribute("chance"), e.Element("Hash").Value, ConvertToComponentsList(e))); + break; + } + else rList.Add(new(100, "WEAPON_PISTOL", new() { "COMPONENT_AT_PI_FLSH" })); + return rList; + } + + internal static List? ConvertToComponentsList(XElement comp) + { + var rList = new List(); + if (comp.Element("Components").Elements("Component") is not null) + foreach (var c in comp.Element("Components").Elements("Component")) + rList.Add(c.Value); + else rList.Add("COMPONENT_AT_PI_FLSH"); + return rList; + } +#nullable disable + + internal static BackupCommon.WeatherType 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 BackupCommon.WeatherType.Sunny; + case WeatherHash.CLEARING: + case WeatherHash.RAIN: + case WeatherHash.THUNDER: + return BackupCommon.WeatherType.Rainy; + case WeatherHash.SNOW: + case WeatherHash.SNOWLIGHT: + case WeatherHash.BLIZZARD: + case WeatherHash.XMAS: + return BackupCommon.WeatherType.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 BackupCommon.WeatherType ConvertToBackupWeather(string weather) + { + weather.ToUpper(); + return weather switch + { + "SUNNY" => BackupCommon.WeatherType.Sunny, + "RAINY" => BackupCommon.WeatherType.Rainy, + "SNOWY" => BackupCommon.WeatherType.Snowy, + _ => BackupCommon.WeatherType.Sunny, + }; + } internal static EWorldZoneCounty ConvertToWorldZoneCounty(string county) { @@ -53,26 +105,53 @@ 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 ZoneCounty ConvertToBackupCounty(string county) + { + county.ToUpper(); + return county switch + { + "LOSSANTOS" => ZoneCounty.LosSantos, + "LOSSANTOSCOUNTY" => ZoneCounty.LosSantosCounty, + "BLAINECOUNTY" => ZoneCounty.BlaineCounty, + "NORTHYANKTON" => ZoneCounty.NorthYankton, + _ => ZoneCounty.LosSantos, + }; + } + + internal static ZoneCounty ConvertToBackupCounty(EWorldZoneCounty county) => + county switch + { + EWorldZoneCounty.LosSantos => ZoneCounty.LosSantos, + EWorldZoneCounty.LosSantosCounty => ZoneCounty.LosSantosCounty, + EWorldZoneCounty.BlaineCounty => ZoneCounty.BlaineCounty, + EWorldZoneCounty.NorthYankton => ZoneCounty.NorthYankton, + _ => ZoneCounty.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 UnitType ConvertToUnitType(string unit) => + unit switch + { + "LocalPatrol" => UnitType.LocalPatrol, + "StatePatrol" => UnitType.StatePatrol, + "LocalFemalePatrol" => UnitType.LocalFemalePatrol, + "StateFemalePatrol" => UnitType.StateFemalePatrol, + "LocalHighwayPatrol" => UnitType.LocalHighwayPatrol, + "StateHighwayPatrol" => UnitType.StateHighwayPatrol, + "LocalFemaleHighwayPatrol" => UnitType.LocalFemaleHighwayPatrol, + "StateFemaleHighwayPatrol" => UnitType.StateFemaleHighwayPatrol, + "BikePatrol" => UnitType.BikePatrol, + "BikeFemalePatrol" => UnitType.BikeFemalePatrol, + "LocalSWAT" => UnitType.LocalSWAT, + "NooseSWAT" => UnitType.NooseSWAT, + "FBILocal" => UnitType.FBILocal, + "FBISWAT" => UnitType.FBISWAT, + "FireTruck" => UnitType.FireTruck, + "Ambulance" => UnitType.Ambulance, + "LocalAir" => UnitType.LocalAir, + "NooseAir" => UnitType.NooseAir, + "AirAmbulance" => UnitType.AirAmbulance, + "AirSWAT" => UnitType.AirSWAT, + "Panic" => UnitType.Panic, + _ => UnitType.LocalPatrol, + }; } \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/DialogExtensions.cs b/DynamicLSPDFR/Extensions/DialogExtensions.cs index fb80b35..ce9f619 100644 --- a/DynamicLSPDFR/Extensions/DialogExtensions.cs +++ b/DynamicLSPDFR/Extensions/DialogExtensions.cs @@ -18,6 +18,7 @@ internal static void Dialog(List<(string, string)> Lines, Ped target = null) sb.Append(RSFormat.Default); sb.Append(": "); if (Lines[i].Item2 is "SushiTerrorismSuspect3-1") sb.Append(string.Format(Translation.GetString(Lines[i].Item2), station, Main.OfficerName)); + else if (Lines[i].Item2 is "HitAndRunTalk4") sb.Append(string.Format(Translation.GetString(Lines[i].Item2), CalloutsCommon.LicenseNumber, CalloutsCommon.VehicleColor.ToString())); else sb.Append(Translation.GetString(Lines[i].Item2)); sb.Append(" ").Append($"({i + 1}/{Lines.Count})"); Game.DisplaySubtitle(sb.ToString(), 10000); @@ -30,7 +31,7 @@ internal static void Dialog(List<(string, string)> Lines, Ped target = null) } if (!HandlingSpeech) break; } - if (target != null) target.Tasks.ClearImmediately(); + if (target && target.IsValid()) target.Tasks.ClearImmediately(); HandlingSpeech = false; Game.HideHelp(); diff --git a/DynamicLSPDFR/Extensions/HeadingExtensions.cs b/DynamicLSPDFR/Extensions/HeadingExtensions.cs index 450627a..d6434a9 100644 --- a/DynamicLSPDFR/Extensions/HeadingExtensions.cs +++ b/DynamicLSPDFR/Extensions/HeadingExtensions.cs @@ -4,22 +4,25 @@ internal static class HeadingExtensions { internal static Directions GetPedDirection(this Ped ped) { - if ((348.75f <= ped.Heading && ped.Heading <= 360f) || (0f <= ped.Heading && ped.Heading < 11.25f)) return Directions.N; - else if (11.25f <= ped.Heading && ped.Heading < 33.75f) return Directions.NNW; - else if (33.75f <= ped.Heading && ped.Heading < 56.25f) return Directions.NW; - else if (56.25 <= ped.Heading && ped.Heading < 78.75f) return Directions.WNW; - else if (78.75f <= ped.Heading && ped.Heading < 101.25f) return Directions.W; - else if (101.25f <= ped.Heading && ped.Heading < 123.75f) return Directions.WSW; - else if (123.75f <= ped.Heading && ped.Heading < 146.25f) return Directions.SW; - else if (146.25f <= ped.Heading && ped.Heading < 168.75f) return Directions.SSW; - else if (168.75f <= ped.Heading && ped.Heading < 191.25f) return Directions.S; - else if (191.25f <= ped.Heading && ped.Heading < 213.75f) return Directions.SSE; - else if (213.75f <= ped.Heading && ped.Heading < 236.25f) return Directions.SE; - else if (236.25f <= ped.Heading && ped.Heading < 258.75f) return Directions.ESE; - else if (258.75f <= ped.Heading && ped.Heading < 281.25f) return Directions.E; - else if (281.25f <= ped.Heading && ped.Heading < 303.75f) return Directions.ENE; - else if (303.75f <= ped.Heading && ped.Heading < 326.25f) return Directions.NE; - else if (326.25f <= ped.Heading && ped.Heading < 348.75f) return Directions.NNE; - else return Directions.Unknown; + switch ((int)((ped.Heading + 11.25f) / 22.5f) % 16) + { + case 0: + case 15: return Directions.N; + case 1: return Directions.NNE; + case 2: return Directions.NE; + case 3: return Directions.ENE; + case 4: return Directions.E; + case 5: return Directions.ESE; + case 6: return Directions.SE; + case 7: return Directions.SSE; + case 8: return Directions.S; + case 9: return Directions.SSW; + case 10: return Directions.SW; + case 11: return Directions.WSW; + case 12: return Directions.W; + case 13: return Directions.WNW; + case 14: return Directions.NW; + default: return Directions.Unknown; + } } } \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/KeyExtensions.cs b/DynamicLSPDFR/Extensions/KeyExtensions.cs index a9c47ec..17d2d7a 100644 --- a/DynamicLSPDFR/Extensions/KeyExtensions.cs +++ b/DynamicLSPDFR/Extensions/KeyExtensions.cs @@ -10,7 +10,12 @@ internal static class KeyExtensions /// Key pressed status. internal static bool IsKeysDown(Keys Main, Keys Modifier = Keys.None) { - return (Game.IsKeyDown(Modifier) || Modifier is Keys.None) && Game.IsKeyDown(Main); + return NativeFunction.Natives.UPDATE_ONSCREEN_KEYBOARD() is not 0 && (Game.IsKeyDown(Modifier) || Modifier is Keys.None) && Game.IsKeyDown(Main); + } + + internal static bool IsKeysDownRightNow(Keys Main, Keys Modifier = Keys.None) + { + return NativeFunction.Natives.UPDATE_ONSCREEN_KEYBOARD() is not 0 && (Game.IsKeyDown(Modifier) || Modifier is Keys.None) && Game.IsKeyDownRightNow(Main); } /// diff --git a/DynamicLSPDFR/Extensions/Localization/Translation.cs b/DynamicLSPDFR/Extensions/Localization/Translation.cs index fe7081d..2e09ee2 100644 --- a/DynamicLSPDFR/Extensions/Localization/Translation.cs +++ b/DynamicLSPDFR/Extensions/Localization/Translation.cs @@ -90,7 +90,11 @@ internal static string GetString(string key) else return key.Replace(keyClean, CustomTranslation.customStr[key]); } - if (data.ContainsKey((int)Main.Language)) return key.Replace(keyClean, data[(int)Main.Language]); + if (data.ContainsKey((int)Main.Language)) + { + if (data[(int)Main.Language] is "[EN]") key.Replace(keyClean, data[(int)DEFAULT_LANGUAGE]); + else return key.Replace(keyClean, data[(int)Main.Language]); + } else if (data.ContainsKey((int)DEFAULT_LANGUAGE)) return key.Replace(keyClean, data[(int)DEFAULT_LANGUAGE]); Logger.Warn($"Translate key was not found in dictionary. Key: {key}", "Translate"); @@ -100,7 +104,7 @@ internal static string GetString(string key) internal static void SetLanguage(Languages id, bool showNotification = true) { Main.Language = id; - DynamicMenu.UpdateTranslate(); + ContextMenu.UpdateTranslate(); if (showNotification) MessageExtensions.SendMessageInGame(string.Format($"The language was set to {Enum.GetName(typeof(Languages), id).Replace("_", " ")} now.")); } diff --git a/DynamicLSPDFR/Extensions/LoggerExtensions.cs b/DynamicLSPDFR/Extensions/LoggerExtensions.cs index 10ee395..dc21c40 100644 --- a/DynamicLSPDFR/Extensions/LoggerExtensions.cs +++ b/DynamicLSPDFR/Extensions/LoggerExtensions.cs @@ -32,10 +32,8 @@ private static void Log(string text, LogType type = LogType.Trace, string tag = if (type is LogType.ErrorCode) { - // MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("ErrorOccurred"), errorCode)); - // MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("PleaseReport"))); - MessageExtensions.SendMessageInGame(string.Format($"~h~[DynamicLSPDFR]~h~\n~y~An error has occurred.~s~\n~y~Error Code: {errorCode}~s~")); - MessageExtensions.SendMessageInGame(string.Format("~r~Please report your bug in my ~p~Discord Server~r~!~s~")); + MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("ErrorOccurred"), errorCode)); + MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("PleaseReport"))); } } } \ No newline at end of file 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/MessageExtensions.cs b/DynamicLSPDFR/Extensions/MessageExtensions.cs index 9cebbc6..c2d2791 100644 --- a/DynamicLSPDFR/Extensions/MessageExtensions.cs +++ b/DynamicLSPDFR/Extensions/MessageExtensions.cs @@ -6,10 +6,7 @@ internal static class MessageExtensions /// Normal Message send in game. /// /// message - internal static void SendMessageInGame(string text) - { - Game.DisplayNotification(text); - } + internal static void SendMessageInGame(string text) => Game.DisplayNotification(text); /// /// Send message with picture. diff --git a/DynamicLSPDFR/Extensions/NativeExtensions.cs b/DynamicLSPDFR/Extensions/NativeExtensions.cs index 7749cc6..927789b 100644 --- a/DynamicLSPDFR/Extensions/NativeExtensions.cs +++ b/DynamicLSPDFR/Extensions/NativeExtensions.cs @@ -3,6 +3,8 @@ namespace DynamicLSPDFR.Extensions; internal static class NativeExtensions { internal static void Drive(Vehicle veh, float speed) => NativeFunction.Natives.SET_VEHICLE_FORWARD_SPEED(veh, speed); + internal static void DoorLock(uint hash, Vector3 pos, bool status) => NativeFunction.Natives.SET_LOCKED_UNSTREAMED_IN_DOOR_OF_TYPE(hash, pos.X, pos.Y, pos.Z, status, 0f, 0f, 0f); + internal static void SetPedSuffersCriticalHits(this Ped p, bool status) => NativeFunction.Natives.SET_PED_SUFFERS_CRITICAL_HITS(p, status); // internal static WeatherHash GetWorldWeather() // { // try diff --git a/DynamicLSPDFR/Extensions/NodeExtensions.cs b/DynamicLSPDFR/Extensions/NodeExtensions.cs new file mode 100644 index 0000000..3740d35 --- /dev/null +++ b/DynamicLSPDFR/Extensions/NodeExtensions.cs @@ -0,0 +1,30 @@ +namespace DynamicLSPDFR.Extensions; + +internal static class NodeExtensions +{ + internal static int[] BlackListedNodeTypes = new int[] { 0, 8, 9, 10, 12, 40, 42, 136 }; + + internal static int GetNearestNodeType(this Vector3 pos) + { + bool get_property_success = false; + uint node_prop_p1; + int found_node_type; + + get_property_success = NativeFunction.Natives.GET_VEHICLE_NODE_PROPERTIES(pos.X, pos.Y, pos.Z, out node_prop_p1, out found_node_type); + + + if (get_property_success) + { + return found_node_type; + } + else + { + return -1; + } + } + + internal static bool IsNodeSafe(this Vector3 pos) + { + return !BlackListedNodeTypes.Contains(GetNearestNodeType(pos)); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/PedExtensions.cs b/DynamicLSPDFR/Extensions/PedExtensions.cs index cf1122b..539bac0 100644 --- a/DynamicLSPDFR/Extensions/PedExtensions.cs +++ b/DynamicLSPDFR/Extensions/PedExtensions.cs @@ -18,15 +18,13 @@ internal static bool EveryoneAreDeadOrArrested(params Ped[] peds) return true; } - internal static void SetTeamAndFight(string teamName, params Ped[] peds) + internal static bool EveryoneAreDeadOrArrested(List peds) { - for (int i = 0; i < peds.Length; i++) + for (int i = 0; i < peds.Count; i++) { - if (peds[i] && peds[i].IsValid()) - { - peds[i].RelationshipGroup = teamName; - peds[i].Tasks.FightAgainstClosestHatedTarget(3000f); - } + if ((peds[i] && peds[i].IsValid() && peds[i].IsDeadOrArrested()) || !peds[i]) continue; + else return false; } + return true; } } \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector2Converter.cs b/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector2Converter.cs new file mode 100644 index 0000000..1b67d19 --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector2Converter.cs @@ -0,0 +1,21 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Converters; + +public class Vector2Converter : JsonConverter +{ + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Vector2); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var val = JObject.Load(reader); + return new Vector2((float)val["x"], (float)val["y"]); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var val = (Vector2)value; + serializer.Serialize(writer, new { val.X, val.Y, }); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector3Converter.cs b/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector3Converter.cs new file mode 100644 index 0000000..a5f4ef1 --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector3Converter.cs @@ -0,0 +1,21 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Converters; + +public class Vector3Converter : JsonConverter +{ + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Vector3); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var val = JObject.Load(reader); + return new Vector3((float)val["x"], (float)val["y"], (float)val["z"]); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var val = (Vector3)value; + serializer.Serialize(writer, new { val.X, val.Y, val.Z }); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector4Converter.cs b/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector4Converter.cs new file mode 100644 index 0000000..8f3b341 --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Converters/Vector4Converter.cs @@ -0,0 +1,21 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Converters; + +public class Vector4Converter : JsonConverter +{ + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Vector4); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var val = JObject.Load(reader); + return new Vector4((float)val["x"], (float)val["y"], (float)val["z"], (float)val["w"]); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var val = (Vector4)value; + serializer.Serialize(writer, new { val.X, val.Y, val.Z, val.W }); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Serializer/JsonSerialiser.cs b/DynamicLSPDFR/Extensions/Save/Core/Serializer/JsonSerialiser.cs new file mode 100644 index 0000000..d73eaba --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Serializer/JsonSerialiser.cs @@ -0,0 +1,20 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Serializers; + +internal static class Serializer +{ + private static readonly JsonSerializerSettings settings = new() + { + Converters = new List() + { + new Vector2Converter(), + new Vector3Converter(), + new Vector4Converter() + } + }; + + private static JsonSerializer serializer = JsonSerializer.Create(settings); + + internal static T DeserializeKey(string key, JObject data) => data[key].ToObject(serializer); + internal static JToken SerializeKey(T data) => JToken.FromObject(data, serializer); + internal static string Serialize(T value) => JsonConvert.SerializeObject(value, settings); +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Settings/Compression.cs b/DynamicLSPDFR/Extensions/Save/Core/Settings/Compression.cs new file mode 100644 index 0000000..6af5fdc --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Settings/Compression.cs @@ -0,0 +1,40 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Settings; + +internal class Compression +{ + internal static string Compress(string value, CompressionMode compressionMode) + { + if (compressionMode is CompressionMode.None) return value; + + var bytes = Encoding.UTF8.GetBytes(value); + + using (var streamIn = new MemoryStream(bytes)) + using (var streamOut = new MemoryStream()) + { + using (var compressionStream = new GZipStream(streamOut, System.IO.Compression.CompressionMode.Compress)) + { + streamIn.CopyTo(compressionStream); + } + + return Convert.ToBase64String(streamOut.ToArray()); + } + } + + internal static string Decompress(string value, CompressionMode compressionMode) + { + if (compressionMode is CompressionMode.None) return value; + + var bytes = Convert.FromBase64String(value); + + using (var streamIn = new MemoryStream(bytes)) + using (var streamOut = new MemoryStream()) + { + using (var compressionStream = new GZipStream(streamIn, System.IO.Compression.CompressionMode.Decompress)) + { + compressionStream.CopyTo(streamOut); + } + + return Encoding.UTF8.GetString(streamOut.ToArray()); + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Settings/Cryptography.cs b/DynamicLSPDFR/Extensions/Save/Core/Settings/Cryptography.cs new file mode 100644 index 0000000..a8cb675 --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Settings/Cryptography.cs @@ -0,0 +1,79 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Settings; + +internal static class Cryptography +{ + internal static string Encrypt(string value, SecurityMode securityMode, string password) + { + switch (securityMode) + { + case SecurityMode.Aes: return AesEncrypt(password, value); + case SecurityMode.Base64: return Base64Encode(value); + default: return value; + } + } + + internal static string Decrypt(string value, SecurityMode securityMode, string password) + { + switch (securityMode) + { + case SecurityMode.Aes: return AesDecrypt(password, value); + case SecurityMode.Base64: return Base64Decode(value); + default: return value; + } + } + + private static string AesEncrypt(string encryptionKey, string value) + { + if (string.IsNullOrEmpty(value)) return value; + + using (var encryptor = Aes.Create()) + { + Rfc2898DeriveBytes pdb = new(encryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); + + encryptor.Key = pdb.GetBytes(32); + encryptor.IV = pdb.GetBytes(16); + + byte[] bytes = Encoding.UTF8.GetBytes(value); + + using (var streamIn = new MemoryStream(bytes)) + using (var streamOut = new MemoryStream()) + { + using (var cryptoStream = new CryptoStream(streamOut, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) + { + streamIn.CopyTo(cryptoStream); + } + + return Convert.ToBase64String(streamOut.ToArray()); + } + } + } + + private static string AesDecrypt(string encryptionKey, string value) + { + if (string.IsNullOrEmpty(value)) return value; + + using (var decryptor = Aes.Create()) + { + Rfc2898DeriveBytes pdb = new(encryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); + + decryptor.Key = pdb.GetBytes(32); + decryptor.IV = pdb.GetBytes(16); + + byte[] bytes = Convert.FromBase64String(value); + + using (var streamIn = new MemoryStream(bytes)) + using (var streamOut = new MemoryStream()) + { + using (var cryptoStream = new CryptoStream(streamIn, decryptor.CreateDecryptor(), CryptoStreamMode.Read)) + { + cryptoStream.CopyTo(streamOut); + } + + return Encoding.UTF8.GetString(streamOut.ToArray()); + } + } + } + + private static string Base64Encode(string value) => Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); + private static string Base64Decode(string value) => Encoding.UTF8.GetString(Convert.FromBase64String(value)); +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/Core/Storage/FileAccess.cs b/DynamicLSPDFR/Extensions/Save/Core/Storage/FileAccess.cs new file mode 100644 index 0000000..46e8647 --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/Core/Storage/FileAccess.cs @@ -0,0 +1,134 @@ +namespace DynamicLSPDFR.Extensions.Save.Core.Storage; + +internal static class FileAccess +{ + private const string FILE_EXTENSIONS = ".dat"; + + private static string BasePath => Path.Combine(SaveExtensions.SAVE_FOLDER_PATH, "SaveData"); + + internal static bool SaveString(string filename, bool includesExtension, string value) + { + filename = GetFilenameWithExtension(filename, includesExtension); + + try + { + CreateRootFolder(); + + using (StreamWriter writer = new(Path.Combine(BasePath, filename))) + { + writer.Write(value); + } + + return true; + } + catch { } + + return false; + } + + internal static bool SaveBytes(string filename, bool includesExtension, byte[] value) + { + filename = GetFilenameWithExtension(filename, includesExtension); + + try + { + CreateRootFolder(); + + using (FileStream fileStream = new(Path.Combine(BasePath, filename), FileMode.Create)) + { + fileStream.Write(value, 0, value.Length); + } + + return true; + } + catch { } + + return false; + } + + internal static string LoadString(string filename, bool includesExtension) + { + filename = GetFilenameWithExtension(filename, includesExtension); + + try + { + CreateRootFolder(); + + if (Exists(filename, true)) + { + using (StreamReader reader = new(Path.Combine(BasePath, filename))) + { + return reader.ReadToEnd(); + } + } + } + catch { } + + return null; + } + + internal static byte[] LoadBytes(string filename, bool includesExtension) + { + filename = GetFilenameWithExtension(filename, includesExtension); + + try + { + CreateRootFolder(); + + if (Exists(filename, true)) + { + using (FileStream fileStream = new(Path.Combine(BasePath, filename), FileMode.Open)) + { + byte[] buffer = new byte[fileStream.Length]; + + fileStream.Read(buffer, 0, buffer.Length); + + return buffer; + } + } + } + catch { } + + return null; + } + + internal static void Delete(string filename, bool includesExtension) + { + filename = GetFilenameWithExtension(filename, includesExtension); + + try + { + CreateRootFolder(); + string fileLocation = Path.Combine(BasePath, filename); + File.Delete(fileLocation); + } + catch { } + } + + internal static bool Exists(string filename, bool includesExtension) + { + filename = GetFilenameWithExtension(filename, includesExtension); + string fileLocation = Path.Combine(BasePath, filename); + return File.Exists(fileLocation); + } + + internal static IEnumerable Files(bool includeExtensions) + { + try + { + CreateRootFolder(); + + if (includeExtensions) return Directory.GetFiles(BasePath, "*.dat").Select(x => Path.GetFileName(x)); + else return Directory.GetFiles(BasePath, "*.dat").Select(x => Path.GetFileNameWithoutExtension(x)); + } + catch { } + + return new List(); + } + + private static string GetFilenameWithExtension(string filename, bool includesExtension) => includesExtension ? filename : new StringBuilder(filename).Append(FILE_EXTENSIONS).ToString(); + private static void CreateRootFolder() + { + if (!Directory.Exists(BasePath)) Directory.CreateDirectory(BasePath); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/SaveBase.cs b/DynamicLSPDFR/Extensions/Save/SaveBase.cs new file mode 100644 index 0000000..c1bb42a --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/SaveBase.cs @@ -0,0 +1,106 @@ +namespace DynamicLSPDFR.Extensions.Save; + +internal abstract class SaveBase +{ + protected JObject items; + protected string root; + protected SaveSettings settings; + + /// + /// Determines if the specified key exists + /// + /// The key to look for + /// Does the key exist + internal bool Exists(string key) => items[key] != null; + + /// + /// Gets the names of all the keys under this root + /// + /// A collection of key names + internal IEnumerable GetAllKeys() => items.Properties().Select(x => x.Name).ToList(); + + protected void Load(bool rootMightNotExist) + { + var json = FileAccess.LoadString(root, false); + + if (string.IsNullOrEmpty(json)) + { + if (rootMightNotExist) + { + items = new(); + return; + } + + throw new DynamicLSPDFRException("Root does not exist"); + } + + // Gzip parses base64 anyway so no need to do it twice + if (settings.CompressionMode is not CompressionMode.Gzip || settings.SecurityMode is not SecurityMode.Base64) + { + try + { + json = Cryptography.Decrypt(json, settings.SecurityMode, settings.Password); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Decryption failed", e); + } + } + + try + { + json = Compression.Decompress(json, settings.CompressionMode); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Decompression failed", e); + } + + try + { + items = JObject.Parse(json); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Deserialisation failed", e); + } + } + + protected void Save() + { + string json; + + try + { + json = Serializer.Serialize(items); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Serialisation failed", e); + } + + try + { + json = Compression.Compress(json, settings.CompressionMode); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Compression failed", e); + } + + // Gzip outputs base64 anyway so no need to do it twice + if (settings.CompressionMode is not CompressionMode.Gzip || settings.SecurityMode is not SecurityMode.Base64) + { + try + { + json = Cryptography.Encrypt(json, settings.SecurityMode, settings.Password); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Encryption failed", e); + } + } + + if (!FileAccess.SaveString(root, false, json)) throw new DynamicLSPDFRException("Failed to write to file"); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/SaveRaw.cs b/DynamicLSPDFR/Extensions/Save/SaveRaw.cs new file mode 100644 index 0000000..e2ea6ef --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/SaveRaw.cs @@ -0,0 +1,136 @@ +namespace DynamicLSPDFR.Extensions.Save; + +internal class SaveRaw +{ + /// + /// Saves a string directly to the specified file, overwriting if it already exists + /// + /// The file to save to + /// The string to save + internal static void SaveString(string filename, string content) => SaveString(filename, content, new SaveSettings()); + + /// + /// Saves a string directly to the specified file using the specified settings, overwriting if it already exists + /// + /// The file to save to + /// The string to save + /// Settings + internal static void SaveString(string filename, string content, SaveSettings settings) + { + string contentToWrite; + + try + { + contentToWrite = Compression.Compress(content, settings.CompressionMode); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Compression failed", e); + } + + // Gzip outputs base64 anyway so no need to do it twice + if (settings.CompressionMode is not CompressionMode.Gzip || settings.SecurityMode is not SecurityMode.Base64) + { + try + { + contentToWrite = Cryptography.Encrypt(contentToWrite, settings.SecurityMode, settings.Password); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Encryption failed", e); + } + } + + if (!FileAccess.SaveString(filename, true, contentToWrite)) + { + throw new DynamicLSPDFRException("Failed to write to file"); + } + } + + /// + /// Saves a byte array directly to the specified file, overwriting if it already exists + /// + /// The file to save to + /// The byte array to save + internal static void SaveBytes(string filename, byte[] content) + { + if (!FileAccess.SaveBytes(filename, true, content)) throw new DynamicLSPDFRException("Failed to write to file"); + } + + /// + /// Loads the contents of the specified file into a string + /// + /// The file to load from + /// The contents of the file as a string + internal static string LoadString(string filename) => LoadString(filename, new SaveSettings()); + + /// + /// Loads the contents of the specified file into a string using the specified settings + /// + /// The file to load from + /// Settings + /// The contents of the file as a string + internal static string LoadString(string filename, SaveSettings settings) + { + var content = FileAccess.LoadString(filename, true); + + if (content is null) throw new DynamicLSPDFRException("Failed to load file"); + + // Gzip parses base64 anyway so no need to do it twice + if (settings.CompressionMode is not CompressionMode.Gzip || settings.SecurityMode is not SecurityMode.Base64) + { + try + { + content = Cryptography.Decrypt(content, settings.SecurityMode, settings.Password); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Decryption failed", e); + } + } + + try + { + content = Compression.Decompress(content, settings.CompressionMode); + } + catch (Exception e) + { + throw new DynamicLSPDFRException("Decompression failed", e); + } + + return content; + } + + /// + /// Loads the contents of the specified file into a byte array + /// + /// The file to load from + /// The contents of the file as a byte array + internal static byte[] LoadBytes(string filename) + { + byte[] content = FileAccess.LoadBytes(filename, true); + + if (content is null) throw new DynamicLSPDFRException("Failed to load file"); + + return content; + } + + /// + /// Deletes the specified file if it exists + /// + /// The file to delete + internal static void Delete(string filename) => FileAccess.Delete(filename, true); + + /// + /// Determines if the specified file exists + /// + /// The file to check + /// Does the file exist + internal static bool Exists(string filename) => FileAccess.Exists(filename, true); + + /// + /// Gets the names of all files that have been saved + /// + /// A collection of file names + internal static IEnumerable GetAllFiles() => FileAccess.Files(false); +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/SaveReader.cs b/DynamicLSPDFR/Extensions/Save/SaveReader.cs new file mode 100644 index 0000000..48bdb4a --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/SaveReader.cs @@ -0,0 +1,103 @@ +namespace DynamicLSPDFR.Extensions.Save; + +internal class SaveReader : SaveBase +{ + private SaveReader(string root, SaveSettings settings) + { + base.root = root; + base.settings = settings; + } + + /// + /// Creates a SaveReader on the specified root + /// + /// The root to read from + /// A SaveReader instance + internal static SaveReader Create(string root) => Create(root, new SaveSettings()); + + /// + /// Creates a SaveReader on the specified root using the specified settings + /// + /// The root to read from + /// Settings + /// A SaveReader instance + internal static SaveReader Create(string root, SaveSettings settings) + { + SaveReader SaveReader = new(root, settings); + SaveReader.Load(false); + return SaveReader; + } + + /// + /// Reads an object under the specified key + /// + /// The type of object to read + /// The key this object was saved under + /// The object that was loaded + internal T Read(string key) + { + if (!Exists(key)) throw new DynamicLSPDFRException("Key does not exists"); + + try + { + return Serializer.DeserializeKey(key, items); + } + catch + { + throw new DynamicLSPDFRException("Deserialisation failed"); + } + } + + /// + /// Reads an object under the specified key + /// + /// The type of object to read + /// The key this object was saved under + /// An action to be called when the read completes + /// The SaveReader + internal SaveReader Read(string key, Action result) + { + if (!Exists(key)) throw new DynamicLSPDFRException("Key does not exists"); + + try + { + result(Serializer.DeserializeKey(key, items)); + } + catch + { + throw new DynamicLSPDFRException("Deserialisation failed"); + } + + return this; + } + + /// + /// Attempts to read an object under the specified key + /// + /// The type of object to read + /// The key this object was saved under + /// The object that was loaded + /// Was the read successful + internal bool TryRead(string key, out T result) + { + result = default(T); + + if (!Exists(key)) return false; + + try + { + result = Serializer.DeserializeKey(key, items); + + return true; + } + catch + { + return false; + } + } + + /// + /// Reloads data from the root + /// + internal void Reload() => Load(false); +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/SaveSettings.cs b/DynamicLSPDFR/Extensions/Save/SaveSettings.cs new file mode 100644 index 0000000..5d48d7f --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/SaveSettings.cs @@ -0,0 +1,26 @@ +namespace DynamicLSPDFR.Extensions.Save +{ + internal class SaveSettings + { + /// + /// The type of encryption to use on the data + /// + internal SecurityMode SecurityMode { get; set; } + + /// + /// The type of compression to use on the data + /// + internal CompressionMode CompressionMode { get; set; } + + /// + /// If aes is selected as the security mode specify a password to use as the encryption key + /// + internal string Password { get; set; } + + internal SaveSettings() + { + SecurityMode = SecurityMode.None; + CompressionMode = CompressionMode.None; + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/Save/SaveWriter.cs b/DynamicLSPDFR/Extensions/Save/SaveWriter.cs new file mode 100644 index 0000000..be7b7c5 --- /dev/null +++ b/DynamicLSPDFR/Extensions/Save/SaveWriter.cs @@ -0,0 +1,77 @@ +namespace DynamicLSPDFR.Extensions.Save; + +internal class SaveWriter : SaveBase +{ + private SaveWriter(string root, SaveSettings settings) + { + base.root = root; + base.settings = settings; + } + + /// + /// Creates a SaveWriter on the specified root + /// + /// The root to write to + /// A SaveWriter instance + internal static SaveWriter Create(string root) => Create(root, new()); + + /// + /// Creates a SaveWriter on the specified root using the specified settings + /// + /// The root to write to + /// Settings + /// A SaveWriter instance + internal static SaveWriter Create(string root, SaveSettings settings) + { + SaveWriter SaveWriter = new(root, settings); + SaveWriter.Load(true); + return SaveWriter; + } + + /// + /// Writes an object to the specified key - you must called commit to write the data to file + /// + /// The type of object to write + /// The key this object will be saved under + /// The object to save + /// The SaveWriter + internal SaveWriter Write(string key, T value) + { + if (Exists(key)) items.Remove(key); + + items.Add(key, Serializer.SerializeKey(value)); + + return this; + } + + /// + /// Deletes the specified key if it exists + /// + /// The key to delete + internal void Delete(string key) + { + if (Exists(key)) items.Remove(key); + } + + /// + /// Commits the changes to file + /// + internal void Commit() => Save(); + + /// + /// Attempts to commit the changes to file + /// + /// Was the commit successful + internal bool TryCommit() + { + try + { + Save(); + return true; + } + catch + { + return false; + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/SaveExtensions.cs b/DynamicLSPDFR/Extensions/SaveExtensions.cs new file mode 100644 index 0000000..7360587 --- /dev/null +++ b/DynamicLSPDFR/Extensions/SaveExtensions.cs @@ -0,0 +1,19 @@ +namespace DynamicLSPDFR.Extensions; + +internal enum SecurityMode +{ + None, + Aes, + Base64 +} + +internal enum CompressionMode +{ + None, + Gzip +} + +internal static class SaveExtensions +{ + internal const string SAVE_FOLDER_PATH = @"plugins/LSPDFR/DynamicLSPDFR"; +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/VehicleExtensions.cs b/DynamicLSPDFR/Extensions/VehicleExtensions.cs new file mode 100644 index 0000000..bff57ba --- /dev/null +++ b/DynamicLSPDFR/Extensions/VehicleExtensions.cs @@ -0,0 +1,170 @@ +namespace DynamicLSPDFR.Extensions; + +internal static class VehicleExtensions +{ + internal static Model[] LSPDVehicles = new Model[] { "POLICE", "POLICE2", "POLICE3" }; + internal static Model[] LSSDVehicles = new Model[] { "SHERIFF", "SHERIFF2" }; + internal static Model[] NorthYanktonPDVehicles = new Model[] { "POLICEOLD1", "POLICEOLD2" }; + + internal static Model GetPoliceVehicleFromCounty() + { + switch (Main.CurrentPlayerCountry) + { + default: + case ZoneCounty.LosSantos: + return LSPDVehicles[RandomNumber.Instance.Next(LSPDVehicles.Length)]; + case ZoneCounty.LosSantosCounty: + case ZoneCounty.BlaineCounty: + return LSSDVehicles[RandomNumber.Instance.Next(LSSDVehicles.Length)]; + case ZoneCounty.NorthYankton: + return NorthYanktonPDVehicles[RandomNumber.Instance.Next(NorthYanktonPDVehicles.Length)]; + } + } + + internal static void DriveToPosition(Ped driver, Vehicle veh, Vector3 pos) + { + int drivingLoopCount = 0; + bool transportVanTeleported = false; + int waitCount = 0; + bool ForceTeleport = false; + + GameFiber.StartNew(delegate + { + while (!ForceTeleport) + { + GameFiber.Yield(); + if (KeyExtensions.IsKeysDown(Main.UnitsTeleportToNearbyPlayerKey, Main.UnitsTeleportToNearbyPlayerModifierKey)) + { + GameFiber.Sleep(500); + if (KeyExtensions.IsKeysDownRightNow(Main.UnitsTeleportToNearbyPlayerKey, Main.UnitsTeleportToNearbyPlayerModifierKey)) + { + GameFiber.Sleep(500); + if (KeyExtensions.IsKeysDown(Main.UnitsTeleportToNearbyPlayerKey, Main.UnitsTeleportToNearbyPlayerModifierKey)) ForceTeleport = true; + else ShowUnitTeleportHelp(); + } + } + } + }); + Task driveToPed = null; + if (driver && driver.IsValid()) driver.Tasks.PerformDrivingManeuver(VehicleManeuver.GoForwardStraight).WaitForCompletion(500); + if (veh && veh.IsValid()) + { + while (Vector3.Distance(veh.Position, pos) > 35f) + { + veh.Repair(); + if (driveToPed == null || !driveToPed.IsActive) driveToPed = driver.Tasks.DriveToPosition(pos, MathHelper.ConvertKilometersPerHourToMetersPerSecond(60f), VehicleDrivingFlags.DriveAroundVehicles | VehicleDrivingFlags.DriveAroundObjects | VehicleDrivingFlags.AllowMedianCrossing | VehicleDrivingFlags.YieldToCrossingPedestrians); + + NativeFunction.Natives.SET_DRIVE_TASK_DRIVING_STYLE(driver, 786607); + NativeFunction.Natives.SET_DRIVER_AGGRESSIVENESS(driver, 0f); + NativeFunction.Natives.SET_DRIVER_ABILITY(driver, 1f); + GameFiber.Wait(600); + + waitCount++; + if (waitCount == 70) ShowUnitTeleportHelp(); + if (veh.Speed < 2f) drivingLoopCount++; + if (Vector3.Distance(pos, veh.Position) > Main.CoronerSpawnDistance + 70f) drivingLoopCount++; + + if (drivingLoopCount >= 33 && drivingLoopCount <= 38) + { + Vector3 SpawnPoint; + float Heading; + bool UseSpecialID = true; + float travelDistance; + int WaitCount = 0; + while (true) + { + GetVehicleSpawnPoint(pos, out SpawnPoint, out Heading, UseSpecialID); + travelDistance = NativeFunction.Natives.CALCULATE_TRAVEL_DISTANCE_BETWEEN_POINTS(SpawnPoint.X, SpawnPoint.Y, SpawnPoint.Z, Game.LocalPlayer.Character.Position.X, Game.LocalPlayer.Character.Position.Y, Game.LocalPlayer.Character.Position.Z); + + if (Vector3.Distance(Game.LocalPlayer.Character.Position, SpawnPoint) > Main.CoronerSpawnDistance - 15f) + { + if (travelDistance < Main.CoronerSpawnDistance * 4.5f) + { + Vector3 directionFromVehicleToPed1 = (Game.LocalPlayer.Character.Position - SpawnPoint); + directionFromVehicleToPed1.Normalize(); + float HeadingToPlayer = MathHelper.ConvertDirectionToHeading(directionFromVehicleToPed1); + + if (Math.Abs(MathHelper.NormalizeHeading(Heading) - MathHelper.NormalizeHeading(HeadingToPlayer)) < 150f) break; + } + } + WaitCount++; + if (WaitCount >= 400) UseSpecialID = false; + + GameFiber.Yield(); + } + + veh.Position = SpawnPoint; + veh.Heading = Heading; + drivingLoopCount = 39; + ShowUnitTeleportHelp(); + } + else if ((drivingLoopCount >= 70 || waitCount >= 110) || ForceTeleport) + { + Vector3 SpawnPoint = World.GetNextPositionOnStreet(pos.Around2D(15f)); + + int waitCounter = 0; + while ((SpawnPoint.Z - pos.Z < -3f) || (SpawnPoint.Z - pos.Z > 3f) || (Vector3.Distance(SpawnPoint, pos) > 25f)) + { + waitCounter++; + SpawnPoint = World.GetNextPositionOnStreet(pos.Around2D(15f)); + GameFiber.Yield(); + if (waitCounter >= 500) + { + SpawnPoint = pos.Around2D(15f); + break; + } + } + veh.Position = SpawnPoint; + Vector3 directionFromVehicleToPed = (pos - SpawnPoint); + directionFromVehicleToPed.Normalize(); + + float vehicleHeading = MathHelper.ConvertDirectionToHeading(directionFromVehicleToPed); + veh.Heading = vehicleHeading; + transportVanTeleported = true; + + break; + } + } + } + + ForceTeleport = true; + Game.HideHelp(); + while ((Vector3.Distance(pos, veh.Position) > 18f) && !transportVanTeleported) + { + Task parkNearSuspect = driver.Tasks.DriveToPosition(pos, 6f, VehicleDrivingFlags.FollowTraffic | VehicleDrivingFlags.DriveAroundVehicles | VehicleDrivingFlags.DriveAroundObjects | VehicleDrivingFlags.AllowMedianCrossing | VehicleDrivingFlags.YieldToCrossingPedestrians); + parkNearSuspect.WaitForCompletion(800); + transportVanTeleported = false; + if (Vector3.Distance(pos, veh.Position) > 80f) + { + Vector3 SpawnPoint = World.GetNextPositionOnStreet(pos.Around2D(12f)); + veh.Position = SpawnPoint; + } + } + GameFiber.Wait(600); + } + + internal static void ShowUnitTeleportHelp() + { + if (Main.UnitsTeleportToNearbyPlayerModifierKey is not Keys.None) Game.DisplayNotification(string.Format(Translation.GetString("TeleportUnit"), $"~{Main.UnitsTeleportToNearbyPlayerKey.GetInstructionalId()}~")); + else Game.DisplayNotification(string.Format(Translation.GetString("TeleportUnit"), $"~{Main.UnitsTeleportToNearbyPlayerKey.GetInstructionalId()}~ ~+~ ~{Main.UnitsTeleportToNearbyPlayerModifierKey.GetInstructionalId()}~")); + } + + internal static void GetVehicleSpawnPoint(Vector3 StartPoint, out Vector3 SpawnPoint1, out float Heading1, bool UseSpecialID) + { + Vector3 tmpSpawn = World.GetNextPositionOnStreet(StartPoint.Around2D(Main.CoronerSpawnDistance + 5f)); + Vector3 SpawnPoint = Vector3.Zero; + float Heading = 0; + unsafe + { + if (!UseSpecialID || !NativeFunction.Natives.GET_NTH_CLOSEST_VEHICLE_NODE_FAVOUR_DIRECTION(tmpSpawn.X, tmpSpawn.Y, tmpSpawn.Z, StartPoint.X, StartPoint.Y, StartPoint.Z, 0, out SpawnPoint, out Heading, 0, 0x40400000, 0) || !NodeExtensions.IsNodeSafe(SpawnPoint)) + { + SpawnPoint = World.GetNextPositionOnStreet(StartPoint.Around2D(Main.CoronerSpawnDistance + 5f)); + Vector3 directionFromVehicleToPed = (StartPoint - SpawnPoint); + directionFromVehicleToPed.Normalize(); + Heading = MathHelper.ConvertDirectionToHeading(directionFromVehicleToPed); + } + } + SpawnPoint1 = SpawnPoint; + Heading1 = Heading; + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Extensions/WeightedExtensions.cs b/DynamicLSPDFR/Extensions/WeightedExtensions.cs new file mode 100644 index 0000000..54e09a4 --- /dev/null +++ b/DynamicLSPDFR/Extensions/WeightedExtensions.cs @@ -0,0 +1,42 @@ +namespace DynamicLSPDFR.Extensions; + +internal static class WeightedExtensions +{ + internal static ChancePed Weighted(List list) + { + int total = 0, pick = 0; + foreach (var l in list) total += (int)l.chance; + + int rnd = RandomNumber.Instance.Next(total); + for (int i = 0; i < list.Count; i++) + { + if (rnd < list[i].chance) + { + pick = i; + break; + } + rnd -= (int)list[i].chance; + } + + return list[pick]; + } + + internal static ChanceVehicle Weighted(List list) + { + int total = 0, pick = 0; + foreach (var l in list) total += (int)l.chance; + + int rnd = RandomNumber.Instance.Next(total); + for (int i = 0; i < list.Count; i++) + { + if (rnd < list[i].chance) + { + pick = i; + break; + } + rnd -= (int)list[i].chance; + } + + return list[pick]; + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Main.cs b/DynamicLSPDFR/Main.cs index f818dd3..139b5e2 100644 --- a/DynamicLSPDFR/Main.cs +++ b/DynamicLSPDFR/Main.cs @@ -4,23 +4,40 @@ global using System.Reflection; global using System.Drawing; global using System.IO; -global using System.Resources; +global using System.IO.Compression; +global using System.Security.Cryptography; +global using System.Text; global using System.Windows.Forms; global using System.Collections.Generic; -global using System.Text; global using System.Text.RegularExpressions; global using System.Xml; +global using System.Xml.Linq; +global using DynamicLSPDFR.Backup; global using DynamicLSPDFR.Callouts; global using DynamicLSPDFR.Common; +global using static DynamicLSPDFR.Common.BackupCommon; global using DynamicLSPDFR.Debug; global using DynamicLSPDFR.Extensions; global using DynamicLSPDFR.Extensions.Random; +global using DynamicLSPDFR.Extensions.Save; global using DynamicLSPDFR.Extensions.Localization; global using DynamicLSPDFR.Modules; global using DynamicLSPDFR.Patches; global using DynamicLSPDFR.Settings; +global using DynamicLSPDFR.Units; global using DynamicLSPDFR.Update; +global using ContextMenu = DynamicLSPDFR.Modules.DynamicMenu.ContextMenu; +global using CalloutsMenu = DynamicLSPDFR.Modules.DynamicMenu.CalloutsMenu; +global using LanguageMenu = DynamicLSPDFR.Modules.DynamicMenu.LanguageMenu; +global using PylonsMenu = DynamicLSPDFR.Modules.DynamicMenu.PylonsMenu; +global using TrafficMenu = DynamicLSPDFR.Modules.DynamicMenu.TrafficMenu; +global using DynamicLSPDFR.Extensions.Save.Core.Converters; +global using DynamicLSPDFR.Extensions.Save.Core.Serializers; +global using DynamicLSPDFR.Extensions.Save.Core.Settings; +global using DynamicLSPDFR.Extensions.Save.Core.Storage; +global using FileAccess = DynamicLSPDFR.Extensions.Save.Core.Storage.FileAccess; +global using CompressionMode = DynamicLSPDFR.Extensions.CompressionMode; global using Rage; global using Rage.Native; @@ -32,9 +49,12 @@ global using LSPD_First_Response.Mod.API; global using LSPD_First_Response.Mod.Callouts; global using LSPD_First_Response.Engine.Scripting; +global using LSPD_First_Response.Mod.Menus; global using RAGENativeUI; global using RAGENativeUI.Elements; +global using RAGENativeUI.PauseMenu; global using static RAGENativeUI.Common; +global using Newtonsoft.Json; global using Newtonsoft.Json.Linq; namespace DynamicLSPDFR; @@ -44,11 +64,16 @@ internal class Main : Plugin internal const string PLUGIN_NAME = "Dynamic LSPDFR"; internal static readonly Version PLUGIN_VERSION = Assembly.GetExecutingAssembly().GetName().Version; internal static readonly string PLUGIN_VERSION_STRING = PLUGIN_VERSION.ToString(); + internal static bool HasUpdate = false; - internal static EWorldZoneCounty CurrentPlayerCountry = EWorldZoneCounty.LosSantos; + internal static ZoneCounty CurrentPlayerCountry = ZoneCounty.LosSantos; internal static Keys OpenContextMenuKey { get; set; } = Keys.O; internal static Keys OpenContextMenuModifierKey { get; set; } = Keys.None; + // internal static Keys OpenTrafficTicketsMenuKey { get; set; } = Keys.U; + // internal static Keys OpenTrafficTicketsMenuModifierKey { get; set; } = Keys.LControlKey; + internal static Keys UnitsTeleportToNearbyPlayerKey { get; set; } = Keys.U; + internal static Keys UnitsTeleportToNearbyPlayerModifierKey { get; set; } = Keys.LControlKey; internal static string OfficerName { get; set; } = "Officer"; internal static string StationName { get; set; } = ""; @@ -56,6 +81,8 @@ internal class Main : Plugin internal static Languages Language { get; set; } = Languages.English; internal static Algorithm Random { get; set; } = Algorithm.SystemRandom; + internal static float CoronerSpawnDistance { get; set; } = 120f; + /// /// LSPDFR初期化 /// @@ -70,6 +97,7 @@ public override void Initialize() /// public override void Finally() { + BackupManager.DeleteAllUnit(); #if Dev ShowPlayerPosition.Remove(); #endif @@ -83,7 +111,8 @@ private static void OnDutyStateChanged(bool OnDuty) // Stop if not duty. if (!OnDuty) { - DynamicMenu.Reset(); + BackupMenu.Reset(); + ContextMenu.Reset(); #if Dev ShowPlayerPosition.Remove(); #endif @@ -93,28 +122,38 @@ private static void OnDutyStateChanged(bool OnDuty) Translation.Load(); ClientSettings.LoadSettings(); Game.AddConsoleCommands(); - DynamicMenu.Initialize(); + ContextMenu.SetupContextMenu(); + // BackupFileLoader.Initialize(); + BackupManager.Initialize(); + BackupMenu.Initialize(); CalloutList.RegisterCallouts(); PylonsManager.Initialize(); KeyDown.StartUpdate(); RandomNumber.SetInstance(Random); + CoronerReport.Initialize(); // Overlay.Initialize(); + GameFiber.StartNew(UpdateChecker.CheckUpdate); GameFiber.StartNew(() => { while (true) { GameFiber.Yield(); - Main.CurrentPlayerCountry = Functions.GetZoneAtPosition(Game.LocalPlayer.Character.Position).County; + Main.CurrentPlayerCountry = ConvertExtensions.ConvertToBackupCounty(Functions.GetZoneAtPosition(Game.LocalPlayer.Character.Position).County); } }); + // GameFiber.StartNew(DynamicMenu.Process, "[DynamicLSPDFR] Context Menu Update Process"); + GameFiber.StartNew(BackupMenu.Process, "[DynamicLSPDFR] Backup Menu Update Process"); + #if Dev ShowPlayerPosition.SetBar(); #endif - - MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("LoadMessage"), PLUGIN_NAME), PLUGIN_NAME, new StringBuilder("Version.").Append(PLUGIN_VERSION_STRING).ToString()); - if (UpdateChecker.HasUpdate()) MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("UpdateMessage"), PLUGIN_NAME), PLUGIN_NAME, Translation.GetString("UpdateAvailable")); + GameFiber.StartNew(() => + { + GameFiber.Wait(8000); + MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("LoadMessage"), PLUGIN_NAME), PLUGIN_NAME, new StringBuilder("Version.").Append(PLUGIN_VERSION_STRING).ToString()); + }, "[DynamicLSPDFR] First Message"); } } } \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/DynamicMenu/ContextMenu.cs b/DynamicLSPDFR/Modules/DynamicMenu/ContextMenu.cs deleted file mode 100644 index fae2e4a..0000000 --- a/DynamicLSPDFR/Modules/DynamicMenu/ContextMenu.cs +++ /dev/null @@ -1,510 +0,0 @@ -namespace DynamicLSPDFR.Modules; - -internal static class DynamicMenu -{ - private const string BANNER_PATH = "DynamicLSPDFR.Resources.Images.Banner.png"; - - // Pool - private static MenuPool Pool; - - // Context Menu - private static UIMenu ContextMenu; - private static UIMenuItem LanguageSelectItem; - internal static UIMenuItem CalloutsListItem; - private static UIMenuListScrollerItem AlgorithmItem; - private static UIMenuItem TrafficControlMenuItem; - // private static UIMenuItem ConvenientToolsItem; - // private static UIMenuItem OverlayItem; - - // Language Select Menu - private static UIMenu LanguageSelectMenu; - private static UIMenuItem[] LanguageItems = new UIMenuItem[(int)Languages.Custom]; - private static UIMenuItem LanguageCustomItem; - - // Start Callouts Menu - private static UIMenu StartCalloutsListMenu; - private static UIMenuItem[] CalloutItems = new UIMenuItem[CalloutList.AllCallouts.Count]; - - // Traffic Menu - private static UIMenu TrafficControlMenu; - private static UIMenuItem PylonsItem; - // private static UIMenuItem TrafficTicketItem; - // private static UIMenuItem SpeedLimitItem; - - // Traffic Pylon Menu - internal static UIMenu PylonMenu; - private static UIMenuListScrollerItem PylonTypeItem; - internal static UIMenuCheckboxItem PylonIsFreeze; - private static UIMenuItem SetPylonPosItem; - internal static UIMenuNumericScrollerItem PylonCountItem; - private static UIMenuItem PlacePylonItem; - private static UIMenuItem ResetPylonItem; - private static UIMenuItem CarryPylonItem; - private static UIMenuListScrollerItem DeletePylonItem; - - // Speed Limit Menu - // private static UIMenu SpeedLimitMenu; - // private static UIMenuNumericScrollerItem SpeedLimitSizeItem; - // private static UIMenuItem SetSpeedLimitItem; - - // Traffic Tickets - // private static UIMenu TrafficTickets; - - // Overlay Options Menu - // private static UIMenu OverlayMenu; - // private static UIMenuItem OverlayVisibleMenuItem; - - // private static UIMenu OverlayVisibleMenu; - // private static UIMenuCheckboxItem Direction; - // private static UIMenuCheckboxItem Time; - // private static UIMenuCheckboxItem Speed; - // private static UIMenuCheckboxItem CountyName; - // private static UIMenuCheckboxItem StreetName; - - // private static UIMenu ConvenientToolsMenu; - // private static UIMenuItem GrabObjectsItem; - - // private static UIMenu GrabMenu; - // private static UIMenuItem GrabObject; - // private static UIMenuItem ReleaseObject; - // #if Dev - // private static UIMenuItem GrabVehicle; - // private static UIMenuItem ReleaseVehicle; - // #endif - - internal static void Initialize() - { - // Pool - Pool = new(); - - // Context Menu - ContextMenu = new(Translation.GetString("ContextMenuTitle"), MenuCommon.PluginInfo); - - // Language Select Menu - LanguageSelectItem = new(Translation.GetString("LanguageSelectItem"), Translation.GetString("LanguageSelectItemDesc")); - LanguageSelectMenu = new(Translation.GetString("LanguageSelectMenuTitle"), MenuCommon.PluginInfo); - for (int i = 0; i < (int)Languages.Custom; i++) - { - var lang = i; - LanguageItems[i] = new(Translation.GetString(((Languages)i).ToString().Replace("_", " ")), GetStatusText((Languages)i).ToString()); - LanguageItems[i].Activated += (m, item) => - { - Translation.SetLanguage((Languages)lang); - ClientSettings.SettingFile.Write("General", "Language", lang); - }; - } - LanguageCustomItem = new("~h~~o~Custom~s~", Translation.GetString("CustomLanguageMenuDesc")); - LanguageCustomItem.Activated += (m, i) => - { - Translation.SetLanguage(Languages.Custom); - ClientSettings.SettingFile.Write("General", "Language", (int)Languages.Custom); - }; - - LanguageSelectMenu.AddItems(LanguageItems); - LanguageSelectMenu.AddItem(LanguageCustomItem); - - // Callouts List Menu - CalloutsListItem = new(Translation.GetString("CalloutsListMenuItem"), Translation.GetString("CalloutsListMenuItemDesc")); - StartCalloutsListMenu = new(Translation.GetString("CalloutsListMenuTitle"), MenuCommon.PluginInfo); - - int j = 0; - foreach (var cl in CalloutList.AllCallouts) - { - CalloutItems[j] = new(Translation.GetString(cl.Value)); - CalloutItems[j].Activated += (m, i) => Functions.StartCallout(cl.Value); - j++; - } - StartCalloutsListMenu.AddItems(CalloutItems); - - // Algorithms Menu - AlgorithmItem = new(Translation.GetString("AlgorithmItem"), Translation.GetString("AlgorithmItemDesc"), new[] { Algorithm.SystemRandom, Algorithm.MersenneTwister, Algorithm.XorShift }); - AlgorithmItem.Activated += (m, i) => - { - RandomNumber.SetInstance(AlgorithmItem.SelectedItem); - MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("ChangeRandomNum"), Enum.GetName(typeof(Algorithm), AlgorithmItem.SelectedItem))); - ClientSettings.SettingFile.Write("General", "RandomNumberAlgorithm", (int)AlgorithmItem.SelectedItem); - }; - - // Traffic Menu - TrafficControlMenuItem = new(Translation.GetString("TrafficControlMenuItem"), Translation.GetString("TrafficControlMenuItemDesc")); - TrafficControlMenu = new(Translation.GetString("TrafficControlMenuTitle"), MenuCommon.PluginInfo); - PylonsItem = new(Translation.GetString("PylonMenuItem"), Translation.GetString("PylonMenuItemDesc")); - // SpeedLimitItem = new(Translation.GetString("SpeedLimitItem"), Translation.GetString("SpeedLimitItemDesc")); - - // Traffic Pylon Menu - PylonMenu = new(Translation.GetString("PylonMenuTitle"), MenuCommon.PluginInfo); - PylonTypeItem = new(Translation.GetString("PylonTypeItem"), Translation.GetString("PylonTypeItemDesc"), new[] { PylonType.prop_roadcone01a, PylonType.prop_roadcone01b, PylonType.prop_roadcone01c, PylonType.prop_roadcone02a, PylonType.prop_roadcone02b, PylonType.prop_roadcone02c }); - PylonTypeItem.IndexChanged += (m, oldI, newI) => - { - Pylons.SelectingPylon = PylonTypeItem.SelectedItem; - if (PylonsManager.CurrentPylons is not null) PylonsManager.CurrentPylons.UpdateGhostPylons(); - }; - PylonIsFreeze = new(Translation.GetString("PylonFreezeItem"), true, Translation.GetString("PylonFreezeItemDesc")); - SetPylonPosItem = new(Translation.GetString("StartSetPylonPos"), Translation.GetString("SetPylonPosesDesc")); - SetPylonPosItem.Activated += (m, i) => - { - switch (Pylons.status) - { - case PylonStatus.IsSetting: - SetPylonPosItem.Text = Translation.GetString("ResetPylonPos"); - Pylons.status = PylonStatus.IsSet; - Pylons.SetEndPosition(); - SetPylonPosItem.HighlightedBackColor = Color.Green; - PylonsManager.CurrentPylons = new Pylons(); - PylonsManager.CurrentPylons.ShowGhostPylons(); - PlacePylonItem.EnableItem(true); - ResetPylonItem.EnableItem(true); - if (PylonsManager.AllPylonObjects.Count is not 0) CarryPylonItem.EnableItem(true); - break; - case PylonStatus.None: - case PylonStatus.IsSet: - if (Pylons.status is PylonStatus.IsSet && Pylons.AllGhostPylons.Count > 0) PylonsManager.CurrentPylons.Reset(); - SetPylonPosItem.Text = Translation.GetString("EndSetPylonPos"); - Pylons.status = PylonStatus.IsSetting; - Pylons.SetStartPosition(); - SetPylonPosItem.HighlightedBackColor = Color.Red; - PlacePylonItem.EnableItem(false); - ResetPylonItem.EnableItem(false); - DeletePylonItem.EnableItem(false); - CarryPylonItem.EnableItem(false); - break; - } - }; - SetPylonPosItem.HighlightedBackColor = Color.Yellow; - PylonCountItem = new(Translation.GetString("PylonCountItem"), "", 1f, 19f, 1f); - PylonCountItem.Description = string.Format(Translation.GetString("PylonCountItemDesc"), PylonCountItem.Value + 1); - PylonCountItem.SliderBar = new(); - PylonCountItem.SliderBar.ForegroundColor = HudColor.Michael.GetColor(); - PylonCountItem.AllowWrapAround = false; - PylonCountItem.IndexChanged += (m, oldI, newI) => - { - PylonCountItem.Description = string.Format(Translation.GetString("PylonCountItemDesc"), PylonCountItem.Value + 1); - if (PylonsManager.CurrentPylons is not null) - { - PylonsManager.CurrentPylons.Count = PylonCountItem.Value; - PylonsManager.CurrentPylons.UpdateGhostPylons(); - } - }; - PlacePylonItem = new(Translation.GetString("PlacePylonsItem"), Translation.GetString("PlacePylonsItemDesc")); - PlacePylonItem.Activated += (m, i) => - { - if (PylonsManager.CurrentPylons is not null) - { - PylonsManager.CurrentPylons.PlacePylons(); - PylonsManager.CurrentPylons = null; - SetPylonPosItem.Text = Translation.GetString("StartSetPylonPos"); - SetPylonPosItem.HighlightedBackColor = Color.Yellow; - PlacePylonItem.EnableItem(false); - ResetPylonItem.EnableItem(false); - DeletePylonItem.EnableItem(true); - CarryPylonItem.EnableItem(true); - } - }; - PlacePylonItem.EnableItem(false); - ResetPylonItem = new(Translation.GetString("ResetPylonItem"), Translation.GetString("ResetPylonItemDesc")); - ResetPylonItem.Activated += (m, i) => - { - if (PylonsManager.CurrentPylons is not null) - { - PylonsManager.CurrentPylons.Reset(); - SetPylonPosItem.Text = Translation.GetString("StartSetPylonPos"); - SetPylonPosItem.HighlightedBackColor = Color.Yellow; - PlacePylonItem.EnableItem(false); - ResetPylonItem.EnableItem(false); - if (PylonsManager.AllPylonObjects.Count is 0) - { - DeletePylonItem.EnableItem(false); - CarryPylonItem.EnableItem(false); - } - else - { - DeletePylonItem.EnableItem(true); - CarryPylonItem.EnableItem(true); - } - } - }; - ResetPylonItem.EnableItem(false); - CarryPylonItem = new(Translation.GetString("CarryPylonItem"), Translation.GetString("CarryPylonItemDesc")); - CarryPylonItem.Activated += (m, i) => - { - PylonsManager.GrabPylon(); - if (PylonsManager.IsCarrying) - { - CarryPylonItem.Text = Translation.GetString("PlacePylonItem"); - CarryPylonItem.Description = Translation.GetString("PlacePylonItemDesc"); - } - else - { - CarryPylonItem.Text = Translation.GetString("CarryPylonItem"); - CarryPylonItem.Description = Translation.GetString("CarryPylonItemDesc"); - } - }; - CarryPylonItem.EnableItem(false); - DeletePylonItem = new(Translation.GetString("DeletePylonItem"), Translation.GetString("DeletePylonItemDesc"), new[] { Translation.GetString("NearestPylon"), Translation.GetString("AllPylon") }); - DeletePylonItem.Activated += (m, i) => - { - switch (DeletePylonItem.Index) - { - default: - case 0: - var deletePylon = PylonsManager.GetNearestPylon(PylonsManager.AllPylonObjects); - PylonsManager.AllPylonObjects.Remove(deletePylon); - if (deletePylon.Item1 && deletePylon.Item1.IsValid()) deletePylon.Item1.Delete(); - if (deletePylon.Item2 && deletePylon.Item2.IsValid()) deletePylon.Item2.Delete(); - if (PylonsManager.AllPylonObjects.Count is 0) - { - DeletePylonItem.EnableItem(false); - CarryPylonItem.EnableItem(false); - } - break; - case 1: - foreach (var p in PylonsManager.AllPylonObjects) - { - if (p.Item1 && p.Item1.IsValid()) p.Item1.Delete(); - if (p.Item2 && p.Item2.IsValid()) p.Item2.Delete(); - } - PylonsManager.AllPylonObjects.Clear(); - DeletePylonItem.EnableItem(false); - CarryPylonItem.EnableItem(false); - break; - } - }; - DeletePylonItem.EnableItem(false); - - // SpeedLimitMenu = new(Translation.GetString("SpeedLimitMenuTitle"), MenuCommon.PluginInfo); - // SpeedLimitSizeItem = new("SpeedLimitScale", "", 2f, 50f, 3f); - // SpeedLimitSizeItem.Description = string.Format(Translation.GetString("SpeedLimitScaleDescription"), SpeedLimitSizeItem.Value); - // SpeedLimitSizeItem.SliderBar = new(); - // SpeedLimitSizeItem.SliderBar.ForegroundColor = HudColor.Michael.GetColor(); - // SpeedLimitSizeItem.AllowWrapAround = true; - // SpeedLimitSizeItem.IndexChanged += (m, oldI, newI) => - // { - // SpeedLimitSizeItem.Description = string.Format(Translation.GetString("PylonCountItemDesc"), PylonCountItem.Value + 1); - // SpeedLimit.SpeedLimitScale = SpeedLimitSizeItem.Value; - // }; - - // Overlay Menu - // OverlayItem = new(Translation.GetString("OverlayOptionsItem"), Translation.GetString("OverlayOptionsItemDesc")); - // OverlayMenu = new(Translation.GetString("OverlayOptionsMenuTitle"), MenuCommon.PluginInfo); - // OverlayVisibleMenuItem = new(Translation.GetString("OverlayVisibleMenuItem"), Translation.GetString("OverlayVisibleMenuItemDesc")); - - // // Overlay Visible Menu - // OverlayVisibleMenu = new(Translation.GetString("OverlayVisibleMenuTitle"), MenuCommon.PluginInfo); - // Direction = new(Translation.GetString("OverlayDirection"), ClientSettings.Direction); - // Direction.CheckboxEvent += (m, value) => - // { - // ClientSettings.SettingFile.Write("Overlay Visible", "Direction", value); - // ClientSettings.Direction = value; - // Overlay.SwitchVisible(OverlayObjects.Direction, value); - // }; - // Time = new(Translation.GetString("OverlayTime"), ClientSettings.Time); - // Time.CheckboxEvent += (m, value) => - // { - // ClientSettings.SettingFile.Write("Overlay Visible", "Time", value); - // ClientSettings.Time = value; - // Overlay.SwitchVisible(OverlayObjects.Time, value); - // }; - // Speed = new(Translation.GetString("OverlaySpeed"), ClientSettings.Speed); - // Speed.CheckboxEvent += (m, value) => - // { - // ClientSettings.SettingFile.Write("Overlay Visible", "Speed", value); - // ClientSettings.Speed = value; - // Overlay.SwitchVisible(OverlayObjects.Speed, value); - // }; - // CountyName = new(Translation.GetString("OverlayCountyName"), ClientSettings.CountyName); - // CountyName.CheckboxEvent += (m, value) => - // { - // ClientSettings.SettingFile.Write("Overlay Visible", "CountyName", value); - // ClientSettings.CountyName = value; - // Overlay.SwitchVisible(OverlayObjects.CountyName, value); - // }; - // StreetName = new(Translation.GetString("OverlayStreetName"), ClientSettings.StreetName); - // StreetName.CheckboxEvent += (m, value) => - // { - // ClientSettings.SettingFile.Write("Overlay Visible", "StreetName", value); - // ClientSettings.StreetName = value; - // Overlay.SwitchVisible(OverlayObjects.StreetName, value); - // }; - - // Add to context menu - Pool.Add(ContextMenu, LanguageSelectMenu, StartCalloutsListMenu, TrafficControlMenu, PylonMenu); - - ContextMenu.AddItems(LanguageSelectItem, CalloutsListItem, AlgorithmItem, TrafficControlMenuItem); - ContextMenu.BindMenuToItem(LanguageSelectMenu, LanguageSelectItem); - ContextMenu.BindMenuToItem(StartCalloutsListMenu, CalloutsListItem); - ContextMenu.BindMenuToItem(TrafficControlMenu, TrafficControlMenuItem); - // ContextMenu.BindMenuToItem(OverlayMenu, OverlayItem); - - TrafficControlMenu.AddItems(PylonsItem); - TrafficControlMenu.BindMenuToItem(PylonMenu, PylonsItem); - PylonMenu.AddItems(PylonTypeItem, PylonIsFreeze, SetPylonPosItem, PylonCountItem, ResetPylonItem, PlacePylonItem, CarryPylonItem, DeletePylonItem); - - // OverlayMenu.AddItems(OverlayVisibleMenuItem); - // OverlayMenu.BindMenuToItem(OverlayVisibleMenu, OverlayVisibleMenuItem); - - // OverlayVisibleMenu.AddItems(Direction, Time, Speed, CountyName, StreetName); - - // ConvenientToolsMenu = new(Translation.GetString("ConvenientToolsMenuTitle"), MenuCommon.PluginInfo); - // GrabObjectsItem = new(Translation.GetString("GrabObjects"), Translation.GetString("GrabObjectsDesc")); - - // GrabMenu = new(Translation.GetString("GrabMenuTitle"), MenuCommon.PluginInfo); - // GrabObject = new(Translation.GetString("GrabObject"), Translation.GetString("GrabObjectDesc")); - // GrabObject.Activated += (m, i) => - // { - - // }; - - ContextMenu.MouseControlsEnabled = false; - ContextMenu.ControlDisablingEnabled = false; - LanguageSelectMenu.MouseControlsEnabled = false; - LanguageSelectMenu.ControlDisablingEnabled = false; - StartCalloutsListMenu.MouseControlsEnabled = false; - StartCalloutsListMenu.ControlDisablingEnabled = false; - TrafficControlMenu.MouseControlsEnabled = false; - TrafficControlMenu.ControlDisablingEnabled = false; - PylonMenu.MouseControlsEnabled = false; - PylonMenu.ControlDisablingEnabled = false; - // OverlayMenu.MouseControlsEnabled = false; - // OverlayMenu.ControlDisablingEnabled = false; - // OverlayVisibleMenu.MouseControlsEnabled = false; - // OverlayVisibleMenu.ControlDisablingEnabled = false; - -#if Dev - Pool.SetBannerType(Game.CreateTextureFromFile(WriteFileFromResources(Assembly.GetExecutingAssembly(), BANNER_PATH))); -#endif - Game.FrameRender += (s, e) => - { -#if Dev - Pool.DrawBanners(e.Graphics); -#endif - DynamicMenu.Process(s, e); - }; - } - - internal static void UpdateTranslate() - { - ContextMenu.TitleText = Translation.GetString("ContextMenuTitle"); - - LanguageSelectItem.Text = Translation.GetString("LanguageSelectItem"); - LanguageSelectItem.Description = Translation.GetString("LanguageSelectItemDesc"); - LanguageSelectMenu.TitleText = Translation.GetString("LanguageSelectMenuTitle"); - - CalloutsListItem.Text = Translation.GetString("CalloutsListMenuItem"); - CalloutsListItem.Description = Translation.GetString("CalloutsListMenuItemDesc"); - StartCalloutsListMenu.TitleText = Translation.GetString("CalloutsListMenuTitle"); - - AlgorithmItem.Text = Translation.GetString("AlgorithmItem"); - AlgorithmItem.Description = Translation.GetString("AlgorithmItemDesc"); - - TrafficControlMenuItem.Text = Translation.GetString("TrafficControlMenuItem"); - TrafficControlMenuItem.Description = Translation.GetString("TrafficControlMenuItemDesc"); - TrafficControlMenuItem.Text = Translation.GetString("TrafficControlMenuItem"); - TrafficControlMenuItem.Description = Translation.GetString("TrafficControlMenuItemDesc"); - TrafficControlMenu.TitleText = Translation.GetString("TrafficControlMenuTitle"); - PylonsItem.Text = Translation.GetString("PylonMenuItem"); - PylonsItem.Description = Translation.GetString("PylonMenuItemDesc"); - - PylonMenu.TitleText = Translation.GetString("PylonMenuTitle"); - PylonIsFreeze.Text = Translation.GetString("PylonFreezeItem"); - PylonIsFreeze.Description = Translation.GetString("PylonFreezeItemDesc"); - PylonTypeItem.Text = Translation.GetString("PylonTypeItem"); - PylonTypeItem.Description = Translation.GetString("PylonTypeItemDesc"); - SetPylonPosItem.Description = Translation.GetString("SetPylonPosesDesc"); - - ResetPylonItem.Text = Translation.GetString("ResetPylonItem"); - ResetPylonItem.Description = Translation.GetString("ResetPylonItemDesc"); - DeletePylonItem.Text = Translation.GetString("DeletePylonItem"); - DeletePylonItem.Description = Translation.GetString("DeletePylonItemDesc"); - DeletePylonItem.Items = new[] { Translation.GetString("NearestPylon"), Translation.GetString("AllPylon") }; - - switch (Pylons.status) - { - case PylonStatus.None: - SetPylonPosItem.Text = Translation.GetString("StartSetPylonPos"); - break; - case PylonStatus.IsSetting: - SetPylonPosItem.Text = Translation.GetString("EndSetPylonPos"); - break; - case PylonStatus.IsSet: - SetPylonPosItem.Text = Translation.GetString("ResetPylonPos"); - break; - } - - if (PylonsManager.IsCarrying) - { - CarryPylonItem.Text = Translation.GetString("PlacePylonItem"); - CarryPylonItem.Description = Translation.GetString("PlacePylonItemDesc"); - } - else - { - CarryPylonItem.Text = Translation.GetString("CarryPylonItem"); - CarryPylonItem.Description = Translation.GetString("CarryPylonItemDesc"); - } - - PylonCountItem.Text = Translation.GetString("PylonCountItem"); - PylonCountItem.Description = string.Format(Translation.GetString("PylonCountItemDesc"), PylonCountItem.Value); - PlacePylonItem.Text = Translation.GetString("PlacePylonsItem"); - PlacePylonItem.Description = Translation.GetString("PlacePylonsItemDesc"); - - for (int i = 0; i < CalloutItems.Length; i++) CalloutItems[i].Text = Translation.GetString(CalloutList.AllCallouts.ElementAt(i).Value); - for (int i = 0; i < (int)Languages.Custom; i++) LanguageItems[i].Description = GetStatusText((Languages)i).ToString(); - LanguageCustomItem.Description = Translation.GetString("CustomLanguageMenuDesc"); - } - - // internal static void SwitchVisible() - // { - - - // if (ContextMenu.Visible) ContextMenu.Visible = false; - // else if (LanguageSelectMenu.Visible) LanguageSelectMenu.Visible = false; - // else if (StartCalloutsListMenu.Visible) StartCalloutsListMenu.Visible = false; - // else if (TrafficControlMenu.Visible) TrafficControlMenu.Visible = false; - // else if (PylonMenu.Visible) PylonMenu.Visible = false; - // else if (!UIMenu.IsAnyMenuVisible) ContextMenu.Visible = true; - // } - - internal static void Process(object sender, GraphicsEventArgs e) - { - Pool.ProcessMenus(); - - if (KeyExtensions.IsKeysDown(Main.OpenContextMenuKey, Main.OpenContextMenuModifierKey)) - { - if (Pool.IsAnyMenuOpen()) - { - Pool.CloseAllMenus(); - Logger.Info("Menu Open", "DynamicLSPDFR"); - } - else if (!Pool.IsAnyMenuOpen()) - { - ContextMenu.Visible = true; - Logger.Info("Menu Open", "DynamicLSPDFR"); - } - } - } - - internal static void Reset() - { - Pool.Clear(); - Game.FrameRender -= (s, e) => - { - Pool.DrawBanners(e.Graphics); - DynamicMenu.Process(s, e); - }; - } - - internal static void EnableItem(this UIMenuItem item, bool enable) - { - if (enable) - { - item.Enabled = true; - item.HighlightedBackColor = HudColor.White.GetColor(); - } - else - { - item.Enabled = false; - item.HighlightedBackColor = HudColor.MenuGrey.GetColor(); - } - } - - private static StringBuilder GetStatusText(Languages lang) => new StringBuilder(Translation.GetString("LanguageMenuDesc")).Append("\n").Append(string.Format(Translation.GetString("TranslateStatus"), Translation.GetTranslateStatus(lang))); -} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/KeyDownUpdate.cs b/DynamicLSPDFR/Modules/KeyDownUpdate.cs index c8f7abb..2bd0a73 100644 --- a/DynamicLSPDFR/Modules/KeyDownUpdate.cs +++ b/DynamicLSPDFR/Modules/KeyDownUpdate.cs @@ -16,5 +16,6 @@ internal static void StartUpdate() internal static void KeyDownUpdate() { + if (KeyExtensions.IsKeysDown(Keys.K)) Coroner.CoronerMain(); } } \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menu/BackupMenu.cs b/DynamicLSPDFR/Modules/Menu/BackupMenu.cs new file mode 100644 index 0000000..ab5cfc6 --- /dev/null +++ b/DynamicLSPDFR/Modules/Menu/BackupMenu.cs @@ -0,0 +1,67 @@ +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(BackupManager.AllBackupUnitData[UnitType.LocalPatrol], ResponseType.Code2, UnitType.LocalPatrol); + + 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 Process() + { + while (true) + { + GameFiber.Yield(); + Pool.ProcessMenus(); + + if (KeyExtensions.IsKeysDown(Keys.I)) + { + if (Pool.IsAnyMenuOpen()) + { + Pool.CloseAllMenus(); + Logger.Info("Menu Open"); + } + else if (!Pool.IsAnyMenuOpen()) + { + BackupMenuMain.Visible = true; + Logger.Info("Menu Open"); + } + } + } + } + + internal static void Reset() + { + Pool.Clear(); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Callouts/CalloutsMenu.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Callouts/CalloutsMenu.cs new file mode 100644 index 0000000..564975a --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Callouts/CalloutsMenu.cs @@ -0,0 +1,21 @@ +namespace DynamicLSPDFR.Modules.DynamicMenu; + +internal static class CalloutsMenu +{ + internal static UIMenu Callouts; + internal static UIMenuItem[] AllCallouts = new UIMenuItem[CalloutList.AllCallouts.Count]; + + internal static void Initialize() + { + Callouts = new(Translation.GetString("CalloutsListMenuTitle"), MenuCommon.PluginInfo); + + int j = 0; + foreach (var cl in CalloutList.AllCallouts) + { + AllCallouts[j] = new(Translation.GetString(cl.Value)); + AllCallouts[j].Activated += (m, i) => Functions.StartCallout(cl.Value); + j++; + } + Callouts.AddItems(AllCallouts); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Context.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Context.cs new file mode 100644 index 0000000..23bafe2 --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Context.cs @@ -0,0 +1,188 @@ +namespace DynamicLSPDFR.Modules.DynamicMenu; + +internal static class ContextMenu +{ + internal static MenuPool Pool; + internal static UIMenu Context; + private static UIMenuItem LanguageSelect; + internal static UIMenuItem CalloutsList; + private static UIMenuListScrollerItem SetAlgorithm; + private static UIMenuItem TrafficControl; + // private static UIMenuItem Overlay; + + internal static void SetupContextMenu() + { + ContextMenu.Initialize(); + CalloutsMenu.Initialize(); + ContextMenu.Initialize(); + CalloutsMenu.Initialize(); + LanguageMenu.Initialize(); + PylonsMenu.Initialize(); + TrafficMenu.Initialize(); + ContextMenu.PostfixInitialize(); + } + + internal static void Initialize() + { + Pool = new(); + Context = new(Translation.GetString("ContextMenuTitle"), MenuCommon.PluginInfo); + LanguageSelect = new(Translation.GetString("LanguageSelectItem"), Translation.GetString("LanguageSelectItemDesc")); + CalloutsList = new(Translation.GetString("CalloutsListMenuItem"), Translation.GetString("CalloutsListMenuItemDesc")); + SetAlgorithm = new(Translation.GetString("AlgorithmItem"), Translation.GetString("AlgorithmItemDesc"), new[] { Algorithm.SystemRandom, Algorithm.MersenneTwister, Algorithm.XorShift }); + SetAlgorithm.Activated += (m, i) => + { + RandomNumber.SetInstance(SetAlgorithm.SelectedItem); + MessageExtensions.SendMessageInGame(string.Format(Translation.GetString("ChangeRandomNum"), Enum.GetName(typeof(Algorithm), SetAlgorithm.SelectedItem))); + ClientSettings.SettingFile.Write("General", "RandomNumberAlgorithm", (int)SetAlgorithm.SelectedItem); + }; + TrafficControl = new(Translation.GetString("TrafficControlMenuItem"), Translation.GetString("TrafficControlMenuItemDesc")); + // Overlay = new(Translation.GetString("OverlayMenuItem"), Translation.GetString("OverlayMenuItemDesc")); + } + + internal static void PostfixInitialize() + { + Pool.Add(Context, LanguageMenu.LanguageSelect, CalloutsMenu.Callouts, TrafficMenu.TrafficControl, PylonsMenu.PylonMenu); + + Context.AddItems(LanguageSelect, CalloutsList, SetAlgorithm, TrafficControl); + Context.BindMenuToItem(LanguageMenu.LanguageSelect, LanguageSelect); + Context.BindMenuToItem(CalloutsMenu.Callouts, CalloutsList); + Context.BindMenuToItem(TrafficMenu.TrafficControl, TrafficControl); + // Context.BindMenuToItem(OverlayMenu.Overlay, Overlay); + + TrafficMenu.TrafficControl.AddItems(TrafficMenu.Pylons); + TrafficMenu.TrafficControl.BindMenuToItem(PylonsMenu.PylonMenu, TrafficMenu.Pylons); + PylonsMenu.PylonMenu.AddItems(PylonsMenu.PylonsType, PylonsMenu.PylonIsFreeze, PylonsMenu.SetPylonPos, PylonsMenu.PylonCount, PylonsMenu.ResetPylon, PylonsMenu.PlacePylon, PylonsMenu.CarryPylon, PylonsMenu.DeletePylon); + + Context.MouseControlsEnabled = false; + Context.ControlDisablingEnabled = false; + LanguageMenu.LanguageSelect.MouseControlsEnabled = false; + LanguageMenu.LanguageSelect.ControlDisablingEnabled = false; + CalloutsMenu.Callouts.MouseControlsEnabled = false; + CalloutsMenu.Callouts.ControlDisablingEnabled = false; + TrafficMenu.TrafficControl.MouseControlsEnabled = false; + TrafficMenu.TrafficControl.ControlDisablingEnabled = false; + PylonsMenu.PylonMenu.MouseControlsEnabled = false; + PylonsMenu.PylonMenu.ControlDisablingEnabled = false; + // OverlayMenu.Overlay.MouseControlsEnabled = false; + // OverlayMenu.Overlay.ControlDisablingEnabled = false; + +#if Dev + Pool.SetBannerType(Game.CreateTextureFromFile(WriteFileFromResources(Assembly.GetExecutingAssembly(), MenuCommon.BANNER_PATH))); +#endif + Game.FrameRender += (s, e) => + { +#if Dev + Pool.DrawBanners(e.Graphics); +#endif + Process(s, e); + }; + } + + internal static void UpdateTranslate() + { + Context.TitleText = Translation.GetString("ContextMenuTitle"); + LanguageMenu.LanguageSelect.TitleText = Translation.GetString("LanguageSelectMenuTitle"); + CalloutsMenu.Callouts.TitleText = Translation.GetString("CalloutsListMenuTitle"); + TrafficMenu.TrafficControl.TitleText = Translation.GetString("TrafficControlMenuTitle"); + PylonsMenu.PylonMenu.TitleText = Translation.GetString("PylonMenuTitle"); + // OverlayMenu.Overlay.TitleText = Translation.GetString("OverlayMenuTitle"); + + LanguageSelect.Text = Translation.GetString("LanguageSelectItem"); + LanguageSelect.Description = Translation.GetString("LanguageSelectItemDesc"); + CalloutsList.Text = Translation.GetString("CalloutsListMenuItem"); + CalloutsList.Description = Translation.GetString("CalloutsListMenuItemDesc"); + SetAlgorithm.Text = Translation.GetString("AlgorithmItem"); + SetAlgorithm.Description = Translation.GetString("AlgorithmItemDesc"); + TrafficControl.Text = Translation.GetString("TrafficControlMenuItem"); + TrafficControl.Description = Translation.GetString("TrafficControlMenuItemDesc"); + TrafficMenu.Pylons.Text = Translation.GetString("PylonMenuItem"); + TrafficMenu.Pylons.Description = Translation.GetString("PylonMenuItemDesc"); + PylonsMenu.PylonIsFreeze.Text = Translation.GetString("PylonFreezeItem"); + PylonsMenu.PylonIsFreeze.Description = Translation.GetString("PylonFreezeItemDesc"); + PylonsMenu.PylonsType.Text = Translation.GetString("PylonTypeItem"); + PylonsMenu.PylonsType.Description = Translation.GetString("PylonTypeItemDesc"); + PylonsMenu.SetPylonPos.Description = Translation.GetString("SetPylonPosesDesc"); + PylonsMenu.ResetPylon.Text = Translation.GetString("ResetPylonItem"); + PylonsMenu.ResetPylon.Description = Translation.GetString("ResetPylonItemDesc"); + PylonsMenu.DeletePylon.Text = Translation.GetString("DeletePylonItem"); + PylonsMenu.DeletePylon.Description = Translation.GetString("DeletePylonItemDesc"); + PylonsMenu.DeletePylon.Items = new[] { Translation.GetString("NearestPylon"), Translation.GetString("AllPylon") }; + + switch (Pylons.status) + { + case PylonStatus.None: + PylonsMenu.SetPylonPos.Text = Translation.GetString("StartSetPylonPos"); + break; + case PylonStatus.IsSetting: + PylonsMenu.SetPylonPos.Text = Translation.GetString("EndSetPylonPos"); + break; + case PylonStatus.IsSet: + PylonsMenu.SetPylonPos.Text = Translation.GetString("ResetPylonPos"); + break; + } + + if (PylonsManager.IsCarrying) + { + PylonsMenu.CarryPylon.Text = Translation.GetString("PlacePylonItem"); + PylonsMenu.CarryPylon.Description = Translation.GetString("PlacePylonItemDesc"); + } + else + { + PylonsMenu.CarryPylon.Text = Translation.GetString("CarryPylonItem"); + PylonsMenu.CarryPylon.Description = Translation.GetString("CarryPylonItemDesc"); + } + + PylonsMenu.PylonCount.Text = Translation.GetString("PylonCountItem"); + PylonsMenu.PylonCount.Description = string.Format(Translation.GetString("PylonCountItemDesc"), PylonsMenu.PylonCount.Value); + PylonsMenu.PlacePylon.Text = Translation.GetString("PlacePylonsItem"); + PylonsMenu.PlacePylon.Description = Translation.GetString("PlacePylonsItemDesc"); + + for (int i = 0; i < CalloutsMenu.AllCallouts.Length; i++) CalloutsMenu.AllCallouts[i].Text = Translation.GetString(CalloutList.AllCallouts.ElementAt(i).Value); + for (int i = 0; i < (int)Languages.Custom; i++) LanguageMenu.AllLanguages[i].Description = MenuCommon.GetStatusText((Languages)i); + + LanguageMenu.LanguageCustom.Description = Translation.GetString("CustomLanguageMenuDesc"); + } + + internal static void Process(object sender, GraphicsEventArgs e) + { + Pool.ProcessMenus(); + + if (KeyExtensions.IsKeysDown(Main.OpenContextMenuKey, Main.OpenContextMenuModifierKey)) + { + if (Pool.IsAnyMenuOpen()) + { + Pool.CloseAllMenus(); + Logger.Info("Menu Close", "DynamicLSPDFR"); + } + else if (!Pool.IsAnyMenuOpen()) + { + Context.Visible = true; + Logger.Info("Menu Open", "DynamicLSPDFR"); + } + } + } + + internal static void Reset() + { + Pool.Clear(); + Game.FrameRender -= (s, e) => + { + Pool.DrawBanners(e.Graphics); + Process(s, e); + }; + } + + internal static void EnableItem(this UIMenuItem item, bool enable) + { + if (enable) + { + item.Enabled = true; + item.HighlightedBackColor = HudColor.White.GetColor(); + } + else + { + item.Enabled = false; + item.HighlightedBackColor = HudColor.MenuGrey.GetColor(); + } + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Language/LanguageMenu.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Language/LanguageMenu.cs new file mode 100644 index 0000000..291de32 --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Language/LanguageMenu.cs @@ -0,0 +1,32 @@ +namespace DynamicLSPDFR.Modules.DynamicMenu; + +internal static class LanguageMenu +{ + internal static UIMenu LanguageSelect; + internal static UIMenuItem[] AllLanguages = new UIMenuItem[(int)Languages.Custom]; + internal static UIMenuItem LanguageCustom; + + internal static void Initialize() + { + LanguageSelect = new(Translation.GetString("LanguageSelectMenuTitle"), MenuCommon.PluginInfo); + for (int i = 0; i < (int)Languages.Custom; i++) + { + var lang = i; + AllLanguages[i] = new(Translation.GetString(((Languages)i).ToString().Replace("_", " ")), MenuCommon.GetStatusText((Languages)i)); + AllLanguages[i].Activated += (m, item) => + { + Translation.SetLanguage((Languages)lang); + ClientSettings.SettingFile.Write("General", "Language", lang); + }; + } + LanguageCustom = new("~h~~o~Custom~s~", Translation.GetString("CustomLanguageMenuDesc")); + LanguageCustom.Activated += (m, i) => + { + Translation.SetLanguage(Languages.Custom); + ClientSettings.SettingFile.Write("General", "Language", (int)Languages.Custom); + }; + + LanguageSelect.AddItems(AllLanguages); + LanguageSelect.AddItem(LanguageCustom); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Overlay.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Overlay.cs new file mode 100644 index 0000000..417433c --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Overlay.cs @@ -0,0 +1,14 @@ +// namespace DynamicLSPDFR.Modules.DynamicMenu; + +// internal static class OverlayMenu +// { +// internal static UIMenu Overlay; +// internal static UIMenuItem Visible; +// internal static UIMenuItem Positions; + +// internal static void Initialize() +// { +// Overlay = new(Translation.GetString("OverlayMenuTitle"), MenuCommon.PluginInfo); +// Visible = new(Translation.GetString("PylonMenuItem"), Translation.GetString("PylonMenuItemDesc")); +// } +// } \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Position/Position.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Position/Position.cs new file mode 100644 index 0000000..dfe78d8 --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Position/Position.cs @@ -0,0 +1,29 @@ +// namespace DynamicLSPDFR.Modules; + +// internal static class OverlayPositionMenu +// { +// internal static UIMenu OverlayPosition; +// internal static UIMenuListScrollerItem Item; +// internal static UIMenuNumericScrollerItem X; +// internal static UIMenuNumericScrollerItem Y; + +// internal static void Initialize() +// { +// OverlayPosition = new(Translation.GetString(""), MenuCommon.PluginInfo); +// Item = new(Translation.GetString(""), Translation.GetString(""), new[] { OverlayObjects.Direction, OverlayObjects.Time, OverlayObjects.Speed, OverlayObjects.SpeedLimit, OverlayObjects.CountyName, OverlayObjects.StreetName }); +// X = new(Translation.GetString(""), Translation.GetString(""), 0.1f, 3.0f, 0.1f); +// X.Activated += (m, i) => +// { + +// }; +// Y = new(Translation.GetString(""), Translation.GetString("")); +// Y.Activated += (m, i) => +// { + +// }; +// Item.IndexChanged += (s, oldI, newI) => +// { +// X.Index = Overlay.GetPositionOnScreen((OverlayObjects)newI).Item1; +// }; +// } +// } \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Visible/Visible.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Overlay/Visible/Visible.cs new file mode 100644 index 0000000..e69de29 diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Traffic/Pylons/Pylons.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Traffic/Pylons/Pylons.cs new file mode 100644 index 0000000..eee7d70 --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Traffic/Pylons/Pylons.cs @@ -0,0 +1,156 @@ +namespace DynamicLSPDFR.Modules.DynamicMenu; + +internal static class PylonsMenu +{ + internal static UIMenu PylonMenu; + internal static UIMenuListScrollerItem PylonsType; + internal static UIMenuCheckboxItem PylonIsFreeze; + internal static UIMenuItem SetPylonPos; + internal static UIMenuNumericScrollerItem PylonCount; + internal static UIMenuItem PlacePylon; + internal static UIMenuItem ResetPylon; + internal static UIMenuItem CarryPylon; + internal static UIMenuListScrollerItem DeletePylon; + + internal static void Initialize() + { + PylonMenu = new(Translation.GetString("PylonMenuTitle"), MenuCommon.PluginInfo); + PylonsType = new(Translation.GetString("PylonTypeItem"), Translation.GetString("PylonTypeItemDesc"), new[] { PylonType.prop_roadcone01a, PylonType.prop_roadcone01b, PylonType.prop_roadcone01c, PylonType.prop_roadcone02a, PylonType.prop_roadcone02b, PylonType.prop_roadcone02c }); + PylonsType.IndexChanged += (m, oldI, newI) => + { + Pylons.SelectingPylon = PylonsType.SelectedItem; + if (PylonsManager.CurrentPylons is not null) PylonsManager.CurrentPylons.UpdateGhostPylons(); + }; + PylonIsFreeze = new(Translation.GetString("PylonFreezeItem"), true, Translation.GetString("PylonFreezeItemDesc")); + SetPylonPos = new(Translation.GetString("StartSetPylonPos"), Translation.GetString("SetPylonPosesDesc")); + SetPylonPos.Activated += (m, i) => + { + switch (Pylons.status) + { + case PylonStatus.IsSetting: + SetPylonPos.Text = Translation.GetString("ResetPylonPos"); + Pylons.status = PylonStatus.IsSet; + Pylons.SetEndPosition(); + SetPylonPos.HighlightedBackColor = Color.Green; + PylonsManager.CurrentPylons = new Pylons(); + PylonsManager.CurrentPylons.ShowGhostPylons(); + PlacePylon.EnableItem(true); + ResetPylon.EnableItem(true); + if (PylonsManager.AllPylonObjects.Count is not 0) CarryPylon.EnableItem(true); + break; + case PylonStatus.None: + case PylonStatus.IsSet: + if (Pylons.status is PylonStatus.IsSet && Pylons.AllGhostPylons.Count > 0) PylonsManager.CurrentPylons.Reset(); + SetPylonPos.Text = Translation.GetString("EndSetPylonPos"); + Pylons.status = PylonStatus.IsSetting; + Pylons.SetStartPosition(); + SetPylonPos.HighlightedBackColor = Color.Red; + PlacePylon.EnableItem(false); + ResetPylon.EnableItem(false); + DeletePylon.EnableItem(false); + CarryPylon.EnableItem(false); + break; + } + }; + SetPylonPos.HighlightedBackColor = Color.Yellow; + PylonCount = new(Translation.GetString("PylonCount"), "", 1f, 19f, 1f); + PylonCount.Description = string.Format(Translation.GetString("PylonCountDesc"), PylonCount.Value + 1); + PylonCount.SliderBar = new(); + PylonCount.SliderBar.ForegroundColor = HudColor.Michael.GetColor(); + PylonCount.AllowWrapAround = false; + PylonCount.IndexChanged += (m, oldI, newI) => + { + PylonCount.Description = string.Format(Translation.GetString("PylonCountDesc"), PylonCount.Value + 1); + if (PylonsManager.CurrentPylons is not null) + { + PylonsManager.CurrentPylons.Count = PylonCount.Value; + PylonsManager.CurrentPylons.UpdateGhostPylons(); + } + }; + PlacePylon = new(Translation.GetString("PlacePylonsItem"), Translation.GetString("PlacePylonsItemDesc")); + PlacePylon.Activated += (m, i) => + { + if (PylonsManager.CurrentPylons is not null) + { + PylonsManager.CurrentPylons.PlacePylons(); + PylonsManager.CurrentPylons = null; + SetPylonPos.Text = Translation.GetString("StartSetPylonPos"); + SetPylonPos.HighlightedBackColor = Color.Yellow; + PlacePylon.EnableItem(false); + ResetPylon.EnableItem(false); + DeletePylon.EnableItem(true); + CarryPylon.EnableItem(true); + } + }; + PlacePylon.EnableItem(false); + ResetPylon = new(Translation.GetString("ResetPylon"), Translation.GetString("ResetPylonDesc")); + ResetPylon.Activated += (m, i) => + { + if (PylonsManager.CurrentPylons is not null) + { + PylonsManager.CurrentPylons.Reset(); + SetPylonPos.Text = Translation.GetString("StartSetPylonPos"); + SetPylonPos.HighlightedBackColor = Color.Yellow; + PlacePylon.EnableItem(false); + ResetPylon.EnableItem(false); + if (PylonsManager.AllPylonObjects.Count is 0) + { + DeletePylon.EnableItem(false); + CarryPylon.EnableItem(false); + } + else + { + DeletePylon.EnableItem(true); + CarryPylon.EnableItem(true); + } + } + }; + ResetPylon.EnableItem(false); + CarryPylon = new(Translation.GetString("CarryPylon"), Translation.GetString("CarryPylonDesc")); + CarryPylon.Activated += (m, i) => + { + PylonsManager.GrabPylon(); + if (PylonsManager.IsCarrying) + { + CarryPylon.Text = Translation.GetString("PlacePylon"); + CarryPylon.Description = Translation.GetString("PlacePylonDesc"); + } + else + { + CarryPylon.Text = Translation.GetString("CarryPylon"); + CarryPylon.Description = Translation.GetString("CarryPylonDesc"); + } + }; + CarryPylon.EnableItem(false); + DeletePylon = new(Translation.GetString("DeletePylon"), Translation.GetString("DeletePylonDesc"), new[] { Translation.GetString("NearestPylon"), Translation.GetString("AllPylon") }); + DeletePylon.Activated += (m, i) => + { + switch (DeletePylon.Index) + { + default: + case 0: + var deletePylon = PylonsManager.GetNearestPylon(PylonsManager.AllPylonObjects); + PylonsManager.AllPylonObjects.Remove(deletePylon); + if (deletePylon.Item1 && deletePylon.Item1.IsValid()) deletePylon.Item1.Delete(); + if (deletePylon.Item2 && deletePylon.Item2.IsValid()) deletePylon.Item2.Delete(); + if (PylonsManager.AllPylonObjects.Count is 0) + { + DeletePylon.EnableItem(false); + CarryPylon.EnableItem(false); + } + break; + case 1: + foreach (var p in PylonsManager.AllPylonObjects) + { + if (p.Item1 && p.Item1.IsValid()) p.Item1.Delete(); + if (p.Item2 && p.Item2.IsValid()) p.Item2.Delete(); + } + PylonsManager.AllPylonObjects.Clear(); + DeletePylon.EnableItem(false); + CarryPylon.EnableItem(false); + break; + } + }; + DeletePylon.EnableItem(false); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/DynamicMenu/Traffic/Traffic.cs b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Traffic/Traffic.cs new file mode 100644 index 0000000..61d0e6d --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/DynamicMenu/Traffic/Traffic.cs @@ -0,0 +1,13 @@ +namespace DynamicLSPDFR.Modules.DynamicMenu; + +internal static class TrafficMenu +{ + internal static UIMenu TrafficControl; + internal static UIMenuItem Pylons; + + internal static void Initialize() + { + TrafficControl = new(Translation.GetString("TrafficControlMenuTitle"), MenuCommon.PluginInfo); + Pylons = new(Translation.GetString("PylonMenuItem"), Translation.GetString("PylonMenuItemDesc")); + } +} \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Menues/TrafficTickets/TrafficTickets.cs b/DynamicLSPDFR/Modules/Menues/TrafficTickets/TrafficTickets.cs new file mode 100644 index 0000000..7732f1a --- /dev/null +++ b/DynamicLSPDFR/Modules/Menues/TrafficTickets/TrafficTickets.cs @@ -0,0 +1,8 @@ +// namespace DynamicLSPDFR.Modules; + +// internal static class TrafficTicketsMenu +// { +// private static UIMenu TicketMenu; +// private static UIMenuListItem FineItem; +// private static List FineList = new(); +// } \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Overlay.cs b/DynamicLSPDFR/Modules/Overlay.cs index 20fea02..931537f 100644 --- a/DynamicLSPDFR/Modules/Overlay.cs +++ b/DynamicLSPDFR/Modules/Overlay.cs @@ -5,22 +5,21 @@ // Direction, // Time, // Speed, +// SpeedLimit, // CountyName, // StreetName, // } // internal static class Overlay // { -// internal static ResRectangle directionBackground, timeBackground, speedBackground, countyBackground, streetBackground; -// internal static ResText directionText, timeText, speedText, countyText, streetText; +// internal static ResText directionText, timeText, speedText, speedLimitText, countyText, streetText; // internal static void Initialize() // { // var screen = UIMenu.GetScreenResolutionMantainRatio(); -// directionBackground = new(new(351, (int)(screen.Height - 125)), new(180, 70), Color.FromArgb(200, Color.Black)); // directionText = new("", new(438, (int)(screen.Height - 125)), 1f, Color.White, EFont.ChaletLondon, ResText.Alignment.Centered); -// timeBackground = new(new()); +// // timeBackground = new(new()); // Game.FrameRender += GameOnFrameRender; // } @@ -30,13 +29,33 @@ // switch (obj) // { // case OverlayObjects.Direction: -// directionBackground.Enabled = visible; // directionText.Enabled = visible; // break; // case OverlayObjects.Time: // break; // case OverlayObjects.Speed: // break; +// case OverlayObjects.SpeedLimit: +// break; +// case OverlayObjects.CountyName: +// break; +// case OverlayObjects.StreetName: +// break; +// } +// } + +// internal static void UpdatePosAndSize(OverlayObjects obj) +// { +// switch (obj) +// { +// case OverlayObjects.Direction: +// break; +// case OverlayObjects.Time: +// break; +// case OverlayObjects.Speed: +// break; +// case OverlayObjects.SpeedLimit: +// break; // case OverlayObjects.CountyName: // break; // case OverlayObjects.StreetName: @@ -44,13 +63,26 @@ // } // } +// internal static (float, float) GetPositionOnScreen(OverlayObjects obj) +// { +// switch (obj) +// { +// case OverlayObjects.Direction: return (directionText.Position.X, directionText.Position.Y); +// case OverlayObjects.Time: return (timeText.Position.X, timeText.Position.Y); +// case OverlayObjects.Speed: return (speedText.Position.X, speedText.Position.Y); +// case OverlayObjects.SpeedLimit: return (speedLimitText.Position.X, speedLimitText.Position.Y); +// case OverlayObjects.CountyName: return (countyText.Position.X, countyText.Position.Y); +// case OverlayObjects.StreetName: return (streetText.Position.X, streetText.Position.Y); +// default: return (0f, 0f); +// } +// } + // private static void GameOnFrameRender(object sender, GraphicsEventArgs e) // { -// directionBackground.Draw(); // directionText.Draw(); // if (HeadingExtensions.GetPedDirection(Game.LocalPlayer.Character) is Directions.Unknown) directionText.Caption = "Unknown"; -// else directionText.Caption = Translation.GetString(HeadingExtensions.GetPedDirection(Game.LocalPlayer.Character).ToString()); +// else directionText.Caption = HeadingExtensions.GetPedDirection(Game.LocalPlayer.Character).ToString(); // // headingText.Caption = HeadingExtensions.GetPedDirection(Game.LocalPlayer.Character).ToString(); // } // } \ No newline at end of file diff --git a/DynamicLSPDFR/Modules/Pylons.cs b/DynamicLSPDFR/Modules/Pylons.cs index 92b0bb3..1476359 100644 --- a/DynamicLSPDFR/Modules/Pylons.cs +++ b/DynamicLSPDFR/Modules/Pylons.cs @@ -59,7 +59,7 @@ internal static void GrabPylon() var pos = Game.LocalPlayer.Character.GetOffsetPositionFront(1f); grabbingPylon.Item1.Position = new(pos.X, pos.Y, (float)World.GetGroundZ(pos, true, true)); grabbingPylon.Item1.Rotation = grabbingPylonRotatorCache; - grabbingPylon.Item1.IsPositionFrozen = DynamicMenu.PylonIsFreeze.Checked; + grabbingPylon.Item1.IsPositionFrozen = DynamicMenu.PylonsMenu.PylonIsFreeze.Checked; grabbingPylon.Item1.IsCollisionEnabled = true; if (grabbingPylon.Item2 && grabbingPylon.Item2.IsValid()) { @@ -112,7 +112,7 @@ internal class Pylons internal static Vector3 startPos = Vector3.Zero, endPos = Vector3.Zero; internal static PylonStatus status = PylonStatus.None; internal static Object StartMarker, EndMarker; - internal float Count = DynamicMenu.PylonCountItem.Value; + internal float Count = DynamicMenu.PylonsMenu.PylonCount.Value; internal Pylons() { } @@ -158,9 +158,9 @@ internal void PlacePylons() if (p) { p.Opacity = 1f; - p.IsPositionFrozen = DynamicMenu.PylonIsFreeze.Checked; + p.IsPositionFrozen = DynamicMenu.PylonsMenu.PylonIsFreeze.Checked; p.IsCollisionEnabled = true; - if (DynamicMenu.PylonIsFreeze.Checked) + if (DynamicMenu.PylonsMenu.PylonIsFreeze.Checked) { ghostPed = new(p.Position); if (ghostPed && ghostPed.IsValid()) diff --git a/DynamicLSPDFR/Modules/TrafficTickets.cs b/DynamicLSPDFR/Modules/TrafficTickets.cs new file mode 100644 index 0000000..ff7c85e --- /dev/null +++ b/DynamicLSPDFR/Modules/TrafficTickets.cs @@ -0,0 +1,50 @@ +// namespace DynamicLSPDFR.Modules; + +// internal class TrafficTickets +// { +// internal string Name { get; set; } = "[NoName]"; +// internal string Category { get; set; } = "[NoCategory]"; +// internal int Points { get; set; } = 0; +// internal int Fine { get; set; } = 5; + +// internal const string XML_FILES_DIRECTORY = @"plugins/LSPDFR/DynamicLSPDFR/"; +// internal static int LimitOfSeize { get; set; } = 10; +// internal static bool SeizeVehicle { get; set; } = false; +// internal static string Unit { get; set; } = "$"; +// internal static bool RightUnitPosition { get; set; } = false; +// internal static Dictionary> AllTrafficTicketCategories { get; set; } = new(); + +// internal static List LoadTickets() +// { +// List AllTrafficTickets = new(); +// if (Directory.Exists(XML_FILES_DIRECTORY)) +// { +// foreach (string file in Directory.EnumerateFiles(XML_FILES_DIRECTORY, "*.xml", SearchOption.TopDirectoryOnly)) +// { +// try +// { +// using (var reader = new StreamReader(file)) +// { +// XmlSerializer deserializer = new(typeof(List), +// new XmlRootAttribute("Tickets")); +// AllTrafficTickets.AddRange((List)deserializer.Deserialize(reader)); +// } +// } +// catch (Exception e) +// { +// Logger.ErrorCode("Traffic Tickets", 30); +// Logger.Error(e.ToString(), "Traffic Tickets"); +// } +// } +// } + +// if (AllTrafficTickets.Count is 0) +// { +// AllTrafficTickets.Add(new TrafficTickets()); +// MessageExtensions.SendMessageInGame(Translation.GetString("NoTickets")); +// } + +// AllTrafficTicketCategories = AllTrafficTickets.GroupBy(x => x.Category).ToDictionary(x => x.Key, x => x.ToList()); +// return AllTrafficTickets; +// } +// } \ No newline at end of file diff --git a/DynamicLSPDFR/Properties/AssemblyInfo.cs b/DynamicLSPDFR/Properties/AssemblyInfo.cs index 1a5e6ef..cf38bbc 100644 --- a/DynamicLSPDFR/Properties/AssemblyInfo.cs +++ b/DynamicLSPDFR/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます // 既定値にすることができます: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.2.0.1")] -[assembly: AssemblyFileVersion("0.2.0.1")] +[assembly: AssemblyVersion("0.3.0.0")] +[assembly: AssemblyFileVersion("0.3.0.0")] diff --git a/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml b/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml index 66052e6..a1a548c 100644 --- a/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml +++ b/DynamicLSPDFR/Resources/BackupFile/DefaultBackup.xml @@ -1,6 +1,6 @@