Koci.Client.GetVehicleProperties = function(vehicle) if Config.FrameWork == "esx" then return Koci.Framework.Game.GetVehicleProperties(vehicle) elseif Config.FrameWork == "qb" then return Koci.Framework.Functions.GetVehicleProperties(vehicle) end end Koci.Client.SetVehicleProperties = function(vehicle, props) if Config.FrameWork == "esx" then return Koci.Framework.Game.SetVehicleProperties(vehicle, props) elseif Config.FrameWork == "qb" then return Koci.Framework.Functions.SetVehicleProperties(vehicle, props) end end --- A simple wrapper around SendNUIMessage that you can use to --- dispatch actions to the React frame. --- ---@param action string The action you wish to target ---@param data any The data you wish to send along with this action Koci.Client.SendReactMessage = function(action, data) SendNUIMessage({ action = action, data = data }) end ---@param system ("esx_notify" | "qb_notify" | "custom_notify") System to be used ---@param type string inform / success / error ---@param title string Notification text ---@param text? string (optional) description, custom notify. ---@param duration? number (optional) Duration in miliseconds, custom notify. ---@param icon? string (optional) icon. Koci.Client.SendNotify = function(title, type, duration, icon, text) system = Config.NotifyType if system == "esx_notify" then if Config.FrameWork == "esx" then Koci.Framework.ShowNotification(title, type, duration) end elseif system == "qb_notify" then if Config.FrameWork == "qb" then Koci.Framework.Functions.Notify(title, type) end elseif system == "custom_notify" then Utils.Functions.CustomNotify(nil, title, type, text, duration, icon) end end --- Gets player data based on the configured framework. ---@return PlayerData table player data. Koci.Client.GetPlayerData = function() if Config.FrameWork == "esx" then return Koci.Framework.GetPlayerData() elseif Config.FrameWork == "qb" then return Koci.Framework.Functions.GetPlayerData() end end -- Draws 3D text at the specified world coordinates. ---@param x (number) The X-coordinate of the text in the world. ---@param y (number) The Y-coordinate of the text in the world. ---@param z (number) The Z-coordinate of the text in the world. ---@param text (string) The text to be displayed. Koci.Client.DrawText3D = function(coords, text) SetTextScale(0.35, 0.35) SetTextFont(4) SetTextColour(255, 255, 255, 215) SetTextEntry("STRING") SetTextCentre(true) AddTextComponentString(text) SetDrawOrigin(coords.x, coords.y, coords.z, 0) DrawText(0.0, 0.0) local factor = (string.len(text)) / 370 DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 70, 134, 123, 75) ClearDrawOrigin() end --- Loads a model on the client. ---@param model number|string The model to load, specified as either a number or a string. Koci.Client.LoadModel = function(model) if HasModelLoaded(model) then return end RequestModel(model) while not HasModelLoaded(model) do Wait(0) end end --- Displays a text UI based on the specified framework. --- @param _type string (optional) The framework to use, defaults to Config.FrameWork if not provided. --- @param message string The message to display in the text UI. --- @param options table (optional) Additional options for displaying the text UI. Koci.Client.ShowTextUI = function(_type, message, options) _type = _type or Config.FrameWork if _type == "qb" then if not Utils.Functions.hasResource("qb-core") then Utils.Functions.debugPrint("qb-core is not active on your server !") return end exports["qb-core"]:DrawText(message, "top") elseif _type == "ox" then if not Utils.Functions.hasResource("ox_lib") then Utils.Functions.debugPrint("ox_lib is not active on your server !") return end if not options then options = { position = "left-center" } end lib.showTextUI(message, options) end end --- Hides the currently displayed text UI. Koci.Client.HideTextUI = function() if Utils.Functions.hasResource("ox_lib") then lib.hideTextUI() end if Utils.Functions.hasResource("qb-core") then exports["qb-core"]:HideText() end end -- @ End core func. -- @ Start script func. function deepCopy(orig) local orig_type = type(orig) local copy if orig_type == "table" then copy = {} for orig_key, orig_value in next, orig, nil do copy[deepCopy(orig_key)] = deepCopy(orig_value) end setmetatable(copy, deepCopy(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end function openGallery(g) OpenedGallery = deepCopy(g) if OpenedGallery.discount and OpenedGallery.discount.active then local updatedVehicles = calculateDiscountedPriceOfVehicles( OpenedGallery.vehicles, OpenedGallery.discount.percentage ) OpenedGallery.vehicles = updatedVehicles end setBankBalance() SetEntityVisible(PlayerPedId(), false) create_gCam(OpenedGallery.camCoords, OpenedGallery.camRotation) SetNuiFocus(true, true) DisplayRadar(false) closeHud() OpenedGallery.currentCam = "selected" playRandomMusic() Koci.Client.SendReactMessage("load_gallery", OpenedGallery) Koci.Client.SendReactMessage("setVisible", true) end function playRandomMusic() if Config.Music.status then local volume = Config.Music.volume local musics = Config.Music.musics local selectedMusic = musics[math.random(#musics)] Koci.Client.SendReactMessage("setPlayingSong", { on = false, file = selectedMusic.fileName, author = selectedMusic.author, name = selectedMusic.name, volume = volume }) else Koci.Client.SendReactMessage("setPlayingSong", { on = false, file = "Ingen", author = "", name = "Ingen musik" }) end end function closeCurrentGallery() DoScreenFadeIn(0) if OpenedGallery then SetEntityCoords(PlayerPedId(), OpenedGallery.coords.x, OpenedGallery.coords.y, OpenedGallery.coords.z-1, true, false, false, false) end OpenedGallery = nil DisplayRadar(1) SetNuiFocus(false, false) destroy_gCam() SetEntityVisible(PlayerPedId(), true, false) openHud() deleteSpawnedCars() end function create_gCam(coords, rot) if not DoesCamExist(gCam) then gCam = CreateCam("DEFAULT_SCRIPTED_CAMERA", 0) SetCamCoord(gCam, coords) SetCamRot(gCam, rot, 2) SetCamActive(gCam, true) RenderScriptCams(true, true, 1000) end end function destroy_gCam() if DoesCamExist(gCam) then DestroyCam(gCam, true) RenderScriptCams(false, false, 1) gCam = nil end end function setGlobalCamCoords(coords, rot) if DoesCamExist(gCam) then RenderScriptCams(false, false, 1) SetCamCoord(gCam, coords) SetCamRot(gCam, rot, 2) RenderScriptCams(true, true, 1000) end end function deleteSpawnedCars() for i, v in pairs(gSpawnedVehicles) do if DoesEntityExist(v) then DeleteEntity(v) end gSpawnedVehicles[i] = nil end end function deleteSpawnedCar(i) if DoesEntityExist(gSpawnedVehicles[i]) then DeleteEntity(gSpawnedVehicles[i]) end gSpawnedVehicles[i] = nil end function setBankBalance() Koci.Client.TriggerServerCallback("hp_vehicleshop:Server:GetPlayerAccountBalance", { type = "bank" }, function(balance) Koci.Client.SendReactMessage("setBankBalance", tonumber(balance)) end) end function calculateVehiclePrice(price, plate, payment) price = tonumber(price) plate = tostring(plate) local platePrice = 0 local blackMoneyPrice = 1 if type(plate) == "string" and #plate > 0 then platePrice = tonumber(OpenedGallery.customPlate.price) end if payment == "black_money" then blackMoneyPrice = tonumber(OpenedGallery.buyWithBlackMoney.multiplier) end return math.floor((price + platePrice) * blackMoneyPrice) end function calculateVehicleRentalFee(price, custom_plate, rented_day, daily_fee) local rentel_fee = math.floor(daily_fee * rented_day) if type(custom_plate) == "string" and #custom_plate > 0 then rentel_fee = rentel_fee + tonumber(OpenedGallery.customPlate.price) end return math.floor(rentel_fee) end function calculateDiscountedPriceOfVehicles(vehicles, percentage) if type(percentage) ~= "number" or percentage < 0 or percentage > 100 then percentage = 0 end if type(vehicles) ~= "table" then return Config.Vehicles or {} end for category, cVehicles in pairs(vehicles) do for key, value in pairs(cVehicles) do local oldPrice = value.price local discountAmount = oldPrice * (percentage / 100) local newPrice = oldPrice - discountAmount value.price = math.floor(newPrice) end end return vehicles end function GetVehicleTractionType(value) if value == 0.0 then return "RWD" elseif value > 0.0 and value < 0.35 then return "AWD 30/70" elseif value == 0.5 then return "AWD" elseif value > 0.5 and value < 0.75 then return "AWD 70/30" elseif value == 1.0 then return "FWD" else return "-undefined-" end end function calculateAccelerationTimeForVehicle(vehicle_top_speed) local initialSpeed = 0 local finalSpeed = 100 local accelerationFactor = 6 local acceleration = vehicle_top_speed / accelerationFactor local accelerationTime = (finalSpeed - initialSpeed) / acceleration return string.format("%.2f", accelerationTime) end function RotateVehicle(vehicle) Citizen.CreateThread(function() while gRotatingVehicles[vehicle] and DoesEntityExist(vehicle) do SetEntityRotation(vehicle, GetEntityRotation(vehicle) + vector3(0, 0, 0.1), false, false, 2, false) Wait(10) end end) end function CheckOverdueRentalCars() Koci.Client.TriggerServerCallback("hp_vehicleshop:Server:CheckOverdueRentalCars", nil, function(r) if r.any_action then Koci.Client.SendNotify(_t("rent.overdue_vehicles_deleted"), "inform") end end) end