58 Commits

Author SHA1 Message Date
Andyyy7666
70a1487420 wip(compat/qb/server): basic functions 2024-03-09 19:07:30 +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
19 changed files with 447 additions and 167 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,14 +29,13 @@ 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)

View File

@@ -22,5 +22,50 @@ RegisterNetEvent("ND:updateLastLocation", function(location)
end) end)
RegisterNetEvent("ND:revivePlayer", function() RegisterNetEvent("ND:revivePlayer", function()
NDCore.revivePlayer(true) if source == "" then return 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
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,11 +7,12 @@ 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", "[]"))

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

@@ -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)
@@ -470,22 +480,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
@@ -697,9 +720,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 +1,73 @@
if not lib.table.contains(Config.compatibility, "qb") then return end if not lib.table.contains(Config.compatibility, "qb") then return end
local QBCore = {}
QBCore.Config = QBConfig
QBCore.Shared = QBShared
QBCore.ClientCallbacks = {}
QBCore.ServerCallbacks = {}
QBCore.Players = {}
QBCore.Functions = {}
QBCore.Player_Buckets = {}
QBCore.Entity_Buckets = {}
QBCore.UsableItems = {}
function QBCore.Functions.GetCoords(entity)
local coords = GetEntityCoords(entity)
local heading = GetEntityHeading(entity)
return vec4(coords.x, coords.y, coords.z, heading)
end
function QBCore.Functions.GetIdentifier(source, idtype)
if GetConvarInt("sv_fxdkMode", 0) == 1 then return "license:fxdk" end
return GetPlayerIdentifierByType(source, idtype or "license")
end
function QBCore.Functions.GetSource(identifier)
for src, _ in pairs(NDCore.players) do
local idens = GetPlayerIdentifiers(src)
for _, id in pairs(idens) do
if identifier == id then
return src
end
end
end
return 0
end
local function createPlayer(player)
end
function QBCore.Functions.GetPlayer(source)
if type(source) == "number" then
return createPlayer(NDCore.players[source])
else
return createPlayer(NDCore.players[QBCore.Functions.GetSource(source)])
end
end
function QBCore.Functions.GetPlayerByCitizenId(citizenid)
for src in pairs(NDCore.players) do
if NDCore.players[src].id == citizenid then
return NDCore.players[src]
end
end
end
-- function QBCore.Functions.GetOfflinePlayerByCitizenId(citizenid)
-- return QBCore.Player.GetOfflinePlayer(citizenid)
-- end
-- function QBCore.Functions.GetPlayerByLicense(license)
-- return QBCore.Player.GetPlayerByLicense(license)
-- end
function QBCore.Functions.GetPlayerByPhone(number)
for src in pairs(NDCore.players) do
if NDCore.players[src].phonenumber == number then
return createPlayer(NDCore.players[src])
end
end
end

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.1.2"
fx_version "cerulean" fx_version "cerulean"
game "gta5" game "gta5"
@@ -38,5 +38,7 @@ files {
} }
dependency "oxmysql" dependency "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

@@ -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,24 +246,23 @@ 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 vehicle.",
restricted = "group.admin", restricted = "group.admin",
}, function(source, args, raw) }, function(source, args, raw)
local ped = GetPlayerPed(source) local ped = GetPlayerPed(source)
@@ -287,13 +309,13 @@ 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
} }
} }

View File

@@ -34,6 +34,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,6 +24,7 @@ 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", "[]"))
} }
@@ -46,8 +48,9 @@ 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.multiCharacter then return end if Config.multiCharacter then return end
Wait(3000) Wait(3000)
@@ -93,14 +96,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 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
@@ -152,3 +155,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
@@ -113,6 +114,7 @@ local function createCharacterTable(info)
end end
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
TriggerEvent("ND:updateCharacter", self)
end end
---@param key string|table ---@param key string|table
@@ -127,6 +129,7 @@ local function createCharacterTable(info)
self.metadata[key] = value self.metadata[key] = value
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
TriggerEvent("ND:updateCharacter", self)
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
@@ -207,6 +226,7 @@ local function createCharacterTable(info)
self.metadata.licenses = {license} self.metadata.licenses = {license}
end end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
TriggerEvent("ND:updateCharacter", self)
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,34 @@ 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, info, isJob)
local groupRank = tonumber(rank) or 1 local groupRank = tonumber(rank) or 1
local groupInfo = Config.groups[name] local groupInfo = info or Config.groups?[name]
local bossRank = groupInfo?.minimumBossRank
-- if not groupInfo then return end -- if not groupInfo then return 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
} }
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
if not isJob then
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
TriggerEvent("ND:updateCharacter", self)
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 +370,15 @@ 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))
TriggerEvent("ND:updateCharacter", self)
lib.removePrincipal(self.source, ("group.%s"):format(name)) lib.removePrincipal(self.source, ("group.%s"):format(name))
return group return group
end end
@@ -348,12 +388,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))
TriggerEvent("ND:updateCharacter", self)
return job return job
end end
@@ -393,12 +434,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 +449,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)
@@ -437,6 +481,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)
@@ -462,9 +507,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

@@ -90,6 +90,9 @@ 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
@@ -102,6 +105,11 @@ function NDCore.getVehicle(entity)
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
@@ -406,11 +414,27 @@ function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading)
if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end if not vehicle or not vehicle.available or vehicle.owner ~= player.id 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 +591,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
@@ -694,3 +730,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)