├── 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 |
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 |
--------------------------------------------------------------------------------