93 Commits

Author SHA1 Message Date
Andy
1547d310eb Merge pull request #81 from ND-Framework/feat/claim-veh-command
feat(server/comands): claim veh command
2025-01-18 14:41:58 +01:00
Andyyy7666
9d4708f6d9 feat(server/comands): claim veh command 2025-01-18 14:41:41 +01:00
Andy
a7713804f0 Merge pull request #80 from ND-Framework/fix/dv-command-inconsistency
fix(server/commands): dv command inconsistency
2025-01-18 14:40:37 +01:00
Andyyy7666
a9785ce754 fix(server/commands): dv command inconsistency 2025-01-18 14:40:01 +01:00
Andy
fe922e1b98 Merge pull request #79 from ND-Framework/fix/unlock-command-inconsistency
fix(server/commands): unlock command inconsistency
2025-01-18 14:38:42 +01:00
Andyyy7666
b97f42bb0a fix(server/commands): unlock command inconsistency 2025-01-18 14:38:13 +01:00
Andy
d132c12025 Merge pull request #78 from ND-Framework/feat/replace-armor
feat(client): replace armor on revive
2025-01-17 21:13:03 +01:00
Andyyy7666
119b0034bf feat(client): replace armor on revive
When reviving player to heal, replace the armor with the previous amount.
2025-01-17 21:12:43 +01:00
Andyyy7666
622b6fa8b0 tweak: add oxlib as dependency 2025-01-17 20:58:48 +01:00
Andyyy7666
2e6dfa9e5f Update fxmanifest.lua 2025-01-17 20:56:23 +01:00
Andy
4c4166de94 Merge pull request #77 from ND-Framework/feat/lan-identifier
feat(server): lan identifer
2025-01-17 20:53:12 +01:00
Andyyy7666
dea7ac6c8f feat(server): lan identifer
Create fake identifier when using sv_lan
2025-01-17 20:52:28 +01:00
Andy
120d924cae Merge pull request #76 from ND-Framework/tweak/lockpick-tries-convar
tweak(client/vehicle): lockpick tries convar
2025-01-17 20:42:59 +01:00
Andyyy7666
2a263a6c2e tweak(client/vehicle): lockpick tries convar 2025-01-17 20:42:27 +01:00
Andy
4ff4f487a1 Merge pull request #75 from ND-Framework/feat/garage-locations
feat: more default garage locations
2025-01-17 20:36:38 +01:00
Andyyy7666
e26821841b feat: more default garage locations 2025-01-17 20:36:16 +01:00
Andy
e9aafe1f2d Merge pull request #70 from NotSomething0/fix-vehicle-metadata
fix(vehicle): set metadata state for vehicle if non-existent
2024-12-20 21:29:39 +01:00
NotSomething0
6987bb63ae Merge branch 'ND-Framework:main' into fix-vehicle-metadata 2024-05-13 17:17:27 -05:00
NotSomething0
e0c4a8419b fix(vehicle): set metadata state for vehicle if non-existent
- Added debug print for when metadata is storage is skipped
2024-05-11 14:54:57 -05:00
Andyyy7666
66f93bde14 Merge branch 'main' of https://github.com/ND-Framework/ND_Core 2024-03-26 17:08:11 +01:00
Andyyy7666
2460df2f34 feat(server/vehicles): remove vehicles from db 2024-03-26 17:08:09 +01:00
Andy
b79bb6ec7f Merge pull request #68 from Hakkodevelopment/main
small changes
2024-03-25 23:12:04 +01:00
Hakko
eef1bb60f5 bump fxmanifest version 2024-03-25 16:04:43 -06:00
Hakko
1320c4ab7d Update commands.lua 2024-03-25 16:02:24 -06:00
Andyyy7666
2c7aa5723a perf(server/vehicle): getting vehicle types 2024-03-19 01:53:06 +01:00
Andyyy7666
97a8b991ae fix(server/vehicle): taking out impounded vehicle 2024-03-19 01:52:45 +01:00
Andyyy7666
2a3ac3b63b feat(server/player): player group removed event 2024-03-19 01:52:18 +01:00
Andyyy7666
5d3c051149 fix(server/vehicle): calculating time 2024-03-17 14:26:11 +01:00
Andyyy7666
fc82b217e9 fix: clothing not updating 2024-03-16 19:12:34 +01:00
Andyyy7666
284411ce66 fix(server/player): nil group metadata 2024-03-16 16:43:20 +01:00
Andyyy7666
a7257cf68f feat(server/player): group metadata 2024-03-10 00:35:45 +01:00
Andyyy7666
841a5771c5 tweak(server/player): custom group data 2024-03-10 00:01:49 +01:00
Andy
8fd7878ba6 Merge pull request #67 from ND-Framework/tweak/updateCharacter-event
tweak(server/player): also pass which data changed
2024-03-09 19:05:49 +01:00
Andyyy7666
60e86d2038 tweak(server/player): also pass which data changed 2024-03-09 19:05:21 +01:00
Andy
626f0ad06f Merge pull request #66 from ND-Framework/feat/custom-group-info
Feat/custom group info
2024-03-09 19:03:19 +01:00
Andyyy7666
d5c3acbd81 tweak(server/player): adding job group 2024-03-09 19:02:18 +01:00
Andyyy7666
98cba87bec feat(server/player): adding custom group info 2024-03-09 19:00:15 +01:00
Andyyy7666
001fc22ef6 fix: syntax error 2024-02-29 23:17:08 +01:00
Andy
87420e99e9 Merge pull request #65 from ND-Framework/feat/discord-groups
feat: receive groups by discord roles
2024-02-29 22:29:13 +01:00
Andyyy7666
c5597161d9 feat: receive groups by discord roles
Player can receive groups based on the discord roles they have, ace perms are also added when group is received.
2024-02-29 22:28:25 +01:00
Andyyy7666
898b9aefe9 feat(server/player): updatecharacter server event 2024-02-28 14:12:18 +01:00
Andyyy7666
5545650dd5 feat: impound non parked vehicles 2024-02-27 23:13:40 +01:00
Andy
f4f01a5b1c fix: config discord invinte 2024-02-27 20:43:15 +01:00
Andyyy7666
3252118f93 tweak: move clothing menu command to core 2024-02-25 23:54:10 +01:00
Andyyy7666
9d4708a1f8 tweak(server/player): save inventory when character created 2024-02-25 23:43:00 +01:00
Andyyy7666
d0d04ae282 tweak(death): damage event for ambulance 2024-02-17 23:57:42 +01:00
Andyyy7666
c005c1b28b tweak(client/death): player death checks 2024-02-12 23:28:18 +01:00
Andyyy7666
590044b5d6 refactor(client): revive function 2024-02-12 23:27:43 +01:00
Andyyy7666
3146172c0c fix: register unloaded event 2024-02-04 16:44:55 +01:00
Andy
2ce47ef8d1 Merge pull request #62 from ND-Framework/tweak/peds-clothing-command
tweak(client/peds): copying clothing
2024-01-30 14:38:25 +01:00
Andyyy7666
6258651d8b tweak(client/peds): copying clothing 2024-01-30 14:37:44 +01:00
Andy
eecbc8f690 Merge pull request #59 from Hakkodevelopment/patch-1
Fix: Command grammar/spelling mistakes/fluidity
2024-01-26 04:29:52 +01:00
Andyyy7666
1d25b54e0f tweak: notifiacitons 2024-01-26 04:16:24 +01:00
Hakko
e18389a52a Fix: Command grammar/spelling mistakes/fluidity 2024-01-21 21:06:51 -07:00
Andy
539e522373 Merge pull request #57 from ND-Framework/fix/driving-locked-vehicle
fix(client/vehicle): vehicle keys issues
2024-01-16 23:31:40 +01:00
Andyyy7666
2bacdd2a20 fix(client/vehicle): vehicle keys issues 2024-01-16 23:31:12 +01:00
Andy
6242a44c0b Merge pull request #53 from realCallMeJ/main
feat: config for requiring discord server membership
2024-01-16 20:59:48 +01:00
Andyyy7666
74e4ccc2de feat: police vehicle callsigns mod 2024-01-15 19:48:24 +01:00
Andyyy7666
2fda7655b5 feat(server/player): save only one column 2024-01-11 00:36:18 +01:00
CallMeJ
ade82436a5 feat: config for requiring discord server membership 2024-01-09 15:50:09 -10:00
Andyyy7666
f512d0ed0c Update fxmanifest.lua 2024-01-09 13:27:11 +01:00
Andyyy7666
0a19fe8a33 fix(client(vehicle): hide weapon while using key 2024-01-09 13:03:42 +01:00
Andyyy7666
2882c7c1ae tweak:(database/characters): phonenumber column 2024-01-07 21:25:44 +01:00
Andyyy7666
6291690bd4 feat(database/characters): phonenumber column 2024-01-07 21:14:30 +01:00
Andyyy7666
af82c447c1 feat(server/player): phonenumber player data 2024-01-07 21:13:56 +01:00
Andyyy7666
46f4bdbd64 Update fxmanifest.lua 2023-12-29 18:43:17 +01:00
Andyyy7666
4870d639d4 fix(server/player): update client data on job set 2023-12-29 02:12:17 +01:00
Andyyy7666
ed895b4e6d tweak(client/vehicle): minimum cruise control speed 2023-12-29 00:10:40 +01:00
Andyyy7666
5d2925d0c3 fix(server/player): comparing rank 2023-12-28 23:30:18 +01:00
Andyyy7666
0500cef32f tweak: remove job if it's group is removed 2023-12-28 20:38:16 +01:00
Andyyy7666
0e1fb43cd2 feat: boss for groups 2023-12-28 20:37:59 +01:00
Andyyy7666
3260aa01f2 fix(compatibility/esx): errors without inventory 2023-12-26 23:33:33 +01:00
Andyyy7666
71a54be704 Update death.lua 2023-12-26 23:00:32 +01:00
Andyyy7666
d939607d64 fix: pay command issues 2023-12-25 23:15:36 +01:00
Andyyy7666
5cb038819c tweak(compatibility): nd v1 character data 2023-12-25 02:01:39 +01:00
Andyyy7666
9f5bb34bdd fix: groups nil issue if incorrect 2023-12-23 23:47:55 +01:00
Andyyy7666
29006870c3 feat: getConfig function 2023-12-23 23:47:24 +01:00
Andyyy7666
a60e1b9e32 fix: checking for driver in spawned vehicle 2023-12-23 00:00:45 +01:00
Andy
7fe7901a13 Update fxmanifest.lua 2023-12-22 22:53:15 +01:00
Andy
ca8f4369f6 Update README.md 2023-12-22 22:51:52 +01:00
Andy
e1d4b2fcff Update README.md 2023-12-22 22:51:09 +01:00
Andyyy7666
8b0f2b010d fix: lock inconsistency on player spawned vehicles 2023-12-22 18:25:00 +01:00
Andy
24e8874050 Merge pull request #46 from realCallMeJ/discord-info-fix
fix: not getting discord info on player connecting
2023-12-20 13:03:28 +01:00
Andy
289d040b5c Update main.lua 2023-12-20 13:02:13 +01:00
CallMeJ
3f0c47c2d0 add next as a local 2023-12-19 17:22:43 -10:00
CallMeJ
543b23a644 fix: not getting discord info till restart 2023-12-19 17:03:49 -10:00
Andyyy7666
4b106d3688 fix: PlayersInfo not being set 2023-12-16 19:46:35 +01:00
Andy
db5c54f2d8 Merge pull request #44 from Blizzard098/main
Escape SQL `groups` column value and LONGTEXT defaults.
2023-12-14 00:54:12 +01:00
Michael G
a1150bb821 Make LONGTEXT default values compatible with MySQL8 2023-12-13 18:27:00 -05:00
Michael G
8f7c52130a Make default values for LONGTEXT compatible with MySQL8 2023-12-13 18:26:36 -05:00
Michael G
6e71142970 Escape the groups column 2023-12-13 18:22:01 -05:00
Andyyy7666
d9df135abb fix: merge issues 2023-12-10 19:07:06 +01:00
Andy
72fb153cfc Merge pull request #43 from ND-Framework/wip-2.0.0
v2
2023-12-10 19:00:39 +01:00
24 changed files with 619 additions and 485 deletions

View File

@@ -16,9 +16,13 @@
* [oxmysql](https://github.com/overextended/oxmysql/releases) * [oxmysql](https://github.com/overextended/oxmysql/releases)
* [ox_lib](https://github.com/overextended/ox_lib/releases) * [ox_lib](https://github.com/overextended/ox_lib/releases)
## v2 Addons ## Addons
* [Character selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2) * [Character selection](https://github.com/ND-Framework/ND_Characters/releases)
* [Banking](https://github.com/ND-Framework/ND_Banking/tree/wip-v2) * [Banking](https://github.com/ND-Framework/ND_Banking/releases)
* [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/tree/wip-v2) * [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/releases)
* [Dealership](https://github.com/ND-Framework/ND_Dealership/tree/v2) * [Dealership](https://github.com/ND-Framework/ND_Dealership/releases)
* [Inventory](https://github.com/overextended/ox_inventory/pull/1403) * [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)

View File

@@ -29,15 +29,14 @@ AddEventHandler("gameEventTriggered", function(name, args)
local victim = args[1] local victim = args[1]
if not IsPedAPlayer(victim) or NetworkGetPlayerIndexFromPed(victim) ~= cache.playerId then return end 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)
local hit, bone = GetPedLastDamageBone(victim) if hit and usingAmbulance then
if hit and usingAmbulance then local damageWeapon = ambulance:getLastDamagingWeapon(victim)
local damageWeapon = ambulance:getLastDamagingWeapon(victim) ambulance:updateBodyDamage(bone, damageWeapon)
return damageWeapon and ambulance:updateBodyDamage(bone, damageWeapon)
end
return
end end
if not IsPedDeadOrDying(victim, true) or GetEntityHealth(victim) > 100 then return end
local killerEntity, deathCause = GetPedSourceOfDeath(cache.ped), GetPedCauseOfDeath(cache.ped) local killerEntity, deathCause = GetPedSourceOfDeath(cache.ped), GetPedCauseOfDeath(cache.ped)
local killerClientId = NetworkGetPlayerIndexFromPed(killerEntity) local killerClientId = NetworkGetPlayerIndexFromPed(killerEntity)
if killerEntity ~= cache.ped and killerClientId and NetworkIsPlayerActive(killerClientId) then if killerEntity ~= cache.ped and killerClientId and NetworkIsPlayerActive(killerClientId) then

View File

@@ -22,5 +22,54 @@ RegisterNetEvent("ND:updateLastLocation", function(location)
end) end)
RegisterNetEvent("ND:revivePlayer", function() RegisterNetEvent("ND:revivePlayer", function()
NDCore.revivePlayer(true) 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:characterUnloaded")
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) end)

View File

@@ -26,56 +26,15 @@ function NDCore.getPlayersFromCoords(distance, coords)
return closePlayers return closePlayers
end end
function NDCore.revivePlayer(reset, keepDead) function NDCore.getConfig(info)
local usingAmbulance = GetResourceState("ND_Ambulance") == "started" if not info then
if not keepDead then return Config
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()
end end
return Config[info]
end end
function NDCore.notify(...) function NDCore.notify(...)
if GetResourceState("ModernHUD") == "started" then lib.notify(...)
exports["ModernHUD"]:notify(...)
elseif GetResourceState("ox_lib") == "started" then
lib.notify(...)
end
end end
for name, func in pairs(NDCore) do for name, func in pairs(NDCore) do

View File

@@ -7,14 +7,16 @@ Config = {
discordAsset = GetConvar("core:discordAsset", "andyyy"), discordAsset = GetConvar("core:discordAsset", "andyyy"),
discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"),
discordActionText = GetConvar("core:discordActionText", "DISCORD"), 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"), discordActionText2 = GetConvar("core:discordActionText2", "STORE"),
discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"),
randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30),
disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1,
randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30),
requireKeys = GetConvarInt("core:requireKeys", 1) == 1,
useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1,
groups = json.decode(GetConvar("core:groups", "[]")), groups = json.decode(GetConvar("core:groups", "[]")),
compatibility = json.decode(GetConvar("core:compatibility", "[]")) compatibility = json.decode(GetConvar("core:compatibility", "[]")),
lockpickTries = GetConvarInt("core:lockpickTries", 3)
} }
-- Discord rich presence. -- Discord rich presence.

View File

@@ -218,10 +218,10 @@ end)
RegisterCommand("getclothing", function(source, args, rawCommand) RegisterCommand("getclothing", function(source, args, rawCommand)
local info = "" local info = ""
for k, v in pairs(clothingComponents) do 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 end
for k, v in pairs(clothingProps) do 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 end
lib.setClipboard(info) lib.setClipboard(info)
end, false) end, false)

View File

@@ -1,4 +1,14 @@
return { return {
{
garageType = "land",
groups = {"lsfd"},
ped = vector4(370.9027, -593.8090, 28.8681, 73.6009),
vehicleSpawns = {
vec4(366.7840, -591.4883, 28.7261, 339.6823),
vec4(365.0753, -582.8641, 28.7156, 135.0064),
vec4(357.4239, -604.7719, 28.6767, 177.1355),
}
},
{ {
garageType = "land", garageType = "land",
groups = {"sahp", "lspd", "bcso"}, groups = {"sahp", "lspd", "bcso"},
@@ -240,4 +250,52 @@ return {
vector4(216.56, -801.90, 30.79, 69.39) vector4(216.56, -801.90, 30.79, 69.39)
} }
}, },
{
garageType = "land",
ped = vector4(587.5941, 2744.2844, 42.0691, 181.0514),
vehicleSpawns = {
vector4(584.3921, 2721.3599, 41.6483, 184.3260),
vector4(577.1069, 2736.2322, 41.6097, 184.6912),
vector4(577.9459, 2721.0010, 41.6481, 185.1414),
vector4(574.1685, 2735.7288, 41.6397, 184.5562)
}
},
{
garageType = "land",
ped = vector4(2746.1726, 3458.7378, 55.8272, 245.1009),
vehicleSpawns = {
vector4(2768.2136, 3456.2710, 55.2840, 67.7897),
vector4(2759.3218, 3451.1448, 55.4786, 248.0150),
vector4(2763.6526, 3445.0544, 55.4741, 66.9754),
vector4(2787.2947, 3467.1001, 54.9407, 247.4356),
vector4(2773.9514, 3471.6238, 55.0407, 246.8264)
}
},
{
garageType = "land",
ped = vector4(120.4042, 6623.5190, 31.9593, 228.8283),
vehicleSpawns = {
vector4(155.9047, 6593.3252, 31.4343, 359.3789),
vector4(155.7900, 6602.6978, 31.4463, 358.8232),
vector4(145.8267, 6614.0996, 31.4007, 178.7948),
vector4(145.6977, 6602.8950, 31.4397, 181.0616),
vector4(132.4042, 6585.2139, 31.5501, 91.0906)
}
},
{
garageType = "heli",
groups = {"lspd"},
ped = vector4(465.6194, -985.4730, 43.6918, 0.8591),
vehicleSpawns = {
vector4(449.3105, -981.1057, 44.0788, 359.6042)
}
},
{
garageType = "heli",
groups = {"lsfd"},
ped = vector4(339.6271, -581.0325, 74.1656, 267.8232),
vehicleSpawns = {
vector4(351.3463, -588.1304, 74.1698, 108.5617)
}
}
} }

View File

@@ -293,6 +293,7 @@ AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, r
lib.setVehicleProperties(entity, props) lib.setVehicleProperties(entity, props)
end) end)
local playingKey = 0
local function playKeyFob(veh) local function playKeyFob(veh)
local keyFob local keyFob
local ped = cache.ped local ped = cache.ped
@@ -300,12 +301,21 @@ local function playKeyFob(veh)
if #(coords-GetEntityCoords(veh)) > 25.0 then return end if #(coords-GetEntityCoords(veh)) > 25.0 then return end
if not playerVehicle then if not playerVehicle then
playingKey += 1
SetPedCurrentWeaponVisible(ped, false, false)
ClearPedTasks(ped) ClearPedTasks(ped)
lib.requestAnimDict("anim@mp_player_intmenu@key_fob@") 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) 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) 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) 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) Wait(700)
SetTimeout(400, function()
playingKey -= 1
end)
SetTimeout(1200, function()
if playingKey > 0 then return end
SetPedCurrentWeaponVisible(ped, true, false)
end)
end end
PlaySoundFromEntity(-1, "Remote_Control_Fob", ped, "PI_Menu_Sounds", true, 0) PlaySoundFromEntity(-1, "Remote_Control_Fob", ped, "PI_Menu_Sounds", true, 0)
@@ -346,8 +356,9 @@ AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved,
end) end)
end) end)
lib.callback.register("ND_Vehicles:getProps", function(netId) local function getProps(veh)
local veh = getVehFromNetId(netId) if not veh or not DoesEntityExist(veh) then return end
local props = lib.getVehicleProperties(veh) local props = lib.getVehicleProperties(veh)
local colorPrimary, colorSecondary = GetVehicleColours(veh) local colorPrimary, colorSecondary = GetVehicleColours(veh)
if not props then return end if not props then return end
@@ -358,7 +369,18 @@ lib.callback.register("ND_Vehicles:getProps", function(netId)
props.className = vehicleClassNames[GetVehicleClass(veh)] props.className = vehicleClassNames[GetVehicleClass(veh)]
props.makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model)) props.makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model))
props.modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) props.modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model))
return props 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) end)
lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model)
@@ -470,22 +492,35 @@ CreateThread(function()
local wait = 500 local wait = 500
while true do while true do
Wait(wait) Wait(wait)
local reset = true
playerVehicle = cache.seat == -1 and cache.vehicle 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 local entering = GetVehiclePedIsEntering(cache.ped)
wait = 0 if entering and DoesEntityExist(entering) and IsVehicleNeedsToBeHotwired(entering) then
DisableControlAction(0, 59) -- disable vehicle air control. SetVehicleNeedsToBeHotwired(entering, false)
DisableControlAction(0, 60) end
elseif not GetIsVehicleEngineRunning(playerVehicle) and not hasVehicleKeysCheck(playerVehicle) then
wait = 0 if not playerVehicle then goto skip end
DisableControlAction(0, 59)
if DoesEntityExist(playerVehicle) and IsVehicleEngineStarting(playerVehicle) then if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(playerVehicle)] and (IsEntityInAir(playerVehicle) or IsEntityUpsidedown(playerVehicle)) then
SetVehicleEngineOn(playerVehicle, false, true, true) -- don't turn on engine if no keys. wait = 0
end reset = false
else DisableControlAction(0, 59) -- disable vehicle air control.
wait = 500 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 end
elseif wait ~= 500 then end
::skip::
if (reset or not playerVehicle) and wait ~= 500 then
wait = 500 wait = 500
end end
end end
@@ -574,7 +609,7 @@ local function lockpickVehicle()
} }
lib.requestAnimDict("veh@break_in@0h@p_m_one@") lib.requestAnimDict("veh@break_in@0h@p_m_one@")
for i=1, 7 do 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) 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 dificulty = dificulties[math.random(1, #dificulties)]
local success = lib.skillCheck(dificulty) local success = lib.skillCheck(dificulty)
@@ -697,9 +732,14 @@ lib.addKeybind({
}) })
end end
if not playerVehicle then return end if not playerVehicle then return end
local speed = math.floor(GetEntitySpeed(playerVehicle) * 2.236936)
if speed < 10 then return end
cruiseControlEnabled = true cruiseControlEnabled = true
cruiseSpeedVehicle = math.floor(GetEntitySpeed(playerVehicle) * 2.236936) cruiseSpeedVehicle = speed
cruiseSpeedSet = cruiseSpeedVehicle cruiseSpeedSet = cruiseSpeedVehicle
lib.notify({ lib.notify({
title = "Cruise control", title = "Cruise control",
description = ("Set to %d mph."):format(math.floor(cruiseSpeedSet)), description = ("Set to %d mph."):format(math.floor(cruiseSpeedSet)),

View File

@@ -5,13 +5,32 @@ exports("GetCoreObject", function()
end) end)
NDCore.Functions = {} NDCore.Functions = {}
NDCore.Functions.GetPlayer = NDCore.getPlayer
NDCore.Functions.GetPlayers = NDCore.getPlayers NDCore.Functions.GetPlayers = NDCore.getPlayers
NDCore.Functions.GetUserDiscordInfo = NDCore.getDiscordInfo NDCore.Functions.GetUserDiscordInfo = NDCore.getDiscordInfo
NDCore.Functions.SetActiveCharacter = NDCore.setActiveCharacter NDCore.Functions.SetActiveCharacter = NDCore.setActiveCharacter
NDCore.Functions.GetPlayerCharacters = NDCore.fetchAllCharacters NDCore.Functions.GetPlayerCharacters = NDCore.fetchAllCharacters
NDCore.Functions.GetPlayerByCharacterId = NDCore.fetchCharacter 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) function NDCore.Functions.GetPlayerIdentifierFromType(identifierType, src)
return GetPlayerIdentifierByType(src, identifierType) return GetPlayerIdentifierByType(src, identifierType)
end end

View File

@@ -60,7 +60,7 @@ function NDCore.GetPlayerData()
player.coords = GetEntityCoords(cache.ped) player.coords = GetEntityCoords(cache.ped)
player.loadout = {} 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.money = player.Accounts.Money
player.sex = player.gender player.sex = player.gender
player.firstName = player.firstname player.firstName = player.firstname
@@ -112,7 +112,7 @@ function NDCore.Progressbar(message, lenght, options)
end end
function NDCore.SearchInventory(item, count) 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 return itemCount >= count and itemCount
end end
@@ -133,7 +133,7 @@ function NDCore.ShowHelpNotification()
end end
function NDCore.ShowInventory() function NDCore.ShowInventory()
exports.ox_inventory:openInventory("player", cache.serverId) return GetResourceState("ox_inventory") == "started" and exports.ox_inventory:openInventory("player", cache.serverId)
end end
function NDCore.ShowNotification(msg, type, time) function NDCore.ShowNotification(msg, type, time)

View File

@@ -5,7 +5,8 @@ local registeredItems = {}
local function getAmmoFromWeapon(weapon) local function getAmmoFromWeapon(weapon)
if not weapon then return end 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 if data.weapon and data.model and data.model:lower() == weapon:lower() then
return data.ammoname return data.ammoname
end end
@@ -60,7 +61,7 @@ local function createPlayerFunctions(self)
end end
function self.addInventoryItem(item, count) 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 end
function self.addMoney(amount) function self.addMoney(amount)
@@ -100,11 +101,11 @@ local function createPlayerFunctions(self)
end end
function self.canCarryItem(item, count) 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 end
function self.canSwapItem(firstItem, firstItemCount, testItem, testItemCount) 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 end
function self.clearMeta(index) function self.clearMeta(index)
@@ -137,7 +138,7 @@ local function createPlayerFunctions(self)
end end
function self.getInventory(minimal) 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 if not minimal then
return playerItems return playerItems
end end
@@ -145,7 +146,7 @@ local function createPlayerFunctions(self)
end end
function self.getInventoryItem(item) 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 for name, data in pairs(playerItems) do
if name == item then if name == item then
return data return data
@@ -162,11 +163,11 @@ local function createPlayerFunctions(self)
end end
function self.hasItem(item, metadata) 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 end
function self.removeInventoryItem(item, count) 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 end
function self.removeMoney(amount) function self.removeMoney(amount)
@@ -304,7 +305,8 @@ end
function NDCore.GetItemLabel(item) function NDCore.GetItemLabel(item)
if not itemNames then if not itemNames then
itemNames = {} 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 itemNames[item] = data.label
end end
end end

View File

@@ -1,74 +0,0 @@
-- For support join my discord: https://discord.gg/Z9Mxu72zZ6
config = {
serverName = "Andy's Development",
characterLimit = 15, -- How many characters can a player create.
customPauseMenu = true, -- A custom pause menu will display your money, characters name and the server name in the pause menu.
enablePVP = true, -- pvp allows doing damage to other players.
-- Money related
startingCash = 2500, -- default cash the character will start with if the character creator doesn't specify it.
startingBank = 8000,-- default money in the bank account the character will start with if the character creator doesn't specify it.
-- If you'd like to whitelist certain roles on discord then set this to true and add role ids.
enableDiscordWhitelist = false,
notWhitelistedMessage = "You're not allowlisted in this server please join our discord to apply for a allowlist: https://discord.gg/Z9Mxu72zZ6",
whitelistRoles = {
"872921520719142932"
},
-- These are admins roles that will give a user permission to admin commands and more.
adminRoles = {
"872921520719142932"
},
-- Discord Rich presence
enableRichPresence = true,
updateIntervall = 60, -- how many seconds of delay until it updates status.
appId = 858146067018416128,
largeLogo = "andyyy",
smallLogo = "andyyy",
firstButtonName = "DISCORD",
firstButtonLink = "https://discord.gg/Z9Mxu72zZ6",
secondButtonName = "TEBEX",
secondButtonLink = "https://andyyy.tebex.io/category/fivem-scripts",
-- Groups can be gangs, jobs, subdivisions, etc.
groups = {
["Ballas"] = {
"Member", -- rank 1
"Boss" -- rank 2
},
["SWAT"] = {
"Member", -- rank 1
"Sniper", -- rank 2
"Team lead", -- rank 3
"Commander" -- rank 4
},
["SAHP"] = {
"Trooper",
"Senior Trooper",
"Corporal",
"Sergeant",
"Lieutenant",
"Cheif"
},
["LSPD"] = {
"Officer",
"Senior officer",
"Corporal",
"Sergeant",
"Lieutenant",
"Cheif"
},
["BCSO"] = {
"Deputy",
"Senior Deputy",
"Corporal",
"Sergeant",
"Lieutenant",
"Cheif"
}
},
}

View File

@@ -1,6 +0,0 @@
-- For support join my discord: https://discord.gg/Z9Mxu72zZ6
server_config = {
discordServerToken = "OTAwODg1MDMxMDQ2Nzc0ODQ1.GU1TSC.Ed8D2_wcU-UQ1vZhe1khahgV8J3hLahOp4N1D8", -- discord bot token for permissions
guildId = "872496972454592523", -- discord guild id
}

View File

@@ -1,15 +1,16 @@
CREATE TABLE IF NOT EXISTS `nd_characters` ( CREATE TABLE IF NOT EXISTS `nd_characters` (
`charid` INT(10) NOT NULL AUTO_INCREMENT, `charid` INT(10) NOT NULL AUTO_INCREMENT,
`identifier` VARCHAR(200) NOT NULL DEFAULT '0', `identifier` VARCHAR(200) NOT NULL DEFAULT '0',
`name` VARCHAR(50) DEFAULT NULL, `name` VARCHAR(50) DEFAULT NULL,
`firstname` VARCHAR(50) DEFAULT NULL, `firstname` VARCHAR(50) DEFAULT NULL,
`lastname` VARCHAR(50) DEFAULT NULL, `lastname` VARCHAR(50) DEFAULT NULL,
`dob` VARCHAR(50) DEFAULT NULL, `dob` VARCHAR(50) DEFAULT NULL,
`gender` VARCHAR(50) DEFAULT NULL, `gender` VARCHAR(50) DEFAULT NULL,
`cash` INT(10) DEFAULT '0', `cash` INT(10) DEFAULT '0',
`bank` INT(10) DEFAULT '0', `bank` INT(10) DEFAULT '0',
`groups` LONGTEXT DEFAULT '[]', `phonenumber` VARCHAR(20) DEFAULT NULL,
`metadata` LONGTEXT DEFAULT '[]', `groups` LONGTEXT DEFAULT ('[]'),
`inventory` LONGTEXT DEFAULT '[]', `metadata` LONGTEXT DEFAULT ('[]'),
PRIMARY KEY (`charid`) USING BTREE `inventory` LONGTEXT DEFAULT ('[]'),
PRIMARY KEY (`charid`) USING BTREE
); );

View File

@@ -1,15 +1,15 @@
CREATE TABLE IF NOT EXISTS `nd_vehicles` ( CREATE TABLE IF NOT EXISTS `nd_vehicles` (
`id` INT(11) NOT NULL AUTO_INCREMENT, `id` INT(11) NOT NULL AUTO_INCREMENT,
`owner` INT(11) DEFAULT NULL, `owner` INT(11) DEFAULT NULL,
`plate` VARCHAR(255) DEFAULT NULL, `plate` VARCHAR(255) DEFAULT NULL,
`glovebox` LONGTEXT DEFAULT '[]', `glovebox` LONGTEXT DEFAULT ('[]'),
`trunk` LONGTEXT DEFAULT '[]', `trunk` LONGTEXT DEFAULT ('[]'),
`properties` LONGTEXT DEFAULT '[]', `properties` LONGTEXT DEFAULT ('[]'),
`stored` INT(11) DEFAULT '1', `stored` INT(11) DEFAULT '1',
`impounded` INT(11) DEFAULT '0', `impounded` INT(11) DEFAULT '0',
`stolen` INT(11) DEFAULT '0', `stolen` INT(11) DEFAULT '0',
`metadata` LONGTEXT DEFAULT '[]', `metadata` LONGTEXT DEFAULT ('[]'),
PRIMARY KEY (`id`) USING BTREE, PRIMARY KEY (`id`) USING BTREE,
INDEX `owner` (`owner`) USING BTREE, INDEX `owner` (`owner`) USING BTREE,
CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE
); );

View File

@@ -2,7 +2,7 @@
author "Andyyy#7666" author "Andyyy#7666"
description "ND Framework Core" description "ND Framework Core"
version "2.0.0" version "2.3.0"
fx_version "cerulean" fx_version "cerulean"
game "gta5" game "gta5"
@@ -37,6 +37,11 @@ files {
"compatibility/**/locale.lua" "compatibility/**/locale.lua"
} }
dependency "oxmysql" dependenies {
"ox_lib",
"oxmysql"
}
-- below were used with backwards compatibility but could interfere with resources checking if the resources are started.
-- provide "es_extended" -- provide "es_extended"
-- provide "qb-Core" -- provide "qb-Core"

View File

@@ -15,7 +15,7 @@ local moneyActions = {
} }
lib.addCommand("setmoney", { lib.addCommand("setmoney", {
help = "Admin command, manage player money.", help = "Admin command, set a players money.",
restricted = "group.admin", restricted = "group.admin",
params = { params = {
{ {
@@ -164,7 +164,7 @@ lib.addCommand("setgroup", {
end) end)
lib.addCommand("skin", { lib.addCommand("skin", {
help = "Admin command, set player into character clothing menu.", help = "Admin command, show character clothing menu for player.",
restricted = "group.admin", restricted = "group.admin",
params = { params = {
{ {
@@ -178,7 +178,7 @@ lib.addCommand("skin", {
end) end)
lib.addCommand("character", { lib.addCommand("character", {
help = "Admin command, set player into character selection menu.", help = "Admin command, show character selection menu for player.",
restricted = "group.admin", restricted = "group.admin",
params = { params = {
{ {
@@ -194,27 +194,50 @@ end)
lib.addCommand("pay", { lib.addCommand("pay", {
help = "give money to nearby player.", help = "give money to nearby player.",
params = { params = {
{
name = "target",
type = "playerId",
help = "Target player's server id"
},
{ {
name = "amount", name = "amount",
type = "number" type = "number",
help = "Amount of cash to give"
} }
} }
}, function(source, args, raw) }, function(source, args, raw)
if not source then return end 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
local player = NDCore.getPlayer(source) local player = NDCore.getPlayer(source)
if not player then return end local targetPlayer = NDCore.getPlayer(args.target)
local success = player.deductMoney("cash", args.amount)
if not player or not targetPlayer then
return player.notify({
title = "Error",
description = "No player found",
type = "error"
})
end
if player.cash < args.amount then
return player.notify({
title = "Error",
description = "You don't have 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 = "Player is not nearby",
type = "error"
})
end
local success = player.deductMoney("cash", args.amount) and targetPlayer.addMoney("cash", args.amount)
if not success then if not success then
return player.notify({ return player.notify({
title = "Couldn't give money", title = "Couldn't give money",
@@ -223,55 +246,38 @@ lib.addCommand("pay", {
}) })
end end
if not targetPlayer or not targetPlayer.addMoney("cash", args.amount) then return end
targetPlayer.notify({ targetPlayer.notify({
title = "Money received", title = "Money received",
description = ("Received $%d in cash"):format(args.amount), description = ("Received $%d in cash"):format(args.amount),
type = "inform", type = "inform",
duration = 10000 duration = 5000
}) })
player.notify({ player.notify({
title = "Money given", title = "Money given",
description = ("You gave someone $%d in cash"):format(args.amount), description = ("You gave $%d in cash"):format(args.amount),
type = "inform", type = "inform",
duration = 10000 duration = 5000
}) })
end) end)
lib.addCommand("unlock", { lib.addCommand("unlock", {
help = "Admin force unlock vehicles", help = "Admin Command, force unlock a vehicle.",
restricted = "group.admin", restricted = "group.admin",
}, function(source, args, raw) }, function(source, args, raw)
local ped = GetPlayerPed(source) local ped = GetPlayerPed(source)
local playerVeh = GetVehiclePedIsIn(ped)
local coords = GetEntityCoords(ped) local coords = GetEntityCoords(ped)
local vehicles = GetAllVehicles() local veh = lib.getClosestVehicle(coords, 2.5, true)
local maxDistance = 2.0
local closestVehicle
if not playerVeh or playerVeh == 0 or not DoesEntityExist(playerVeh) then if not veh then return end
for i=1, #vehicles do
local vehicle = vehicles[i]
local vehicleCoords = GetEntityCoords(vehicle)
local distance = #(coords-vehicleCoords)
if distance < maxDistance then local state = Entity(veh).state
maxDistance = distance state.hotwired = true
closestVehicle = vehicle state.locked = false
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
end) end)
lib.addCommand("revive", { lib.addCommand("revive", {
help = "Admin command, revive player.", help = "Admin command, revive a player.",
restricted = "group.admin", restricted = "group.admin",
params = { params = {
{ {
@@ -287,60 +293,48 @@ lib.addCommand("revive", {
end) end)
lib.addCommand("dv", { 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", restricted = "group.admin",
params = { params = {
{ {
name = "range", name = "range",
type = "number", type = "number",
help = "The range to select vehicles for deleteion from", help = "Range to delete vehicles in",
optional = true optional = true
} }
} }
}, function(source, args, raw) }, function(source, args, raw)
local ped = GetPlayerPed(source) 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 coords = GetEntityCoords(ped)
local count = 0
local message = nil
local veh = lib.getClosestVehicle(coords, 3.0)
if args.range then if args.range then
for _, veh in ipairs(GetAllVehicles()) do local vehicles = lib.getNearbyVehicles(coords, args.range)
local vehDist = #(GetEntityCoords(veh) - coords) count = #vehicles
if vehDist < args.range then
DeleteEntity(veh) for i=1, count do
count += 1 local veh = vehicles[i]
end DeleteEntity(veh.vehicle)
end end
return TriggerClientEvent("chat:addMessage", source, { elseif veh then
color = {50, 100, 235}, DeleteEntity(veh)
multiline = true, count = 1
args = {"Staff action", ("deleted %d vehicles"):format(count)}
})
end end
local closest, dist if count == 0 then
for _, veh in ipairs(GetAllVehicles()) do message = {"Staff action [dv]", "no vehicles found"}
local vehDist = #(GetEntityCoords(veh) - coords) elseif count == 1 then
if vehDist < 5.0 and not closest or (dist and dist > vehDist) then message = {"Staff action [dv]", "deleted 1 vehicle"}
closest = veh else
dist = vehDist message = {"Staff action [dv]", ("deleted %d vehicles"):format(count)}
end
end end
if not closest then return "no vehicle found nearby" end
DeleteEntity(closest)
TriggerClientEvent("chat:addMessage", source, { TriggerClientEvent("chat:addMessage", source, {
color = {50, 100, 235}, color = {50, 100, 235},
multiline = true, multiline = true,
args = {"Staff action", "deleted 1 vehicle"} args = message
}) })
end) end)
@@ -411,7 +405,7 @@ lib.addCommand("unfreeze", {
end) end)
lib.addCommand("vehicle", { lib.addCommand("vehicle", {
help = "Admin command, unfreeze a player.", help = "Admin command, spawn a vehicle.",
restricted = "group.admin", restricted = "group.admin",
params = { params = {
{ {
@@ -444,3 +438,45 @@ lib.addCommand("vehicle", {
Wait(100) Wait(100)
end end
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)

View File

@@ -1,117 +0,0 @@
-- For support join my discord: https://discord.gg/Z9Mxu72zZ6
-- Check if discord is connected, and if whitelist is enabled then it will only allow you to join if you have the roles.
AddEventHandler("playerConnecting", function(name, setKickReason, deferrals)
local player = source
local discordIdentifier = NDCore.Functions.GetPlayerIdentifierFromType("discord", player)
deferrals.defer()
Wait(0)
deferrals.update("Connecting to discord.")
Wait(0)
if not discordIdentifier then
deferrals.done("Your discord isn't connected to FiveM, make sure discord is open and restart FiveM.")
else
if config.enableDiscordWhitelist then
local discordUserId = discordIdentifier:gsub("discord:", "")
local discordInfo = NDCore.Functions.GetUserDiscordInfo(discordUserId)
for _, whitelistRole in pairs(config.whitelistRoles) do
if whitelistRole == 0 or whitelistRole == "0" or (discordInfo and discordInfo.roles[whitelistRole]) then
deferrals.done()
break
end
end
deferrals.done(config.notWhitelistedMessage)
else
deferrals.done()
end
end
end)
-- Getting all the characters the player has and returning them to the client.
RegisterNetEvent("ND:GetCharacters", function()
local player = source
TriggerClientEvent("ND:returnCharacters", player, NDCore.Functions.GetPlayerCharacters(player))
end)
-- Creating a new character.
RegisterNetEvent("ND:newCharacter", function(newCharacter)
local player = source
NDCore.Functions.CreateCharacter(player, newCharacter.firstName, newCharacter.lastName, newCharacter.dob, newCharacter.gender, newCharacter.cash, newCharacter.bank)
end)
-- Update the character info when edited.
RegisterNetEvent("ND:editCharacter", function(newCharacter)
local player = source
local characters = NDCore.Functions.GetPlayerCharacters(player)
if not characters[newCharacter.id] then return end
NDCore.Functions.UpdateCharacterData(newCharacter.id, newCharacter.firstName, newCharacter.lastName, newCharacter.dob, newCharacter.gender)
end)
-- Delete character from database.
RegisterNetEvent("ND:deleteCharacter", function(characterId)
local player = source
local characters = NDCore.Functions.GetPlayerCharacters(player)
if not characters[characterId] then return end
NDCore.Functions.DeleteCharacter(characterId)
end)
-- add a player to the table.
RegisterNetEvent("ND:setCharacterOnline", function(id)
local player = source
local characters = NDCore.Functions.GetPlayerCharacters(player)
if not characters[id] then return end
NDCore.Functions.SetActiveCharacter(player, id)
end)
-- Update the characters clothes.
RegisterNetEvent("ND:updateClothes", function(clothing)
local player = source
local character = NDCore.Players[player]
NDCore.Functions.SetPlayerData(character.id, "clothing", clothing)
end)
-- Disconnecting a player
RegisterNetEvent("ND:exitGame", function()
local player = source
DropPlayer(player, "Disconnected.")
end)
-- Remove player from NDCore.Players table when they leave.
AddEventHandler("playerDropped", function()
local player = source
local character = NDCore.Players[player]
if character then
local ped = GetPlayerPed(player)
local lastLocation = GetEntityCoords(ped)
NDCore.Functions.UpdateLastLocation(character.id, {x = lastLocation.x, y = lastLocation.y, z = lastLocation.z})
end
TriggerEvent("ND:characterUnloaded", player, character)
character = nil
end)
-- Get player discord info on join.
AddEventHandler("playerJoining", function()
local src = source
local discordUserId = NDCore.Functions.GetPlayerIdentifierFromType("discord", src):gsub("discord:", "")
local discordInfo = NDCore.Functions.GetUserDiscordInfo(discordUserId)
NDCore.PlayersDiscordInfo[src] = discordInfo
end)
AddEventHandler("onResourceStart", function(resourceName)
if (GetCurrentResourceName() ~= resourceName) then
return
end
Wait(1000)
if not next(NDCore.PlayersDiscordInfo) then
for _, playerId in ipairs(GetPlayers()) do
local discordUserId = NDCore.Functions.GetPlayerIdentifierFromType("discord", playerId):gsub("discord:", "")
local discordInfo = NDCore.Functions.GetUserDiscordInfo(discordUserId)
NDCore.PlayersDiscordInfo[tonumber(playerId)] = discordInfo
end
end
end)

View File

@@ -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 ---@param src number
---@return table ---@return table
function NDCore.getPlayer(src) function NDCore.getPlayer(src)
@@ -34,6 +41,13 @@ function NDCore.getPlayerServerInfo(source)
return PlayersInfo[source] return PlayersInfo[source]
end end
function NDCore.getConfig(info)
if not info then
return Config
end
return Config[info]
end
---@param fileLocation string|tabale ---@param fileLocation string|tabale
---@return boolean ---@return boolean
function NDCore.loadSQL(fileLocation, resource) function NDCore.loadSQL(fileLocation, resource)

View File

@@ -7,6 +7,7 @@ local tempPlayersInfo = {}
Config = { Config = {
serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"),
discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"),
discordMemeberRequired = GetConvarInt("core:discordMemeberRequired", 1) == 1,
discordAppId = GetConvar("core:discordAppId", "858146067018416128"), discordAppId = GetConvar("core:discordAppId", "858146067018416128"),
discordAsset = GetConvar("core:discordAsset", "andyyy"), discordAsset = GetConvar("core:discordAsset", "andyyy"),
discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"),
@@ -23,8 +24,10 @@ Config = {
groups = json.decode(GetConvar("core:groups", "[]")), groups = json.decode(GetConvar("core:groups", "[]")),
admins = json.decode(GetConvar("core:admins", "[]")), admins = json.decode(GetConvar("core:admins", "[]")),
adminDiscordRoles = json.decode(GetConvar("core:adminDiscordRoles", "[]")), adminDiscordRoles = json.decode(GetConvar("core:adminDiscordRoles", "[]")),
groupRoles = json.decode(GetConvar("core:groupRoles", "[]")),
multiCharacter = false, multiCharacter = false,
compatibility = json.decode(GetConvar("core:compatibility", "[]")) compatibility = json.decode(GetConvar("core:compatibility", "[]")),
sv_lan = GetConvar("sv_lan", "false") == "true",
} }
SetConvarServerInfo("Discord", Config.discordInvite) SetConvarServerInfo("Discord", Config.discordInvite)
@@ -41,13 +44,23 @@ local function getIdentifierList(src)
list[identifierType] = identifier list[identifierType] = identifier
end end
end end
if Config.sv_lan then
list[Config.characterIdentifier] = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
end
return list return list
end end
AddEventHandler("playerJoining", function(oldId) AddEventHandler("playerJoining", function(oldId)
local src = source local src = source
PlayersInfo[src] = tempPlayersInfo[oldId] local oldTempId = tonumber(oldId)
tempPlayersInfo[oldId] = nil PlayersInfo[src] = tempPlayersInfo[oldTempId]
tempPlayersInfo[oldTempId] = nil
if Config.sv_lan then
lib.addPrincipal(("player.%s"):format(src), "group.admin")
end
if Config.multiCharacter then return end if Config.multiCharacter then return end
Wait(3000) Wait(3000)
@@ -93,14 +106,14 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals)
local tempSrc = source local tempSrc = source
local identifiers = getIdentifierList(tempSrc) local identifiers = getIdentifierList(tempSrc)
local mainIdentifier = identifiers[Config.characterIdentifier] local mainIdentifier = identifiers[Config.characterIdentifier]
local discordInfo = {} local discordInfo = nil
deferrals.defer() deferrals.defer()
Wait(0) 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) discordInfo = checkDiscordIdentifier(identifiers)
if not discordInfo then if not discordInfo and Config.discordMemeberRequired and not Config.sv_lan then
deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite)) deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite))
Wait(0) Wait(0)
end end
@@ -109,6 +122,17 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals)
deferrals.update("Connecting...") deferrals.update("Connecting...")
Wait(0) Wait(0)
if Config.sv_lan then
tempPlayersInfo[tempSrc] = {
identifiers = {
[Config.characterIdentifier] = "sv_lan"
},
discord = discordInfo
}
deferrals.done()
return
end
if mainIdentifier then if mainIdentifier then
tempPlayersInfo[tempSrc] = { tempPlayersInfo[tempSrc] = {
identifiers = identifiers, identifiers = identifiers,
@@ -152,3 +176,10 @@ RegisterNetEvent("ND:playerEliminated", function(info)
deathInfo = info deathInfo = info
}) })
end) 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)

View File

@@ -25,6 +25,7 @@ local function createCharacterTable(info)
gender = info.gender, gender = info.gender,
cash = info.cash, cash = info.cash,
bank = info.bank, bank = info.bank,
phonenumber = info.phonenumber,
groups = info.groups, groups = info.groups,
metadata = info.metadata, metadata = info.metadata,
inventory = info.inventory inventory = info.inventory
@@ -112,7 +113,8 @@ local function createCharacterTable(info)
TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason) TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason)
end end
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), key)
TriggerEvent("ND:updateCharacter", self, key)
end end
---@param key string|table ---@param key string|table
@@ -126,7 +128,8 @@ local function createCharacterTable(info)
else else
self.metadata[key] = value self.metadata[key] = value
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata")
TriggerEvent("ND:updateCharacter", self, "metadata")
return self.metadata return self.metadata
end end
@@ -164,8 +167,23 @@ local function createCharacterTable(info)
end end
-- Save character information to database -- Save character information to database
function self.save() function self.save(key)
local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ? WHERE charid = ?", { 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 name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, phonenumber = ?, `groups` = ?, metadata = ? WHERE charid = ?", {
self.name, self.name,
self.firstname, self.firstname,
self.lastname, self.lastname,
@@ -173,6 +191,7 @@ local function createCharacterTable(info)
self.gender, self.gender,
self.cash, self.cash,
self.bank, self.bank,
self.phonenumber,
json.encode(self.groups), json.encode(self.groups),
json.encode(self.metadata), json.encode(self.metadata),
self.id self.id
@@ -206,7 +225,8 @@ local function createCharacterTable(info)
else else
self.metadata.licenses = {license} self.metadata.licenses = {license}
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata")
TriggerEvent("ND:updateCharacter", self, "metadata")
end end
function self.getLicense(identifier) function self.getLicense(identifier)
@@ -252,11 +272,7 @@ local function createCharacterTable(info)
function self.notify(...) function self.notify(...)
if not self.source then return end if not self.source then return end
if GetResourceState("ModernHUD") == "started" then TriggerClientEvent("ox_lib:notify", self.source, ...)
TriggerClientEvent("ModernHUD:notify", self.source, ...)
elseif GetResourceState("ox_lib") == "started" then
TriggerClientEvent("ox_lib:notify", self.source, ...)
end
return true return true
end end
@@ -293,6 +309,12 @@ local function createCharacterTable(info)
self.addGroup("admin") self.addGroup("admin")
end end
end end
for group, role in pairs(Config.groupRoles) do
if lib.table.contains(roles, role) then
self.addGroup(group)
end
end
end end
for name, _ in pairs(self.groups) do for name, _ in pairs(self.groups) do
@@ -307,23 +329,38 @@ local function createCharacterTable(info)
---@param rank number ---@param rank number
---@param isJob boolean ---@param isJob boolean
---@return boolean ---@return boolean
function self.addGroup(name, rank, isJob) function self.addGroup(name, rank, customGroup, isJob)
local groupRank = tonumber(rank) or 1 local groupRank = tonumber(rank) or 1
local groupInfo = Config.groups[name] local groupInfo = lib.table.deepclone(Config.groups?[name] or {})
-- if not groupInfo then return end local bossRank = groupInfo?.minimumBossRank
for k, v in pairs(customGroup or {}) do
groupInfo[k] = v
end
if isJob then if isJob then
for _, group in pairs(self.groups) do for _, group in pairs(self.groups) do
group.isJob = nil group.isJob = nil
end end
end end
self.groups[name] = { self.groups[name] = {
label = groupInfo and groupInfo.label or name, name = name,
rankName = groupInfo and groupInfo.ranks[groupRank] or groupRank, label = groupInfo?.label or name,
rankName = groupInfo?.ranks?[groupRank] or groupRank,
rank = groupRank, rank = groupRank,
isJob = isJob isJob = isJob,
isBoss = bossRank and groupRank >= bossRank,
metadata = groupInfo.metadata or {}
} }
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
if not isJob then
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups")
TriggerEvent("ND:updateCharacter", self, "groups")
end
lib.addPrincipal(self.source, ("group.%s"):format(name)) lib.addPrincipal(self.source, ("group.%s"):format(name))
return self.groups[name] return self.groups[name]
end end
@@ -337,8 +374,16 @@ local function createCharacterTable(info)
function self.removeGroup(name) function self.removeGroup(name)
local group = self.groups[name] local group = self.groups[name]
if not group then return end if not group then return end
if self.job == name then
self.job = nil
self.jobInfo = nil
end
self.groups[name] = nil 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)) lib.removePrincipal(self.source, ("group.%s"):format(name))
return group return group
end end
@@ -348,12 +393,13 @@ local function createCharacterTable(info)
---@return boolean ---@return boolean
function self.setJob(name, rank) function self.setJob(name, rank)
self.removeGroup(self.job) self.removeGroup(self.job)
local job = self.addGroup(name, rank, true) local job = self.addGroup(name, rank, nil, true)
local jobName, jobInfo = self.getJob() if job then
if jobInfo then self.job = job.name
self.job = jobName self.jobInfo = job
self.jobInfo = jobInfo
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "job")
TriggerEvent("ND:updateCharacter", self, "job")
return job return job
end end
@@ -380,7 +426,7 @@ end
---@param info table ---@param info table
---@return table ---@return table
function NDCore.newCharacter(src, info) function NDCore.newCharacter(src, info)
local identifier = GetPlayerIdentifierByType(src, Config.characterIdentifier) local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
if not identifier then return end if not identifier then return end
local charInfo = { local charInfo = {
@@ -393,12 +439,13 @@ function NDCore.newCharacter(src, info)
gender = info.gender or "", gender = info.gender or "",
cash = info.cash or 0, cash = info.cash or 0,
bank = info.bank or 0, bank = info.bank or 0,
phonenumber = info.phonenumber,
groups = info.groups or {}, groups = info.groups or {},
metadata = info.metadata 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", { charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, phonenumber, `groups`, metadata, inventory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", {
identifier, identifier,
charInfo.name, charInfo.name,
charInfo.firstname, charInfo.firstname,
@@ -407,8 +454,10 @@ function NDCore.newCharacter(src, info)
charInfo.gender, charInfo.gender,
charInfo.cash, charInfo.cash,
charInfo.bank, charInfo.bank,
charInfo.phonenumber,
json.encode(charInfo.groups), json.encode(charInfo.groups),
json.encode(charInfo.metadata) json.encode(charInfo.metadata),
json.encode(charInfo.inventory)
}) })
return createCharacterTable(charInfo) return createCharacterTable(charInfo)
@@ -419,7 +468,7 @@ end
function NDCore.fetchCharacter(id, src) function NDCore.fetchCharacter(id, src)
local result local result
if src then if src then
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? and identifier = ?", {id, GetPlayerIdentifierByType(src, Config.characterIdentifier)}) result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? and identifier = ?", {id, NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)})
else else
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id}) result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id})
end end
@@ -437,6 +486,7 @@ function NDCore.fetchCharacter(id, src)
gender = info.gender, gender = info.gender,
cash = info.cash, cash = info.cash,
bank = info.bank, bank = info.bank,
phonenumber = info.phonenumber,
groups = json.decode(info.groups), groups = json.decode(info.groups),
metadata = json.decode(info.metadata), metadata = json.decode(info.metadata),
inventory = json.decode(info.inventory) inventory = json.decode(info.inventory)
@@ -447,7 +497,7 @@ end
---@return table ---@return table
function NDCore.fetchAllCharacters(src) function NDCore.fetchAllCharacters(src)
local characters = {} local characters = {}
local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {GetPlayerIdentifierByType(src, Config.characterIdentifier)}) local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)})
for i=1, #result do for i=1, #result do
local info = result[i] local info = result[i]
@@ -462,9 +512,10 @@ function NDCore.fetchAllCharacters(src)
gender = info.gender, gender = info.gender,
cash = info.cash, cash = info.cash,
bank = info.bank, bank = info.bank,
phonenumber = info.phonenumber,
groups = json.decode(info.groups), groups = json.decode(info.groups),
metadata = json.decode(info.metadata), metadata = json.decode(info.metadata),
inventory = json.decode(info.inventory), inventory = json.decode(info.inventory)
}) })
end end
return characters return characters

View File

@@ -1,6 +1,7 @@
local ox_inventory local ox_inventory
local inventoryStarted = false local inventoryStarted = false
local spawnedPlayerVehicles = {} local spawnedPlayerVehicles = {}
local vehicleTypes = json.decode(GetResourceKvpString("ND_Core:vehTypes") or "[]")
NDCore.isResourceStarted("ox_inventory", function(started) NDCore.isResourceStarted("ox_inventory", function(started)
inventoryStarted = started inventoryStarted = started
@@ -8,16 +9,24 @@ NDCore.isResourceStarted("ox_inventory", function(started)
ox_inventory = exports.ox_inventory ox_inventory = exports.ox_inventory
end) end)
local function getVehicleType(model) local function getVehicleType(coords, model)
local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, true, true) 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() 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 if not DoesEntityExist(tempVehicle) then return end
local entityType = GetVehicleType(tempVehicle)
local vehType = GetVehicleType(tempVehicle)
DeleteEntity(tempVehicle) DeleteEntity(tempVehicle)
return entityType
vehicleTypes[model] = vehType
SetResourceKvp("ND_Core:vehTypes", json.encode(vehicleTypes))
return vehType
end end
local function generatePlate() local function generatePlate()
@@ -90,18 +99,35 @@ function NDCore.getVehicle(entity)
if saveProperties and self.id and self.owner then if saveProperties and self.id and self.owner then
local properties = lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity), self.netId) local properties = lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity), self.netId)
if properties then if properties then
if self.properties?.callsign then
properties.callsign = true
end
MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id}) MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id})
end end
end end
DeleteEntity(entity) DeleteEntity(entity)
end 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 --- set vehicle properties
---@param props table ---@param props table
function self.setProperties(props) function self.setProperties(props)
if not DoesEntityExist(entity) then return end if not DoesEntityExist(entity) then return end
local properties = type(props) == "string" and json.decode(props) or props local properties = type(props) == "string" and json.decode(props) or props
local state = Entity(entity).state local state = Entity(entity).state
if self.properties?.callsign then
properties.callsign = true
end
state.props = properties state.props = properties
self.properties = properties self.properties = properties
@@ -148,13 +174,22 @@ function NDCore.getVehicle(entity)
function self.setMetadata(key, value) function self.setMetadata(key, value)
self.metadata[key] = value self.metadata[key] = value
if DoesEntityExist(entity) then if DoesEntityExist(entity) then
local state = Entity(self.entity).state local state = Entity(self.entity).state
local metadata = state.metadata
metadata[key] = value if not state.metadata then
state.metadata = metadata state.metadata = self.metadata
else
state.metadata[key] = value
end
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}) MySQL.query("UPDATE nd_vehicles SET metadata = ? WHERE id = ?", {json.encode(self.metadata), self.id})
end end
@@ -245,14 +280,14 @@ function NDCore.createVehicle(info)
end end
local model = info.model or properties.model local model = info.model or properties.model
local vehType = getVehicleType(model) local vehType = getVehicleType(spawnCoords, model)
if not vehType then if not vehType then
return Citizen.Trace("NDCore.createVehicle", "vehType not found") return Citizen.Trace("NDCore.createVehicle", "vehType not found")
end end
local veh = CreateVehicleServerSetter(model, vehType, spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w) local veh = CreateVehicleServerSetter(model, vehType, spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w)
local time = os.time() 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 if not veh or not DoesEntityExist(veh) then
return Citizen.Trace("NDCore.createVehicle", "vehicle entity doesn't exist") return Citizen.Trace("NDCore.createVehicle", "vehicle entity doesn't exist")
end end
@@ -403,14 +438,31 @@ function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading)
if not player then return end if not player then return end
local vehicle = NDCore.getVehicleById(vehicleId) local vehicle = NDCore.getVehicleById(vehicleId)
if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end 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}) 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({ return NDCore.createVehicle({
owner = player.id, 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), coords = vec4(coords.x, coords.y, coords.z, coords.w or coords.heading or heading),
properties = vehicle.properties, properties = properties,
vehicleId = vehicleId, vehicleId = vehicleId,
source = source source = source
}) })
@@ -567,12 +619,24 @@ RegisterNetEvent("ND_Vehicles:toggleVehicleLock", function(netId)
end) end)
-- locking of npc vehicles, if the players spawns inside a vehicle it won't be locked. -- locking of npc vehicles, if the players spawns inside a vehicle it won't be locked.
RegisterNetEvent("entityCreated", function(entity) AddEventHandler("entityCreated", function(entity)
local time = os.time()
while not DoesEntityExist(entity) and os.time()-time < 5 do Wait(50) end
if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end
local state = Entity(entity).state local state = Entity(entity).state
if state.owner or state.locked ~= nil then return end if state.owner or state.locked ~= nil then return end
time = os.time()
local driver = GetPedInVehicleSeat(entity, -1) 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 if DoesEntityExist(driver) and IsPedAPlayer(driver) then
state.locked = false state.locked = false
state.hotwired = true state.hotwired = true
@@ -665,6 +729,11 @@ RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations)
local vehicle = NDCore.getVehicleById(vehId) local vehicle = NDCore.getVehicleById(vehId)
local player = NDCore.getPlayer(src) local player = NDCore.getPlayer(src)
if not player or not vehicle or vehicle.owner ~= player.id then return end if not player or not vehicle or vehicle.owner ~= player.id then return end
local info = NDCore.spawnOwnedVehicle(src, vehicle.id, isParkingAvailable(locations))
if not info then return end
TriggerClientEvent("ND_Vehicles:blip", src, info.netId, true)
if vehicle.impounded then if vehicle.impounded then
local reclaimPrice = vehicle.metadata.impoundReclaimPrice or 200 local reclaimPrice = vehicle.metadata.impoundReclaimPrice or 200
if not player.deductMoney("bank", reclaimPrice, "Vehicle impound reclaim") then if not player.deductMoney("bank", reclaimPrice, "Vehicle impound reclaim") then
@@ -683,10 +752,6 @@ RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations)
}) })
MySQL.query.await("UPDATE nd_vehicles SET impounded = ? WHERE id = ?", {0, vehicle.id}) MySQL.query.await("UPDATE nd_vehicles SET impounded = ? WHERE id = ?", {0, vehicle.id})
end end
local info = NDCore.spawnOwnedVehicle(src, vehicle.id, isParkingAvailable(locations))
if not info then return end
TriggerClientEvent("ND_Vehicles:blip", src, info.netId, true)
end) end)
lib.callback.register("ND_Vehicles:getOwnedVehicles", function(src) lib.callback.register("ND_Vehicles:getOwnedVehicles", function(src)
@@ -694,3 +759,30 @@ lib.callback.register("ND_Vehicles:getOwnedVehicles", function(src)
if not player then return end if not player then return end
return NDCore.getVehicles(player.id) return NDCore.getVehicles(player.id)
end) 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 impounded = ? WHERE owner = ? AND id IN (%s)"):format(table.concat(vehiclesToImpound, ", "))
MySQL.rawExecute(query, {1, player.id})
end)

View File

@@ -1 +0,0 @@
NDCore = exports["ND_Core"]:GetCoreObject()

View File

@@ -1,30 +0,0 @@
local startedResources = {}
local callbacks = {}
AddEventHandler("onResourceStart", function(resourceName)
startedResources[resourceName] = true
local callback = callbacks[resourceName]
if not callback then return end
callback(true)
end)
AddEventHandler("onResourceStop", function(resourceName)
startedResources[resourceName] = nil
local callback = callbacks[resourceName]
if not callback then return end
callback(false)
end)
function isResourceStarted(resourceName, cb)
local started = GetResourceState(resourceName) == "started"
startedResources[resourceName] = started
if cb then
callbacks[resourceName] = cb
cb(started)
end
return started
end
exports("isResourceStarted", isResourceStarted)