├── compatibility ├── qb │ ├── server.lua │ └── client.lua ├── backwards │ ├── client.lua │ └── server.lua └── esx │ ├── locale.lua │ ├── server.lua │ └── client.lua ├── init.lua ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── database ├── characters.sql └── vehicles.sql ├── shared └── functions.lua ├── client ├── functions.lua ├── death.lua ├── events.lua ├── main.lua ├── peds.lua └── vehicle │ ├── garages.lua │ └── data.lua ├── fxmanifest.lua ├── README.md ├── server ├── functions.lua ├── main.lua ├── commands.lua └── player.lua └── locales ├── ar.json ├── en.json ├── da.json ├── no.json ├── sv.json ├── nl.json ├── de.json ├── it.json ├── ru.json ├── es.json ├── fr.json └── fi.json /compatibility/qb/server.lua: -------------------------------------------------------------------------------- 1 | if not lib.table.contains(Config.compatibility, "qb") then return end 2 | -------------------------------------------------------------------------------- /compatibility/qb/client.lua: -------------------------------------------------------------------------------- 1 | if not lib.table.contains(Config.compatibility, "qb") then return end 2 | 3 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | local nd_core = exports["ND_Core"] 2 | 3 | NDCore = setmetatable({}, { 4 | __index = function(self, index) 5 | self[index] = function(...) 6 | return nd_core[index](nil, ...) 7 | end 8 | 9 | return self[index] 10 | end 11 | }) 12 | 13 | return NDCore 14 | -------------------------------------------------------------------------------- /compatibility/backwards/client.lua: -------------------------------------------------------------------------------- 1 | if not lib.table.contains(Config.compatibility, "backwards") then return end 2 | 3 | NDCore.Functions = {} 4 | NDCore.Functions.GetSelectedCharacter = NDCore.getPlayer 5 | NDCore.Functions.GetCharacters = NDCore.getCharacters 6 | NDCore.Functions.GetPlayersFromCoords = NDCore.getPlayersFromCoords 7 | 8 | exports("GetCoreObject", function() 9 | return NDCore 10 | end) 11 | 12 | RegisterNetEvent("ND:returnCharacters", function(characters) 13 | NDCore.characters = characters 14 | end) 15 | 16 | RegisterNetEvent("ND:setCharacter", function(character) 17 | NDCore.player = character 18 | end) 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /database/characters.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `nd_characters` ( 2 | `charid` INT(10) NOT NULL AUTO_INCREMENT, 3 | `identifier` VARCHAR(200) NOT NULL DEFAULT '0', 4 | `name` VARCHAR(50) DEFAULT NULL, 5 | `firstname` VARCHAR(50) DEFAULT NULL, 6 | `lastname` VARCHAR(50) DEFAULT NULL, 7 | `dob` VARCHAR(50) DEFAULT NULL, 8 | `gender` VARCHAR(50) DEFAULT NULL, 9 | `cash` INT(10) DEFAULT '0', 10 | `bank` INT(10) DEFAULT '0', 11 | `phonenumber` VARCHAR(20) DEFAULT NULL, 12 | `groups` LONGTEXT DEFAULT ('[]'), 13 | `metadata` LONGTEXT DEFAULT ('[]'), 14 | `inventory` LONGTEXT DEFAULT ('[]'), 15 | PRIMARY KEY (`charid`) USING BTREE 16 | ); 17 | -------------------------------------------------------------------------------- /database/vehicles.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `nd_vehicles` ( 2 | `id` INT(11) NOT NULL AUTO_INCREMENT, 3 | `owner` INT(11) DEFAULT NULL, 4 | `plate` VARCHAR(255) DEFAULT NULL, 5 | `glovebox` LONGTEXT DEFAULT ('[]'), 6 | `trunk` LONGTEXT DEFAULT ('[]'), 7 | `properties` LONGTEXT DEFAULT ('[]'), 8 | `stored` INT(11) DEFAULT '1', 9 | `impounded` INT(11) DEFAULT '0', 10 | `stolen` INT(11) DEFAULT '0', 11 | `metadata` LONGTEXT DEFAULT ('[]'), 12 | PRIMARY KEY (`id`) USING BTREE, 13 | INDEX `owner` (`owner`) USING BTREE, 14 | CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE 15 | ); 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /shared/functions.lua: -------------------------------------------------------------------------------- 1 | local startedResources = {} 2 | 3 | local function stateChanged(resourceName, state) 4 | local callbacks = startedResources[resourceName] 5 | if not callbacks then return end 6 | for i=1, #callbacks do 7 | local cb = callbacks[i] 8 | cb(state) 9 | end 10 | end 11 | 12 | AddEventHandler("onResourceStart", function(resourceName) 13 | stateChanged(resourceName, true) 14 | end) 15 | 16 | AddEventHandler("onResourceStop", function(resourceName) 17 | stateChanged(resourceName, false) 18 | end) 19 | 20 | function NDCore.isResourceStarted(resourceName, cb) 21 | local started = GetResourceState(resourceName) == "started" 22 | if cb then 23 | if not startedResources[resourceName] then 24 | startedResources[resourceName] = {} 25 | end 26 | startedResources[resourceName][#startedResources[resourceName]+1] = cb 27 | cb(started) 28 | end 29 | return started 30 | end 31 | -------------------------------------------------------------------------------- /compatibility/esx/locale.lua: -------------------------------------------------------------------------------- 1 | local config_locale = "en" 2 | Locales = {} 3 | 4 | function Translate(str, ...) -- Translate string 5 | if not str then 6 | print(("[^1ERROR^7] Resource ^5%s^7 You did not specify a parameter for the Translate function or the value is nil!"):format(GetInvokingResource() or GetCurrentResourceName())) 7 | return 'Given translate function parameter is nil!' 8 | end 9 | if Locales[config_locale] then 10 | if Locales[config_locale][str] then 11 | return string.format(Locales[config_locale][str], ...) 12 | elseif config_locale ~= 'en' and Locales['en'] and Locales['en'][str] then 13 | return string.format(Locales['en'][str], ...) 14 | else 15 | return 'Translation [' .. config_locale .. '][' .. str .. '] does not exist' 16 | end 17 | elseif config_locale ~= 'en' and Locales['en'] and Locales['en'][str] then 18 | return string.format(Locales['en'][str], ...) 19 | else 20 | return 'Locale [' .. config_locale .. '] does not exist' 21 | end 22 | end 23 | 24 | function TranslateCap(str, ...) -- Translate string first char uppercase 25 | return _(str, ...):gsub("^%l", string.upper) 26 | end 27 | 28 | _ = Translate 29 | _U = TranslateCap 30 | -------------------------------------------------------------------------------- /client/functions.lua: -------------------------------------------------------------------------------- 1 | function NDCore.getPlayer() 2 | return NDCore.player 3 | end 4 | 5 | function NDCore.getCharacters() 6 | return NDCore.characters 7 | end 8 | 9 | function NDCore.getPlayersFromCoords(distance, coords) 10 | if coords then 11 | coords = type(coords) == "table" and vec3(coords.x, coords.y, coords.z) or coords 12 | else 13 | coords = GetEntityCoords(PlayerPedId()) 14 | end 15 | distance = distance or 5 16 | local closePlayers = {} 17 | local players = GetActivePlayers() 18 | for _, player in ipairs(players) do 19 | local target = GetPlayerPed(player) 20 | local targetCoords = GetEntityCoords(target) 21 | local targetdistance = #(targetCoords - coords) 22 | if targetdistance <= distance then 23 | closePlayers[#closePlayers + 1] = player 24 | end 25 | end 26 | return closePlayers 27 | end 28 | 29 | function NDCore.getConfig(info) 30 | if not info then 31 | return Config 32 | end 33 | return Config[info] 34 | end 35 | 36 | function NDCore.notify(...) 37 | lib.notify(...) 38 | end 39 | 40 | for name, func in pairs(NDCore) do 41 | if type(func) == "function" then 42 | exports(name, func) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | -- For support join my discord: https://discord.gg/Z9Mxu72zZ6 2 | 3 | author "Andyyy#7666" 4 | description "ND Framework Core" 5 | version "2.3.2" 6 | 7 | fx_version "cerulean" 8 | game "gta5" 9 | lua54 "yes" 10 | 11 | shared_script "@ox_lib/init.lua" 12 | client_scripts { 13 | "client/main.lua", 14 | "shared/functions.lua", 15 | "client/peds.lua", 16 | "client/vehicle/main.lua", 17 | "client/vehicle/garages.lua", 18 | "client/functions.lua", 19 | "client/events.lua", 20 | "client/death.lua", 21 | "compatibility/**/client.lua" 22 | } 23 | server_scripts { 24 | "@oxmysql/lib/MySQL.lua", 25 | "server/main.lua", 26 | "shared/functions.lua", 27 | "server/player.lua", 28 | "server/vehicle.lua", 29 | "server/functions.lua", 30 | "compatibility/**/server.lua", 31 | "server/commands.lua" 32 | } 33 | 34 | files { 35 | "init.lua", 36 | "client/vehicle/data.lua", 37 | "compatibility/**/locale.lua", 38 | "locales/*.json" 39 | } 40 | 41 | dependencies { 42 | "ox_lib", 43 | "oxmysql" 44 | } 45 | 46 | -- below were used with backwards compatibility but could interfere with resources checking if the resources are started. 47 | -- provide "es_extended" 48 | -- provide "qb-Core" 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

Documentation 8 | 9 |

10 | 11 | Andyyy Development Server 12 | 13 |
14 | 15 | ## Dependencies: 16 | * [oxmysql](https://github.com/overextended/oxmysql/releases) 17 | * [ox_lib](https://github.com/overextended/ox_lib/releases) 18 | 19 | ## Addons 20 | * [Character selection](https://github.com/ND-Framework/ND_Characters/releases) 21 | * [Banking](https://github.com/ND-Framework/ND_Banking/releases) 22 | * [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/releases) 23 | * [Dealership](https://github.com/ND-Framework/ND_Dealership/releases) 24 | * [Inventory](https://github.com/overextended/ox_inventory/releases) 25 | * [Ambulance job](https://github.com/ND-Framework/ND_Ambulance/releases) 26 | * [Casino](https://github.com/ND-Framework/ND_Casino/releases) 27 | * [Blackjack](https://github.com/ND-Framework/ND_Blackjack/releases) 28 | * [Nitro](https://github.com/ND-Framework/ND_Nitro/releases) 29 | -------------------------------------------------------------------------------- /client/death.lua: -------------------------------------------------------------------------------- 1 | local ambulance 2 | local usingAmbulance = false 3 | local alreadyEliminated = false 4 | 5 | NDCore.isResourceStarted("ND_Ambulance", function(started) 6 | usingAmbulance = started 7 | if not usingAmbulance then return end 8 | ambulance = exports["ND_Ambulance"] 9 | end) 10 | 11 | local function PlayerEliminated(deathCause, killerServerId, killerClientId) 12 | if alreadyEliminated then return end 13 | alreadyEliminated = true 14 | local info = { 15 | deathCause = deathCause, 16 | killerServerId = killerServerId, 17 | killerClientId = killerClientId, 18 | damagedBones = usingAmbulance and ambulance:getBodyDamage() or {} 19 | } 20 | TriggerEvent("ND:playerEliminated", info) 21 | TriggerServerEvent("ND:playerEliminated", info) 22 | Wait(1000) 23 | alreadyEliminated = false 24 | end 25 | 26 | AddEventHandler("gameEventTriggered", function(name, args) 27 | if name ~= "CEventNetworkEntityDamage" then return end 28 | 29 | local victim = args[1] 30 | if not IsPedAPlayer(victim) or NetworkGetPlayerIndexFromPed(victim) ~= cache.playerId then return end 31 | 32 | local hit, bone = GetPedLastDamageBone(victim) 33 | if hit and usingAmbulance then 34 | local damageWeapon = ambulance:getLastDamagingWeapon(victim) 35 | ambulance:updateBodyDamage(bone, damageWeapon) 36 | end 37 | 38 | if not IsPedDeadOrDying(victim, true) or GetEntityHealth(victim) > 100 then return end 39 | 40 | local killerEntity, deathCause = GetPedSourceOfDeath(cache.ped), GetPedCauseOfDeath(cache.ped) 41 | local killerClientId = NetworkGetPlayerIndexFromPed(killerEntity) 42 | if killerEntity ~= cache.ped and killerClientId and NetworkIsPlayerActive(killerClientId) then 43 | return PlayerEliminated(deathCause, GetPlayerServerId(killerClientId), killerClientId) 44 | end 45 | PlayerEliminated(deathCause) 46 | end) 47 | 48 | local firstSpawn = true 49 | exports.spawnmanager:setAutoSpawnCallback(function() 50 | if firstSpawn then 51 | firstSpawn = false 52 | return exports.spawnmanager:spawnPlayer() and exports.spawnmanager:setAutoSpawn(false) 53 | end 54 | end) 55 | -------------------------------------------------------------------------------- /client/events.lua: -------------------------------------------------------------------------------- 1 | RegisterNetEvent("ND:notifyClient", function(...) 2 | NDCore.notify(...) 3 | end) 4 | 5 | -- updates the money on the client. 6 | RegisterNetEvent("ND:updateMoney", function(cash, bank) 7 | if not NDCore.player then return end 8 | NDCore.player.cash = cash 9 | NDCore.player.bank = bank 10 | end) 11 | 12 | -- Sets main character. 13 | RegisterNetEvent("ND:characterLoaded", function(character) 14 | NDCore.player = character 15 | end) 16 | 17 | -- Update main character info. 18 | RegisterNetEvent("ND:updateCharacter", function(character) 19 | NDCore.player = character 20 | end) 21 | 22 | -- Updates last lcoation. 23 | RegisterNetEvent("ND:updateLastLocation", function(location) 24 | if not NDCore.player then return end 25 | NDCore.player.lastLocation = location 26 | end) 27 | 28 | RegisterNetEvent("ND:revivePlayer", function() 29 | if source == "" then return end 30 | local oldPed = cache.ped 31 | local veh = GetVehiclePedIsIn(oldPed) 32 | local seat = cache.seat 33 | local coords = GetEntityCoords(oldPed) 34 | local armor = GetPedArmour(oldPed) 35 | 36 | NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, GetEntityHeading(oldPed), true, true, false) 37 | 38 | local ped = PlayerPedId() 39 | if oldPed ~= ped then 40 | DeleteEntity(oldPed) 41 | ClearAreaOfPeds(coords.x, coords.y, coords.z, 0.2, false) 42 | end 43 | 44 | SetEntityInvincible(ped, false) 45 | FreezeEntityPosition(ped, false) 46 | SetEntityVisible(ped, true) 47 | SetEveryoneIgnorePlayer(ped, false) 48 | SetPedCanBeTargetted(ped, true) 49 | SetEntityCanBeDamaged(ped, true) 50 | SetBlockingOfNonTemporaryEvents(ped, false) 51 | SetPedCanRagdollFromPlayerImpact(ped, true) 52 | ClearPedTasksImmediately(ped) 53 | SetPedArmour(ped, armor) 54 | 55 | if veh and veh ~= 0 then 56 | SetPedIntoVehicle(ped, veh, seat) 57 | end 58 | end) 59 | 60 | RegisterNetEvent("ND:characterUnloaded") 61 | 62 | RegisterNetEvent("ND:clothingMenu", function() 63 | if GetResourceState("fivem-appearance") ~= "started" then return end 64 | 65 | local function customize(appearance) 66 | if not appearance then return end 67 | TriggerServerEvent("ND:updateClothing", appearance) 68 | end 69 | 70 | exports["fivem-appearance"]:startPlayerCustomization(customize, { 71 | ped = false, 72 | headBlend = true, 73 | faceFeatures = true, 74 | headOverlays = true, 75 | components = true, 76 | props = true, 77 | tattoos = true 78 | }) 79 | end) 80 | -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | lib.locale() 2 | NDCore = {} 3 | 4 | Config = { 5 | serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), 6 | discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), 7 | discordAppId = GetConvar("core:discordAppId", "858146067018416128"), 8 | discordAsset = GetConvar("core:discordAsset", "andyyy"), 9 | discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), 10 | discordActionText = GetConvar("core:discordActionText", "DISCORD"), 11 | discordActionLink = GetConvar("core:discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), 12 | discordActionText2 = GetConvar("core:discordActionText2", "STORE"), 13 | discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), 14 | disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, 15 | randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), 16 | requireKeys = GetConvarInt("core:requireKeys", 1) == 1, 17 | useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, 18 | groups = json.decode(GetConvar("core:groups", "[]")), 19 | compatibility = json.decode(GetConvar("core:compatibility", "[]")), 20 | lockpickTries = GetConvarInt("core:lockpickTries", 3) 21 | } 22 | 23 | -- Discord rich presence. 24 | CreateThread(function() 25 | SetDiscordAppId(Config.discordAppId) 26 | SetDiscordRichPresenceAsset(Config.discordAsset) 27 | SetDiscordRichPresenceAssetSmall(Config.discordAssetSmall) 28 | SetDiscordRichPresenceAction(0, Config.discordActionText, Config.discordActionLink) 29 | SetDiscordRichPresenceAction(1, Config.discordActionText2, Config.discordActionLink2) 30 | local presenceText = locale("discord_text_server", Config.serverName) 31 | while true do 32 | if NDCore.player then 33 | local presence = locale("discord_text", Config.serverName, NDCore.player.firstname, NDCore.player.lastname) 34 | local presenceTextSmall = locale("discord_text_small", NDCore.player.firstname, NDCore.player.lastname) 35 | SetRichPresence(presence) 36 | SetDiscordRichPresenceAssetText(presenceText) 37 | SetDiscordRichPresenceAssetSmallText(presenceTextSmall) 38 | end 39 | Wait(60000) 40 | end 41 | end) 42 | 43 | -- Pause menu information. 44 | CreateThread(function() 45 | AddTextEntry("FE_THDR_GTAO", Config.serverName) 46 | local sleep = 500 47 | while true do 48 | Wait(sleep) 49 | if NDCore.player and IsPauseMenuActive() then 50 | sleep = 0 51 | BeginScaleformMovieMethodOnFrontendHeader("SET_HEADING_DETAILS") 52 | ScaleformMovieMethodAddParamPlayerNameString(("%s %s"):format(NDCore.player.firstname, NDCore.player.lastname)) 53 | ScaleformMovieMethodAddParamTextureNameString(locale("pause_menu_cash", NDCore.player.cash)) 54 | ScaleformMovieMethodAddParamTextureNameString(locale("pause_menu_bank", NDCore.player.bank)) 55 | EndScaleformMovieMethod() 56 | elseif sleep == 0 then 57 | sleep = 500 58 | end 59 | end 60 | end) 61 | 62 | AddEventHandler("playerSpawned", function() 63 | print("^0ND Framework support discord: ^5https://discord.gg/Z9Mxu72zZ6") 64 | SetCanAttackFriendly(PlayerPedId(), true, false) 65 | NetworkSetFriendlyFireOption(true) 66 | end) 67 | 68 | AddEventHandler("onResourceStart", function(resourceName) 69 | if resourceName ~= GetCurrentResourceName() then return end 70 | SetCanAttackFriendly(PlayerPedId(), true, false) 71 | NetworkSetFriendlyFireOption(true) 72 | end) 73 | -------------------------------------------------------------------------------- /server/functions.lua: -------------------------------------------------------------------------------- 1 | function NDCore.getPlayerIdentifierByType(src, indentifierType) 2 | if Config.sv_lan then 3 | return ("%s:sv_lan"):format(indentifierType) 4 | end 5 | return GetPlayerIdentifierByType(src, indentifierType) 6 | end 7 | 8 | ---@param src number 9 | ---@return table 10 | function NDCore.getPlayer(src) 11 | return NDCore.players[src] 12 | end 13 | 14 | ---@param metadata string 15 | ---@param data any 16 | ---@return table 17 | function NDCore.getPlayers(key, value, returnArray) 18 | if not key or not value then return NDCore.players end 19 | 20 | local players = {} 21 | local keyTypes = { 22 | id = "id", 23 | firstname = "firstname", 24 | lastname = "lastname", 25 | gender = "gender", 26 | groups = "groups", 27 | job = "job", 28 | gender = "gender" 29 | } 30 | 31 | local findBy = keyTypes[key] or "metadata" 32 | 33 | if findBy then 34 | for src, info in pairs(NDCore.players) do 35 | if findBy == "metadata" and info["metadata"][key] == value or info[findBy] == value then 36 | if returnArray then 37 | players[#players+1] = info 38 | else 39 | players[src] = info 40 | end 41 | end 42 | end 43 | end 44 | return players 45 | end 46 | 47 | ---@param source number 48 | ---@return table 49 | function NDCore.getPlayerServerInfo(source) 50 | return PlayersInfo[source] 51 | end 52 | 53 | function NDCore.getConfig(info) 54 | if not info then 55 | return Config 56 | end 57 | return Config[info] 58 | end 59 | 60 | ---@param fileLocation string|tabale 61 | ---@return boolean 62 | function NDCore.loadSQL(fileLocation, resource) 63 | local resourceName = resource or GetInvokingResource() or GetCurrentResourceName() 64 | 65 | if type(fileLocation) == "string" then 66 | local file = LoadResourceFile(resourceName, fileLocation) 67 | if not file then return end 68 | MySQL.query(file) 69 | return true 70 | end 71 | 72 | for i=1, #fileLocation do 73 | local file = LoadResourceFile(resourceName, fileLocation[i]) 74 | if file then 75 | MySQL.query(file) 76 | Wait(100) 77 | end 78 | end 79 | return true 80 | end 81 | 82 | function NDCore.getDiscordInfo(discordUserId) 83 | if not discordUserId or not Config.discordBotToken or not Config.discordGuildId then return end 84 | local done = false 85 | local data 86 | local discordErrors = { 87 | [400] = "Improper HTTP request", 88 | [401] = "Discord bot token might be missing or incorrect", 89 | [404] = "User might not be in the server", 90 | [429] = "Discord bot rate limited" 91 | } 92 | 93 | if type(discordUserId) == "string" and discordUserId:find("discord:") then discordUserId:gsub("discord:", "") end 94 | 95 | PerformHttpRequest(("https://discordapp.com/api/guilds/%s/members/%s"):format(Config.discordGuildId, discordUserId), function(errorCode, resultData, resultHeaders) 96 | if errorCode ~= 200 then 97 | done = true 98 | return print(("^3Warning: %d %s"):format(errorCode, discordErrors[errorCode])) 99 | end 100 | 101 | local result = json.decode(resultData) 102 | data = { 103 | nickname = result.nick or result.user.username, 104 | user = result.user, 105 | roles = result.roles 106 | } 107 | done = true 108 | end, "GET", "", {["Content-Type"] = "application/json", ["Authorization"] = ("Bot %s"):format(Config.discordBotToken)}) 109 | 110 | while not done do Wait(50) end 111 | return data 112 | end 113 | 114 | function NDCore.enableMultiCharacter(enable) 115 | Config.multiCharacter = enable 116 | end 117 | 118 | for name, func in pairs(NDCore) do 119 | if type(func) == "function" then 120 | exports(name, func) 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /locales/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "لم يتم العثور على حساب ديسكورد الخاص بك، انضم إلى ديسكورد هنا: %s.", 3 | "connecting": "جارٍ الاتصال...", 4 | "identifier_not_found": "لم يتم العثور على %s الخاص بك.", 5 | "deposit": "إيداع", 6 | "withdraw": "سحب", 7 | "vehicle_ownership_transfered": "تم نقل الملكية", 8 | "vehicle_ownership_transfered2": "تم نقل ملكية المركبة %s.", 9 | "vehicle_ownership_received": "تم استلام الملكية", 10 | "vehicle_ownership_received2": "تم استلام ملكية المركبة %s.", 11 | "keys_shared": "تمت مشاركة المفاتيح", 12 | "keys_shared2": "لقد شاركت مفاتيح المركبة مع %s.", 13 | "keys_received": "تم استلام المفاتيح", 14 | "keys_received2": "لقد استلمت مفاتيح المركبة من %s.", 15 | "no_signal": "لا يوجد إشارة", 16 | "veh_key_disabled": "تم تعطيل مفتاح المركبة.", 17 | "veh_key_disabled2": "تم تعطيل هذا المفتاح ولا يمكن استخدامه بعد الآن.", 18 | "veh_far_away": "المركبة بعيدة جدًا.", 19 | "veh_locked": "مقفول", 20 | "veh_locked2": "تم قفل مركبتك الآن.", 21 | "veh_unlocked": "مفتوح", 22 | "veh_unlocked2": "تم فتح مركبتك الآن.", 23 | "garage": "كراج", 24 | "no_owned_veh_nearby": "لم يتم العثور على مركبة مملوكة بالقرب منك.", 25 | "veh_stored_in_garage": "تم تخزين المركبة في الكراج.", 26 | "impound_reclaim": "استعادة المركبة من الحجز", 27 | "impound": "حجز", 28 | "impound_not_enough": "سعر الاستعادة هو $%d، ليس لديك ما يكفي!", 29 | "impound_paid": "تم دفع $%d لاستعادة المركبة!", 30 | "success": "نجاح", 31 | "staff_action": "إجراء من الموظفين", 32 | "staff_money_removed": "تم إزالة $%d (%s) من %s", 33 | "user_money_removed": "تمت إزالة $%d من %s", 34 | "staff_money_added": "تمت إضافة $%d (%s) إلى %s", 35 | "user_money_added": "تمت إضافة $%d إلى %s", 36 | "staff_money_set": "تم ضبط %s (%s) على $%d", 37 | "user_money_set": "تم ضبط %s على $%d", 38 | "job_updated_noti": "تم تحديث الوظيفة إلى %s، الرتبة %s.", 39 | "group_added_noti": "تمت إضافتك إلى مجموعة %s، الرتبة %s.", 40 | "group_removed_noti": "تمت إزالتك من مجموعة %s.", 41 | "no_player_found": "لم يتم العثور على لاعب", 42 | "not_enough_cash": "ليس لديك ما يكفي من النقود", 43 | "player_not_nearby": "اللاعب ليس بالقرب منك", 44 | "cant_give_money": "تعذر إعطاء المال", 45 | "money_received": "تم استلام المال", 46 | "money_received2": "تم استلام $%d نقدًا", 47 | "money_given": "تم إعطاء المال", 48 | "money_given2": "لقد أعطيت $%d نقدًا", 49 | "player_in_veh": "اللاعب داخل المركبة!", 50 | "perfect": "ممتاز", 51 | "good": "جيد", 52 | "bad": "سيء", 53 | "very_bad": "سيء جدًا", 54 | "veh_make_brand": "الصانع", 55 | "veh_model": "الطراز", 56 | "veh_plate": "اللوحة", 57 | "engine_status": "حالة المحرك", 58 | "vehicle": "مركبة", 59 | "not_found": "غير موجود", 60 | "park_veh": "ركن المركبة", 61 | "no_vehs_found": "لم يتم العثور على مركبات", 62 | "vehicle_impound": "حجز المركبة", 63 | "parking_garage": "كراج سيارات", 64 | "impound_w_location": "الحجز (%s)", 65 | "garage_w_location": "كراج السيارات (%s)", 66 | "view_impounded_vehs": "عرض المركبات المحجوزة", 67 | "view_garage": "عرض الكراج", 68 | "personal_vehicle": "مركبة شخصية", 69 | "keybind_carkey": "فتح/قفل المركبة (نقر مزدوج)", 70 | "progress_hotwiring": "تشغيل المركبة بدون مفتاح", 71 | "cruise_control": "التحكم بالسرعة", 72 | "cruise_control_enabled": "تم تفعيل التحكم بالسرعة", 73 | "cruise_control_disabled": "تم تعطيل التحكم بالسرعة", 74 | "cruise_control_toggle": "تبديل التحكم بالسرعة", 75 | "cruise_control_increase": "زيادة سرعة التحكم", 76 | "seat_shuffle": "تبديل مقعد المركبة", 77 | "discord_text": "تلعب: %s كـ %s %s", 78 | "discord_text_small": "تلعب كـ: %s %s", 79 | "discord_text_server": "تلعب: %s", 80 | "pause_menu_cash": "نقد: $%d", 81 | "pause_menu_bank": "البنك: $%d", 82 | "veh_color_black": "أسود", 83 | "veh_color_silver": "فضي", 84 | "veh_color_grey": "رمادي", 85 | "veh_color_metal": "معدني", 86 | "veh_color_graphite": "جرافيت", 87 | "veh_color_red": "أحمر", 88 | "veh_color_orange": "برتقالي", 89 | "veh_color_gold": "ذهبي", 90 | "veh_color_yellow": "أصفر", 91 | "veh_color_green": "أخضر", 92 | "veh_color_blue": "أزرق", 93 | "veh_color_bblue": "أزرق فاتح", 94 | "veh_color_bronze": "برونزي", 95 | "veh_color_lime": "ليموني", 96 | "veh_color_champagne": "شامبانيا", 97 | "veh_color_beige": "بيج", 98 | "veh_color_ivory": "عاجي", 99 | "veh_color_brown": "بني", 100 | "veh_color_beechwood": "خشب الزان", 101 | "veh_color_sand": "رملي", 102 | "veh_color_cream": "كريمي", 103 | "veh_color_white": "أبيض", 104 | "veh_color_steel": "فولاذي", 105 | "veh_color_aluminium": "ألمنيوم", 106 | "veh_color_chrome": "كروم", 107 | "veh_color_pink": "وردي", 108 | "veh_color_purple": "بنفسجي", 109 | "veh_color_tan": "قمحي", 110 | "veh_color_alloy": "سبيكة", 111 | "veh_class_compact": "مدمجة", 112 | "veh_class_sedan": "سيدان", 113 | "veh_class_suv": "دفع رباعي", 114 | "veh_class_coupe": "كوبيه", 115 | "veh_class_muscle": "عضلية", 116 | "veh_class_sports_classic": "رياضية كلاسيكية", 117 | "veh_class_sport": "رياضية", 118 | "veh_class_super": "سوبر", 119 | "veh_class_motorcycle": "دراجة نارية", 120 | "veh_class_off_road": "طرق وعرة", 121 | "veh_class_industrial": "صناعية", 122 | "veh_class_utility": "خدمية", 123 | "veh_class_van": "فان", 124 | "veh_class_cycle": "دراجة", 125 | "veh_class_boat": "قارب", 126 | "veh_class_helicopter": "مروحية", 127 | "veh_class_plane": "طائرة", 128 | "veh_class_service": "خدمة", 129 | "veh_class_emergency": "طوارئ", 130 | "veh_class_military": "عسكرية", 131 | "veh_class_commercial": "تجارية", 132 | "veh_class_train": "قطار", 133 | "veh_class_open_wheel": "مفتوح العجلات" 134 | } 135 | -------------------------------------------------------------------------------- /locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Your discord was not found, join our discord here: %s.", 3 | "connecting": "Connecting...", 4 | "identifier_not_found": "Your %s was not found.", 5 | "deposit": "Deposit", 6 | "withdraw": "Withdraw", 7 | "vehicle_ownership_transfered": "Ownership transfered", 8 | "vehicle_ownership_transfered2": "Vehicle ownership of %s has been transfered.", 9 | "vehicle_ownership_received": "Ownership received", 10 | "vehicle_ownership_received2": "Received vehicle ownership of %s.", 11 | "keys_shared": "Keys shared", 12 | "keys_shared2": "You've shared vehicle keys to %s.", 13 | "keys_received": "Keys received", 14 | "keys_received2": "You've shared vehicle keys to %s.", 15 | "no_signal": "No signal", 16 | "veh_key_disabled": "Vehicle key disabled.", 17 | "veh_key_disabled2": "This vehicle key has been disabled and cannot be used anymore.", 18 | "veh_far_away": "Vehicle to far away.", 19 | "veh_locked": "LOCKED", 20 | "veh_locked2": "Your vehicle has now been locked.", 21 | "veh_unlocked": "UNLOCKED", 22 | "veh_unlocked2": "Your vehicle has now been unlocked.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "No owned vehicle found nearby.", 25 | "veh_stored_in_garage": "Vehicle stored in garage.", 26 | "impound_reclaim": "Vehicle impound reclaim", 27 | "impound": "Impound", 28 | "impound_not_enough": "Price to reclaim is $%d, you don't have enough!", 29 | "impound_paid": "Paid $%d to reclaim vehicle!", 30 | "success": "success", 31 | "staff_action": "Staff action", 32 | "staff_money_removed": "Removed $%d (%s) from %s", 33 | "user_money_removed": "removed $%d from %s", 34 | "staff_money_added": "Added $%d (%s) to %s", 35 | "user_money_added": "added $%d to %s", 36 | "staff_money_set": "Set %s's (%s) to $%d", 37 | "user_money_set": "set %s to $%d", 38 | "job_updated_noti": "Job updated to %s, rank %s.", 39 | "group_added_noti": "Added to group %s, rank %s.", 40 | "group_removed_noti": "Removed from group %s.", 41 | "no_player_found": "No player found", 42 | "not_enough_cash": "You don't have enough cash", 43 | "player_not_nearby": "Player is not nearby", 44 | "cant_give_money": "Couldn't give money", 45 | "money_received": "Money received", 46 | "money_received2": "Received $%d in cash", 47 | "money_given": "Money given", 48 | "money_given2": "You gave $%d in cash", 49 | "player_in_veh": "Player in vehicle!", 50 | "perfect": "Perfect", 51 | "good": "Good", 52 | "bad": "Bad", 53 | "very_bad": "Very bad", 54 | "veh_make_brand": "Make", 55 | "veh_model": "Model", 56 | "veh_plate": "Plate", 57 | "engine_status": "Engine status", 58 | "vehicle": "Vehicle", 59 | "not_found": "not found", 60 | "park_veh": "Park vehicle", 61 | "no_vehs_found": "No vehicles found", 62 | "vehicle_impound": "Vehicle impound", 63 | "parking_garage": "Parking garage", 64 | "impound_w_location": "Impound (%s)", 65 | "garage_w_location": "Parking garage (%s)", 66 | "view_impounded_vehs": "View impounded vehicles", 67 | "view_garage": "View garage", 68 | "personal_vehicle": "Personal vehicle", 69 | "keybind_carkey": "Unlock/lock vehicle (double click)", 70 | "progress_hotwiring": "Hotwiring", 71 | "cruise_control": "Cruise control", 72 | "cruise_control_enabled": "Vehicle cruise control enabled", 73 | "cruise_control_disabled": "Vehicle cruise control disabled", 74 | "cruise_control_toggle": "Toggle vehicle cruise control", 75 | "cruise_control_increase": "Increased cruise control speed", 76 | "seat_shuffle": "Shuffle vehicle seat", 77 | "discord_text": "Playing: %s as %s %s", 78 | "discord_text_small": "Playing as: %s %s", 79 | "discord_text_server": "Playing: %s", 80 | "pause_menu_cash": "Cash: $%d", 81 | "pause_menu_bank": "Bank: $%d", 82 | 83 | "veh_color_black": "Black", 84 | "veh_color_silver": "Silver", 85 | "veh_color_grey": "Grey", 86 | "veh_color_metal": "Metal", 87 | "veh_color_graphite": "Graphite", 88 | "veh_color_red": "Red", 89 | "veh_color_orange": "Orange", 90 | "veh_color_gold": "Gold", 91 | "veh_color_yellow": "Yellow", 92 | "veh_color_green": "Green", 93 | "veh_color_blue": "Blue", 94 | "veh_color_bblue": "Bblue", 95 | "veh_color_bronze": "Bronze", 96 | "veh_color_lime": "Lime", 97 | "veh_color_champagne": "Champagne", 98 | "veh_color_beige": "Beige", 99 | "veh_color_ivory": "Ivory", 100 | "veh_color_brown": "Brown", 101 | "veh_color_beechwood": "Beechwood", 102 | "veh_color_sand": "Sand", 103 | "veh_color_cream": "Cream", 104 | "veh_color_white": "White", 105 | "veh_color_steel": "Steel", 106 | "veh_color_aluminium": "Aluminium", 107 | "veh_color_chrome": "Chrome", 108 | "veh_color_pink": "Pink", 109 | "veh_color_purple": "Purple", 110 | "veh_color_tan": "Tan", 111 | "veh_color_alloy": "Alloy", 112 | "veh_class_compact": "Compact", 113 | "veh_class_sedan": "Sedan", 114 | "veh_class_suv": "SUV", 115 | "veh_class_coupe": "Coupe", 116 | "veh_class_muscle": "Muscle", 117 | "veh_class_sports_classic": "Sports Classic", 118 | "veh_class_sport": "Sport", 119 | "veh_class_super": "Super", 120 | "veh_class_motorcycle": "Motorcycle", 121 | "veh_class_off_road": "Off-road", 122 | "veh_class_industrial": "Industrial", 123 | "veh_class_utility": "Utility", 124 | "veh_class_van": "Van", 125 | "veh_class_cycle": "Cycle", 126 | "veh_class_boat": "Boat", 127 | "veh_class_helicopter": "Helicopter", 128 | "veh_class_plane": "Plane", 129 | "veh_class_service": "Service", 130 | "veh_class_emergency": "Emergency", 131 | "veh_class_military": "Military", 132 | "veh_class_commercial": "Commercial", 133 | "veh_class_train": "Train", 134 | "veh_class_open_wheel": "Open wheel" 135 | } 136 | -------------------------------------------------------------------------------- /locales/da.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Din Discord blev ikke fundet, deltag i vores Discord her: %s.", 3 | "connecting": "Forbinder...", 4 | "identifier_not_found": "Din %s blev ikke fundet.", 5 | "deposit": "Indsæt", 6 | "withdraw": "Hæv", 7 | "vehicle_ownership_transfered": "Ejerskab overført", 8 | "vehicle_ownership_transfered2": "Ejerskab af køretøj %s er blevet overført.", 9 | "vehicle_ownership_received": "Ejerskab modtaget", 10 | "vehicle_ownership_received2": "Modtog ejerskab af køretøj %s.", 11 | "keys_shared": "Nøgler delt", 12 | "keys_shared2": "Du har delt bilnøgler med %s.", 13 | "keys_received": "Nøgler modtaget", 14 | "keys_received2": "Du har modtaget bilnøgler fra %s.", 15 | "no_signal": "Ingen signal", 16 | "veh_key_disabled": "Bilnøgle deaktiveret.", 17 | "veh_key_disabled2": "Denne bilnøgle er blevet deaktiveret og kan ikke bruges længere.", 18 | "veh_far_away": "Køretøj for langt væk.", 19 | "veh_locked": "LÅST", 20 | "veh_locked2": "Dit køretøj er nu låst.", 21 | "veh_unlocked": "ULÅST", 22 | "veh_unlocked2": "Dit køretøj er nu ulåst.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "Ingen ejet køretøj fundet i nærheden.", 25 | "veh_stored_in_garage": "Køretøj gemt i garage.", 26 | "impound_reclaim": "Hent køretøj fra beslaglæggelse", 27 | "impound": "Beslaglæggelse", 28 | "impound_not_enough": "Prisen for at hente er %d kr, du har ikke nok!", 29 | "impound_paid": "Betalt %d kr for at hente køretøj!", 30 | "success": "succes", 31 | "staff_action": "Personalehandling", 32 | "staff_money_removed": "Fjernede %d kr (%s) fra %s", 33 | "user_money_removed": "fjernede %d kr fra %s", 34 | "staff_money_added": "Tilføjede %d kr (%s) til %s", 35 | "user_money_added": "tilføjede %d kr til %s", 36 | "staff_money_set": "Satte %s's (%s) til %d kr", 37 | "user_money_set": "satte %s til %d kr", 38 | "job_updated_noti": "Job opdateret til %s, rang %s.", 39 | "group_added_noti": "Tilføjet til gruppe %s, rang %s.", 40 | "group_removed_noti": "Fjernet fra gruppe %s.", 41 | "no_player_found": "Ingen spiller fundet", 42 | "not_enough_cash": "Du har ikke nok kontanter", 43 | "player_not_nearby": "Spiller ikke i nærheden", 44 | "cant_give_money": "Kunne ikke give penge", 45 | "money_received": "Penge modtaget", 46 | "money_received2": "Modtog %d kr i kontanter", 47 | "money_given": "Penge givet", 48 | "money_given2": "Du gav %d kr i kontanter", 49 | "player_in_veh": "Spiller i køretøj!", 50 | "perfect": "Perfekt", 51 | "good": "God", 52 | "bad": "Dårlig", 53 | "very_bad": "Meget dårlig", 54 | "veh_make_brand": "Mærke", 55 | "veh_model": "Model", 56 | "veh_plate": "Nummerplade", 57 | "engine_status": "Motorstatus", 58 | "vehicle": "Køretøj", 59 | "not_found": "ikke fundet", 60 | "park_veh": "Parkér køretøj", 61 | "no_vehs_found": "Ingen køretøjer fundet", 62 | "vehicle_impound": "Køretøj beslaglagt", 63 | "parking_garage": "Parkeringsgarage", 64 | "impound_w_location": "Beslaglæggelse (%s)", 65 | "garage_w_location": "Parkeringsgarage (%s)", 66 | "view_impounded_vehs": "Se beslaglagte køretøjer", 67 | "view_garage": "Se garage", 68 | "personal_vehicle": "Personligt køretøj", 69 | "keybind_carkey": "Lås/lås op for køretøj (dobbeltklik)", 70 | "progress_hotwiring": "Koble tænding", 71 | "cruise_control": "Fartpilot", 72 | "cruise_control_enabled": "Fartpilot aktiveret", 73 | "cruise_control_disabled": "Fartpilot deaktiveret", 74 | "cruise_control_toggle": "Skift fartpilot", 75 | "cruise_control_increase": "Øget fartpilot hastighed", 76 | "seat_shuffle": "Skift sæde", 77 | "discord_text": "Spiller: %s som %s %s", 78 | "discord_text_small": "Spiller som: %s %s", 79 | "discord_text_server": "Spiller: %s", 80 | "pause_menu_cash": "Kontanter: %d", 81 | "pause_menu_bank": "Bank: %d", 82 | "veh_color_black": "Sort", 83 | "veh_color_silver": "Sølv", 84 | "veh_color_grey": "Grå", 85 | "veh_color_metal": "Metal", 86 | "veh_color_graphite": "Grafit", 87 | "veh_color_red": "Rød", 88 | "veh_color_orange": "Orange", 89 | "veh_color_gold": "Guld", 90 | "veh_color_yellow": "Gul", 91 | "veh_color_green": "Grøn", 92 | "veh_color_blue": "Blå", 93 | "veh_color_bblue": "Lyseblå", 94 | "veh_color_bronze": "Bronze", 95 | "veh_color_lime": "Lime", 96 | "veh_color_champagne": "Champagne", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Elfenben", 99 | "veh_color_brown": "Brun", 100 | "veh_color_beechwood": "Bøgetræ", 101 | "veh_color_sand": "Sand", 102 | "veh_color_cream": "Creme", 103 | "veh_color_white": "Hvid", 104 | "veh_color_steel": "Stål", 105 | "veh_color_aluminium": "Aluminium", 106 | "veh_color_chrome": "Krom", 107 | "veh_color_pink": "Pink", 108 | "veh_color_purple": "Lilla", 109 | "veh_color_tan": "Tan", 110 | "veh_color_alloy": "Legering", 111 | "veh_class_compact": "Kompakt", 112 | "veh_class_sedan": "Sedan", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupé", 115 | "veh_class_muscle": "Muskelbil", 116 | "veh_class_sports_classic": "Klassisk sportsvogn", 117 | "veh_class_sport": "Sport", 118 | "veh_class_super": "Superbil", 119 | "veh_class_motorcycle": "Motorcykel", 120 | "veh_class_off_road": "Off-road", 121 | "veh_class_industrial": "Industriel", 122 | "veh_class_utility": "Utility", 123 | "veh_class_van": "Varevogn", 124 | "veh_class_cycle": "Cykel", 125 | "veh_class_boat": "Båd", 126 | "veh_class_helicopter": "Helikopter", 127 | "veh_class_plane": "Fly", 128 | "veh_class_service": "Service", 129 | "veh_class_emergency": "Nød", 130 | "veh_class_military": "Militær", 131 | "veh_class_commercial": "Kommerciel", 132 | "veh_class_train": "Tog", 133 | "veh_class_open_wheel": "Åben hjul" 134 | } 135 | -------------------------------------------------------------------------------- /locales/no.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Discorden din ble ikke funnet, bli med i vår Discord her: %s.", 3 | "connecting": "Kobler til...", 4 | "identifier_not_found": "Din %s ble ikke funnet.", 5 | "deposit": "Innskudd", 6 | "withdraw": "Ta ut", 7 | "vehicle_ownership_transfered": "Eierskap overført", 8 | "vehicle_ownership_transfered2": "Eierskapet til kjøretøyet %s har blitt overført.", 9 | "vehicle_ownership_received": "Eierskap mottatt", 10 | "vehicle_ownership_received2": "Mottatt eierskap til kjøretøyet %s.", 11 | "keys_shared": "Nøkler delt", 12 | "keys_shared2": "Du har delt kjøretøynøkler med %s.", 13 | "keys_received": "Nøkler mottatt", 14 | "keys_received2": "Du har mottatt kjøretøynøkler fra %s.", 15 | "no_signal": "Ingen signal", 16 | "veh_key_disabled": "Kjøretøynøkkel deaktivert.", 17 | "veh_key_disabled2": "Denne nøkkelen er deaktivert og kan ikke brukes lenger.", 18 | "veh_far_away": "Kjøretøyet er for langt unna.", 19 | "veh_locked": "LÅST", 20 | "veh_locked2": "Kjøretøyet ditt er nå låst.", 21 | "veh_unlocked": "ÅPEN", 22 | "veh_unlocked2": "Kjøretøyet ditt er nå åpnet.", 23 | "garage": "Garasje", 24 | "no_owned_veh_nearby": "Ingen eide kjøretøy i nærheten.", 25 | "veh_stored_in_garage": "Kjøretøy lagret i garasje.", 26 | "impound_reclaim": "Hent kjøretøy fra beslag", 27 | "impound": "Beslag", 28 | "impound_not_enough": "Pris for å hente er %d kr, du har ikke nok!", 29 | "impound_paid": "Betalt %d kr for å hente kjøretøy!", 30 | "success": "suksess", 31 | "staff_action": "Ansatt handling", 32 | "staff_money_removed": "Fjernet %d kr (%s) fra %s", 33 | "user_money_removed": "Fjernet %d kr fra %s", 34 | "staff_money_added": "Lagt til %d kr (%s) til %s", 35 | "user_money_added": "Lagt til %d kr til %s", 36 | "staff_money_set": "Stilt inn %s sin (%s) til %d kr", 37 | "user_money_set": "Stilt inn %s til %d kr", 38 | "job_updated_noti": "Jobb oppdatert til %s, rang %s.", 39 | "group_added_noti": "Lagt til gruppe %s, rang %s.", 40 | "group_removed_noti": "Fjernet fra gruppe %s.", 41 | "no_player_found": "Ingen spiller funnet", 42 | "not_enough_cash": "Du har ikke nok kontanter", 43 | "player_not_nearby": "Spiller er ikke i nærheten", 44 | "cant_give_money": "Kunne ikke gi penger", 45 | "money_received": "Penger mottatt", 46 | "money_received2": "Mottatt %d kr kontant", 47 | "money_given": "Penger gitt", 48 | "money_given2": "Du ga %d kr kontant", 49 | "player_in_veh": "Spiller i kjøretøy!", 50 | "perfect": "Perfekt", 51 | "good": "Bra", 52 | "bad": "Dårlig", 53 | "very_bad": "Veldig dårlig", 54 | "veh_make_brand": "Merke", 55 | "veh_model": "Modell", 56 | "veh_plate": "Skilt", 57 | "engine_status": "Motorstatus", 58 | "vehicle": "Kjøretøy", 59 | "not_found": "ikke funnet", 60 | "park_veh": "Parker kjøretøy", 61 | "no_vehs_found": "Ingen kjøretøy funnet", 62 | "vehicle_impound": "Kjøretøy beslag", 63 | "parking_garage": "Parkeringsgarasje", 64 | "impound_w_location": "Beslag (%s)", 65 | "garage_w_location": "Garasje (%s)", 66 | "view_impounded_vehs": "Se beslaglagte kjøretøy", 67 | "view_garage": "Se garasje", 68 | "personal_vehicle": "Personlig kjøretøy", 69 | "keybind_carkey": "Lås/åpne kjøretøy (dobbeltklikk)", 70 | "progress_hotwiring": "Tjuvstart", 71 | "cruise_control": "Cruise control", 72 | "cruise_control_enabled": "Cruise control aktivert", 73 | "cruise_control_disabled": "Cruise control deaktivert", 74 | "cruise_control_toggle": "Bytt cruise control", 75 | "cruise_control_increase": "Cruise control hastighet økt", 76 | "seat_shuffle": "Bytt plass i kjøretøy", 77 | "discord_text": "Spiller: %s som %s %s", 78 | "discord_text_small": "Spiller som: %s %s", 79 | "discord_text_server": "Spiller: %s", 80 | "pause_menu_cash": "Kontanter: %d", 81 | "pause_menu_bank": "Bank: %d", 82 | "veh_color_black": "Svart", 83 | "veh_color_silver": "Sølv", 84 | "veh_color_grey": "Grå", 85 | "veh_color_metal": "Metall", 86 | "veh_color_graphite": "Grafitt", 87 | "veh_color_red": "Rød", 88 | "veh_color_orange": "Oransje", 89 | "veh_color_gold": "Gull", 90 | "veh_color_yellow": "Gul", 91 | "veh_color_green": "Grønn", 92 | "veh_color_blue": "Blå", 93 | "veh_color_bblue": "Lyseblå", 94 | "veh_color_bronze": "Bronse", 95 | "veh_color_lime": "Lime", 96 | "veh_color_champagne": "Champagne", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Elfenben", 99 | "veh_color_brown": "Brun", 100 | "veh_color_beechwood": "Bøk", 101 | "veh_color_sand": "Sand", 102 | "veh_color_cream": "Krem", 103 | "veh_color_white": "Hvit", 104 | "veh_color_steel": "Stål", 105 | "veh_color_aluminium": "Aluminium", 106 | "veh_color_chrome": "Krom", 107 | "veh_color_pink": "Rosa", 108 | "veh_color_purple": "Lilla", 109 | "veh_color_tan": "Brunlig", 110 | "veh_color_alloy": "Aluminiumlegering", 111 | "veh_class_compact": "Kompakt", 112 | "veh_class_sedan": "Sedan", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupe", 115 | "veh_class_muscle": "Muscle", 116 | "veh_class_sports_classic": "Sports Classic", 117 | "veh_class_sport": "Sport", 118 | "veh_class_super": "Super", 119 | "veh_class_motorcycle": "Motorsykkel", 120 | "veh_class_off_road": "Terreng", 121 | "veh_class_industrial": "Industriell", 122 | "veh_class_utility": "Nyttig", 123 | "veh_class_van": "Varebil", 124 | "veh_class_cycle": "Sykkel", 125 | "veh_class_boat": "Båt", 126 | "veh_class_helicopter": "Helikopter", 127 | "veh_class_plane": "Fly", 128 | "veh_class_service": "Service", 129 | "veh_class_emergency": "Nød", 130 | "veh_class_military": "Militær", 131 | "veh_class_commercial": "Kommersiell", 132 | "veh_class_train": "Tog", 133 | "veh_class_open_wheel": "Åpen hjul" 134 | } 135 | -------------------------------------------------------------------------------- /locales/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Din Discord hittades inte, gå med i vår Discord här: %s.", 3 | "connecting": "Ansluter...", 4 | "identifier_not_found": "Din %s hittades inte.", 5 | "deposit": "Sätt in", 6 | "withdraw": "Ta ut", 7 | "vehicle_ownership_transfered": "Ägarskap överfört", 8 | "vehicle_ownership_transfered2": "Fordonets ägarskap för %s har överförts.", 9 | "vehicle_ownership_received": "Ägarskap mottaget", 10 | "vehicle_ownership_received2": "Mottagit fordonets ägarskap för %s.", 11 | "keys_shared": "Nycklar delade", 12 | "keys_shared2": "Du har delat fordonsnycklar med %s.", 13 | "keys_received": "Nycklar mottagna", 14 | "keys_received2": "Du har mottagit fordonsnycklar från %s.", 15 | "no_signal": "Ingen signal", 16 | "veh_key_disabled": "Fordonsnyckel inaktiverad.", 17 | "veh_key_disabled2": "Denna fordonsnyckel har inaktiverats och kan inte längre användas.", 18 | "veh_far_away": "Fordonet är för långt borta.", 19 | "veh_locked": "LÅST", 20 | "veh_locked2": "Ditt fordon är nu låst.", 21 | "veh_unlocked": "OLÅST", 22 | "veh_unlocked2": "Ditt fordon är nu olåst.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "Inget ägt fordon hittades i närheten.", 25 | "veh_stored_in_garage": "Fordonet har placerats i garaget.", 26 | "impound_reclaim": "Återtagning av beslagtaget fordon", 27 | "impound": "Beslag", 28 | "impound_not_enough": "Priset för att återta är %d kr, du har inte tillräckligt!", 29 | "impound_paid": "Betalade %d kr för att återta fordonet!", 30 | "success": "framgång", 31 | "staff_action": "Personalåtgärd", 32 | "staff_money_removed": "Tog bort %d kr (%s) från %s", 33 | "user_money_removed": "tog bort %d kr från %s", 34 | "staff_money_added": "Lade till %d kr (%s) till %s", 35 | "user_money_added": "lade till %d kr till %s", 36 | "staff_money_set": "Satte %s:s (%s) till %d kr", 37 | "user_money_set": "satte %s till %d kr", 38 | "job_updated_noti": "Jobb uppdaterat till %s, rang %s.", 39 | "group_added_noti": "Tillagd i gruppen %s, rang %s.", 40 | "group_removed_noti": "Borttagen från gruppen %s.", 41 | "no_player_found": "Ingen spelare hittades", 42 | "not_enough_cash": "Du har inte tillräckligt med kontanter", 43 | "player_not_nearby": "Spelaren är inte i närheten", 44 | "cant_give_money": "Kunde inte ge pengar", 45 | "money_received": "Pengar mottagna", 46 | "money_received2": "Mottog %d kr kontant", 47 | "money_given": "Pengar givna", 48 | "money_given2": "Du gav %d kr kontant", 49 | "player_in_veh": "Spelaren är i fordon!", 50 | "perfect": "Perfekt", 51 | "good": "Bra", 52 | "bad": "Dålig", 53 | "very_bad": "Mycket dålig", 54 | "veh_make_brand": "Märke", 55 | "veh_model": "Modell", 56 | "veh_plate": "Registreringsskylt", 57 | "engine_status": "Motorstatus", 58 | "vehicle": "Fordon", 59 | "not_found": "hittades inte", 60 | "park_veh": "Parkera fordon", 61 | "no_vehs_found": "Inga fordon hittades", 62 | "vehicle_impound": "Beslagtagna fordon", 63 | "parking_garage": "Parkeringsgarage", 64 | "impound_w_location": "Beslag (%s)", 65 | "garage_w_location": "Parkeringsgarage (%s)", 66 | "view_impounded_vehs": "Visa beslagtagna fordon", 67 | "view_garage": "Visa garage", 68 | "personal_vehicle": "Personligt fordon", 69 | "keybind_carkey": "Lås/öppna fordon (dubbelklick)", 70 | "progress_hotwiring": "Kopplar om", 71 | "cruise_control": "Farthållare", 72 | "cruise_control_enabled": "Fordonets farthållare aktiverad", 73 | "cruise_control_disabled": "Fordonets farthållare avaktiverad", 74 | "cruise_control_toggle": "Växla fordonets farthållare", 75 | "cruise_control_increase": "Ökade farthållarens hastighet", 76 | "seat_shuffle": "Byt plats i fordonet", 77 | "discord_text": "Spelar: %s som %s %s", 78 | "discord_text_small": "Spelar som: %s %s", 79 | "discord_text_server": "Spelar: %s", 80 | "pause_menu_cash": "Kontanter: %d", 81 | "pause_menu_bank": "Bank: %d", 82 | 83 | "veh_color_black": "Svart", 84 | "veh_color_silver": "Silver", 85 | "veh_color_grey": "Grå", 86 | "veh_color_metal": "Metall", 87 | "veh_color_graphite": "Grafit", 88 | "veh_color_red": "Röd", 89 | "veh_color_orange": "Orange", 90 | "veh_color_gold": "Guld", 91 | "veh_color_yellow": "Gul", 92 | "veh_color_green": "Grön", 93 | "veh_color_blue": "Blå", 94 | "veh_color_bblue": "Ljusblå", 95 | "veh_color_bronze": "Brons", 96 | "veh_color_lime": "Lime", 97 | "veh_color_champagne": "Champagne", 98 | "veh_color_beige": "Beige", 99 | "veh_color_ivory": "Elfenben", 100 | "veh_color_brown": "Brun", 101 | "veh_color_beechwood": "Bokträ", 102 | "veh_color_sand": "Sand", 103 | "veh_color_cream": "Kräm", 104 | "veh_color_white": "Vit", 105 | "veh_color_steel": "Stål", 106 | "veh_color_aluminium": "Aluminium", 107 | "veh_color_chrome": "Krom", 108 | "veh_color_pink": "Rosa", 109 | "veh_color_purple": "Lila", 110 | "veh_color_tan": "Ljusbrun", 111 | "veh_color_alloy": "Legering", 112 | "veh_class_compact": "Kompakt", 113 | "veh_class_sedan": "Sedan", 114 | "veh_class_suv": "SUV", 115 | "veh_class_coupe": "Coupé", 116 | "veh_class_muscle": "Muskelbil", 117 | "veh_class_sports_classic": "Klassisk sportbil", 118 | "veh_class_sport": "Sport", 119 | "veh_class_super": "Superbil", 120 | "veh_class_motorcycle": "Motorcykel", 121 | "veh_class_off_road": "Terräng", 122 | "veh_class_industrial": "Industri", 123 | "veh_class_utility": "Nyttolast", 124 | "veh_class_van": "Skåpbil", 125 | "veh_class_cycle": "Cykel", 126 | "veh_class_boat": "Båt", 127 | "veh_class_helicopter": "Helikopter", 128 | "veh_class_plane": "Flygplan", 129 | "veh_class_service": "Service", 130 | "veh_class_emergency": "Utryckning", 131 | "veh_class_military": "Militär", 132 | "veh_class_commercial": "Kommersiell", 133 | "veh_class_train": "Tåg", 134 | "veh_class_open_wheel": "Öppen hjulbil" 135 | } 136 | -------------------------------------------------------------------------------- /locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Je Discord-account is niet gevonden, sluit je hier aan bij onze Discord: %s.", 3 | "connecting": "Verbinden...", 4 | "identifier_not_found": "Je %s is niet gevonden.", 5 | "deposit": "Storten", 6 | "withdraw": "Opnemen", 7 | "vehicle_ownership_transfered": "Eigendom overgedragen", 8 | "vehicle_ownership_transfered2": "Het eigendom van het voertuig %s is overgedragen.", 9 | "vehicle_ownership_received": "Eigendom ontvangen", 10 | "vehicle_ownership_received2": "Je hebt het eigendom van voertuig %s ontvangen.", 11 | "keys_shared": "Sleutels gedeeld", 12 | "keys_shared2": "Je hebt voertuig sleutels gedeeld met %s.", 13 | "keys_received": "Sleutels ontvangen", 14 | "keys_received2": "Je hebt voertuig sleutels ontvangen van %s.", 15 | "no_signal": "Geen signaal", 16 | "veh_key_disabled": "Voertuigsleutel uitgeschakeld.", 17 | "veh_key_disabled2": "Deze voertuigsleutel is uitgeschakeld en kan niet meer worden gebruikt.", 18 | "veh_far_away": "Voertuig te ver weg.", 19 | "veh_locked": "GESLOTEN", 20 | "veh_locked2": "Je voertuig is nu gesloten.", 21 | "veh_unlocked": "GEOPEND", 22 | "veh_unlocked2": "Je voertuig is nu geopend.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "Geen eigendom voertuig in de buurt gevonden.", 25 | "veh_stored_in_garage": "Voertuig opgeslagen in garage.", 26 | "impound_reclaim": "Voertuig terugvorderen uit beslag", 27 | "impound": "Beslag", 28 | "impound_not_enough": "Prijs voor terugvordering is €%d, je hebt niet genoeg!", 29 | "impound_paid": "€%d betaald om voertuig terug te vorderen!", 30 | "success": "succes", 31 | "staff_action": "Staf actie", 32 | "staff_money_removed": "€%d (%s) verwijderd van %s", 33 | "user_money_removed": "€%d verwijderd van %s", 34 | "staff_money_added": "€%d (%s) toegevoegd aan %s", 35 | "user_money_added": "€%d toegevoegd aan %s", 36 | "staff_money_set": "Saldo van %s (%s) ingesteld op €%d", 37 | "user_money_set": "Saldo van %s ingesteld op €%d", 38 | "job_updated_noti": "Baan bijgewerkt naar %s, rang %s.", 39 | "group_added_noti": "Toegevoegd aan groep %s, rang %s.", 40 | "group_removed_noti": "Verwijderd uit groep %s.", 41 | "no_player_found": "Geen speler gevonden", 42 | "not_enough_cash": "Je hebt niet genoeg contant geld", 43 | "player_not_nearby": "Speler is niet in de buurt", 44 | "cant_give_money": "Kon geen geld geven", 45 | "money_received": "Geld ontvangen", 46 | "money_received2": "€%d contant ontvangen", 47 | "money_given": "Geld gegeven", 48 | "money_given2": "Je hebt €%d contant gegeven", 49 | "player_in_veh": "Speler in voertuig!", 50 | "perfect": "Perfect", 51 | "good": "Goed", 52 | "bad": "Slecht", 53 | "very_bad": "Zeer slecht", 54 | "veh_make_brand": "Merk", 55 | "veh_model": "Model", 56 | "veh_plate": "Kenteken", 57 | "engine_status": "Motorstatus", 58 | "vehicle": "Voertuig", 59 | "not_found": "niet gevonden", 60 | "park_veh": "Voertuig parkeren", 61 | "no_vehs_found": "Geen voertuigen gevonden", 62 | "vehicle_impound": "Voertuigen in beslag", 63 | "parking_garage": "Parkeergarage", 64 | "impound_w_location": "Beslag (%s)", 65 | "garage_w_location": "Parkeergarage (%s)", 66 | "view_impounded_vehs": "Bekijk in beslag genomen voertuigen", 67 | "view_garage": "Bekijk garage", 68 | "personal_vehicle": "Persoonlijk voertuig", 69 | "keybind_carkey": "Voertuig openen/sluiten (dubbelklik)", 70 | "progress_hotwiring": "Hotwiring", 71 | "cruise_control": "Cruise control", 72 | "cruise_control_enabled": "Cruise control ingeschakeld", 73 | "cruise_control_disabled": "Cruise control uitgeschakeld", 74 | "cruise_control_toggle": "Cruise control aan/uit", 75 | "cruise_control_increase": "Cruise control snelheid verhoogd", 76 | "seat_shuffle": "Stoel wisselen", 77 | "discord_text": "Spelend: %s als %s %s", 78 | "discord_text_small": "Spelend als: %s %s", 79 | "discord_text_server": "Spelend: %s", 80 | "pause_menu_cash": "Contant: €%d", 81 | "pause_menu_bank": "Bank: €%d", 82 | "veh_color_black": "Zwart", 83 | "veh_color_silver": "Zilver", 84 | "veh_color_grey": "Grijs", 85 | "veh_color_metal": "Metaal", 86 | "veh_color_graphite": "Grafiet", 87 | "veh_color_red": "Rood", 88 | "veh_color_orange": "Oranje", 89 | "veh_color_gold": "Goud", 90 | "veh_color_yellow": "Geel", 91 | "veh_color_green": "Groen", 92 | "veh_color_blue": "Blauw", 93 | "veh_color_bblue": "Helderblauw", 94 | "veh_color_bronze": "Brons", 95 | "veh_color_lime": "Limoen", 96 | "veh_color_champagne": "Champagne", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Ivoor", 99 | "veh_color_brown": "Bruin", 100 | "veh_color_beechwood": "Beukenhout", 101 | "veh_color_sand": "Zand", 102 | "veh_color_cream": "Crème", 103 | "veh_color_white": "Wit", 104 | "veh_color_steel": "Staal", 105 | "veh_color_aluminium": "Aluminium", 106 | "veh_color_chrome": "Chroom", 107 | "veh_color_pink": "Roze", 108 | "veh_color_purple": "Paars", 109 | "veh_color_tan": "Bruin-beige", 110 | "veh_color_alloy": "Alloy", 111 | "veh_class_compact": "Compact", 112 | "veh_class_sedan": "Sedan", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupe", 115 | "veh_class_muscle": "Muscle", 116 | "veh_class_sports_classic": "Sport Classic", 117 | "veh_class_sport": "Sport", 118 | "veh_class_super": "Super", 119 | "veh_class_motorcycle": "Motorfiets", 120 | "veh_class_off_road": "Off-road", 121 | "veh_class_industrial": "Industrieel", 122 | "veh_class_utility": "Utility", 123 | "veh_class_van": "Bus", 124 | "veh_class_cycle": "Fiets", 125 | "veh_class_boat": "Boot", 126 | "veh_class_helicopter": "Helikopter", 127 | "veh_class_plane": "Vliegtuig", 128 | "veh_class_service": "Dienst", 129 | "veh_class_emergency": "Nooddienst", 130 | "veh_class_military": "Militair", 131 | "veh_class_commercial": "Commercieel", 132 | "veh_class_train": "Trein", 133 | "veh_class_open_wheel": "Open wiel" 134 | } 135 | -------------------------------------------------------------------------------- /locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Dein Discord wurde nicht gefunden, trete unserem Discord hier bei: %s.", 3 | "connecting": "Verbinde...", 4 | "identifier_not_found": "Dein %s wurde nicht gefunden.", 5 | "deposit": "Einzahlen", 6 | "withdraw": "Abheben", 7 | "vehicle_ownership_transfered": "Besitz übertragen", 8 | "vehicle_ownership_transfered2": "Fahrzeugbesitz von %s wurde übertragen.", 9 | "vehicle_ownership_received": "Besitz erhalten", 10 | "vehicle_ownership_received2": "Fahrzeugbesitz von %s erhalten.", 11 | "keys_shared": "Schlüssel geteilt", 12 | "keys_shared2": "Du hast die Fahrzeugschlüssel an %s gegeben.", 13 | "keys_received": "Schlüssel erhalten", 14 | "keys_received2": "Du hast die Fahrzeugschlüssel von %s erhalten.", 15 | "no_signal": "Kein Signal", 16 | "veh_key_disabled": "Fahrzeugschlüssel deaktiviert.", 17 | "veh_key_disabled2": "Dieser Fahrzeugschlüssel wurde deaktiviert und kann nicht mehr verwendet werden.", 18 | "veh_far_away": "Fahrzeug zu weit entfernt.", 19 | "veh_locked": "GESCHLOSSEN", 20 | "veh_locked2": "Dein Fahrzeug ist jetzt abgeschlossen.", 21 | "veh_unlocked": "GEÖFFNET", 22 | "veh_unlocked2": "Dein Fahrzeug ist jetzt geöffnet.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "Kein eigenes Fahrzeug in der Nähe gefunden.", 25 | "veh_stored_in_garage": "Fahrzeug in Garage gespeichert.", 26 | "impound_reclaim": "Fahrzeug aus dem Abschlepphof zurückholen", 27 | "impound": "Abschlepphof", 28 | "impound_not_enough": "Der Preis zur Rückholung beträgt %d €, du hast nicht genug!", 29 | "impound_paid": "%d € bezahlt, um das Fahrzeug zurückzuholen!", 30 | "success": "Erfolg", 31 | "staff_action": "Mitarbeiteraktion", 32 | "staff_money_removed": "%d € (%s) von %s entfernt", 33 | "user_money_removed": "%d € von %s entfernt", 34 | "staff_money_added": "%d € (%s) zu %s hinzugefügt", 35 | "user_money_added": "%d € zu %s hinzugefügt", 36 | "staff_money_set": "%s's (%s) auf %d € gesetzt", 37 | "user_money_set": "%s auf %d € gesetzt", 38 | "job_updated_noti": "Job auf %s, Rang %s aktualisiert.", 39 | "group_added_noti": "Zur Gruppe %s, Rang %s hinzugefügt.", 40 | "group_removed_noti": "Aus der Gruppe %s entfernt.", 41 | "no_player_found": "Kein Spieler gefunden", 42 | "not_enough_cash": "Du hast nicht genug Bargeld", 43 | "player_not_nearby": "Spieler nicht in der Nähe", 44 | "cant_give_money": "Konnte kein Geld geben", 45 | "money_received": "Geld erhalten", 46 | "money_received2": "%d € in bar erhalten", 47 | "money_given": "Geld gegeben", 48 | "money_given2": "Du hast %d € in bar gegeben", 49 | "player_in_veh": "Spieler im Fahrzeug!", 50 | "perfect": "Perfekt", 51 | "good": "Gut", 52 | "bad": "Schlecht", 53 | "very_bad": "Sehr schlecht", 54 | "veh_make_brand": "Marke", 55 | "veh_model": "Modell", 56 | "veh_plate": "Kennzeichen", 57 | "engine_status": "Motorstatus", 58 | "vehicle": "Fahrzeug", 59 | "not_found": "nicht gefunden", 60 | "park_veh": "Fahrzeug parken", 61 | "no_vehs_found": "Keine Fahrzeuge gefunden", 62 | "vehicle_impound": "Fahrzeugabschlepphof", 63 | "parking_garage": "Parkgarage", 64 | "impound_w_location": "Abschlepphof (%s)", 65 | "garage_w_location": "Parkgarage (%s)", 66 | "view_impounded_vehs": "Abgeschleppte Fahrzeuge anzeigen", 67 | "view_garage": "Garage anzeigen", 68 | "personal_vehicle": "Privatfahrzeug", 69 | "keybind_carkey": "Fahrzeug auf-/zusperren (Doppelklick)", 70 | "progress_hotwiring": "Kurzschließen", 71 | "cruise_control": "Tempomat", 72 | "cruise_control_enabled": "Tempomat aktiviert", 73 | "cruise_control_disabled": "Tempomat deaktiviert", 74 | "cruise_control_toggle": "Tempomat umschalten", 75 | "cruise_control_increase": "Tempomatgeschwindigkeit erhöht", 76 | "seat_shuffle": "Sitz wechseln", 77 | "discord_text": "Spielt: %s als %s %s", 78 | "discord_text_small": "Spielt als: %s %s", 79 | "discord_text_server": "Spielt: %s", 80 | "pause_menu_cash": "Bargeld: €%d", 81 | "pause_menu_bank": "Bank: €%d", 82 | "veh_color_black": "Schwarz", 83 | "veh_color_silver": "Silber", 84 | "veh_color_grey": "Grau", 85 | "veh_color_metal": "Metall", 86 | "veh_color_graphite": "Graphit", 87 | "veh_color_red": "Rot", 88 | "veh_color_orange": "Orange", 89 | "veh_color_gold": "Gold", 90 | "veh_color_yellow": "Gelb", 91 | "veh_color_green": "Grün", 92 | "veh_color_blue": "Blau", 93 | "veh_color_bblue": "Hellblau", 94 | "veh_color_bronze": "Bronze", 95 | "veh_color_lime": "Limette", 96 | "veh_color_champagne": "Champagner", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Elfenbein", 99 | "veh_color_brown": "Braun", 100 | "veh_color_beechwood": "Buchenholz", 101 | "veh_color_sand": "Sand", 102 | "veh_color_cream": "Creme", 103 | "veh_color_white": "Weiß", 104 | "veh_color_steel": "Stahl", 105 | "veh_color_aluminium": "Aluminium", 106 | "veh_color_chrome": "Chrom", 107 | "veh_color_pink": "Pink", 108 | "veh_color_purple": "Lila", 109 | "veh_color_tan": "Hellbraun", 110 | "veh_color_alloy": "Legierung", 111 | "veh_class_compact": "Kompakt", 112 | "veh_class_sedan": "Limousine", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupé", 115 | "veh_class_muscle": "Muscle-Car", 116 | "veh_class_sports_classic": "Klassischer Sportwagen", 117 | "veh_class_sport": "Sportwagen", 118 | "veh_class_super": "Supersportwagen", 119 | "veh_class_motorcycle": "Motorrad", 120 | "veh_class_off_road": "Geländewagen", 121 | "veh_class_industrial": "Industriefahrzeug", 122 | "veh_class_utility": "Nutzfahrzeug", 123 | "veh_class_van": "Van", 124 | "veh_class_cycle": "Fahrrad", 125 | "veh_class_boat": "Boot", 126 | "veh_class_helicopter": "Hubschrauber", 127 | "veh_class_plane": "Flugzeug", 128 | "veh_class_service": "Service", 129 | "veh_class_emergency": "Notfall", 130 | "veh_class_military": "Militär", 131 | "veh_class_commercial": "Gewerblich", 132 | "veh_class_train": "Zug", 133 | "veh_class_open_wheel": "Offenes Rad" 134 | } 135 | -------------------------------------------------------------------------------- /locales/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Il tuo account Discord non è stato trovato, unisciti al nostro Discord qui: %s.", 3 | "connecting": "Connessione in corso...", 4 | "identifier_not_found": "Il tuo %s non è stato trovato.", 5 | "deposit": "Deposito", 6 | "withdraw": "Prelievo", 7 | "vehicle_ownership_transfered": "Proprietà trasferita", 8 | "vehicle_ownership_transfered2": "La proprietà del veicolo %s è stata trasferita.", 9 | "vehicle_ownership_received": "Proprietà ricevuta", 10 | "vehicle_ownership_received2": "Hai ricevuto la proprietà del veicolo %s.", 11 | "keys_shared": "Chiavi condivise", 12 | "keys_shared2": "Hai condiviso le chiavi del veicolo con %s.", 13 | "keys_received": "Chiavi ricevute", 14 | "keys_received2": "Hai ricevuto le chiavi del veicolo da %s.", 15 | "no_signal": "Nessun segnale", 16 | "veh_key_disabled": "Chiave del veicolo disattivata.", 17 | "veh_key_disabled2": "Questa chiave del veicolo è stata disattivata e non può più essere utilizzata.", 18 | "veh_far_away": "Veicolo troppo lontano.", 19 | "veh_locked": "CHIUSO", 20 | "veh_locked2": "Il tuo veicolo è ora chiuso.", 21 | "veh_unlocked": "APERTO", 22 | "veh_unlocked2": "Il tuo veicolo è ora aperto.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "Nessun veicolo di tua proprietà trovato nelle vicinanze.", 25 | "veh_stored_in_garage": "Veicolo riposto in garage.", 26 | "impound_reclaim": "Riscatto veicolo dal deposito", 27 | "impound": "Deposito", 28 | "impound_not_enough": "Il prezzo per riscattare è %d €, non hai abbastanza soldi!", 29 | "impound_paid": "Hai pagato %d € per riscattare il veicolo!", 30 | "success": "successo", 31 | "staff_action": "Azione staff", 32 | "staff_money_removed": "Rimossi %d € (%s) da %s", 33 | "user_money_removed": "rimossi %d € da %s", 34 | "staff_money_added": "Aggiunti %d € (%s) a %s", 35 | "user_money_added": "aggiunti %d € a %s", 36 | "staff_money_set": "Impostato il saldo di %s (%s) a %d €", 37 | "user_money_set": "impostato %s a %d €", 38 | "job_updated_noti": "Lavoro aggiornato a %s, grado %s.", 39 | "group_added_noti": "Aggiunto al gruppo %s, grado %s.", 40 | "group_removed_noti": "Rimosso dal gruppo %s.", 41 | "no_player_found": "Nessun giocatore trovato", 42 | "not_enough_cash": "Non hai abbastanza contanti", 43 | "player_not_nearby": "Il giocatore non è vicino", 44 | "cant_give_money": "Impossibile dare denaro", 45 | "money_received": "Denaro ricevuto", 46 | "money_received2": "Ricevuti %d € in contanti", 47 | "money_given": "Denaro dato", 48 | "money_given2": "Hai dato %d € in contanti", 49 | "player_in_veh": "Giocatore in veicolo!", 50 | "perfect": "Perfetto", 51 | "good": "Buono", 52 | "bad": "Scarso", 53 | "very_bad": "Molto scarso", 54 | "veh_make_brand": "Marca", 55 | "veh_model": "Modello", 56 | "veh_plate": "Targa", 57 | "engine_status": "Stato motore", 58 | "vehicle": "Veicolo", 59 | "not_found": "non trovato", 60 | "park_veh": "Parcheggia veicolo", 61 | "no_vehs_found": "Nessun veicolo trovato", 62 | "vehicle_impound": "Deposito veicoli", 63 | "parking_garage": "Parcheggio", 64 | "impound_w_location": "Deposito (%s)", 65 | "garage_w_location": "Parcheggio (%s)", 66 | "view_impounded_vehs": "Visualizza veicoli in deposito", 67 | "view_garage": "Visualizza garage", 68 | "personal_vehicle": "Veicolo personale", 69 | "keybind_carkey": "Apri/chiudi veicolo (doppio click)", 70 | "progress_hotwiring": "Avviamento forzato", 71 | "cruise_control": "Cruise control", 72 | "cruise_control_enabled": "Cruise control attivato", 73 | "cruise_control_disabled": "Cruise control disattivato", 74 | "cruise_control_toggle": "Attiva/disattiva cruise control", 75 | "cruise_control_increase": "Velocità cruise control aumentata", 76 | "seat_shuffle": "Cambia posto nel veicolo", 77 | "discord_text": "Giocando: %s come %s %s", 78 | "discord_text_small": "Giocando come: %s %s", 79 | "discord_text_server": "Giocando: %s", 80 | "pause_menu_cash": "Contanti: €%d", 81 | "pause_menu_bank": "Banca: €%d", 82 | "veh_color_black": "Nero", 83 | "veh_color_silver": "Argento", 84 | "veh_color_grey": "Grigio", 85 | "veh_color_metal": "Metallo", 86 | "veh_color_graphite": "Grafite", 87 | "veh_color_red": "Rosso", 88 | "veh_color_orange": "Arancione", 89 | "veh_color_gold": "Oro", 90 | "veh_color_yellow": "Giallo", 91 | "veh_color_green": "Verde", 92 | "veh_color_blue": "Blu", 93 | "veh_color_bblue": "Blu chiaro", 94 | "veh_color_bronze": "Bronzo", 95 | "veh_color_lime": "Lime", 96 | "veh_color_champagne": "Champagne", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Avorio", 99 | "veh_color_brown": "Marrone", 100 | "veh_color_beechwood": "Faggio", 101 | "veh_color_sand": "Sabbia", 102 | "veh_color_cream": "Crema", 103 | "veh_color_white": "Bianco", 104 | "veh_color_steel": "Acciaio", 105 | "veh_color_aluminium": "Alluminio", 106 | "veh_color_chrome": "Cromo", 107 | "veh_color_pink": "Rosa", 108 | "veh_color_purple": "Viola", 109 | "veh_color_tan": "Marrone chiaro", 110 | "veh_color_alloy": "Lega", 111 | "veh_class_compact": "Compatta", 112 | "veh_class_sedan": "Berlina", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupé", 115 | "veh_class_muscle": "Muscle car", 116 | "veh_class_sports_classic": "Sport classico", 117 | "veh_class_sport": "Sport", 118 | "veh_class_super": "Super", 119 | "veh_class_motorcycle": "Motocicletta", 120 | "veh_class_off_road": "Fuoristrada", 121 | "veh_class_industrial": "Industriale", 122 | "veh_class_utility": "Utility", 123 | "veh_class_van": "Furgone", 124 | "veh_class_cycle": "Bicicletta", 125 | "veh_class_boat": "Barca", 126 | "veh_class_helicopter": "Elicottero", 127 | "veh_class_plane": "Aereo", 128 | "veh_class_service": "Servizio", 129 | "veh_class_emergency": "Emergenza", 130 | "veh_class_military": "Militare", 131 | "veh_class_commercial": "Commerciale", 132 | "veh_class_train": "Treno", 133 | "veh_class_open_wheel": "Ruota scoperta" 134 | } 135 | -------------------------------------------------------------------------------- /locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Ваш Discord не найден, присоединяйтесь к нашему Discord здесь: %s.", 3 | "connecting": "Подключение...", 4 | "identifier_not_found": "Ваш %s не найден.", 5 | "deposit": "Внести", 6 | "withdraw": "Снять", 7 | "vehicle_ownership_transfered": "Владение передано", 8 | "vehicle_ownership_transfered2": "Владение транспортным средством %s было передано.", 9 | "vehicle_ownership_received": "Владение получено", 10 | "vehicle_ownership_received2": "Вы получили транспортное средство %s.", 11 | "keys_shared": "Ключи переданы", 12 | "keys_shared2": "Вы передали ключи от транспортного средства %s.", 13 | "keys_received": "Ключи получены", 14 | "keys_received2": "Вы получили ключи от транспортного средства %s.", 15 | "no_signal": "Нет сигнала", 16 | "veh_key_disabled": "Ключ от транспортного средства отключен.", 17 | "veh_key_disabled2": "Этот ключ отключен и больше не может использоваться.", 18 | "veh_far_away": "Транспорт слишком далеко.", 19 | "veh_locked": "ЗАПЕРТО", 20 | "veh_locked2": "Ваш транспорт теперь заперт.", 21 | "veh_unlocked": "ОТКРЫТО", 22 | "veh_unlocked2": "Ваш транспорт теперь открыт.", 23 | "garage": "Гараж", 24 | "no_owned_veh_nearby": "Вблизи не найдено вашего транспортного средства.", 25 | "veh_stored_in_garage": "Транспортное средство помещено в гараж.", 26 | "impound_reclaim": "Забрать транспорт из штрафстоянки", 27 | "impound": "Штрафстоянка", 28 | "impound_not_enough": "Цена за возврат %d₽, у вас недостаточно!", 29 | "impound_paid": "Оплачено %d₽ за возврат транспортного средства!", 30 | "success": "успешно", 31 | "staff_action": "Действие персонала", 32 | "staff_money_removed": "Удалено %d₽ (%s) у %s", 33 | "user_money_removed": "Удалено %d₽ у %s", 34 | "staff_money_added": "Добавлено %d₽ (%s) к %s", 35 | "user_money_added": "Добавлено %d₽ к %s", 36 | "staff_money_set": "Баланс %s (%s) установлен на %d₽", 37 | "user_money_set": "Баланс %s установлен на %d₽", 38 | "job_updated_noti": "Работа обновлена до %s, ранг %s.", 39 | "group_added_noti": "Добавлен в группу %s, ранг %s.", 40 | "group_removed_noti": "Удалён из группы %s.", 41 | "no_player_found": "Игрок не найден", 42 | "not_enough_cash": "У вас недостаточно наличных", 43 | "player_not_nearby": "Игрок не рядом", 44 | "cant_give_money": "Не удалось передать деньги", 45 | "money_received": "Деньги получены", 46 | "money_received2": "Получено %d₽ наличными", 47 | "money_given": "Деньги переданы", 48 | "money_given2": "Вы передали %d₽ наличными", 49 | "player_in_veh": "Игрок в транспортном средстве!", 50 | "perfect": "Идеально", 51 | "good": "Хорошо", 52 | "bad": "Плохо", 53 | "very_bad": "Очень плохо", 54 | "veh_make_brand": "Марка", 55 | "veh_model": "Модель", 56 | "veh_plate": "Номер", 57 | "engine_status": "Состояние двигателя", 58 | "vehicle": "Транспортное средство", 59 | "not_found": "не найдено", 60 | "park_veh": "Припарковать транспорт", 61 | "no_vehs_found": "Транспортные средства не найдены", 62 | "vehicle_impound": "Штрафстоянка", 63 | "parking_garage": "Парковка", 64 | "impound_w_location": "Штрафстоянка (%s)", 65 | "garage_w_location": "Гараж (%s)", 66 | "view_impounded_vehs": "Просмотр транспортных средств на штрафстоянке", 67 | "view_garage": "Просмотр гаража", 68 | "personal_vehicle": "Личный транспорт", 69 | "keybind_carkey": "Открыть/закрыть транспорт (двойной клик)", 70 | "progress_hotwiring": "Вскрытие замка", 71 | "cruise_control": "Круиз-контроль", 72 | "cruise_control_enabled": "Круиз-контроль включен", 73 | "cruise_control_disabled": "Круиз-контроль выключен", 74 | "cruise_control_toggle": "Переключить круиз-контроль", 75 | "cruise_control_increase": "Скорость круиз-контроля увеличена", 76 | "seat_shuffle": "Смена места в транспорте", 77 | "discord_text": "Играет: %s как %s %s", 78 | "discord_text_small": "Играет как: %s %s", 79 | "discord_text_server": "Играет: %s", 80 | "pause_menu_cash": "Наличные: ₽%d", 81 | "pause_menu_bank": "Банк: ₽%d", 82 | "veh_color_black": "Черный", 83 | "veh_color_silver": "Серебристый", 84 | "veh_color_grey": "Серый", 85 | "veh_color_metal": "Металлик", 86 | "veh_color_graphite": "Графит", 87 | "veh_color_red": "Красный", 88 | "veh_color_orange": "Оранжевый", 89 | "veh_color_gold": "Золотой", 90 | "veh_color_yellow": "Желтый", 91 | "veh_color_green": "Зеленый", 92 | "veh_color_blue": "Синий", 93 | "veh_color_bblue": "Ярко-синий", 94 | "veh_color_bronze": "Бронзовый", 95 | "veh_color_lime": "Лаймовый", 96 | "veh_color_champagne": "Шампанское", 97 | "veh_color_beige": "Бежевый", 98 | "veh_color_ivory": "Слоновая кость", 99 | "veh_color_brown": "Коричневый", 100 | "veh_color_beechwood": "Бук", 101 | "veh_color_sand": "Песочный", 102 | "veh_color_cream": "Кремовый", 103 | "veh_color_white": "Белый", 104 | "veh_color_steel": "Стальной", 105 | "veh_color_aluminium": "Алюминий", 106 | "veh_color_chrome": "Хром", 107 | "veh_color_pink": "Розовый", 108 | "veh_color_purple": "Фиолетовый", 109 | "veh_color_tan": "Загар", 110 | "veh_color_alloy": "Легкосплавный", 111 | "veh_class_compact": "Компакт", 112 | "veh_class_sedan": "Седан", 113 | "veh_class_suv": "Внедорожник", 114 | "veh_class_coupe": "Купе", 115 | "veh_class_muscle": "Мускул", 116 | "veh_class_sports_classic": "Спорт Классик", 117 | "veh_class_sport": "Спорт", 118 | "veh_class_super": "Супер", 119 | "veh_class_motorcycle": "Мотоцикл", 120 | "veh_class_off_road": "Внедорожник", 121 | "veh_class_industrial": "Промышленный", 122 | "veh_class_utility": "Утилитарный", 123 | "veh_class_van": "Фургон", 124 | "veh_class_cycle": "Велосипед", 125 | "veh_class_boat": "Лодка", 126 | "veh_class_helicopter": "Вертолет", 127 | "veh_class_plane": "Самолет", 128 | "veh_class_service": "Служебный", 129 | "veh_class_emergency": "Экстренный", 130 | "veh_class_military": "Военный", 131 | "veh_class_commercial": "Коммерческий", 132 | "veh_class_train": "Поезд", 133 | "veh_class_open_wheel": "Открытые колеса" 134 | } 135 | -------------------------------------------------------------------------------- /locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "No se encontró tu Discord, únete a nuestro Discord aquí: %s.", 3 | "connecting": "Conectando...", 4 | "identifier_not_found": "No se encontró tu %s.", 5 | "deposit": "Depositar", 6 | "withdraw": "Retirar", 7 | "vehicle_ownership_transfered": "Propiedad transferida", 8 | "vehicle_ownership_transfered2": "La propiedad del vehículo %s ha sido transferida.", 9 | "vehicle_ownership_received": "Propiedad recibida", 10 | "vehicle_ownership_received2": "Has recibido la propiedad del vehículo %s.", 11 | "keys_shared": "Llaves compartidas", 12 | "keys_shared2": "Has compartido las llaves del vehículo con %s.", 13 | "keys_received": "Llaves recibidas", 14 | "keys_received2": "Has recibido las llaves del vehículo de %s.", 15 | "no_signal": "Sin señal", 16 | "veh_key_disabled": "Llave del vehículo desactivada.", 17 | "veh_key_disabled2": "Esta llave ha sido desactivada y no se puede usar más.", 18 | "veh_far_away": "Vehículo demasiado lejos.", 19 | "veh_locked": "BLOQUEADO", 20 | "veh_locked2": "Tu vehículo ha sido bloqueado.", 21 | "veh_unlocked": "DESBLOQUEADO", 22 | "veh_unlocked2": "Tu vehículo ha sido desbloqueado.", 23 | "garage": "Garaje", 24 | "no_owned_veh_nearby": "No se encontró un vehículo propio cerca.", 25 | "veh_stored_in_garage": "Vehículo guardado en el garaje.", 26 | "impound_reclaim": "Recuperar vehículo del depósito", 27 | "impound": "Depósito", 28 | "impound_not_enough": "El precio para recuperar es %d €, ¡no tienes suficiente!", 29 | "impound_paid": "¡Has pagado %d € para recuperar el vehículo!", 30 | "success": "Éxito", 31 | "staff_action": "Acción del personal", 32 | "staff_money_removed": "Se retiraron %d € (%s) de %s", 33 | "user_money_removed": "se retiraron %d € de %s", 34 | "staff_money_added": "Se añadieron %d € (%s) a %s", 35 | "user_money_added": "se añadieron %d € a %s", 36 | "staff_money_set": "Se estableció %s's (%s) en %d €", 37 | "user_money_set": "se estableció %s en %d €", 38 | "job_updated_noti": "Trabajo actualizado a %s, rango %s.", 39 | "group_added_noti": "Añadido al grupo %s, rango %s.", 40 | "group_removed_noti": "Eliminado del grupo %s.", 41 | "no_player_found": "No se encontró jugador", 42 | "not_enough_cash": "No tienes suficiente dinero en efectivo", 43 | "player_not_nearby": "El jugador no está cerca", 44 | "cant_give_money": "No se pudo dar dinero", 45 | "money_received": "Dinero recibido", 46 | "money_received2": "Has recibido %d € en efectivo", 47 | "money_given": "Dinero entregado", 48 | "money_given2": "Has dado %d € en efectivo", 49 | "player_in_veh": "¡Jugador en el vehículo!", 50 | "perfect": "Perfecto", 51 | "good": "Bueno", 52 | "bad": "Malo", 53 | "very_bad": "Muy malo", 54 | "veh_make_brand": "Marca", 55 | "veh_model": "Modelo", 56 | "veh_plate": "Matrícula", 57 | "engine_status": "Estado del motor", 58 | "vehicle": "Vehículo", 59 | "not_found": "no encontrado", 60 | "park_veh": "Aparcar vehículo", 61 | "no_vehs_found": "No se encontraron vehículos", 62 | "vehicle_impound": "Depósito de vehículos", 63 | "parking_garage": "Aparcamiento", 64 | "impound_w_location": "Depósito (%s)", 65 | "garage_w_location": "Aparcamiento (%s)", 66 | "view_impounded_vehs": "Ver vehículos en el depósito", 67 | "view_garage": "Ver garaje", 68 | "personal_vehicle": "Vehículo personal", 69 | "keybind_carkey": "Bloquear/desbloquear vehículo (doble clic)", 70 | "progress_hotwiring": "Forzando encendido", 71 | "cruise_control": "Control de crucero", 72 | "cruise_control_enabled": "Control de crucero activado", 73 | "cruise_control_disabled": "Control de crucero desactivado", 74 | "cruise_control_toggle": "Alternar control de crucero", 75 | "cruise_control_increase": "Velocidad de control de crucero aumentada", 76 | "seat_shuffle": "Cambiar de asiento", 77 | "discord_text": "Jugando: %s como %s %s", 78 | "discord_text_small": "Jugando como: %s %s", 79 | "discord_text_server": "Jugando: %s", 80 | "pause_menu_cash": "Efectivo: €%d", 81 | "pause_menu_bank": "Banco: €%d", 82 | "veh_color_black": "Negro", 83 | "veh_color_silver": "Plateado", 84 | "veh_color_grey": "Gris", 85 | "veh_color_metal": "Metal", 86 | "veh_color_graphite": "Grafito", 87 | "veh_color_red": "Rojo", 88 | "veh_color_orange": "Naranja", 89 | "veh_color_gold": "Oro", 90 | "veh_color_yellow": "Amarillo", 91 | "veh_color_green": "Verde", 92 | "veh_color_blue": "Azul", 93 | "veh_color_bblue": "Azul claro", 94 | "veh_color_bronze": "Bronce", 95 | "veh_color_lime": "Lima", 96 | "veh_color_champagne": "Champán", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Marfil", 99 | "veh_color_brown": "Marrón", 100 | "veh_color_beechwood": "Haya", 101 | "veh_color_sand": "Arena", 102 | "veh_color_cream": "Crema", 103 | "veh_color_white": "Blanco", 104 | "veh_color_steel": "Acero", 105 | "veh_color_aluminium": "Aluminio", 106 | "veh_color_chrome": "Cromo", 107 | "veh_color_pink": "Rosa", 108 | "veh_color_purple": "Púrpura", 109 | "veh_color_tan": "Bronceado", 110 | "veh_color_alloy": "Aleación", 111 | "veh_class_compact": "Compacto", 112 | "veh_class_sedan": "Sedán", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupé", 115 | "veh_class_muscle": "Muscle car", 116 | "veh_class_sports_classic": "Deportivo clásico", 117 | "veh_class_sport": "Deportivo", 118 | "veh_class_super": "Superdeportivo", 119 | "veh_class_motorcycle": "Motocicleta", 120 | "veh_class_off_road": "Todoterreno", 121 | "veh_class_industrial": "Industrial", 122 | "veh_class_utility": "Utilitario", 123 | "veh_class_van": "Furgoneta", 124 | "veh_class_cycle": "Bicicleta", 125 | "veh_class_boat": "Barco", 126 | "veh_class_helicopter": "Helicóptero", 127 | "veh_class_plane": "Avión", 128 | "veh_class_service": "Servicio", 129 | "veh_class_emergency": "Emergencia", 130 | "veh_class_military": "Militar", 131 | "veh_class_commercial": "Comercial", 132 | "veh_class_train": "Tren", 133 | "veh_class_open_wheel": "Rueda abierta" 134 | } 135 | -------------------------------------------------------------------------------- /locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Votre Discord n'a pas été trouvé, rejoignez notre Discord ici : %s.", 3 | "connecting": "Connexion...", 4 | "identifier_not_found": "Votre %s n'a pas été trouvé.", 5 | "deposit": "Dépôt", 6 | "withdraw": "Retrait", 7 | "vehicle_ownership_transfered": "Propriété transférée", 8 | "vehicle_ownership_transfered2": "La propriété du véhicule %s a été transférée.", 9 | "vehicle_ownership_received": "Propriété reçue", 10 | "vehicle_ownership_received2": "Vous avez reçu la propriété du véhicule %s.", 11 | "keys_shared": "Clés partagées", 12 | "keys_shared2": "Vous avez partagé les clés du véhicule avec %s.", 13 | "keys_received": "Clés reçues", 14 | "keys_received2": "Vous avez reçu les clés du véhicule de %s.", 15 | "no_signal": "Pas de signal", 16 | "veh_key_disabled": "Clé de véhicule désactivée.", 17 | "veh_key_disabled2": "Cette clé de véhicule a été désactivée et ne peut plus être utilisée.", 18 | "veh_far_away": "Véhicule trop éloigné.", 19 | "veh_locked": "VERROUILLÉ", 20 | "veh_locked2": "Votre véhicule est maintenant verrouillé.", 21 | "veh_unlocked": "DÉVERROUILLÉ", 22 | "veh_unlocked2": "Votre véhicule est maintenant déverrouillé.", 23 | "garage": "Garage", 24 | "no_owned_veh_nearby": "Aucun véhicule possédé trouvé à proximité.", 25 | "veh_stored_in_garage": "Véhicule rangé dans le garage.", 26 | "impound_reclaim": "Récupération de véhicule mis en fourrière", 27 | "impound": "Fourrière", 28 | "impound_not_enough": "Le prix pour récupérer est de %d €, vous n'avez pas assez!", 29 | "impound_paid": "Vous avez payé %d € pour récupérer le véhicule!", 30 | "success": "succès", 31 | "staff_action": "Action du staff", 32 | "staff_money_removed": "%d € (%s) retiré de %s", 33 | "user_money_removed": "%d € retiré de %s", 34 | "staff_money_added": "%d € (%s) ajouté à %s", 35 | "user_money_added": "%d € ajouté à %s", 36 | "staff_money_set": "Solde de %s (%s) défini à %d €", 37 | "user_money_set": "Solde de %s défini à %d €", 38 | "job_updated_noti": "Emploi mis à jour en %s, grade %s.", 39 | "group_added_noti": "Ajouté au groupe %s, grade %s.", 40 | "group_removed_noti": "Retiré du groupe %s.", 41 | "no_player_found": "Aucun joueur trouvé", 42 | "not_enough_cash": "Vous n'avez pas assez d'argent liquide", 43 | "player_not_nearby": "Joueur non à proximité", 44 | "cant_give_money": "Impossible de donner de l'argent", 45 | "money_received": "Argent reçu", 46 | "money_received2": "Vous avez reçu %d € en liquide", 47 | "money_given": "Argent donné", 48 | "money_given2": "Vous avez donné %d € en liquide", 49 | "player_in_veh": "Joueur dans un véhicule!", 50 | "perfect": "Parfait", 51 | "good": "Bon", 52 | "bad": "Mauvais", 53 | "very_bad": "Très mauvais", 54 | "veh_make_brand": "Marque", 55 | "veh_model": "Modèle", 56 | "veh_plate": "Plaque", 57 | "engine_status": "État du moteur", 58 | "vehicle": "Véhicule", 59 | "not_found": "introuvable", 60 | "park_veh": "Garer le véhicule", 61 | "no_vehs_found": "Aucun véhicule trouvé", 62 | "vehicle_impound": "Fourrière", 63 | "parking_garage": "Garage de stationnement", 64 | "impound_w_location": "Fourrière (%s)", 65 | "garage_w_location": "Garage (%s)", 66 | "view_impounded_vehs": "Voir les véhicules en fourrière", 67 | "view_garage": "Voir le garage", 68 | "personal_vehicle": "Véhicule personnel", 69 | "keybind_carkey": "Verrouiller/déverrouiller le véhicule (double clic)", 70 | "progress_hotwiring": "Démarrage forcé", 71 | "cruise_control": "Régulateur de vitesse", 72 | "cruise_control_enabled": "Régulateur de vitesse activé", 73 | "cruise_control_disabled": "Régulateur de vitesse désactivé", 74 | "cruise_control_toggle": "Activer/désactiver le régulateur de vitesse", 75 | "cruise_control_increase": "Vitesse du régulateur augmentée", 76 | "seat_shuffle": "Changer de siège", 77 | "discord_text": "Joue : %s en tant que %s %s", 78 | "discord_text_small": "Joue en tant que : %s %s", 79 | "discord_text_server": "Joue : %s", 80 | "pause_menu_cash": "Argent liquide : €%d", 81 | "pause_menu_bank": "Banque : €%d", 82 | "veh_color_black": "Noir", 83 | "veh_color_silver": "Argent", 84 | "veh_color_grey": "Gris", 85 | "veh_color_metal": "Métal", 86 | "veh_color_graphite": "Graphite", 87 | "veh_color_red": "Rouge", 88 | "veh_color_orange": "Orange", 89 | "veh_color_gold": "Or", 90 | "veh_color_yellow": "Jaune", 91 | "veh_color_green": "Vert", 92 | "veh_color_blue": "Bleu", 93 | "veh_color_bblue": "Bleu clair", 94 | "veh_color_bronze": "Bronze", 95 | "veh_color_lime": "Citron vert", 96 | "veh_color_champagne": "Champagne", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Ivoire", 99 | "veh_color_brown": "Marron", 100 | "veh_color_beechwood": "Hêtre", 101 | "veh_color_sand": "Sable", 102 | "veh_color_cream": "Crème", 103 | "veh_color_white": "Blanc", 104 | "veh_color_steel": "Acier", 105 | "veh_color_aluminium": "Aluminium", 106 | "veh_color_chrome": "Chrome", 107 | "veh_color_pink": "Rose", 108 | "veh_color_purple": "Violet", 109 | "veh_color_tan": "Fauve", 110 | "veh_color_alloy": "Alliage", 111 | "veh_class_compact": "Compact", 112 | "veh_class_sedan": "Berline", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupé", 115 | "veh_class_muscle": "Muscle car", 116 | "veh_class_sports_classic": "Sport classique", 117 | "veh_class_sport": "Sport", 118 | "veh_class_super": "Super", 119 | "veh_class_motorcycle": "Moto", 120 | "veh_class_off_road": "Tout-terrain", 121 | "veh_class_industrial": "Industriel", 122 | "veh_class_utility": "Utilitaire", 123 | "veh_class_van": "Van", 124 | "veh_class_cycle": "Vélo", 125 | "veh_class_boat": "Bateau", 126 | "veh_class_helicopter": "Hélicoptère", 127 | "veh_class_plane": "Avion", 128 | "veh_class_service": "Service", 129 | "veh_class_emergency": "Urgence", 130 | "veh_class_military": "Militaire", 131 | "veh_class_commercial": "Commercial", 132 | "veh_class_train": "Train", 133 | "veh_class_open_wheel": "Monoplace" 134 | } 135 | -------------------------------------------------------------------------------- /locales/fi.json: -------------------------------------------------------------------------------- 1 | { 2 | "not_in_discord": "Discord-tiliäsi ei löytynyt, liity Discordiimme täällä: %s.", 3 | "connecting": "Yhdistetään...", 4 | "identifier_not_found": "%s-tunnistetta ei löytynyt.", 5 | "deposit": "Talleta", 6 | "withdraw": "Nosta", 7 | "vehicle_ownership_transfered": "Omistajuus siirretty", 8 | "vehicle_ownership_transfered2": "Ajoneuvon %s omistajuus on siirretty.", 9 | "vehicle_ownership_received": "Omistajuus vastaanotettu", 10 | "vehicle_ownership_received2": "Olet saanut ajoneuvon %s omistajuuden.", 11 | "keys_shared": "Avaimet jaettu", 12 | "keys_shared2": "Olet jakanut ajoneuvon avaimet käyttäjälle %s.", 13 | "keys_received": "Avaimet vastaanotettu", 14 | "keys_received2": "Olet saanut ajoneuvon avaimet käyttäjältä %s.", 15 | "no_signal": "Ei signaalia", 16 | "veh_key_disabled": "Ajoneuvoavain poistettu käytöstä.", 17 | "veh_key_disabled2": "Tämä ajoneuvoavain on poistettu käytöstä eikä sitä voi enää käyttää.", 18 | "veh_far_away": "Ajoneuvo liian kaukana.", 19 | "veh_locked": "LUKITTU", 20 | "veh_locked2": "Ajoneuvosi on nyt lukittu.", 21 | "veh_unlocked": "AVATTU", 22 | "veh_unlocked2": "Ajoneuvosi on nyt avattu.", 23 | "garage": "Autotalli", 24 | "no_owned_veh_nearby": "Lähistöltä ei löytynyt omistamaasi ajoneuvoa.", 25 | "veh_stored_in_garage": "Ajoneuvo tallennettu autotalliin.", 26 | "impound_reclaim": "Ajoneuvon takaisinlunastus varikolta", 27 | "impound": "Varikko", 28 | "impound_not_enough": "Takaisinlunastuksen hinta on %d €, sinulla ei ole tarpeeksi rahaa!", 29 | "impound_paid": "Maksettu %d € ajoneuvon takaisinlunastuksesta!", 30 | "success": "Onnistui", 31 | "staff_action": "Ylläpidon toimenpide", 32 | "staff_money_removed": "Poistettu %d € (%s) käyttäjältä %s", 33 | "user_money_removed": "poistettu %d € käyttäjältä %s", 34 | "staff_money_added": "Lisätty %d € (%s) käyttäjälle %s", 35 | "user_money_added": "lisätty %d € käyttäjälle %s", 36 | "staff_money_set": "Asetettu %s (%s) summaksi %d €", 37 | "user_money_set": "asetettu %s summaksi %d €", 38 | "job_updated_noti": "Työ päivitetty: %s, taso %s.", 39 | "group_added_noti": "Lisätty ryhmään %s, taso %s.", 40 | "group_removed_noti": "Poistettu ryhmästä %s.", 41 | "no_player_found": "Pelaajaa ei löytynyt", 42 | "not_enough_cash": "Sinulla ei ole tarpeeksi käteistä", 43 | "player_not_nearby": "Pelaaja ei ole lähellä", 44 | "cant_give_money": "Rahan antaminen epäonnistui", 45 | "money_received": "Raha vastaanotettu", 46 | "money_received2": "Vastaanotettu %d € käteisenä", 47 | "money_given": "Raha annettu", 48 | "money_given2": "Annoit %d € käteisenä", 49 | "player_in_veh": "Pelaaja ajoneuvossa!", 50 | "perfect": "Täydellinen", 51 | "good": "Hyvä", 52 | "bad": "Huono", 53 | "very_bad": "Todella huono", 54 | "veh_make_brand": "Merkki", 55 | "veh_model": "Malli", 56 | "veh_plate": "Rekisterikilpi", 57 | "engine_status": "Moottorin tila", 58 | "vehicle": "Ajoneuvo", 59 | "not_found": "ei löytynyt", 60 | "park_veh": "Pysäköi ajoneuvo", 61 | "no_vehs_found": "Ajoneuvoja ei löytynyt", 62 | "vehicle_impound": "Ajoneuvovarikko", 63 | "parking_garage": "Pysäköintihalli", 64 | "impound_w_location": "Varikko (%s)", 65 | "garage_w_location": "Pysäköintihalli (%s)", 66 | "view_impounded_vehs": "Näytä varikolla olevat ajoneuvot", 67 | "view_garage": "Näytä autotalli", 68 | "personal_vehicle": "Henkilökohtainen ajoneuvo", 69 | "keybind_carkey": "Lukitse/avaa ajoneuvo (kaksoisklikkaus)", 70 | "progress_hotwiring": "Käynnistetään luvatta", 71 | "cruise_control": "Vakionopeudensäädin", 72 | "cruise_control_enabled": "Vakionopeudensäädin käytössä", 73 | "cruise_control_disabled": "Vakionopeudensäädin pois käytöstä", 74 | "cruise_control_toggle": "Vaihda vakionopeudensäädin päälle/pois", 75 | "cruise_control_increase": "Vakionopeus kasvoi", 76 | "seat_shuffle": "Vaihda istumapaikkaa ajoneuvossa", 77 | "discord_text": "Pelaa: %s hahmona %s %s", 78 | "discord_text_small": "Pelaa hahmona: %s %s", 79 | "discord_text_server": "Pelaa: %s", 80 | "pause_menu_cash": "Käteinen: €%d", 81 | "pause_menu_bank": "Pankki: €%d", 82 | "veh_color_black": "Musta", 83 | "veh_color_silver": "Hopea", 84 | "veh_color_grey": "Harmaa", 85 | "veh_color_metal": "Metalli", 86 | "veh_color_graphite": "Grafiitti", 87 | "veh_color_red": "Punainen", 88 | "veh_color_orange": "Oranssi", 89 | "veh_color_gold": "Kulta", 90 | "veh_color_yellow": "Keltainen", 91 | "veh_color_green": "Vihreä", 92 | "veh_color_blue": "Sininen", 93 | "veh_color_bblue": "Vaaleansininen", 94 | "veh_color_bronze": "Pronssi", 95 | "veh_color_lime": "Lime", 96 | "veh_color_champagne": "Samppanja", 97 | "veh_color_beige": "Beige", 98 | "veh_color_ivory": "Norsunluu", 99 | "veh_color_brown": "Ruskea", 100 | "veh_color_beechwood": "Pyökki", 101 | "veh_color_sand": "Hiekka", 102 | "veh_color_cream": "Kerma", 103 | "veh_color_white": "Valkoinen", 104 | "veh_color_steel": "Teräs", 105 | "veh_color_aluminium": "Alumiini", 106 | "veh_color_chrome": "Kromi", 107 | "veh_color_pink": "Vaaleanpunainen", 108 | "veh_color_purple": "Violetti", 109 | "veh_color_tan": "Vaalea ruskea", 110 | "veh_color_alloy": "Seosmetalli", 111 | "veh_class_compact": "Pieni auto", 112 | "veh_class_sedan": "Sedan", 113 | "veh_class_suv": "SUV", 114 | "veh_class_coupe": "Coupe", 115 | "veh_class_muscle": "Muskeliauto", 116 | "veh_class_sports_classic": "Klassinen urheiluauto", 117 | "veh_class_sport": "Urheiluauto", 118 | "veh_class_super": "Superauto", 119 | "veh_class_motorcycle": "Moottoripyörä", 120 | "veh_class_off_road": "Maastoauto", 121 | "veh_class_industrial": "Teollisuusajoneuvo", 122 | "veh_class_utility": "Hyötyajoneuvo", 123 | "veh_class_van": "Pakettiauto", 124 | "veh_class_cycle": "Polkupyörä", 125 | "veh_class_boat": "Vene", 126 | "veh_class_helicopter": "Helikopteri", 127 | "veh_class_plane": "Lentokone", 128 | "veh_class_service": "Huoltoajoneuvo", 129 | "veh_class_emergency": "Hätäajoneuvo", 130 | "veh_class_military": "Sotilasajoneuvo", 131 | "veh_class_commercial": "Kaupallinen ajoneuvo", 132 | "veh_class_train": "Juna", 133 | "veh_class_open_wheel": "Avoin pyörä" 134 | } 135 | -------------------------------------------------------------------------------- /server/main.lua: -------------------------------------------------------------------------------- 1 | lib.locale() 2 | NDCore = {} 3 | NDCore.players = {} 4 | PlayersInfo = {} 5 | local resourceName = GetCurrentResourceName() 6 | local tempPlayersInfo = {} 7 | 8 | Config = { 9 | serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), 10 | discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), 11 | discordMemeberRequired = GetConvarInt("core:discordMemeberRequired", 1) == 1, 12 | discordAppId = GetConvar("core:discordAppId", "858146067018416128"), 13 | discordAsset = GetConvar("core:discordAsset", "andyyy"), 14 | discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), 15 | discordActionText = GetConvar("core:discordActionText", "DISCORD"), 16 | discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), 17 | discordActionText2 = GetConvar("core:discordActionText2", "STORE"), 18 | discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), 19 | characterIdentifier = GetConvar("core:characterIdentifier", "license"), 20 | discordGuildId = GetConvar("core:discordGuildId", "false"), 21 | discordBotToken = GetConvar("core:discordBotToken", "false"), 22 | randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), 23 | disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, 24 | useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, 25 | groups = json.decode(GetConvar("core:groups", "[]")), 26 | admins = json.decode(GetConvar("core:admins", "[]")), 27 | adminDiscordRoles = json.decode(GetConvar("core:adminDiscordRoles", "[]")), 28 | groupRoles = json.decode(GetConvar("core:groupRoles", "[]")), 29 | multiCharacter = false, 30 | compatibility = json.decode(GetConvar("core:compatibility", "[]")), 31 | sv_lan = GetConvar("sv_lan", "false") == "true", 32 | platePattern = GetConvar("core:platePattern", "11AAA111") 33 | } 34 | 35 | SetConvarServerInfo("Discord", Config.discordInvite) 36 | SetConvarServerInfo("NDCore", GetResourceMetadata(resourceName, "version", 0) or "invalid") 37 | SetConvarReplicated("inventory:framework", "nd") 38 | 39 | lib.versionCheck('ND-Framework/ND_Core') 40 | 41 | local function getIdentifierList(src) 42 | local list = {} 43 | for i=0, GetNumPlayerIdentifiers(src) do 44 | local identifier = GetPlayerIdentifier(src, i) 45 | if identifier then 46 | local colon = identifier:find(":") 47 | local identifierType = identifier:sub(1, colon-1) 48 | list[identifierType] = identifier 49 | end 50 | end 51 | 52 | if Config.sv_lan then 53 | list[Config.characterIdentifier] = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier) 54 | end 55 | 56 | return list 57 | end 58 | 59 | AddEventHandler("playerJoining", function(oldId) 60 | local src = source 61 | local oldTempId = tonumber(oldId) 62 | PlayersInfo[src] = tempPlayersInfo[oldTempId] 63 | tempPlayersInfo[oldTempId] = nil 64 | 65 | if Config.sv_lan then 66 | lib.addPrincipal(("player.%s"):format(src), "group.admin") 67 | end 68 | 69 | if Config.multiCharacter then return end 70 | Wait(3000) 71 | 72 | local characters = NDCore.fetchAllCharacters(src) 73 | local id = next(characters) 74 | if id then 75 | return NDCore.setActiveCharacter(src, id) 76 | end 77 | 78 | local player = NDCore.newCharacter(src, { 79 | firstname = GetPlayerName(src), 80 | lastname = "", 81 | dob = "", 82 | gender = "" 83 | }) 84 | NDCore.setActiveCharacter(src, player.id) 85 | end) 86 | 87 | local function checkDiscordIdentifier(identifiers) 88 | if Config.discordBotToken == "false" or Config.discordGuildId == "false" then return end 89 | 90 | local discordIdentifier = identifiers["discord"] 91 | if not discordIdentifier then return end 92 | 93 | return NDCore.getDiscordInfo(discordIdentifier:gsub("discord:", "")) 94 | end 95 | 96 | AddEventHandler("onResourceStart", function(name) 97 | if name ~= resourceName then return end 98 | for _, playerId in ipairs(GetPlayers()) do 99 | local src = tonumber(playerId) 100 | local identifiers = getIdentifierList(src) 101 | PlayersInfo[src] = { 102 | identifiers = identifiers, 103 | discord = checkDiscordIdentifier(identifiers) or {} 104 | } 105 | Wait(65) 106 | end 107 | end) 108 | 109 | AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) 110 | local tempSrc = source 111 | local identifiers = getIdentifierList(tempSrc) 112 | local mainIdentifier = identifiers[Config.characterIdentifier] 113 | local discordInfo = nil 114 | 115 | deferrals.defer() 116 | Wait(0) 117 | 118 | if mainIdentifier and Config.discordBotToken ~= "false" and Config.discordGuildId ~= "false" then 119 | discordInfo = checkDiscordIdentifier(identifiers) 120 | if not discordInfo and Config.discordMemeberRequired and not Config.sv_lan then 121 | deferrals.done(locale("not_in_discord", Config.discordInvite)) 122 | Wait(0) 123 | end 124 | end 125 | 126 | deferrals.update(locale("connecting")) 127 | Wait(0) 128 | 129 | if Config.sv_lan then 130 | tempPlayersInfo[tempSrc] = { 131 | identifiers = { 132 | [Config.characterIdentifier] = "sv_lan" 133 | }, 134 | discord = discordInfo 135 | } 136 | deferrals.done() 137 | return 138 | end 139 | 140 | if mainIdentifier then 141 | tempPlayersInfo[tempSrc] = { 142 | identifiers = identifiers, 143 | discord = discordInfo 144 | } 145 | deferrals.done() 146 | else 147 | deferrals.done(locale("identifier_not_found", Config.characterIdentifier)) 148 | Wait(0) 149 | end 150 | end) 151 | 152 | AddEventHandler("playerDropped", function() 153 | local src = source 154 | local char = NDCore.players[src] 155 | if char then char.unload() end 156 | PlayersInfo[src] = nil 157 | end) 158 | 159 | AddEventHandler("onResourceStop", function(name) 160 | if name ~= resourceName then return end 161 | for _, player in pairs(NDCore.players) do 162 | player.unload() 163 | Wait(10) 164 | end 165 | end) 166 | 167 | MySQL.ready(function() 168 | Wait(100) 169 | NDCore.loadSQL({ 170 | "database/characters.sql", 171 | "database/vehicles.sql" 172 | }, resourceName) 173 | end) 174 | 175 | RegisterNetEvent("ND:playerEliminated", function(info) 176 | local src = source 177 | local player = NDCore.getPlayer(src) 178 | if not player then return end 179 | player.setMetadata({ 180 | dead = true, 181 | deathInfo = info 182 | }) 183 | end) 184 | 185 | RegisterNetEvent("ND:updateClothing", function(clothing) 186 | local src = source 187 | local player = NDCore.getPlayer(src) 188 | if not player or not clothing or type(clothing) ~= "table" then return end 189 | player.setMetadata("clothing", clothing) 190 | end) 191 | -------------------------------------------------------------------------------- /client/peds.lua: -------------------------------------------------------------------------------- 1 | local target, ox_target 2 | local locations = {} 3 | local pedBlips = {} 4 | local clothingComponents = { 5 | face = 0, 6 | mask = 1, 7 | hair = 2, 8 | torso = 3, 9 | leg = 4, 10 | bag = 5, 11 | shoes = 6, 12 | accessory = 7, 13 | undershirt = 8, 14 | kevlar = 9, 15 | badge = 10, 16 | torso2 = 11 17 | } 18 | local clothingProps = { 19 | hat = 0, 20 | glasses = 1, 21 | ears = 2, 22 | watch = 6, 23 | bracelets = 7 24 | } 25 | 26 | NDCore.isResourceStarted("ox_target", function(started) 27 | target = started 28 | if not target then return end 29 | ox_target = exports.ox_target 30 | end) 31 | 32 | local function configPed(ped) 33 | SetPedCanBeTargetted(ped, false) 34 | SetEntityCanBeDamaged(ped, false) 35 | SetBlockingOfNonTemporaryEvents(ped, true) 36 | SetPedCanRagdollFromPlayerImpact(ped, false) 37 | SetPedResetFlag(ped, 249, true) 38 | SetPedConfigFlag(ped, 185, true) 39 | SetPedConfigFlag(ped, 108, true) 40 | SetPedConfigFlag(ped, 208, true) 41 | SetPedCanRagdoll(ped, false) 42 | end 43 | 44 | local function setClothing(ped, clothing) 45 | if not clothing then return end 46 | for component, clothingInfo in pairs(clothing) do 47 | if clothingComponents[component] then 48 | SetPedComponentVariation(ped, clothingComponents[component], clothingInfo.drawable, clothingInfo.texture, 0) 49 | elseif clothingProps[component] then 50 | SetPedPropIndex(ped, clothingProps[component], clothingInfo.drawable, clothingInfo.texture, true) 51 | end 52 | end 53 | end 54 | 55 | local function groupCheck(groups, playerGroups) 56 | if not groups or #groups == 0 then return true end 57 | for i=1, #groups do 58 | if playerGroups and playerGroups[groups[i]] then 59 | return true 60 | end 61 | end 62 | end 63 | 64 | local function createBlip(coords, info, i) 65 | if not info then return end 66 | local groups = info.groups 67 | local playerGroups = NDCore.player?.groups 68 | 69 | local key = i or #pedBlips+1 70 | pedBlips[key] = { 71 | groups = groups, 72 | info = info, 73 | coords = coords 74 | } 75 | 76 | if not groupCheck(groups, playerGroups) then return end 77 | local blip = AddBlipForCoord(coords.x, coords.y, coords.z) 78 | SetBlipSprite(blip, info.sprite or 280) 79 | SetBlipScale(blip, info.scale or 0.8) 80 | SetBlipColour(blip, info.color or 3) 81 | SetBlipAsShortRange(blip, true) 82 | if info.label then 83 | BeginTextCommandSetBlipName("STRING") 84 | AddTextComponentString(info.label) 85 | EndTextCommandSetBlipName(blip) 86 | end 87 | 88 | pedBlips[key].blip = blip 89 | return blip 90 | end 91 | 92 | local function updateBlips(playerGroups) 93 | for i=1, #pedBlips do 94 | local blipInfo = pedBlips[i] 95 | local access = groupCheck(blipInfo.groups, playerGroups) 96 | if access and not blipInfo.blip or not DoesBlipExist(blipInfo.blip) then 97 | createBlip(blipInfo.coords, blipInfo.info, i) 98 | elseif not access and blipInfo.blip and DoesBlipExist(blipInfo.blip) then 99 | RemoveBlip(blipInfo.blip) 100 | end 101 | end 102 | end 103 | 104 | local function disableCollisionWithModels(ped, models) 105 | if not models then return end 106 | local coords = GetEntityCoords(ped) 107 | for i=1, #models do 108 | local m = models[i] 109 | local model = type(m) == "number" and m or GetHashKey(m) 110 | local obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, model, false, false, false) 111 | SetEntityNoCollisionEntity(ped, obj, false) 112 | end 113 | end 114 | 115 | function NDCore.createAiPed(info, cb) 116 | local ped 117 | local model = type(info.model) == "string" and GetHashKey(info.model) or info.model 118 | local blipInfo = info.blip 119 | local anim = info.anim 120 | local clothing = info.clothing 121 | local coords = info.coords 122 | local options = info.options 123 | local blip = createBlip(coords, blipInfo) 124 | local point = lib.points.new({ 125 | coords = coords.xyz, 126 | distance = info.distance or 25.0 127 | }) 128 | 129 | local id = #locations+1 130 | locations[id] = { 131 | point = point, 132 | blip = blip, 133 | options = info.options, 134 | resource = info.resource or GetInvokingResource() 135 | } 136 | 137 | if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) and #(GetEntityCoords(cache.ped)-coords.xyz) > point.distance then 138 | SetBlipAlpha(blip, 0) 139 | end 140 | 141 | function point:onEnter() 142 | local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true) 143 | lib.requestModel(model) 144 | ped = CreatePed(4, model, coords.x, coords.y, found and ground or coords.z, coords.w or coords.h or info.heading, false, false) 145 | 146 | local time = GetCloudTimeAsInt() 147 | while not DoesEntityExist(ped) and time-GetCloudTimeAsInt() < 5 do 148 | Wait(100) 149 | end 150 | 151 | if cb then 152 | cb(ped) 153 | end 154 | 155 | disableCollisionWithModels(ped, info.disableCollisionWithModels) 156 | configPed(ped) 157 | setClothing(ped, clothing) 158 | locations[id].ped = ped 159 | 160 | if anim and anim.dict and anim.clip then 161 | lib.requestAnimDict(anim.dict) 162 | TaskPlayAnim(ped, anim.dict, anim.clip, 2.0, 8.0, -1, 1, 0, 0, 0, 0) 163 | end 164 | 165 | if target and options then 166 | Wait(500) 167 | ox_target:addLocalEntity({ped}, options) 168 | end 169 | 170 | if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) then 171 | SetBlipAlpha(blip, 255) 172 | end 173 | end 174 | 175 | function point:onExit() 176 | if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) then 177 | SetBlipAlpha(blip, 0) 178 | end 179 | if ped and DoesEntityExist(ped) then 180 | if target and options then 181 | ox_target:removeLocalEntity({ped}) 182 | end 183 | Wait(500) 184 | DeleteEntity(ped) 185 | end 186 | end 187 | 188 | return id 189 | end 190 | 191 | function NDCore.removeAiPed(id) 192 | local info = locations[id] 193 | if not info then return end 194 | 195 | local ped = info.ped 196 | local blip = info.blip 197 | info.point:remove() 198 | locations[id] = nil 199 | 200 | if blip and DoesBlipExist(blip) then 201 | RemoveBlip(blip) 202 | end 203 | 204 | if ped and DoesEntityExist(ped) then 205 | if info.options then 206 | ox_target:removeLocalEntity({ped}) 207 | end 208 | DeleteEntity(ped) 209 | end 210 | end 211 | 212 | RegisterNetEvent("ND:updateCharacter", function(character) 213 | Wait(3000) 214 | if character.id ~= NDCore.player?.id then return end 215 | updateBlips(character.groups) 216 | end) 217 | 218 | RegisterNetEvent("ND:characterLoaded", function(character) 219 | Wait(3000) 220 | if character.id ~= NDCore.player?.id then return end 221 | updateBlips(character.groups) 222 | end) 223 | 224 | AddEventHandler("onResourceStop", function(name) 225 | if name == GetCurrentResourceName() then 226 | for i, _ in ipairs(locations) do 227 | NDCore.removeAiPed(i) 228 | end 229 | else 230 | for i, v in ipairs(locations) do 231 | if v.resource == name then 232 | NDCore.removeAiPed(i) 233 | end 234 | end 235 | end 236 | end) 237 | 238 | RegisterCommand("getclothing", function(source, args, rawCommand) 239 | local info = "" 240 | for k, v in pairs(clothingComponents) do 241 | info = ("%s\n%s = { drawable = %s, texture = %s },"):format(info, k, GetPedDrawableVariation(cache.ped, v), GetPedTextureVariation(cache.ped, v)) 242 | end 243 | for k, v in pairs(clothingProps) do 244 | info = ("%s\n%s = { drawable = %s, texture = %s },"):format(info, k, GetPedPropIndex(cache.ped, v), GetPedPropTextureIndex(cache.ped, v)) 245 | end 246 | lib.setClipboard(info) 247 | end, false) 248 | -------------------------------------------------------------------------------- /client/vehicle/garages.lua: -------------------------------------------------------------------------------- 1 | local locations = require "client.vehicle.data" 2 | local sprite = { 3 | ["water"] = 356, 4 | ["heli"] = 360, 5 | ["plane"] = 359, 6 | ["land"] = 357 7 | } 8 | local garageTypes = { 9 | ["water"] = 14, 10 | ["heli"] = 15, 11 | ["plane"] = 16 12 | } 13 | 14 | local clothing = { 15 | { 16 | face = { 17 | drawable = 1, 18 | texture = 1 19 | }, 20 | undershirt = { 21 | drawable = 0, 22 | texture = 0 23 | }, 24 | torso = { 25 | drawable = 1, 26 | texture = 1 27 | }, 28 | leg = { 29 | drawable = 0, 30 | texture = 0 31 | }, 32 | glasses = { 33 | drawable = 1, 34 | texture = 0 35 | }, 36 | hat = { 37 | drawable = -1, 38 | texture = -1 39 | }, 40 | }, 41 | { 42 | leg = { 43 | drawable = 0, 44 | texture = 1 45 | }, 46 | undershirt = { 47 | drawable = 0, 48 | texture = 0 49 | }, 50 | face = { 51 | drawable = 0, 52 | texture = 0 53 | }, 54 | torso = { 55 | drawable = 0, 56 | texture = 2 57 | }, 58 | glasses = { 59 | drawable = -1, 60 | texture = -1 61 | }, 62 | hat = { 63 | drawable = 0, 64 | texture = 0 65 | }, 66 | }, 67 | { 68 | face = { 69 | drawable = 0, 70 | texture = 2 71 | }, 72 | undershirt = { 73 | drawable = 0, 74 | texture = 0 75 | }, 76 | torso = { 77 | drawable = 1, 78 | texture = 2 79 | }, 80 | leg = { 81 | drawable = 0, 82 | texture = 0 83 | }, 84 | hat = { 85 | drawable = -1, 86 | texture = -1 87 | }, 88 | glasses = { 89 | drawable = -1, 90 | texture = -1 91 | }, 92 | } 93 | } 94 | 95 | local function getClosestOwnedVehicle() 96 | local coords = GetEntityCoords(cache.ped) 97 | local vehicles = lib.getNearbyVehicles(coords, 50.0, true) 98 | local nearestVeh = {} 99 | 100 | local function setNearestVehicle(veh) 101 | local state = Entity(veh.vehicle).state 102 | if not state.owner or state.owner ~= NDCore.player?.id then return end 103 | 104 | local nearestDist = nearestVeh.dist 105 | local dist = #(coords-veh.coords) 106 | if not nearestDist or dist < nearestDist then 107 | nearestVeh.dist = dist 108 | nearestVeh.coords = veh.coords 109 | nearestVeh.entity = veh.vehicle 110 | end 111 | end 112 | 113 | for i=1, #vehicles do 114 | setNearestVehicle(vehicles[i]) 115 | end 116 | return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist 117 | end 118 | 119 | local function parkVehicle(veh) 120 | if not veh or not DoesEntityExist(veh) then 121 | return NDCore.notify({ 122 | title = locale("garage"), 123 | description = locale("no_owned_veh_nearby"), 124 | type = "error", 125 | position = "bottom", 126 | duration = 3000 127 | }) 128 | end 129 | if GetPedInVehicleSeat(veh, -1) ~= 0 then 130 | NDCore.notify({ 131 | title = locale("garage"), 132 | description = locale("player_in_veh"), 133 | type = "error", 134 | position = "bottom", 135 | duration = 3000 136 | }) 137 | return 138 | end 139 | 140 | local properties = lib.getVehicleProperties(veh) 141 | properties.class = GetVehicleClass(veh) 142 | TriggerServerEvent("ND_Vehicles:storeVehicle", VehToNet(veh)) 143 | end 144 | 145 | local function isVehicleAvailable(vehicle, garageType, impound) 146 | local class = vehicle.properties.class 147 | local available = vehicle.available and not impound or vehicle.impounded and impound 148 | if available and not garageTypes[garageType] then return true end 149 | 150 | for garType, garClass in pairs(garageTypes) do 151 | if available and garType == garageType and garClass == class then 152 | return true 153 | end 154 | end 155 | end 156 | 157 | local function getEngineStatus(health) 158 | if health > 950 then 159 | return locale("perfect") 160 | elseif health > 750 then 161 | return locale("good") 162 | elseif health > 500 then 163 | return locale("bad") 164 | end 165 | return locale("very_bad") 166 | end 167 | 168 | local function createMenuOptions(vehicle, vehicleSpawns) 169 | local props = vehicle.properties 170 | local makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model)) 171 | local modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) 172 | local metadata = {} 173 | 174 | if not makeName or makeName == "NULL" then 175 | makeName = "" 176 | else 177 | metadata[#metadata+1] = {label = locale("veh_make_brand"), value = makeName} 178 | makeName = makeName .. " " 179 | end 180 | if not modelName or modelName == "NULL" then 181 | modelName = "" 182 | else 183 | metadata[#metadata+1] = {label = locale("veh_model"), value = modelName} 184 | end 185 | 186 | if props?.plate then 187 | metadata[#metadata+1] = {label = locale("veh_plate"), value = props.plate} 188 | end 189 | if props?.engineHealth then 190 | metadata[#metadata+1] = { 191 | label = locale("engine_status"), 192 | value = getEngineStatus(props.engineHealth), 193 | progress = props.engineHealth/10, 194 | colorScheme = "blue" 195 | } 196 | end 197 | 198 | if props?.fuelLevel then 199 | metadata[#metadata+1] = { 200 | label = "Fuel", 201 | value = ("%d%s"):format(props.fuelLevel, "%"), 202 | progress = props.fuelLevel, 203 | colorScheme = "yellow" 204 | } 205 | end 206 | 207 | return { 208 | title = ("%s: %s%s\n%s: %s"):format(locale("vehicle"), makeName, modelName, locale("veh_plate"), props?.plate or locale("not_found")), 209 | metadata = metadata, 210 | onSelect = function(args) 211 | TriggerServerEvent("ND_Vehicles:takeVehicle", vehicle.id, vehicleSpawns) 212 | end, 213 | } 214 | end 215 | 216 | local function createMenu(vehicles, garageType, vehicleSpawns, impound) 217 | local options = {} 218 | if not impound then 219 | options[#options+1] = { 220 | title = locale("park_veh"), 221 | onSelect = function(args) 222 | local veh = getClosestOwnedVehicle() 223 | parkVehicle(veh) 224 | end 225 | } 226 | end 227 | for _, vehicle in ipairs(vehicles) do 228 | if isVehicleAvailable(vehicle, garageType, impound) then 229 | options[#options+1] = createMenuOptions(vehicle, vehicleSpawns) 230 | end 231 | end 232 | if impound and #options == 0 then 233 | options[#options+1] = { 234 | title = locale("no_vehs_found"), 235 | readOnly = true 236 | } 237 | end 238 | return { 239 | id = ("garage_%s"):format(garageType), 240 | title = impound and locale("vehicle_impound") or locale("parking_garage"), 241 | options = options, 242 | onExit = function() 243 | garageOpen = false 244 | end 245 | } 246 | end 247 | 248 | for i=1, #locations do 249 | local location = locations[i] 250 | NDCore.createAiPed({ 251 | model = `s_m_y_airworker`, 252 | coords = location.ped, 253 | distance = 45.0, 254 | clothing = clothing[math.random(1, #clothing)], 255 | blip = { 256 | label = location.impound and locale("impound_w_location", location.garageType) or locale("garage_w_location", location.garageType), 257 | sprite = location.impound and 285 or sprite[location.garageType], 258 | scale = 0.7, 259 | color = 3, 260 | groups = location.groups 261 | }, 262 | anim = { 263 | dict = "anim@amb@casino@valet_scenario@pose_d@", 264 | clip = "base_a_m_y_vinewood_01" 265 | }, 266 | options = { 267 | { 268 | name = "nd_core:garagePed", 269 | icon = "fa-solid fa-warehouse", 270 | label = location.impound and locale("view_impounded_vehs") or locale("view_garage"), 271 | distance = 2.0, 272 | canInteract = function(entity, distance, coords, name, bone) 273 | if not location.groups then return true end 274 | local groups = location.groups 275 | local playerGroups = NDCore.player?.groups 276 | for i=1, #groups do 277 | if playerGroups?[groups[i]] then 278 | return true 279 | end 280 | end 281 | end, 282 | onSelect = function(data) 283 | local vehicles = lib.callback.await("ND_Vehicles:getOwnedVehicles") or {} 284 | local menu = createMenu(vehicles, location.garageType, location.vehicleSpawns, location.impound) 285 | lib.registerContext(menu) 286 | lib.showContext(menu.id) 287 | end 288 | } 289 | }, 290 | }) 291 | end 292 | -------------------------------------------------------------------------------- /compatibility/backwards/server.lua: -------------------------------------------------------------------------------- 1 | if not lib.table.contains(Config.compatibility, "backwards") then return end 2 | 3 | exports("GetCoreObject", function() 4 | return NDCore 5 | end) 6 | 7 | NDCore.Functions = {} 8 | NDCore.Functions.GetPlayers = NDCore.getPlayers 9 | NDCore.Functions.GetUserDiscordInfo = NDCore.getDiscordInfo 10 | NDCore.Functions.SetActiveCharacter = NDCore.setActiveCharacter 11 | NDCore.Functions.GetPlayerCharacters = NDCore.fetchAllCharacters 12 | NDCore.Functions.GetPlayerByCharacterId = NDCore.fetchCharacter 13 | 14 | function NDCore.Functions.GetPlayer(src) 15 | local player = NDCore.getPlayer(src) 16 | return { 17 | source = player.source, 18 | id = player.id, 19 | firstName = player.firstname, 20 | lastName = player.lastname, 21 | dob = player.dob, 22 | gender = player.gender, 23 | cash = player.cash, 24 | bank = player.bank, 25 | phoneNumber = player.metadata.phone, 26 | lastLocation = player.metadata.lastLocation, 27 | inventory = player.inventory, 28 | discordInfo = player.discordInfo, 29 | data = player.metadata, 30 | job = player.job 31 | } 32 | end 33 | 34 | function NDCore.Functions.GetPlayerIdentifierFromType(identifierType, src) 35 | return GetPlayerIdentifierByType(src, identifierType) 36 | end 37 | 38 | function NDCore.Functions.GetNearbyPedToPlayer(src) 39 | local pedCoords = GetEntityCoords(GetPlayerPed(src)) 40 | for targetId, targetInfo in pairs(NDCore.players) do 41 | local targetCoords = GetEntityCoords(GetPlayerPed(targetId)) 42 | if #(pedCoords - targetCoords) < 2.0 and targetId ~= src then 43 | return targetId, targetInfo 44 | end 45 | end 46 | end 47 | 48 | function NDCore.Functions.UpdateMoney(src) 49 | local player = NDCore.getPlayer(src) 50 | player.triggerEvent("ND:updateMoney", player.cash, player.bank) 51 | end 52 | 53 | function NDCore.Functions.TransferBank(amount, source, target, descriptionSender, descriptionReceiver) 54 | local amount = tonumber(amount) 55 | local src = tonumber(source) 56 | local target = tonumber(target) 57 | local player = NDCore.getPlayer(src) 58 | if not player then return end 59 | 60 | if src == target then 61 | return false, "Can't transfer money to same account" 62 | elseif GetPlayerPing(target) == 0 then 63 | return false, "Account not found" 64 | elseif amount <= 0 then 65 | return false, "Invalid amount" 66 | elseif player.bank < amount then 67 | return false, "Insufficient funds" 68 | end 69 | 70 | local targetPlayer = NDCore.getPlayer(target) 71 | if not targetPlayer then return end 72 | return player.deductMoney("bank", amount, descriptionSender or "Transfer") and targetPlayer.addMoney("bank", amount, descriptionReceiver or "Transfer") 73 | end 74 | 75 | function NDCore.Functions.GiveCash(amount, source, target) 76 | local amount = tonumber(amount) 77 | local src = tonumber(source) 78 | local target = tonumber(target) 79 | local player = NDCore.getPlayer(src) 80 | if not player then return end 81 | 82 | if src == target then 83 | return false, "Can't give to self" 84 | elseif GetPlayerPing(target) == 0 then 85 | return false, "Target not found" 86 | elseif amount <= 0 then 87 | return false, "Invalid amount" 88 | elseif player.bank < amount then 89 | return false, "Not enough money" 90 | end 91 | 92 | local targetPlayer = NDCore.getPlayer(target) 93 | if not targetPlayer then return endn end 94 | return player.deductMoney("cash", amount) and targetPlayer.addMoney("cash", amount) 95 | end 96 | 97 | function NDCore.Functions.GiveCashToNearbyPlayer(source, amount) 98 | local targetId = NDCore.Functions.GetNearbyPedToPlayer(source) 99 | if not targetId then return end 100 | return NDCore.Functions.GiveCash(amount, source, targetId) 101 | end 102 | 103 | function NDCore.Functions.WithdrawMoney(amount, source) 104 | local player = NDCore.getPlayer(source) 105 | return player and player.withdrawMoney(amount) 106 | end 107 | 108 | function NDCore.Functions.DepositMoney(amount, source) 109 | local player = NDCore.getPlayer(source) 110 | return player and player.depositMoney(amount) 111 | end 112 | 113 | function NDCore.Functions.DeductMoney(amount, source, account, description) 114 | local player = NDCore.getPlayer(source) 115 | return player and player.deductMoney(amount, account, description) 116 | end 117 | 118 | function NDCore.Functions.AddMoney(amount, source, account, description) 119 | local player = NDCore.getPlayer(source) 120 | return player and player.addMoney(amount, account, description) 121 | end 122 | 123 | function NDCore.Functions.CreateCharacter(src, firstName, lastName, dob, gender, cb) 124 | local player = NDCore.newCharacter(src, { 125 | firstname = firstName, 126 | lastname = lastName, 127 | dob = dob, 128 | gender = gender, 129 | }) 130 | 131 | if cb then cb(player.id) end 132 | player.triggerEvent("ND:returnCharacters", NDCore.fetchAllCharacters(src)) 133 | return player.id 134 | end 135 | 136 | function NDCore.Functions.UpdateCharacter(characterId, firstName, lastName, dob, gender) 137 | local player = NDCore.fetchCharacter(characterId) 138 | player.setData({ 139 | source = src, 140 | firstname = firstName, 141 | lastname = lastName, 142 | dob = dob, 143 | gender = gender 144 | }) 145 | return player 146 | end 147 | 148 | function NDCore.Functions.DeleteCharacter(characterId) 149 | local player = NDCore.fetchCharacter(characterId) 150 | return player and player.delete() 151 | end 152 | 153 | function NDCore.Functions.SetPlayerData(characterId, key, value) 154 | local player = NDCore.fetchCharacter(characterId) 155 | if not player then return end 156 | 157 | if player[key] then 158 | return player.setData(key, value) 159 | end 160 | return player.setMetadata(key, value) 161 | end 162 | 163 | function NDCore.Functions.CreatePlayerLicense(characterId, licenseType, expire) 164 | local player = NDCore.fetchCharacter(characterId) 165 | return self.createLicense(licenseType, expire) 166 | end 167 | 168 | -- find a players license by it's identifier. 169 | function NDCore.Functions.FindLicenseByIdentifier(licences, identifier) 170 | for key, license in pairs(licences) do 171 | if license.identifier == identifier then 172 | return license, key 173 | end 174 | end 175 | return {} 176 | end 177 | 178 | -- Edit a license by the license identifier. 179 | function NDCore.Functions.EditPlayerLicense(characterId, identifier, newData) 180 | local player = NDCore.fetchCharacter(characterId) 181 | if not player then return end 182 | player.updateLicense(identifier, newData) 183 | end 184 | 185 | -- Set the players job and job rank. 186 | function NDCore.Functions.SetPlayerJob(characterId, job, rank) 187 | local player = NDCore.fetchCharacter(characterId) 188 | if not player then return end 189 | if player.source then 190 | local oldJob = player.getJob(job) 191 | TriggerEvent("ND:jobChanged", player.source, {name = job, rank = rank or 1}, {name = player.job, rank = oldJob and oldJob.rank or 1}) 192 | TriggerClientEvent("ND:jobChanged", player.source, {name = job, rank = rank or 1}, {name = player.job, rank = oldJob and oldJob.rank or 1}) 193 | end 194 | return player.setJob(job, rank) 195 | end 196 | 197 | -- Set a player to a group. 198 | function NDCore.Functions.SetPlayerToGroup(characterId, group, rank) 199 | local player = NDCore.fetchCharacter(characterId) 200 | return player and player.addGroup(group, rank) 201 | end 202 | 203 | -- Remove a player from a group. 204 | function NDCore.Functions.RemovePlayerFromGroup(characterId, group) 205 | local player = NDCore.fetchCharacter(characterId) 206 | return player and player.removeGroup(group) 207 | end 208 | 209 | -- Update the characters last location into the database. 210 | function NDCore.Functions.UpdateLastLocation(characterId, location) 211 | local player = NDCore.fetchCharacter(characterId) 212 | return player and player.setMetadata("location", { 213 | x = location.x, 214 | y = location.y, 215 | x = location.z, 216 | w = location.h or location.heading or location.w or 0.0 217 | }) 218 | end 219 | 220 | function NDCore.Functions.IsPlayerAdmin(src) 221 | local player = NDCore.getPlayer(src) 222 | if player.groups["admin"] then 223 | return true 224 | end 225 | end 226 | 227 | -- Getting all the characters the player has and returning them to the client. 228 | RegisterNetEvent("ND:GetCharacters", function() 229 | local src = source 230 | TriggerClientEvent("ND:returnCharacters", src, NDCore.fetchAllCharacters(src)) 231 | end) 232 | 233 | -- Creating a new character. 234 | RegisterNetEvent("ND:newCharacter", function(newCharacter) 235 | local src = source 236 | NDCore.newCharacter(src, { 237 | firstname = newCharacter.firstName, 238 | lastname = newCharacter.lastName, 239 | dob = newCharacter.dob, 240 | gender = newCharacter.gender, 241 | cash = 0, 242 | bank = 0, 243 | }) 244 | end) 245 | 246 | -- Update the character info when edited. 247 | RegisterNetEvent("ND:editCharacter", function(newCharacter) 248 | local src = source 249 | local player = NDCore.fetchCharacter(newCharacter.id, src) 250 | return player and player.setData({ 251 | source = src, 252 | firstname = newCharacter.firstName, 253 | lastname = newCharacter.lastName, 254 | dob = newCharacter.dob, 255 | gender = newCharacter.gender 256 | }) 257 | end) 258 | 259 | -- Delete character from database. 260 | RegisterNetEvent("ND:deleteCharacter", function(characterId) 261 | local src = source 262 | local player = NDCore.fetchCharacter(characterId, src) 263 | if not player or player.source ~= source then return end 264 | return player.delete() 265 | end) 266 | 267 | -- add a player to the table. 268 | RegisterNetEvent("ND:setCharacterOnline", function(id) 269 | local src = source 270 | NDCore.setActiveCharacter(src, tonumber(id)) 271 | end) 272 | 273 | -- Update the characters clothes. 274 | RegisterNetEvent("ND:updateClothes", function(clothing) 275 | local src = source 276 | local player = NDCore.getPlayer(src) 277 | player.setMetadata("clothing", clothing) 278 | end) 279 | -------------------------------------------------------------------------------- /compatibility/esx/server.lua: -------------------------------------------------------------------------------- 1 | if not lib.table.contains(Config.compatibility, "esx") then return end 2 | 3 | local itemNames 4 | local registeredItems = {} 5 | 6 | local function getAmmoFromWeapon(weapon) 7 | if not weapon then return end 8 | local items = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:Items() or {} 9 | for item, data in pairs(items) do 10 | if data.weapon and data.model and data.model:lower() == weapon:lower() then 11 | return data.ammoname 12 | end 13 | end 14 | end 15 | 16 | local function createPlayerFunctions(self) 17 | self.Accounts = { 18 | Bank = self.bank, 19 | Money = self.Cash, 20 | Black = self.Cash 21 | } 22 | 23 | if self.jobInfo then 24 | self.job = { 25 | id = self.job, 26 | name = self.job, 27 | label = self.jobInfo.label, 28 | grade = self.jobInfo.rank, 29 | grade_name = self.jobInfo.rankName, 30 | grade_label = self.jobInfo.rankName, 31 | grade_salary = 0, 32 | skin_male = {}, 33 | skin_female = {} 34 | } 35 | end 36 | 37 | local ped = GetPlayerPed(self.source) 38 | if DoesEntityExist(ped) then 39 | self.coords = GetEntityCoords() 40 | end 41 | 42 | self.loadout = {} 43 | self.maxWeight = 30000 44 | self.money = self.Accounts.Money 45 | self.sex = self.gender 46 | self.firstName = self.firstname 47 | self.lastName = self.lastname 48 | self.dateofbirth = self.dob 49 | self.height = 120 50 | self.dead = self.getMetadata("dead") 51 | 52 | function self.addAccountMoney(account, amount) 53 | local amount = tonumber(amount) 54 | if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end 55 | self[account] += amount 56 | if NDCore.players[self.source] then 57 | self.triggerEvent("ND:updateMoney", self.cash, self.bank) 58 | TriggerEvent("ND:moneyChange", self.source, account, amount, "add") 59 | end 60 | return true 61 | end 62 | 63 | function self.addInventoryItem(item, count) 64 | return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:AddItem(self.source, item, count) 65 | end 66 | 67 | function self.addMoney(amount) 68 | local amount = tonumber(amount) 69 | if not amount or amount <= 0 then return end 70 | self["bank"] += amount 71 | if NDCore.players[self.source] then 72 | self.triggerEvent("ND:updateMoney", self.cash, self.bank) 73 | TriggerEvent("ND:moneyChange", self.source, "bank", amount, "add", reason) 74 | end 75 | return true 76 | end 77 | 78 | function self.addWeaponAmmo(weaponName, ammoCount) 79 | local ammoName = getAmmoFromWeapon(weaponName) 80 | if not ammoName then return end 81 | self.addInventoryItem(ammoName, ammoCount) 82 | end 83 | 84 | function self.addWeapon(weaponName, ammo) 85 | local name = nil 86 | if weaponName:find("weapon") then 87 | name = weaponName 88 | else 89 | name = ("weapon_%s"):format(weaponName) 90 | end 91 | self.addInventoryItem(name, 1) 92 | self.addWeaponAmmo(name, ammo) 93 | 94 | -- local weapon = GetHashKey(name) 95 | -- local ped = GetPlayerPed(self.source) 96 | -- GiveWeaponToPed(ped, weapon, ammo, false, false) 97 | end 98 | 99 | function self.addWeaponComponent(_, component) 100 | self.addInventoryItem(component, 1) 101 | end 102 | 103 | function self.canCarryItem(item, count) 104 | return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:CanCarryItem(self.source, item, count) 105 | end 106 | 107 | function self.canSwapItem(firstItem, firstItemCount, testItem, testItemCount) 108 | return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:CanSwapItem(self.source, firstItem, firstItemCount, testItem, testItemCount) 109 | end 110 | 111 | function self.clearMeta(index) 112 | self.setMetadata(index, nil) 113 | end 114 | 115 | function self.getMeta(index, subIndex) 116 | local meta = self.getMetadata(index) 117 | if type(meta) == "table" then 118 | return meta[subIndex] 119 | end 120 | return meta 121 | end 122 | 123 | function self.getCoords(useVector) 124 | local ped = GetPlayerPed(self.source) 125 | local coords = GetEntityCoords(ped) 126 | if useVector then 127 | return coords 128 | end 129 | return { 130 | x = coords.x, 131 | y = coords.y, 132 | z = coords.z 133 | } 134 | end 135 | 136 | function self.getIdentifier() 137 | return self.identifier 138 | end 139 | 140 | function self.getInventory(minimal) 141 | local playerItems = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetInventoryItems(self.source) or {} 142 | if not minimal then 143 | return playerItems 144 | end 145 | -- minimal stuff 146 | end 147 | 148 | function self.getInventoryItem(item) 149 | local playerItems = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetInventoryItems(self.source) or {} 150 | for name, data in pairs(playerItems) do 151 | if name == item then 152 | return data 153 | end 154 | end 155 | end 156 | 157 | function self.getMoney() 158 | return self.bank 159 | end 160 | 161 | function self.getName() 162 | return self.fullname 163 | end 164 | 165 | function self.hasItem(item, metadata) 166 | return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetItem(self.source, item, metadata) 167 | end 168 | 169 | function self.removeInventoryItem(item, count) 170 | return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:RemoveItem(self.source, item, count) 171 | end 172 | 173 | function self.removeMoney(amount) 174 | self.deductMoney("bank", amount) 175 | end 176 | 177 | function setMoney(amount) 178 | self.setData("bank", amount) 179 | end 180 | 181 | self.kick = self.drop 182 | self.removeAccountMoney = self.deductMoney 183 | self.setMeta = self.setMetadata 184 | self.setAccountMoney = self.setData 185 | self.setInventoryItem = self.addInventoryItem 186 | 187 | 188 | -- self.getAccount(account) 189 | -- self.getAccounts() 190 | -- self.getGroup() 191 | -- self.getJob() 192 | -- self.getLoadout() 193 | -- self.getMissingAccounts(cb) 194 | -- self.getWeapon(weaponName) 195 | -- self.getWeaponTint(weaponName, weaponTintIndex) 196 | -- self.getWeight() 197 | -- self.hasWeapon(weaponName) 198 | -- self.hasWeaponComponent(weaponName, weaponComponent) 199 | -- self.removeWeapon(weaponName) 200 | -- self.removeWeaponAmmo(weaponName, ammoCount) 201 | -- self.removeWeaponComponent(weaponName, weaponComponent) 202 | -- self.setMaxWeight(newWeight) 203 | -- self.setName(newName) 204 | -- self.setWeaponTint(weaponName, weaponTintIndex) 205 | -- self.showAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex) 206 | -- self.showHelpNotification(msg, thisFrame, beep, duration) 207 | -- self.showNotification(msg, flash, saveToBrief, hudColorIndex) 208 | 209 | return self 210 | end 211 | 212 | NDCore.OneSync = {} 213 | NDCore.RegisterServerCallback = lib.callback.register 214 | NDCore.SetTimeout = SetTimeout 215 | NDCore.Trace = Citizen.Trace 216 | 217 | local function exportHandler(resource, exportName, cb) 218 | AddEventHandler(("__cfx_export_%s_%s"):format(resource, exportName), function(setCB) 219 | setCB(cb) 220 | end) 221 | end 222 | 223 | exportHandler("es_extended", "getSharedObject", function() 224 | return NDCore 225 | end) 226 | 227 | function NDCore.ClearTimeout() 228 | print("[^3WARNING^7] ESX Function 'ClearTimeout' is not compatible with NDCore!") 229 | end 230 | 231 | function NDCore.CreatePickup() 232 | print("[^3WARNING^7] ESX Function 'CreatePickup' is not compatible with NDCore!") 233 | end 234 | 235 | function NDCore.DiscordLog() 236 | print("[^3WARNING^7] ESX Function 'DiscordLog' is not compatible with NDCore!") 237 | end 238 | 239 | function NDCore.DiscordLogFields() 240 | print("[^3WARNING^7] ESX Function 'DiscordLogFields' is not compatible with NDCore!") 241 | end 242 | 243 | function NDCore.RegisterUsableItem() 244 | print("[^3WARNING^7] ESX Function 'RegisterUsableItem' is not compatible with NDCore!") 245 | end 246 | 247 | function NDCore.UseItem() 248 | print("[^3WARNING^7] ESX Function 'UseItem' is not compatible with NDCore!") 249 | end 250 | 251 | 252 | function NDCore.GetPlayerFromId(src) 253 | return createPlayerFunctions(NDCore.getPlayer(src)) 254 | end 255 | 256 | function NDCore.GetExtendedPlayers(key, value) 257 | local players = {} 258 | if not key or not value then 259 | for _, info in pairs(NDCore.players) do 260 | players[#players+1] = createPlayerFunctions(info) 261 | end 262 | return players 263 | end 264 | 265 | local keyTypes = {id = "id", firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"} 266 | local findBy = keyTypes[key] or "metadata" 267 | if findBy then 268 | for _, info in pairs(NDCore.players) do 269 | if findBy == "metadata" and info["metadata"][key] == value or info[findBy] == value then 270 | players[#players+1] = createPlayerFunctions(info) 271 | end 272 | end 273 | end 274 | return players 275 | end 276 | 277 | NDCore.GetPlayers = NDCore.GetExtendedPlayers 278 | 279 | function NDCore.RegisterCommand(name, perms, cb, allowConsole, suggestion) 280 | lib.addCommand(name, { 281 | help = suggestion.help, 282 | params = suggestion.arguments, 283 | restricted = perms and ("group.%s"):format(perms) 284 | }, function(source, args, raw) 285 | if allowConsole and source == 0 then 286 | return print("[^3WARNING^7] ^5Command Cannot be executed from console") 287 | end 288 | local player = NDCore.getPlayer(source) 289 | cb(player, args, function(msg) 290 | if source == 0 then 291 | return print(("[^3WARNING^7] %s^7"):format(msg)) 292 | end 293 | player.showNotification(msg) 294 | end) 295 | end) 296 | end 297 | 298 | function NDCore.DoesJobExist(job, grade) 299 | local groupInfo = Config.groups[job] 300 | if groupInfo and groupInfo.ranks[grade] then 301 | return true 302 | end 303 | end 304 | 305 | function NDCore.GetItemLabel(item) 306 | if not itemNames then 307 | itemNames = {} 308 | local items = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:Items() or {} 309 | for item, data in pairs(items) do 310 | itemNames[item] = data.label 311 | end 312 | end 313 | return itemNames[item] 314 | end 315 | 316 | function NDCore.GetJobs() 317 | return Config.groups 318 | end 319 | 320 | function NDCore.GetPlayerFromIdentifier(identifier) 321 | for _, info in pairs(NDCore.players) do 322 | if info.identifier:find(identifier) then 323 | return info 324 | end 325 | end 326 | end 327 | 328 | function NDCore.OneSync.SpawnObject(model, coords, heading, cb) 329 | local entity = CreateObject(model, coords.x, coords.y, coords.z, true, false, false) 330 | local value = lib.waitFor(function() 331 | if DoesEntityExist(entity) then return entity end 332 | end, "Failed to spawn object", 5000) 333 | if not value then return end 334 | SetEntityHeading(value, heading) 335 | if not cb then return end 336 | cb(value) 337 | end 338 | 339 | function NDCore.OneSync.SpawnPed(model, coords, heading, cb) 340 | local entity = CreatePed(0, model, coords.x, coords.y, coords.z, heading, true, false) 341 | local value = lib.waitFor(function() 342 | if DoesEntityExist(entity) then return entity end 343 | end, "Failed to spawn ped", 5000) 344 | if not value or not cb then return end 345 | cb(NetworkGetNetworkIdFromEntity(value)) 346 | end 347 | 348 | function NDCore.OneSync.SpawnVehicle(model, coords, heading, properties, cb) 349 | local vehicle = NDCore.createVehicle({ 350 | model = model, 351 | coords = coords, 352 | heading = heading 353 | }) 354 | if not cb then return end 355 | cb(vehicle.netId) 356 | end 357 | 358 | function NDCore.OneSync.SpawnPedInVehicle(model, vehicle, seat, cb) 359 | local entity = CreatePedInsideVehicle(vehicle, 0, model, seat, true, false) 360 | local value = lib.waitFor(function() 361 | if DoesEntityExist(entity) then return entity end 362 | end, "Failed to spawn ped", 5000) 363 | if not value or not cb then return end 364 | cb(value) 365 | end 366 | 367 | AddEventHandler("ND:characterUnloaded", function(src, character) 368 | TriggerEvent("esx:playerDropped", src, "") 369 | end) 370 | 371 | AddEventHandler("ND:characterLoaded", function(character) 372 | TriggerEvent("esx:playerLoaded", character.source, createPlayerFunctions(character)) 373 | end) 374 | -------------------------------------------------------------------------------- /client/vehicle/data.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | garageType = "land", 4 | groups = {"lsfd"}, 5 | ped = vector4(370.9027, -593.8090, 28.8681, 73.6009), 6 | vehicleSpawns = { 7 | vec4(366.7840, -591.4883, 28.7261, 339.6823), 8 | vec4(365.0753, -582.8641, 28.7156, 135.0064), 9 | vec4(357.4239, -604.7719, 28.6767, 177.1355), 10 | } 11 | }, 12 | { 13 | garageType = "land", 14 | groups = {"sahp", "lspd", "bcso"}, 15 | ped = vector4(452.83, -1027.79, 28.54, 2.49), 16 | vehicleSpawns = { 17 | vector4(446.19, -1025.47, 28.24, 185.96), 18 | vector4(438.75, -1026.09, 28.38, 184.29), 19 | vector4(434.98, -1026.61, 28.46, 185.99), 20 | vector4(431.26, -1027.31, 28.53, 185.59), 21 | vector4(427.52, -1026.84, 28.58, 184.25) 22 | } 23 | }, 24 | { 25 | garageType = "land", 26 | impound = true, 27 | ped = vector4(407.99, -1624.74, 29.29, 229.93), 28 | vehicleSpawns = { 29 | vector4(396.34, -1644.28, 28.86, 319.10), 30 | vector4(398.56, -1646.42, 28.8616, 319.18), 31 | vector4(400.68, -1648.86, 28.86, 140.78), 32 | vector4(403.32, -1650.54, 28.86, 319.92), 33 | vector4(403.21, -1650.68, 28.86, 139.40) 34 | } 35 | }, 36 | { 37 | garageType = "plane", 38 | ped = vector4(-941.05, -2966.04, 13.95, 136.06), 39 | vehicleSpawns = { 40 | vector4(-974.98, -2977.90, 14.55, 59.91) 41 | } 42 | }, 43 | { 44 | garageType = "heli", 45 | ped = vector4(-731.00, -1394.54, 5.00, 245.52), 46 | vehicleSpawns = { 47 | vector4(-746.01, -1469.39, 5.68, 139.27), 48 | vector4(-725.26, -1444.72, 5.68, 139.58) 49 | } 50 | }, 51 | { 52 | garageType = "plane", 53 | ped = vector4(1742.77, 3298.25, 41.22, 132.91), 54 | vehicleSpawns = { 55 | vector4(1734.26, 3250.98, 41.96, 81.11), 56 | vector4(1729.32, 3270.69, 41.74, 145.39) 57 | } 58 | }, 59 | { 60 | garageType = "plane", 61 | ped = vector4(-1241.26, -3391.48, 13.94, 35.03), 62 | vehicleSpawns = { 63 | vector4(-1254.71, -3388.15, 14.54, 330.04), 64 | vector4(-1270.36, -3378.80, 14.54, 330.20), 65 | vector4(-1286.05, -3369.44, 14.54, 329.99) 66 | } 67 | }, 68 | { 69 | garageType = "plane", 70 | ped = vector4(-1621.10, -3151.63, 13.99, 41.26), 71 | vehicleSpawns = { 72 | vector4(-1634.04, -3147.84, 14.60, 330.28), 73 | vector4(-1649.75, -3138.34, 14.60, 329.20), 74 | vector4(-1665.42, -3129.26, 14.60, 330.10) 75 | } 76 | }, 77 | { 78 | garageType = "heli", 79 | ped = vector4(-1121.87, -2839.88, 13.95, 150.58), 80 | vehicleSpawns = { 81 | vector4(-1178.71, -2846.51, 14.62, 150.16), 82 | vector4(-1146.40, -2865.22, 14.62, 151.08), 83 | vector4(-1112.85, -2884.66, 14.62, 149.70) 84 | } 85 | }, 86 | { 87 | garageType = "water", 88 | ped = vector4(-831.03, -1359.53, 5.00, 299.80), 89 | vehicleSpawns = { 90 | vector4(-846.16, -1362.07, 0.39, 110.54), 91 | vector4(-842.53, -1372.00, 0.39, 111.54), 92 | vector4(-849.29, -1353.26, 0.38, 109.53), 93 | vector4(-852.26, -1345.09, 0.41, 110.52), 94 | vector4(-855.49, -1336.60, 0.40, 107.43), 95 | vector4(-858.63, -1328.30, 0.40, 109.82), 96 | vector4(-839.35, -1380.30, 0.39, 109.27), 97 | vector4(-836.38, -1388.93, 0.41, 110.11), 98 | vector4(-833.29, -1397.32, 0.40, 110.80), 99 | vector4(-830.34, -1405.65, 0.37, 110.16) 100 | } 101 | }, 102 | { 103 | garageType = "land", 104 | ped = vector4(-280.32, -888.42, 31.32, 250.68), 105 | vehicleSpawns = { 106 | vector4(-282.36, -915.11, 30.38, 68.81), 107 | vector4(-284.27, -918.37, 30.38, 70.58), 108 | vector4(-285.44, -921.82, 30.38, 250.22), 109 | vector4(-285.70, -887.62, 30.38, 167.04), 110 | vector4(-292.70, -885.93, 30.38, 167.09), 111 | vector4(-285.76, -887.47, 30.38, 169.59), 112 | vector4(-300.44, -885.14, 30.38, 347.05), 113 | vector4(-303.59, -883.71, 30.38, 167.69), 114 | vector4(-309.38, -896.88, 30.38, 167.15), 115 | vector4(-312.98, -896.30, 30.38, 166.46), 116 | vector4(-316.68, -896.26, 30.37, 347.94), 117 | vector4(-311.06, -881.94, 30.38, 168.21), 118 | vector4(-314.78, -881.86, 30.37, 348.45) 119 | } 120 | }, 121 | { 122 | garageType = "land", 123 | ped = vector4(597.53, 91.08, 93.13, 250.54), 124 | vehicleSpawns = { 125 | vector4(598.53, 98.37, 92.27, 69.47), 126 | vector4(599.79, 102.00, 92.27, 249.38), 127 | vector4(608.21, 103.90, 92.18, 248.98), 128 | vector4(600.60, 111.38, 92.27, 73.07), 129 | vector4(609.91, 107.59, 92.23, 68.77), 130 | vector4(601.36, 114.99, 92.27, 250.19), 131 | vector4(611.07, 111.39, 92.29, 249.69), 132 | vector4(603.52, 118.52, 92.26, 67.62), 133 | vector4(612.74, 114.94, 92.28, 69.01), 134 | vector4(604.00, 122.58, 92.27, 249.76), 135 | vector4(613.78, 118.83, 92.29, 248.82), 136 | vector4(622.43, 115.48, 91.99, 70.39), 137 | vector4(628.56, 110.25, 91.47, 249.30), 138 | vector4(620.67, 111.89, 92.03, 250.24), 139 | vector4(618.41, 104.51, 91.97, 72.22), 140 | vector4(624.87, 99.25, 91.36, 63.32), 141 | vector4(616.59, 100.82, 91.97, 249.00) 142 | } 143 | }, 144 | { 145 | garageType = "land", 146 | ped = vector4(100.64, -1072.88, 29.37, 341.55), 147 | vehicleSpawns = { 148 | vector4(106.13, -1063.24, 28.51, 66.86), 149 | vector4(107.82, -1059.73, 28.51, 66.99), 150 | vector4(112.39, -1049.71, 28.52, 67.40), 151 | vector4(110.76, -1053.09, 28.51, 68.13), 152 | vector4(109.06, -1056.38, 28.51, 66.45), 153 | vector4(117.53, -1081.03, 28.50, 181.40), 154 | vector4(119.09, -1069.44, 28.50, 181.12), 155 | vector4(121.22, -1081.42, 28.50, 0.57), 156 | vector4(122.32, -1070.46, 28.50, 0.67), 157 | vector4(125.62, -1069.54, 28.50, 179.25), 158 | vector4(124.83, -1081.17, 28.50, 180.37), 159 | vector4(128.62, -1081.71, 28.50, 1.36), 160 | vector4(128.93, -1070.62, 28.50, 359.89), 161 | vector4(132.24, -1069.35, 28.50, 181.54), 162 | vector4(132.31, -1081.45, 28.50, 180.86), 163 | vector4(135.93, -1081.35, 28.50, 358.82), 164 | vector4(135.64, -1070.75, 28.50, 0.43), 165 | vector4(139.75, -1081.91, 28.50, 182.18), 166 | vector4(138.86, -1070.37, 28.50, 181.90), 167 | vector4(143.55, -1081.41, 28.50, 0.89), 168 | vector4(147.13, -1081.48, 28.50, 179.02), 169 | vector4(150.95, -1081.49, 28.50, 358.79), 170 | vector4(154.64, -1081.32, 28.50, 179.51), 171 | vector4(158.38, -1082.03, 28.50, 1.57), 172 | vector4(162.10, -1081.32, 28.50, 179.88) 173 | } 174 | }, 175 | { 176 | garageType = "land", 177 | ped = vector4(214.84, -806.24, 30.81, 342.23), 178 | vehicleSpawns = { 179 | vector4(251.26, -774.63, 29.98, 67.34), 180 | vector4(245.36, -772.34, 30.02, 66.81), 181 | vector4(219.19, -765.93, 30.14, 249.78), 182 | vector4(228.11, -768.94, 30.10, 249.66), 183 | vector4(233.68, -771.18, 30.07, 249.82), 184 | vector4(244.01, -775.06, 29.99, 249.41), 185 | vector4(249.53, -777.07, 29.95, 251.09), 186 | vector4(248.74, -779.52, 29.92, 69.14), 187 | vector4(243.64, -777.63, 29.96, 69.23), 188 | vector4(233.47, -773.91, 30.05, 69.88), 189 | vector4(218.62, -768.58, 30.14, 69.81), 190 | vector4(217.02, -770.83, 30.16, 248.11), 191 | vector4(226.09, -773.95, 30.09, 249.30), 192 | vector4(231.82, -776.14, 30.04, 249.17), 193 | vector4(242.05, -779.91, 29.93, 246.99), 194 | vector4(247.72, -782.02, 29.88, 251.04), 195 | vector4(216.19, -773.65, 30.16, 67.90), 196 | vector4(225.82, -776.85, 30.08, 65.64), 197 | vector4(230.77, -778.89, 30.03, 69.40), 198 | vector4(241.72, -782.84, 29.90, 69.13), 199 | vector4(247.12, -784.93, 29.84, 67.56), 200 | vector4(246.23, -787.13, 29.82, 247.56), 201 | vector4(239.40, -784.68, 29.90, 248.06), 202 | vector4(230.42, -781.41, 30.01, 247.48), 203 | vector4(224.14, -779.01, 30.07, 247.39), 204 | vector4(215.32, -775.95, 30.17, 248.99), 205 | vector4(243.81, -792.19, 29.77, 246.23), 206 | vector4(238.08, -789.95, 29.85, 250.62), 207 | vector4(228.14, -786.30, 30.01, 249.07), 208 | vector4(221.98, -783.92, 30.08, 248.25), 209 | vector4(213.41, -781.01, 30.19, 249.21), 210 | vector4(214.77, -778.70, 30.17, 67.86), 211 | vector4(224.02, -781.91, 30.07, 68.82), 212 | vector4(229.34, -784.05, 30.01, 66.57), 213 | vector4(239.87, -787.83, 29.86, 69.37), 214 | vector4(245.18, -789.87, 29.79, 68.00), 215 | vector4(241.85, -797.20, 29.72, 245.51), 216 | vector4(236.57, -795.07, 29.82, 247.54), 217 | vector4(226.12, -791.34, 29.99, 247.84), 218 | vector4(220.61, -789.19, 30.08, 248.09), 219 | vector4(212.00, -786.22, 30.21, 248.93), 220 | vector4(212.91, -783.71, 30.19, 68.61), 221 | vector4(222.11, -786.89, 30.07, 70.10), 222 | vector4(227.84, -789.16, 29.99, 67.55), 223 | vector4(237.73, -792.73, 29.82, 69.40), 224 | vector4(243.54, -794.88, 29.73, 68.74), 225 | vector4(237.03, -812.64, 29.59, 243.63), 226 | vector4(207.66, -798.70, 30.29, 70.06), 227 | vector4(216.56, -801.92, 30.10, 70.31), 228 | vector4(222.28, -804.17, 29.98, 69.97), 229 | vector4(232.41, -807.95, 29.74, 69.42), 230 | vector4(238.16, -810.17, 29.60, 68.95), 231 | vector4(238.32, -807.43, 29.63, 248.28), 232 | vector4(233.32, -805.44, 29.75, 248.13), 233 | vector4(222.46, -801.41, 29.98, 248.50), 234 | vector4(216.85, -799.11, 30.10, 248.54), 235 | vector4(207.60, -795.90, 30.29, 248.64), 236 | vector4(209.28, -793.74, 30.25, 69.82), 237 | vector4(218.31, -796.92, 30.08, 68.55), 238 | vector4(224.50, -799.34, 29.96, 68.63), 239 | vector4(234.40, -803.02, 29.76, 66.67), 240 | vector4(240.09, -805.27, 29.64, 69.02), 241 | vector4(240.53, -802.44, 29.67, 248.95), 242 | vector4(234.46, -800.13, 29.79, 249.40), 243 | vector4(224.30, -796.51, 29.98, 248.31), 244 | vector4(218.23, -794.00, 30.09, 249.09), 245 | vector4(209.84, -791.10, 30.24, 251.56), 246 | vector4(211.09, -788.68, 30.22, 69.53), 247 | vector4(220.17, -791.73, 30.06, 70.56), 248 | vector4(225.84, -794.12, 29.98, 67.53), 249 | vector4(236.38, -797.85, 29.79, 69.39), 250 | vector4(216.56, -801.90, 30.79, 69.39) 251 | } 252 | }, 253 | { 254 | garageType = "land", 255 | ped = vector4(587.5941, 2744.2844, 42.0691, 181.0514), 256 | vehicleSpawns = { 257 | vector4(584.3921, 2721.3599, 41.6483, 184.3260), 258 | vector4(577.1069, 2736.2322, 41.6097, 184.6912), 259 | vector4(577.9459, 2721.0010, 41.6481, 185.1414), 260 | vector4(574.1685, 2735.7288, 41.6397, 184.5562) 261 | } 262 | }, 263 | { 264 | garageType = "land", 265 | ped = vector4(2746.1726, 3458.7378, 55.8272, 245.1009), 266 | vehicleSpawns = { 267 | vector4(2768.2136, 3456.2710, 55.2840, 67.7897), 268 | vector4(2759.3218, 3451.1448, 55.4786, 248.0150), 269 | vector4(2763.6526, 3445.0544, 55.4741, 66.9754), 270 | vector4(2787.2947, 3467.1001, 54.9407, 247.4356), 271 | vector4(2773.9514, 3471.6238, 55.0407, 246.8264) 272 | } 273 | }, 274 | { 275 | garageType = "land", 276 | ped = vector4(120.4042, 6623.5190, 31.9593, 228.8283), 277 | vehicleSpawns = { 278 | vector4(155.9047, 6593.3252, 31.4343, 359.3789), 279 | vector4(155.7900, 6602.6978, 31.4463, 358.8232), 280 | vector4(145.8267, 6614.0996, 31.4007, 178.7948), 281 | vector4(145.6977, 6602.8950, 31.4397, 181.0616), 282 | vector4(132.4042, 6585.2139, 31.5501, 91.0906) 283 | } 284 | }, 285 | { 286 | garageType = "heli", 287 | groups = {"lspd"}, 288 | ped = vector4(465.6194, -985.4730, 43.6918, 0.8591), 289 | vehicleSpawns = { 290 | vector4(449.3105, -981.1057, 44.0788, 359.6042) 291 | } 292 | }, 293 | { 294 | garageType = "heli", 295 | groups = {"lsfd"}, 296 | ped = vector4(339.6271, -581.0325, 74.1656, 267.8232), 297 | vehicleSpawns = { 298 | vector4(351.3463, -588.1304, 74.1698, 108.5617) 299 | } 300 | } 301 | } -------------------------------------------------------------------------------- /compatibility/esx/client.lua: -------------------------------------------------------------------------------- 1 | if not lib.table.contains(Config.compatibility, "esx") then return end 2 | 3 | NDCore.Game = {} 4 | NDCore.Game.Utils = {} 5 | NDCore.Scaleform = {} 6 | NDCore.Scaleform.Utils = {} 7 | NDCore.Streaming = {} 8 | NDCore.UI = {} 9 | NDCore.UI.HUD = {} 10 | NDCore.UI.Menu = {} 11 | NDCore.PlayerData = {} 12 | 13 | local uiMetatable = { 14 | __index = function(table, key) 15 | if type(key) == "string" and key:match("^%a+$") then 16 | return function() 17 | print(("[^3WARNING^7] ESX Function '%s' is not compatible with NDCore!"):format(key)) 18 | end 19 | end 20 | end 21 | } 22 | 23 | setmetatable(NDCore.UI, uiMetatable) 24 | setmetatable(NDCore.UI.HUD, uiMetatable) 25 | setmetatable(NDCore.UI.Menu, uiMetatable) 26 | 27 | local function exportHandler(resource, exportName, cb) 28 | AddEventHandler(("__cfx_export_%s_%s"):format(resource, exportName), function(setCB) 29 | setCB(cb) 30 | end) 31 | end 32 | 33 | exportHandler("es_extended", "getSharedObject", function() 34 | return NDCore 35 | end) 36 | 37 | function NDCore.GetPlayerData() 38 | local player = NDCore.getPlayer() 39 | if not player then return {} end 40 | 41 | player.Accounts = { 42 | Bank = player.bank, 43 | Money = player.Cash, 44 | Black = player.Cash 45 | } 46 | 47 | if player.jobInfo then 48 | player.job = { 49 | id = player.job, 50 | name = player.job, 51 | label = player.jobInfo.label, 52 | grade = player.jobInfo.rank, 53 | grade_name = player.jobInfo.rankName, 54 | grade_label = player.jobInfo.rankName, 55 | grade_salary = 0, 56 | skin_male = {}, 57 | skin_female = {} 58 | } 59 | end 60 | 61 | player.coords = GetEntityCoords(cache.ped) 62 | player.loadout = {} 63 | player.maxWeight = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetPlayerMaxWeight() or 0 64 | player.money = player.Accounts.Money 65 | player.sex = player.gender 66 | player.firstName = player.firstname 67 | player.lastName = player.lastname 68 | player.dateofbirth = player.dob 69 | player.height = 120 70 | player.dead = LocalPlayer.state.dead or false 71 | NDCore.PlayerData = player 72 | 73 | return player 74 | end 75 | 76 | function NDCore.IsPlayerLoaded() 77 | return NDCore.getPlayer() ~= nil 78 | end 79 | 80 | function NDCore.Progressbar(message, lenght, options) 81 | local newOptions = { 82 | duration = lenght, 83 | label = message, 84 | canCancel = true 85 | } 86 | 87 | if options.animation then 88 | newOptions.anim = {} 89 | if options.animation.type == "anim" then 90 | if options.animation.dict then 91 | newOptions.anim.dict = options.animation.dict 92 | end 93 | if options.animation.lib then 94 | newOptions.anim.clip = options.animation.lib 95 | end 96 | elseif options.animation.type == "Scenario" then 97 | newOptions.anim.scenario = options.animation.Scenario 98 | end 99 | end 100 | 101 | if options.FreezePlayer then FreezeEntityPosition(cache.ped, true) end 102 | local complete = lib.progressBar(newOptions) 103 | 104 | if options.FreezePlayer then FreezeEntityPosition(cache.ped, false) end 105 | if newOptions.onFinish and complete then 106 | newOptions.onFinish() 107 | end 108 | if newOptions.onCancel and not complete then 109 | newOptions.onCancel() 110 | end 111 | return complete 112 | end 113 | 114 | function NDCore.SearchInventory(item, count) 115 | local itemCount = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:Search(item) or 0 116 | return itemCount >= count and itemCount 117 | end 118 | 119 | function NDCore.SetPlayerData() 120 | print("[^3WARNING^7] ESX Function 'SetPlayerData' is not compatible with NDCore!") 121 | end 122 | 123 | function NDCore.ShowAdvancedNotification() 124 | print("[^3WARNING^7] ESX Function 'ShowAdvancedNotification' is not compatible with NDCore!") 125 | end 126 | 127 | function NDCore.ShowFloatingHelpNotification() 128 | print("[^3WARNING^7] ESX Function 'ShowFloatingHelpNotification' is not compatible with NDCore!") 129 | end 130 | 131 | function NDCore.ShowHelpNotification() 132 | print("[^3WARNING^7] ESX Function 'ShowHelpNotification' is not compatible with NDCore!") 133 | end 134 | 135 | function NDCore.ShowInventory() 136 | return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:openInventory("player", cache.serverId) 137 | end 138 | 139 | function NDCore.ShowNotification(msg, type, time) 140 | NDCore.notify({ 141 | title = "Notification", 142 | description = msg, 143 | type = type == "info" and "inform" or type, 144 | duration = time 145 | }) 146 | end 147 | 148 | function NDCore.TriggerServerCallback(name, cb, ...) 149 | lib.callback(name, nil, cb, ...) 150 | end 151 | 152 | function NDCore.Streaming.RequestAnimDict(animDict, cb) 153 | lib.requestAnimDict(animDict) 154 | if cb then cb(animDict) end 155 | return animDict 156 | end 157 | 158 | function NDCore.Streaming.RequestAnimSet(animSet, cb) 159 | lib.requestAnimSet(animSet) 160 | if cb then cb(animSet) end 161 | return animSet 162 | end 163 | 164 | function NDCore.Streaming.RequestModel(model, cb) 165 | lib.requestModel(model) 166 | if cb then cb(model) end 167 | return model 168 | end 169 | 170 | function NDCore.Streaming.RequestNamedPtfxAsset(assetName, cb) 171 | lib.requestNamedPtfxAsset(assetName) 172 | if cb then cb(assetName) end 173 | return assetName 174 | end 175 | 176 | function NDCore.Streaming.RequestStreamedTextureDict(textureDict, cb) 177 | lib.requestStreamedTextureDict(textureDict) 178 | if cb then cb(textureDict) end 179 | return textureDict 180 | end 181 | 182 | function NDCore.Streaming.RequestWeaponAsset(weaponHash, cb) 183 | lib.requestWeaponAsset(weaponHash) 184 | if cb then cb(weaponHash) end 185 | return weaponHash 186 | end 187 | 188 | function NDCore.Scaleform.ShowBreakingNews() 189 | print("[^3WARNING^7] ESX Function 'Scaleform.ShowBreakingNews' is not compatible with NDCore!") 190 | end 191 | 192 | function NDCore.Scaleform.ShowFreemodeMessage() 193 | print("[^3WARNING^7] ESX Function 'Scaleform.ShowFreemodeMessage' is not compatible with NDCore!") 194 | end 195 | 196 | function NDCore.Scaleform.ShowPopupWarning() 197 | print("[^3WARNING^7] ESX Function 'Scaleform.ShowPopupWarning' is not compatible with NDCore!") 198 | end 199 | 200 | function NDCore.Scaleform.ShowTrafficMovie() 201 | print("[^3WARNING^7] ESX Function 'Scaleform.ShowTrafficMovie' is not compatible with NDCore!") 202 | end 203 | 204 | function NDCore.Scaleform.Utils.RequestScaleformMovie() 205 | print("[^3WARNING^7] ESX Function 'Scaleform.Utils.RequestScaleformMovie' is not compatible with NDCore!") 206 | end 207 | 208 | function NDCore.Game.Utils.DrawText3D(coords, text, size, font) 209 | local onScreen, x, y = World3dToScreen2d(coords.x, coords.y, coords.z) 210 | if not onScreen then return end 211 | SetTextScale(size or 0.4, size or 0.4) 212 | SetTextFont(font or 4) 213 | SetTextProportional(1) 214 | SetTextEntry("STRING") 215 | SetTextCentre(true) 216 | SetTextColour(255, 255, 255, 255) 217 | SetTextOutline() 218 | AddTextComponentString(text) 219 | DrawText(x, y) 220 | end 221 | 222 | function NDCore.Game.DeleteObject(object) 223 | if not DoesEntityExist(object) then return end 224 | DeleteEntity(object) 225 | end 226 | 227 | function NDCore.Game.DeleteVehicle(vehicle) 228 | if not DoesEntityExist(vehicle) then return end 229 | DeleteEntity(vehicle) 230 | end 231 | 232 | function NDCore.Game.GetClosestEntity(coords) 233 | coords = coords or GetEntityCoords(cache.ped) 234 | local loc = vec3(coords.x, coords.y, coords.z) 235 | local entities = {lib.getClosestObject(loc), lib.getClosestPed(loc), lib.getClosestVehicle(loc)} 236 | local closest = nil 237 | local closestDistance = math.huge 238 | 239 | for i = 1, #entities do 240 | local ent = entities[i] 241 | if DoesEntityExist(ent.object or ent.ped or ent.vehicle) then 242 | local distance = #(loc-ent.coords) 243 | if distance < closestDistance then 244 | closest = ent 245 | closestDistance = distance 246 | end 247 | end 248 | end 249 | return closest.object or closest.ped or closest.vehicle 250 | end 251 | 252 | function NDCore.Game.GetClosestObject(coords) 253 | coords = coords or GetEntityCoords(cache.ped) 254 | local loc = vec3(coords.x, coords.y, coords.z) 255 | return lib.getClosestObject(loc).object 256 | end 257 | 258 | function NDCore.Game.GetClosestPed(coords) 259 | coords = coords or GetEntityCoords(cache.ped) 260 | local loc = vec3(coords.x, coords.y, coords.z) 261 | return lib.getClosestPed(loc).ped 262 | end 263 | 264 | function NDCore.Game.GetClosestPlayer(coords) 265 | coords = coords or GetEntityCoords(cache.ped) 266 | local loc = vec3(coords.x, coords.y, coords.z) 267 | local ply = lib.getClosestPlayer(loc) 268 | return ply.playerId, #(ply.playerCoords-loc) 269 | end 270 | 271 | function NDCore.Game.GetClosestVehicle(coords) 272 | coords = coords or GetEntityCoords(cache.ped) 273 | local loc = vec3(coords.x, coords.y, coords.z) 274 | return lib.getClosestVehicle(loc).vehicle 275 | end 276 | 277 | function NDCore.Game.GetObjects() 278 | return GetGamePool("CObject") 279 | end 280 | 281 | function NDCore.Game.GetPedMugshot() 282 | print("[^3WARNING^7] ESX Function 'Game.GetPedMugshot' is not compatible with NDCore!") 283 | end 284 | 285 | function NDCore.Game.GetPeds(onlyOtherPeds) 286 | local peds = GetGamePool("CPed") 287 | if onlyOtherPeds then 288 | for i=1, #peds do 289 | if peds[i] == cache.ped then 290 | table.remove(peds, i) 291 | end 292 | end 293 | end 294 | return peds 295 | end 296 | 297 | function NDCore.Game.GetPlayers() 298 | print("[^3WARNING^7] ESX Function 'Game.GetPlayers' is not compatible with NDCore!") 299 | end 300 | 301 | function NDCore.Game.GetPlayersInArea() 302 | print("[^3WARNING^7] ESX Function 'Game.GetPlayersInArea' is not compatible with NDCore!") 303 | end 304 | 305 | function NDCore.Game.GetPlayersInArea() 306 | print("[^3WARNING^7] ESX Function 'Game.GetPlayersInArea' is not compatible with NDCore!") 307 | end 308 | 309 | function NDCore.Game.GetVehicleInDirection() 310 | print("[^3WARNING^7] ESX Function 'Game.GetVehicleInDirection' is not compatible with NDCore!") 311 | end 312 | 313 | function NDCore.Game.GetVehicleProperties(vehicle) 314 | return lib.getVehicleProperties(vehicle) 315 | end 316 | 317 | function NDCore.Game.GetVehicles() 318 | return GetGamePool("CVehicle") 319 | end 320 | 321 | function NDCore.Game.GetVehiclesInArea() 322 | print("[^3WARNING^7] ESX Function 'Game.GetVehiclesInArea' is not compatible with NDCore!") 323 | end 324 | 325 | function NDCore.Game.IsSpawnPointClear() 326 | print("[^3WARNING^7] ESX Function 'Game.IsSpawnPointClear' is not compatible with NDCore!") 327 | end 328 | 329 | function NDCore.Game.IsVehicleEmpty(vehicle) 330 | for i=-1, 6 do 331 | if not IsVehicleSeatFree(vehicle, i) then 332 | return false 333 | end 334 | end 335 | return true 336 | end 337 | 338 | function NDCore.Game.SetVehicleProperties(vehicle, props) 339 | lib.setVehicleProperties(vehicle, props) 340 | end 341 | 342 | function NDCore.Game.SpawnLocalObject(model, coords, cb) 343 | if type(model) == "string" then 344 | model = GetHashKey(model) 345 | end 346 | local entity = CreateObject(model, coords.x, coords.y, coords.z, false, false, false) 347 | entity = lib.waitFor(function() 348 | if DoesEntityExist(entity) then return entity end 349 | end) 350 | if cb then cb(entity) end 351 | return entity 352 | end 353 | 354 | function NDCore.Game.SpawnLocalVehicle(model, coords, heading, cb) 355 | if type(model) == "string" then 356 | model = GetHashKey(model) 357 | end 358 | local entity = CreateVehicle(model, coords.x, coords.y, coords.z, heading, false, false, false) 359 | entity = lib.waitFor(function() 360 | if DoesEntityExist(entity) then return entity end 361 | end) 362 | if cb then cb(entity) end 363 | return entity 364 | end 365 | 366 | function NDCore.Game.SpawnObject(model, coords, cb) 367 | if type(model) == "string" then 368 | model = GetHashKey(model) 369 | end 370 | local entity = CreateObject(model, coords.x, coords.y, coords.z, true, false, false) 371 | entity = lib.waitFor(function() 372 | if DoesEntityExist(entity) then return entity end 373 | end) 374 | if cb then cb(entity) end 375 | return entity 376 | end 377 | 378 | function NDCore.Game.SpawnVehicle(model, coords, heading, cb) 379 | if type(model) == "string" then 380 | model = GetHashKey(model) 381 | end 382 | local entity = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false, false) 383 | entity = lib.waitFor(function() 384 | if DoesEntityExist(entity) then return entity end 385 | end) 386 | if cb then cb(entity) end 387 | return entity 388 | end 389 | 390 | function NDCore.Game.Teleport(entity, coords, cb) 391 | if DoesEntityExist(entity) then 392 | RequestCollisionAtCoord(coords.x, coords.y, coords.z) 393 | while not HasCollisionLoadedAroundEntity(entity) do 394 | Wait(0) 395 | end 396 | 397 | SetEntityCoords(entity, coords.x, coords.y, coords.z, false, false, false, false) 398 | SetEntityHeading(entity, coords.w or coords.heading or 0.0) 399 | end 400 | 401 | if cb then 402 | cb() 403 | end 404 | end 405 | 406 | AddEventHandler("ND:characterLoaded", function() 407 | NDCore.GetPlayerData() 408 | end) 409 | 410 | AddEventHandler("ND:updateCharacter", function() 411 | NDCore.GetPlayerData() 412 | end) 413 | -------------------------------------------------------------------------------- /server/commands.lua: -------------------------------------------------------------------------------- 1 | local moneyTypes = {"bank", "cash"} 2 | local moneyActions = { 3 | remove = function(player, account, amount) 4 | player.deductMoney(account, amount, locale("staff_action")) 5 | return locale("staff_money_removed", amount, account, player.name), locale("user_money_removed", amount, account) 6 | end, 7 | add = function(player, account, amount) 8 | player.addMoney(account, amount, locale("staff_action")) 9 | return locale("staff_money_added", amount, account, player.name), locale("user_money_added", amount, account) 10 | end, 11 | set = function(player, account, amount) 12 | player.setData(account, amount, locale("staff_action")) 13 | return locale("staff_money_set", player.name, account, amount), locale("user_money_set", account, amount) 14 | end 15 | } 16 | 17 | lib.addCommand("setmoney", { 18 | help = "Admin command, set a players money.", 19 | restricted = "group.admin", 20 | params = { 21 | { 22 | name = "target", 23 | type = "playerId", 24 | help = "Target player's server id" 25 | }, 26 | { 27 | name = "action", 28 | type = "string", 29 | help = "remove/add/set" 30 | }, 31 | { 32 | name = "type", 33 | type = "string", 34 | help = "bank/cash" 35 | }, 36 | { 37 | name = "amount", 38 | type = "number" 39 | } 40 | } 41 | }, function(source, args, raw) 42 | local action = moneyActions[args.action] 43 | local moneyType = args.type:lower() 44 | if not action or not lib.table.contains(moneyTypes, moneyType) then return end 45 | 46 | local player = NDCore.getPlayer(args.target) 47 | if not player then return end 48 | local staffMessage, userMessage = action(player, moneyType, args.amount) 49 | 50 | player.notify({ 51 | title = locale("staff_action"), 52 | description = userMessage, 53 | type = "inform", 54 | duration = 10000 55 | }) 56 | 57 | if not source or source == 0 then return end 58 | TriggerClientEvent("chat:addMessage", source, { 59 | color = {50, 100, 235}, 60 | multiline = true, 61 | args = {locale("staff_action"), staffMessage} 62 | }) 63 | end) 64 | 65 | lib.addCommand("setjob", { 66 | help = "Admin command, set a players job.", 67 | restricted = "group.admin", 68 | params = { 69 | { 70 | name = "target", 71 | type = "playerId", 72 | help = "Target player's server id" 73 | }, 74 | { 75 | name = "job", 76 | type = "string", 77 | help = "Job name" 78 | }, 79 | { 80 | name = "rank", 81 | type = "number", 82 | optional = true 83 | } 84 | } 85 | }, function(source, args, raw) 86 | local player = NDCore.getPlayer(args.target) 87 | if not player then return end 88 | 89 | local job = args.job:lower() 90 | local jobInfo = player.setJob(job, args.rank) 91 | if not player or not jobInfo then return end 92 | player.notify({ 93 | title = locale("staff_action"), 94 | description = locale("job_updated_noti", jobInfo.label, jobInfo.rankName), 95 | type = "inform", 96 | duration = 10000 97 | }) 98 | 99 | if not source or source == 0 then return end 100 | TriggerClientEvent("chat:addMessage", source, { 101 | color = {50, 100, 235}, 102 | multiline = true, 103 | args = {locale("staff_action"), locale("success")} 104 | }) 105 | end) 106 | 107 | lib.addCommand("setgroup", { 108 | help = "Admin command, set a players group.", 109 | restricted = "group.admin", 110 | params = { 111 | { 112 | name = "target", 113 | type = "playerId", 114 | help = "Target player's server id" 115 | }, 116 | { 117 | name = "action", 118 | type = "string", 119 | help = "remove/add" 120 | }, 121 | { 122 | name = "group", 123 | type = "string", 124 | help = "group name" 125 | }, 126 | { 127 | name = "rank", 128 | type = "number", 129 | optional = true 130 | } 131 | } 132 | }, function(source, args, raw) 133 | local player = NDCore.getPlayer(args.target) 134 | if not player then return end 135 | 136 | if args.action == "add" then 137 | local groupInfo = player.addGroup(args.group, args.rank) 138 | if not groupInfo then return end 139 | player.notify({ 140 | title = locale("staff_action"), 141 | description = locale("group_added_noti", groupInfo.label, groupInfo.rankName), 142 | type = "inform", 143 | duration = 10000 144 | }) 145 | elseif args.action == "remove" then 146 | local groupInfo = player.removeGroup(args.group) 147 | if not groupInfo then return end 148 | player.notify({ 149 | title = locale("staff_action"), 150 | description = locale("group_removed_noti", groupInfo.label), 151 | type = "inform", 152 | duration = 10000 153 | }) 154 | else 155 | return 156 | end 157 | 158 | if not source or source == 0 then return end 159 | TriggerClientEvent("chat:addMessage", source, { 160 | color = {50, 100, 235}, 161 | multiline = true, 162 | args = {locale("staff_action"), locale("success")} 163 | }) 164 | end) 165 | 166 | lib.addCommand("skin", { 167 | help = "Admin command, show character clothing menu for player.", 168 | restricted = "group.admin", 169 | params = { 170 | { 171 | name = "target", 172 | type = "playerId", 173 | help = "Target player's server id" 174 | } 175 | } 176 | }, function(source, args, raw) 177 | TriggerClientEvent("ND:clothingMenu", args.target) 178 | end) 179 | 180 | lib.addCommand("character", { 181 | help = "Admin command, show character selection menu for player.", 182 | restricted = "group.admin", 183 | params = { 184 | { 185 | name = "target", 186 | type = "playerId", 187 | help = "Target player's server id" 188 | } 189 | } 190 | }, function(source, args, raw) 191 | TriggerClientEvent("ND:characterMenu", args.target) 192 | end) 193 | 194 | lib.addCommand("pay", { 195 | help = "give money to nearby player.", 196 | params = { 197 | { 198 | name = "target", 199 | type = "playerId", 200 | help = "Target player's server id" 201 | }, 202 | { 203 | name = "amount", 204 | type = "number", 205 | help = "Amount of cash to give" 206 | } 207 | } 208 | }, function(source, args, raw) 209 | if not source or source == 0 then return end 210 | 211 | local player = NDCore.getPlayer(source) 212 | local targetPlayer = NDCore.getPlayer(args.target) 213 | 214 | if not player or not targetPlayer then 215 | return player.notify({ 216 | title = "Error", 217 | description = locale("no_player_found"), 218 | type = "error" 219 | }) 220 | end 221 | 222 | if player.cash < args.amount then 223 | return player.notify({ 224 | title = "Error", 225 | description = locale("not_enough_cash"), 226 | type = "error" 227 | }) 228 | end 229 | 230 | local playerCoords = GetEntityCoords(GetPlayerPed(player.source)) 231 | local targetCoords = GetEntityCoords(GetPlayerPed(targetPlayer.source)) 232 | if #(playerCoords-targetCoords) > 2.0 then 233 | return player.notify({ 234 | title = "Error", 235 | description = locale("player_not_nearby"), 236 | type = "error" 237 | }) 238 | end 239 | 240 | local success = player.deductMoney("cash", args.amount) and targetPlayer.addMoney("cash", args.amount) 241 | if not success then 242 | return player.notify({ 243 | title = locale("cant_give_money"), 244 | type = "error", 245 | duration = 5000 246 | }) 247 | end 248 | 249 | targetPlayer.notify({ 250 | title = locale("money_received"), 251 | description = locale("money_received2", args.amount), 252 | type = "inform", 253 | duration = 5000 254 | }) 255 | 256 | player.notify({ 257 | title = locale("money_given"), 258 | description = locale("money_given2", args.amount), 259 | type = "inform", 260 | duration = 5000 261 | }) 262 | end) 263 | 264 | lib.addCommand("unlock", { 265 | help = "Admin Command, force unlock a vehicle.", 266 | restricted = "group.admin", 267 | }, function(source, args, raw) 268 | local ped = GetPlayerPed(source) 269 | local coords = GetEntityCoords(ped) 270 | local veh = lib.getClosestVehicle(coords, 2.5, true) 271 | 272 | if not veh then return end 273 | 274 | local state = Entity(veh).state 275 | state.hotwired = true 276 | state.locked = false 277 | end) 278 | 279 | lib.addCommand("revive", { 280 | help = "Admin command, revive a player.", 281 | restricted = "group.admin", 282 | params = { 283 | { 284 | name = "target", 285 | type = "playerId", 286 | help = "Target player's server id" 287 | } 288 | } 289 | }, function(source, args, raw) 290 | local player = NDCore.getPlayer(args.target) 291 | if not player then return end 292 | player.revive() 293 | end) 294 | 295 | lib.addCommand("dv", { 296 | help = "Admin command, delete vehicles within the range or the closest.", 297 | restricted = "group.admin", 298 | params = { 299 | { 300 | name = "range", 301 | type = "number", 302 | help = "Range to delete vehicles in", 303 | optional = true 304 | } 305 | } 306 | }, function(source, args, raw) 307 | local ped = GetPlayerPed(source) 308 | local coords = GetEntityCoords(ped) 309 | local count = 0 310 | local message = nil 311 | local veh = lib.getClosestVehicle(coords, 3.0) 312 | 313 | if args.range then 314 | local vehicles = lib.getNearbyVehicles(coords, args.range) 315 | count = #vehicles 316 | 317 | for i=1, count do 318 | local veh = vehicles[i] 319 | DeleteEntity(veh.vehicle) 320 | end 321 | elseif veh then 322 | DeleteEntity(veh) 323 | count = 1 324 | end 325 | 326 | local messageLabel = ("%s [dv]"):format(locale("staff_action")) 327 | if count == 0 then 328 | message = {messageLabel, "no vehicles found"} 329 | elseif count == 1 then 330 | message = {messageLabel, "deleted 1 vehicle"} 331 | else 332 | message = {messageLabel, ("deleted %d vehicles"):format(count)} 333 | end 334 | 335 | TriggerClientEvent("chat:addMessage", source, { 336 | color = {50, 100, 235}, 337 | multiline = true, 338 | args = message 339 | }) 340 | end) 341 | 342 | lib.addCommand("goto", { 343 | help = "Admin command, teleport to a player.", 344 | restricted = "group.admin", 345 | params = { 346 | { 347 | name = "target", 348 | type = "playerId", 349 | help = "Target player's server id" 350 | } 351 | } 352 | }, function(source, args, raw) 353 | if args.target == source then return end 354 | local target = GetPlayerPed(args.target) 355 | local coords = GetEntityCoords(target) 356 | local ped = GetPlayerPed(source) 357 | SetEntityCoords(ped, coords.x, coords.y, coords.z) 358 | end) 359 | 360 | lib.addCommand("bring", { 361 | help = "Admin command, teleport a player to you.", 362 | restricted = "group.admin", 363 | params = { 364 | { 365 | name = "target", 366 | type = "playerId", 367 | help = "Target player's server id" 368 | } 369 | } 370 | }, function(source, args, raw) 371 | if args.target == source then return end 372 | local ped = GetPlayerPed(source) 373 | local coords = GetEntityCoords(ped) 374 | local targetPed = GetPlayerPed(args.target) 375 | SetEntityCoords(targetPed, coords.x, coords.y, coords.z) 376 | end) 377 | 378 | lib.addCommand("freeze", { 379 | help = "Admin command, freeze a player.", 380 | restricted = "group.admin", 381 | params = { 382 | { 383 | name = "target", 384 | type = "playerId", 385 | help = "Target player's server id" 386 | } 387 | } 388 | }, function(source, args, raw) 389 | local ped = GetPlayerPed(args.target) 390 | FreezeEntityPosition(ped, true) 391 | end) 392 | 393 | lib.addCommand("unfreeze", { 394 | help = "Admin command, unfreeze a player.", 395 | restricted = "group.admin", 396 | params = { 397 | { 398 | name = "target", 399 | type = "playerId", 400 | help = "Target player's server id" 401 | } 402 | } 403 | }, function(source, args, raw) 404 | local ped = GetPlayerPed(args.target) 405 | FreezeEntityPosition(ped, false) 406 | end) 407 | 408 | lib.addCommand("vehicle", { 409 | help = "Admin command, spawn a vehicle.", 410 | restricted = "group.admin", 411 | params = { 412 | { 413 | name = "model", 414 | type = "string", 415 | help = "Name of the vehicle to spawn" 416 | } 417 | } 418 | }, function(source, args, raw) 419 | local player = NDCore.getPlayer(source) 420 | if not player then return end 421 | 422 | local ped = GetPlayerPed(source) 423 | local coords = GetEntityCoords(ped) 424 | local heading = GetEntityHeading(ped) 425 | local info = NDCore.createVehicle({ 426 | owner = player.id, 427 | coords = coords, 428 | heading = heading, 429 | model = GetHashKey(args.model) 430 | }) 431 | 432 | local veh = info.entity 433 | for i=1, 10 do 434 | if GetPedInVehicleSeat(veh, -1) ~= ped then 435 | SetPedIntoVehicle(ped, veh, -1) 436 | else 437 | break 438 | end 439 | Wait(100) 440 | end 441 | end) 442 | 443 | lib.addCommand("claim-veh", { 444 | help = "Admin command, add a copy of the vehicle you're inside to players garage.", 445 | restricted = "group.admin", 446 | params = { 447 | { 448 | name = "target", 449 | type = "playerId", 450 | help = "Target player's server id" 451 | } 452 | } 453 | }, function(source, args, raw) 454 | local player = NDCore.getPlayer(source) 455 | if not player then return end 456 | 457 | local targetPlayer = NDCore.getPlayer(args.target) 458 | if not targetPlayer then 459 | return player.notify({ 460 | title = "Player not found!", 461 | description = "Target player not found, make sure they select a charcter!", 462 | type = "error" 463 | }) 464 | end 465 | 466 | 467 | local properties = lib.callback.await("ND_Vehicles:getPropsFromCurrentVeh", source) 468 | if not properties then 469 | return player.notify({ 470 | title = "Vehicle data not found!", 471 | description = "The vehicle properties data was not found!", 472 | type = "error" 473 | }) 474 | end 475 | 476 | NDCore.setVehicleOwned(targetPlayer.id, properties, true) 477 | 478 | player.notify({ 479 | title = "Vehicle added!", 480 | description = ("The vehicle has now been added to %s's garage!"):format(targetPlayer.name), 481 | type = "success" 482 | }) 483 | end) 484 | -------------------------------------------------------------------------------- /server/player.lua: -------------------------------------------------------------------------------- 1 | local avoidSavingLastLocations = {} 2 | 3 | local function removeCharacterFunctions(character) 4 | local newData = {} 5 | for k, v in pairs(character) do 6 | if type(v) ~= "function" then 7 | newData[k] = v 8 | end 9 | end 10 | return newData 11 | end 12 | 13 | local function createCharacterTable(info) 14 | local playerInfo = PlayersInfo[info.source] or {} 15 | 16 | local self = { 17 | id = info.id, 18 | source = info.source, 19 | identifier = info.identifier, 20 | identifiers = playerInfo.identifiers or {}, 21 | discord = playerInfo.discord or {}, 22 | name = info.name, 23 | firstname = info.firstname, 24 | lastname = info.lastname, 25 | fullname = ("%s %s"):format(info.firstname, info.lastname), 26 | dob = info.dob, 27 | gender = info.gender, 28 | cash = info.cash, 29 | bank = info.bank, 30 | phonenumber = info.phonenumber, 31 | groups = info.groups, 32 | metadata = info.metadata, 33 | inventory = info.inventory 34 | } 35 | 36 | ---@param account string 37 | ---@param amount number 38 | ---@param reason string|nil 39 | ---@return boolean 40 | function self.deductMoney(account, amount, reason) 41 | local amount = tonumber(amount) 42 | if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end 43 | self[account] -= amount 44 | if NDCore.players[self.source] then 45 | self.triggerEvent("ND:updateMoney", self.cash, self.bank) 46 | TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason) 47 | end 48 | return true 49 | end 50 | 51 | ---@param account string 52 | ---@param amount number 53 | ---@param reason string|nil 54 | ---@return boolean 55 | function self.addMoney(account, amount, reason) 56 | local amount = tonumber(amount) 57 | if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end 58 | self[account] += amount 59 | if NDCore.players[self.source] then 60 | self.triggerEvent("ND:updateMoney", self.cash, self.bank) 61 | TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason) 62 | end 63 | return true 64 | end 65 | 66 | ---@param amount number 67 | ---@return boolean 68 | function self.depositMoney(amount) 69 | local amount = tonumber(amount) 70 | if not amount or self.cash < amount or amount <= 0 then return end 71 | return self.deductMoney("cash", amount, locale("deposit")) and self.addMoney("bank", amount, locale("deposit")) 72 | end 73 | 74 | ---@param amount number 75 | ---@return boolean 76 | function self.withdrawMoney(amount) 77 | local amount = tonumber(amount) 78 | if not amount or self.bank < amount or amount <= 0 then return end 79 | return self.deductMoney("bank", amount, locale("withdraw")) and self.addMoney("cash", amount, locale("withdraw")) 80 | end 81 | 82 | ---@param data string 83 | ---@return any 84 | function self.getData(data) 85 | return self[data] 86 | end 87 | 88 | ---@param metadata string|table 89 | ---@return any 90 | function self.getMetadata(metadata) 91 | if type(metadata) ~= "table" then 92 | return self.metadata[metadata] 93 | end 94 | local returnData = {} 95 | for i=1, #metadata do 96 | local data = metadata[i] 97 | returnData[data] = self.metadata[data] 98 | end 99 | return returnData 100 | end 101 | 102 | ---@param key string|table 103 | ---@param value any 104 | function self.setData(key, value, reason) 105 | if type(key) == "table" then 106 | for k, v in pairs(key) do 107 | self[k] = v 108 | if k == "cash" or k == "bank" then 109 | TriggerEvent("ND:moneyChange", self.source, k, v, "set", reason) 110 | end 111 | end 112 | else 113 | self[key] = value 114 | if key == "cash" or key == "bank" then 115 | TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason) 116 | end 117 | end 118 | self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), key) 119 | TriggerEvent("ND:updateCharacter", self, key) 120 | end 121 | 122 | ---@param key string|table 123 | ---@param value any 124 | ---@return table 125 | function self.setMetadata(key, value) 126 | if type(key) == "table" then 127 | for k, v in pairs(key) do 128 | self.metadata[k] = v 129 | end 130 | else 131 | self.metadata[key] = value 132 | end 133 | self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata") 134 | TriggerEvent("ND:updateCharacter", self, "metadata") 135 | return self.metadata 136 | end 137 | 138 | -- Completely delete character 139 | function self.delete() 140 | local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id}) 141 | if result and NDCore.players[self.source] then 142 | NDCore.players[self.source] = nil 143 | end 144 | return result 145 | end 146 | 147 | -- Unload and save character 148 | function self.unload() 149 | if not NDCore.players[self.source] then return end 150 | 151 | for name, _ in pairs(self.groups) do 152 | lib.removePrincipal(self.source, ("group.%s"):format(name)) 153 | end 154 | 155 | local ped = GetPlayerPed(self.source) 156 | if ped then 157 | local coords = GetEntityCoords(ped) 158 | local heading = GetEntityHeading(ped) 159 | local saveLocation = true 160 | 161 | for i=1, #avoidSavingLastLocations do 162 | local loc = avoidSavingLastLocations[i] 163 | if #(loc.coords-coords) < loc.dist then 164 | saveLocation = false 165 | end 166 | end 167 | 168 | if saveLocation then 169 | self.setMetadata("location", { 170 | x = coords.x, 171 | y = coords.y, 172 | z = coords.z, 173 | w = heading 174 | }) 175 | end 176 | end 177 | 178 | self.triggerEvent("ND:characterUnloaded") 179 | TriggerEvent("ND:characterUnloaded", self.source, self) 180 | 181 | local saved = self.save() 182 | NDCore.players[self.source] = nil 183 | 184 | return saved 185 | end 186 | 187 | -- Save character information to database 188 | function self.save(key) 189 | if key then 190 | local charData = self[key] 191 | if not charData or type(charData) == "function" then 192 | return lib.print.error(key, "Is not valid character data.") 193 | end 194 | 195 | if type(charData) == "table" then 196 | charData = json.encode(charData) 197 | end 198 | 199 | local query = ("UPDATE nd_characters SET `%s` = ? WHERE charid = ?"):format(key) 200 | local affectedRows = MySQL.update.await(query, {charData, self.id}) 201 | return affectedRows > 0 202 | end 203 | 204 | local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, phonenumber = ?, `groups` = ?, metadata = ? WHERE charid = ?", { 205 | self.name, 206 | self.firstname, 207 | self.lastname, 208 | self.dob, 209 | self.gender, 210 | self.cash, 211 | self.bank, 212 | self.phonenumber, 213 | json.encode(self.groups), 214 | json.encode(self.metadata), 215 | self.id 216 | }) 217 | return affectedRows > 0 218 | end 219 | 220 | ---Create a license/permit for the character 221 | ---@param licenseType string 222 | ---@param expire number 223 | function self.createLicense(licenseType, expire) 224 | local expireIn = tonumber(expire) or 2592000 225 | local time = os.time() 226 | local licenses = self.metadata.licenses 227 | local identifier = {} 228 | 229 | for i=1, 16 do 230 | identifier[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) 231 | end 232 | 233 | local license = { 234 | type = licenseType, 235 | status = "valid", 236 | issued = time, 237 | expires = time+expireIn, 238 | identifier = table.concat(identifier) 239 | } 240 | 241 | if licenses then 242 | local found = false 243 | 244 | for i=1, #licenses do 245 | if licenses[i].type == licenseType then 246 | self.metadata.licenses[i] = license 247 | found = true 248 | break 249 | end 250 | end 251 | 252 | if not found then 253 | self.metadata.licenses[#licenses+1] = license 254 | end 255 | else 256 | self.metadata.licenses = {license} 257 | end 258 | self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata") 259 | TriggerEvent("ND:updateCharacter", self, "metadata") 260 | end 261 | 262 | function self.getLicense(identifier) 263 | local licenses = self.metadata.licenses or {} 264 | for i=1, #licenses do 265 | local data = licenses[i] 266 | if data.identifier == identifier then 267 | return data, i 268 | end 269 | end 270 | end 271 | 272 | function self.updateLicense(identifier, newData) 273 | local data, i = self.getLicense(identifier) 274 | if not data then return end 275 | for k, v in pairs(newData) do 276 | data[k] = v 277 | end 278 | self.save() 279 | end 280 | 281 | ---@param coords vector3|vector4 282 | ---@return boolean 283 | function self.setCoords(coords) 284 | if not self.source or not coords then return end 285 | local ped = GetPlayerPed(self.source) 286 | if not DoesEntityExist(ped) then return end 287 | SetEntityCoords(ped, coords.x, coords.y, coords.z) 288 | if coords.w then 289 | SetEntityHeading(ped, coords.w) 290 | end 291 | return true 292 | end 293 | 294 | ---@param eventName string 295 | ---@param ... any 296 | ---@return boolean 297 | function self.triggerEvent(eventName, ...) 298 | if not self.source then return end 299 | TriggerClientEvent(eventName, self.source, ...) 300 | return true 301 | end 302 | 303 | function self.notify(...) 304 | if not self.source then return end 305 | TriggerClientEvent("ox_lib:notify", self.source, ...) 306 | return true 307 | end 308 | 309 | function self.revive() 310 | self.triggerEvent("ND:revivePlayer") 311 | self.setMetadata({ 312 | dead = false, 313 | deathInfo = false, 314 | }) 315 | end 316 | 317 | ---@param reason string 318 | function self.drop(reason) 319 | if not self.source then return end 320 | DropPlayer(self.source, reason) 321 | end 322 | 323 | -- Set the character as the players active character/currently playing character 324 | function self.active() 325 | local char = NDCore.players[self.source] 326 | if char and char.id == self.id then return end 327 | if char then char.unload() end 328 | for identifierType, identifier in pairs(self.identifiers) do 329 | if lib.table.contains(Config.admins, ("%s:%s"):format(identifierType, identifier)) then 330 | self.addGroup("admin") 331 | end 332 | end 333 | 334 | local roles = self.discord.roles 335 | if roles then 336 | for i=1, #Config.adminDiscordRoles do 337 | local role = Config.adminDiscordRoles[i] 338 | if lib.table.contains(roles, role) then 339 | self.addGroup("admin") 340 | end 341 | end 342 | 343 | for group, role in pairs(Config.groupRoles) do 344 | if lib.table.contains(roles, role) then 345 | self.addGroup(group) 346 | end 347 | end 348 | end 349 | 350 | for name, _ in pairs(self.groups) do 351 | lib.addPrincipal(self.source, ("group.%s"):format(name)) 352 | end 353 | NDCore.players[self.source] = self 354 | TriggerEvent("ND:characterLoaded", self) 355 | self.triggerEvent("ND:characterLoaded", removeCharacterFunctions(self)) 356 | end 357 | 358 | ---@param name string 359 | ---@param rank number 360 | ---@param isJob boolean 361 | ---@return boolean 362 | function self.addGroup(name, rank, customGroup, isJob) 363 | local groupRank = tonumber(rank) or 1 364 | local groupInfo = lib.table.deepclone(Config.groups?[name] or {}) 365 | local bossRank = groupInfo?.minimumBossRank 366 | 367 | for k, v in pairs(customGroup or {}) do 368 | groupInfo[k] = v 369 | end 370 | 371 | if isJob then 372 | for _, group in pairs(self.groups) do 373 | group.isJob = nil 374 | end 375 | end 376 | 377 | self.groups[name] = { 378 | name = name, 379 | label = groupInfo?.label or name, 380 | rankName = groupInfo?.ranks?[groupRank] or groupRank, 381 | rank = groupRank, 382 | isJob = isJob, 383 | isBoss = bossRank and groupRank >= bossRank, 384 | metadata = groupInfo.metadata or {} 385 | } 386 | 387 | if not isJob then 388 | self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups") 389 | TriggerEvent("ND:updateCharacter", self, "groups") 390 | end 391 | 392 | lib.addPrincipal(self.source, ("group.%s"):format(name)) 393 | 394 | return self.groups[name] 395 | end 396 | 397 | ---@param name string 398 | ---@return table 399 | function self.getGroup(name) 400 | return self.groups[name] 401 | end 402 | 403 | ---@param name string 404 | function self.removeGroup(name) 405 | local group = self.groups[name] 406 | if not group then return end 407 | 408 | if self.job == name then 409 | self.job = nil 410 | self.jobInfo = nil 411 | end 412 | 413 | self.groups[name] = nil 414 | self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups") 415 | TriggerEvent("ND:updateCharacter", self, "groups") 416 | TriggerEvent("ND:groupRemoved", self, group) 417 | lib.removePrincipal(self.source, ("group.%s"):format(name)) 418 | return group 419 | end 420 | 421 | ---@param name string 422 | ---@param rank number 423 | ---@return boolean 424 | function self.setJob(name, rank, customGroup) 425 | self.removeGroup(self.job) 426 | local job = self.addGroup(name, rank, customGroup, true) 427 | if job then 428 | self.job = job.name 429 | self.jobInfo = job 430 | end 431 | self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "job") 432 | TriggerEvent("ND:updateCharacter", self, "job") 433 | return job 434 | end 435 | 436 | ---@param job string 437 | ---@return boolean 438 | function self.getJob() 439 | for name, group in pairs(self.groups) do 440 | if group.isJob then 441 | return name, group 442 | end 443 | end 444 | end 445 | 446 | local jobName, jobInfo = self.getJob() 447 | if jobInfo then 448 | self.job = jobName 449 | self.jobInfo = jobInfo 450 | end 451 | 452 | return self 453 | end 454 | 455 | function NDCore.avoidSavingLocation(coords, dist) 456 | avoidSavingLastLocations[#avoidSavingLastLocations+1] = { coords = coords, dist = dist} 457 | end 458 | 459 | function NDCore.validateCharacterData(charInfo) 460 | local errors = {} 461 | 462 | local function checkLength(field, value, maxLength) 463 | if type(value) ~= "string" then return end 464 | if #value > maxLength then 465 | table.insert(errors, string.format("%s is too long (max %d, got %d)", field, maxLength, #value)) 466 | end 467 | end 468 | 469 | if charInfo.name then checkLength("game name", charInfo.name, 50) end 470 | if charInfo.firstname then checkLength("firstname", charInfo.firstname, 50) end 471 | if charInfo.lastname then checkLength("lastname", charInfo.lastname, 50) end 472 | if charInfo.dob then checkLength("dob", charInfo.dob, 50) end 473 | if charInfo.gender then checkLength("gender", charInfo.gender, 50) end 474 | if type(charInfo.cash) ~= "number" then table.insert(errors, "cash must be a number") end 475 | if type(charInfo.bank) ~= "number" then table.insert(errors, "bank must be a number") end 476 | 477 | if #errors > 0 then 478 | return false, errors 479 | else 480 | return true, {} 481 | end 482 | end 483 | 484 | ---@param src number 485 | ---@param info table 486 | ---@return table 487 | function NDCore.newCharacter(src, info) 488 | local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier) 489 | if not identifier then return end 490 | 491 | local charInfo = { 492 | source = src, 493 | identifier = identifier, 494 | name = GetPlayerName(src) or "", 495 | firstname = info.firstname or "", 496 | lastname = info.lastname or "", 497 | dob = info.dob or "", 498 | gender = info.gender or "", 499 | cash = info.cash or 0, 500 | bank = info.bank or 0, 501 | phonenumber = info.phonenumber, 502 | groups = info.groups or {}, 503 | metadata = info.metadata or {}, 504 | inventory = info.inventory or {} 505 | } 506 | 507 | local valid, issues = NDCore.validateCharacterData(charInfo) 508 | if not valid then 509 | for _, err in ipairs(issues) do 510 | Wait(100) 511 | TriggerClientEvent("ND:notifyClient", src, { 512 | description = err, 513 | type = "error" 514 | }) 515 | end 516 | return 517 | end 518 | 519 | charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, phonenumber, `groups`, metadata, inventory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", { 520 | identifier, 521 | charInfo.name, 522 | charInfo.firstname, 523 | charInfo.lastname, 524 | charInfo.dob, 525 | charInfo.gender, 526 | charInfo.cash, 527 | charInfo.bank, 528 | charInfo.phonenumber, 529 | json.encode(charInfo.groups), 530 | json.encode(charInfo.metadata), 531 | json.encode(charInfo.inventory) 532 | }) 533 | 534 | return createCharacterTable(charInfo) 535 | end 536 | 537 | ---@param id number 538 | ---@return table 539 | function NDCore.fetchCharacter(id, src) 540 | local result 541 | if src then 542 | result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? and identifier = ?", {id, NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)}) 543 | else 544 | result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id}) 545 | end 546 | 547 | if not result then return end 548 | local info = result[1] 549 | return createCharacterTable({ 550 | source = src, 551 | id = info.charid, 552 | identifier = info.identifier, 553 | name = info.name, 554 | firstname = info.firstname, 555 | lastname = info.lastname, 556 | dob = info.dob, 557 | gender = info.gender, 558 | cash = info.cash, 559 | bank = info.bank, 560 | phonenumber = info.phonenumber, 561 | groups = json.decode(info.groups), 562 | metadata = json.decode(info.metadata), 563 | inventory = json.decode(info.inventory) 564 | }) 565 | end 566 | 567 | ---@param src number 568 | ---@return table 569 | function NDCore.fetchAllCharacters(src) 570 | local characters = {} 571 | local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)}) 572 | 573 | for i=1, #result do 574 | local info = result[i] 575 | characters[info.charid] = createCharacterTable({ 576 | source = src, 577 | id = info.charid, 578 | identifier = info.identifier, 579 | name = info.name, 580 | firstname = info.firstname, 581 | lastname = info.lastname, 582 | dob = info.dob, 583 | gender = info.gender, 584 | cash = info.cash, 585 | bank = info.bank, 586 | phonenumber = info.phonenumber, 587 | groups = json.decode(info.groups), 588 | metadata = json.decode(info.metadata), 589 | inventory = json.decode(info.inventory) 590 | }) 591 | end 592 | return characters 593 | end 594 | 595 | ---@param src number 596 | ---@param id number 597 | ---@return table 598 | function NDCore.setActiveCharacter(src, id) 599 | local char = NDCore.players[src] 600 | if not src or char and char.id == id then return end 601 | 602 | local character = NDCore.fetchCharacter(id, src) 603 | if not character then return end 604 | 605 | character.name = GetPlayerName(src) 606 | character.active() 607 | return character 608 | end 609 | --------------------------------------------------------------------------------