using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using CardealerClient.Exhibits; using CardealerClient.Menus; using CitizenFX.Core; using CitizenFX.Core.Native; using static CitizenFX.Core.Native.API; using Nexd.ESX.Client; namespace CardealerClient { public class CarDealer : BaseScript { public static CarDealer Instance; public static ExhibitCar[] RealExhibitCars; public static bool[] SlotReady; public static string[] CarNames; public static bool ExhibitsSpawned; public static bool MenuOpen; public static bool DigitalMenuOpen; private static TaskCompletionSource _dialogTask; public CarDealer() { RealExhibitCars = new ExhibitCar[Config.CarSlots.Length]; SlotReady = new bool[Config.CarSlots.Length]; CarNames = new string[Config.CarSlots.Length]; ExhibitsSpawned = false; EventHandlers["onClientResourceStart"] += new Action(OnStart); EventHandlers["onResourceStop"] += new Action(OnStop); EventHandlers["cardealer:spawnVehicle"] += new Action(SpawnExhibit); EventHandlers["cardealer:openmenu"] += new Action(SellMenu.Open); EventHandlers["cardealer:testDriveClient"] += new Action((slot, value) => RealExhibitCars[slot]?.SetTestDrive(value)); EventHandlers["cardealer:openManageMenu"] += new Action(OwnerMenu.Open); EventHandlers["cardealer:dialog_accept"] += new Action(() => _dialogTask?.TrySetResult(true)); EventHandlers["cardealer:dialog_deny"] += new Action(() => _dialogTask?.TrySetResult(false)); Instance = this; } public static Task ServerCallback(string name, dynamic args = null) { var source = new TaskCompletionSource(); ESX.TriggerServerCallback(name, new Action(o => { source.TrySetResult(o); }), args); return source.Task; } public static Task SpawnVehicle(dynamic model, Vector3 coords, float heading, bool local = false) { var source = new TaskCompletionSource(); if (local) { ESX.Game.Raw.SpawnLocalVehicle(model, coords, heading, new Action(vehicle => { source.TrySetResult(vehicle); })); } else { ESX.Game.Raw.SpawnVehicle(model, coords, heading, new Action(vehicle => { source.TrySetResult(vehicle); })); } return source.Task; } public static Task> GetVehicleNames(dynamic models) { var source = new TaskCompletionSource>(); TriggerEvent("cardealer:getCarNames", models, new Action(o => { source.TrySetResult((o as List).Select(val => val.ToString()).ToList()); })); return source.Task; } public static Task DisplayTextDialog(string placeholder, int maxLength) { var source = new TaskCompletionSource(); DisplayOnscreenKeyboard(0, "FMMC_KEY_TIP8", "", placeholder, "", "", "", maxLength); new Thread(() => { bool input = true; while (input) { if (UpdateOnscreenKeyboard() == 3 || UpdateOnscreenKeyboard() == 2) input = false; else if (UpdateOnscreenKeyboard() == 1) { input = false; source.TrySetResult(GetOnscreenKeyboardResult()); } Thread.Sleep(0); } }).Start(); return source.Task; } public static Task DisplayConfirmationDialog(string title) { _dialogTask = new TaskCompletionSource(); TriggerEvent("okokRequests:RequestMenuData", -1, Config.MenuTitle, title, "cardealer:dialog_accept", "client", "", 0, "cardealer:dialog_deny"); return _dialogTask.Task; } public static void DrawText3D(Vector3 pos, string text, float scale = 1, bool rotate = true) { OutputArgument screenX = new OutputArgument(), screenY = new OutputArgument(); bool onScreen = Function.Call(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD , pos.X, pos.Y, pos.Z, screenX, screenY); if (onScreen) { Vector3 cam = GetGameplayCamCoord(); float dist = GetDistanceBetweenCoords(cam.X, cam.Y, cam.Z, pos.X, pos.Y, pos.Z, true); if (rotate) scale = (((1 / dist) * 2) * (1 / GetGameplayCamFov()) * 100) * scale; SetTextColour(220, 220, 220, 255); SetTextScale(0.0f * scale, 0.40f * scale); SetTextFont(4); SetTextProportional(true); SetTextCentre(true); SetTextEntry("STRING"); AddTextComponentString(text); EndTextCommandDisplayText(screenX.GetResult(), screenY.GetResult()); } } private void ShowText(int slot) { Exhibit exhibit = RealExhibitCars[slot].Exhibit; float x = exhibit.Location.X; float y = exhibit.Location.Y; float z = exhibit.Location.Z; if (RealExhibitCars[slot].TestDrive) DrawText3D(new Vector3(x, y, z + 0.8f), "[~r~Dieses Auto ist gerade bei einer Probefahrt~s~]"); else { VehicleData data = RealExhibitCars[slot].Data; string turbo = "~r~Nein"; if (Convert.ToBoolean(data.Properties.Raw.modTurbo)) turbo = "~g~Ja"; string line1 = "[~b~" + CarNames[slot] + "~s~]"; string line2 = "[Turbo : " + turbo + "~s~] [Motor : ~r~" + data.Properties.modEngine + "~s~] [Getriebe : ~r~" + data.Properties.modTransmission + "~s~]"; string line3 = "[Federung : ~r~" + data.Properties.modSuspension + "~s~] [Panzerung : ~r~" + data.Properties.modArmor + "~s~] [Bremsen : ~r~" + data.Properties.modBrakes + "~s~]"; string line4 = "[Beschreibung : ~r~" + data.Description + "~s~]"; string line5 = "Drücke [~r~E~s~] zum kaufen [$~r~" + data.Price + "~s~]"; string line6 = "Drücke [~r~F~s~] zum probefahren [~r~90 sek~s~]"; DrawText3D(new Vector3(x, y, z + 1.1f), line6); DrawText3D(new Vector3(x, y, z + 1.2f), line5); DrawText3D(new Vector3(x, y, z + 1.3f), line4); DrawText3D(new Vector3(x, y, z + 1.4f), line3); DrawText3D(new Vector3(x, y, z + 1.5f), line2); DrawText3D(new Vector3(x, y, z + 1.6f), line1); } } public static void SendNotification(string message) { SetNotificationTextEntry("STRING"); AddTextComponentString(message); DrawNotification(false, false); } public static void Notify(string message, string type, int duration = 5000) { TriggerEvent("okokNotify:Alert", Config.MenuTitle, message, duration, type); } public static void AddTickHandler(Func handler) { Instance.Tick += handler; } public static void RemoveTickHandler(Func handler) { Instance.Tick -= handler; } private void DisplayHelpTextThisFrame(string text) { SetTextComponentFormat("STRING"); AddTextComponentString(text); EndTextCommandDisplayHelp(0, false, true, -1); } private void SpawnRealExhibits() { if (ExhibitsSpawned) return; for (int i = 1; i < Config.CarSlots.Length; i++) { TriggerEvent("cardealer:spawnVehicle", i); } ExhibitsSpawned = true; } private void DeleteRealExhibits() { if (!ExhibitsSpawned) return; for (int i = 1; i < Config.CarSlots.Length; i++) { if (SlotReady[i]) RealExhibitCars[i].Despawn(); } ExhibitsSpawned = false; } // EVENT FUNCTIONS private void OnStart(string resourceName) { if (resourceName != GetCurrentResourceName()) return; for (int i = 1; i < Config.CarSlots.Length; i++) SlotReady[i] = false; Tick += async () => { ExhibitCar.RepairVehicles(); //EXHIBIT INTERACTION Vector3 player = GetEntityCoords(GetPlayerPed(-1), IsDecalAlive(GetPlayerPed(-1))); bool playerIsInRadius = GetDistanceBetweenCoords( player.X, player.Y, player.Z, Config.BlipLocation.X, Config.BlipLocation.Y, Config.BlipLocation.Z, false) < Config.SpawnRadius; if (!ExhibitsSpawned && playerIsInRadius) SpawnRealExhibits(); if (ExhibitsSpawned && !playerIsInRadius) DeleteRealExhibits(); bool found = false; float closestDist = float.MaxValue; int closestSlot = 0; for (int i = 0; i < RealExhibitCars.Length; i++) { if (!SlotReady[i]) continue; Vector4 exPos = RealExhibitCars[i].Exhibit.Location; float dist = GetDistanceBetweenCoords(player.X, player.Y, player.Z, exPos.X, exPos.Y, exPos.Z, true); if (closestDist > dist) { closestDist = dist; found = true; closestSlot = RealExhibitCars[i].Exhibit.Slot; } } if (found) { if (closestDist < 5) { ShowText(closestSlot); if (!RealExhibitCars[closestSlot].TestDrive) { if (IsControlJustReleased(0, 38) && !RealExhibitCars[closestSlot].TestDrive) RealExhibitCars[closestSlot].Buy(); if (IsControlJustReleased(0, 23) && !RealExhibitCars[closestSlot].TestDrive) RealExhibitCars[closestSlot].StartTestDrive(); } } } DigitalExhibit.ShowDigitalText(); //BLIP INTERACTION if (IsPedInAnyVehicle(GetPlayerPed(-1), false)) { DrawMarker(1, Config.BlipLocation.X, Config.BlipLocation.Y, Config.BlipLocation.Z, 0, 0, 0, 0, 0, 0, 3.7f, 3.7f, 0.2f, 3, 155, 229, 255, false, false, 0, false, null, null, false); float distanceToMarker = GetDistanceBetweenCoords(player.X, player.Y, player.Z, Config.BlipLocation.X, Config.BlipLocation.Y, Config.BlipLocation.Z, true); if (distanceToMarker <= 4f) { if (!MenuOpen) DisplayHelpTextThisFrame("Drücke ~INPUT_CONTEXT~ um das Menü zu öffnen"); if (IsControlJustReleased(0, 38) && !MenuOpen) SellMenu.Open(); } } float digiDist = GetDistanceBetweenCoords(player.X, player.Y, player.Z, Config.DigitalShowPoint.X, Config.DigitalShowPoint.Y, Config.DigitalShowPoint.Z, true); if (digiDist < 2 && !DigitalMenuOpen) { DrawText3D(Config.DigitalShowPoint, "Drücke ~g~E~s~ um den Katalog zu öffnen"); if (IsControlJustReleased(0, 38)) DigitalExhibit.HandleDigitalExhibit(); } }; } private void OnStop(string resourceName) { if (resourceName != GetCurrentResourceName()) return; DeleteRealExhibits(); } private async void SpawnExhibit(int slot) { Exhibit exhibit = Config.CarSlots[slot]; ExhibitCar old = RealExhibitCars[slot]; if (old != null) ESX.Game.DeleteVehicle(old.Vehicle.ToString()); var vehicle = await ServerCallback("cardealer:getVehicleData", slot); if (vehicle is List) { if (RealExhibitCars[slot] != null) { RealExhibitCars[slot].Despawn(); } RealExhibitCars[slot] = null; return; } VehicleData data = new VehicleData(vehicle); if (data.Properties == null) { if (RealExhibitCars[slot] != null) { RealExhibitCars[slot].Despawn(); } RealExhibitCars[slot] = null; return; } ExhibitCar car = new ExhibitCar(exhibit, data); car.SetTestDrive(await ServerCallback("cardealer:getTestDrive", slot)); RealExhibitCars[slot] = car; } } }