mirror of
https://github.com/ND-Framework/ND_Core.git
synced 2026-08-17 05:56:01 +01:00
Compare commits
125 Commits
v2.0.0
...
b837771300
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b837771300 | ||
|
|
b7222a1f8f | ||
|
|
ec05cce082 | ||
|
|
7ecd8fa883 | ||
|
|
72a0b8c53f | ||
|
|
df0d0a8160 | ||
|
|
ba2d4951b9 | ||
|
|
8a13170999 | ||
|
|
2ab26fe51d | ||
|
|
6939566cf4 | ||
|
|
075fb087f2 | ||
|
|
3d5c1c32bb | ||
|
|
d3278e9be1 | ||
|
|
24229485d9 | ||
|
|
4f9d274c62 | ||
|
|
73f38c12f6 | ||
|
|
0b39a8c039 | ||
|
|
568185ca19 | ||
|
|
bc41788cc9 | ||
|
|
8a3502e515 | ||
|
|
a431a49d8c | ||
|
|
0bd76ed79f | ||
|
|
3f20d0f918 | ||
|
|
31bf86a826 | ||
|
|
9bd42fb26e | ||
|
|
085bb71dcc | ||
|
|
35d8d887c8 | ||
|
|
544ad64779 | ||
|
|
43f690e356 | ||
|
|
e883c4e64f | ||
|
|
5928d89edf | ||
|
|
9b846dc07e | ||
|
|
baac4c75dd | ||
|
|
fb708cc7e7 | ||
|
|
1547d310eb | ||
|
|
9d4708f6d9 | ||
|
|
a7713804f0 | ||
|
|
a9785ce754 | ||
|
|
fe922e1b98 | ||
|
|
b97f42bb0a | ||
|
|
d132c12025 | ||
|
|
119b0034bf | ||
|
|
622b6fa8b0 | ||
|
|
2e6dfa9e5f | ||
|
|
4c4166de94 | ||
|
|
dea7ac6c8f | ||
|
|
120d924cae | ||
|
|
2a263a6c2e | ||
|
|
4ff4f487a1 | ||
|
|
e26821841b | ||
|
|
e9aafe1f2d | ||
|
|
6987bb63ae | ||
|
|
e0c4a8419b | ||
|
|
66f93bde14 | ||
|
|
2460df2f34 | ||
|
|
b79bb6ec7f | ||
|
|
eef1bb60f5 | ||
|
|
1320c4ab7d | ||
|
|
2c7aa5723a | ||
|
|
97a8b991ae | ||
|
|
2a3ac3b63b | ||
|
|
5d3c051149 | ||
|
|
fc82b217e9 | ||
|
|
284411ce66 | ||
|
|
a7257cf68f | ||
|
|
841a5771c5 | ||
|
|
8fd7878ba6 | ||
|
|
60e86d2038 | ||
|
|
626f0ad06f | ||
|
|
d5c3acbd81 | ||
|
|
98cba87bec | ||
|
|
001fc22ef6 | ||
|
|
87420e99e9 | ||
|
|
c5597161d9 | ||
|
|
898b9aefe9 | ||
|
|
5545650dd5 | ||
|
|
f4f01a5b1c | ||
|
|
3252118f93 | ||
|
|
9d4708a1f8 | ||
|
|
d0d04ae282 | ||
|
|
c005c1b28b | ||
|
|
590044b5d6 | ||
|
|
3146172c0c | ||
|
|
2ce47ef8d1 | ||
|
|
6258651d8b | ||
|
|
eecbc8f690 | ||
|
|
1d25b54e0f | ||
|
|
e18389a52a | ||
|
|
539e522373 | ||
|
|
2bacdd2a20 | ||
|
|
6242a44c0b | ||
|
|
74e4ccc2de | ||
|
|
2fda7655b5 | ||
|
|
ade82436a5 | ||
|
|
f512d0ed0c | ||
|
|
0a19fe8a33 | ||
|
|
2882c7c1ae | ||
|
|
6291690bd4 | ||
|
|
af82c447c1 | ||
|
|
46f4bdbd64 | ||
|
|
4870d639d4 | ||
|
|
ed895b4e6d | ||
|
|
5d2925d0c3 | ||
|
|
0500cef32f | ||
|
|
0e1fb43cd2 | ||
|
|
3260aa01f2 | ||
|
|
71a54be704 | ||
|
|
d939607d64 | ||
|
|
5cb038819c | ||
|
|
9f5bb34bdd | ||
|
|
29006870c3 | ||
|
|
a60e1b9e32 | ||
|
|
7fe7901a13 | ||
|
|
ca8f4369f6 | ||
|
|
e1d4b2fcff | ||
|
|
8b0f2b010d | ||
|
|
24e8874050 | ||
|
|
289d040b5c | ||
|
|
3f0c47c2d0 | ||
|
|
543b23a644 | ||
|
|
4b106d3688 | ||
|
|
db5c54f2d8 | ||
|
|
a1150bb821 | ||
|
|
8f7c52130a | ||
|
|
6e71142970 |
16
README.md
16
README.md
@@ -16,9 +16,13 @@
|
||||
* [oxmysql](https://github.com/overextended/oxmysql/releases)
|
||||
* [ox_lib](https://github.com/overextended/ox_lib/releases)
|
||||
|
||||
## v2 Addons
|
||||
* [Character selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2)
|
||||
* [Banking](https://github.com/ND-Framework/ND_Banking/tree/wip-v2)
|
||||
* [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/tree/wip-v2)
|
||||
* [Dealership](https://github.com/ND-Framework/ND_Dealership/tree/v2)
|
||||
* [Inventory](https://github.com/overextended/ox_inventory/pull/1403)
|
||||
## Addons
|
||||
* [Character selection](https://github.com/ND-Framework/ND_Characters/releases)
|
||||
* [Banking](https://github.com/ND-Framework/ND_Banking/releases)
|
||||
* [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/releases)
|
||||
* [Dealership](https://github.com/ND-Framework/ND_Dealership/releases)
|
||||
* [Inventory](https://github.com/overextended/ox_inventory/releases)
|
||||
* [Ambulance job](https://github.com/ND-Framework/ND_Ambulance/releases)
|
||||
* [Casino](https://github.com/ND-Framework/ND_Casino/releases)
|
||||
* [Blackjack](https://github.com/ND-Framework/ND_Blackjack/releases)
|
||||
* [Nitro](https://github.com/ND-Framework/ND_Nitro/releases)
|
||||
|
||||
@@ -15,7 +15,8 @@ local function PlayerEliminated(deathCause, killerServerId, killerClientId)
|
||||
deathCause = deathCause,
|
||||
killerServerId = killerServerId,
|
||||
killerClientId = killerClientId,
|
||||
damagedBones = usingAmbulance and ambulance:getBodyDamage() or {}
|
||||
damagedBones = usingAmbulance and ambulance:getBodyDamage() or {},
|
||||
timestamp = GetCloudTimeAsInt()
|
||||
}
|
||||
TriggerEvent("ND:playerEliminated", info)
|
||||
TriggerServerEvent("ND:playerEliminated", info)
|
||||
@@ -29,14 +30,13 @@ AddEventHandler("gameEventTriggered", function(name, args)
|
||||
local victim = args[1]
|
||||
if not IsPedAPlayer(victim) or NetworkGetPlayerIndexFromPed(victim) ~= cache.playerId then return end
|
||||
|
||||
if not IsPedDeadOrDying(victim, true) and GetEntityHealth(victim) > 0 then
|
||||
local hit, bone = GetPedLastDamageBone(victim)
|
||||
if hit and usingAmbulance then
|
||||
local damageWeapon = ambulance:getLastDamagingWeapon(victim)
|
||||
return damageWeapon and ambulance:updateBodyDamage(bone, damageWeapon)
|
||||
end
|
||||
return
|
||||
local hit, bone = GetPedLastDamageBone(victim)
|
||||
if hit and usingAmbulance then
|
||||
local damageWeapon = ambulance:getLastDamagingWeapon(victim)
|
||||
ambulance:updateBodyDamage(bone, damageWeapon)
|
||||
end
|
||||
|
||||
if not IsPedDeadOrDying(victim, true) or GetEntityHealth(victim) > 100 then return end
|
||||
|
||||
local killerEntity, deathCause = GetPedSourceOfDeath(cache.ped), GetPedCauseOfDeath(cache.ped)
|
||||
local killerClientId = NetworkGetPlayerIndexFromPed(killerEntity)
|
||||
|
||||
@@ -1,26 +1,85 @@
|
||||
RegisterNetEvent("ND:notifyClient", function(...)
|
||||
NDCore.notify(...)
|
||||
end)
|
||||
|
||||
-- updates the money on the client.
|
||||
RegisterNetEvent("ND:updateMoney", function(cash, bank)
|
||||
if not NDCore.player then return end
|
||||
if source == "" or not NDCore.player then return end
|
||||
NDCore.player.cash = cash
|
||||
NDCore.player.bank = bank
|
||||
end)
|
||||
|
||||
-- Sets main character.
|
||||
RegisterNetEvent("ND:characterLoaded", function(character)
|
||||
if source == "" then return end
|
||||
NDCore.player = character
|
||||
end)
|
||||
|
||||
-- Update main character info.
|
||||
RegisterNetEvent("ND:updateCharacter", function(character)
|
||||
if source == "" or not NDCore.player then return end
|
||||
NDCore.player = character
|
||||
end)
|
||||
|
||||
-- Updates last lcoation.
|
||||
RegisterNetEvent("ND:updateLastLocation", function(location)
|
||||
if not NDCore.player then return end
|
||||
if source == "" or not NDCore.player then return end
|
||||
NDCore.player.lastLocation = location
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND:revivePlayer", function()
|
||||
NDCore.revivePlayer(true)
|
||||
-- Unsets main character.
|
||||
RegisterNetEvent("ND:characterUnloaded", function()
|
||||
if source == "" or not NDCore.player then return end
|
||||
NDCore.player = nil
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND:revivePlayer", function()
|
||||
if source == "" then return end
|
||||
local oldPed = cache.ped
|
||||
local veh = GetVehiclePedIsIn(oldPed)
|
||||
local seat = cache.seat
|
||||
local coords = GetEntityCoords(oldPed)
|
||||
local armor = GetPedArmour(oldPed)
|
||||
|
||||
NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, GetEntityHeading(oldPed), true, true, false)
|
||||
|
||||
local ped = PlayerPedId()
|
||||
if oldPed ~= ped then
|
||||
DeleteEntity(oldPed)
|
||||
ClearAreaOfPeds(coords.x, coords.y, coords.z, 0.2, false)
|
||||
end
|
||||
|
||||
SetEntityInvincible(ped, false)
|
||||
FreezeEntityPosition(ped, false)
|
||||
SetEntityVisible(ped, true)
|
||||
SetEveryoneIgnorePlayer(ped, false)
|
||||
SetPedCanBeTargetted(ped, true)
|
||||
SetEntityCanBeDamaged(ped, true)
|
||||
SetBlockingOfNonTemporaryEvents(ped, false)
|
||||
SetPedCanRagdollFromPlayerImpact(ped, true)
|
||||
ClearPedTasksImmediately(ped)
|
||||
SetPedArmour(ped, armor)
|
||||
|
||||
if veh and veh ~= 0 then
|
||||
SetPedIntoVehicle(ped, veh, seat)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND:clothingMenu", function()
|
||||
if GetResourceState("fivem-appearance") ~= "started" then return end
|
||||
|
||||
local function customize(appearance)
|
||||
if not appearance then return end
|
||||
TriggerServerEvent("ND:updateClothing", appearance)
|
||||
end
|
||||
|
||||
exports["fivem-appearance"]:startPlayerCustomization(customize, {
|
||||
ped = false,
|
||||
headBlend = true,
|
||||
faceFeatures = true,
|
||||
headOverlays = true,
|
||||
components = true,
|
||||
props = true,
|
||||
tattoos = true
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -26,56 +26,15 @@ function NDCore.getPlayersFromCoords(distance, coords)
|
||||
return closePlayers
|
||||
end
|
||||
|
||||
function NDCore.revivePlayer(reset, keepDead)
|
||||
local usingAmbulance = GetResourceState("ND_Ambulance") == "started"
|
||||
if not keepDead then
|
||||
LocalPlayer.state.dead = false
|
||||
if usingAmbulance then
|
||||
local state = Player(cache.serverId).state
|
||||
state:set("isDead", false, true)
|
||||
end
|
||||
end
|
||||
|
||||
local veh = GetVehiclePedIsIn(cache.ped)
|
||||
local seat = cache.seat
|
||||
local coords = GetEntityCoords(cache.ped)
|
||||
NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, GetEntityHeading(cache.ped), true, true, false)
|
||||
|
||||
local ped = PlayerPedId()
|
||||
if cache.ped ~= ped then
|
||||
DeleteEntity(cache.ped)
|
||||
ClearAreaOfPeds(coords.x, coords.y, coords.z, 0.2, false)
|
||||
end
|
||||
|
||||
SetEntityInvincible(ped, false)
|
||||
FreezeEntityPosition(ped, false)
|
||||
SetEntityVisible(ped, true)
|
||||
SetEveryoneIgnorePlayer(ped, false)
|
||||
SetPedCanBeTargetted(ped, true)
|
||||
SetEntityCanBeDamaged(ped, true)
|
||||
SetBlockingOfNonTemporaryEvents(ped, false)
|
||||
SetPedCanRagdollFromPlayerImpact(ped, true)
|
||||
ClearPedTasksImmediately(ped)
|
||||
|
||||
if veh and veh ~= 0 then
|
||||
SetPedIntoVehicle(ped, veh, seat)
|
||||
end
|
||||
if reset and GetPedMovementClipset(ped) == `move_m@injured` then
|
||||
ClearEntityLastDamageEntity(ped)
|
||||
SetPedMoveRateOverride(ped, 1.0)
|
||||
ResetPedMovementClipset(ped, 0)
|
||||
end
|
||||
if reset and usingAmbulance then
|
||||
exports["ND_Ambulance"]:resetBodyDamage()
|
||||
function NDCore.getConfig(info)
|
||||
if not info then
|
||||
return Config
|
||||
end
|
||||
return Config[info]
|
||||
end
|
||||
|
||||
function NDCore.notify(...)
|
||||
if GetResourceState("ModernHUD") == "started" then
|
||||
exports["ModernHUD"]:notify(...)
|
||||
elseif GetResourceState("ox_lib") == "started" then
|
||||
lib.notify(...)
|
||||
end
|
||||
lib.notify(...)
|
||||
end
|
||||
|
||||
for name, func in pairs(NDCore) do
|
||||
|
||||
32
client/groupadmin.lua
Normal file
32
client/groupadmin.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
local isOpen = false
|
||||
|
||||
RegisterNUICallback("hide", function(_, cb)
|
||||
isOpen = false
|
||||
SetNuiFocus(false, false)
|
||||
cb("ok")
|
||||
end)
|
||||
|
||||
RegisterNUICallback("groups:create", function(data, cb)
|
||||
local result = lib.callback.await("ND_Core:groups:create", false, data)
|
||||
cb(result or {})
|
||||
end)
|
||||
|
||||
RegisterNUICallback("groups:edit", function(data, cb)
|
||||
local result = lib.callback.await("ND_Core:groups:edit", false, data)
|
||||
cb(result or {})
|
||||
end)
|
||||
|
||||
RegisterNUICallback("groups:delete", function(data, cb)
|
||||
local result = lib.callback.await("ND_Core:groups:delete", false, data)
|
||||
cb(result or {})
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND:openGroupAdmin", function(groups)
|
||||
if isOpen then return end
|
||||
isOpen = true
|
||||
SetNuiFocus(true, true)
|
||||
SendNUIMessage({
|
||||
type = "groups:open",
|
||||
groups = groups
|
||||
})
|
||||
end)
|
||||
@@ -1,3 +1,4 @@
|
||||
lib.locale()
|
||||
NDCore = {}
|
||||
|
||||
Config = {
|
||||
@@ -7,14 +8,16 @@ Config = {
|
||||
discordAsset = GetConvar("core:discordAsset", "andyyy"),
|
||||
discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"),
|
||||
discordActionText = GetConvar("core:discordActionText", "DISCORD"),
|
||||
discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"),
|
||||
discordActionLink = GetConvar("core:discordActionLink", "https://discord.gg/Z9Mxu72zZ6"),
|
||||
discordActionText2 = GetConvar("core:discordActionText2", "STORE"),
|
||||
discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"),
|
||||
randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30),
|
||||
disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1,
|
||||
randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30),
|
||||
requireKeys = GetConvarInt("core:requireKeys", 1) == 1,
|
||||
useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1,
|
||||
groups = json.decode(GetConvar("core:groups", "[]")),
|
||||
compatibility = json.decode(GetConvar("core:compatibility", "[]"))
|
||||
groups = {},
|
||||
compatibility = json.decode(GetConvar("core:compatibility", "[]")),
|
||||
lockpickTries = GetConvarInt("core:lockpickTries", 3)
|
||||
}
|
||||
|
||||
-- Discord rich presence.
|
||||
@@ -24,11 +27,11 @@ CreateThread(function()
|
||||
SetDiscordRichPresenceAssetSmall(Config.discordAssetSmall)
|
||||
SetDiscordRichPresenceAction(0, Config.discordActionText, Config.discordActionLink)
|
||||
SetDiscordRichPresenceAction(1, Config.discordActionText2, Config.discordActionLink2)
|
||||
local presenceText = ("Playing: %s"):format(Config.serverName)
|
||||
local presenceText = locale("discord_text_server", Config.serverName)
|
||||
while true do
|
||||
if NDCore.player then
|
||||
local presence = ("Playing: %s as %s %s"):format(Config.serverName, NDCore.player.firstname, NDCore.player.lastname)
|
||||
local presenceTextSmall = ("Playing as: %s %s"):format(NDCore.player.firstname, NDCore.player.lastname)
|
||||
local presence = locale("discord_text", Config.serverName, NDCore.player.firstname, NDCore.player.lastname)
|
||||
local presenceTextSmall = locale("discord_text_small", NDCore.player.firstname, NDCore.player.lastname)
|
||||
SetRichPresence(presence)
|
||||
SetDiscordRichPresenceAssetText(presenceText)
|
||||
SetDiscordRichPresenceAssetSmallText(presenceTextSmall)
|
||||
@@ -47,8 +50,8 @@ CreateThread(function()
|
||||
sleep = 0
|
||||
BeginScaleformMovieMethodOnFrontendHeader("SET_HEADING_DETAILS")
|
||||
ScaleformMovieMethodAddParamPlayerNameString(("%s %s"):format(NDCore.player.firstname, NDCore.player.lastname))
|
||||
ScaleformMovieMethodAddParamTextureNameString(("Cash: $%d"):format(NDCore.player.cash))
|
||||
ScaleformMovieMethodAddParamTextureNameString(("Bank: $%d"):format(NDCore.player.bank))
|
||||
ScaleformMovieMethodAddParamTextureNameString(locale("pause_menu_cash", NDCore.player.cash))
|
||||
ScaleformMovieMethodAddParamTextureNameString(locale("pause_menu_bank", NDCore.player.bank))
|
||||
EndScaleformMovieMethod()
|
||||
elseif sleep == 0 then
|
||||
sleep = 500
|
||||
|
||||
@@ -101,7 +101,18 @@ local function updateBlips(playerGroups)
|
||||
end
|
||||
end
|
||||
|
||||
function NDCore.createAiPed(info)
|
||||
local function disableCollisionWithModels(ped, models)
|
||||
if not models then return end
|
||||
local coords = GetEntityCoords(ped)
|
||||
for i=1, #models do
|
||||
local m = models[i]
|
||||
local model = type(m) == "number" and m or GetHashKey(m)
|
||||
local obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, model, false, false, false)
|
||||
SetEntityNoCollisionEntity(ped, obj, false)
|
||||
end
|
||||
end
|
||||
|
||||
function NDCore.createAiPed(info, cb)
|
||||
local ped
|
||||
local model = type(info.model) == "string" and GetHashKey(info.model) or info.model
|
||||
local blipInfo = info.blip
|
||||
@@ -111,7 +122,7 @@ function NDCore.createAiPed(info)
|
||||
local options = info.options
|
||||
local blip = createBlip(coords, blipInfo)
|
||||
local point = lib.points.new({
|
||||
coords = vec3(coords.x, coords.y, coords.z),
|
||||
coords = coords.xyz,
|
||||
distance = info.distance or 25.0
|
||||
})
|
||||
|
||||
@@ -123,6 +134,10 @@ function NDCore.createAiPed(info)
|
||||
resource = info.resource or GetInvokingResource()
|
||||
}
|
||||
|
||||
if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) and #(GetEntityCoords(cache.ped)-coords.xyz) > point.distance then
|
||||
SetBlipAlpha(blip, 0)
|
||||
end
|
||||
|
||||
function point:onEnter()
|
||||
local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true)
|
||||
lib.requestModel(model)
|
||||
@@ -133,6 +148,11 @@ function NDCore.createAiPed(info)
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
if cb then
|
||||
cb(ped)
|
||||
end
|
||||
|
||||
disableCollisionWithModels(ped, info.disableCollisionWithModels)
|
||||
configPed(ped)
|
||||
setClothing(ped, clothing)
|
||||
locations[id].ped = ped
|
||||
@@ -218,10 +238,10 @@ end)
|
||||
RegisterCommand("getclothing", function(source, args, rawCommand)
|
||||
local info = ""
|
||||
for k, v in pairs(clothingComponents) do
|
||||
info = ("%s\n%s = {\n drawable = %s,\n texture = %s\n},"):format(info, k, GetPedDrawableVariation(cache.ped, v), GetPedTextureVariation(cache.ped, v))
|
||||
info = ("%s\n%s = { drawable = %s, texture = %s },"):format(info, k, GetPedDrawableVariation(cache.ped, v), GetPedTextureVariation(cache.ped, v))
|
||||
end
|
||||
for k, v in pairs(clothingProps) do
|
||||
info = ("%s\n%s = {\n drawable = %s,\n texture = %s\n},"):format(info, k, GetPedPropIndex(cache.ped, v), GetPedPropTextureIndex(cache.ped, v))
|
||||
info = ("%s\n%s = { drawable = %s, texture = %s },"):format(info, k, GetPedPropIndex(cache.ped, v), GetPedPropTextureIndex(cache.ped, v))
|
||||
end
|
||||
lib.setClipboard(info)
|
||||
end, false)
|
||||
|
||||
@@ -1,188 +1,188 @@
|
||||
local vehicleColorNames = {
|
||||
[0] = "Black",
|
||||
[1] = "Black",
|
||||
[2] = "Black",
|
||||
[3] = "Silver",
|
||||
[4] = "Silver",
|
||||
[5] = "Silver",
|
||||
[6] = "Gray",
|
||||
[7] = "Silver",
|
||||
[8] = "Silver",
|
||||
[9] = "Silver",
|
||||
[10] = "Metal",
|
||||
[11] = "Grey",
|
||||
[12] = "Black",
|
||||
[13] = "Gray",
|
||||
[14] = "Grey",
|
||||
[15] = "Black",
|
||||
[16] = "Black",
|
||||
[17] = "Silver",
|
||||
[18] = "Silver",
|
||||
[19] = "Metal",
|
||||
[20] = "Silver",
|
||||
[21] = "Black",
|
||||
[22] = "Graphite",
|
||||
[23] = "Silver",
|
||||
[24] = "Silver",
|
||||
[25] = "Silver",
|
||||
[26] = "Silver",
|
||||
[27] = "Red",
|
||||
[28] = "Red",
|
||||
[29] = "Red",
|
||||
[30] = "Red",
|
||||
[31] = "Red",
|
||||
[32] = "Red",
|
||||
[33] = "Red",
|
||||
[34] = "Red",
|
||||
[35] = "Red",
|
||||
[36] = "Orange",
|
||||
[37] = "Gold",
|
||||
[38] = "Orange",
|
||||
[39] = "Red",
|
||||
[40] = "Red",
|
||||
[41] = "Orange",
|
||||
[42] = "Yellow",
|
||||
[43] = "Red",
|
||||
[44] = "Red",
|
||||
[45] = "Red",
|
||||
[46] = "Red",
|
||||
[47] = "Red",
|
||||
[48] = "Red",
|
||||
[49] = "Green",
|
||||
[50] = "Green",
|
||||
[51] = "Green",
|
||||
[52] = "Green",
|
||||
[53] = "Green",
|
||||
[54] = "Green",
|
||||
[55] = "Green",
|
||||
[56] = "Green",
|
||||
[57] = "Green",
|
||||
[58] = "Green",
|
||||
[59] = "Green",
|
||||
[60] = "Green",
|
||||
[61] = "Blue",
|
||||
[62] = "Blue",
|
||||
[63] = "Blue",
|
||||
[64] = "Blue",
|
||||
[65] = "Blue",
|
||||
[66] = "Blue",
|
||||
[67] = "Blue",
|
||||
[68] = "Blue",
|
||||
[69] = "Blue",
|
||||
[70] = "Blue",
|
||||
[71] = "Blue",
|
||||
[72] = "Blue",
|
||||
[73] = "Blue",
|
||||
[74] = "Blue",
|
||||
[75] = "Blue",
|
||||
[76] = "Blue",
|
||||
[77] = "Blue",
|
||||
[78] = "Blue",
|
||||
[79] = "Bblue",
|
||||
[80] = "Blue",
|
||||
[81] = "Blue",
|
||||
[82] = "Blue",
|
||||
[83] = "Blue",
|
||||
[84] = "Blue",
|
||||
[85] = "Blue",
|
||||
[86] = "Blue",
|
||||
[87] = "Blue",
|
||||
[88] = "Yellow",
|
||||
[89] = "Yellow",
|
||||
[90] = "Bronze",
|
||||
[91] = "Yellow",
|
||||
[92] = "Lime",
|
||||
[93] = "Champagne",
|
||||
[94] = "Beige",
|
||||
[95] = "Ivory",
|
||||
[96] = "Brown",
|
||||
[97] = "Brown",
|
||||
[98] = "Brown",
|
||||
[99] = "Beige",
|
||||
[100] = "Brown",
|
||||
[101] = "Brown",
|
||||
[102] = "Beechwood",
|
||||
[103] = "Beechwood",
|
||||
[104] = "Orange",
|
||||
[105] = "Sand",
|
||||
[106] = "Sand",
|
||||
[107] = "Cream",
|
||||
[108] = "Brown",
|
||||
[109] = "Brown",
|
||||
[110] = "Brown",
|
||||
[111] = "White",
|
||||
[112] = "White",
|
||||
[113] = "Beige",
|
||||
[114] = "Brown",
|
||||
[115] = "Brown",
|
||||
[116] = "Beige",
|
||||
[117] = "Steel",
|
||||
[118] = "Steel",
|
||||
[119] = "Aluminium",
|
||||
[120] = "Chrome",
|
||||
[121] = "White",
|
||||
[122] = "White",
|
||||
[123] = "Orange",
|
||||
[124] = "Orange",
|
||||
[125] = "Green",
|
||||
[126] = "Yellow",
|
||||
[127] = "Blue",
|
||||
[128] = "Green",
|
||||
[129] = "Brown",
|
||||
[130] = "Orange",
|
||||
[131] = "White",
|
||||
[132] = "White",
|
||||
[133] = "Green",
|
||||
[134] = "White",
|
||||
[135] = "Pink",
|
||||
[136] = "pink",
|
||||
[137] = "Pink",
|
||||
[138] = "Orange",
|
||||
[139] = "Green",
|
||||
[140] = "Blue",
|
||||
[141] = "Black",
|
||||
[142] = "Black",
|
||||
[143] = "Black",
|
||||
[144] = "Green",
|
||||
[145] = "Purple",
|
||||
[146] = "Blue",
|
||||
[147] = "Black",
|
||||
[148] = "Purple",
|
||||
[149] = "Purple",
|
||||
[150] = "Red",
|
||||
[151] = "Green",
|
||||
[152] = "Green",
|
||||
[153] = "Brown",
|
||||
[154] = "Tan",
|
||||
[155] = "Green",
|
||||
[156] = "ALLOY",
|
||||
[157] = "Blue",
|
||||
[0] = locale("veh_color_black"),
|
||||
[1] = locale("veh_color_black"),
|
||||
[2] = locale("veh_color_black"),
|
||||
[3] = locale("veh_color_silver"),
|
||||
[4] = locale("veh_color_silver"),
|
||||
[5] = locale("veh_color_silver"),
|
||||
[6] = locale("veh_color_grey"),
|
||||
[7] = locale("veh_color_silver"),
|
||||
[8] = locale("veh_color_silver"),
|
||||
[9] = locale("veh_color_silver"),
|
||||
[10] = locale("veh_color_metal"),
|
||||
[11] = locale("veh_color_grey"),
|
||||
[12] = locale("veh_color_black"),
|
||||
[13] = locale("veh_color_grey"),
|
||||
[14] = locale("veh_color_grey"),
|
||||
[15] = locale("veh_color_black"),
|
||||
[16] = locale("veh_color_black"),
|
||||
[17] = locale("veh_color_silver"),
|
||||
[18] = locale("veh_color_silver"),
|
||||
[19] = locale("veh_color_metal"),
|
||||
[20] = locale("veh_color_silver"),
|
||||
[21] = locale("veh_color_black"),
|
||||
[22] = locale("veh_color_graphite"),
|
||||
[23] = locale("veh_color_silver"),
|
||||
[24] = locale("veh_color_silver"),
|
||||
[25] = locale("veh_color_silver"),
|
||||
[26] = locale("veh_color_silver"),
|
||||
[27] = locale("veh_color_red"),
|
||||
[28] = locale("veh_color_red"),
|
||||
[29] = locale("veh_color_red"),
|
||||
[30] = locale("veh_color_red"),
|
||||
[31] = locale("veh_color_red"),
|
||||
[32] = locale("veh_color_red"),
|
||||
[33] = locale("veh_color_red"),
|
||||
[34] = locale("veh_color_red"),
|
||||
[35] = locale("veh_color_red"),
|
||||
[36] = locale("veh_color_orange"),
|
||||
[37] = locale("veh_color_gold"),
|
||||
[38] = locale("veh_color_orange"),
|
||||
[39] = locale("veh_color_red"),
|
||||
[40] = locale("veh_color_red"),
|
||||
[41] = locale("veh_color_orange"),
|
||||
[42] = locale("veh_color_yellow"),
|
||||
[43] = locale("veh_color_red"),
|
||||
[44] = locale("veh_color_red"),
|
||||
[45] = locale("veh_color_red"),
|
||||
[46] = locale("veh_color_red"),
|
||||
[47] = locale("veh_color_red"),
|
||||
[48] = locale("veh_color_red"),
|
||||
[49] = locale("veh_color_green"),
|
||||
[50] = locale("veh_color_green"),
|
||||
[51] = locale("veh_color_green"),
|
||||
[52] = locale("veh_color_green"),
|
||||
[53] = locale("veh_color_green"),
|
||||
[54] = locale("veh_color_green"),
|
||||
[55] = locale("veh_color_green"),
|
||||
[56] = locale("veh_color_green"),
|
||||
[57] = locale("veh_color_green"),
|
||||
[58] = locale("veh_color_green"),
|
||||
[59] = locale("veh_color_green"),
|
||||
[60] = locale("veh_color_green"),
|
||||
[61] = locale("veh_color_blue"),
|
||||
[62] = locale("veh_color_blue"),
|
||||
[63] = locale("veh_color_blue"),
|
||||
[64] = locale("veh_color_blue"),
|
||||
[65] = locale("veh_color_blue"),
|
||||
[66] = locale("veh_color_blue"),
|
||||
[67] = locale("veh_color_blue"),
|
||||
[68] = locale("veh_color_blue"),
|
||||
[69] = locale("veh_color_blue"),
|
||||
[70] = locale("veh_color_blue"),
|
||||
[71] = locale("veh_color_blue"),
|
||||
[72] = locale("veh_color_blue"),
|
||||
[73] = locale("veh_color_blue"),
|
||||
[74] = locale("veh_color_blue"),
|
||||
[75] = locale("veh_color_blue"),
|
||||
[76] = locale("veh_color_blue"),
|
||||
[77] = locale("veh_color_blue"),
|
||||
[78] = locale("veh_color_blue"),
|
||||
[79] = locale("veh_color_bblue"),
|
||||
[80] = locale("veh_color_blue"),
|
||||
[81] = locale("veh_color_blue"),
|
||||
[82] = locale("veh_color_blue"),
|
||||
[83] = locale("veh_color_blue"),
|
||||
[84] = locale("veh_color_blue"),
|
||||
[85] = locale("veh_color_blue"),
|
||||
[86] = locale("veh_color_blue"),
|
||||
[87] = locale("veh_color_blue"),
|
||||
[88] = locale("veh_color_yellow"),
|
||||
[89] = locale("veh_color_yellow"),
|
||||
[90] = locale("veh_color_bronze"),
|
||||
[91] = locale("veh_color_yellow"),
|
||||
[92] = locale("veh_color_lime"),
|
||||
[93] = locale("veh_color_champagne"),
|
||||
[94] = locale("veh_color_beige"),
|
||||
[95] = locale("veh_color_ivory"),
|
||||
[96] = locale("veh_color_brown"),
|
||||
[97] = locale("veh_color_brown"),
|
||||
[98] = locale("veh_color_brown"),
|
||||
[99] = locale("veh_color_beige"),
|
||||
[100] = locale("veh_color_brown"),
|
||||
[101] = locale("veh_color_brown"),
|
||||
[102] = locale("veh_color_beechwood"),
|
||||
[103] = locale("veh_color_beechwood"),
|
||||
[104] = locale("veh_color_orange"),
|
||||
[105] = locale("veh_color_sand"),
|
||||
[106] = locale("veh_color_sand"),
|
||||
[107] = locale("veh_color_cream"),
|
||||
[108] = locale("veh_color_brown"),
|
||||
[109] = locale("veh_color_brown"),
|
||||
[110] = locale("veh_color_brown"),
|
||||
[111] = locale("veh_color_white"),
|
||||
[112] = locale("veh_color_white"),
|
||||
[113] = locale("veh_color_beige"),
|
||||
[114] = locale("veh_color_brown"),
|
||||
[115] = locale("veh_color_brown"),
|
||||
[116] = locale("veh_color_beige"),
|
||||
[117] = locale("veh_color_steel"),
|
||||
[118] = locale("veh_color_steel"),
|
||||
[119] = locale("veh_color_aluminium"),
|
||||
[120] = locale("veh_color_chrome"),
|
||||
[121] = locale("veh_color_white"),
|
||||
[122] = locale("veh_color_white"),
|
||||
[123] = locale("veh_color_orange"),
|
||||
[124] = locale("veh_color_orange"),
|
||||
[125] = locale("veh_color_green"),
|
||||
[126] = locale("veh_color_yellow"),
|
||||
[127] = locale("veh_color_blue"),
|
||||
[128] = locale("veh_color_green"),
|
||||
[129] = locale("veh_color_brown"),
|
||||
[130] = locale("veh_color_orange"),
|
||||
[131] = locale("veh_color_white"),
|
||||
[132] = locale("veh_color_white"),
|
||||
[133] = locale("veh_color_green"),
|
||||
[134] = locale("veh_color_white"),
|
||||
[135] = locale("veh_color_pink"),
|
||||
[136] = locale("veh_color_pink"),
|
||||
[137] = locale("veh_color_pink"),
|
||||
[138] = locale("veh_color_orange"),
|
||||
[139] = locale("veh_color_green"),
|
||||
[140] = locale("veh_color_blue"),
|
||||
[141] = locale("veh_color_black"),
|
||||
[142] = locale("veh_color_black"),
|
||||
[143] = locale("veh_color_black"),
|
||||
[144] = locale("veh_color_green"),
|
||||
[145] = locale("veh_color_purple"),
|
||||
[146] = locale("veh_color_blue"),
|
||||
[147] = locale("veh_color_black"),
|
||||
[148] = locale("veh_color_purple"),
|
||||
[149] = locale("veh_color_purple"),
|
||||
[150] = locale("veh_color_red"),
|
||||
[151] = locale("veh_color_green"),
|
||||
[152] = locale("veh_color_green"),
|
||||
[153] = locale("veh_color_brown"),
|
||||
[154] = locale("veh_color_tan"),
|
||||
[155] = locale("veh_color_green"),
|
||||
[156] = locale("veh_color_alloy"),
|
||||
[157] = locale("veh_color_blue"),
|
||||
}
|
||||
|
||||
local vehicleClassNames = {
|
||||
[0] = "Compact",
|
||||
[1] = "Sedan",
|
||||
[2] = "SUV",
|
||||
[3] = "Coupe",
|
||||
[4] = "Muscle",
|
||||
[5] = "Sports Classic",
|
||||
[6] = "Sport",
|
||||
[7] = "Super",
|
||||
[8] = "Motorcycle",
|
||||
[9] = "Off-road",
|
||||
[10] = "Industrial",
|
||||
[11] = "Utility",
|
||||
[12] = "Van",
|
||||
[13] = "Cycle",
|
||||
[14] = "Boat",
|
||||
[15] = "Helicopter",
|
||||
[16] = "Plane",
|
||||
[17] = "Service",
|
||||
[18] = "Emergency",
|
||||
[19] = "Military",
|
||||
[20] = "Commercial",
|
||||
[21] = "Train",
|
||||
[22] = "Open wheel"
|
||||
[0] = locale("veh_class_compact"),
|
||||
[1] = locale("veh_class_sedan"),
|
||||
[2] = locale("veh_class_suv"),
|
||||
[3] = locale("veh_class_coupe"),
|
||||
[4] = locale("veh_class_muscle"),
|
||||
[5] = locale("veh_class_sports_classic"),
|
||||
[6] = locale("veh_class_sport"),
|
||||
[7] = locale("veh_class_super"),
|
||||
[8] = locale("veh_class_motorcycle"),
|
||||
[9] = locale("veh_class_off_road"),
|
||||
[10] = locale("veh_class_industrial"),
|
||||
[11] = locale("veh_class_utility"),
|
||||
[12] = locale("veh_class_van"),
|
||||
[13] = locale("veh_class_cycle"),
|
||||
[14] = locale("veh_class_boat"),
|
||||
[15] = locale("veh_class_helicopter"),
|
||||
[16] = locale("veh_class_plane"),
|
||||
[17] = locale("veh_class_service"),
|
||||
[18] = locale("veh_class_emergency"),
|
||||
[19] = locale("veh_class_military"),
|
||||
[20] = locale("veh_class_commercial"),
|
||||
[21] = locale("veh_class_train"),
|
||||
[22] = locale("veh_class_open_wheel")
|
||||
}
|
||||
|
||||
local cruiseSpeedSet = 0
|
||||
@@ -242,30 +242,36 @@ local function getVehicleBlipSprite(entity)
|
||||
end
|
||||
|
||||
local function getVehFromNetId(netId)
|
||||
local time = GetCloudTimeAsInt()
|
||||
while not NetworkDoesNetworkIdExist(netId) or not NetworkDoesEntityExistWithNetworkId(netId) and time-GetCloudTimeAsInt() < 5 do
|
||||
local startTime = GetCloudTimeAsInt()
|
||||
while (not NetworkDoesNetworkIdExist(netId) or not NetworkDoesEntityExistWithNetworkId(netId)) and (GetCloudTimeAsInt() - startTime < 5) do
|
||||
Wait(100)
|
||||
end
|
||||
return NetToVeh(netId)
|
||||
end
|
||||
|
||||
RegisterNetEvent("ND_Vehicles:blip", function(netId, status)
|
||||
local veh = getVehFromNetId(netId)
|
||||
if not veh then return end
|
||||
if not status then
|
||||
local blip = GetBlipFromEntity(veh)
|
||||
if not blip or not DoesBlipExist(blip) then return end
|
||||
return RemoveBlip(blip)
|
||||
|
||||
local function setVehicleBlip(veh, status, groupVehicle)
|
||||
local currentBlip = GetBlipFromEntity(veh)
|
||||
if currentBlip and DoesBlipExist(currentBlip) then
|
||||
RemoveBlip(currentBlip)
|
||||
end
|
||||
|
||||
if not status then return end
|
||||
|
||||
local blip = AddBlipForEntity(veh)
|
||||
SetBlipSprite(blip, getVehicleBlipSprite(veh))
|
||||
SetBlipColour(blip, 0)
|
||||
SetBlipScale(blip, 0.8)
|
||||
SetBlipAsShortRange(blip, true)
|
||||
BeginTextCommandSetBlipName("STRING")
|
||||
AddTextComponentSubstringPlayerName("Personal vehicle")
|
||||
AddTextComponentSubstringPlayerName(groupVehicle and locale("group_vehicle") or locale("personal_vehicle"))
|
||||
EndTextCommandSetBlipName(blip)
|
||||
end
|
||||
|
||||
RegisterNetEvent("ND_Vehicles:blip", function(netId, status, groupVehicle)
|
||||
local veh = getVehFromNetId(netId)
|
||||
if not veh then return end
|
||||
setVehicleBlip(veh, status, groupVehicle)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND_Vehicles:syncAlarm", function(netId)
|
||||
@@ -284,15 +290,42 @@ RegisterNetEvent("ND_VehicleSystem:setOwnedIfNot", function(netId)
|
||||
end)
|
||||
|
||||
AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, replicated)
|
||||
local entity = GetEntityFromStateBagName(bagName)
|
||||
if not value or not DoesEntityExist(entity) or NetworkGetEntityOwner(entity) ~= cache.playerId then return end
|
||||
if not value then return end
|
||||
|
||||
local props = value
|
||||
if type(value) == "string" then
|
||||
props = json.decode(value)
|
||||
end
|
||||
|
||||
if not props or type(props) ~= "table" then return end
|
||||
|
||||
local entity = GetEntityFromStateBagName(bagName)
|
||||
if not DoesEntityExist(entity) or NetworkGetEntityOwner(entity) ~= cache.playerId then return end
|
||||
|
||||
lib.setVehicleProperties(entity, props)
|
||||
TriggerServerEvent("ND_Vehicles:propsApplied", DoesEntityExist(entity) and NetworkGetNetworkIdFromEntity(entity))
|
||||
end)
|
||||
|
||||
local function hasBlipGroup(player, groups)
|
||||
groups = groups or {}
|
||||
for i=1, #groups do
|
||||
local group = groups[i]
|
||||
if player.groups[group] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
AddStateBagChangeHandler("blipGroups", nil, function(bagName, key, value, reserved, replicated)
|
||||
local entity = GetEntityFromStateBagName(bagName)
|
||||
if not value or not DoesEntityExist(entity) then return end
|
||||
|
||||
local player = NDCore.getPlayer()
|
||||
if not player then return end
|
||||
setVehicleBlip(entity, hasBlipGroup(player, value), true)
|
||||
end)
|
||||
|
||||
local playingKey = 0
|
||||
local function playKeyFob(veh)
|
||||
local keyFob
|
||||
local ped = cache.ped
|
||||
@@ -300,12 +333,21 @@ local function playKeyFob(veh)
|
||||
if #(coords-GetEntityCoords(veh)) > 25.0 then return end
|
||||
|
||||
if not playerVehicle then
|
||||
playingKey += 1
|
||||
SetPedCurrentWeaponVisible(ped, false, false)
|
||||
ClearPedTasks(ped)
|
||||
lib.requestAnimDict("anim@mp_player_intmenu@key_fob@")
|
||||
TaskPlayAnim(ped, "anim@mp_player_intmenu@key_fob@", "fob_click_fp", 8.0, 8.0, -1, 48, 1, false, false, false)
|
||||
keyFob = CreateObject(`lr_prop_carkey_fob`, 0, 0, 0, true, true, true)
|
||||
AttachEntityToEntity(keyFob, ped, GetPedBoneIndex(ped, 0xDEAD), 0.12, 0.04, -0.025, -100.0, 100.0, 0.0, true, true, false, true, 1, true)
|
||||
Wait(700)
|
||||
SetTimeout(400, function()
|
||||
playingKey -= 1
|
||||
end)
|
||||
SetTimeout(1200, function()
|
||||
if playingKey > 0 then return end
|
||||
SetPedCurrentWeaponVisible(ped, true, false)
|
||||
end)
|
||||
end
|
||||
|
||||
PlaySoundFromEntity(-1, "Remote_Control_Fob", ped, "PI_Menu_Sounds", true, 0)
|
||||
@@ -346,8 +388,9 @@ AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved,
|
||||
end)
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Vehicles:getProps", function(netId)
|
||||
local veh = getVehFromNetId(netId)
|
||||
local function getProps(veh)
|
||||
if not veh or not DoesEntityExist(veh) then return end
|
||||
|
||||
local props = lib.getVehicleProperties(veh)
|
||||
local colorPrimary, colorSecondary = GetVehicleColours(veh)
|
||||
if not props then return end
|
||||
@@ -358,7 +401,18 @@ lib.callback.register("ND_Vehicles:getProps", function(netId)
|
||||
props.className = vehicleClassNames[GetVehicleClass(veh)]
|
||||
props.makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model))
|
||||
props.modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model))
|
||||
|
||||
return props
|
||||
end
|
||||
|
||||
lib.callback.register("ND_Vehicles:getProps", function(netId)
|
||||
local veh = getVehFromNetId(netId)
|
||||
return getProps(veh)
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Vehicles:getPropsFromCurrentVeh", function()
|
||||
local veh = GetVehiclePedIsIn(cache.ped)
|
||||
return getProps(veh)
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model)
|
||||
@@ -389,6 +443,17 @@ local function hasVehicleKeys(veh, checkEngine)
|
||||
return hasKey or checkEngine and state.hotwired
|
||||
end
|
||||
|
||||
AddStateBagChangeHandler("owner", nil, function(bagName, key, value, reserved, replicated)
|
||||
local entity = GetEntityFromStateBagName(bagName)
|
||||
if not value or not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end
|
||||
|
||||
local player = NDCore.getPlayer()
|
||||
if not player or player.id ~= value then return end
|
||||
|
||||
local hasKey = hasVehicleKeys(entity)
|
||||
setVehicleBlip(entity, hasKey)
|
||||
end)
|
||||
|
||||
local function hasVehicleKeysCheck(veh)
|
||||
local time = GetCloudTimeAsInt()
|
||||
if time-keyCheckTime.lastCheck < 5 then
|
||||
@@ -425,7 +490,7 @@ end
|
||||
|
||||
local vehicleLockKeybind = lib.addKeybind({
|
||||
name = "vehicleKey",
|
||||
description = "Unlock/lock vehicle (double click)",
|
||||
description = locale("keybind_carkey"),
|
||||
defaultKey = "E",
|
||||
onPressed = function(self)
|
||||
local time = GetCloudTimeAsInt()
|
||||
@@ -447,8 +512,8 @@ NDCore.isResourceStarted("ox_inventory", function(started)
|
||||
Wait(1000)
|
||||
vehicleLockKeybind:disable(true)
|
||||
exports.ox_inventory:displayMetadata({
|
||||
vehPlate = "Plate",
|
||||
vehModel = "Model"
|
||||
vehPlate = locale("veh_plate"),
|
||||
vehModel = locale("veh_model")
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -470,27 +535,67 @@ CreateThread(function()
|
||||
local wait = 500
|
||||
while true do
|
||||
Wait(wait)
|
||||
|
||||
local reset = true
|
||||
playerVehicle = cache.seat == -1 and cache.vehicle
|
||||
if playerVehicle then
|
||||
if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(playerVehicle)] and (IsEntityInAir(playerVehicle) or IsEntityUpsidedown(playerVehicle)) then
|
||||
wait = 0
|
||||
DisableControlAction(0, 59) -- disable vehicle air control.
|
||||
DisableControlAction(0, 60)
|
||||
elseif not GetIsVehicleEngineRunning(playerVehicle) and not hasVehicleKeysCheck(playerVehicle) then
|
||||
wait = 0
|
||||
DisableControlAction(0, 59)
|
||||
if DoesEntityExist(playerVehicle) and IsVehicleEngineStarting(playerVehicle) then
|
||||
SetVehicleEngineOn(playerVehicle, false, true, true) -- don't turn on engine if no keys.
|
||||
end
|
||||
else
|
||||
wait = 500
|
||||
|
||||
local entering = GetVehiclePedIsEntering(cache.ped)
|
||||
if entering and DoesEntityExist(entering) and IsVehicleNeedsToBeHotwired(entering) then
|
||||
SetVehicleNeedsToBeHotwired(entering, false)
|
||||
end
|
||||
|
||||
if not playerVehicle then goto skip end
|
||||
|
||||
if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(playerVehicle)] and (IsEntityInAir(playerVehicle) or IsEntityUpsidedown(playerVehicle)) then
|
||||
wait = 0
|
||||
reset = false
|
||||
DisableControlAction(0, 59) -- disable vehicle air control.
|
||||
DisableControlAction(0, 60)
|
||||
end
|
||||
if Config.requireKeys and not GetIsVehicleEngineRunning(playerVehicle) and not hasVehicleKeysCheck(playerVehicle) then
|
||||
wait = 0
|
||||
reset = false
|
||||
DisableControlAction(0, 59)
|
||||
DisableControlAction(0, 71)
|
||||
if DoesEntityExist(playerVehicle) and IsVehicleEngineStarting(playerVehicle) then
|
||||
SetVehicleEngineOn(playerVehicle, false, true, true) -- don't turn on engine if no keys.
|
||||
end
|
||||
elseif wait ~= 500 then
|
||||
end
|
||||
|
||||
::skip::
|
||||
if (reset or not playerVehicle) and wait ~= 500 then
|
||||
wait = 500
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(100)
|
||||
local veh = cache.vehicle
|
||||
if veh then goto skip end
|
||||
|
||||
local ped = cache.ped
|
||||
local vehEntering = GetVehiclePedIsEntering(ped)
|
||||
if not DoesEntityExist(vehEntering) or NetworkGetEntityOwner(vehEntering) == cache.serverId then goto skip end
|
||||
|
||||
local state = Entity(vehEntering).state
|
||||
if state.owner or state.locked ~= nil then goto skip end
|
||||
|
||||
local driver = GetPedInVehicleSeat(vehEntering, -1)
|
||||
if DoesEntityExist(driver) and IsPedAPlayer(driver) then
|
||||
state.locked = false
|
||||
state.hotwired = true
|
||||
goto skip
|
||||
end
|
||||
|
||||
if math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end
|
||||
state.locked = true
|
||||
|
||||
::skip::
|
||||
end
|
||||
end)
|
||||
|
||||
local function hotwireVehicle()
|
||||
local state = playerVehicle and Entity(playerVehicle).state
|
||||
if not playerVehicle or state.hotwired then return end
|
||||
@@ -517,7 +622,7 @@ local function hotwireVehicle()
|
||||
|
||||
local success = lib.progressCircle({
|
||||
duration = math.random(10000, 20000),
|
||||
label = "Hotwiring",
|
||||
label = locale("progress_hotwiring"),
|
||||
useWhileDead = false,
|
||||
allowRagdoll = false,
|
||||
allowCuffed = false,
|
||||
@@ -562,34 +667,51 @@ local function lockpickVehicle()
|
||||
local veh = lib.getClosestVehicle(pos, 2.5, false)
|
||||
if not veh then return end
|
||||
|
||||
local dificulties = {
|
||||
"easy",
|
||||
"medium",
|
||||
"hard"
|
||||
}
|
||||
local dificultyTime = {
|
||||
easy = 500,
|
||||
medium = 800,
|
||||
hard = 1000
|
||||
}
|
||||
|
||||
lib.requestAnimDict("veh@break_in@0h@p_m_one@")
|
||||
for i=1, 7 do
|
||||
TaskPlayAnimAdvanced(cache.ped, "veh@break_in@0h@p_m_one@", "std_force_entry_ds", pos.x, pos.y, pos.z+0.025, rot.x, rot.y, rot.z, 8.0, 8.0, 1800, 28, 0.1)
|
||||
local dificulty = dificulties[math.random(1, #dificulties)]
|
||||
local success = lib.skillCheck(dificulty)
|
||||
local lockPickResource = GetResourceState("lockpick") == "started" and "lockpick" or GetResourceState("t3_lockpick") == "started" and "t3_lockpick"
|
||||
if lockPickResource then
|
||||
local success = exports[lockPickResource]:startLockpick("lockpick_vehicle", 4, Config.lockpickTries)
|
||||
if not success or not DoesEntityExist(veh) or #(pos-GetEntityCoords(veh)) > 2.5 then
|
||||
TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), false)
|
||||
if success == nil then -- nil would mean the minigame was cancelled
|
||||
return false, false
|
||||
end
|
||||
return false, true
|
||||
end
|
||||
Wait(dificultyTime[dificulty])
|
||||
else
|
||||
local dificulties = {
|
||||
"easy",
|
||||
"medium",
|
||||
"hard"
|
||||
}
|
||||
local dificultyTime = {
|
||||
easy = 500,
|
||||
medium = 800,
|
||||
hard = 1000
|
||||
}
|
||||
|
||||
lib.requestAnimDict("veh@break_in@0h@p_m_one@")
|
||||
for i=1, Config.lockpickTries do
|
||||
TaskPlayAnimAdvanced(cache.ped, "veh@break_in@0h@p_m_one@", "std_force_entry_ds", pos.x, pos.y, pos.z+0.025, rot.x, rot.y, rot.z, 8.0, 8.0, 1800, 28, 0.1)
|
||||
local dificulty = dificulties[math.random(1, #dificulties)]
|
||||
local success = lib.skillCheck(dificulty)
|
||||
if not success or not DoesEntityExist(veh) or #(pos-GetEntityCoords(veh)) > 2.5 then
|
||||
TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), false)
|
||||
return false, true
|
||||
end
|
||||
Wait(dificultyTime[dificulty])
|
||||
end
|
||||
end
|
||||
|
||||
veh = lib.getClosestVehicle(pos, 2.5, false)
|
||||
if not veh then return false, true end
|
||||
TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), true)
|
||||
PlaySoundFromEntity(-1, "Remote_Control_Fob", cache.ped, "PI_Menu_Sounds", true, 0)
|
||||
return true, true
|
||||
|
||||
if lockPickResource then
|
||||
return true, false
|
||||
else
|
||||
return true, true
|
||||
end
|
||||
end
|
||||
|
||||
exports("lockpick", function(data, slot)
|
||||
@@ -612,8 +734,8 @@ exports("keyControl", function(action, slot)
|
||||
if data.slot == slot then
|
||||
if metadata and not metadata.keyEnabled then
|
||||
return lib.notify({
|
||||
title = "No signal",
|
||||
description = "Vehicle key disabled.",
|
||||
title = locale("no_signal"),
|
||||
description = locale("veh_key_disabled"),
|
||||
type = "error",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -635,8 +757,8 @@ exports("keyControl", function(action, slot)
|
||||
elseif action == "disable" then
|
||||
TriggerServerEvent("ND_Vehicles:disableKey", slot)
|
||||
lib.notify({
|
||||
title = "Key disabled",
|
||||
description = "This vehicle key has been disabled and cannot be used anymore.",
|
||||
title = locale("veh_key_disabled"),
|
||||
description = locale("veh_key_disabled2"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -648,8 +770,8 @@ local function cruiseControl()
|
||||
cruiseSpeedVehicle = GetEntitySpeed(playerVehicle) * 2.236936
|
||||
if not playerVehicle then
|
||||
lib.notify({
|
||||
title = "Cruise control",
|
||||
description = "Vehicle cruise control disabled.",
|
||||
title = locale("cruise_control"),
|
||||
description = locale("cruise_control_disabled"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -658,8 +780,8 @@ local function cruiseControl()
|
||||
end
|
||||
if cruiseSpeedVehicle < cruiseSpeedSet/3 then
|
||||
lib.notify({
|
||||
title = "Cruise control",
|
||||
description = "Vehicle cruise control disabled.",
|
||||
title = locale("cruise_control"),
|
||||
description = locale("cruise_control_disabled"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -674,14 +796,14 @@ end
|
||||
|
||||
lib.addKeybind({
|
||||
name = "vehicleCruiseControl",
|
||||
description = "Toggle vehicle cruise control",
|
||||
description = locale("cruise_control_toggle"),
|
||||
defaultKey = "",
|
||||
onPressed = function(self)
|
||||
if cruiseControlEnabled and cruiseSpeedVehicle-1 > cruiseSpeedSet then
|
||||
cruiseSpeedSet = cruiseSpeedVehicle
|
||||
return lib.notify({
|
||||
title = "Cruise control",
|
||||
description = ("Increased to %d mph."):format(math.floor(cruiseSpeedSet)),
|
||||
title = locale("cruise_control"),
|
||||
description = locale("cruise_control_increase"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -689,20 +811,25 @@ lib.addKeybind({
|
||||
elseif cruiseControlEnabled then
|
||||
cruiseControlEnabled = false
|
||||
return lib.notify({
|
||||
title = "Cruise control",
|
||||
description = "Vehicle cruise control disabled.",
|
||||
title = locale("cruise_control"),
|
||||
description = locale("cruise_control_disabled"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
})
|
||||
end
|
||||
if not playerVehicle then return end
|
||||
|
||||
local speed = math.floor(GetEntitySpeed(playerVehicle) * 2.236936)
|
||||
if speed < 10 then return end
|
||||
|
||||
cruiseControlEnabled = true
|
||||
cruiseSpeedVehicle = math.floor(GetEntitySpeed(playerVehicle) * 2.236936)
|
||||
cruiseSpeedVehicle = speed
|
||||
cruiseSpeedSet = cruiseSpeedVehicle
|
||||
|
||||
lib.notify({
|
||||
title = "Cruise control",
|
||||
description = ("Set to %d mph."):format(math.floor(cruiseSpeedSet)),
|
||||
title = locale("cruise_control"),
|
||||
description = locale("cruise_control_enabled"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -716,7 +843,7 @@ lib.addKeybind({
|
||||
|
||||
lib.addKeybind({
|
||||
name = "vehicleShuffleSeat",
|
||||
description = "Suffle vehicle seat.",
|
||||
description = locale("seat_shuffle"),
|
||||
defaultKey = "",
|
||||
onPressed = function(self)
|
||||
if not cache.vehicle then return end
|
||||
@@ -1,243 +0,0 @@
|
||||
return {
|
||||
{
|
||||
garageType = "land",
|
||||
groups = {"sahp", "lspd", "bcso"},
|
||||
ped = vector4(452.83, -1027.79, 28.54, 2.49),
|
||||
vehicleSpawns = {
|
||||
vector4(446.19, -1025.47, 28.24, 185.96),
|
||||
vector4(438.75, -1026.09, 28.38, 184.29),
|
||||
vector4(434.98, -1026.61, 28.46, 185.99),
|
||||
vector4(431.26, -1027.31, 28.53, 185.59),
|
||||
vector4(427.52, -1026.84, 28.58, 184.25)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "land",
|
||||
impound = true,
|
||||
ped = vector4(407.99, -1624.74, 29.29, 229.93),
|
||||
vehicleSpawns = {
|
||||
vector4(396.34, -1644.28, 28.86, 319.10),
|
||||
vector4(398.56, -1646.42, 28.8616, 319.18),
|
||||
vector4(400.68, -1648.86, 28.86, 140.78),
|
||||
vector4(403.32, -1650.54, 28.86, 319.92),
|
||||
vector4(403.21, -1650.68, 28.86, 139.40)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "plane",
|
||||
ped = vector4(-941.05, -2966.04, 13.95, 136.06),
|
||||
vehicleSpawns = {
|
||||
vector4(-974.98, -2977.90, 14.55, 59.91)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "heli",
|
||||
ped = vector4(-731.00, -1394.54, 5.00, 245.52),
|
||||
vehicleSpawns = {
|
||||
vector4(-746.01, -1469.39, 5.68, 139.27),
|
||||
vector4(-725.26, -1444.72, 5.68, 139.58)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "plane",
|
||||
ped = vector4(1742.77, 3298.25, 41.22, 132.91),
|
||||
vehicleSpawns = {
|
||||
vector4(1734.26, 3250.98, 41.96, 81.11),
|
||||
vector4(1729.32, 3270.69, 41.74, 145.39)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "plane",
|
||||
ped = vector4(-1241.26, -3391.48, 13.94, 35.03),
|
||||
vehicleSpawns = {
|
||||
vector4(-1254.71, -3388.15, 14.54, 330.04),
|
||||
vector4(-1270.36, -3378.80, 14.54, 330.20),
|
||||
vector4(-1286.05, -3369.44, 14.54, 329.99)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "plane",
|
||||
ped = vector4(-1621.10, -3151.63, 13.99, 41.26),
|
||||
vehicleSpawns = {
|
||||
vector4(-1634.04, -3147.84, 14.60, 330.28),
|
||||
vector4(-1649.75, -3138.34, 14.60, 329.20),
|
||||
vector4(-1665.42, -3129.26, 14.60, 330.10)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "heli",
|
||||
ped = vector4(-1121.87, -2839.88, 13.95, 150.58),
|
||||
vehicleSpawns = {
|
||||
vector4(-1178.71, -2846.51, 14.62, 150.16),
|
||||
vector4(-1146.40, -2865.22, 14.62, 151.08),
|
||||
vector4(-1112.85, -2884.66, 14.62, 149.70)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "water",
|
||||
ped = vector4(-831.03, -1359.53, 5.00, 299.80),
|
||||
vehicleSpawns = {
|
||||
vector4(-846.16, -1362.07, 0.39, 110.54),
|
||||
vector4(-842.53, -1372.00, 0.39, 111.54),
|
||||
vector4(-849.29, -1353.26, 0.38, 109.53),
|
||||
vector4(-852.26, -1345.09, 0.41, 110.52),
|
||||
vector4(-855.49, -1336.60, 0.40, 107.43),
|
||||
vector4(-858.63, -1328.30, 0.40, 109.82),
|
||||
vector4(-839.35, -1380.30, 0.39, 109.27),
|
||||
vector4(-836.38, -1388.93, 0.41, 110.11),
|
||||
vector4(-833.29, -1397.32, 0.40, 110.80),
|
||||
vector4(-830.34, -1405.65, 0.37, 110.16)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "land",
|
||||
ped = vector4(-280.32, -888.42, 31.32, 250.68),
|
||||
vehicleSpawns = {
|
||||
vector4(-282.36, -915.11, 30.38, 68.81),
|
||||
vector4(-284.27, -918.37, 30.38, 70.58),
|
||||
vector4(-285.44, -921.82, 30.38, 250.22),
|
||||
vector4(-285.70, -887.62, 30.38, 167.04),
|
||||
vector4(-292.70, -885.93, 30.38, 167.09),
|
||||
vector4(-285.76, -887.47, 30.38, 169.59),
|
||||
vector4(-300.44, -885.14, 30.38, 347.05),
|
||||
vector4(-303.59, -883.71, 30.38, 167.69),
|
||||
vector4(-309.38, -896.88, 30.38, 167.15),
|
||||
vector4(-312.98, -896.30, 30.38, 166.46),
|
||||
vector4(-316.68, -896.26, 30.37, 347.94),
|
||||
vector4(-311.06, -881.94, 30.38, 168.21),
|
||||
vector4(-314.78, -881.86, 30.37, 348.45)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "land",
|
||||
ped = vector4(597.53, 91.08, 93.13, 250.54),
|
||||
vehicleSpawns = {
|
||||
vector4(598.53, 98.37, 92.27, 69.47),
|
||||
vector4(599.79, 102.00, 92.27, 249.38),
|
||||
vector4(608.21, 103.90, 92.18, 248.98),
|
||||
vector4(600.60, 111.38, 92.27, 73.07),
|
||||
vector4(609.91, 107.59, 92.23, 68.77),
|
||||
vector4(601.36, 114.99, 92.27, 250.19),
|
||||
vector4(611.07, 111.39, 92.29, 249.69),
|
||||
vector4(603.52, 118.52, 92.26, 67.62),
|
||||
vector4(612.74, 114.94, 92.28, 69.01),
|
||||
vector4(604.00, 122.58, 92.27, 249.76),
|
||||
vector4(613.78, 118.83, 92.29, 248.82),
|
||||
vector4(622.43, 115.48, 91.99, 70.39),
|
||||
vector4(628.56, 110.25, 91.47, 249.30),
|
||||
vector4(620.67, 111.89, 92.03, 250.24),
|
||||
vector4(618.41, 104.51, 91.97, 72.22),
|
||||
vector4(624.87, 99.25, 91.36, 63.32),
|
||||
vector4(616.59, 100.82, 91.97, 249.00)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "land",
|
||||
ped = vector4(100.64, -1072.88, 29.37, 341.55),
|
||||
vehicleSpawns = {
|
||||
vector4(106.13, -1063.24, 28.51, 66.86),
|
||||
vector4(107.82, -1059.73, 28.51, 66.99),
|
||||
vector4(112.39, -1049.71, 28.52, 67.40),
|
||||
vector4(110.76, -1053.09, 28.51, 68.13),
|
||||
vector4(109.06, -1056.38, 28.51, 66.45),
|
||||
vector4(117.53, -1081.03, 28.50, 181.40),
|
||||
vector4(119.09, -1069.44, 28.50, 181.12),
|
||||
vector4(121.22, -1081.42, 28.50, 0.57),
|
||||
vector4(122.32, -1070.46, 28.50, 0.67),
|
||||
vector4(125.62, -1069.54, 28.50, 179.25),
|
||||
vector4(124.83, -1081.17, 28.50, 180.37),
|
||||
vector4(128.62, -1081.71, 28.50, 1.36),
|
||||
vector4(128.93, -1070.62, 28.50, 359.89),
|
||||
vector4(132.24, -1069.35, 28.50, 181.54),
|
||||
vector4(132.31, -1081.45, 28.50, 180.86),
|
||||
vector4(135.93, -1081.35, 28.50, 358.82),
|
||||
vector4(135.64, -1070.75, 28.50, 0.43),
|
||||
vector4(139.75, -1081.91, 28.50, 182.18),
|
||||
vector4(138.86, -1070.37, 28.50, 181.90),
|
||||
vector4(143.55, -1081.41, 28.50, 0.89),
|
||||
vector4(147.13, -1081.48, 28.50, 179.02),
|
||||
vector4(150.95, -1081.49, 28.50, 358.79),
|
||||
vector4(154.64, -1081.32, 28.50, 179.51),
|
||||
vector4(158.38, -1082.03, 28.50, 1.57),
|
||||
vector4(162.10, -1081.32, 28.50, 179.88)
|
||||
}
|
||||
},
|
||||
{
|
||||
garageType = "land",
|
||||
ped = vector4(214.84, -806.24, 30.81, 342.23),
|
||||
vehicleSpawns = {
|
||||
vector4(251.26, -774.63, 29.98, 67.34),
|
||||
vector4(245.36, -772.34, 30.02, 66.81),
|
||||
vector4(219.19, -765.93, 30.14, 249.78),
|
||||
vector4(228.11, -768.94, 30.10, 249.66),
|
||||
vector4(233.68, -771.18, 30.07, 249.82),
|
||||
vector4(244.01, -775.06, 29.99, 249.41),
|
||||
vector4(249.53, -777.07, 29.95, 251.09),
|
||||
vector4(248.74, -779.52, 29.92, 69.14),
|
||||
vector4(243.64, -777.63, 29.96, 69.23),
|
||||
vector4(233.47, -773.91, 30.05, 69.88),
|
||||
vector4(218.62, -768.58, 30.14, 69.81),
|
||||
vector4(217.02, -770.83, 30.16, 248.11),
|
||||
vector4(226.09, -773.95, 30.09, 249.30),
|
||||
vector4(231.82, -776.14, 30.04, 249.17),
|
||||
vector4(242.05, -779.91, 29.93, 246.99),
|
||||
vector4(247.72, -782.02, 29.88, 251.04),
|
||||
vector4(216.19, -773.65, 30.16, 67.90),
|
||||
vector4(225.82, -776.85, 30.08, 65.64),
|
||||
vector4(230.77, -778.89, 30.03, 69.40),
|
||||
vector4(241.72, -782.84, 29.90, 69.13),
|
||||
vector4(247.12, -784.93, 29.84, 67.56),
|
||||
vector4(246.23, -787.13, 29.82, 247.56),
|
||||
vector4(239.40, -784.68, 29.90, 248.06),
|
||||
vector4(230.42, -781.41, 30.01, 247.48),
|
||||
vector4(224.14, -779.01, 30.07, 247.39),
|
||||
vector4(215.32, -775.95, 30.17, 248.99),
|
||||
vector4(243.81, -792.19, 29.77, 246.23),
|
||||
vector4(238.08, -789.95, 29.85, 250.62),
|
||||
vector4(228.14, -786.30, 30.01, 249.07),
|
||||
vector4(221.98, -783.92, 30.08, 248.25),
|
||||
vector4(213.41, -781.01, 30.19, 249.21),
|
||||
vector4(214.77, -778.70, 30.17, 67.86),
|
||||
vector4(224.02, -781.91, 30.07, 68.82),
|
||||
vector4(229.34, -784.05, 30.01, 66.57),
|
||||
vector4(239.87, -787.83, 29.86, 69.37),
|
||||
vector4(245.18, -789.87, 29.79, 68.00),
|
||||
vector4(241.85, -797.20, 29.72, 245.51),
|
||||
vector4(236.57, -795.07, 29.82, 247.54),
|
||||
vector4(226.12, -791.34, 29.99, 247.84),
|
||||
vector4(220.61, -789.19, 30.08, 248.09),
|
||||
vector4(212.00, -786.22, 30.21, 248.93),
|
||||
vector4(212.91, -783.71, 30.19, 68.61),
|
||||
vector4(222.11, -786.89, 30.07, 70.10),
|
||||
vector4(227.84, -789.16, 29.99, 67.55),
|
||||
vector4(237.73, -792.73, 29.82, 69.40),
|
||||
vector4(243.54, -794.88, 29.73, 68.74),
|
||||
vector4(237.03, -812.64, 29.59, 243.63),
|
||||
vector4(207.66, -798.70, 30.29, 70.06),
|
||||
vector4(216.56, -801.92, 30.10, 70.31),
|
||||
vector4(222.28, -804.17, 29.98, 69.97),
|
||||
vector4(232.41, -807.95, 29.74, 69.42),
|
||||
vector4(238.16, -810.17, 29.60, 68.95),
|
||||
vector4(238.32, -807.43, 29.63, 248.28),
|
||||
vector4(233.32, -805.44, 29.75, 248.13),
|
||||
vector4(222.46, -801.41, 29.98, 248.50),
|
||||
vector4(216.85, -799.11, 30.10, 248.54),
|
||||
vector4(207.60, -795.90, 30.29, 248.64),
|
||||
vector4(209.28, -793.74, 30.25, 69.82),
|
||||
vector4(218.31, -796.92, 30.08, 68.55),
|
||||
vector4(224.50, -799.34, 29.96, 68.63),
|
||||
vector4(234.40, -803.02, 29.76, 66.67),
|
||||
vector4(240.09, -805.27, 29.64, 69.02),
|
||||
vector4(240.53, -802.44, 29.67, 248.95),
|
||||
vector4(234.46, -800.13, 29.79, 249.40),
|
||||
vector4(224.30, -796.51, 29.98, 248.31),
|
||||
vector4(218.23, -794.00, 30.09, 249.09),
|
||||
vector4(209.84, -791.10, 30.24, 251.56),
|
||||
vector4(211.09, -788.68, 30.22, 69.53),
|
||||
vector4(220.17, -791.73, 30.06, 70.56),
|
||||
vector4(225.84, -794.12, 29.98, 67.53),
|
||||
vector4(236.38, -797.85, 29.79, 69.39),
|
||||
vector4(216.56, -801.90, 30.79, 69.39)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
local locations = require "client.vehicle.data"
|
||||
local sprite = {
|
||||
["water"] = 356,
|
||||
["heli"] = 360,
|
||||
["plane"] = 359,
|
||||
["land"] = 357
|
||||
}
|
||||
local garageTypes = {
|
||||
["water"] = 14,
|
||||
["heli"] = 15,
|
||||
["plane"] = 16
|
||||
}
|
||||
|
||||
local clothing = {
|
||||
{
|
||||
face = {
|
||||
drawable = 1,
|
||||
texture = 1
|
||||
},
|
||||
undershirt = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
torso = {
|
||||
drawable = 1,
|
||||
texture = 1
|
||||
},
|
||||
leg = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
glasses = {
|
||||
drawable = 1,
|
||||
texture = 0
|
||||
},
|
||||
hat = {
|
||||
drawable = -1,
|
||||
texture = -1
|
||||
},
|
||||
},
|
||||
{
|
||||
leg = {
|
||||
drawable = 0,
|
||||
texture = 1
|
||||
},
|
||||
undershirt = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
face = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
torso = {
|
||||
drawable = 0,
|
||||
texture = 2
|
||||
},
|
||||
glasses = {
|
||||
drawable = -1,
|
||||
texture = -1
|
||||
},
|
||||
hat = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
},
|
||||
{
|
||||
face = {
|
||||
drawable = 0,
|
||||
texture = 2
|
||||
},
|
||||
undershirt = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
torso = {
|
||||
drawable = 1,
|
||||
texture = 2
|
||||
},
|
||||
leg = {
|
||||
drawable = 0,
|
||||
texture = 0
|
||||
},
|
||||
hat = {
|
||||
drawable = -1,
|
||||
texture = -1
|
||||
},
|
||||
glasses = {
|
||||
drawable = -1,
|
||||
texture = -1
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
local function getClosestOwnedVehicle()
|
||||
local coords = GetEntityCoords(cache.ped)
|
||||
local vehicles = lib.getNearbyVehicles(coords, 50.0, true)
|
||||
local nearestVeh = {}
|
||||
|
||||
local function setNearestVehicle(veh)
|
||||
local state = Entity(veh.vehicle).state
|
||||
if not state.owner or state.owner ~= NDCore.player?.id then return end
|
||||
|
||||
local nearestDist = nearestVeh.dist
|
||||
local dist = #(coords-veh.coords)
|
||||
if not nearestDist or dist < nearestDist then
|
||||
nearestVeh.dist = dist
|
||||
nearestVeh.coords = veh.coords
|
||||
nearestVeh.entity = veh.vehicle
|
||||
end
|
||||
end
|
||||
|
||||
for i=1, #vehicles do
|
||||
setNearestVehicle(vehicles[i])
|
||||
end
|
||||
return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist
|
||||
end
|
||||
|
||||
local function parkVehicle(veh)
|
||||
if not veh or not DoesEntityExist(veh) then
|
||||
return NDCore.notify({
|
||||
title = "Garage",
|
||||
description = "No owned vehicle found nearby.",
|
||||
type = "error",
|
||||
position = "bottom",
|
||||
duration = 3000
|
||||
})
|
||||
end
|
||||
if GetPedInVehicleSeat(veh, -1) ~= 0 then
|
||||
NDCore.notify({
|
||||
title = "Garage",
|
||||
description = "Player in vehicle!",
|
||||
type = "error",
|
||||
position = "bottom",
|
||||
duration = 3000
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
local properties = lib.getVehicleProperties(veh)
|
||||
properties.class = GetVehicleClass(veh)
|
||||
TriggerServerEvent("ND_Vehicles:storeVehicle", VehToNet(veh))
|
||||
end
|
||||
|
||||
local function isVehicleAvailable(vehicle, garageType, impound)
|
||||
local class = vehicle.properties.class
|
||||
local available = vehicle.available and not impound or vehicle.impounded and impound
|
||||
if available and not garageTypes[garageType] then return true end
|
||||
|
||||
for garType, garClass in pairs(garageTypes) do
|
||||
if available and garType == garageType and garClass == class then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getEngineStatus(health)
|
||||
if health > 950 then
|
||||
return "Perfect"
|
||||
elseif health > 750 then
|
||||
return "Good"
|
||||
elseif health > 500 then
|
||||
return "Bad"
|
||||
end
|
||||
return "Very bad"
|
||||
end
|
||||
|
||||
local function createMenuOptions(vehicle, vehicleSpawns)
|
||||
local props = vehicle.properties
|
||||
local makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model))
|
||||
local modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model))
|
||||
local metadata = {}
|
||||
|
||||
if not makeName or makeName == "NULL" then
|
||||
makeName = ""
|
||||
else
|
||||
metadata[#metadata+1] = {label = "Make", value = makeName}
|
||||
makeName = makeName .. " "
|
||||
end
|
||||
if not modelName or modelName == "NULL" then
|
||||
modelName = ""
|
||||
else
|
||||
metadata[#metadata+1] = {label = "Model", value = modelName}
|
||||
end
|
||||
|
||||
if props?.plate then
|
||||
metadata[#metadata+1] = {label = "Plate", value = props.plate}
|
||||
end
|
||||
if props?.engineHealth then
|
||||
metadata[#metadata+1] = {
|
||||
label = "Engine status",
|
||||
value = getEngineStatus(props.engineHealth),
|
||||
progress = props.engineHealth/10,
|
||||
colorScheme = "blue"
|
||||
}
|
||||
end
|
||||
|
||||
if props?.fuelLevel then
|
||||
metadata[#metadata+1] = {
|
||||
label = "Fuel",
|
||||
value = ("%d%s"):format(props.fuelLevel, "%"),
|
||||
progress = props.fuelLevel,
|
||||
colorScheme = "yellow"
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
title = ("Vehicle: %s%s\nPlate: %s"):format(makeName, modelName, props?.plate or "not found"),
|
||||
metadata = metadata,
|
||||
onSelect = function(args)
|
||||
TriggerServerEvent("ND_Vehicles:takeVehicle", vehicle.id, vehicleSpawns)
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local function createMenu(vehicles, garageType, vehicleSpawns, impound)
|
||||
local options = {}
|
||||
if not impound then
|
||||
options[#options+1] = {
|
||||
title = "Park vehicle",
|
||||
onSelect = function(args)
|
||||
local veh = getClosestOwnedVehicle()
|
||||
parkVehicle(veh)
|
||||
end
|
||||
}
|
||||
end
|
||||
for _, vehicle in ipairs(vehicles) do
|
||||
if isVehicleAvailable(vehicle, garageType, impound) then
|
||||
options[#options+1] = createMenuOptions(vehicle, vehicleSpawns)
|
||||
end
|
||||
end
|
||||
if impound and #options == 0 then
|
||||
options[#options+1] = {
|
||||
title = "No vehicles found",
|
||||
readOnly = true
|
||||
}
|
||||
end
|
||||
return {
|
||||
id = ("garage_%s"):format(garageType),
|
||||
title = impound and "Vehicle impound" or "Parking garage",
|
||||
options = options,
|
||||
onExit = function()
|
||||
garageOpen = false
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
for i=1, #locations do
|
||||
local location = locations[i]
|
||||
NDCore.createAiPed({
|
||||
model = `s_m_y_airworker`,
|
||||
coords = location.ped,
|
||||
distance = 45.0,
|
||||
clothing = clothing[math.random(1, #clothing)],
|
||||
blip = {
|
||||
label = location.impound and ("Impound (%s)"):format(location.garageType) or ("Parking garage (%s)"):format(location.garageType),
|
||||
sprite = location.impound and 285 or sprite[location.garageType],
|
||||
scale = 0.7,
|
||||
color = 3,
|
||||
groups = location.groups
|
||||
},
|
||||
anim = {
|
||||
dict = "anim@amb@casino@valet_scenario@pose_d@",
|
||||
clip = "base_a_m_y_vinewood_01"
|
||||
},
|
||||
options = {
|
||||
{
|
||||
name = "nd_core:garagePed",
|
||||
icon = "fa-solid fa-warehouse",
|
||||
label = location.impound and "View impounded vehicles" or "View garage",
|
||||
distance = 2.0,
|
||||
canInteract = function(entity, distance, coords, name, bone)
|
||||
if not location.groups then return true end
|
||||
local groups = location.groups
|
||||
local playerGroups = NDCore.player?.groups
|
||||
for i=1, #groups do
|
||||
if playerGroups?[groups[i]] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end,
|
||||
onSelect = function(data)
|
||||
local vehicles = lib.callback.await("ND_Vehicles:getOwnedVehicles") or {}
|
||||
local menu = createMenu(vehicles, location.garageType, location.vehicleSpawns, location.impound)
|
||||
lib.registerContext(menu)
|
||||
lib.showContext(menu.id)
|
||||
end
|
||||
}
|
||||
},
|
||||
})
|
||||
end
|
||||
@@ -5,13 +5,32 @@ exports("GetCoreObject", function()
|
||||
end)
|
||||
|
||||
NDCore.Functions = {}
|
||||
NDCore.Functions.GetPlayer = NDCore.getPlayer
|
||||
NDCore.Functions.GetPlayers = NDCore.getPlayers
|
||||
NDCore.Functions.GetUserDiscordInfo = NDCore.getDiscordInfo
|
||||
NDCore.Functions.SetActiveCharacter = NDCore.setActiveCharacter
|
||||
NDCore.Functions.GetPlayerCharacters = NDCore.fetchAllCharacters
|
||||
NDCore.Functions.GetPlayerByCharacterId = NDCore.fetchCharacter
|
||||
|
||||
function NDCore.Functions.GetPlayer(src)
|
||||
local player = NDCore.getPlayer(src)
|
||||
return {
|
||||
source = player.source,
|
||||
id = player.id,
|
||||
firstName = player.firstname,
|
||||
lastName = player.lastname,
|
||||
dob = player.dob,
|
||||
gender = player.gender,
|
||||
cash = player.cash,
|
||||
bank = player.bank,
|
||||
phoneNumber = player.metadata.phone,
|
||||
lastLocation = player.metadata.lastLocation,
|
||||
inventory = player.inventory,
|
||||
discordInfo = player.discordInfo,
|
||||
data = player.metadata,
|
||||
job = player.job
|
||||
}
|
||||
end
|
||||
|
||||
function NDCore.Functions.GetPlayerIdentifierFromType(identifierType, src)
|
||||
return GetPlayerIdentifierByType(src, identifierType)
|
||||
end
|
||||
|
||||
@@ -60,7 +60,7 @@ function NDCore.GetPlayerData()
|
||||
|
||||
player.coords = GetEntityCoords(cache.ped)
|
||||
player.loadout = {}
|
||||
player.maxWeight = exports.ox_inventory:GetPlayerMaxWeight()
|
||||
player.maxWeight = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetPlayerMaxWeight() or 0
|
||||
player.money = player.Accounts.Money
|
||||
player.sex = player.gender
|
||||
player.firstName = player.firstname
|
||||
@@ -112,7 +112,7 @@ function NDCore.Progressbar(message, lenght, options)
|
||||
end
|
||||
|
||||
function NDCore.SearchInventory(item, count)
|
||||
local itemCount = exports.ox_inventory:Search(item)
|
||||
local itemCount = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:Search(item) or 0
|
||||
return itemCount >= count and itemCount
|
||||
end
|
||||
|
||||
@@ -133,7 +133,7 @@ function NDCore.ShowHelpNotification()
|
||||
end
|
||||
|
||||
function NDCore.ShowInventory()
|
||||
exports.ox_inventory:openInventory("player", cache.serverId)
|
||||
return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:openInventory("player", cache.serverId)
|
||||
end
|
||||
|
||||
function NDCore.ShowNotification(msg, type, time)
|
||||
|
||||
@@ -5,7 +5,8 @@ local registeredItems = {}
|
||||
|
||||
local function getAmmoFromWeapon(weapon)
|
||||
if not weapon then return end
|
||||
for item, data in pairs(exports.ox_inventory:Items()) do
|
||||
local items = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:Items() or {}
|
||||
for item, data in pairs(items) do
|
||||
if data.weapon and data.model and data.model:lower() == weapon:lower() then
|
||||
return data.ammoname
|
||||
end
|
||||
@@ -60,7 +61,7 @@ local function createPlayerFunctions(self)
|
||||
end
|
||||
|
||||
function self.addInventoryItem(item, count)
|
||||
exports.ox_inventory:AddItem(self.source, item, count)
|
||||
return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:AddItem(self.source, item, count)
|
||||
end
|
||||
|
||||
function self.addMoney(amount)
|
||||
@@ -100,11 +101,11 @@ local function createPlayerFunctions(self)
|
||||
end
|
||||
|
||||
function self.canCarryItem(item, count)
|
||||
return exports.ox_inventory:CanCarryItem(self.source, item, count)
|
||||
return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:CanCarryItem(self.source, item, count)
|
||||
end
|
||||
|
||||
function self.canSwapItem(firstItem, firstItemCount, testItem, testItemCount)
|
||||
exports.ox_inventory:CanSwapItem(self.source, firstItem, firstItemCount, testItem, testItemCount)
|
||||
return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:CanSwapItem(self.source, firstItem, firstItemCount, testItem, testItemCount)
|
||||
end
|
||||
|
||||
function self.clearMeta(index)
|
||||
@@ -137,7 +138,7 @@ local function createPlayerFunctions(self)
|
||||
end
|
||||
|
||||
function self.getInventory(minimal)
|
||||
local playerItems = exports.ox_inventory:GetInventoryItems(self.source)
|
||||
local playerItems = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetInventoryItems(self.source) or {}
|
||||
if not minimal then
|
||||
return playerItems
|
||||
end
|
||||
@@ -145,7 +146,7 @@ local function createPlayerFunctions(self)
|
||||
end
|
||||
|
||||
function self.getInventoryItem(item)
|
||||
local playerItems = exports.ox_inventory:GetInventoryItems(self.source)
|
||||
local playerItems = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetInventoryItems(self.source) or {}
|
||||
for name, data in pairs(playerItems) do
|
||||
if name == item then
|
||||
return data
|
||||
@@ -162,11 +163,11 @@ local function createPlayerFunctions(self)
|
||||
end
|
||||
|
||||
function self.hasItem(item, metadata)
|
||||
return exports.ox_inventory:GetItem(self.source, item, metadata)
|
||||
return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:GetItem(self.source, item, metadata)
|
||||
end
|
||||
|
||||
function self.removeInventoryItem(item, count)
|
||||
exports.ox_inventory:RemoveItem(self.source, item, count)
|
||||
return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:RemoveItem(self.source, item, count)
|
||||
end
|
||||
|
||||
function self.removeMoney(amount)
|
||||
@@ -304,7 +305,8 @@ end
|
||||
function NDCore.GetItemLabel(item)
|
||||
if not itemNames then
|
||||
itemNames = {}
|
||||
for item, data in pairs(exports.ox_inventory:Items()) do
|
||||
local items = GetResourceState("ox_inventory") == "started" and exports.ox_inventory:Items() or {}
|
||||
for item, data in pairs(items) do
|
||||
itemNames[item] = data.label
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
CREATE TABLE IF NOT EXISTS `nd_characters` (
|
||||
`charid` INT(10) NOT NULL AUTO_INCREMENT,
|
||||
`identifier` VARCHAR(200) NOT NULL DEFAULT '0',
|
||||
`name` VARCHAR(50) DEFAULT NULL,
|
||||
`firstname` VARCHAR(50) DEFAULT NULL,
|
||||
`lastname` VARCHAR(50) DEFAULT NULL,
|
||||
`dob` VARCHAR(50) DEFAULT NULL,
|
||||
`gender` VARCHAR(50) DEFAULT NULL,
|
||||
`cash` INT(10) DEFAULT '0',
|
||||
`bank` INT(10) DEFAULT '0',
|
||||
`groups` LONGTEXT DEFAULT '[]',
|
||||
`metadata` LONGTEXT DEFAULT '[]',
|
||||
`inventory` LONGTEXT DEFAULT '[]',
|
||||
PRIMARY KEY (`charid`) USING BTREE
|
||||
`charid` INT(10) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INT(11) DEFAULT NULL,
|
||||
`identifier` VARCHAR(200) NOT NULL DEFAULT '0',
|
||||
`name` VARCHAR(50) DEFAULT NULL,
|
||||
`firstname` VARCHAR(50) DEFAULT NULL,
|
||||
`lastname` VARCHAR(50) DEFAULT NULL,
|
||||
`dob` VARCHAR(50) DEFAULT NULL,
|
||||
`gender` VARCHAR(50) DEFAULT NULL,
|
||||
`cash` INT(10) DEFAULT '0',
|
||||
`bank` INT(10) DEFAULT '0',
|
||||
`phonenumber` VARCHAR(20) DEFAULT NULL,
|
||||
`deleted_at` DATETIME NULL DEFAULT NULL,
|
||||
`groups` LONGTEXT DEFAULT ('[]'),
|
||||
`metadata` LONGTEXT DEFAULT ('[]'),
|
||||
`inventory` LONGTEXT DEFAULT ('[]'),
|
||||
PRIMARY KEY (`charid`) USING BTREE,
|
||||
INDEX `idx_nd_characters_user_id` (`user_id`),
|
||||
CONSTRAINT `fk_nd_characters_user_id` FOREIGN KEY (`user_id`) REFERENCES `nd_users` (`user_id`) ON UPDATE CASCADE ON DELETE SET NULL
|
||||
);
|
||||
|
||||
10
database/group_ranks.sql
Normal file
10
database/group_ranks.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS `nd_group_ranks` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`group_name` VARCHAR(50) NOT NULL,
|
||||
`label` VARCHAR(100) NOT NULL,
|
||||
`weight` INT NOT NULL DEFAULT 1,
|
||||
`isBoss` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `idx_group_name` (`group_name`),
|
||||
CONSTRAINT `fk_group_ranks_group` FOREIGN KEY (`group_name`) REFERENCES `nd_groups`(`name`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
6
database/groups.sql
Normal file
6
database/groups.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS `nd_groups` (
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`label` VARCHAR(100) NOT NULL,
|
||||
`isJob` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
13
database/moneylogs.sql
Normal file
13
database/moneylogs.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
CREATE TABLE IF NOT EXISTS `nd_money_log` (
|
||||
`log_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`character_id` INT(10) NOT NULL,
|
||||
`action` VARCHAR(50) DEFAULT NULL,
|
||||
`account` ENUM('cash', 'bank') NOT NULL,
|
||||
`amount` INT(10) NOT NULL,
|
||||
`reason` VARCHAR(255) DEFAULT NULL,
|
||||
`trace` TEXT DEFAULT NULL,
|
||||
`date_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`log_id`),
|
||||
KEY `idx_character_id` (`character_id`),
|
||||
CONSTRAINT `fk_moneylog_character` FOREIGN KEY (`character_id`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
12
database/users.sql
Normal file
12
database/users.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE IF NOT EXISTS `nd_users` (
|
||||
`user_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`id_steam` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_discord` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_xbl` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_live` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_license` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_license2` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_fivem` LONGTEXT NULL DEFAULT NULL,
|
||||
`id_ip` LONGTEXT NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`user_id`) USING BTREE
|
||||
) COLLATE='utf8mb4_unicode_ci';
|
||||
@@ -1,15 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS `nd_vehicles` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`owner` INT(11) DEFAULT NULL,
|
||||
`plate` VARCHAR(255) DEFAULT NULL,
|
||||
`glovebox` LONGTEXT DEFAULT '[]',
|
||||
`trunk` LONGTEXT DEFAULT '[]',
|
||||
`properties` LONGTEXT DEFAULT '[]',
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`owner` INT(11) DEFAULT NULL,
|
||||
`plate` VARCHAR(255) DEFAULT NULL,
|
||||
`glovebox` LONGTEXT DEFAULT ('[]'),
|
||||
`trunk` LONGTEXT DEFAULT ('[]'),
|
||||
`properties` LONGTEXT DEFAULT ('[]'),
|
||||
`stored` INT(11) DEFAULT '1',
|
||||
`impounded` INT(11) DEFAULT '0',
|
||||
`stolen` INT(11) DEFAULT '0',
|
||||
`metadata` LONGTEXT DEFAULT '[]',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `owner` (`owner`) USING BTREE,
|
||||
CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
`impounded` INT(11) DEFAULT '0',
|
||||
`stolen` INT(11) DEFAULT '0',
|
||||
`metadata` LONGTEXT DEFAULT ('[]'),
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `owner` (`owner`) USING BTREE,
|
||||
CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
@@ -2,22 +2,31 @@
|
||||
|
||||
author "Andyyy#7666"
|
||||
description "ND Framework Core"
|
||||
version "2.0.0"
|
||||
version "2.3.2"
|
||||
|
||||
fx_version "cerulean"
|
||||
game "gta5"
|
||||
lua54 "yes"
|
||||
|
||||
files {
|
||||
"init.lua",
|
||||
"compatibility/**/locale.lua",
|
||||
"locales/*.json",
|
||||
"ui/**"
|
||||
}
|
||||
|
||||
ui_page "ui/index.html"
|
||||
|
||||
shared_script "@ox_lib/init.lua"
|
||||
client_scripts {
|
||||
"client/main.lua",
|
||||
"shared/functions.lua",
|
||||
"client/peds.lua",
|
||||
"client/vehicle/main.lua",
|
||||
"client/vehicle/garages.lua",
|
||||
"client/vehicle.lua",
|
||||
"client/functions.lua",
|
||||
"client/events.lua",
|
||||
"client/death.lua",
|
||||
"client/groupadmin.lua",
|
||||
"compatibility/**/client.lua"
|
||||
}
|
||||
server_scripts {
|
||||
@@ -27,16 +36,17 @@ server_scripts {
|
||||
"server/player.lua",
|
||||
"server/vehicle.lua",
|
||||
"server/functions.lua",
|
||||
"server/groups.lua",
|
||||
"server/groupadmin.lua",
|
||||
"compatibility/**/server.lua",
|
||||
"server/commands.lua"
|
||||
}
|
||||
|
||||
files {
|
||||
"init.lua",
|
||||
"client/vehicle/data.lua",
|
||||
"compatibility/**/locale.lua"
|
||||
dependencies {
|
||||
"ox_lib",
|
||||
"oxmysql"
|
||||
}
|
||||
|
||||
dependency "oxmysql"
|
||||
-- below were used with backwards compatibility but could interfere with resources checking if the resources are started.
|
||||
-- provide "es_extended"
|
||||
-- provide "qb-Core"
|
||||
|
||||
2
init.lua
2
init.lua
@@ -9,3 +9,5 @@ NDCore = setmetatable({}, {
|
||||
return self[index]
|
||||
end
|
||||
})
|
||||
|
||||
return NDCore
|
||||
|
||||
134
locales/ar.json
Normal file
134
locales/ar.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "لم يتم العثور على حساب ديسكورد الخاص بك، انضم إلى ديسكورد هنا: %s.",
|
||||
"connecting": "جارٍ الاتصال...",
|
||||
"identifier_not_found": "لم يتم العثور على %s الخاص بك.",
|
||||
"deposit": "إيداع",
|
||||
"withdraw": "سحب",
|
||||
"vehicle_ownership_transfered": "تم نقل الملكية",
|
||||
"vehicle_ownership_transfered2": "تم نقل ملكية المركبة %s.",
|
||||
"vehicle_ownership_received": "تم استلام الملكية",
|
||||
"vehicle_ownership_received2": "تم استلام ملكية المركبة %s.",
|
||||
"keys_shared": "تمت مشاركة المفاتيح",
|
||||
"keys_shared2": "لقد شاركت مفاتيح المركبة مع %s.",
|
||||
"keys_received": "تم استلام المفاتيح",
|
||||
"keys_received2": "لقد استلمت مفاتيح المركبة من %s.",
|
||||
"no_signal": "لا يوجد إشارة",
|
||||
"veh_key_disabled": "تم تعطيل مفتاح المركبة.",
|
||||
"veh_key_disabled2": "تم تعطيل هذا المفتاح ولا يمكن استخدامه بعد الآن.",
|
||||
"veh_far_away": "المركبة بعيدة جدًا.",
|
||||
"veh_locked": "مقفول",
|
||||
"veh_locked2": "تم قفل مركبتك الآن.",
|
||||
"veh_unlocked": "مفتوح",
|
||||
"veh_unlocked2": "تم فتح مركبتك الآن.",
|
||||
"garage": "كراج",
|
||||
"no_owned_veh_nearby": "لم يتم العثور على مركبة مملوكة بالقرب منك.",
|
||||
"veh_stored_in_garage": "تم تخزين المركبة في الكراج.",
|
||||
"impound_reclaim": "استعادة المركبة من الحجز",
|
||||
"impound": "حجز",
|
||||
"impound_not_enough": "سعر الاستعادة هو $%d، ليس لديك ما يكفي!",
|
||||
"impound_paid": "تم دفع $%d لاستعادة المركبة!",
|
||||
"success": "نجاح",
|
||||
"staff_action": "إجراء من الموظفين",
|
||||
"staff_money_removed": "تم إزالة $%d (%s) من %s",
|
||||
"user_money_removed": "تمت إزالة $%d من %s",
|
||||
"staff_money_added": "تمت إضافة $%d (%s) إلى %s",
|
||||
"user_money_added": "تمت إضافة $%d إلى %s",
|
||||
"staff_money_set": "تم ضبط %s (%s) على $%d",
|
||||
"user_money_set": "تم ضبط %s على $%d",
|
||||
"job_updated_noti": "تم تحديث الوظيفة إلى %s، الرتبة %s.",
|
||||
"group_added_noti": "تمت إضافتك إلى مجموعة %s، الرتبة %s.",
|
||||
"group_removed_noti": "تمت إزالتك من مجموعة %s.",
|
||||
"no_player_found": "لم يتم العثور على لاعب",
|
||||
"not_enough_cash": "ليس لديك ما يكفي من النقود",
|
||||
"player_not_nearby": "اللاعب ليس بالقرب منك",
|
||||
"cant_give_money": "تعذر إعطاء المال",
|
||||
"money_received": "تم استلام المال",
|
||||
"money_received2": "تم استلام $%d نقدًا",
|
||||
"money_given": "تم إعطاء المال",
|
||||
"money_given2": "لقد أعطيت $%d نقدًا",
|
||||
"player_in_veh": "اللاعب داخل المركبة!",
|
||||
"perfect": "ممتاز",
|
||||
"good": "جيد",
|
||||
"bad": "سيء",
|
||||
"very_bad": "سيء جدًا",
|
||||
"veh_make_brand": "الصانع",
|
||||
"veh_model": "الطراز",
|
||||
"veh_plate": "اللوحة",
|
||||
"engine_status": "حالة المحرك",
|
||||
"vehicle": "مركبة",
|
||||
"not_found": "غير موجود",
|
||||
"park_veh": "ركن المركبة",
|
||||
"no_vehs_found": "لم يتم العثور على مركبات",
|
||||
"vehicle_impound": "حجز المركبة",
|
||||
"parking_garage": "كراج سيارات",
|
||||
"impound_w_location": "الحجز (%s)",
|
||||
"garage_w_location": "كراج السيارات (%s)",
|
||||
"view_impounded_vehs": "عرض المركبات المحجوزة",
|
||||
"view_garage": "عرض الكراج",
|
||||
"personal_vehicle": "مركبة شخصية",
|
||||
"keybind_carkey": "فتح/قفل المركبة (نقر مزدوج)",
|
||||
"progress_hotwiring": "تشغيل المركبة بدون مفتاح",
|
||||
"cruise_control": "التحكم بالسرعة",
|
||||
"cruise_control_enabled": "تم تفعيل التحكم بالسرعة",
|
||||
"cruise_control_disabled": "تم تعطيل التحكم بالسرعة",
|
||||
"cruise_control_toggle": "تبديل التحكم بالسرعة",
|
||||
"cruise_control_increase": "زيادة سرعة التحكم",
|
||||
"seat_shuffle": "تبديل مقعد المركبة",
|
||||
"discord_text": "تلعب: %s كـ %s %s",
|
||||
"discord_text_small": "تلعب كـ: %s %s",
|
||||
"discord_text_server": "تلعب: %s",
|
||||
"pause_menu_cash": "نقد: $%d",
|
||||
"pause_menu_bank": "البنك: $%d",
|
||||
"veh_color_black": "أسود",
|
||||
"veh_color_silver": "فضي",
|
||||
"veh_color_grey": "رمادي",
|
||||
"veh_color_metal": "معدني",
|
||||
"veh_color_graphite": "جرافيت",
|
||||
"veh_color_red": "أحمر",
|
||||
"veh_color_orange": "برتقالي",
|
||||
"veh_color_gold": "ذهبي",
|
||||
"veh_color_yellow": "أصفر",
|
||||
"veh_color_green": "أخضر",
|
||||
"veh_color_blue": "أزرق",
|
||||
"veh_color_bblue": "أزرق فاتح",
|
||||
"veh_color_bronze": "برونزي",
|
||||
"veh_color_lime": "ليموني",
|
||||
"veh_color_champagne": "شامبانيا",
|
||||
"veh_color_beige": "بيج",
|
||||
"veh_color_ivory": "عاجي",
|
||||
"veh_color_brown": "بني",
|
||||
"veh_color_beechwood": "خشب الزان",
|
||||
"veh_color_sand": "رملي",
|
||||
"veh_color_cream": "كريمي",
|
||||
"veh_color_white": "أبيض",
|
||||
"veh_color_steel": "فولاذي",
|
||||
"veh_color_aluminium": "ألمنيوم",
|
||||
"veh_color_chrome": "كروم",
|
||||
"veh_color_pink": "وردي",
|
||||
"veh_color_purple": "بنفسجي",
|
||||
"veh_color_tan": "قمحي",
|
||||
"veh_color_alloy": "سبيكة",
|
||||
"veh_class_compact": "مدمجة",
|
||||
"veh_class_sedan": "سيدان",
|
||||
"veh_class_suv": "دفع رباعي",
|
||||
"veh_class_coupe": "كوبيه",
|
||||
"veh_class_muscle": "عضلية",
|
||||
"veh_class_sports_classic": "رياضية كلاسيكية",
|
||||
"veh_class_sport": "رياضية",
|
||||
"veh_class_super": "سوبر",
|
||||
"veh_class_motorcycle": "دراجة نارية",
|
||||
"veh_class_off_road": "طرق وعرة",
|
||||
"veh_class_industrial": "صناعية",
|
||||
"veh_class_utility": "خدمية",
|
||||
"veh_class_van": "فان",
|
||||
"veh_class_cycle": "دراجة",
|
||||
"veh_class_boat": "قارب",
|
||||
"veh_class_helicopter": "مروحية",
|
||||
"veh_class_plane": "طائرة",
|
||||
"veh_class_service": "خدمة",
|
||||
"veh_class_emergency": "طوارئ",
|
||||
"veh_class_military": "عسكرية",
|
||||
"veh_class_commercial": "تجارية",
|
||||
"veh_class_train": "قطار",
|
||||
"veh_class_open_wheel": "مفتوح العجلات"
|
||||
}
|
||||
134
locales/da.json
Normal file
134
locales/da.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Din Discord blev ikke fundet, deltag i vores Discord her: %s.",
|
||||
"connecting": "Forbinder...",
|
||||
"identifier_not_found": "Din %s blev ikke fundet.",
|
||||
"deposit": "Indsæt",
|
||||
"withdraw": "Hæv",
|
||||
"vehicle_ownership_transfered": "Ejerskab overført",
|
||||
"vehicle_ownership_transfered2": "Ejerskab af køretøj %s er blevet overført.",
|
||||
"vehicle_ownership_received": "Ejerskab modtaget",
|
||||
"vehicle_ownership_received2": "Modtog ejerskab af køretøj %s.",
|
||||
"keys_shared": "Nøgler delt",
|
||||
"keys_shared2": "Du har delt bilnøgler med %s.",
|
||||
"keys_received": "Nøgler modtaget",
|
||||
"keys_received2": "Du har modtaget bilnøgler fra %s.",
|
||||
"no_signal": "Ingen signal",
|
||||
"veh_key_disabled": "Bilnøgle deaktiveret.",
|
||||
"veh_key_disabled2": "Denne bilnøgle er blevet deaktiveret og kan ikke bruges længere.",
|
||||
"veh_far_away": "Køretøj for langt væk.",
|
||||
"veh_locked": "LÅST",
|
||||
"veh_locked2": "Dit køretøj er nu låst.",
|
||||
"veh_unlocked": "ULÅST",
|
||||
"veh_unlocked2": "Dit køretøj er nu ulåst.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "Ingen ejet køretøj fundet i nærheden.",
|
||||
"veh_stored_in_garage": "Køretøj gemt i garage.",
|
||||
"impound_reclaim": "Hent køretøj fra beslaglæggelse",
|
||||
"impound": "Beslaglæggelse",
|
||||
"impound_not_enough": "Prisen for at hente er %d kr, du har ikke nok!",
|
||||
"impound_paid": "Betalt %d kr for at hente køretøj!",
|
||||
"success": "succes",
|
||||
"staff_action": "Personalehandling",
|
||||
"staff_money_removed": "Fjernede %d kr (%s) fra %s",
|
||||
"user_money_removed": "fjernede %d kr fra %s",
|
||||
"staff_money_added": "Tilføjede %d kr (%s) til %s",
|
||||
"user_money_added": "tilføjede %d kr til %s",
|
||||
"staff_money_set": "Satte %s's (%s) til %d kr",
|
||||
"user_money_set": "satte %s til %d kr",
|
||||
"job_updated_noti": "Job opdateret til %s, rang %s.",
|
||||
"group_added_noti": "Tilføjet til gruppe %s, rang %s.",
|
||||
"group_removed_noti": "Fjernet fra gruppe %s.",
|
||||
"no_player_found": "Ingen spiller fundet",
|
||||
"not_enough_cash": "Du har ikke nok kontanter",
|
||||
"player_not_nearby": "Spiller ikke i nærheden",
|
||||
"cant_give_money": "Kunne ikke give penge",
|
||||
"money_received": "Penge modtaget",
|
||||
"money_received2": "Modtog %d kr i kontanter",
|
||||
"money_given": "Penge givet",
|
||||
"money_given2": "Du gav %d kr i kontanter",
|
||||
"player_in_veh": "Spiller i køretøj!",
|
||||
"perfect": "Perfekt",
|
||||
"good": "God",
|
||||
"bad": "Dårlig",
|
||||
"very_bad": "Meget dårlig",
|
||||
"veh_make_brand": "Mærke",
|
||||
"veh_model": "Model",
|
||||
"veh_plate": "Nummerplade",
|
||||
"engine_status": "Motorstatus",
|
||||
"vehicle": "Køretøj",
|
||||
"not_found": "ikke fundet",
|
||||
"park_veh": "Parkér køretøj",
|
||||
"no_vehs_found": "Ingen køretøjer fundet",
|
||||
"vehicle_impound": "Køretøj beslaglagt",
|
||||
"parking_garage": "Parkeringsgarage",
|
||||
"impound_w_location": "Beslaglæggelse (%s)",
|
||||
"garage_w_location": "Parkeringsgarage (%s)",
|
||||
"view_impounded_vehs": "Se beslaglagte køretøjer",
|
||||
"view_garage": "Se garage",
|
||||
"personal_vehicle": "Personligt køretøj",
|
||||
"keybind_carkey": "Lås/lås op for køretøj (dobbeltklik)",
|
||||
"progress_hotwiring": "Koble tænding",
|
||||
"cruise_control": "Fartpilot",
|
||||
"cruise_control_enabled": "Fartpilot aktiveret",
|
||||
"cruise_control_disabled": "Fartpilot deaktiveret",
|
||||
"cruise_control_toggle": "Skift fartpilot",
|
||||
"cruise_control_increase": "Øget fartpilot hastighed",
|
||||
"seat_shuffle": "Skift sæde",
|
||||
"discord_text": "Spiller: %s som %s %s",
|
||||
"discord_text_small": "Spiller som: %s %s",
|
||||
"discord_text_server": "Spiller: %s",
|
||||
"pause_menu_cash": "Kontanter: %d",
|
||||
"pause_menu_bank": "Bank: %d",
|
||||
"veh_color_black": "Sort",
|
||||
"veh_color_silver": "Sølv",
|
||||
"veh_color_grey": "Grå",
|
||||
"veh_color_metal": "Metal",
|
||||
"veh_color_graphite": "Grafit",
|
||||
"veh_color_red": "Rød",
|
||||
"veh_color_orange": "Orange",
|
||||
"veh_color_gold": "Guld",
|
||||
"veh_color_yellow": "Gul",
|
||||
"veh_color_green": "Grøn",
|
||||
"veh_color_blue": "Blå",
|
||||
"veh_color_bblue": "Lyseblå",
|
||||
"veh_color_bronze": "Bronze",
|
||||
"veh_color_lime": "Lime",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Elfenben",
|
||||
"veh_color_brown": "Brun",
|
||||
"veh_color_beechwood": "Bøgetræ",
|
||||
"veh_color_sand": "Sand",
|
||||
"veh_color_cream": "Creme",
|
||||
"veh_color_white": "Hvid",
|
||||
"veh_color_steel": "Stål",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Krom",
|
||||
"veh_color_pink": "Pink",
|
||||
"veh_color_purple": "Lilla",
|
||||
"veh_color_tan": "Tan",
|
||||
"veh_color_alloy": "Legering",
|
||||
"veh_class_compact": "Kompakt",
|
||||
"veh_class_sedan": "Sedan",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupé",
|
||||
"veh_class_muscle": "Muskelbil",
|
||||
"veh_class_sports_classic": "Klassisk sportsvogn",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Superbil",
|
||||
"veh_class_motorcycle": "Motorcykel",
|
||||
"veh_class_off_road": "Off-road",
|
||||
"veh_class_industrial": "Industriel",
|
||||
"veh_class_utility": "Utility",
|
||||
"veh_class_van": "Varevogn",
|
||||
"veh_class_cycle": "Cykel",
|
||||
"veh_class_boat": "Båd",
|
||||
"veh_class_helicopter": "Helikopter",
|
||||
"veh_class_plane": "Fly",
|
||||
"veh_class_service": "Service",
|
||||
"veh_class_emergency": "Nød",
|
||||
"veh_class_military": "Militær",
|
||||
"veh_class_commercial": "Kommerciel",
|
||||
"veh_class_train": "Tog",
|
||||
"veh_class_open_wheel": "Åben hjul"
|
||||
}
|
||||
134
locales/de.json
Normal file
134
locales/de.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Dein Discord wurde nicht gefunden, trete unserem Discord hier bei: %s.",
|
||||
"connecting": "Verbinde...",
|
||||
"identifier_not_found": "Dein %s wurde nicht gefunden.",
|
||||
"deposit": "Einzahlen",
|
||||
"withdraw": "Abheben",
|
||||
"vehicle_ownership_transfered": "Besitz übertragen",
|
||||
"vehicle_ownership_transfered2": "Fahrzeugbesitz von %s wurde übertragen.",
|
||||
"vehicle_ownership_received": "Besitz erhalten",
|
||||
"vehicle_ownership_received2": "Fahrzeugbesitz von %s erhalten.",
|
||||
"keys_shared": "Schlüssel geteilt",
|
||||
"keys_shared2": "Du hast die Fahrzeugschlüssel an %s gegeben.",
|
||||
"keys_received": "Schlüssel erhalten",
|
||||
"keys_received2": "Du hast die Fahrzeugschlüssel von %s erhalten.",
|
||||
"no_signal": "Kein Signal",
|
||||
"veh_key_disabled": "Fahrzeugschlüssel deaktiviert.",
|
||||
"veh_key_disabled2": "Dieser Fahrzeugschlüssel wurde deaktiviert und kann nicht mehr verwendet werden.",
|
||||
"veh_far_away": "Fahrzeug zu weit entfernt.",
|
||||
"veh_locked": "GESCHLOSSEN",
|
||||
"veh_locked2": "Dein Fahrzeug ist jetzt abgeschlossen.",
|
||||
"veh_unlocked": "GEÖFFNET",
|
||||
"veh_unlocked2": "Dein Fahrzeug ist jetzt geöffnet.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "Kein eigenes Fahrzeug in der Nähe gefunden.",
|
||||
"veh_stored_in_garage": "Fahrzeug in Garage gespeichert.",
|
||||
"impound_reclaim": "Fahrzeug aus dem Abschlepphof zurückholen",
|
||||
"impound": "Abschlepphof",
|
||||
"impound_not_enough": "Der Preis zur Rückholung beträgt %d €, du hast nicht genug!",
|
||||
"impound_paid": "%d € bezahlt, um das Fahrzeug zurückzuholen!",
|
||||
"success": "Erfolg",
|
||||
"staff_action": "Mitarbeiteraktion",
|
||||
"staff_money_removed": "%d € (%s) von %s entfernt",
|
||||
"user_money_removed": "%d € von %s entfernt",
|
||||
"staff_money_added": "%d € (%s) zu %s hinzugefügt",
|
||||
"user_money_added": "%d € zu %s hinzugefügt",
|
||||
"staff_money_set": "%s's (%s) auf %d € gesetzt",
|
||||
"user_money_set": "%s auf %d € gesetzt",
|
||||
"job_updated_noti": "Job auf %s, Rang %s aktualisiert.",
|
||||
"group_added_noti": "Zur Gruppe %s, Rang %s hinzugefügt.",
|
||||
"group_removed_noti": "Aus der Gruppe %s entfernt.",
|
||||
"no_player_found": "Kein Spieler gefunden",
|
||||
"not_enough_cash": "Du hast nicht genug Bargeld",
|
||||
"player_not_nearby": "Spieler nicht in der Nähe",
|
||||
"cant_give_money": "Konnte kein Geld geben",
|
||||
"money_received": "Geld erhalten",
|
||||
"money_received2": "%d € in bar erhalten",
|
||||
"money_given": "Geld gegeben",
|
||||
"money_given2": "Du hast %d € in bar gegeben",
|
||||
"player_in_veh": "Spieler im Fahrzeug!",
|
||||
"perfect": "Perfekt",
|
||||
"good": "Gut",
|
||||
"bad": "Schlecht",
|
||||
"very_bad": "Sehr schlecht",
|
||||
"veh_make_brand": "Marke",
|
||||
"veh_model": "Modell",
|
||||
"veh_plate": "Kennzeichen",
|
||||
"engine_status": "Motorstatus",
|
||||
"vehicle": "Fahrzeug",
|
||||
"not_found": "nicht gefunden",
|
||||
"park_veh": "Fahrzeug parken",
|
||||
"no_vehs_found": "Keine Fahrzeuge gefunden",
|
||||
"vehicle_impound": "Fahrzeugabschlepphof",
|
||||
"parking_garage": "Parkgarage",
|
||||
"impound_w_location": "Abschlepphof (%s)",
|
||||
"garage_w_location": "Parkgarage (%s)",
|
||||
"view_impounded_vehs": "Abgeschleppte Fahrzeuge anzeigen",
|
||||
"view_garage": "Garage anzeigen",
|
||||
"personal_vehicle": "Privatfahrzeug",
|
||||
"keybind_carkey": "Fahrzeug auf-/zusperren (Doppelklick)",
|
||||
"progress_hotwiring": "Kurzschließen",
|
||||
"cruise_control": "Tempomat",
|
||||
"cruise_control_enabled": "Tempomat aktiviert",
|
||||
"cruise_control_disabled": "Tempomat deaktiviert",
|
||||
"cruise_control_toggle": "Tempomat umschalten",
|
||||
"cruise_control_increase": "Tempomatgeschwindigkeit erhöht",
|
||||
"seat_shuffle": "Sitz wechseln",
|
||||
"discord_text": "Spielt: %s als %s %s",
|
||||
"discord_text_small": "Spielt als: %s %s",
|
||||
"discord_text_server": "Spielt: %s",
|
||||
"pause_menu_cash": "Bargeld: €%d",
|
||||
"pause_menu_bank": "Bank: €%d",
|
||||
"veh_color_black": "Schwarz",
|
||||
"veh_color_silver": "Silber",
|
||||
"veh_color_grey": "Grau",
|
||||
"veh_color_metal": "Metall",
|
||||
"veh_color_graphite": "Graphit",
|
||||
"veh_color_red": "Rot",
|
||||
"veh_color_orange": "Orange",
|
||||
"veh_color_gold": "Gold",
|
||||
"veh_color_yellow": "Gelb",
|
||||
"veh_color_green": "Grün",
|
||||
"veh_color_blue": "Blau",
|
||||
"veh_color_bblue": "Hellblau",
|
||||
"veh_color_bronze": "Bronze",
|
||||
"veh_color_lime": "Limette",
|
||||
"veh_color_champagne": "Champagner",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Elfenbein",
|
||||
"veh_color_brown": "Braun",
|
||||
"veh_color_beechwood": "Buchenholz",
|
||||
"veh_color_sand": "Sand",
|
||||
"veh_color_cream": "Creme",
|
||||
"veh_color_white": "Weiß",
|
||||
"veh_color_steel": "Stahl",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Chrom",
|
||||
"veh_color_pink": "Pink",
|
||||
"veh_color_purple": "Lila",
|
||||
"veh_color_tan": "Hellbraun",
|
||||
"veh_color_alloy": "Legierung",
|
||||
"veh_class_compact": "Kompakt",
|
||||
"veh_class_sedan": "Limousine",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupé",
|
||||
"veh_class_muscle": "Muscle-Car",
|
||||
"veh_class_sports_classic": "Klassischer Sportwagen",
|
||||
"veh_class_sport": "Sportwagen",
|
||||
"veh_class_super": "Supersportwagen",
|
||||
"veh_class_motorcycle": "Motorrad",
|
||||
"veh_class_off_road": "Geländewagen",
|
||||
"veh_class_industrial": "Industriefahrzeug",
|
||||
"veh_class_utility": "Nutzfahrzeug",
|
||||
"veh_class_van": "Van",
|
||||
"veh_class_cycle": "Fahrrad",
|
||||
"veh_class_boat": "Boot",
|
||||
"veh_class_helicopter": "Hubschrauber",
|
||||
"veh_class_plane": "Flugzeug",
|
||||
"veh_class_service": "Service",
|
||||
"veh_class_emergency": "Notfall",
|
||||
"veh_class_military": "Militär",
|
||||
"veh_class_commercial": "Gewerblich",
|
||||
"veh_class_train": "Zug",
|
||||
"veh_class_open_wheel": "Offenes Rad"
|
||||
}
|
||||
136
locales/en.json
Normal file
136
locales/en.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"not_in_discord": "Your discord was not found, join our discord here: %s.",
|
||||
"connecting": "Connecting...",
|
||||
"identifier_not_found": "Your %s was not found.",
|
||||
"deposit": "Deposit",
|
||||
"withdraw": "Withdraw",
|
||||
"vehicle_ownership_transfered": "Ownership transfered",
|
||||
"vehicle_ownership_transfered2": "Vehicle ownership of %s has been transfered.",
|
||||
"vehicle_ownership_received": "Ownership received",
|
||||
"vehicle_ownership_received2": "Received vehicle ownership of %s.",
|
||||
"keys_shared": "Keys shared",
|
||||
"keys_shared2": "You've shared vehicle keys to %s.",
|
||||
"keys_received": "Keys received",
|
||||
"keys_received2": "You've shared vehicle keys to %s.",
|
||||
"no_signal": "No signal",
|
||||
"veh_key_disabled": "Vehicle key disabled.",
|
||||
"veh_key_disabled2": "This vehicle key has been disabled and cannot be used anymore.",
|
||||
"veh_far_away": "Vehicle to far away.",
|
||||
"veh_locked": "LOCKED",
|
||||
"veh_locked2": "Your vehicle has now been locked.",
|
||||
"veh_unlocked": "UNLOCKED",
|
||||
"veh_unlocked2": "Your vehicle has now been unlocked.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "No owned vehicle found nearby.",
|
||||
"veh_stored_in_garage": "Vehicle stored in garage.",
|
||||
"impound_reclaim": "Vehicle impound reclaim",
|
||||
"impound": "Impound",
|
||||
"impound_not_enough": "Price to reclaim is $%d, you don't have enough!",
|
||||
"impound_paid": "Paid $%d to reclaim vehicle!",
|
||||
"success": "success",
|
||||
"staff_action": "Staff action",
|
||||
"staff_money_removed": "Removed $%d (%s) from %s",
|
||||
"user_money_removed": "removed $%d from %s",
|
||||
"staff_money_added": "Added $%d (%s) to %s",
|
||||
"user_money_added": "added $%d to %s",
|
||||
"staff_money_set": "Set %s's (%s) to $%d",
|
||||
"user_money_set": "set %s to $%d",
|
||||
"job_updated_noti": "Job updated to %s, rank %s.",
|
||||
"group_added_noti": "Added to group %s, rank %s.",
|
||||
"group_removed_noti": "Removed from group %s.",
|
||||
"no_player_found": "No player found",
|
||||
"not_enough_cash": "You don't have enough cash",
|
||||
"player_not_nearby": "Player is not nearby",
|
||||
"cant_give_money": "Couldn't give money",
|
||||
"money_received": "Money received",
|
||||
"money_received2": "Received $%d in cash",
|
||||
"money_given": "Money given",
|
||||
"money_given2": "You gave $%d in cash",
|
||||
"player_in_veh": "Player in vehicle!",
|
||||
"perfect": "Perfect",
|
||||
"good": "Good",
|
||||
"bad": "Bad",
|
||||
"very_bad": "Very bad",
|
||||
"veh_make_brand": "Make",
|
||||
"veh_model": "Model",
|
||||
"veh_plate": "Plate",
|
||||
"engine_status": "Engine status",
|
||||
"vehicle": "Vehicle",
|
||||
"not_found": "not found",
|
||||
"park_veh": "Park vehicle",
|
||||
"no_vehs_found": "No vehicles found",
|
||||
"vehicle_impound": "Vehicle impound",
|
||||
"parking_garage": "Parking garage",
|
||||
"impound_w_location": "Impound (%s)",
|
||||
"garage_w_location": "Parking garage (%s)",
|
||||
"view_impounded_vehs": "View impounded vehicles",
|
||||
"view_garage": "View garage",
|
||||
"personal_vehicle": "Personal vehicle",
|
||||
"group_vehicle": "Vehicle",
|
||||
"keybind_carkey": "Unlock/lock vehicle (double click)",
|
||||
"progress_hotwiring": "Hotwiring",
|
||||
"cruise_control": "Cruise control",
|
||||
"cruise_control_enabled": "Vehicle cruise control enabled",
|
||||
"cruise_control_disabled": "Vehicle cruise control disabled",
|
||||
"cruise_control_toggle": "Toggle vehicle cruise control",
|
||||
"cruise_control_increase": "Increased cruise control speed",
|
||||
"seat_shuffle": "Shuffle vehicle seat",
|
||||
"discord_text": "Playing: %s as %s %s",
|
||||
"discord_text_small": "Playing as: %s %s",
|
||||
"discord_text_server": "Playing: %s",
|
||||
"pause_menu_cash": "Cash: $%d",
|
||||
"pause_menu_bank": "Bank: $%d",
|
||||
|
||||
"veh_color_black": "Black",
|
||||
"veh_color_silver": "Silver",
|
||||
"veh_color_grey": "Grey",
|
||||
"veh_color_metal": "Metal",
|
||||
"veh_color_graphite": "Graphite",
|
||||
"veh_color_red": "Red",
|
||||
"veh_color_orange": "Orange",
|
||||
"veh_color_gold": "Gold",
|
||||
"veh_color_yellow": "Yellow",
|
||||
"veh_color_green": "Green",
|
||||
"veh_color_blue": "Blue",
|
||||
"veh_color_bblue": "Bblue",
|
||||
"veh_color_bronze": "Bronze",
|
||||
"veh_color_lime": "Lime",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Ivory",
|
||||
"veh_color_brown": "Brown",
|
||||
"veh_color_beechwood": "Beechwood",
|
||||
"veh_color_sand": "Sand",
|
||||
"veh_color_cream": "Cream",
|
||||
"veh_color_white": "White",
|
||||
"veh_color_steel": "Steel",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Chrome",
|
||||
"veh_color_pink": "Pink",
|
||||
"veh_color_purple": "Purple",
|
||||
"veh_color_tan": "Tan",
|
||||
"veh_color_alloy": "Alloy",
|
||||
"veh_class_compact": "Compact",
|
||||
"veh_class_sedan": "Sedan",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupe",
|
||||
"veh_class_muscle": "Muscle",
|
||||
"veh_class_sports_classic": "Sports Classic",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Super",
|
||||
"veh_class_motorcycle": "Motorcycle",
|
||||
"veh_class_off_road": "Off-road",
|
||||
"veh_class_industrial": "Industrial",
|
||||
"veh_class_utility": "Utility",
|
||||
"veh_class_van": "Van",
|
||||
"veh_class_cycle": "Cycle",
|
||||
"veh_class_boat": "Boat",
|
||||
"veh_class_helicopter": "Helicopter",
|
||||
"veh_class_plane": "Plane",
|
||||
"veh_class_service": "Service",
|
||||
"veh_class_emergency": "Emergency",
|
||||
"veh_class_military": "Military",
|
||||
"veh_class_commercial": "Commercial",
|
||||
"veh_class_train": "Train",
|
||||
"veh_class_open_wheel": "Open wheel"
|
||||
}
|
||||
134
locales/es.json
Normal file
134
locales/es.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "No se encontró tu Discord, únete a nuestro Discord aquí: %s.",
|
||||
"connecting": "Conectando...",
|
||||
"identifier_not_found": "No se encontró tu %s.",
|
||||
"deposit": "Depositar",
|
||||
"withdraw": "Retirar",
|
||||
"vehicle_ownership_transfered": "Propiedad transferida",
|
||||
"vehicle_ownership_transfered2": "La propiedad del vehículo %s ha sido transferida.",
|
||||
"vehicle_ownership_received": "Propiedad recibida",
|
||||
"vehicle_ownership_received2": "Has recibido la propiedad del vehículo %s.",
|
||||
"keys_shared": "Llaves compartidas",
|
||||
"keys_shared2": "Has compartido las llaves del vehículo con %s.",
|
||||
"keys_received": "Llaves recibidas",
|
||||
"keys_received2": "Has recibido las llaves del vehículo de %s.",
|
||||
"no_signal": "Sin señal",
|
||||
"veh_key_disabled": "Llave del vehículo desactivada.",
|
||||
"veh_key_disabled2": "Esta llave ha sido desactivada y no se puede usar más.",
|
||||
"veh_far_away": "Vehículo demasiado lejos.",
|
||||
"veh_locked": "BLOQUEADO",
|
||||
"veh_locked2": "Tu vehículo ha sido bloqueado.",
|
||||
"veh_unlocked": "DESBLOQUEADO",
|
||||
"veh_unlocked2": "Tu vehículo ha sido desbloqueado.",
|
||||
"garage": "Garaje",
|
||||
"no_owned_veh_nearby": "No se encontró un vehículo propio cerca.",
|
||||
"veh_stored_in_garage": "Vehículo guardado en el garaje.",
|
||||
"impound_reclaim": "Recuperar vehículo del depósito",
|
||||
"impound": "Depósito",
|
||||
"impound_not_enough": "El precio para recuperar es %d €, ¡no tienes suficiente!",
|
||||
"impound_paid": "¡Has pagado %d € para recuperar el vehículo!",
|
||||
"success": "Éxito",
|
||||
"staff_action": "Acción del personal",
|
||||
"staff_money_removed": "Se retiraron %d € (%s) de %s",
|
||||
"user_money_removed": "se retiraron %d € de %s",
|
||||
"staff_money_added": "Se añadieron %d € (%s) a %s",
|
||||
"user_money_added": "se añadieron %d € a %s",
|
||||
"staff_money_set": "Se estableció %s's (%s) en %d €",
|
||||
"user_money_set": "se estableció %s en %d €",
|
||||
"job_updated_noti": "Trabajo actualizado a %s, rango %s.",
|
||||
"group_added_noti": "Añadido al grupo %s, rango %s.",
|
||||
"group_removed_noti": "Eliminado del grupo %s.",
|
||||
"no_player_found": "No se encontró jugador",
|
||||
"not_enough_cash": "No tienes suficiente dinero en efectivo",
|
||||
"player_not_nearby": "El jugador no está cerca",
|
||||
"cant_give_money": "No se pudo dar dinero",
|
||||
"money_received": "Dinero recibido",
|
||||
"money_received2": "Has recibido %d € en efectivo",
|
||||
"money_given": "Dinero entregado",
|
||||
"money_given2": "Has dado %d € en efectivo",
|
||||
"player_in_veh": "¡Jugador en el vehículo!",
|
||||
"perfect": "Perfecto",
|
||||
"good": "Bueno",
|
||||
"bad": "Malo",
|
||||
"very_bad": "Muy malo",
|
||||
"veh_make_brand": "Marca",
|
||||
"veh_model": "Modelo",
|
||||
"veh_plate": "Matrícula",
|
||||
"engine_status": "Estado del motor",
|
||||
"vehicle": "Vehículo",
|
||||
"not_found": "no encontrado",
|
||||
"park_veh": "Aparcar vehículo",
|
||||
"no_vehs_found": "No se encontraron vehículos",
|
||||
"vehicle_impound": "Depósito de vehículos",
|
||||
"parking_garage": "Aparcamiento",
|
||||
"impound_w_location": "Depósito (%s)",
|
||||
"garage_w_location": "Aparcamiento (%s)",
|
||||
"view_impounded_vehs": "Ver vehículos en el depósito",
|
||||
"view_garage": "Ver garaje",
|
||||
"personal_vehicle": "Vehículo personal",
|
||||
"keybind_carkey": "Bloquear/desbloquear vehículo (doble clic)",
|
||||
"progress_hotwiring": "Forzando encendido",
|
||||
"cruise_control": "Control de crucero",
|
||||
"cruise_control_enabled": "Control de crucero activado",
|
||||
"cruise_control_disabled": "Control de crucero desactivado",
|
||||
"cruise_control_toggle": "Alternar control de crucero",
|
||||
"cruise_control_increase": "Velocidad de control de crucero aumentada",
|
||||
"seat_shuffle": "Cambiar de asiento",
|
||||
"discord_text": "Jugando: %s como %s %s",
|
||||
"discord_text_small": "Jugando como: %s %s",
|
||||
"discord_text_server": "Jugando: %s",
|
||||
"pause_menu_cash": "Efectivo: €%d",
|
||||
"pause_menu_bank": "Banco: €%d",
|
||||
"veh_color_black": "Negro",
|
||||
"veh_color_silver": "Plateado",
|
||||
"veh_color_grey": "Gris",
|
||||
"veh_color_metal": "Metal",
|
||||
"veh_color_graphite": "Grafito",
|
||||
"veh_color_red": "Rojo",
|
||||
"veh_color_orange": "Naranja",
|
||||
"veh_color_gold": "Oro",
|
||||
"veh_color_yellow": "Amarillo",
|
||||
"veh_color_green": "Verde",
|
||||
"veh_color_blue": "Azul",
|
||||
"veh_color_bblue": "Azul claro",
|
||||
"veh_color_bronze": "Bronce",
|
||||
"veh_color_lime": "Lima",
|
||||
"veh_color_champagne": "Champán",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Marfil",
|
||||
"veh_color_brown": "Marrón",
|
||||
"veh_color_beechwood": "Haya",
|
||||
"veh_color_sand": "Arena",
|
||||
"veh_color_cream": "Crema",
|
||||
"veh_color_white": "Blanco",
|
||||
"veh_color_steel": "Acero",
|
||||
"veh_color_aluminium": "Aluminio",
|
||||
"veh_color_chrome": "Cromo",
|
||||
"veh_color_pink": "Rosa",
|
||||
"veh_color_purple": "Púrpura",
|
||||
"veh_color_tan": "Bronceado",
|
||||
"veh_color_alloy": "Aleación",
|
||||
"veh_class_compact": "Compacto",
|
||||
"veh_class_sedan": "Sedán",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupé",
|
||||
"veh_class_muscle": "Muscle car",
|
||||
"veh_class_sports_classic": "Deportivo clásico",
|
||||
"veh_class_sport": "Deportivo",
|
||||
"veh_class_super": "Superdeportivo",
|
||||
"veh_class_motorcycle": "Motocicleta",
|
||||
"veh_class_off_road": "Todoterreno",
|
||||
"veh_class_industrial": "Industrial",
|
||||
"veh_class_utility": "Utilitario",
|
||||
"veh_class_van": "Furgoneta",
|
||||
"veh_class_cycle": "Bicicleta",
|
||||
"veh_class_boat": "Barco",
|
||||
"veh_class_helicopter": "Helicóptero",
|
||||
"veh_class_plane": "Avión",
|
||||
"veh_class_service": "Servicio",
|
||||
"veh_class_emergency": "Emergencia",
|
||||
"veh_class_military": "Militar",
|
||||
"veh_class_commercial": "Comercial",
|
||||
"veh_class_train": "Tren",
|
||||
"veh_class_open_wheel": "Rueda abierta"
|
||||
}
|
||||
134
locales/fi.json
Normal file
134
locales/fi.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Discord-tiliäsi ei löytynyt, liity Discordiimme täällä: %s.",
|
||||
"connecting": "Yhdistetään...",
|
||||
"identifier_not_found": "%s-tunnistetta ei löytynyt.",
|
||||
"deposit": "Talleta",
|
||||
"withdraw": "Nosta",
|
||||
"vehicle_ownership_transfered": "Omistajuus siirretty",
|
||||
"vehicle_ownership_transfered2": "Ajoneuvon %s omistajuus on siirretty.",
|
||||
"vehicle_ownership_received": "Omistajuus vastaanotettu",
|
||||
"vehicle_ownership_received2": "Olet saanut ajoneuvon %s omistajuuden.",
|
||||
"keys_shared": "Avaimet jaettu",
|
||||
"keys_shared2": "Olet jakanut ajoneuvon avaimet käyttäjälle %s.",
|
||||
"keys_received": "Avaimet vastaanotettu",
|
||||
"keys_received2": "Olet saanut ajoneuvon avaimet käyttäjältä %s.",
|
||||
"no_signal": "Ei signaalia",
|
||||
"veh_key_disabled": "Ajoneuvoavain poistettu käytöstä.",
|
||||
"veh_key_disabled2": "Tämä ajoneuvoavain on poistettu käytöstä eikä sitä voi enää käyttää.",
|
||||
"veh_far_away": "Ajoneuvo liian kaukana.",
|
||||
"veh_locked": "LUKITTU",
|
||||
"veh_locked2": "Ajoneuvosi on nyt lukittu.",
|
||||
"veh_unlocked": "AVATTU",
|
||||
"veh_unlocked2": "Ajoneuvosi on nyt avattu.",
|
||||
"garage": "Autotalli",
|
||||
"no_owned_veh_nearby": "Lähistöltä ei löytynyt omistamaasi ajoneuvoa.",
|
||||
"veh_stored_in_garage": "Ajoneuvo tallennettu autotalliin.",
|
||||
"impound_reclaim": "Ajoneuvon takaisinlunastus varikolta",
|
||||
"impound": "Varikko",
|
||||
"impound_not_enough": "Takaisinlunastuksen hinta on %d €, sinulla ei ole tarpeeksi rahaa!",
|
||||
"impound_paid": "Maksettu %d € ajoneuvon takaisinlunastuksesta!",
|
||||
"success": "Onnistui",
|
||||
"staff_action": "Ylläpidon toimenpide",
|
||||
"staff_money_removed": "Poistettu %d € (%s) käyttäjältä %s",
|
||||
"user_money_removed": "poistettu %d € käyttäjältä %s",
|
||||
"staff_money_added": "Lisätty %d € (%s) käyttäjälle %s",
|
||||
"user_money_added": "lisätty %d € käyttäjälle %s",
|
||||
"staff_money_set": "Asetettu %s (%s) summaksi %d €",
|
||||
"user_money_set": "asetettu %s summaksi %d €",
|
||||
"job_updated_noti": "Työ päivitetty: %s, taso %s.",
|
||||
"group_added_noti": "Lisätty ryhmään %s, taso %s.",
|
||||
"group_removed_noti": "Poistettu ryhmästä %s.",
|
||||
"no_player_found": "Pelaajaa ei löytynyt",
|
||||
"not_enough_cash": "Sinulla ei ole tarpeeksi käteistä",
|
||||
"player_not_nearby": "Pelaaja ei ole lähellä",
|
||||
"cant_give_money": "Rahan antaminen epäonnistui",
|
||||
"money_received": "Raha vastaanotettu",
|
||||
"money_received2": "Vastaanotettu %d € käteisenä",
|
||||
"money_given": "Raha annettu",
|
||||
"money_given2": "Annoit %d € käteisenä",
|
||||
"player_in_veh": "Pelaaja ajoneuvossa!",
|
||||
"perfect": "Täydellinen",
|
||||
"good": "Hyvä",
|
||||
"bad": "Huono",
|
||||
"very_bad": "Todella huono",
|
||||
"veh_make_brand": "Merkki",
|
||||
"veh_model": "Malli",
|
||||
"veh_plate": "Rekisterikilpi",
|
||||
"engine_status": "Moottorin tila",
|
||||
"vehicle": "Ajoneuvo",
|
||||
"not_found": "ei löytynyt",
|
||||
"park_veh": "Pysäköi ajoneuvo",
|
||||
"no_vehs_found": "Ajoneuvoja ei löytynyt",
|
||||
"vehicle_impound": "Ajoneuvovarikko",
|
||||
"parking_garage": "Pysäköintihalli",
|
||||
"impound_w_location": "Varikko (%s)",
|
||||
"garage_w_location": "Pysäköintihalli (%s)",
|
||||
"view_impounded_vehs": "Näytä varikolla olevat ajoneuvot",
|
||||
"view_garage": "Näytä autotalli",
|
||||
"personal_vehicle": "Henkilökohtainen ajoneuvo",
|
||||
"keybind_carkey": "Lukitse/avaa ajoneuvo (kaksoisklikkaus)",
|
||||
"progress_hotwiring": "Käynnistetään luvatta",
|
||||
"cruise_control": "Vakionopeudensäädin",
|
||||
"cruise_control_enabled": "Vakionopeudensäädin käytössä",
|
||||
"cruise_control_disabled": "Vakionopeudensäädin pois käytöstä",
|
||||
"cruise_control_toggle": "Vaihda vakionopeudensäädin päälle/pois",
|
||||
"cruise_control_increase": "Vakionopeus kasvoi",
|
||||
"seat_shuffle": "Vaihda istumapaikkaa ajoneuvossa",
|
||||
"discord_text": "Pelaa: %s hahmona %s %s",
|
||||
"discord_text_small": "Pelaa hahmona: %s %s",
|
||||
"discord_text_server": "Pelaa: %s",
|
||||
"pause_menu_cash": "Käteinen: €%d",
|
||||
"pause_menu_bank": "Pankki: €%d",
|
||||
"veh_color_black": "Musta",
|
||||
"veh_color_silver": "Hopea",
|
||||
"veh_color_grey": "Harmaa",
|
||||
"veh_color_metal": "Metalli",
|
||||
"veh_color_graphite": "Grafiitti",
|
||||
"veh_color_red": "Punainen",
|
||||
"veh_color_orange": "Oranssi",
|
||||
"veh_color_gold": "Kulta",
|
||||
"veh_color_yellow": "Keltainen",
|
||||
"veh_color_green": "Vihreä",
|
||||
"veh_color_blue": "Sininen",
|
||||
"veh_color_bblue": "Vaaleansininen",
|
||||
"veh_color_bronze": "Pronssi",
|
||||
"veh_color_lime": "Lime",
|
||||
"veh_color_champagne": "Samppanja",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Norsunluu",
|
||||
"veh_color_brown": "Ruskea",
|
||||
"veh_color_beechwood": "Pyökki",
|
||||
"veh_color_sand": "Hiekka",
|
||||
"veh_color_cream": "Kerma",
|
||||
"veh_color_white": "Valkoinen",
|
||||
"veh_color_steel": "Teräs",
|
||||
"veh_color_aluminium": "Alumiini",
|
||||
"veh_color_chrome": "Kromi",
|
||||
"veh_color_pink": "Vaaleanpunainen",
|
||||
"veh_color_purple": "Violetti",
|
||||
"veh_color_tan": "Vaalea ruskea",
|
||||
"veh_color_alloy": "Seosmetalli",
|
||||
"veh_class_compact": "Pieni auto",
|
||||
"veh_class_sedan": "Sedan",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupe",
|
||||
"veh_class_muscle": "Muskeliauto",
|
||||
"veh_class_sports_classic": "Klassinen urheiluauto",
|
||||
"veh_class_sport": "Urheiluauto",
|
||||
"veh_class_super": "Superauto",
|
||||
"veh_class_motorcycle": "Moottoripyörä",
|
||||
"veh_class_off_road": "Maastoauto",
|
||||
"veh_class_industrial": "Teollisuusajoneuvo",
|
||||
"veh_class_utility": "Hyötyajoneuvo",
|
||||
"veh_class_van": "Pakettiauto",
|
||||
"veh_class_cycle": "Polkupyörä",
|
||||
"veh_class_boat": "Vene",
|
||||
"veh_class_helicopter": "Helikopteri",
|
||||
"veh_class_plane": "Lentokone",
|
||||
"veh_class_service": "Huoltoajoneuvo",
|
||||
"veh_class_emergency": "Hätäajoneuvo",
|
||||
"veh_class_military": "Sotilasajoneuvo",
|
||||
"veh_class_commercial": "Kaupallinen ajoneuvo",
|
||||
"veh_class_train": "Juna",
|
||||
"veh_class_open_wheel": "Avoin pyörä"
|
||||
}
|
||||
134
locales/fr.json
Normal file
134
locales/fr.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Votre Discord n'a pas été trouvé, rejoignez notre Discord ici : %s.",
|
||||
"connecting": "Connexion...",
|
||||
"identifier_not_found": "Votre %s n'a pas été trouvé.",
|
||||
"deposit": "Dépôt",
|
||||
"withdraw": "Retrait",
|
||||
"vehicle_ownership_transfered": "Propriété transférée",
|
||||
"vehicle_ownership_transfered2": "La propriété du véhicule %s a été transférée.",
|
||||
"vehicle_ownership_received": "Propriété reçue",
|
||||
"vehicle_ownership_received2": "Vous avez reçu la propriété du véhicule %s.",
|
||||
"keys_shared": "Clés partagées",
|
||||
"keys_shared2": "Vous avez partagé les clés du véhicule avec %s.",
|
||||
"keys_received": "Clés reçues",
|
||||
"keys_received2": "Vous avez reçu les clés du véhicule de %s.",
|
||||
"no_signal": "Pas de signal",
|
||||
"veh_key_disabled": "Clé de véhicule désactivée.",
|
||||
"veh_key_disabled2": "Cette clé de véhicule a été désactivée et ne peut plus être utilisée.",
|
||||
"veh_far_away": "Véhicule trop éloigné.",
|
||||
"veh_locked": "VERROUILLÉ",
|
||||
"veh_locked2": "Votre véhicule est maintenant verrouillé.",
|
||||
"veh_unlocked": "DÉVERROUILLÉ",
|
||||
"veh_unlocked2": "Votre véhicule est maintenant déverrouillé.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "Aucun véhicule possédé trouvé à proximité.",
|
||||
"veh_stored_in_garage": "Véhicule rangé dans le garage.",
|
||||
"impound_reclaim": "Récupération de véhicule mis en fourrière",
|
||||
"impound": "Fourrière",
|
||||
"impound_not_enough": "Le prix pour récupérer est de %d €, vous n'avez pas assez!",
|
||||
"impound_paid": "Vous avez payé %d € pour récupérer le véhicule!",
|
||||
"success": "succès",
|
||||
"staff_action": "Action du staff",
|
||||
"staff_money_removed": "%d € (%s) retiré de %s",
|
||||
"user_money_removed": "%d € retiré de %s",
|
||||
"staff_money_added": "%d € (%s) ajouté à %s",
|
||||
"user_money_added": "%d € ajouté à %s",
|
||||
"staff_money_set": "Solde de %s (%s) défini à %d €",
|
||||
"user_money_set": "Solde de %s défini à %d €",
|
||||
"job_updated_noti": "Emploi mis à jour en %s, grade %s.",
|
||||
"group_added_noti": "Ajouté au groupe %s, grade %s.",
|
||||
"group_removed_noti": "Retiré du groupe %s.",
|
||||
"no_player_found": "Aucun joueur trouvé",
|
||||
"not_enough_cash": "Vous n'avez pas assez d'argent liquide",
|
||||
"player_not_nearby": "Joueur non à proximité",
|
||||
"cant_give_money": "Impossible de donner de l'argent",
|
||||
"money_received": "Argent reçu",
|
||||
"money_received2": "Vous avez reçu %d € en liquide",
|
||||
"money_given": "Argent donné",
|
||||
"money_given2": "Vous avez donné %d € en liquide",
|
||||
"player_in_veh": "Joueur dans un véhicule!",
|
||||
"perfect": "Parfait",
|
||||
"good": "Bon",
|
||||
"bad": "Mauvais",
|
||||
"very_bad": "Très mauvais",
|
||||
"veh_make_brand": "Marque",
|
||||
"veh_model": "Modèle",
|
||||
"veh_plate": "Plaque",
|
||||
"engine_status": "État du moteur",
|
||||
"vehicle": "Véhicule",
|
||||
"not_found": "introuvable",
|
||||
"park_veh": "Garer le véhicule",
|
||||
"no_vehs_found": "Aucun véhicule trouvé",
|
||||
"vehicle_impound": "Fourrière",
|
||||
"parking_garage": "Garage de stationnement",
|
||||
"impound_w_location": "Fourrière (%s)",
|
||||
"garage_w_location": "Garage (%s)",
|
||||
"view_impounded_vehs": "Voir les véhicules en fourrière",
|
||||
"view_garage": "Voir le garage",
|
||||
"personal_vehicle": "Véhicule personnel",
|
||||
"keybind_carkey": "Verrouiller/déverrouiller le véhicule (double clic)",
|
||||
"progress_hotwiring": "Démarrage forcé",
|
||||
"cruise_control": "Régulateur de vitesse",
|
||||
"cruise_control_enabled": "Régulateur de vitesse activé",
|
||||
"cruise_control_disabled": "Régulateur de vitesse désactivé",
|
||||
"cruise_control_toggle": "Activer/désactiver le régulateur de vitesse",
|
||||
"cruise_control_increase": "Vitesse du régulateur augmentée",
|
||||
"seat_shuffle": "Changer de siège",
|
||||
"discord_text": "Joue : %s en tant que %s %s",
|
||||
"discord_text_small": "Joue en tant que : %s %s",
|
||||
"discord_text_server": "Joue : %s",
|
||||
"pause_menu_cash": "Argent liquide : €%d",
|
||||
"pause_menu_bank": "Banque : €%d",
|
||||
"veh_color_black": "Noir",
|
||||
"veh_color_silver": "Argent",
|
||||
"veh_color_grey": "Gris",
|
||||
"veh_color_metal": "Métal",
|
||||
"veh_color_graphite": "Graphite",
|
||||
"veh_color_red": "Rouge",
|
||||
"veh_color_orange": "Orange",
|
||||
"veh_color_gold": "Or",
|
||||
"veh_color_yellow": "Jaune",
|
||||
"veh_color_green": "Vert",
|
||||
"veh_color_blue": "Bleu",
|
||||
"veh_color_bblue": "Bleu clair",
|
||||
"veh_color_bronze": "Bronze",
|
||||
"veh_color_lime": "Citron vert",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Ivoire",
|
||||
"veh_color_brown": "Marron",
|
||||
"veh_color_beechwood": "Hêtre",
|
||||
"veh_color_sand": "Sable",
|
||||
"veh_color_cream": "Crème",
|
||||
"veh_color_white": "Blanc",
|
||||
"veh_color_steel": "Acier",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Chrome",
|
||||
"veh_color_pink": "Rose",
|
||||
"veh_color_purple": "Violet",
|
||||
"veh_color_tan": "Fauve",
|
||||
"veh_color_alloy": "Alliage",
|
||||
"veh_class_compact": "Compact",
|
||||
"veh_class_sedan": "Berline",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupé",
|
||||
"veh_class_muscle": "Muscle car",
|
||||
"veh_class_sports_classic": "Sport classique",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Super",
|
||||
"veh_class_motorcycle": "Moto",
|
||||
"veh_class_off_road": "Tout-terrain",
|
||||
"veh_class_industrial": "Industriel",
|
||||
"veh_class_utility": "Utilitaire",
|
||||
"veh_class_van": "Van",
|
||||
"veh_class_cycle": "Vélo",
|
||||
"veh_class_boat": "Bateau",
|
||||
"veh_class_helicopter": "Hélicoptère",
|
||||
"veh_class_plane": "Avion",
|
||||
"veh_class_service": "Service",
|
||||
"veh_class_emergency": "Urgence",
|
||||
"veh_class_military": "Militaire",
|
||||
"veh_class_commercial": "Commercial",
|
||||
"veh_class_train": "Train",
|
||||
"veh_class_open_wheel": "Monoplace"
|
||||
}
|
||||
134
locales/it.json
Normal file
134
locales/it.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Il tuo account Discord non è stato trovato, unisciti al nostro Discord qui: %s.",
|
||||
"connecting": "Connessione in corso...",
|
||||
"identifier_not_found": "Il tuo %s non è stato trovato.",
|
||||
"deposit": "Deposito",
|
||||
"withdraw": "Prelievo",
|
||||
"vehicle_ownership_transfered": "Proprietà trasferita",
|
||||
"vehicle_ownership_transfered2": "La proprietà del veicolo %s è stata trasferita.",
|
||||
"vehicle_ownership_received": "Proprietà ricevuta",
|
||||
"vehicle_ownership_received2": "Hai ricevuto la proprietà del veicolo %s.",
|
||||
"keys_shared": "Chiavi condivise",
|
||||
"keys_shared2": "Hai condiviso le chiavi del veicolo con %s.",
|
||||
"keys_received": "Chiavi ricevute",
|
||||
"keys_received2": "Hai ricevuto le chiavi del veicolo da %s.",
|
||||
"no_signal": "Nessun segnale",
|
||||
"veh_key_disabled": "Chiave del veicolo disattivata.",
|
||||
"veh_key_disabled2": "Questa chiave del veicolo è stata disattivata e non può più essere utilizzata.",
|
||||
"veh_far_away": "Veicolo troppo lontano.",
|
||||
"veh_locked": "CHIUSO",
|
||||
"veh_locked2": "Il tuo veicolo è ora chiuso.",
|
||||
"veh_unlocked": "APERTO",
|
||||
"veh_unlocked2": "Il tuo veicolo è ora aperto.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "Nessun veicolo di tua proprietà trovato nelle vicinanze.",
|
||||
"veh_stored_in_garage": "Veicolo riposto in garage.",
|
||||
"impound_reclaim": "Riscatto veicolo dal deposito",
|
||||
"impound": "Deposito",
|
||||
"impound_not_enough": "Il prezzo per riscattare è %d €, non hai abbastanza soldi!",
|
||||
"impound_paid": "Hai pagato %d € per riscattare il veicolo!",
|
||||
"success": "successo",
|
||||
"staff_action": "Azione staff",
|
||||
"staff_money_removed": "Rimossi %d € (%s) da %s",
|
||||
"user_money_removed": "rimossi %d € da %s",
|
||||
"staff_money_added": "Aggiunti %d € (%s) a %s",
|
||||
"user_money_added": "aggiunti %d € a %s",
|
||||
"staff_money_set": "Impostato il saldo di %s (%s) a %d €",
|
||||
"user_money_set": "impostato %s a %d €",
|
||||
"job_updated_noti": "Lavoro aggiornato a %s, grado %s.",
|
||||
"group_added_noti": "Aggiunto al gruppo %s, grado %s.",
|
||||
"group_removed_noti": "Rimosso dal gruppo %s.",
|
||||
"no_player_found": "Nessun giocatore trovato",
|
||||
"not_enough_cash": "Non hai abbastanza contanti",
|
||||
"player_not_nearby": "Il giocatore non è vicino",
|
||||
"cant_give_money": "Impossibile dare denaro",
|
||||
"money_received": "Denaro ricevuto",
|
||||
"money_received2": "Ricevuti %d € in contanti",
|
||||
"money_given": "Denaro dato",
|
||||
"money_given2": "Hai dato %d € in contanti",
|
||||
"player_in_veh": "Giocatore in veicolo!",
|
||||
"perfect": "Perfetto",
|
||||
"good": "Buono",
|
||||
"bad": "Scarso",
|
||||
"very_bad": "Molto scarso",
|
||||
"veh_make_brand": "Marca",
|
||||
"veh_model": "Modello",
|
||||
"veh_plate": "Targa",
|
||||
"engine_status": "Stato motore",
|
||||
"vehicle": "Veicolo",
|
||||
"not_found": "non trovato",
|
||||
"park_veh": "Parcheggia veicolo",
|
||||
"no_vehs_found": "Nessun veicolo trovato",
|
||||
"vehicle_impound": "Deposito veicoli",
|
||||
"parking_garage": "Parcheggio",
|
||||
"impound_w_location": "Deposito (%s)",
|
||||
"garage_w_location": "Parcheggio (%s)",
|
||||
"view_impounded_vehs": "Visualizza veicoli in deposito",
|
||||
"view_garage": "Visualizza garage",
|
||||
"personal_vehicle": "Veicolo personale",
|
||||
"keybind_carkey": "Apri/chiudi veicolo (doppio click)",
|
||||
"progress_hotwiring": "Avviamento forzato",
|
||||
"cruise_control": "Cruise control",
|
||||
"cruise_control_enabled": "Cruise control attivato",
|
||||
"cruise_control_disabled": "Cruise control disattivato",
|
||||
"cruise_control_toggle": "Attiva/disattiva cruise control",
|
||||
"cruise_control_increase": "Velocità cruise control aumentata",
|
||||
"seat_shuffle": "Cambia posto nel veicolo",
|
||||
"discord_text": "Giocando: %s come %s %s",
|
||||
"discord_text_small": "Giocando come: %s %s",
|
||||
"discord_text_server": "Giocando: %s",
|
||||
"pause_menu_cash": "Contanti: €%d",
|
||||
"pause_menu_bank": "Banca: €%d",
|
||||
"veh_color_black": "Nero",
|
||||
"veh_color_silver": "Argento",
|
||||
"veh_color_grey": "Grigio",
|
||||
"veh_color_metal": "Metallo",
|
||||
"veh_color_graphite": "Grafite",
|
||||
"veh_color_red": "Rosso",
|
||||
"veh_color_orange": "Arancione",
|
||||
"veh_color_gold": "Oro",
|
||||
"veh_color_yellow": "Giallo",
|
||||
"veh_color_green": "Verde",
|
||||
"veh_color_blue": "Blu",
|
||||
"veh_color_bblue": "Blu chiaro",
|
||||
"veh_color_bronze": "Bronzo",
|
||||
"veh_color_lime": "Lime",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Avorio",
|
||||
"veh_color_brown": "Marrone",
|
||||
"veh_color_beechwood": "Faggio",
|
||||
"veh_color_sand": "Sabbia",
|
||||
"veh_color_cream": "Crema",
|
||||
"veh_color_white": "Bianco",
|
||||
"veh_color_steel": "Acciaio",
|
||||
"veh_color_aluminium": "Alluminio",
|
||||
"veh_color_chrome": "Cromo",
|
||||
"veh_color_pink": "Rosa",
|
||||
"veh_color_purple": "Viola",
|
||||
"veh_color_tan": "Marrone chiaro",
|
||||
"veh_color_alloy": "Lega",
|
||||
"veh_class_compact": "Compatta",
|
||||
"veh_class_sedan": "Berlina",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupé",
|
||||
"veh_class_muscle": "Muscle car",
|
||||
"veh_class_sports_classic": "Sport classico",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Super",
|
||||
"veh_class_motorcycle": "Motocicletta",
|
||||
"veh_class_off_road": "Fuoristrada",
|
||||
"veh_class_industrial": "Industriale",
|
||||
"veh_class_utility": "Utility",
|
||||
"veh_class_van": "Furgone",
|
||||
"veh_class_cycle": "Bicicletta",
|
||||
"veh_class_boat": "Barca",
|
||||
"veh_class_helicopter": "Elicottero",
|
||||
"veh_class_plane": "Aereo",
|
||||
"veh_class_service": "Servizio",
|
||||
"veh_class_emergency": "Emergenza",
|
||||
"veh_class_military": "Militare",
|
||||
"veh_class_commercial": "Commerciale",
|
||||
"veh_class_train": "Treno",
|
||||
"veh_class_open_wheel": "Ruota scoperta"
|
||||
}
|
||||
134
locales/nl.json
Normal file
134
locales/nl.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Je Discord-account is niet gevonden, sluit je hier aan bij onze Discord: %s.",
|
||||
"connecting": "Verbinden...",
|
||||
"identifier_not_found": "Je %s is niet gevonden.",
|
||||
"deposit": "Storten",
|
||||
"withdraw": "Opnemen",
|
||||
"vehicle_ownership_transfered": "Eigendom overgedragen",
|
||||
"vehicle_ownership_transfered2": "Het eigendom van het voertuig %s is overgedragen.",
|
||||
"vehicle_ownership_received": "Eigendom ontvangen",
|
||||
"vehicle_ownership_received2": "Je hebt het eigendom van voertuig %s ontvangen.",
|
||||
"keys_shared": "Sleutels gedeeld",
|
||||
"keys_shared2": "Je hebt voertuig sleutels gedeeld met %s.",
|
||||
"keys_received": "Sleutels ontvangen",
|
||||
"keys_received2": "Je hebt voertuig sleutels ontvangen van %s.",
|
||||
"no_signal": "Geen signaal",
|
||||
"veh_key_disabled": "Voertuigsleutel uitgeschakeld.",
|
||||
"veh_key_disabled2": "Deze voertuigsleutel is uitgeschakeld en kan niet meer worden gebruikt.",
|
||||
"veh_far_away": "Voertuig te ver weg.",
|
||||
"veh_locked": "GESLOTEN",
|
||||
"veh_locked2": "Je voertuig is nu gesloten.",
|
||||
"veh_unlocked": "GEOPEND",
|
||||
"veh_unlocked2": "Je voertuig is nu geopend.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "Geen eigendom voertuig in de buurt gevonden.",
|
||||
"veh_stored_in_garage": "Voertuig opgeslagen in garage.",
|
||||
"impound_reclaim": "Voertuig terugvorderen uit beslag",
|
||||
"impound": "Beslag",
|
||||
"impound_not_enough": "Prijs voor terugvordering is €%d, je hebt niet genoeg!",
|
||||
"impound_paid": "€%d betaald om voertuig terug te vorderen!",
|
||||
"success": "succes",
|
||||
"staff_action": "Staf actie",
|
||||
"staff_money_removed": "€%d (%s) verwijderd van %s",
|
||||
"user_money_removed": "€%d verwijderd van %s",
|
||||
"staff_money_added": "€%d (%s) toegevoegd aan %s",
|
||||
"user_money_added": "€%d toegevoegd aan %s",
|
||||
"staff_money_set": "Saldo van %s (%s) ingesteld op €%d",
|
||||
"user_money_set": "Saldo van %s ingesteld op €%d",
|
||||
"job_updated_noti": "Baan bijgewerkt naar %s, rang %s.",
|
||||
"group_added_noti": "Toegevoegd aan groep %s, rang %s.",
|
||||
"group_removed_noti": "Verwijderd uit groep %s.",
|
||||
"no_player_found": "Geen speler gevonden",
|
||||
"not_enough_cash": "Je hebt niet genoeg contant geld",
|
||||
"player_not_nearby": "Speler is niet in de buurt",
|
||||
"cant_give_money": "Kon geen geld geven",
|
||||
"money_received": "Geld ontvangen",
|
||||
"money_received2": "€%d contant ontvangen",
|
||||
"money_given": "Geld gegeven",
|
||||
"money_given2": "Je hebt €%d contant gegeven",
|
||||
"player_in_veh": "Speler in voertuig!",
|
||||
"perfect": "Perfect",
|
||||
"good": "Goed",
|
||||
"bad": "Slecht",
|
||||
"very_bad": "Zeer slecht",
|
||||
"veh_make_brand": "Merk",
|
||||
"veh_model": "Model",
|
||||
"veh_plate": "Kenteken",
|
||||
"engine_status": "Motorstatus",
|
||||
"vehicle": "Voertuig",
|
||||
"not_found": "niet gevonden",
|
||||
"park_veh": "Voertuig parkeren",
|
||||
"no_vehs_found": "Geen voertuigen gevonden",
|
||||
"vehicle_impound": "Voertuigen in beslag",
|
||||
"parking_garage": "Parkeergarage",
|
||||
"impound_w_location": "Beslag (%s)",
|
||||
"garage_w_location": "Parkeergarage (%s)",
|
||||
"view_impounded_vehs": "Bekijk in beslag genomen voertuigen",
|
||||
"view_garage": "Bekijk garage",
|
||||
"personal_vehicle": "Persoonlijk voertuig",
|
||||
"keybind_carkey": "Voertuig openen/sluiten (dubbelklik)",
|
||||
"progress_hotwiring": "Hotwiring",
|
||||
"cruise_control": "Cruise control",
|
||||
"cruise_control_enabled": "Cruise control ingeschakeld",
|
||||
"cruise_control_disabled": "Cruise control uitgeschakeld",
|
||||
"cruise_control_toggle": "Cruise control aan/uit",
|
||||
"cruise_control_increase": "Cruise control snelheid verhoogd",
|
||||
"seat_shuffle": "Stoel wisselen",
|
||||
"discord_text": "Spelend: %s als %s %s",
|
||||
"discord_text_small": "Spelend als: %s %s",
|
||||
"discord_text_server": "Spelend: %s",
|
||||
"pause_menu_cash": "Contant: €%d",
|
||||
"pause_menu_bank": "Bank: €%d",
|
||||
"veh_color_black": "Zwart",
|
||||
"veh_color_silver": "Zilver",
|
||||
"veh_color_grey": "Grijs",
|
||||
"veh_color_metal": "Metaal",
|
||||
"veh_color_graphite": "Grafiet",
|
||||
"veh_color_red": "Rood",
|
||||
"veh_color_orange": "Oranje",
|
||||
"veh_color_gold": "Goud",
|
||||
"veh_color_yellow": "Geel",
|
||||
"veh_color_green": "Groen",
|
||||
"veh_color_blue": "Blauw",
|
||||
"veh_color_bblue": "Helderblauw",
|
||||
"veh_color_bronze": "Brons",
|
||||
"veh_color_lime": "Limoen",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Ivoor",
|
||||
"veh_color_brown": "Bruin",
|
||||
"veh_color_beechwood": "Beukenhout",
|
||||
"veh_color_sand": "Zand",
|
||||
"veh_color_cream": "Crème",
|
||||
"veh_color_white": "Wit",
|
||||
"veh_color_steel": "Staal",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Chroom",
|
||||
"veh_color_pink": "Roze",
|
||||
"veh_color_purple": "Paars",
|
||||
"veh_color_tan": "Bruin-beige",
|
||||
"veh_color_alloy": "Alloy",
|
||||
"veh_class_compact": "Compact",
|
||||
"veh_class_sedan": "Sedan",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupe",
|
||||
"veh_class_muscle": "Muscle",
|
||||
"veh_class_sports_classic": "Sport Classic",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Super",
|
||||
"veh_class_motorcycle": "Motorfiets",
|
||||
"veh_class_off_road": "Off-road",
|
||||
"veh_class_industrial": "Industrieel",
|
||||
"veh_class_utility": "Utility",
|
||||
"veh_class_van": "Bus",
|
||||
"veh_class_cycle": "Fiets",
|
||||
"veh_class_boat": "Boot",
|
||||
"veh_class_helicopter": "Helikopter",
|
||||
"veh_class_plane": "Vliegtuig",
|
||||
"veh_class_service": "Dienst",
|
||||
"veh_class_emergency": "Nooddienst",
|
||||
"veh_class_military": "Militair",
|
||||
"veh_class_commercial": "Commercieel",
|
||||
"veh_class_train": "Trein",
|
||||
"veh_class_open_wheel": "Open wiel"
|
||||
}
|
||||
134
locales/no.json
Normal file
134
locales/no.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Discorden din ble ikke funnet, bli med i vår Discord her: %s.",
|
||||
"connecting": "Kobler til...",
|
||||
"identifier_not_found": "Din %s ble ikke funnet.",
|
||||
"deposit": "Innskudd",
|
||||
"withdraw": "Ta ut",
|
||||
"vehicle_ownership_transfered": "Eierskap overført",
|
||||
"vehicle_ownership_transfered2": "Eierskapet til kjøretøyet %s har blitt overført.",
|
||||
"vehicle_ownership_received": "Eierskap mottatt",
|
||||
"vehicle_ownership_received2": "Mottatt eierskap til kjøretøyet %s.",
|
||||
"keys_shared": "Nøkler delt",
|
||||
"keys_shared2": "Du har delt kjøretøynøkler med %s.",
|
||||
"keys_received": "Nøkler mottatt",
|
||||
"keys_received2": "Du har mottatt kjøretøynøkler fra %s.",
|
||||
"no_signal": "Ingen signal",
|
||||
"veh_key_disabled": "Kjøretøynøkkel deaktivert.",
|
||||
"veh_key_disabled2": "Denne nøkkelen er deaktivert og kan ikke brukes lenger.",
|
||||
"veh_far_away": "Kjøretøyet er for langt unna.",
|
||||
"veh_locked": "LÅST",
|
||||
"veh_locked2": "Kjøretøyet ditt er nå låst.",
|
||||
"veh_unlocked": "ÅPEN",
|
||||
"veh_unlocked2": "Kjøretøyet ditt er nå åpnet.",
|
||||
"garage": "Garasje",
|
||||
"no_owned_veh_nearby": "Ingen eide kjøretøy i nærheten.",
|
||||
"veh_stored_in_garage": "Kjøretøy lagret i garasje.",
|
||||
"impound_reclaim": "Hent kjøretøy fra beslag",
|
||||
"impound": "Beslag",
|
||||
"impound_not_enough": "Pris for å hente er %d kr, du har ikke nok!",
|
||||
"impound_paid": "Betalt %d kr for å hente kjøretøy!",
|
||||
"success": "suksess",
|
||||
"staff_action": "Ansatt handling",
|
||||
"staff_money_removed": "Fjernet %d kr (%s) fra %s",
|
||||
"user_money_removed": "Fjernet %d kr fra %s",
|
||||
"staff_money_added": "Lagt til %d kr (%s) til %s",
|
||||
"user_money_added": "Lagt til %d kr til %s",
|
||||
"staff_money_set": "Stilt inn %s sin (%s) til %d kr",
|
||||
"user_money_set": "Stilt inn %s til %d kr",
|
||||
"job_updated_noti": "Jobb oppdatert til %s, rang %s.",
|
||||
"group_added_noti": "Lagt til gruppe %s, rang %s.",
|
||||
"group_removed_noti": "Fjernet fra gruppe %s.",
|
||||
"no_player_found": "Ingen spiller funnet",
|
||||
"not_enough_cash": "Du har ikke nok kontanter",
|
||||
"player_not_nearby": "Spiller er ikke i nærheten",
|
||||
"cant_give_money": "Kunne ikke gi penger",
|
||||
"money_received": "Penger mottatt",
|
||||
"money_received2": "Mottatt %d kr kontant",
|
||||
"money_given": "Penger gitt",
|
||||
"money_given2": "Du ga %d kr kontant",
|
||||
"player_in_veh": "Spiller i kjøretøy!",
|
||||
"perfect": "Perfekt",
|
||||
"good": "Bra",
|
||||
"bad": "Dårlig",
|
||||
"very_bad": "Veldig dårlig",
|
||||
"veh_make_brand": "Merke",
|
||||
"veh_model": "Modell",
|
||||
"veh_plate": "Skilt",
|
||||
"engine_status": "Motorstatus",
|
||||
"vehicle": "Kjøretøy",
|
||||
"not_found": "ikke funnet",
|
||||
"park_veh": "Parker kjøretøy",
|
||||
"no_vehs_found": "Ingen kjøretøy funnet",
|
||||
"vehicle_impound": "Kjøretøy beslag",
|
||||
"parking_garage": "Parkeringsgarasje",
|
||||
"impound_w_location": "Beslag (%s)",
|
||||
"garage_w_location": "Garasje (%s)",
|
||||
"view_impounded_vehs": "Se beslaglagte kjøretøy",
|
||||
"view_garage": "Se garasje",
|
||||
"personal_vehicle": "Personlig kjøretøy",
|
||||
"keybind_carkey": "Lås/åpne kjøretøy (dobbeltklikk)",
|
||||
"progress_hotwiring": "Tjuvstart",
|
||||
"cruise_control": "Cruise control",
|
||||
"cruise_control_enabled": "Cruise control aktivert",
|
||||
"cruise_control_disabled": "Cruise control deaktivert",
|
||||
"cruise_control_toggle": "Bytt cruise control",
|
||||
"cruise_control_increase": "Cruise control hastighet økt",
|
||||
"seat_shuffle": "Bytt plass i kjøretøy",
|
||||
"discord_text": "Spiller: %s som %s %s",
|
||||
"discord_text_small": "Spiller som: %s %s",
|
||||
"discord_text_server": "Spiller: %s",
|
||||
"pause_menu_cash": "Kontanter: %d",
|
||||
"pause_menu_bank": "Bank: %d",
|
||||
"veh_color_black": "Svart",
|
||||
"veh_color_silver": "Sølv",
|
||||
"veh_color_grey": "Grå",
|
||||
"veh_color_metal": "Metall",
|
||||
"veh_color_graphite": "Grafitt",
|
||||
"veh_color_red": "Rød",
|
||||
"veh_color_orange": "Oransje",
|
||||
"veh_color_gold": "Gull",
|
||||
"veh_color_yellow": "Gul",
|
||||
"veh_color_green": "Grønn",
|
||||
"veh_color_blue": "Blå",
|
||||
"veh_color_bblue": "Lyseblå",
|
||||
"veh_color_bronze": "Bronse",
|
||||
"veh_color_lime": "Lime",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Elfenben",
|
||||
"veh_color_brown": "Brun",
|
||||
"veh_color_beechwood": "Bøk",
|
||||
"veh_color_sand": "Sand",
|
||||
"veh_color_cream": "Krem",
|
||||
"veh_color_white": "Hvit",
|
||||
"veh_color_steel": "Stål",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Krom",
|
||||
"veh_color_pink": "Rosa",
|
||||
"veh_color_purple": "Lilla",
|
||||
"veh_color_tan": "Brunlig",
|
||||
"veh_color_alloy": "Aluminiumlegering",
|
||||
"veh_class_compact": "Kompakt",
|
||||
"veh_class_sedan": "Sedan",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupe",
|
||||
"veh_class_muscle": "Muscle",
|
||||
"veh_class_sports_classic": "Sports Classic",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Super",
|
||||
"veh_class_motorcycle": "Motorsykkel",
|
||||
"veh_class_off_road": "Terreng",
|
||||
"veh_class_industrial": "Industriell",
|
||||
"veh_class_utility": "Nyttig",
|
||||
"veh_class_van": "Varebil",
|
||||
"veh_class_cycle": "Sykkel",
|
||||
"veh_class_boat": "Båt",
|
||||
"veh_class_helicopter": "Helikopter",
|
||||
"veh_class_plane": "Fly",
|
||||
"veh_class_service": "Service",
|
||||
"veh_class_emergency": "Nød",
|
||||
"veh_class_military": "Militær",
|
||||
"veh_class_commercial": "Kommersiell",
|
||||
"veh_class_train": "Tog",
|
||||
"veh_class_open_wheel": "Åpen hjul"
|
||||
}
|
||||
134
locales/ru.json
Normal file
134
locales/ru.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"not_in_discord": "Ваш Discord не найден, присоединяйтесь к нашему Discord здесь: %s.",
|
||||
"connecting": "Подключение...",
|
||||
"identifier_not_found": "Ваш %s не найден.",
|
||||
"deposit": "Внести",
|
||||
"withdraw": "Снять",
|
||||
"vehicle_ownership_transfered": "Владение передано",
|
||||
"vehicle_ownership_transfered2": "Владение транспортным средством %s было передано.",
|
||||
"vehicle_ownership_received": "Владение получено",
|
||||
"vehicle_ownership_received2": "Вы получили транспортное средство %s.",
|
||||
"keys_shared": "Ключи переданы",
|
||||
"keys_shared2": "Вы передали ключи от транспортного средства %s.",
|
||||
"keys_received": "Ключи получены",
|
||||
"keys_received2": "Вы получили ключи от транспортного средства %s.",
|
||||
"no_signal": "Нет сигнала",
|
||||
"veh_key_disabled": "Ключ от транспортного средства отключен.",
|
||||
"veh_key_disabled2": "Этот ключ отключен и больше не может использоваться.",
|
||||
"veh_far_away": "Транспорт слишком далеко.",
|
||||
"veh_locked": "ЗАПЕРТО",
|
||||
"veh_locked2": "Ваш транспорт теперь заперт.",
|
||||
"veh_unlocked": "ОТКРЫТО",
|
||||
"veh_unlocked2": "Ваш транспорт теперь открыт.",
|
||||
"garage": "Гараж",
|
||||
"no_owned_veh_nearby": "Вблизи не найдено вашего транспортного средства.",
|
||||
"veh_stored_in_garage": "Транспортное средство помещено в гараж.",
|
||||
"impound_reclaim": "Забрать транспорт из штрафстоянки",
|
||||
"impound": "Штрафстоянка",
|
||||
"impound_not_enough": "Цена за возврат %d₽, у вас недостаточно!",
|
||||
"impound_paid": "Оплачено %d₽ за возврат транспортного средства!",
|
||||
"success": "успешно",
|
||||
"staff_action": "Действие персонала",
|
||||
"staff_money_removed": "Удалено %d₽ (%s) у %s",
|
||||
"user_money_removed": "Удалено %d₽ у %s",
|
||||
"staff_money_added": "Добавлено %d₽ (%s) к %s",
|
||||
"user_money_added": "Добавлено %d₽ к %s",
|
||||
"staff_money_set": "Баланс %s (%s) установлен на %d₽",
|
||||
"user_money_set": "Баланс %s установлен на %d₽",
|
||||
"job_updated_noti": "Работа обновлена до %s, ранг %s.",
|
||||
"group_added_noti": "Добавлен в группу %s, ранг %s.",
|
||||
"group_removed_noti": "Удалён из группы %s.",
|
||||
"no_player_found": "Игрок не найден",
|
||||
"not_enough_cash": "У вас недостаточно наличных",
|
||||
"player_not_nearby": "Игрок не рядом",
|
||||
"cant_give_money": "Не удалось передать деньги",
|
||||
"money_received": "Деньги получены",
|
||||
"money_received2": "Получено %d₽ наличными",
|
||||
"money_given": "Деньги переданы",
|
||||
"money_given2": "Вы передали %d₽ наличными",
|
||||
"player_in_veh": "Игрок в транспортном средстве!",
|
||||
"perfect": "Идеально",
|
||||
"good": "Хорошо",
|
||||
"bad": "Плохо",
|
||||
"very_bad": "Очень плохо",
|
||||
"veh_make_brand": "Марка",
|
||||
"veh_model": "Модель",
|
||||
"veh_plate": "Номер",
|
||||
"engine_status": "Состояние двигателя",
|
||||
"vehicle": "Транспортное средство",
|
||||
"not_found": "не найдено",
|
||||
"park_veh": "Припарковать транспорт",
|
||||
"no_vehs_found": "Транспортные средства не найдены",
|
||||
"vehicle_impound": "Штрафстоянка",
|
||||
"parking_garage": "Парковка",
|
||||
"impound_w_location": "Штрафстоянка (%s)",
|
||||
"garage_w_location": "Гараж (%s)",
|
||||
"view_impounded_vehs": "Просмотр транспортных средств на штрафстоянке",
|
||||
"view_garage": "Просмотр гаража",
|
||||
"personal_vehicle": "Личный транспорт",
|
||||
"keybind_carkey": "Открыть/закрыть транспорт (двойной клик)",
|
||||
"progress_hotwiring": "Вскрытие замка",
|
||||
"cruise_control": "Круиз-контроль",
|
||||
"cruise_control_enabled": "Круиз-контроль включен",
|
||||
"cruise_control_disabled": "Круиз-контроль выключен",
|
||||
"cruise_control_toggle": "Переключить круиз-контроль",
|
||||
"cruise_control_increase": "Скорость круиз-контроля увеличена",
|
||||
"seat_shuffle": "Смена места в транспорте",
|
||||
"discord_text": "Играет: %s как %s %s",
|
||||
"discord_text_small": "Играет как: %s %s",
|
||||
"discord_text_server": "Играет: %s",
|
||||
"pause_menu_cash": "Наличные: ₽%d",
|
||||
"pause_menu_bank": "Банк: ₽%d",
|
||||
"veh_color_black": "Черный",
|
||||
"veh_color_silver": "Серебристый",
|
||||
"veh_color_grey": "Серый",
|
||||
"veh_color_metal": "Металлик",
|
||||
"veh_color_graphite": "Графит",
|
||||
"veh_color_red": "Красный",
|
||||
"veh_color_orange": "Оранжевый",
|
||||
"veh_color_gold": "Золотой",
|
||||
"veh_color_yellow": "Желтый",
|
||||
"veh_color_green": "Зеленый",
|
||||
"veh_color_blue": "Синий",
|
||||
"veh_color_bblue": "Ярко-синий",
|
||||
"veh_color_bronze": "Бронзовый",
|
||||
"veh_color_lime": "Лаймовый",
|
||||
"veh_color_champagne": "Шампанское",
|
||||
"veh_color_beige": "Бежевый",
|
||||
"veh_color_ivory": "Слоновая кость",
|
||||
"veh_color_brown": "Коричневый",
|
||||
"veh_color_beechwood": "Бук",
|
||||
"veh_color_sand": "Песочный",
|
||||
"veh_color_cream": "Кремовый",
|
||||
"veh_color_white": "Белый",
|
||||
"veh_color_steel": "Стальной",
|
||||
"veh_color_aluminium": "Алюминий",
|
||||
"veh_color_chrome": "Хром",
|
||||
"veh_color_pink": "Розовый",
|
||||
"veh_color_purple": "Фиолетовый",
|
||||
"veh_color_tan": "Загар",
|
||||
"veh_color_alloy": "Легкосплавный",
|
||||
"veh_class_compact": "Компакт",
|
||||
"veh_class_sedan": "Седан",
|
||||
"veh_class_suv": "Внедорожник",
|
||||
"veh_class_coupe": "Купе",
|
||||
"veh_class_muscle": "Мускул",
|
||||
"veh_class_sports_classic": "Спорт Классик",
|
||||
"veh_class_sport": "Спорт",
|
||||
"veh_class_super": "Супер",
|
||||
"veh_class_motorcycle": "Мотоцикл",
|
||||
"veh_class_off_road": "Внедорожник",
|
||||
"veh_class_industrial": "Промышленный",
|
||||
"veh_class_utility": "Утилитарный",
|
||||
"veh_class_van": "Фургон",
|
||||
"veh_class_cycle": "Велосипед",
|
||||
"veh_class_boat": "Лодка",
|
||||
"veh_class_helicopter": "Вертолет",
|
||||
"veh_class_plane": "Самолет",
|
||||
"veh_class_service": "Служебный",
|
||||
"veh_class_emergency": "Экстренный",
|
||||
"veh_class_military": "Военный",
|
||||
"veh_class_commercial": "Коммерческий",
|
||||
"veh_class_train": "Поезд",
|
||||
"veh_class_open_wheel": "Открытые колеса"
|
||||
}
|
||||
136
locales/sv.json
Normal file
136
locales/sv.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"not_in_discord": "Din Discord hittades inte, gå med i vår Discord här: %s.",
|
||||
"connecting": "Ansluter...",
|
||||
"identifier_not_found": "Din %s hittades inte.",
|
||||
"deposit": "Sätt in",
|
||||
"withdraw": "Ta ut",
|
||||
"vehicle_ownership_transfered": "Ägarskap överfört",
|
||||
"vehicle_ownership_transfered2": "Fordonets ägarskap för %s har överförts.",
|
||||
"vehicle_ownership_received": "Ägarskap mottaget",
|
||||
"vehicle_ownership_received2": "Mottagit fordonets ägarskap för %s.",
|
||||
"keys_shared": "Nycklar delade",
|
||||
"keys_shared2": "Du har delat fordonsnycklar med %s.",
|
||||
"keys_received": "Nycklar mottagna",
|
||||
"keys_received2": "Du har mottagit fordonsnycklar från %s.",
|
||||
"no_signal": "Ingen signal",
|
||||
"veh_key_disabled": "Fordonsnyckel inaktiverad.",
|
||||
"veh_key_disabled2": "Denna fordonsnyckel har inaktiverats och kan inte längre användas.",
|
||||
"veh_far_away": "Fordonet är för långt borta.",
|
||||
"veh_locked": "LÅST",
|
||||
"veh_locked2": "Ditt fordon är nu låst.",
|
||||
"veh_unlocked": "OLÅST",
|
||||
"veh_unlocked2": "Ditt fordon är nu olåst.",
|
||||
"garage": "Garage",
|
||||
"no_owned_veh_nearby": "Inget ägt fordon hittades i närheten.",
|
||||
"veh_stored_in_garage": "Fordonet har placerats i garaget.",
|
||||
"impound_reclaim": "Återtagning av beslagtaget fordon",
|
||||
"impound": "Beslag",
|
||||
"impound_not_enough": "Priset för att återta är %d kr, du har inte tillräckligt!",
|
||||
"impound_paid": "Betalade %d kr för att återta fordonet!",
|
||||
"success": "framgång",
|
||||
"staff_action": "Personalåtgärd",
|
||||
"staff_money_removed": "Tog bort %d kr (%s) från %s",
|
||||
"user_money_removed": "tog bort %d kr från %s",
|
||||
"staff_money_added": "Lade till %d kr (%s) till %s",
|
||||
"user_money_added": "lade till %d kr till %s",
|
||||
"staff_money_set": "Satte %s:s (%s) till %d kr",
|
||||
"user_money_set": "satte %s till %d kr",
|
||||
"job_updated_noti": "Jobb uppdaterat till %s, rang %s.",
|
||||
"group_added_noti": "Tillagd i gruppen %s, rang %s.",
|
||||
"group_removed_noti": "Borttagen från gruppen %s.",
|
||||
"no_player_found": "Ingen spelare hittades",
|
||||
"not_enough_cash": "Du har inte tillräckligt med kontanter",
|
||||
"player_not_nearby": "Spelaren är inte i närheten",
|
||||
"cant_give_money": "Kunde inte ge pengar",
|
||||
"money_received": "Pengar mottagna",
|
||||
"money_received2": "Mottog %d kr kontant",
|
||||
"money_given": "Pengar givna",
|
||||
"money_given2": "Du gav %d kr kontant",
|
||||
"player_in_veh": "Spelaren är i fordon!",
|
||||
"perfect": "Perfekt",
|
||||
"good": "Bra",
|
||||
"bad": "Dålig",
|
||||
"very_bad": "Mycket dålig",
|
||||
"veh_make_brand": "Märke",
|
||||
"veh_model": "Modell",
|
||||
"veh_plate": "Registreringsskylt",
|
||||
"engine_status": "Motorstatus",
|
||||
"vehicle": "Fordon",
|
||||
"not_found": "hittades inte",
|
||||
"park_veh": "Parkera fordon",
|
||||
"no_vehs_found": "Inga fordon hittades",
|
||||
"vehicle_impound": "Beslagtagna fordon",
|
||||
"parking_garage": "Parkeringsgarage",
|
||||
"impound_w_location": "Beslag (%s)",
|
||||
"garage_w_location": "Parkeringsgarage (%s)",
|
||||
"view_impounded_vehs": "Visa beslagtagna fordon",
|
||||
"view_garage": "Visa garage",
|
||||
"personal_vehicle": "Personligt fordon",
|
||||
"group_vehicle": "Fordon",
|
||||
"keybind_carkey": "Lås/öppna fordon (dubbelklick)",
|
||||
"progress_hotwiring": "Kopplar om",
|
||||
"cruise_control": "Farthållare",
|
||||
"cruise_control_enabled": "Fordonets farthållare aktiverad",
|
||||
"cruise_control_disabled": "Fordonets farthållare avaktiverad",
|
||||
"cruise_control_toggle": "Växla fordonets farthållare",
|
||||
"cruise_control_increase": "Ökade farthållarens hastighet",
|
||||
"seat_shuffle": "Byt plats i fordonet",
|
||||
"discord_text": "Spelar: %s som %s %s",
|
||||
"discord_text_small": "Spelar som: %s %s",
|
||||
"discord_text_server": "Spelar: %s",
|
||||
"pause_menu_cash": "Kontanter: %d",
|
||||
"pause_menu_bank": "Bank: %d",
|
||||
|
||||
"veh_color_black": "Svart",
|
||||
"veh_color_silver": "Silver",
|
||||
"veh_color_grey": "Grå",
|
||||
"veh_color_metal": "Metall",
|
||||
"veh_color_graphite": "Grafit",
|
||||
"veh_color_red": "Röd",
|
||||
"veh_color_orange": "Orange",
|
||||
"veh_color_gold": "Guld",
|
||||
"veh_color_yellow": "Gul",
|
||||
"veh_color_green": "Grön",
|
||||
"veh_color_blue": "Blå",
|
||||
"veh_color_bblue": "Ljusblå",
|
||||
"veh_color_bronze": "Brons",
|
||||
"veh_color_lime": "Lime",
|
||||
"veh_color_champagne": "Champagne",
|
||||
"veh_color_beige": "Beige",
|
||||
"veh_color_ivory": "Elfenben",
|
||||
"veh_color_brown": "Brun",
|
||||
"veh_color_beechwood": "Bokträ",
|
||||
"veh_color_sand": "Sand",
|
||||
"veh_color_cream": "Kräm",
|
||||
"veh_color_white": "Vit",
|
||||
"veh_color_steel": "Stål",
|
||||
"veh_color_aluminium": "Aluminium",
|
||||
"veh_color_chrome": "Krom",
|
||||
"veh_color_pink": "Rosa",
|
||||
"veh_color_purple": "Lila",
|
||||
"veh_color_tan": "Ljusbrun",
|
||||
"veh_color_alloy": "Legering",
|
||||
"veh_class_compact": "Kompakt",
|
||||
"veh_class_sedan": "Sedan",
|
||||
"veh_class_suv": "SUV",
|
||||
"veh_class_coupe": "Coupé",
|
||||
"veh_class_muscle": "Muskelbil",
|
||||
"veh_class_sports_classic": "Klassisk sportbil",
|
||||
"veh_class_sport": "Sport",
|
||||
"veh_class_super": "Superbil",
|
||||
"veh_class_motorcycle": "Motorcykel",
|
||||
"veh_class_off_road": "Terräng",
|
||||
"veh_class_industrial": "Industri",
|
||||
"veh_class_utility": "Nyttolast",
|
||||
"veh_class_van": "Skåpbil",
|
||||
"veh_class_cycle": "Cykel",
|
||||
"veh_class_boat": "Båt",
|
||||
"veh_class_helicopter": "Helikopter",
|
||||
"veh_class_plane": "Flygplan",
|
||||
"veh_class_service": "Service",
|
||||
"veh_class_emergency": "Utryckning",
|
||||
"veh_class_military": "Militär",
|
||||
"veh_class_commercial": "Kommersiell",
|
||||
"veh_class_train": "Tåg",
|
||||
"veh_class_open_wheel": "Öppen hjulbil"
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
local moneyTypes = {"bank", "cash"}
|
||||
local moneyActions = {
|
||||
remove = function(player, account, amount)
|
||||
player.deductMoney(account, amount, "Staff action")
|
||||
return ("Removed $%d (%s) to %s"):format(amount, account, player.name), ("removed $%d from %s"):format(amount, account)
|
||||
player.deductMoney(account, amount, locale("staff_action"))
|
||||
return locale("staff_money_removed", amount, account, player.name), locale("user_money_removed", amount, account)
|
||||
end,
|
||||
add = function(player, account, amount)
|
||||
player.addMoney(account, amount, "Staff action")
|
||||
return ("Added $%d (%s) to %s"):format(amount, account, player.name), ("added $%d to %s"):format(amount, account)
|
||||
player.addMoney(account, amount, locale("staff_action"))
|
||||
return locale("staff_money_added", amount, account, player.name), locale("user_money_added", amount, account)
|
||||
end,
|
||||
set = function(player, account, amount)
|
||||
player.setData(account, amount, "Staff action")
|
||||
return ("Set %s's (%s) to $%d"):format(player.name, account, amount), ("set %s to $%d"):format(account, amount)
|
||||
player.setData(account, amount, locale("staff_action"))
|
||||
return locale("staff_money_set", player.name, account, amount), locale("user_money_set", account, amount)
|
||||
end
|
||||
}
|
||||
|
||||
lib.addCommand("setmoney", {
|
||||
help = "Admin command, manage player money.",
|
||||
help = "Admin command, set a players money.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
@@ -48,17 +48,17 @@ lib.addCommand("setmoney", {
|
||||
local staffMessage, userMessage = action(player, moneyType, args.amount)
|
||||
|
||||
player.notify({
|
||||
title = "Staff action",
|
||||
title = locale("staff_action"),
|
||||
description = userMessage,
|
||||
type = "inform",
|
||||
duration = 10000
|
||||
})
|
||||
|
||||
if not source then return end
|
||||
if not source or source == 0 then return end
|
||||
TriggerClientEvent("chat:addMessage", source, {
|
||||
color = {50, 100, 235},
|
||||
multiline = true,
|
||||
args = {"Staff action", staffMessage}
|
||||
args = {locale("staff_action"), staffMessage}
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -90,17 +90,17 @@ lib.addCommand("setjob", {
|
||||
local jobInfo = player.setJob(job, args.rank)
|
||||
if not player or not jobInfo then return end
|
||||
player.notify({
|
||||
title = "Staff action",
|
||||
description = ("Job updated to %s, rank %s"):format(jobInfo.label, jobInfo.rankName),
|
||||
title = locale("staff_action"),
|
||||
description = locale("job_updated_noti", jobInfo.label, jobInfo.rankName),
|
||||
type = "inform",
|
||||
duration = 10000
|
||||
})
|
||||
|
||||
if not source then return end
|
||||
if not source or source == 0 then return end
|
||||
TriggerClientEvent("chat:addMessage", source, {
|
||||
color = {50, 100, 235},
|
||||
multiline = true,
|
||||
args = {"Staff action", "success"}
|
||||
args = {locale("staff_action"), locale("success")}
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -137,8 +137,8 @@ lib.addCommand("setgroup", {
|
||||
local groupInfo = player.addGroup(args.group, args.rank)
|
||||
if not groupInfo then return end
|
||||
player.notify({
|
||||
title = "Staff action",
|
||||
description = ("Added to group %s, rank %s."):format(groupInfo.label, groupInfo.rankName),
|
||||
title = locale("staff_action"),
|
||||
description = locale("group_added_noti", groupInfo.label, groupInfo.rankName),
|
||||
type = "inform",
|
||||
duration = 10000
|
||||
})
|
||||
@@ -146,8 +146,8 @@ lib.addCommand("setgroup", {
|
||||
local groupInfo = player.removeGroup(args.group)
|
||||
if not groupInfo then return end
|
||||
player.notify({
|
||||
title = "Staff action",
|
||||
description = ("Removed from group %s."):format(groupInfo.label),
|
||||
title = locale("staff_action"),
|
||||
description = locale("group_removed_noti", groupInfo.label),
|
||||
type = "inform",
|
||||
duration = 10000
|
||||
})
|
||||
@@ -155,16 +155,16 @@ lib.addCommand("setgroup", {
|
||||
return
|
||||
end
|
||||
|
||||
if not source then return end
|
||||
if not source or source == 0 then return end
|
||||
TriggerClientEvent("chat:addMessage", source, {
|
||||
color = {50, 100, 235},
|
||||
multiline = true,
|
||||
args = {"Staff action", "success"}
|
||||
args = {locale("staff_action"), locale("success")}
|
||||
})
|
||||
end)
|
||||
|
||||
lib.addCommand("skin", {
|
||||
help = "Admin command, set player into character clothing menu.",
|
||||
help = "Admin command, show character clothing menu for player.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
@@ -178,7 +178,7 @@ lib.addCommand("skin", {
|
||||
end)
|
||||
|
||||
lib.addCommand("character", {
|
||||
help = "Admin command, set player into character selection menu.",
|
||||
help = "Admin command, show character selection menu for player.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
@@ -194,84 +194,90 @@ end)
|
||||
lib.addCommand("pay", {
|
||||
help = "give money to nearby player.",
|
||||
params = {
|
||||
{
|
||||
name = "target",
|
||||
type = "playerId",
|
||||
help = "Target player's server id"
|
||||
},
|
||||
{
|
||||
name = "amount",
|
||||
type = "number"
|
||||
type = "number",
|
||||
help = "Amount of cash to give"
|
||||
}
|
||||
}
|
||||
}, function(source, args, raw)
|
||||
if not source then return end
|
||||
local targetPlayer
|
||||
local pedCoords = GetEntityCoords(GetPlayerPed(source))
|
||||
for targetId, targetInfo in pairs(NDCore.players) do
|
||||
local targetCoords = GetEntityCoords(GetPlayerPed(targetId))
|
||||
if #(pedCoords-targetCoords) < 2.0 and targetId ~= source then
|
||||
targetPlayer = targetInfo
|
||||
break
|
||||
end
|
||||
end
|
||||
if not source or source == 0 then return end
|
||||
|
||||
local player = NDCore.getPlayer(source)
|
||||
if not player then return end
|
||||
local success = player.deductMoney("cash", args.amount)
|
||||
local targetPlayer = NDCore.getPlayer(args.target)
|
||||
|
||||
if not player or not targetPlayer then
|
||||
return player.notify({
|
||||
title = "Error",
|
||||
description = locale("no_player_found"),
|
||||
type = "error"
|
||||
})
|
||||
end
|
||||
|
||||
if player.cash < args.amount then
|
||||
return player.notify({
|
||||
title = "Error",
|
||||
description = locale("not_enough_cash"),
|
||||
type = "error"
|
||||
})
|
||||
end
|
||||
|
||||
local playerCoords = GetEntityCoords(GetPlayerPed(player.source))
|
||||
local targetCoords = GetEntityCoords(GetPlayerPed(targetPlayer.source))
|
||||
if #(playerCoords-targetCoords) > 2.0 then
|
||||
return player.notify({
|
||||
title = "Error",
|
||||
description = locale("player_not_nearby"),
|
||||
type = "error"
|
||||
})
|
||||
end
|
||||
|
||||
local success = player.deductMoney("cash", args.amount) and targetPlayer.addMoney("cash", args.amount)
|
||||
if not success then
|
||||
return player.notify({
|
||||
title = "Couldn't give money",
|
||||
title = locale("cant_give_money"),
|
||||
type = "error",
|
||||
duration = 5000
|
||||
})
|
||||
end
|
||||
|
||||
if not targetPlayer or not targetPlayer.addMoney("cash", args.amount) then return end
|
||||
targetPlayer.notify({
|
||||
title = "Money received",
|
||||
description = ("Received $%d in cash"):format(args.amount),
|
||||
title = locale("money_received"),
|
||||
description = locale("money_received2", args.amount),
|
||||
type = "inform",
|
||||
duration = 10000
|
||||
duration = 5000
|
||||
})
|
||||
|
||||
player.notify({
|
||||
title = "Money given",
|
||||
description = ("You gave someone $%d in cash"):format(args.amount),
|
||||
title = locale("money_given"),
|
||||
description = locale("money_given2", args.amount),
|
||||
type = "inform",
|
||||
duration = 10000
|
||||
duration = 5000
|
||||
})
|
||||
end)
|
||||
|
||||
lib.addCommand("unlock", {
|
||||
help = "Admin force unlock vehicles",
|
||||
help = "Admin Command, force unlock a vehicle.",
|
||||
restricted = "group.admin",
|
||||
}, function(source, args, raw)
|
||||
local ped = GetPlayerPed(source)
|
||||
local playerVeh = GetVehiclePedIsIn(ped)
|
||||
local coords = GetEntityCoords(ped)
|
||||
local vehicles = GetAllVehicles()
|
||||
local maxDistance = 2.0
|
||||
local closestVehicle
|
||||
local veh = lib.getClosestVehicle(coords, 2.5, true)
|
||||
|
||||
if not playerVeh or playerVeh == 0 or not DoesEntityExist(playerVeh) then
|
||||
for i=1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
local vehicleCoords = GetEntityCoords(vehicle)
|
||||
local distance = #(coords-vehicleCoords)
|
||||
|
||||
if distance < maxDistance then
|
||||
maxDistance = distance
|
||||
closestVehicle = vehicle
|
||||
end
|
||||
end
|
||||
if not closestVehicle or not DoesEntityExist(closestVehicle) then return end
|
||||
local state = Entity(closestVehicle).state
|
||||
state.locked = false
|
||||
else
|
||||
local state = Entity(playerVeh).state
|
||||
state.hotwired = true
|
||||
end
|
||||
if not veh then return end
|
||||
|
||||
local state = Entity(veh).state
|
||||
state.hotwired = true
|
||||
state.locked = false
|
||||
end)
|
||||
|
||||
lib.addCommand("revive", {
|
||||
help = "Admin command, revive player.",
|
||||
help = "Admin command, revive a player.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
@@ -287,60 +293,49 @@ lib.addCommand("revive", {
|
||||
end)
|
||||
|
||||
lib.addCommand("dv", {
|
||||
help = "Admin command, delete vehicles within the range.",
|
||||
help = "Admin command, delete vehicles within the range or the closest.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
name = "range",
|
||||
type = "number",
|
||||
help = "The range to select vehicles for deleteion from",
|
||||
help = "Range to delete vehicles in",
|
||||
optional = true
|
||||
}
|
||||
}
|
||||
}, function(source, args, raw)
|
||||
local ped = GetPlayerPed(source)
|
||||
local veh = GetVehiclePedIsIn(ped)
|
||||
if veh and veh ~= 0 and DoesEntityExist(veh) then
|
||||
DeleteEntity(veh)
|
||||
return TriggerClientEvent("chat:addMessage", source, {
|
||||
color = {50, 100, 235},
|
||||
multiline = true,
|
||||
args = {"Staff action", "deleted 1 vehicle"}
|
||||
})
|
||||
end
|
||||
|
||||
local count = 0
|
||||
local coords = GetEntityCoords(ped)
|
||||
local count = 0
|
||||
local message = nil
|
||||
local veh = lib.getClosestVehicle(coords, 3.0)
|
||||
|
||||
if args.range then
|
||||
for _, veh in ipairs(GetAllVehicles()) do
|
||||
local vehDist = #(GetEntityCoords(veh) - coords)
|
||||
if vehDist < args.range then
|
||||
DeleteEntity(veh)
|
||||
count += 1
|
||||
end
|
||||
local vehicles = lib.getNearbyVehicles(coords, args.range)
|
||||
count = #vehicles
|
||||
|
||||
for i=1, count do
|
||||
local veh = vehicles[i]
|
||||
DeleteEntity(veh.vehicle)
|
||||
end
|
||||
return TriggerClientEvent("chat:addMessage", source, {
|
||||
color = {50, 100, 235},
|
||||
multiline = true,
|
||||
args = {"Staff action", ("deleted %d vehicles"):format(count)}
|
||||
})
|
||||
elseif veh then
|
||||
DeleteEntity(veh)
|
||||
count = 1
|
||||
end
|
||||
|
||||
local closest, dist
|
||||
for _, veh in ipairs(GetAllVehicles()) do
|
||||
local vehDist = #(GetEntityCoords(veh) - coords)
|
||||
if vehDist < 5.0 and not closest or (dist and dist > vehDist) then
|
||||
closest = veh
|
||||
dist = vehDist
|
||||
end
|
||||
local messageLabel = ("%s [dv]"):format(locale("staff_action"))
|
||||
if count == 0 then
|
||||
message = {messageLabel, "no vehicles found"}
|
||||
elseif count == 1 then
|
||||
message = {messageLabel, "deleted 1 vehicle"}
|
||||
else
|
||||
message = {messageLabel, ("deleted %d vehicles"):format(count)}
|
||||
end
|
||||
|
||||
if not closest then return "no vehicle found nearby" end
|
||||
DeleteEntity(closest)
|
||||
TriggerClientEvent("chat:addMessage", source, {
|
||||
color = {50, 100, 235},
|
||||
multiline = true,
|
||||
args = {"Staff action", "deleted 1 vehicle"}
|
||||
args = message
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -411,7 +406,7 @@ lib.addCommand("unfreeze", {
|
||||
end)
|
||||
|
||||
lib.addCommand("vehicle", {
|
||||
help = "Admin command, unfreeze a player.",
|
||||
help = "Admin command, spawn a vehicle.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
@@ -444,3 +439,45 @@ lib.addCommand("vehicle", {
|
||||
Wait(100)
|
||||
end
|
||||
end)
|
||||
|
||||
lib.addCommand("claim-veh", {
|
||||
help = "Admin command, add a copy of the vehicle you're inside to players garage.",
|
||||
restricted = "group.admin",
|
||||
params = {
|
||||
{
|
||||
name = "target",
|
||||
type = "playerId",
|
||||
help = "Target player's server id"
|
||||
}
|
||||
}
|
||||
}, function(source, args, raw)
|
||||
local player = NDCore.getPlayer(source)
|
||||
if not player then return end
|
||||
|
||||
local targetPlayer = NDCore.getPlayer(args.target)
|
||||
if not targetPlayer then
|
||||
return player.notify({
|
||||
title = "Player not found!",
|
||||
description = "Target player not found, make sure they select a charcter!",
|
||||
type = "error"
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
local properties = lib.callback.await("ND_Vehicles:getPropsFromCurrentVeh", source)
|
||||
if not properties then
|
||||
return player.notify({
|
||||
title = "Vehicle data not found!",
|
||||
description = "The vehicle properties data was not found!",
|
||||
type = "error"
|
||||
})
|
||||
end
|
||||
|
||||
NDCore.setVehicleOwned(targetPlayer.id, properties, true)
|
||||
|
||||
player.notify({
|
||||
title = "Vehicle added!",
|
||||
description = ("The vehicle has now been added to %s's garage!"):format(targetPlayer.name),
|
||||
type = "success"
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
function NDCore.getPlayerIdentifierByType(src, indentifierType)
|
||||
if Config.sv_lan then
|
||||
return ("%s:sv_lan"):format(indentifierType)
|
||||
end
|
||||
return GetPlayerIdentifierByType(src, indentifierType)
|
||||
end
|
||||
|
||||
---@param src number
|
||||
---@return table
|
||||
function NDCore.getPlayer(src)
|
||||
@@ -11,7 +18,16 @@ function NDCore.getPlayers(key, value, returnArray)
|
||||
if not key or not value then return NDCore.players end
|
||||
|
||||
local players = {}
|
||||
local keyTypes = {id = "id", firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"}
|
||||
local keyTypes = {
|
||||
id = "id",
|
||||
firstname = "firstname",
|
||||
lastname = "lastname",
|
||||
gender = "gender",
|
||||
groups = "groups",
|
||||
job = "job",
|
||||
gender = "gender"
|
||||
}
|
||||
|
||||
local findBy = keyTypes[key] or "metadata"
|
||||
|
||||
if findBy then
|
||||
@@ -34,6 +50,13 @@ function NDCore.getPlayerServerInfo(source)
|
||||
return PlayersInfo[source]
|
||||
end
|
||||
|
||||
function NDCore.getConfig(info)
|
||||
if not info then
|
||||
return Config
|
||||
end
|
||||
return Config[info]
|
||||
end
|
||||
|
||||
---@param fileLocation string|tabale
|
||||
---@return boolean
|
||||
function NDCore.loadSQL(fileLocation, resource)
|
||||
|
||||
60
server/groupadmin.lua
Normal file
60
server/groupadmin.lua
Normal file
@@ -0,0 +1,60 @@
|
||||
lib.addCommand("groupadmin", {
|
||||
help = "Open the group management admin panel.",
|
||||
restricted = "group.admin"
|
||||
}, function(source, args, raw)
|
||||
if not source or source == 0 then return end
|
||||
local groups = NDCore.getAllGroups()
|
||||
TriggerClientEvent("ND:openGroupAdmin", source, groups)
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Core:groups:create", function(source, data)
|
||||
local player = NDCore.getPlayer(source)
|
||||
if not player or not player.groups["admin"] then return { success = false } end
|
||||
|
||||
if not data or not data.name or not data.label then
|
||||
return { success = false }
|
||||
end
|
||||
|
||||
local success, err = NDCore.createNewGroup(data.name, data.label, data.isJob, data.ranks)
|
||||
if not success then
|
||||
return { success = false, error = err }
|
||||
end
|
||||
|
||||
return { success = true, groups = NDCore.getAllGroups() }
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Core:groups:edit", function(source, data)
|
||||
local player = NDCore.getPlayer(source)
|
||||
if not player or not player.groups["admin"] then return { success = false } end
|
||||
|
||||
if not data or not data.name then
|
||||
return { success = false }
|
||||
end
|
||||
|
||||
local success, err = NDCore.editGroupData(data.name, {
|
||||
label = data.label,
|
||||
isJob = data.isJob,
|
||||
ranks = data.ranks
|
||||
})
|
||||
if not success then
|
||||
return { success = false, error = err }
|
||||
end
|
||||
|
||||
return { success = true, groups = NDCore.getAllGroups() }
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Core:groups:delete", function(source, data)
|
||||
local player = NDCore.getPlayer(source)
|
||||
if not player or not player.groups["admin"] then return { success = false } end
|
||||
|
||||
if not data or not data.name then
|
||||
return { success = false }
|
||||
end
|
||||
|
||||
local success = NDCore.deleteGroup(data.name)
|
||||
if not success then
|
||||
return { success = false }
|
||||
end
|
||||
|
||||
return { success = true, groups = NDCore.getAllGroups() }
|
||||
end)
|
||||
226
server/groups.lua
Normal file
226
server/groups.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
-- Cache of all groups loaded from DB: Config.groups[name] = { label, isJob, ranks = { [weight] = { id, label, weight, isBoss } } }
|
||||
|
||||
local function loadGroupsFromDB()
|
||||
local groups = {}
|
||||
local rows = MySQL.query.await("SELECT * FROM nd_groups")
|
||||
if not rows then return groups end
|
||||
|
||||
for _, row in ipairs(rows) do
|
||||
groups[row.name] = {
|
||||
label = row.label,
|
||||
isJob = row.isJob == 1,
|
||||
ranks = {}
|
||||
}
|
||||
end
|
||||
|
||||
local ranks = MySQL.query.await("SELECT * FROM nd_group_ranks ORDER BY weight ASC")
|
||||
if ranks then
|
||||
for _, rank in ipairs(ranks) do
|
||||
local g = groups[rank.group_name]
|
||||
if g then
|
||||
g.ranks[rank.weight] = {
|
||||
id = rank.id,
|
||||
label = rank.label,
|
||||
weight = rank.weight,
|
||||
isBoss = rank.isBoss == 1
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return groups
|
||||
end
|
||||
|
||||
--- Check if a group exists in the database/cache.
|
||||
---@param name string
|
||||
---@return boolean
|
||||
function NDCore.doesGroupExist(name)
|
||||
return Config.groups[name] ~= nil
|
||||
end
|
||||
|
||||
--- Get group data from cache.
|
||||
---@param name string
|
||||
---@return table|nil
|
||||
function NDCore.getGroupData(name)
|
||||
return Config.groups[name]
|
||||
end
|
||||
|
||||
--- Create a new group in the database and cache.
|
||||
---@param name string Unique key
|
||||
---@param label string Display name
|
||||
---@param isJob boolean
|
||||
---@param ranks table Array of { label = string, weight = number, isBoss = boolean }
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function NDCore.createNewGroup(name, label, isJob, ranks)
|
||||
if not name or name == "" then return false, "name is required" end
|
||||
if Config.groups[name] then return false, "group already exists" end
|
||||
|
||||
MySQL.insert.await("INSERT INTO nd_groups (`name`, `label`, `isJob`) VALUES (?, ?, ?)", {
|
||||
name, label or name, isJob and 1 or 0
|
||||
})
|
||||
|
||||
Config.groups[name] = {
|
||||
label = label or name,
|
||||
isJob = isJob or false,
|
||||
ranks = {}
|
||||
}
|
||||
|
||||
if ranks and #ranks > 0 then
|
||||
for _, rank in ipairs(ranks) do
|
||||
local id = MySQL.insert.await("INSERT INTO nd_group_ranks (`group_name`, `label`, `weight`, `isBoss`) VALUES (?, ?, ?, ?)", {
|
||||
name, rank.label, rank.weight, rank.isBoss and 1 or 0
|
||||
})
|
||||
Config.groups[name].ranks[rank.weight] = {
|
||||
id = id,
|
||||
label = rank.label,
|
||||
weight = rank.weight,
|
||||
isBoss = rank.isBoss or false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
NDCore.syncGroupsToClients()
|
||||
return true
|
||||
end
|
||||
|
||||
--- Edit an existing group in the database and cache.
|
||||
---@param name string Group key
|
||||
---@param data table { label?, isJob?, ranks? }
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function NDCore.editGroupData(name, data)
|
||||
if not name or not Config.groups[name] then return false, "group does not exist" end
|
||||
|
||||
if data.label ~= nil or data.isJob ~= nil then
|
||||
local label = data.label or Config.groups[name].label
|
||||
local isJob = data.isJob ~= nil and data.isJob or Config.groups[name].isJob
|
||||
MySQL.update.await("UPDATE nd_groups SET `label` = ?, `isJob` = ? WHERE `name` = ?", {
|
||||
label, isJob and 1 or 0, name
|
||||
})
|
||||
Config.groups[name].label = label
|
||||
Config.groups[name].isJob = isJob
|
||||
end
|
||||
|
||||
if data.ranks then
|
||||
-- Delete all old ranks and replace with new set
|
||||
MySQL.update.await("DELETE FROM nd_group_ranks WHERE `group_name` = ?", { name })
|
||||
Config.groups[name].ranks = {}
|
||||
|
||||
for _, rank in ipairs(data.ranks) do
|
||||
local id = MySQL.insert.await("INSERT INTO nd_group_ranks (`group_name`, `label`, `weight`, `isBoss`) VALUES (?, ?, ?, ?)", {
|
||||
name, rank.label, rank.weight, rank.isBoss and 1 or 0
|
||||
})
|
||||
Config.groups[name].ranks[rank.weight] = {
|
||||
id = id,
|
||||
label = rank.label,
|
||||
weight = rank.weight,
|
||||
isBoss = rank.isBoss or false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
NDCore.syncGroupsToClients()
|
||||
return true
|
||||
end
|
||||
|
||||
--- Delete a group from the database and cache.
|
||||
---@param name string
|
||||
---@return boolean
|
||||
function NDCore.deleteGroup(name)
|
||||
if not name or not Config.groups[name] then return false end
|
||||
MySQL.update.await("DELETE FROM nd_groups WHERE `name` = ?", { name })
|
||||
Config.groups[name] = nil
|
||||
NDCore.syncGroupsToClients()
|
||||
return true
|
||||
end
|
||||
|
||||
--- Get all groups formatted for UI or external use.
|
||||
---@return table
|
||||
function NDCore.getAllGroups()
|
||||
local result = {}
|
||||
for name, group in pairs(Config.groups) do
|
||||
local ranks = {}
|
||||
for weight, rank in pairs(group.ranks) do
|
||||
ranks[#ranks+1] = {
|
||||
id = rank.id,
|
||||
label = rank.label,
|
||||
weight = rank.weight,
|
||||
isBoss = rank.isBoss
|
||||
}
|
||||
end
|
||||
table.sort(ranks, function(a, b) return a.weight < b.weight end)
|
||||
result[#result+1] = {
|
||||
name = name,
|
||||
label = group.label,
|
||||
isJob = group.isJob,
|
||||
ranks = ranks
|
||||
}
|
||||
end
|
||||
table.sort(result, function(a, b) return a.name < b.name end)
|
||||
return result
|
||||
end
|
||||
|
||||
--- Sync groups convar to all clients after changes.
|
||||
function NDCore.syncGroupsToClients()
|
||||
-- Build a simplified map for the convar (backwards compatible format)
|
||||
local simplified = {}
|
||||
for name, group in pairs(Config.groups) do
|
||||
local rankLabels = {}
|
||||
local sortedRanks = {}
|
||||
for weight, rank in pairs(group.ranks) do
|
||||
sortedRanks[#sortedRanks+1] = rank
|
||||
end
|
||||
table.sort(sortedRanks, function(a, b) return a.weight < b.weight end)
|
||||
for _, rank in ipairs(sortedRanks) do
|
||||
rankLabels[#rankLabels+1] = rank.label
|
||||
end
|
||||
simplified[name] = {
|
||||
label = group.label,
|
||||
isJob = group.isJob,
|
||||
ranks = rankLabels
|
||||
}
|
||||
end
|
||||
SetConvarReplicated("core:groups", json.encode(simplified))
|
||||
end
|
||||
|
||||
--- Load groups from DB into Config.groups. Called at startup.
|
||||
function NDCore.loadGroups()
|
||||
Config.groups = loadGroupsFromDB()
|
||||
NDCore.syncGroupsToClients()
|
||||
end
|
||||
|
||||
--- Seed the database from the old JSON config if tables are empty.
|
||||
function NDCore.seedGroupsFromJson()
|
||||
local count = MySQL.scalar.await("SELECT COUNT(*) FROM nd_groups")
|
||||
if count and count > 0 then return false end
|
||||
|
||||
local groupsJson = lib.loadJson("_config.groups") or json.decode(GetConvar("core:groups", "[]"))
|
||||
if not groupsJson then return false end
|
||||
|
||||
for name, group in pairs(groupsJson) do
|
||||
MySQL.insert.await("INSERT INTO nd_groups (`name`, `label`, `isJob`) VALUES (?, ?, ?)", {
|
||||
name, group.label or name, (group.isJob and 1 or 0)
|
||||
})
|
||||
|
||||
if group.ranks then
|
||||
for i, rankLabel in ipairs(group.ranks) do
|
||||
local isBoss = false
|
||||
if group.minimumBossRank and i >= group.minimumBossRank then
|
||||
isBoss = true
|
||||
end
|
||||
MySQL.insert.await("INSERT INTO nd_group_ranks (`group_name`, `label`, `weight`, `isBoss`) VALUES (?, ?, ?, ?)", {
|
||||
name, rankLabel, i, isBoss and 1 or 0
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
for name, func in pairs(NDCore) do
|
||||
if type(func) == "function" then
|
||||
exports(name, func)
|
||||
end
|
||||
end
|
||||
149
server/main.lua
149
server/main.lua
@@ -1,12 +1,15 @@
|
||||
lib.locale()
|
||||
NDCore = {}
|
||||
NDCore.players = {}
|
||||
PlayersInfo = {}
|
||||
local resourceName = GetCurrentResourceName()
|
||||
local tempPlayersInfo = {}
|
||||
-- Groups are now loaded from the database in MySQL.ready via NDCore.loadGroups()
|
||||
|
||||
Config = {
|
||||
serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"),
|
||||
discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"),
|
||||
discordMemeberRequired = GetConvarInt("core:discordMemeberRequired", 1) == 1,
|
||||
discordAppId = GetConvar("core:discordAppId", "858146067018416128"),
|
||||
discordAsset = GetConvar("core:discordAsset", "andyyy"),
|
||||
discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"),
|
||||
@@ -15,22 +18,28 @@ Config = {
|
||||
discordActionText2 = GetConvar("core:discordActionText2", "STORE"),
|
||||
discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"),
|
||||
characterIdentifier = GetConvar("core:characterIdentifier", "license"),
|
||||
selectIdentifiers = json.decode(GetConvar("core:selectIdentifiers", '["discord", "license", "license2", "fivem"]')),
|
||||
discordGuildId = GetConvar("core:discordGuildId", "false"),
|
||||
discordBotToken = GetConvar("core:discordBotToken", "false"),
|
||||
randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30),
|
||||
disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1,
|
||||
useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1,
|
||||
groups = json.decode(GetConvar("core:groups", "[]")),
|
||||
groups = {},
|
||||
admins = json.decode(GetConvar("core:admins", "[]")),
|
||||
adminDiscordRoles = json.decode(GetConvar("core:adminDiscordRoles", "[]")),
|
||||
groupRoles = json.decode(GetConvar("core:groupRoles", "[]")),
|
||||
multiCharacter = false,
|
||||
compatibility = json.decode(GetConvar("core:compatibility", "[]"))
|
||||
compatibility = json.decode(GetConvar("core:compatibility", "[]")),
|
||||
sv_lan = GetConvar("sv_lan", "false") == "true",
|
||||
platePattern = GetConvar("core:platePattern", "11AAA111")
|
||||
}
|
||||
|
||||
SetConvarServerInfo("Discord", Config.discordInvite)
|
||||
SetConvarServerInfo("NDCore", GetResourceMetadata(resourceName, "version", 0) or "invalid")
|
||||
SetConvarReplicated("inventory:framework", "nd")
|
||||
|
||||
lib.versionCheck('ND-Framework/ND_Core')
|
||||
|
||||
local function getIdentifierList(src)
|
||||
local list = {}
|
||||
for i=0, GetNumPlayerIdentifiers(src) do
|
||||
@@ -41,13 +50,88 @@ local function getIdentifierList(src)
|
||||
list[identifierType] = identifier
|
||||
end
|
||||
end
|
||||
|
||||
if Config.sv_lan then
|
||||
list[Config.characterIdentifier] = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
AddEventHandler("playerJoining", function(oldId)
|
||||
local src = source
|
||||
PlayersInfo[src] = tempPlayersInfo[oldId]
|
||||
tempPlayersInfo[oldId] = nil
|
||||
local oldTempId = tonumber(oldId)
|
||||
PlayersInfo[src] = tempPlayersInfo[oldTempId]
|
||||
tempPlayersInfo[oldTempId] = nil
|
||||
|
||||
if Config.sv_lan then
|
||||
lib.addPrincipal(("player.%s"):format(src), "group.admin")
|
||||
end
|
||||
|
||||
local identifiers = PlayersInfo[src] and PlayersInfo[src].identifiers or getIdentifierList(src)
|
||||
|
||||
local whereParts = {}
|
||||
local params = {}
|
||||
|
||||
for identifierType, identifier in pairs(identifiers) do
|
||||
local isSelected = false
|
||||
for i=1, #Config.selectIdentifiers do
|
||||
local selectedType = Config.selectIdentifiers[i]
|
||||
if identifierType == selectedType then
|
||||
isSelected = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if isSelected then
|
||||
local columnName = "id_" .. identifierType
|
||||
local cleanId = identifier:gsub("^[^:]*:", "")
|
||||
table.insert(whereParts, columnName .. " = ?")
|
||||
table.insert(params, cleanId)
|
||||
end
|
||||
end
|
||||
|
||||
local user = nil
|
||||
if #whereParts > 0 then
|
||||
local query = "SELECT user_id FROM nd_users WHERE " .. table.concat(whereParts, " OR ") .. " LIMIT 1"
|
||||
user = MySQL.query.await(query, params)
|
||||
end
|
||||
|
||||
if user and user[1] then
|
||||
PlayersInfo[src].userId = user[1].user_id
|
||||
|
||||
local updateParts = {}
|
||||
local updateParams = {}
|
||||
|
||||
for identifierType, identifier in pairs(identifiers) do
|
||||
local columnName = "id_" .. identifierType
|
||||
local cleanId = identifier:gsub("^[^:]*:", "")
|
||||
table.insert(updateParts, columnName .. " = ?")
|
||||
table.insert(updateParams, cleanId)
|
||||
end
|
||||
|
||||
if #updateParts > 0 then
|
||||
table.insert(updateParams, user[1].user_id)
|
||||
local updateQuery = "UPDATE nd_users SET " .. table.concat(updateParts, ", ") .. " WHERE user_id = ?"
|
||||
MySQL.update.await(updateQuery, updateParams)
|
||||
end
|
||||
else
|
||||
local columns = {}
|
||||
local values = {}
|
||||
local insertParams = {}
|
||||
|
||||
for identifierType, identifier in pairs(identifiers) do
|
||||
local columnName = "id_" .. identifierType
|
||||
local cleanId = identifier:gsub("^[^:]*:", "")
|
||||
table.insert(columns, columnName)
|
||||
table.insert(values, "?")
|
||||
table.insert(insertParams, cleanId)
|
||||
end
|
||||
|
||||
local insertQuery = "INSERT INTO nd_users (" .. table.concat(columns, ", ") .. ") VALUES (" .. table.concat(values, ", ") .. ")"
|
||||
local user_id = MySQL.insert.await(insertQuery, insertParams)
|
||||
PlayersInfo[src].userId = user_id
|
||||
end
|
||||
|
||||
if Config.multiCharacter then return end
|
||||
Wait(3000)
|
||||
@@ -93,22 +177,33 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals)
|
||||
local tempSrc = source
|
||||
local identifiers = getIdentifierList(tempSrc)
|
||||
local mainIdentifier = identifiers[Config.characterIdentifier]
|
||||
local discordInfo = {}
|
||||
local discordInfo = nil
|
||||
|
||||
deferrals.defer()
|
||||
Wait(0)
|
||||
|
||||
if mainIdentifier and Config.discordBotToken ~= "false" and Config.discordGuildId ~= "false" and not discordInfo then
|
||||
if mainIdentifier and Config.discordBotToken ~= "false" and Config.discordGuildId ~= "false" then
|
||||
discordInfo = checkDiscordIdentifier(identifiers)
|
||||
if not discordInfo then
|
||||
deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite))
|
||||
if not discordInfo and Config.discordMemeberRequired and not Config.sv_lan then
|
||||
deferrals.done(locale("not_in_discord", Config.discordInvite))
|
||||
Wait(0)
|
||||
end
|
||||
end
|
||||
|
||||
deferrals.update("Connecting...")
|
||||
deferrals.update(locale("connecting"))
|
||||
Wait(0)
|
||||
|
||||
if Config.sv_lan then
|
||||
tempPlayersInfo[tempSrc] = {
|
||||
identifiers = {
|
||||
[Config.characterIdentifier] = "sv_lan"
|
||||
},
|
||||
discord = discordInfo
|
||||
}
|
||||
deferrals.done()
|
||||
return
|
||||
end
|
||||
|
||||
if mainIdentifier then
|
||||
tempPlayersInfo[tempSrc] = {
|
||||
identifiers = identifiers,
|
||||
@@ -116,7 +211,7 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals)
|
||||
}
|
||||
deferrals.done()
|
||||
else
|
||||
deferrals.done(("Your %s was not found."):format(Config.characterIdentifier))
|
||||
deferrals.done(locale("identifier_not_found", Config.characterIdentifier))
|
||||
Wait(0)
|
||||
end
|
||||
end)
|
||||
@@ -136,13 +231,6 @@ AddEventHandler("onResourceStop", function(name)
|
||||
end
|
||||
end)
|
||||
|
||||
SetTimeout(500, function()
|
||||
NDCore.loadSQL({
|
||||
"database/characters.sql",
|
||||
"database/vehicles.sql"
|
||||
}, resourceName)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND:playerEliminated", function(info)
|
||||
local src = source
|
||||
local player = NDCore.getPlayer(src)
|
||||
@@ -152,3 +240,30 @@ RegisterNetEvent("ND:playerEliminated", function(info)
|
||||
deathInfo = info
|
||||
})
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND:updateClothing", function(clothing)
|
||||
local src = source
|
||||
local player = NDCore.getPlayer(src)
|
||||
if not player or not clothing or type(clothing) ~= "table" then return end
|
||||
player.setMetadata("clothing", clothing)
|
||||
end)
|
||||
|
||||
MySQL.ready(function()
|
||||
Wait(100)
|
||||
NDCore.loadSQL({
|
||||
"database/users.sql",
|
||||
"database/characters.sql",
|
||||
"database/vehicles.sql",
|
||||
"database/moneylogs.sql",
|
||||
"database/groups.sql",
|
||||
"database/group_ranks.sql"
|
||||
}, resourceName)
|
||||
Wait(200)
|
||||
NDCore.seedGroupsFromJson()
|
||||
NDCore.loadGroups()
|
||||
end)
|
||||
|
||||
-- Hourly cron, purge soft-deleted characters older than 30 days.
|
||||
lib.cron.new("0 * * * *", function()
|
||||
MySQL.update.await("DELETE FROM nd_characters WHERE deleted_at IS NOT NULL AND deleted_at < DATE_SUB(NOW(), INTERVAL 30 DAY)")
|
||||
end)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
local avoidSavingLastLocations = {}
|
||||
local moneyLogs = {}
|
||||
|
||||
local function removeCharacterFunctions(character)
|
||||
local newData = {}
|
||||
for k, v in pairs(character) do
|
||||
@@ -8,6 +11,13 @@ local function removeCharacterFunctions(character)
|
||||
return newData
|
||||
end
|
||||
|
||||
local function logMoney(charId, action, account, amount, reason, trace)
|
||||
if not trace or (action == "set" and not reason) then return end
|
||||
local traceText = trace:match("%((.-)%)")
|
||||
traceText = traceText and traceText:gsub("%^%d", "") or traceText or ""
|
||||
table.insert(moneyLogs, { charId, action, account, amount, reason or "[NO REASON GIVEN]", traceText })
|
||||
end
|
||||
|
||||
local function createCharacterTable(info)
|
||||
local playerInfo = PlayersInfo[info.source] or {}
|
||||
|
||||
@@ -15,6 +25,7 @@ local function createCharacterTable(info)
|
||||
id = info.id,
|
||||
source = info.source,
|
||||
identifier = info.identifier,
|
||||
user_id = info.user_id,
|
||||
identifiers = playerInfo.identifiers or {},
|
||||
discord = playerInfo.discord or {},
|
||||
name = info.name,
|
||||
@@ -25,6 +36,7 @@ local function createCharacterTable(info)
|
||||
gender = info.gender,
|
||||
cash = info.cash,
|
||||
bank = info.bank,
|
||||
phonenumber = info.phonenumber,
|
||||
groups = info.groups,
|
||||
metadata = info.metadata,
|
||||
inventory = info.inventory
|
||||
@@ -42,6 +54,9 @@ local function createCharacterTable(info)
|
||||
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
|
||||
TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason)
|
||||
end
|
||||
|
||||
logMoney(self.id, "deduct", account, amount, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -57,6 +72,9 @@ local function createCharacterTable(info)
|
||||
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
|
||||
TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason)
|
||||
end
|
||||
|
||||
logMoney(self.id, "add", account, amount, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -65,7 +83,7 @@ local function createCharacterTable(info)
|
||||
function self.depositMoney(amount)
|
||||
local amount = tonumber(amount)
|
||||
if not amount or self.cash < amount or amount <= 0 then return end
|
||||
return self.deductMoney("cash", amount, "Deposit") and self.addMoney("bank", amount, "Deposit")
|
||||
return self.deductMoney("cash", amount, locale("deposit")) and self.addMoney("bank", amount, locale("deposit"))
|
||||
end
|
||||
|
||||
---@param amount number
|
||||
@@ -73,7 +91,7 @@ local function createCharacterTable(info)
|
||||
function self.withdrawMoney(amount)
|
||||
local amount = tonumber(amount)
|
||||
if not amount or self.bank < amount or amount <= 0 then return end
|
||||
return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw")
|
||||
return self.deductMoney("bank", amount, locale("withdraw")) and self.addMoney("cash", amount, locale("withdraw"))
|
||||
end
|
||||
|
||||
---@param data string
|
||||
@@ -103,16 +121,19 @@ local function createCharacterTable(info)
|
||||
for k, v in pairs(key) do
|
||||
self[k] = v
|
||||
if k == "cash" or k == "bank" then
|
||||
logMoney(self.id, "set", k, value, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
||||
TriggerEvent("ND:moneyChange", self.source, k, v, "set", reason)
|
||||
end
|
||||
end
|
||||
else
|
||||
self[key] = value
|
||||
if key == "cash" or key == "bank" then
|
||||
logMoney(self.id, "set", key, value, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
||||
TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason)
|
||||
end
|
||||
end
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), key)
|
||||
TriggerEvent("ND:updateCharacter", self, key)
|
||||
end
|
||||
|
||||
---@param key string|table
|
||||
@@ -126,46 +147,85 @@ local function createCharacterTable(info)
|
||||
else
|
||||
self.metadata[key] = value
|
||||
end
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata")
|
||||
TriggerEvent("ND:updateCharacter", self, "metadata")
|
||||
return self.metadata
|
||||
end
|
||||
|
||||
-- Completely delete character
|
||||
-- Mark character deleted
|
||||
function self.delete()
|
||||
local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id})
|
||||
local result = MySQL.update.await("UPDATE nd_characters SET deleted_at = NOW() WHERE charid = ?", {self.id})
|
||||
if result and NDCore.players[self.source] then
|
||||
NDCore.players[self.source] = nil
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--save last location
|
||||
function self.saveLastLocation()
|
||||
local ped = GetPlayerPed(self.source)
|
||||
if not ped or ped == 0 then return end
|
||||
|
||||
local coords = GetEntityCoords(ped)
|
||||
local heading = GetEntityHeading(ped)
|
||||
local saveLocation = true
|
||||
|
||||
for i=1, #avoidSavingLastLocations do
|
||||
local loc = avoidSavingLastLocations[i]
|
||||
if #(loc.coords-coords) < loc.dist then
|
||||
saveLocation = false
|
||||
end
|
||||
end
|
||||
|
||||
if not saveLocation then return end
|
||||
|
||||
self.setMetadata("location", {
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z,
|
||||
w = heading
|
||||
})
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Unload and save character
|
||||
function self.unload()
|
||||
if not NDCore.players[self.source] then return end
|
||||
|
||||
for name, _ in pairs(self.groups) do
|
||||
lib.removePrincipal(self.source, ("group.%s"):format(name))
|
||||
end
|
||||
local ped = GetPlayerPed(self.source)
|
||||
if ped then
|
||||
local coords = GetEntityCoords(ped)
|
||||
local heading = GetEntityHeading(ped)
|
||||
self.setMetadata("location", {
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z,
|
||||
w = heading
|
||||
})
|
||||
end
|
||||
|
||||
self.saveLastLocation()
|
||||
self.triggerEvent("ND:characterUnloaded")
|
||||
TriggerEvent("ND:characterUnloaded", self.source, self)
|
||||
|
||||
local saved = self.save()
|
||||
NDCore.players[self.source] = nil
|
||||
|
||||
return saved
|
||||
end
|
||||
|
||||
-- Save character information to database
|
||||
function self.save()
|
||||
local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ? WHERE charid = ?", {
|
||||
function self.save(key)
|
||||
if key then
|
||||
local charData = self[key]
|
||||
if not charData or type(charData) == "function" then
|
||||
return lib.print.error(key, "Is not valid character data.")
|
||||
end
|
||||
|
||||
if type(charData) == "table" then
|
||||
charData = json.encode(charData)
|
||||
end
|
||||
|
||||
local query = ("UPDATE nd_characters SET `%s` = ? WHERE charid = ?"):format(key)
|
||||
local affectedRows = MySQL.update.await(query, {charData, self.id})
|
||||
return affectedRows > 0
|
||||
end
|
||||
|
||||
local affectedRows = MySQL.update.await("UPDATE nd_characters SET user_id = ?, name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, phonenumber = ?, `groups` = ?, metadata = ? WHERE charid = ?", {
|
||||
self.user_id,
|
||||
self.name,
|
||||
self.firstname,
|
||||
self.lastname,
|
||||
@@ -173,6 +233,7 @@ local function createCharacterTable(info)
|
||||
self.gender,
|
||||
self.cash,
|
||||
self.bank,
|
||||
self.phonenumber,
|
||||
json.encode(self.groups),
|
||||
json.encode(self.metadata),
|
||||
self.id
|
||||
@@ -202,11 +263,24 @@ local function createCharacterTable(info)
|
||||
}
|
||||
|
||||
if licenses then
|
||||
self.metadata.licenses[#licenses+1] = license
|
||||
local found = false
|
||||
|
||||
for i=1, #licenses do
|
||||
if licenses[i].type == licenseType then
|
||||
self.metadata.licenses[i] = license
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
self.metadata.licenses[#licenses+1] = license
|
||||
end
|
||||
else
|
||||
self.metadata.licenses = {license}
|
||||
end
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata")
|
||||
TriggerEvent("ND:updateCharacter", self, "metadata")
|
||||
end
|
||||
|
||||
function self.getLicense(identifier)
|
||||
@@ -252,11 +326,7 @@ local function createCharacterTable(info)
|
||||
|
||||
function self.notify(...)
|
||||
if not self.source then return end
|
||||
if GetResourceState("ModernHUD") == "started" then
|
||||
TriggerClientEvent("ModernHUD:notify", self.source, ...)
|
||||
elseif GetResourceState("ox_lib") == "started" then
|
||||
TriggerClientEvent("ox_lib:notify", self.source, ...)
|
||||
end
|
||||
TriggerClientEvent("ox_lib:notify", self.source, ...)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -286,11 +356,36 @@ local function createCharacterTable(info)
|
||||
end
|
||||
|
||||
local roles = self.discord.roles
|
||||
if roles then
|
||||
if roles then
|
||||
local hasAdmin = false
|
||||
for i=1, #Config.adminDiscordRoles do
|
||||
local role = Config.adminDiscordRoles[i]
|
||||
if lib.table.contains(roles, role) then
|
||||
self.addGroup("admin")
|
||||
hasAdmin = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if hasAdmin then
|
||||
self.addGroup("admin")
|
||||
elseif self.groups["admin"] then
|
||||
self.removeGroup("admin")
|
||||
end
|
||||
|
||||
local function hasRole(groupRoles)
|
||||
for i=1, #groupRoles do
|
||||
local role = groupRoles[i]
|
||||
if lib.table.contains(roles, role) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for group, groupRoles in pairs(Config.groupRoles) do
|
||||
if hasRole(groupRoles) then
|
||||
self.addGroup(group)
|
||||
elseif self.groups[group] then
|
||||
self.removeGroup(group)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -307,22 +402,42 @@ local function createCharacterTable(info)
|
||||
---@param rank number
|
||||
---@param isJob boolean
|
||||
---@return boolean
|
||||
function self.addGroup(name, rank, isJob)
|
||||
function self.addGroup(name, rank, customGroup, isJob)
|
||||
local groupRank = tonumber(rank) or 1
|
||||
local groupInfo = Config.groups[name]
|
||||
-- if not groupInfo then return end
|
||||
local groupInfo = Config.groups?[name] or {}
|
||||
local rankData = groupInfo?.ranks?[groupRank]
|
||||
|
||||
local groupLabel = groupInfo?.label or name
|
||||
local rankName = rankData?.label or groupRank
|
||||
local isBoss = rankData?.isBoss or false
|
||||
|
||||
if customGroup then
|
||||
if customGroup.label then groupLabel = customGroup.label end
|
||||
if customGroup.rankName then rankName = customGroup.rankName end
|
||||
if customGroup.isBoss ~= nil then isBoss = customGroup.isBoss end
|
||||
end
|
||||
|
||||
if isJob then
|
||||
for _, group in pairs(self.groups) do
|
||||
group.isJob = nil
|
||||
end
|
||||
end
|
||||
|
||||
self.groups[name] = {
|
||||
label = groupInfo and groupInfo.label or name,
|
||||
rankName = groupInfo and groupInfo.ranks[groupRank] or groupRank,
|
||||
name = name,
|
||||
label = groupLabel,
|
||||
rankName = rankName,
|
||||
rank = groupRank,
|
||||
isJob = isJob
|
||||
isJob = isJob,
|
||||
isBoss = isBoss
|
||||
}
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
||||
|
||||
if not isJob then
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups")
|
||||
TriggerEvent("ND:updateCharacter", self, "groups")
|
||||
end
|
||||
|
||||
TriggerEvent("ND:groupAdded", self, self.groups[name])
|
||||
lib.addPrincipal(self.source, ("group.%s"):format(name))
|
||||
return self.groups[name]
|
||||
end
|
||||
@@ -337,8 +452,16 @@ local function createCharacterTable(info)
|
||||
function self.removeGroup(name)
|
||||
local group = self.groups[name]
|
||||
if not group then return end
|
||||
|
||||
if self.job == name then
|
||||
self.job = nil
|
||||
self.jobInfo = nil
|
||||
end
|
||||
|
||||
self.groups[name] = nil
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups")
|
||||
TriggerEvent("ND:updateCharacter", self, "groups")
|
||||
TriggerEvent("ND:groupRemoved", self, group)
|
||||
lib.removePrincipal(self.source, ("group.%s"):format(name))
|
||||
return group
|
||||
end
|
||||
@@ -346,14 +469,20 @@ local function createCharacterTable(info)
|
||||
---@param name string
|
||||
---@param rank number
|
||||
---@return boolean
|
||||
function self.setJob(name, rank)
|
||||
self.removeGroup(self.job)
|
||||
local job = self.addGroup(name, rank, true)
|
||||
local jobName, jobInfo = self.getJob()
|
||||
if jobInfo then
|
||||
self.job = jobName
|
||||
self.jobInfo = jobInfo
|
||||
function self.setJob(name, rank, customGroup, keepGroup)
|
||||
if not keepGroup then
|
||||
self.removeGroup(self.job)
|
||||
end
|
||||
|
||||
local job = self.addGroup(name, rank, customGroup, true)
|
||||
|
||||
if job then
|
||||
self.job = job.name
|
||||
self.jobInfo = job
|
||||
end
|
||||
|
||||
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "job")
|
||||
TriggerEvent("ND:updateCharacter", self, "job")
|
||||
return job
|
||||
end
|
||||
|
||||
@@ -376,16 +505,48 @@ local function createCharacterTable(info)
|
||||
return self
|
||||
end
|
||||
|
||||
function NDCore.avoidSavingLocation(coords, dist)
|
||||
avoidSavingLastLocations[#avoidSavingLastLocations+1] = { coords = coords, dist = dist}
|
||||
end
|
||||
|
||||
function NDCore.validateCharacterData(charInfo)
|
||||
local errors = {}
|
||||
|
||||
local function checkLength(field, value, maxLength)
|
||||
if type(value) ~= "string" then return end
|
||||
if #value > maxLength then
|
||||
table.insert(errors, string.format("%s is too long (max %d, got %d)", field, maxLength, #value))
|
||||
end
|
||||
end
|
||||
|
||||
if charInfo.name then checkLength("game name", charInfo.name, 50) end
|
||||
if charInfo.firstname then checkLength("firstname", charInfo.firstname, 50) end
|
||||
if charInfo.lastname then checkLength("lastname", charInfo.lastname, 50) end
|
||||
if charInfo.dob then checkLength("dob", charInfo.dob, 50) end
|
||||
if charInfo.gender then checkLength("gender", charInfo.gender, 50) end
|
||||
if type(charInfo.cash) ~= "number" then table.insert(errors, "cash must be a number") end
|
||||
if type(charInfo.bank) ~= "number" then table.insert(errors, "bank must be a number") end
|
||||
|
||||
if #errors > 0 then
|
||||
return false, errors
|
||||
else
|
||||
return true, {}
|
||||
end
|
||||
end
|
||||
|
||||
---@param src number
|
||||
---@param info table
|
||||
---@return table
|
||||
function NDCore.newCharacter(src, info)
|
||||
local identifier = GetPlayerIdentifierByType(src, Config.characterIdentifier)
|
||||
local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
||||
if not identifier then return end
|
||||
|
||||
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
||||
|
||||
local charInfo = {
|
||||
source = src,
|
||||
identifier = identifier,
|
||||
user_id = userId,
|
||||
name = GetPlayerName(src) or "",
|
||||
firstname = info.firstname or "",
|
||||
lastname = info.lastname or "",
|
||||
@@ -393,13 +554,27 @@ function NDCore.newCharacter(src, info)
|
||||
gender = info.gender or "",
|
||||
cash = info.cash or 0,
|
||||
bank = info.bank or 0,
|
||||
phonenumber = info.phonenumber,
|
||||
groups = info.groups or {},
|
||||
metadata = info.metadata or {},
|
||||
inventory = info.inventory or {},
|
||||
inventory = info.inventory or {}
|
||||
}
|
||||
|
||||
charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, groups, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", {
|
||||
local valid, issues = NDCore.validateCharacterData(charInfo)
|
||||
if not valid then
|
||||
for _, err in ipairs(issues) do
|
||||
Wait(100)
|
||||
TriggerClientEvent("ND:notifyClient", src, {
|
||||
description = err,
|
||||
type = "error"
|
||||
})
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, user_id, name, firstname, lastname, dob, gender, cash, bank, phonenumber, `groups`, metadata, inventory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", {
|
||||
identifier,
|
||||
userId,
|
||||
charInfo.name,
|
||||
charInfo.firstname,
|
||||
charInfo.lastname,
|
||||
@@ -407,8 +582,10 @@ function NDCore.newCharacter(src, info)
|
||||
charInfo.gender,
|
||||
charInfo.cash,
|
||||
charInfo.bank,
|
||||
charInfo.phonenumber,
|
||||
json.encode(charInfo.groups),
|
||||
json.encode(charInfo.metadata)
|
||||
json.encode(charInfo.metadata),
|
||||
json.encode(charInfo.inventory)
|
||||
})
|
||||
|
||||
return createCharacterTable(charInfo)
|
||||
@@ -419,17 +596,30 @@ end
|
||||
function NDCore.fetchCharacter(id, src)
|
||||
local result
|
||||
if src then
|
||||
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? and identifier = ?", {id, GetPlayerIdentifierByType(src, Config.characterIdentifier)})
|
||||
local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
||||
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
||||
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? AND (identifier = ? OR user_id = ?)", {id, identifier, userId})
|
||||
else
|
||||
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id})
|
||||
end
|
||||
|
||||
if not result then return end
|
||||
local info = result[1]
|
||||
local info = result?[1]
|
||||
if not info then return end
|
||||
|
||||
-- Check if character has user_id, if not assign it
|
||||
if not info.user_id and src then
|
||||
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
||||
if userId then
|
||||
MySQL.update.await("UPDATE nd_characters SET user_id = ? WHERE charid = ?", {userId, info.charid})
|
||||
info.user_id = userId
|
||||
end
|
||||
end
|
||||
|
||||
return createCharacterTable({
|
||||
source = src,
|
||||
id = info.charid,
|
||||
identifier = info.identifier,
|
||||
user_id = info.user_id,
|
||||
name = info.name,
|
||||
firstname = info.firstname,
|
||||
lastname = info.lastname,
|
||||
@@ -437,6 +627,7 @@ function NDCore.fetchCharacter(id, src)
|
||||
gender = info.gender,
|
||||
cash = info.cash,
|
||||
bank = info.bank,
|
||||
phonenumber = info.phonenumber,
|
||||
groups = json.decode(info.groups),
|
||||
metadata = json.decode(info.metadata),
|
||||
inventory = json.decode(info.inventory)
|
||||
@@ -446,15 +637,27 @@ end
|
||||
---@param src number
|
||||
---@return table
|
||||
function NDCore.fetchAllCharacters(src)
|
||||
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
||||
local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
||||
local characters = {}
|
||||
local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {GetPlayerIdentifierByType(src, Config.characterIdentifier)})
|
||||
|
||||
-- Query by either identifier OR user_id, excluding deleted characters
|
||||
local result = MySQL.query.await("SELECT * FROM nd_characters WHERE (identifier = ? OR user_id = ?) AND deleted_at IS NULL", {identifier, userId})
|
||||
|
||||
for i=1, #result do
|
||||
local info = result[i]
|
||||
|
||||
-- Check if character has user_id, if not assign it
|
||||
if not info.user_id and userId then
|
||||
MySQL.update.await("UPDATE nd_characters SET user_id = ? WHERE charid = ?", {userId, info.charid})
|
||||
info.user_id = userId
|
||||
end
|
||||
|
||||
characters[info.charid] = createCharacterTable({
|
||||
source = src,
|
||||
id = info.charid,
|
||||
identifier = info.identifier,
|
||||
user_id = info.user_id,
|
||||
name = info.name,
|
||||
firstname = info.firstname,
|
||||
lastname = info.lastname,
|
||||
@@ -462,9 +665,10 @@ function NDCore.fetchAllCharacters(src)
|
||||
gender = info.gender,
|
||||
cash = info.cash,
|
||||
bank = info.bank,
|
||||
phonenumber = info.phonenumber,
|
||||
groups = json.decode(info.groups),
|
||||
metadata = json.decode(info.metadata),
|
||||
inventory = json.decode(info.inventory),
|
||||
inventory = json.decode(info.inventory)
|
||||
})
|
||||
end
|
||||
return characters
|
||||
@@ -484,3 +688,30 @@ function NDCore.setActiveCharacter(src, id)
|
||||
character.active()
|
||||
return character
|
||||
end
|
||||
|
||||
-- runs every 10 minutes, saves all characters and money logs.
|
||||
lib.cron.new("*/10 * * * *", function()
|
||||
local players = NDCore.getPlayers()
|
||||
for _, player in pairs(players) do
|
||||
player.saveLastLocation()
|
||||
player.save()
|
||||
end
|
||||
|
||||
if #moneyLogs == 0 then return end
|
||||
|
||||
local placeholders = {}
|
||||
local params = {}
|
||||
local logs = moneyLogs
|
||||
moneyLogs = {}
|
||||
|
||||
for i=1, #logs do
|
||||
local row = logs[i]
|
||||
for j = 1, 6 do
|
||||
table.insert(params, row[j])
|
||||
end
|
||||
table.insert(placeholders, "(?, ?, ?, ?, ?, ?)")
|
||||
end
|
||||
|
||||
local query = "INSERT INTO nd_money_log (character_id, action, account, amount, reason, trace) VALUES " .. table.concat(placeholders, ",")
|
||||
MySQL.prepare(query, params)
|
||||
end)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
local ox_inventory
|
||||
local inventoryStarted = false
|
||||
local spawnedPlayerVehicles = {}
|
||||
local vehicleTypes = json.decode(GetResourceKvpString("ND_Core:vehTypes") or "[]")
|
||||
|
||||
NDCore.isResourceStarted("ox_inventory", function(started)
|
||||
inventoryStarted = started
|
||||
@@ -8,23 +9,62 @@ NDCore.isResourceStarted("ox_inventory", function(started)
|
||||
ox_inventory = exports.ox_inventory
|
||||
end)
|
||||
|
||||
local function getVehicleType(model)
|
||||
local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, true, true)
|
||||
local function getVehicleType(coords, model)
|
||||
if vehicleTypes[model] then
|
||||
return vehicleTypes[model]
|
||||
end
|
||||
|
||||
local tempVehicle = CreateVehicle(model, coords.x, coords.y, coords.z-5.0, coords.w, true, false)
|
||||
local time = os.time()
|
||||
while not DoesEntityExist(tempVehicle) and time-os.time() < 5 do Wait(5) end
|
||||
|
||||
|
||||
while not DoesEntityExist(tempVehicle) and os.time()-time < 5 do Wait(0) end
|
||||
if not DoesEntityExist(tempVehicle) then return end
|
||||
local entityType = GetVehicleType(tempVehicle)
|
||||
|
||||
local vehType = GetVehicleType(tempVehicle)
|
||||
DeleteEntity(tempVehicle)
|
||||
return entityType
|
||||
|
||||
vehicleTypes[model] = vehType
|
||||
SetResourceKvp("ND_Core:vehTypes", json.encode(vehicleTypes))
|
||||
|
||||
return vehType
|
||||
end
|
||||
|
||||
local function generatePlate()
|
||||
local plate = {}
|
||||
for i=1, 8 do
|
||||
plate[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9)
|
||||
local i = 1
|
||||
local p = 1
|
||||
|
||||
-- ensure pattern exists and pad to 8 chars
|
||||
local pattern = Config.platePattern
|
||||
if #pattern < 8 then
|
||||
pattern = pattern .. string.rep(" ", 8 - #pattern)
|
||||
end
|
||||
|
||||
while i <= 8 do
|
||||
local char = pattern:sub(p, p)
|
||||
|
||||
if char == "^" then
|
||||
-- literal next character
|
||||
p = p + 1
|
||||
plate[i] = pattern:sub(p, p)
|
||||
elseif char == "1" then
|
||||
plate[i] = tostring(math.random(0, 9))
|
||||
elseif char == "A" then
|
||||
plate[i] = string.char(math.random(65, 90)) -- A–Z
|
||||
elseif char == "." then
|
||||
if math.random(0, 1) == 0 then
|
||||
plate[i] = tostring(math.random(0, 9))
|
||||
else
|
||||
plate[i] = string.char(math.random(65, 90))
|
||||
end
|
||||
else
|
||||
plate[i] = char
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
p = p + 1
|
||||
end
|
||||
|
||||
return table.concat(plate)
|
||||
end
|
||||
|
||||
@@ -44,11 +84,14 @@ local function getVehicleDatabaseInfo(vehicle)
|
||||
if not vehicle then return end
|
||||
local stored = vehicle.stored == 1
|
||||
local impounded = vehicle.impounded == 1
|
||||
local properties = json.decode(vehicle.properties) or {}
|
||||
properties.plate = vehicle.plate
|
||||
|
||||
return {
|
||||
id = vehicle.id,
|
||||
owner = vehicle.owner,
|
||||
plate = vehicle.plate,
|
||||
properties = json.decode(vehicle.properties) or {},
|
||||
properties = properties,
|
||||
stored = stored,
|
||||
impounded = impounded,
|
||||
stolen = vehicle.stolen == 1,
|
||||
@@ -57,6 +100,14 @@ local function getVehicleDatabaseInfo(vehicle)
|
||||
}
|
||||
end
|
||||
|
||||
function NDCore.generateVehiclePlate()
|
||||
return generatePlate()
|
||||
end
|
||||
|
||||
function NDCore.generateUniqueVehiclePlate()
|
||||
return generateVehiclePlate()
|
||||
end
|
||||
|
||||
--- get vehicle information queried from the database by the vehicle id
|
||||
---@param vehicleId string | numb
|
||||
---@return table | nil
|
||||
@@ -84,16 +135,53 @@ function NDCore.getVehicle(entity)
|
||||
netId = NetworkGetNetworkIdFromEntity(entity)
|
||||
}
|
||||
|
||||
-- save properties to db
|
||||
function self.saveProperties(props, propsToSave)
|
||||
local properties = {}
|
||||
local vehProps = props or lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity), self.netId)
|
||||
|
||||
if propsToSave then
|
||||
properties = self.properties
|
||||
if vehProps then
|
||||
for prop, value in pairs(vehProps) do
|
||||
if lib.table.contains(propsToSave, prop) then
|
||||
properties[prop] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
properties = vehProps
|
||||
end
|
||||
|
||||
if self.properties?.callsign then
|
||||
properties.callsign = true
|
||||
end
|
||||
|
||||
self.properties = properties
|
||||
state.props = properties
|
||||
|
||||
MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id})
|
||||
end
|
||||
|
||||
--- delete the vehicle
|
||||
function self.delete(saveProperties)
|
||||
function self.delete(saveProperties, propsToSave)
|
||||
if not DoesEntityExist(entity) then return end
|
||||
if saveProperties and self.id and self.owner then
|
||||
local properties = lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity), self.netId)
|
||||
if properties then
|
||||
MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id})
|
||||
end
|
||||
self.saveProperties(false, propsToSave)
|
||||
end
|
||||
DeleteEntity(entity)
|
||||
if DoesEntityExist(entity) then
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- remvoe vehicle from db.
|
||||
function self.remove(keepEntity)
|
||||
if not keepEntity and DoesEntityExist(entity) then
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
MySQL.query("DELETE FROM nd_vehicles WHERE id = ?", {self.id})
|
||||
end
|
||||
|
||||
--- set vehicle properties
|
||||
@@ -102,11 +190,17 @@ function NDCore.getVehicle(entity)
|
||||
if not DoesEntityExist(entity) then return end
|
||||
local properties = type(props) == "string" and json.decode(props) or props
|
||||
local state = Entity(entity).state
|
||||
|
||||
if self.properties?.callsign then
|
||||
properties.callsign = true
|
||||
end
|
||||
|
||||
state.allPropsApplied = false
|
||||
state.props = properties
|
||||
self.properties = properties
|
||||
|
||||
if not self.id or not self.owner then return end
|
||||
MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id})
|
||||
self.saveProperties(properties)
|
||||
end
|
||||
|
||||
--- set vehicle locked/unlocked
|
||||
@@ -126,6 +220,7 @@ function NDCore.getVehicle(entity)
|
||||
if self.properties then
|
||||
self.properties.plate = plate
|
||||
local state = Entity(entity).state
|
||||
state.allPropsApplied = false
|
||||
state.props = self.properties
|
||||
end
|
||||
|
||||
@@ -137,9 +232,9 @@ function NDCore.getVehicle(entity)
|
||||
--- set the vehicle availability status
|
||||
---@param statusType string
|
||||
---@param status boolean
|
||||
function self.setStatus(statusType, status)
|
||||
function self.setStatus(statusType, status, keepEntity)
|
||||
if not lib.table.contains({"stored", "impounded", "stolen"}, statusType) then return end
|
||||
if statusType ~= "stolen" then self.delete(true) end
|
||||
if statusType ~= "stolen" and not keepEntity then self.delete(true) end
|
||||
if not self.id or not self.owner then return end
|
||||
local query = ("UPDATE nd_vehicles SET %s = ? WHERE id = ?"):format(statusType)
|
||||
MySQL.query(query, {status and 1 or 0, self.id})
|
||||
@@ -148,13 +243,22 @@ function NDCore.getVehicle(entity)
|
||||
|
||||
function self.setMetadata(key, value)
|
||||
self.metadata[key] = value
|
||||
|
||||
if DoesEntityExist(entity) then
|
||||
local state = Entity(self.entity).state
|
||||
local metadata = state.metadata
|
||||
metadata[key] = value
|
||||
state.metadata = metadata
|
||||
|
||||
if not state.metadata then
|
||||
state.metadata = self.metadata
|
||||
else
|
||||
state.metadata[key] = value
|
||||
end
|
||||
end
|
||||
if not self.id or not self.owner then return end
|
||||
|
||||
if not self.id or not self.owner then
|
||||
lib.print.debug(('Skipped storing metadata for vehicle %s as it does not have an id/owner associated with it.'):format(self.entity))
|
||||
return
|
||||
end
|
||||
|
||||
MySQL.query("UPDATE nd_vehicles SET metadata = ? WHERE id = ?", {json.encode(self.metadata), self.id})
|
||||
end
|
||||
|
||||
@@ -205,14 +309,15 @@ function NDCore.giveVehicleAccess(source, vehicle, access, info)
|
||||
end
|
||||
|
||||
if not inventoryStarted or not Config.useInventoryForKeys then return end
|
||||
local plate = info?.plate or GetVehicleNumberPlateText(vehicle)
|
||||
local model = info?.model or GetEntityModel(vehicle)
|
||||
local modelName = info?.modelName or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or ""
|
||||
local hasKey = ox_inventory:GetSlotIdWithItem(source, "keys", {
|
||||
vehId = vehicleId
|
||||
})
|
||||
|
||||
if access and not hasKey then
|
||||
local plate = info?.plate or GetVehicleNumberPlateText(vehicle)
|
||||
local model = info?.model or GetEntityModel(vehicle)
|
||||
local modelName = info?.modelName or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or ""
|
||||
|
||||
ox_inventory:AddItem(source, "keys", 1, {
|
||||
vehOwner = owner or state.owner,
|
||||
vehId = vehicleId,
|
||||
@@ -245,14 +350,14 @@ function NDCore.createVehicle(info)
|
||||
end
|
||||
|
||||
local model = info.model or properties.model
|
||||
local vehType = getVehicleType(model)
|
||||
local vehType = getVehicleType(spawnCoords, model)
|
||||
if not vehType then
|
||||
return Citizen.Trace("NDCore.createVehicle", "vehType not found")
|
||||
end
|
||||
|
||||
local veh = CreateVehicleServerSetter(model, vehType, spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w)
|
||||
local time = os.time()
|
||||
while not DoesEntityExist(veh) and time-os.time() < 5 do Wait(5) end
|
||||
while not DoesEntityExist(veh) and os.time()-time < 5 do Wait(5) end
|
||||
if not veh or not DoesEntityExist(veh) then
|
||||
return Citizen.Trace("NDCore.createVehicle", "vehicle entity doesn't exist")
|
||||
end
|
||||
@@ -262,17 +367,24 @@ function NDCore.createVehicle(info)
|
||||
state.locked = true
|
||||
local keys = info.keys or {}
|
||||
|
||||
if not properties.plate then
|
||||
properties.plate = generateVehiclePlate()
|
||||
if not properties.plate or info.plate then
|
||||
properties.plate = info.plate or generateVehiclePlate()
|
||||
end
|
||||
if owner then
|
||||
keys[owner] = true
|
||||
state.owner = owner
|
||||
end
|
||||
if info.blipGroups then
|
||||
state.blipGroups = info.blipGroups
|
||||
end
|
||||
|
||||
state.keys = keys
|
||||
state.props = properties
|
||||
state.id = vehicleId
|
||||
|
||||
if not info.ignoreProps then
|
||||
state.props = properties
|
||||
end
|
||||
|
||||
local vehicleName
|
||||
if inventoryStarted and Config.useInventoryForKeys then
|
||||
for charId, _ in pairs(keys) do
|
||||
@@ -288,7 +400,7 @@ function NDCore.createVehicle(info)
|
||||
NDCore.giveVehicleAccess(playerSource, veh, true, {
|
||||
vehicleId = vehicleId,
|
||||
netId = netId,
|
||||
plate = properties?.plate,
|
||||
plate = info.plate or properties?.plate,
|
||||
model = model,
|
||||
vehicleName = vehicleName,
|
||||
owner = owner
|
||||
@@ -297,6 +409,13 @@ function NDCore.createVehicle(info)
|
||||
end
|
||||
end
|
||||
|
||||
for i=1, 3 do
|
||||
if DoesEntityExist(veh) then
|
||||
SetVehicleNumberPlateText(veh, properties.plate)
|
||||
end
|
||||
Wait(500)
|
||||
end
|
||||
|
||||
return NDCore.getVehicle(veh)
|
||||
end
|
||||
|
||||
@@ -339,14 +458,14 @@ function NDCore.transferVehicleOwnership(vehicleId, fromSource, toSource)
|
||||
end
|
||||
|
||||
playerFrom.notify({
|
||||
title = "Ownership transfered",
|
||||
description = ("Vehicle ownership of %s has been transfered."):format(vehicle.plate),
|
||||
title = locale("vehicle_ownership_transfered"),
|
||||
description = locale("vehicle_ownership_transfered2", vehicle.plate),
|
||||
position = "bottom-right",
|
||||
type = "success"
|
||||
})
|
||||
playerTo.notify({
|
||||
title = "Ownership received",
|
||||
description = ("Received vehicle ownership of %s."):format(vehicle.plate),
|
||||
title = locale("vehicle_ownership_received"),
|
||||
description = locale("vehicle_ownership_received2", vehicle.plate),
|
||||
position = "bottom-right"
|
||||
})
|
||||
return true
|
||||
@@ -357,8 +476,8 @@ end
|
||||
---@param properties table
|
||||
---@param stored boolean
|
||||
---@return vehicleId number
|
||||
function NDCore.setVehicleOwned(playerId, properties, stored)
|
||||
local plate = generateVehiclePlate()
|
||||
function NDCore.setVehicleOwned(playerId, properties, stored, setPlate)
|
||||
local plate = setPlate or generateVehiclePlate()
|
||||
properties.plate = plate
|
||||
return MySQL.insert.await("INSERT INTO nd_vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {playerId, plate, json.encode(properties), stored and 1 or 0})
|
||||
end
|
||||
@@ -380,15 +499,15 @@ function NDCore.shareVehicleKeys(source, target, vehicle)
|
||||
local plate = GetVehicleNumberPlateText(vehicle)
|
||||
|
||||
player.notify({
|
||||
title = "Keys shared",
|
||||
description = ("You've shared vehicle keys to %s."):format(plate),
|
||||
title = locale("keys_shared"),
|
||||
description = locale("keys_shared2", plate),
|
||||
position = "bottom-right",
|
||||
type = "success",
|
||||
type = "success"
|
||||
})
|
||||
targetPlayer.notify({
|
||||
title = "Keys received",
|
||||
description = ("Received vehicle keys to %s."):format(plate),
|
||||
position = "bottom-right",
|
||||
title = locale("keys_received"),
|
||||
description = locale("keys_received2", plate),
|
||||
position = "bottom-right"
|
||||
})
|
||||
return true
|
||||
end
|
||||
@@ -398,19 +517,36 @@ end
|
||||
---@param vehicleId number
|
||||
---@param coords vector4
|
||||
---@return table
|
||||
function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading)
|
||||
function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading, _vehicle)
|
||||
local player = NDCore.getPlayer(source)
|
||||
if not player then return end
|
||||
|
||||
local vehicle = NDCore.getVehicleById(vehicleId)
|
||||
if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end
|
||||
local vehicle = _vehicle or NDCore.getVehicleById(vehicleId)
|
||||
if not vehicle or vehicle.owner ~= player.id then return end
|
||||
if not vehicle.available and not vehicle.impounded then return end
|
||||
|
||||
MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId})
|
||||
|
||||
local properties = vehicle.properties
|
||||
if properties.callsign then
|
||||
local callsign = player.getMetadata("callsign")
|
||||
if not callsign or callsign == "" then goto skip end
|
||||
|
||||
callsign = tostring(callsign)
|
||||
if not callsign or callsign:len() ~= 3 then goto skip end
|
||||
|
||||
properties.modFender = tonumber(callsign:sub(1, 1))
|
||||
properties.modRightFender = tonumber(callsign:sub(2, 2))
|
||||
properties.modRoof = tonumber(callsign:sub(3, 3))
|
||||
end
|
||||
|
||||
::skip::
|
||||
|
||||
return NDCore.createVehicle({
|
||||
owner = player.id,
|
||||
model = vehicle.properties.model,
|
||||
model = properties.model,
|
||||
coords = vec4(coords.x, coords.y, coords.z, coords.w or coords.heading or heading),
|
||||
properties = vehicle.properties,
|
||||
properties = properties,
|
||||
vehicleId = vehicleId,
|
||||
source = source
|
||||
})
|
||||
@@ -421,16 +557,16 @@ local function toggleVehicleLock(source, entity, nearby, metadata)
|
||||
|
||||
if metadata and not metadata.keyEnabled then
|
||||
return player.notify({
|
||||
title = "No signal",
|
||||
description = "Vehicle key disabled.",
|
||||
title = locale("no_signal"),
|
||||
description = locale("veh_key_disabled"),
|
||||
type = "error",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
})
|
||||
elseif not nearby then
|
||||
return player.notify({
|
||||
title = "No signal",
|
||||
description = "Vehicle to far away.",
|
||||
title = locale("no_signal"),
|
||||
description = locale("veh_far_away"),
|
||||
type = "error",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -444,16 +580,16 @@ local function toggleVehicleLock(source, entity, nearby, metadata)
|
||||
player.triggerEvent("ND_Vehicles:keyFob", vehicle.netId)
|
||||
if locked then
|
||||
return player.notify({
|
||||
title = "LOCKED",
|
||||
description = "Your vehicle has now been locked.",
|
||||
title = locale("veh_locked"),
|
||||
description = locale("veh_locked2"),
|
||||
type = "success",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
})
|
||||
end
|
||||
player.notify({
|
||||
title = "UNLOCKED",
|
||||
description = "Your vehicle has now been unlocked.",
|
||||
title = locale("veh_unlocked"),
|
||||
description = locale("veh_unlocked2"),
|
||||
type = "inform",
|
||||
position = "bottom-right",
|
||||
duration = 3000
|
||||
@@ -494,6 +630,16 @@ local function lockNearestVehicle(source, vehId, metadata)
|
||||
end
|
||||
end
|
||||
|
||||
local function hasBlipGroup(player, groups)
|
||||
groups = groups or {}
|
||||
for i=1, #groups do
|
||||
local group = groups[i]
|
||||
if player.groups[group] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- inventory keys using item.
|
||||
exports("keys", function(event, item, inventory, slot, data)
|
||||
if event ~= "usingItem" or not Config.useInventoryForKeys or not inventoryStarted then return end
|
||||
@@ -520,7 +666,9 @@ RegisterCommand("getkeys", function(source, args, rawCommand)
|
||||
local player = NDCore.getPlayer(source)
|
||||
local state = Entity(veh).state
|
||||
local owner = state.owner
|
||||
if not owner or owner ~= player.id then return end
|
||||
local blipGroups = state.blipGroups
|
||||
|
||||
if not state.id or (not owner or owner ~= player.id) and (not hasBlipGroup(player, blipGroups)) then return end
|
||||
|
||||
local props = state.props
|
||||
ox_inventory:AddItem(source, "keys", 1, {
|
||||
@@ -567,20 +715,32 @@ RegisterNetEvent("ND_Vehicles:toggleVehicleLock", function(netId)
|
||||
end)
|
||||
|
||||
-- locking of npc vehicles, if the players spawns inside a vehicle it won't be locked.
|
||||
RegisterNetEvent("entityCreated", function(entity)
|
||||
if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end
|
||||
local state = Entity(entity).state
|
||||
if state.owner or state.locked ~= nil then return end
|
||||
-- AddEventHandler("entityCreated", function(entity)
|
||||
-- local time = os.time()
|
||||
-- while not DoesEntityExist(entity) and os.time()-time < 5 do Wait(50) end
|
||||
|
||||
local driver = GetPedInVehicleSeat(entity, -1)
|
||||
if DoesEntityExist(driver) and IsPedAPlayer(driver) then
|
||||
state.locked = false
|
||||
state.hotwired = true
|
||||
end
|
||||
-- if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end
|
||||
|
||||
if math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end
|
||||
state.locked = true
|
||||
end)
|
||||
-- local state = Entity(entity).state
|
||||
-- if state.owner or state.locked ~= nil then return end
|
||||
|
||||
-- time = os.time()
|
||||
-- local driver = GetPedInVehicleSeat(entity, -1)
|
||||
-- while DoesEntityExist(entity) and driver == 0 and os.time()-time < 2 do
|
||||
-- driver = GetPedInVehicleSeat(entity, -1)
|
||||
-- Wait(100)
|
||||
-- end
|
||||
|
||||
-- if not DoesEntityExist(entity) then return end
|
||||
|
||||
-- if DoesEntityExist(driver) and IsPedAPlayer(driver) then
|
||||
-- state.locked = false
|
||||
-- state.hotwired = true
|
||||
-- end
|
||||
|
||||
-- if math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end
|
||||
-- state.locked = true
|
||||
-- end)
|
||||
|
||||
-- disables inventory vehicles keys, disabled vehicles keys can no longer be used. Kinda like taking the battery out.
|
||||
RegisterNetEvent("ND_Vehicles:disableKey", function(slot)
|
||||
@@ -622,71 +782,12 @@ RegisterNetEvent("ND_Vehicles:hotwire", function(netId)
|
||||
state.hotwired = true
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ND_Vehicles:storeVehicle", function(netId)
|
||||
local src = source
|
||||
local vehicle = NDCore.getVehicle(NetworkGetEntityFromNetworkId(netId))
|
||||
if not vehicle then return end
|
||||
RegisterNetEvent("ND_Vehicles:propsApplied", function(netId)
|
||||
local veh = NetworkGetEntityFromNetworkId(netId)
|
||||
if not veh or not DoesEntityExist(veh) then return end
|
||||
|
||||
local player = NDCore.getPlayer(src)
|
||||
if not vehicle.setStatus("stored", true) or not player or player.id ~= vehicle.owner then
|
||||
return player.notify({
|
||||
title = "Garage",
|
||||
description = "No owned vehicle found nearby.",
|
||||
type = "error",
|
||||
position = "bottom",
|
||||
duration = 3000
|
||||
})
|
||||
end
|
||||
player.notify({
|
||||
title = "Garage",
|
||||
description = "Vehicle stored in garage.",
|
||||
type = "success",
|
||||
position = "bottom",
|
||||
duration = 3000
|
||||
})
|
||||
NDCore.giveVehicleAccess(src, vehicle.entity, false, {
|
||||
vehicleId = vehicle.id,
|
||||
netId = vehicle.netId,
|
||||
owner = vehicle.owner
|
||||
})
|
||||
end)
|
||||
|
||||
local function isParkingAvailable(locations)
|
||||
for i=1, #locations do
|
||||
local loc = locations[math.random(1, #locations)]
|
||||
if #getNearbyVehicles(vec3(loc.x, loc.y, loc.z), 2.0) == 0 then
|
||||
return loc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations)
|
||||
local src = source
|
||||
local vehicle = NDCore.getVehicleById(vehId)
|
||||
local player = NDCore.getPlayer(src)
|
||||
if not player or not vehicle or vehicle.owner ~= player.id then return end
|
||||
if vehicle.impounded then
|
||||
local reclaimPrice = vehicle.metadata.impoundReclaimPrice or 200
|
||||
if not player.deductMoney("bank", reclaimPrice, "Vehicle impound reclaim") then
|
||||
return player.notify({
|
||||
title = "Impound",
|
||||
description = ("Price to reclaim is $%d, you don't have enough!"):format(reclaimPrice),
|
||||
type = "error",
|
||||
position = "bottom"
|
||||
})
|
||||
end
|
||||
player.notify({
|
||||
title = "Impound",
|
||||
description = ("Paid $%d to reclaim vehicle!"):format(reclaimPrice),
|
||||
type = "success",
|
||||
position = "bottom"
|
||||
})
|
||||
MySQL.query.await("UPDATE nd_vehicles SET impounded = ? WHERE id = ?", {0, vehicle.id})
|
||||
end
|
||||
|
||||
local info = NDCore.spawnOwnedVehicle(src, vehicle.id, isParkingAvailable(locations))
|
||||
if not info then return end
|
||||
TriggerClientEvent("ND_Vehicles:blip", src, info.netId, true)
|
||||
local state = Entity(veh).state
|
||||
state:set("allPropsApplied", true, true)
|
||||
end)
|
||||
|
||||
lib.callback.register("ND_Vehicles:getOwnedVehicles", function(src)
|
||||
@@ -694,3 +795,30 @@ lib.callback.register("ND_Vehicles:getOwnedVehicles", function(src)
|
||||
if not player then return end
|
||||
return NDCore.getVehicles(player.id)
|
||||
end)
|
||||
|
||||
AddEventHandler("ND:characterLoaded", function(player)
|
||||
local ownedExistingVehicles = {}
|
||||
local ownedVehicles = NDCore.getVehicles(player.id)
|
||||
local vehicles = GetAllVehicles()
|
||||
local vehiclesToImpound = {}
|
||||
|
||||
for i=1, #vehicles do
|
||||
local veh = vehicles[i]
|
||||
local state = Entity(veh).state
|
||||
if state.owner == player.id then
|
||||
ownedExistingVehicles[#ownedExistingVehicles+1] = state.id
|
||||
end
|
||||
end
|
||||
|
||||
for i=1, #ownedVehicles do
|
||||
local veh = ownedVehicles[i]
|
||||
if veh and not veh.stored and not veh.impounded and not lib.table.contains(ownedExistingVehicles, veh.id) then
|
||||
vehiclesToImpound[#vehiclesToImpound+1] = veh.id
|
||||
end
|
||||
end
|
||||
|
||||
if #vehiclesToImpound == 0 then return end
|
||||
|
||||
local query = ("UPDATE nd_vehicles SET stored = ? WHERE owner = ? AND id IN (%s)"):format(table.concat(vehiclesToImpound, ", "))
|
||||
MySQL.rawExecute(query, {1, player.id})
|
||||
end)
|
||||
|
||||
13
ui/index.html
Normal file
13
ui/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script type="module" src="main.js" defer></script>
|
||||
<title>Group Management</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
283
ui/main.js
Normal file
283
ui/main.js
Normal file
@@ -0,0 +1,283 @@
|
||||
import van from "https://cdn.jsdelivr.net/gh/vanjs-org/van/public/van-1.5.3.min.js";
|
||||
import { nuiMessage } from "./modules/listener.js";
|
||||
import { fetchCallback } from "./modules/fetch.js";
|
||||
import { closeUI, setOpen } from "./modules/close.js";
|
||||
|
||||
const { div, h1, h2, h3, p, button, input, label, span, i: icon } = van.tags;
|
||||
|
||||
let groups = [];
|
||||
let container = null;
|
||||
let editPanel = null;
|
||||
let currentEdit = null;
|
||||
|
||||
function createGroupCard(group) {
|
||||
const card = div({ class: "group-card", onclick: () => openEditPanel(group) },
|
||||
div({ class: "group-card-header" },
|
||||
h3(group.label),
|
||||
span({ class: "group-name" }, group.name)
|
||||
),
|
||||
div({ class: "group-card-tags" },
|
||||
group.isJob ? span({ class: "tag tag-job" }, "Job") : null,
|
||||
span({ class: "tag tag-ranks" }, `${group.ranks.length} rank${group.ranks.length !== 1 ? "s" : ""}`)
|
||||
)
|
||||
);
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderGroupList() {
|
||||
if (!container) return;
|
||||
container.innerHTML = "";
|
||||
|
||||
const header = div({ class: "list-header" },
|
||||
h1("Group Management"),
|
||||
button({ class: "btn btn-create", onclick: () => openCreatePanel() },
|
||||
icon({ class: "fa-solid fa-plus" }),
|
||||
" New Group"
|
||||
)
|
||||
);
|
||||
container.appendChild(header);
|
||||
|
||||
const searchRow = div({ class: "search-row" },
|
||||
input({ class: "search-input", type: "text", placeholder: "Search groups...", oninput: (e) => filterGroups(e.target.value) })
|
||||
);
|
||||
container.appendChild(searchRow);
|
||||
|
||||
const grid = div({ class: "group-grid", id: "group-grid" });
|
||||
groups.forEach(g => grid.appendChild(createGroupCard(g)));
|
||||
container.appendChild(grid);
|
||||
}
|
||||
|
||||
function filterGroups(query) {
|
||||
const grid = document.getElementById("group-grid");
|
||||
if (!grid) return;
|
||||
grid.innerHTML = "";
|
||||
const lower = query.toLowerCase();
|
||||
const filtered = groups.filter(g => g.name.toLowerCase().includes(lower) || g.label.toLowerCase().includes(lower));
|
||||
filtered.forEach(g => grid.appendChild(createGroupCard(g)));
|
||||
}
|
||||
|
||||
function createRankRow(rank, index, ranksList) {
|
||||
const row = div({ class: "rank-row", "data-index": index },
|
||||
div({ class: "rank-order" },
|
||||
button({ class: "btn-icon", title: "Move up", onclick: () => moveRank(ranksList, index, -1) },
|
||||
icon({ class: "fa-solid fa-chevron-up" })
|
||||
),
|
||||
span({ class: "rank-weight" }, String(rank.weight)),
|
||||
button({ class: "btn-icon", title: "Move down", onclick: () => moveRank(ranksList, index, 1) },
|
||||
icon({ class: "fa-solid fa-chevron-down" })
|
||||
)
|
||||
),
|
||||
input({ class: "rank-label-input", type: "text", value: rank.label, placeholder: "Rank label", oninput: (e) => {
|
||||
ranksList[index].label = e.target.value;
|
||||
}}),
|
||||
div({ class: "rank-boss-toggle" },
|
||||
label({ class: "checkbox-label" },
|
||||
input({ type: "checkbox", checked: rank.isBoss, onchange: (e) => {
|
||||
ranksList[index].isBoss = e.target.checked;
|
||||
}}),
|
||||
" Boss"
|
||||
)
|
||||
),
|
||||
button({ class: "btn-icon btn-danger", title: "Delete rank", onclick: () => {
|
||||
ranksList.splice(index, 1);
|
||||
recalculateWeights(ranksList);
|
||||
rerenderRanks(ranksList);
|
||||
}},
|
||||
icon({ class: "fa-solid fa-trash" })
|
||||
)
|
||||
);
|
||||
return row;
|
||||
}
|
||||
|
||||
function moveRank(ranksList, index, direction) {
|
||||
const newIndex = index + direction;
|
||||
if (newIndex < 0 || newIndex >= ranksList.length) return;
|
||||
const temp = ranksList[index];
|
||||
ranksList[index] = ranksList[newIndex];
|
||||
ranksList[newIndex] = temp;
|
||||
recalculateWeights(ranksList);
|
||||
rerenderRanks(ranksList);
|
||||
}
|
||||
|
||||
function recalculateWeights(ranksList) {
|
||||
for (let i = 0; i < ranksList.length; i++) {
|
||||
ranksList[i].weight = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
function rerenderRanks(ranksList) {
|
||||
const ranksContainer = document.getElementById("ranks-container");
|
||||
if (!ranksContainer) return;
|
||||
ranksContainer.innerHTML = "";
|
||||
ranksList.forEach((rank, idx) => {
|
||||
ranksContainer.appendChild(createRankRow(rank, idx, ranksList));
|
||||
});
|
||||
}
|
||||
|
||||
function openEditPanel(group) {
|
||||
currentEdit = JSON.parse(JSON.stringify(group));
|
||||
showEditPanel(false);
|
||||
}
|
||||
|
||||
function openCreatePanel() {
|
||||
currentEdit = { name: "", label: "", isJob: false, ranks: [] };
|
||||
showEditPanel(true);
|
||||
}
|
||||
|
||||
function showEditPanel(isNew) {
|
||||
if (!editPanel) return;
|
||||
editPanel.innerHTML = "";
|
||||
editPanel.classList.add("open");
|
||||
|
||||
const ranksList = currentEdit.ranks.map(r => ({ ...r }));
|
||||
|
||||
const panelContent = div({ class: "edit-content" },
|
||||
div({ class: "edit-header" },
|
||||
h2(isNew ? "Create Group" : "Edit Group"),
|
||||
button({ class: "btn-icon", onclick: () => closeEditPanel() },
|
||||
icon({ class: "fa-solid fa-xmark" })
|
||||
)
|
||||
),
|
||||
div({ class: "edit-form" },
|
||||
div({ class: "form-group" },
|
||||
label("Name (unique key)"),
|
||||
input({
|
||||
class: "form-input",
|
||||
type: "text",
|
||||
value: currentEdit.name,
|
||||
disabled: !isNew,
|
||||
placeholder: "e.g. polis",
|
||||
oninput: (e) => { currentEdit.name = e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g, ""); e.target.value = currentEdit.name; }
|
||||
})
|
||||
),
|
||||
div({ class: "form-group" },
|
||||
label("Label"),
|
||||
input({
|
||||
class: "form-input",
|
||||
type: "text",
|
||||
value: currentEdit.label,
|
||||
placeholder: "e.g. Polis",
|
||||
oninput: (e) => { currentEdit.label = e.target.value; }
|
||||
})
|
||||
),
|
||||
div({ class: "form-group form-row" },
|
||||
label({ class: "checkbox-label" },
|
||||
input({
|
||||
type: "checkbox",
|
||||
checked: currentEdit.isJob,
|
||||
onchange: (e) => { currentEdit.isJob = e.target.checked; }
|
||||
}),
|
||||
" Is Job"
|
||||
)
|
||||
),
|
||||
div({ class: "form-group" },
|
||||
div({ class: "ranks-header" },
|
||||
h3("Ranks"),
|
||||
button({ class: "btn btn-small", onclick: () => {
|
||||
ranksList.push({ label: "", weight: ranksList.length + 1, isBoss: false });
|
||||
rerenderRanks(ranksList);
|
||||
}},
|
||||
icon({ class: "fa-solid fa-plus" }),
|
||||
" Add Rank"
|
||||
)
|
||||
),
|
||||
div({ class: "ranks-info" },
|
||||
p("Rank 1 is the lowest. Higher weight = higher rank. Boss ranks grant boss permissions.")
|
||||
),
|
||||
div({ id: "ranks-container", class: "ranks-container" })
|
||||
)
|
||||
),
|
||||
div({ class: "edit-actions" },
|
||||
!isNew ? button({ class: "btn btn-danger", onclick: () => deleteGroup(currentEdit.name) },
|
||||
icon({ class: "fa-solid fa-trash" }),
|
||||
" Delete Group"
|
||||
) : null,
|
||||
div({ class: "edit-actions-right" },
|
||||
button({ class: "btn btn-secondary", onclick: () => closeEditPanel() }, "Cancel"),
|
||||
button({ class: "btn btn-primary", onclick: () => saveGroup(isNew, ranksList) },
|
||||
icon({ class: "fa-solid fa-floppy-disk" }),
|
||||
" Save"
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
editPanel.appendChild(panelContent);
|
||||
rerenderRanks(ranksList);
|
||||
}
|
||||
|
||||
function closeEditPanel() {
|
||||
if (!editPanel) return;
|
||||
editPanel.classList.remove("open");
|
||||
editPanel.innerHTML = "";
|
||||
currentEdit = null;
|
||||
}
|
||||
|
||||
function saveGroup(isNew, ranksList) {
|
||||
if (!currentEdit.name || !currentEdit.label) return;
|
||||
|
||||
const ranks = ranksList.map((r, idx) => ({
|
||||
label: r.label,
|
||||
weight: idx + 1,
|
||||
isBoss: r.isBoss
|
||||
}));
|
||||
|
||||
if (isNew) {
|
||||
fetchCallback("groups:create", {
|
||||
name: currentEdit.name,
|
||||
label: currentEdit.label,
|
||||
isJob: currentEdit.isJob,
|
||||
ranks: ranks
|
||||
}, (result) => {
|
||||
if (result && result.success) {
|
||||
groups = result.groups || groups;
|
||||
renderGroupList();
|
||||
closeEditPanel();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fetchCallback("groups:edit", {
|
||||
name: currentEdit.name,
|
||||
label: currentEdit.label,
|
||||
isJob: currentEdit.isJob,
|
||||
ranks: ranks
|
||||
}, (result) => {
|
||||
if (result && result.success) {
|
||||
groups = result.groups || groups;
|
||||
renderGroupList();
|
||||
closeEditPanel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function deleteGroup(name) {
|
||||
if (!name) return;
|
||||
fetchCallback("groups:delete", { name: name }, (result) => {
|
||||
if (result && result.success) {
|
||||
groups = result.groups || groups;
|
||||
renderGroupList();
|
||||
closeEditPanel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
container = div({ class: "main-page" });
|
||||
editPanel = div({ class: "edit-panel" });
|
||||
document.body.appendChild(container);
|
||||
document.body.appendChild(editPanel);
|
||||
}
|
||||
|
||||
nuiMessage("groups:open", (info) => {
|
||||
setOpen(true);
|
||||
document.body.style.opacity = "1";
|
||||
groups = info.groups || [];
|
||||
renderGroupList();
|
||||
});
|
||||
|
||||
nuiMessage("groups:close", () => {
|
||||
closeUI();
|
||||
});
|
||||
|
||||
init();
|
||||
46
ui/modules/close.js
Normal file
46
ui/modules/close.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { fetchCallback } from "./fetch.js";
|
||||
|
||||
let isOpen = false;
|
||||
|
||||
export function closeUI() {
|
||||
if (!isOpen) return;
|
||||
isOpen = false;
|
||||
document.body.style.opacity = "0";
|
||||
setTimeout(() => {
|
||||
fetchCallback("hide");
|
||||
}, 400);
|
||||
}
|
||||
|
||||
export function setOpen(state) {
|
||||
isOpen = state;
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") closeUI();
|
||||
});
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
const mainPage = document.querySelector(".main-page");
|
||||
const editPanel = document.querySelector(".edit-panel");
|
||||
if (!mainPage || !editPanel) return;
|
||||
|
||||
const mainRect = mainPage.getBoundingClientRect();
|
||||
const editRect = editPanel.getBoundingClientRect();
|
||||
|
||||
const insideMainPage =
|
||||
event.clientX >= mainRect.left &&
|
||||
event.clientX <= mainRect.right &&
|
||||
event.clientY >= mainRect.top &&
|
||||
event.clientY <= mainRect.bottom;
|
||||
|
||||
const insideEditPanel =
|
||||
event.clientX >= editRect.left &&
|
||||
event.clientX <= editRect.right &&
|
||||
event.clientY >= editRect.top &&
|
||||
event.clientY <= editRect.bottom;
|
||||
|
||||
if (!insideMainPage && !insideEditPanel) {
|
||||
closeUI();
|
||||
}
|
||||
});
|
||||
|
||||
9
ui/modules/fetch.js
Normal file
9
ui/modules/fetch.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export function fetchCallback(name, data, cb) {
|
||||
fetch(`https://${GetParentResourceName()}/${name}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
||||
body: JSON.stringify(data)
|
||||
}).then(resp => resp.json()).then(resp => {
|
||||
if (cb) cb(resp);
|
||||
});
|
||||
}
|
||||
15
ui/modules/listener.js
Normal file
15
ui/modules/listener.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const nuiMessages = {};
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
const item = event.data;
|
||||
const listener = nuiMessages[item.type];
|
||||
if (!listener) return;
|
||||
for (let i = 0; i < listener.length; i++) {
|
||||
listener[i](item);
|
||||
}
|
||||
});
|
||||
|
||||
export function nuiMessage(name, cb) {
|
||||
if (!nuiMessages[name]) nuiMessages[name] = [];
|
||||
nuiMessages[name].push(cb);
|
||||
}
|
||||
455
ui/style.css
Normal file
455
ui/style.css
Normal file
@@ -0,0 +1,455 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Abel&display=swap');
|
||||
|
||||
:root {
|
||||
--font: "Abel", sans-serif;
|
||||
--bg-primary: #1a1a1bfd;
|
||||
--bg-secondary: #202020;
|
||||
--bg-tertiary: #262627;
|
||||
--bg-hover: #2e2e2f;
|
||||
--text-primary: #d4d4d3;
|
||||
--text-secondary: #999;
|
||||
--accent: #0063b1;
|
||||
--accent-hover: #0078d4;
|
||||
--danger: #c42b1c;
|
||||
--danger-hover: #e03e2d;
|
||||
--border: #333;
|
||||
--success: #107c10;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
user-select: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
font-family: var(--font);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.main-page {
|
||||
position: relative;
|
||||
background-color: var(--bg-primary);
|
||||
border-radius: 12px;
|
||||
width: 60rem;
|
||||
max-height: 45rem;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.list-header h1 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 0.6rem 1rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font);
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.group-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
|
||||
gap: 0.75rem;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.25rem;
|
||||
max-height: 35rem;
|
||||
}
|
||||
|
||||
.group-grid::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.group-grid::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.group-grid::-webkit-scrollbar-thumb {
|
||||
background: var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.group-card {
|
||||
background-color: var(--bg-secondary);
|
||||
border-radius: 10px;
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s, transform 0.15s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.group-card:hover {
|
||||
background-color: var(--bg-tertiary);
|
||||
border-color: var(--border);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.group-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.group-card-header h3 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.group-name {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.group-card-tags {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
background-color: var(--bg-tertiary);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.tag-job {
|
||||
background-color: rgba(0, 99, 177, 0.25);
|
||||
color: var(--accent-hover);
|
||||
}
|
||||
|
||||
.tag-ranks {
|
||||
background-color: rgba(16, 124, 16, 0.2);
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
/* Edit Panel */
|
||||
.edit-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: -35rem;
|
||||
width: 35rem;
|
||||
height: 100vh;
|
||||
background-color: var(--bg-primary);
|
||||
border-left: 1px solid var(--border);
|
||||
transition: right 0.3s ease;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.edit-panel.open {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.edit-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.edit-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.edit-header h2 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.edit-form {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.edit-form::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.edit-form::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.edit-form::-webkit-scrollbar-thumb {
|
||||
background: var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.form-group > label {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font);
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.form-input:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
accent-color: var(--accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Ranks */
|
||||
.ranks-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ranks-header h3 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.ranks-info {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.ranks-info p {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.ranks-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.rank-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background-color: var(--bg-secondary);
|
||||
padding: 0.5rem 0.6rem;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
|
||||
.rank-row:hover {
|
||||
background-color: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.rank-order {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.15rem;
|
||||
min-width: 4rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rank-weight {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
min-width: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rank-label-input {
|
||||
flex: 1;
|
||||
padding: 0.35rem 0.6rem;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font);
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.rank-label-input:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.rank-boss-toggle {
|
||||
min-width: 3.5rem;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
border-radius: 7px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font);
|
||||
font-size: 0.95rem;
|
||||
transition: background-color 0.2s, opacity 0.2s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--accent);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--accent-hover);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--bg-hover);
|
||||
}
|
||||
|
||||
.btn-create {
|
||||
background-color: var(--accent);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-create:hover {
|
||||
background-color: var(--accent-hover);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: var(--danger);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: var(--danger-hover);
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: 0.3rem 0.7rem;
|
||||
font-size: 0.85rem;
|
||||
background-color: var(--accent);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
padding: 0.2rem;
|
||||
font-size: 0.85rem;
|
||||
border-radius: 4px;
|
||||
transition: color 0.2s, background-color 0.15s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.btn-icon:hover {
|
||||
color: var(--text-primary);
|
||||
background-color: var(--bg-hover);
|
||||
}
|
||||
|
||||
.btn-icon.btn-danger {
|
||||
background: none;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.btn-icon.btn-danger:hover {
|
||||
color: var(--danger-hover);
|
||||
background-color: rgba(196, 43, 28, 0.15);
|
||||
}
|
||||
|
||||
/* Edit Actions */
|
||||
.edit-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.edit-actions-right {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
Reference in New Issue
Block a user