From 3afa513b4adbd0066da7edc96c1495e7de6c91da Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Mon, 28 Jul 2025 00:09:15 +0200 Subject: [PATCH] ed --- .../[Nordi]/tgiann-weapons-on-back/.fxap | Bin 0 -> 178 bytes .../tgiann-weapons-on-back/checkInv.lua | 28 + .../client/bridge/codem_inv.lua | 89 +++ .../client/bridge/core_inv.lua | 116 +++ .../client/bridge/esx_inv.lua | 88 +++ .../client/bridge/main.lua | 99 +++ .../client/bridge/mf_inv.lua | 77 ++ .../client/bridge/origen_inv.lua | 79 ++ .../client/bridge/ox_inv.lua | 64 ++ .../client/bridge/qb_inv.lua | 64 ++ .../client/bridge/qs_inv.lua | 89 +++ .../client/bridge/tgiann_inv.lua | 62 ++ .../tgiann-weapons-on-back/client/client.lua | Bin 0 -> 6569 bytes .../client/editable.lua | 12 + .../tgiann-weapons-on-back/configs/config.lua | 206 +++++ .../configs/core_inv_config.lua | 5 + .../tgiann-weapons-on-back/fxmanifest.lua | 34 + .../server/bridge/core_inv.lua | 13 + .../server/bridge/esx_inv.lua | 10 + .../tgiann-weapons-on-back/server/server.lua | Bin 0 -> 1954 bytes resources/[standalone]/nordi_tdm/client.lua | 735 +++++------------- resources/[standalone]/nordi_tdm/server.lua | 36 +- 22 files changed, 1331 insertions(+), 575 deletions(-) create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/.fxap create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/checkInv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/codem_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/core_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/esx_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/main.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/mf_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/origen_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/ox_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qb_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qs_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/tgiann_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/client.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/editable.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/config.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/core_inv_config.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/fxmanifest.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/core_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/esx_inv.lua create mode 100644 resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/server.lua diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/.fxap b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/.fxap new file mode 100644 index 0000000000000000000000000000000000000000..f01780bd685b90fca0525a253e377028642c2db6 GIT binary patch literal 178 zcmV;j08Rf!SV2$$000000LKl5+$8u-e)qbG5^XV$dN^^Dvf32#H=*~aP2xFc#sJQ) z%iHa7K`xZQL(KW#rN=$LC*Nhs(hvC~yzg;71;Di`Z@2nIg!(8j%UGL0oW?(k3>15x zAQWH0C!ERw_MKjQ{`u literal 0 HcmV?d00001 diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/checkInv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/checkInv.lua new file mode 100644 index 000000000..c7a602bab --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/checkInv.lua @@ -0,0 +1,28 @@ +config.useDefaultInventory = true + +local function log(msg) + print(string.format('\x1b[32m[tgiann-weapons-on-back]\x1b[0m %s', msg)) +end + +-- Chezza Studios inventory works fine with normal esx(don't add to the list) +local inventorys = { + "tgiann-inventory", + "mf-inventory", + "ox_inventory", + "core_inventory", + "qs-inventory", + "codem-inventory", + "origen_inventory" +} + +for i = 1, #inventorys do + local inventory = inventorys[i] + local isStarted = GetResourceState(inventory) == "started" + if isStarted then + config[inventory] = true + config.useDefaultInventory = false + log(string.format("Started with %s inventory", inventory)) + end +end + +if config.useDefaultInventory then log(string.format("Started with %s default inventory", config.framework == "qb" and "QB" or "ESX")) end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/codem_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/codem_inv.lua new file mode 100644 index 000000000..490b46e57 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/codem_inv.lua @@ -0,0 +1,89 @@ +if not config["codem-inventory"] then return end + +local playerJob = "" +local lastItems = {} + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + SetTimeout(2000, function() -- some waiting time because the character's inventory data is loaded later + lastItems = exports['codem-inventory']:GetClientPlayerInventory() + weaponCheck() + end) +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +RegisterNetEvent('codem-inventory:client:additem') +AddEventHandler('codem-inventory:client:additem', function(slot, data) + lastItems[tostring(slot)] = data + weaponCheck() +end) + +RegisterNetEvent('codem-inventory:client:removeitemtoclientInventory') +AddEventHandler('codem-inventory:client:removeitemtoclientInventory', function(slot, amount) + slot = tostring(slot) + if lastItems[slot] then + local itemAmount = lastItems[slot].count or lastItems[slot].amount + if itemAmount == amount then + lastItems[slot] = nil + end + end + weaponCheck() +end) + +RegisterNetEvent('codem-inventory:client:clearinventory') +AddEventHandler('codem-inventory:client:clearinventory', function() + lastItems = {} + weaponCheck() +end) + +RegisterNetEvent('codem-inventory:client:setitembyslot') +AddEventHandler('codem-inventory:client:setitembyslot', function(slot, itemData) + lastItems[tostring(slot)] = itemData + weaponCheck() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if key == item.info?.serie or item.name then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + if not lastItems then return end + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and item.type == "weapon" then + self.Functions.AddWeapon({ + weapon = item.name, + key = item?.info?.serie or item.name, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.info.tgiattachments, joaat(item.name)) or item.info.attachments, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/core_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/core_inv.lua new file mode 100644 index 000000000..4c3a8ae84 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/core_inv.lua @@ -0,0 +1,116 @@ +-- https://docs.c8re.store/core-inventory/api +if not config.core_inventory then return end + +local function splitStr(str, delimiter) + local result = {} + local from = 1 + local delim_from, delim_to = string.find(str, delimiter, from) + while delim_from do + result[#result + 1] = string.sub(str, from, delim_from - 1) + from = delim_to + 1 + delim_from, delim_to = string.find(str, delimiter, from) + end + result[#result + 1] = string.sub(str, from) + return result +end + +local playerJob = "" +local invItems = {} +local playerItems = {} + +local function getInventoryItems() + tgiCore.cbFunction('tgiann-weapons-on-back:core_inventory:server:getInventory', function(newPlayerItems) + playerItems = newPlayerItems + weaponCheck(true) + end) +end + +RegisterNetEvent('core_inventory:client:sync', function(inventory, data) + local split = splitStr(inventory, '-')[1] + if config.enableInv[split] then + playerItems[split] = data.content + end +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + if not PlayerData then return end + playerJob = PlayerData.job.name + getInventoryItems(PlayerData.identifier) +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck(false) +end) + +if config.framework == "qb" then + RegisterNetEvent('QBCore:Player:SetPlayerData', function(PlayerData) + playerJob = PlayerData.job.name + if not config.enableInv["content"] then return end + playerItems["content"] = PlayerData?.items + weaponCheck(true) + end) +else + RegisterNetEvent('esx:addInventoryItem') + AddEventHandler('esx:addInventoryItem', function(job) + local PlayerData = exports["tgiann-core"]:getPlayerData() + if not PlayerData then return end + getInventoryItems(PlayerData.identifier) + end) + + RegisterNetEvent('esx:removeInventoryItem') + AddEventHandler('esx:removeInventoryItem', function(job) + local PlayerData = exports["tgiann-core"]:getPlayerData() + if not PlayerData then return end + getInventoryItems(PlayerData.identifier) + end) +end + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(invItems) do + if item and key == (item.metadata?.serial or item.name) then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck(updateData) + if updateData then + invItems = {} + for _, inv in pairs(playerItems) do + for _, item in pairs(inv) do + invItems[#invItems + 1] = item + end + end + end + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(invItems) do + if item and string.find(string.lower(item.name), "weapon") then + self.Functions.AddWeapon({ + weapon = item.name, + key = item.metadata?.serial or item.name, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.metadata.tgiattachments, joaat(item.name)) or item.metadata.attachments, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/esx_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/esx_inv.lua new file mode 100644 index 000000000..ff1881be5 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/esx_inv.lua @@ -0,0 +1,88 @@ +if config.framework ~= "esx" then return end +if not config.useDefaultInventory then return end + +local playerJob = "" +local lastItems = {} + +local function getInventoryItems() + tgiCore.cbFunction('tgiann-weapons-on-back:esx_inv:server:getInventory', function(playerItems, loadout) + local itemList = {} + -- ESX is suck + if loadout and #loadout > 0 then + for i = 1, #loadout do + itemList[#itemList + 1] = loadout[i] + end + end + + if playerItems and #playerItems > 0 then + for i = 1, #playerItems do + if playerItems[i].count > 0 then + itemList[#itemList + 1] = playerItems[i] + end + end + end + lastItems = itemList + weaponCheck() + end) +end + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + getInventoryItems() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('esx:addInventoryItem') +AddEventHandler('esx:addInventoryItem', function(job) + getInventoryItems() +end) + +RegisterNetEvent('esx:removeInventoryItem') +AddEventHandler('esx:removeInventoryItem', function(job) + getInventoryItems() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if item and key == item.name then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + self.Functions.CheckWeaponIsRemoved() + Wait(100) + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and string.find(string.lower(item.name), "weapon") then + self.Functions.AddWeapon({ + weapon = item.name, + key = item.name, + attachments = config.tgiann_attachments and + getTgiannAttachments(item?.info?.tgiattachments, joaat(item.name)) or item.components, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/main.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/main.lua new file mode 100644 index 000000000..786946d5f --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/main.lua @@ -0,0 +1,99 @@ +tgiCore = nil +CreateThread(function() + while not tgiCore do + tgiCore = tgiCoreExports:getCore() + Wait(200) + end +end) + +local function LoadModel(model) + if HasModelLoaded(model) then return end + RequestModel(model) + while not HasModelLoaded(model) do Wait(0) end +end + +self.Functions.AddAttachments = function(entity, weaponName, weaponHash, attachments) + if not attachments then return end + if config.tgiann_attachments then + for _, data in pairs(attachments) do + local model = GetWeaponComponentTypeModel(data.component) + if model ~= 0 then + LoadModel(model) + GiveWeaponComponentToWeaponObject(entity, data.component) + SetModelAsNoLongerNeeded(data.component) + else + SetWeaponObjectTintIndex(entity, data.component) + end + end + elseif config.core_inventory then + for _, data in pairs(attachments) do + local model = GetWeaponComponentTypeModel(data.componentHash) + if model ~= 0 then + LoadModel(model) + GiveWeaponComponentToWeaponObject(entity, data.componentHash) + SetModelAsNoLongerNeeded(data.componentHash) + else + SetWeaponObjectTintIndex(entity, data.componentHash) + end + end + elseif config.ox_inventory then + if not oxItems then oxItems = exports.ox_inventory:Items() end + for i = 1, #attachments do + local components = oxItems[attachments[i]].client.component + for v = 1, #components do + local component = components[v] + if DoesWeaponTakeWeaponComponent(weaponHash, component) then + local model = GetWeaponComponentTypeModel(component) + if model ~= 0 then + LoadModel(model) + GiveWeaponComponentToWeaponObject(entity, component) + SetModelAsNoLongerNeeded(component) + end + end + end + end + elseif config.framework == "qb" then + for _, data in pairs(attachments) do + local model = GetWeaponComponentTypeModel(data.component) + if model ~= 0 then + LoadModel(model) + GiveWeaponComponentToWeaponObject(entity, data.component) + SetModelAsNoLongerNeeded(data.component) + else + SetWeaponObjectTintIndex(entity, data.component) + end + end + else + --ESX is suck + for i = 1, #attachments do + local componentData = tgiCore.core.GetWeaponComponent(weaponName, attachments[i]) + if componentData then + local hash = componentData.hash + local model = GetWeaponComponentTypeModel(hash) + if model ~= 0 then + LoadModel(model) + GiveWeaponComponentToWeaponObject(entity, hash) + SetModelAsNoLongerNeeded(hash) + else + SetWeaponObjectTintIndex(entity, hash) + end + end + end + end +end + +function getTgiannAttachments(tgiattachments, weapon) + local invSettings = exports["tgiann-attachment"]:inventoryConfig() + if invSettings then + return tgiattachments + else + local returnVal = nil + local waitCb = true + tgiCore.cbFunction("tgiann-attachment:getAttachment", function(data) + returnVal = data + waitCb = false + end, weapon) + while waitCb do Wait(10) end + return returnVal + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/mf_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/mf_inv.lua new file mode 100644 index 000000000..c8bcde8df --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/mf_inv.lua @@ -0,0 +1,77 @@ +if not config["mf-inventory"] then return end + +local playerJob = "" +local lastItems = {} + +local function getInventoryItems(identifier) + exports["mf-inventory"]:getInventoryItems(identifier, function(items) + lastItems = items + weaponCheck() + end) +end + +RegisterNetEvent('esx:playerLoaded') +AddEventHandler('esx:playerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + getInventoryItems(PlayerData.identifier) +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('esx:addInventoryItem') +AddEventHandler('esx:addInventoryItem', function(job) + local PlayerData = exports["tgiann-core"]:getPlayerData() + if not PlayerData then return end + getInventoryItems(PlayerData.identifier) +end) + +RegisterNetEvent('esx:removeInventoryItem') +AddEventHandler('esx:removeInventoryItem', function(job) + local PlayerData = exports["tgiann-core"]:getPlayerData() + if not PlayerData then return end + getInventoryItems(PlayerData.identifier) +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if item and key == item.name then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + if not lastItems then return end + Wait(100) + self.Functions.CheckWeaponIsRemoved(lastItems) + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and string.find(string.lower(item.name), "weapon") then + self.Functions.AddWeapon({ + weapon = item.name, + key = item.name, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.metadata.tgiattachments, joaat(item.name)), + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/origen_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/origen_inv.lua new file mode 100644 index 000000000..aee6b831a --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/origen_inv.lua @@ -0,0 +1,79 @@ +if not config.origen_inventory then return end + +local origen_inventory = exports.origen_inventory +local playerJob = "" +local lastItems = {} + +local function getInventoryItems() + lastItems = origen_inventory:GetInventory() + weaponCheck() +end + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + getInventoryItems() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +--QB +RegisterNetEvent('QBCore:Player:SetPlayerData', function(PlayerData) + playerJob = PlayerData.job.name + getInventoryItems() +end) + +--ESX +RegisterNetEvent('esx:addInventoryItem') +AddEventHandler('esx:addInventoryItem', function() + getInventoryItems() +end) + +RegisterNetEvent('esx:removeInventoryItem') +AddEventHandler('esx:removeInventoryItem', function() + getInventoryItems() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if key == item.info.serie then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + if not lastItems then return end + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and item.type == "weapon" then + self.Functions.AddWeapon({ + weapon = item.name, + key = item.info.serie, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.info.tgiattachments, joaat(item.name)) or item.info.attachments, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/ox_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/ox_inv.lua new file mode 100644 index 000000000..84715b1e3 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/ox_inv.lua @@ -0,0 +1,64 @@ +if not config.ox_inventory then return end + +local playerJob = "" +local lastItems = {} + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + lastItems = exports.ox_inventory:GetPlayerItems() or {} + weaponCheck() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +AddEventHandler('ox_inventory:updateInventory', function(changes) + for i, value in pairs(changes) do + lastItems[i] = value or nil + end + weaponCheck() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if item and key == (item.metadata.serial or item.name) then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + if not lastItems then return end + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and string.find(string.lower(item.name), "weapon") then + self.Functions.AddWeapon({ + weapon = item.name, + key = item.metadata.serial or item.name, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.metadata.tgiattachments, joaat(item.name)) or item.metadata.components, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qb_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qb_inv.lua new file mode 100644 index 000000000..7ee834347 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qb_inv.lua @@ -0,0 +1,64 @@ +if config.framework ~= "qb" then return end +if not config.useDefaultInventory then return end + +local playerJob = "" +local lastItems = {} + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + lastItems = PlayerData?.items + weaponCheck() +end) + +RegisterNetEvent('QBCore:Player:SetPlayerData', function(PlayerData) + playerJob = PlayerData.job.name + lastItems = PlayerData?.items + weaponCheck() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if key == item.info.serie then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + if not lastItems then return end + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and item.type == "weapon" then + self.Functions.AddWeapon({ + weapon = item.name, + key = item.info.serie, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.info.tgiattachments, joaat(item.name)) or item.info.attachments, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qs_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qs_inv.lua new file mode 100644 index 000000000..387313a79 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/qs_inv.lua @@ -0,0 +1,89 @@ +if not config["qs-inventory"] then return end + +local playerJob = "" +local lastItems = {} +local isBussy = false + +local function getInventoryItems() + if isBussy then return end + isBussy = true + while exports['qs-inventory']:inInventory() do Wait(10) end -- getUserInventory not updating when inventory is open + Wait(1000) -- getUserInventory is updating late + lastItems = exports['qs-inventory']:getUserInventory() + weaponCheck(playerJob, true) + isBussy = false +end + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + getInventoryItems() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +--ESX +RegisterNetEvent('esx:addInventoryItem') +AddEventHandler('esx:addInventoryItem', function() + getInventoryItems() +end) + +RegisterNetEvent('esx:removeInventoryItem') +AddEventHandler('esx:removeInventoryItem', function() + getInventoryItems() +end) + +--QB +RegisterNetEvent('QBCore:Player:SetPlayerData', function(PlayerData) + playerJob = PlayerData.job.name + lastItems = PlayerData?.items + weaponCheck() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck(playerJob) +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if item and key == (item.info?.serie or item.name) then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item then + local isWeapon = string.find(string.lower(item.name), "weapon") + debug(item.name .. " Check For Add Weapon", "isWeapon: " .. tostring(isWeapon)) + if isWeapon then + debug(item.name .. " Added") + self.Functions.AddWeapon({ + weapon = item.name, + key = item.info?.serie or item.name, + attachments = config.tgiann_attachments and + getTgiannAttachments(item.info.tgiattachments, joaat(item.name)) or item.info?.attachments, + playerJob = playerJob, + isMale = isMale + }) + end + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/tgiann_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/tgiann_inv.lua new file mode 100644 index 000000000..71e036f7f --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/bridge/tgiann_inv.lua @@ -0,0 +1,62 @@ +if not config["tgiann-inventory"] then return end + +local playerJob = "" +local lastItems = {} + +RegisterNetEvent('tgiCore:Client:OnPlayerLogout', function() + self.Functions.RemoveAllWeapons() +end) + +RegisterNetEvent('tgiCore:Client:OnPlayerLoaded') +AddEventHandler('tgiCore:Client:OnPlayerLoaded', function(PlayerData) + playerJob = PlayerData.job.name + lastItems = exports["tgiann-inventory"]:GetPlayerItems() + weaponCheck() +end) + +RegisterNetEvent('tgiann-inventory:inventoryUpdated') +AddEventHandler('tgiann-inventory:inventoryUpdated', function(items) + lastItems = items + weaponCheck() +end) + +RegisterNetEvent('tgiCore:Client:OnJobUpdate') +AddEventHandler('tgiCore:Client:OnJobUpdate', function(job) + self.Functions.RemoveAllWeapons() + playerJob = job.name + weaponCheck() +end) + +self.Functions.CheckWeaponIsRemoved = function() + if not next(self.weapons) then return end + for key, _ in pairs(self.weapons) do + local success = false + for _, item in pairs(lastItems) do + if key == item?.info?.serie then + success = true + break + end + end + if not success then + self.Functions.RemoveWeapon(key) + end + end +end + +function weaponCheck() + if not lastItems then return end + Wait(100) + self.Functions.CheckWeaponIsRemoved() + local isMale = GetEntityModel(PlayerPedId()) == `mp_m_freemode_01` + for _, item in pairs(lastItems) do + if item and item.type == "weapon" then + self.Functions.AddWeapon({ + weapon = string.gsub(item.name, "_police", ""), + key = item?.info?.serie or item.name, + attachments = item?.info?.tgiattachments or {}, + playerJob = playerJob, + isMale = isMale + }) + end + end +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/client.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/client.lua new file mode 100644 index 0000000000000000000000000000000000000000..b45246355dd59bea1537a1850de4ed4b69adec11 GIT binary patch literal 6569 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 bqk#c^1^A++u_L_%ykAe-CU_tzlr@crGiJtU literal 0 HcmV?d00001 diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/editable.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/editable.lua new file mode 100644 index 000000000..a5f9866b3 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/client/editable.lua @@ -0,0 +1,12 @@ +function canShow(targetPed) + local isEntityVisible = IsEntityVisible(targetPed) + local getEntityAlpha = GetEntityAlpha(targetPed) + local pedIsVisible = isEntityVisible and getEntityAlpha == 255 + debug("pedIsVisible: " .. tostring(pedIsVisible), "ped:" .. targetPed, "isEntityVisible: " .. tostring(isEntityVisible), "getEntityAlpha: " .. getEntityAlpha) + return pedIsVisible +end + +function debug(...) + if not config.debug then return end + print(...) +end diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/config.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/config.lua new file mode 100644 index 000000000..db92c0b04 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/config.lua @@ -0,0 +1,206 @@ +--[[ + - this script needs tgiann-core script to work, you can download the script from your keymaster account + Start tgiann-core script after es_extented/qb-core script and before tgiann-* scripts + Adjust the tgiann-core config file according to the framework you are using + + https://tgiann.gitbook.io/tgiann/scripts/tgiann-weapons-on-back + + Note: If you want the weapons to be invisible while in Noclip, the character must be completely invisible. If your character is invisible to other players, other players cannot see these weapons. + If you want to edit this, take a look at client/editable.lua +]] + +tgiCoreExports = exports["tgiann-core"] +config = tgiCoreExports:getConfig() +config.debug = false + +config.tgiann_attachments = GetResourceState("tgiann-attachment") ~= "missing" -- https://tgiann.tebex.io/package/5399235 + +-- Weapon positions for male and female characters. You can add additional positions here if you like. +config.positions = { + male = { + back = { -- We have set 3 positions for the back. You can add or remove extra positions if you like. This setting also applies to other positions. + { -- position 1 + bone = 24816, + offset = vector3(0.285, -0.17, 0.13), + rot = vector3(0.0, 170.0, 0.0), + }, + { -- position 2 + bone = 24816, + offset = vector3(0.285, -0.17, 0.0), + rot = vector3(0.0, 170.0, 0.0), + }, + { -- position 3 + bone = 24816, + offset = vector3(0.285, -0.17, -0.13), + rot = vector3(0.0, 170.0, 0.0), + } + }, + front = { + { + bone = 24818, + offset = vector3(-0.03, 0.19, 0.0), + rot = vector3(-10.0, 40.0, 5.0), + } + }, + right = { + { + bone = 11816, + offset = vector3(-0.01, 0.02, 0.215), + rot = vector3(-100.0, 60.0, 45.0), + } + }, + rLegBack = { + { + bone = 11816, + offset = vector3(-0.15, -0.11, 0.22), + rot = vector3(0.0, 95.0, 180.0), + } + }, + waist = { + { + bone = 11816, + offset = vector3(-0.07, -0.13, 0.05), + rot = vector3(180.0, -30.0, 10.0), + } + }, + -- You can add extra positions here if you like. + -- The positions you add can be used in the `config.weaponPositions`, `config.weaponGroupPositions` and `config.weaponGroupJobPositions` settings. + --[[ exampleCustomName = { + { + bone = 11816, + offset = vector3(-0.07, -0.13, 0.05), + rot = vector3(180.0, -30.0, 10.0), + } + }, ]] + }, + female = { + back = { + { + bone = 24816, + offset = vector3(0.285, -0.15, 0.13), + rot = vector3(0.0, 170.0, 0.0), + }, + { + bone = 24816, + offset = vector3(0.285, -0.15, 0.0), + rot = vector3(0.0, 170.0, 0.0), + }, + { + bone = 24816, + offset = vector3(0.285, -0.15, -0.13), + rot = vector3(0.0, 170.0, 0.0), + } + }, + front = { + { + bone = 24818, + offset = vector3(-0.03, 0.21, 0.0), + rot = vector3(-10.0, 40.0, 5.0), + } + }, + right = { + { + bone = 11816, + offset = vector3(-0.09, 0.03, 0.18), + rot = vector3(-105.0, 75.0, 45.0), + } + }, + rLegBack = { + { + bone = 11816, + offset = vector3(-0.15, -0.11, 0.22), + rot = vector3(0.0, 95.0, 180.0), + } + }, + waist = { + { + bone = 11816, + offset = vector3(-0.07, -0.09, 0.05), + rot = vector3(180.0, -30.0, 10.0), + } + } + } + +} + +-- Weapons in the list do not appear on the character +config.disabledWeapons = { + weapon_flashlight = true, + weapon_knuckle = true, + weapon_bottle = true, + weapon_snowball = true, +} + +-- adjusts the location of the weapon regardless of its group +config.weaponPositions = { + --weapon_pistol = "right", +} + +-- adjusts the position of the weapon regardless of its group +config.weaponCustomPositions = { + male = { + weapon_bat = { + bone = 24816, + offset = vector3(0.0, -0.15, 0.03), + rot = vector3(0.0, 80.0, 0.0), + } + }, + female = { + weapon_bat = { + bone = 24816, + offset = vector3(0.0, -0.15, 0.03), + rot = vector3(0.0, 80.0, 0.0), + } + } +} + +--"waist" - "back" - "front" - "rigt" - "rLegBack" - "none" +config.weaponGroupPostions = { + [3539449195] = "back", --GROUP_DIGISCANNER + [-37788308] = "rLegBack", --GROUP_FIREEXTINGUISHER + [1175761940] = "none", --GROUP_HACKINGDEVICE + [2725924767] = "back", --GROUP_HEAVY + [-728555052] = "back", --GROUP_MELEE + [3759491383] = "none", --GROUP_METALDETECTOR + [1159398588] = "back", --GROUP_MG + [3493187224] = "none", --GROUP_NIGHTVISION + [431593103] = "none", --GROUP_PARACHUTE + [1595662460] = "none", --GROUP_PETROLCAN + [416676503] = "waist", --GROUP_PISTOL + [970310034] = "back", --GROUP_RIFLE + [860033945] = "back", --GROUP_SHOTGUN + [-957766203] = "front", --GROUP_SMG + [-1212426201] = "back", --GROUP_SNIPER + [690389602] = "none", --GROUP_STUNGUN + [1548507267] = "none", --GROUP_THROWN + [75159441] = "back", --GROUP_TRANQILIZER + [2685387236] = "none", --GROUP_UNARMED +} + +-- weapon locations for jobs +config.weaponGroupJobPostions = { + { + jobs = { "police" }, -- u can add multible job name + postions = { + [3539449195] = "back", --GROUP_DIGISCANNER + [-37788308] = "rLegBack", --GROUP_FIREEXTINGUISHER + [1175761940] = "none", --GROUP_HACKINGDEVICE + [2725924767] = "back", --GROUP_HEAVY + [-728555052] = "back", --GROUP_MELEE + [3759491383] = "none", --GROUP_METALDETECTOR + [1159398588] = "back", --GROUP_MG + [3493187224] = "none", --GROUP_NIGHTVISION + [431593103] = "none", --GROUP_PARACHUTE + [1595662460] = "none", --GROUP_PETROLCAN + [416676503] = "right", --GROUP_PISTOL + [970310034] = "back", --GROUP_RIFLE + [860033945] = "back", --GROUP_SHOTGUN + [-957766203] = "front", --GROUP_SMG + [-1212426201] = "back", --GROUP_SNIPER + [690389602] = "none", --GROUP_STUNGUN + [1548507267] = "none", --GROUP_THROWN + [75159441] = "back", --GROUP_TRANQILIZER + [2685387236] = "none", --GROUP_UNARMED + } + } +} diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/core_inv_config.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/core_inv_config.lua new file mode 100644 index 000000000..be3ce7e4f --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/configs/core_inv_config.lua @@ -0,0 +1,5 @@ +config.enableInv = { + ["content"] = true, + ["primary"] = true, + ["secondry"] = true, +} \ No newline at end of file diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/fxmanifest.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/fxmanifest.lua new file mode 100644 index 000000000..c59b2fef0 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/fxmanifest.lua @@ -0,0 +1,34 @@ +fx_version 'cerulean' +game 'gta5' +version '1.0.1' + +lua54 'yes' + +dependencies { + 'tgiann-core', +} + +escrow_ignore { + 'configs/*.lua', + 'checkInv.lua', + 'client/bridge/*.lua', + 'client/editable.lua', + 'server/bridge/*.lua' +} + +shared_script { + 'configs/*.lua', + 'checkInv.lua', +} + +client_scripts { + 'client/*.lua', + 'client/bridge/*.lua' +} + +server_scripts { + "server/server.lua", + 'server/bridge/*.lua', +} + +dependency '/assetpacks' \ No newline at end of file diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/core_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/core_inv.lua new file mode 100644 index 000000000..b330e546d --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/core_inv.lua @@ -0,0 +1,13 @@ +if not config.core_inventory then return end + +tgiCore.cbFunction('tgiann-weapons-on-back:core_inventory:server:getInventory', function(source, cb) + local src = source + local xPlayer = tgiCore.getPlayer(src) + if not xPlayer then return end + local citizenid = tgiCore.getCid(xPlayer) + local playerItems = {} + for inv, canAdd in pairs(config.enableInv) do + if canAdd then playerItems[inv] = exports['core_inventory']:getInventory(string.format("%s-%s", inv, citizenid)) end + end + cb(playerItems) +end) diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/esx_inv.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/esx_inv.lua new file mode 100644 index 000000000..a59396790 --- /dev/null +++ b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/bridge/esx_inv.lua @@ -0,0 +1,10 @@ +if config.framework ~= "esx" then return end +if not config.useDefaultInventory then return end + +-- esx is very bad, I can't access current inventory data from client +tgiCore.cbFunction('tgiann-weapons-on-back:esx_inv:server:getInventory', function(source, cb) + local src = source + local xPlayer = tgiCore.getPlayer(src) + if not xPlayer then return end + cb(xPlayer.getInventory(), xPlayer.getLoadout()) +end) \ No newline at end of file diff --git a/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/server.lua b/resources/[Developer]/[Nordi]/tgiann-weapons-on-back/server/server.lua new file mode 100644 index 0000000000000000000000000000000000000000..794191b64bc92968410b62866720d8454831d3d1 GIT binary patch literal 1954 zcmV;T2VM9^SV2$$000000NBIjGT#ig0>Cpfk|w_=6SOuW_c7GZM1b+b+%|xXcP4rk z04pvgD`Q=2avJx>lK+@^mYWWb&@%B}c=)j?}%LwdDz;FL6mqBiO>A7UxT?jT;(~< z2xwA}b9(K8o$-iRTv=S_eGHAD?njTKNUnbezh)EGVzG5yvPcBUAi z>@2fVu!%w8Puqp+<)ltj5#*J8k^QTdranGY zU&hm^^81?(eBPaHf3NW3WJM z9Q=aVT=x)veB)&9WzJL}`;%U{afgM9AsX2k6+cW}Xt{dsq9y9d%*D9VyZ5yNbCFpI6gwRj#g`^$2%0wa;L$CHQ2tx1jJ=0 zj1w9x-L)Ff1H;Eg4&8~toPcXTuZ77DPtn2TQLyNWw$#emU+M;#Kllwf zv1f}+9eUpvlg3?Jz~_TZ1z#tre0_Y}?x^%_6*2%*-9&B`vR!;9uz@OP;zhWndSDAW zGtcgHllIUb}U*C45>cew`f-5(Q6WIYfL+z2X`$nF3i+gk0j*2*ZE_^PL22ZPT; z(jFE>l=rR0GyLFCRz&T6B|@{#x8?C(RbFk(r!Joj#%4iv`9!6-Y*Q75c82~tja_;h z4Tw=`wDNDuD#V?a0`s>CeHo) zL**giR82Vil}L@uqMZ!q5S`$~DJnGiK58Fj;9WO>VT#Abqm;Py#B!lzv}ihSym}UY z@=j%rLv=P;3BVZb2^dasW4o|Zw_SfEC(v97N`agy+5^h-bo0vudY{5)+ zl?;LjyW8^ZjFi2Qu{@5LM1`!{@1uMm!li?ivql5nwZ!H!#;U;U<;#<$kK*~mLSRwe z>0jgHu`VgM#?jfzxIjy~fDW7+({$liQvPhYLr#EZOk-ta>^9&l3*~@K_D>Nr8x>pI zkUeZ>kfx1;r1e^2owhQbpZ!GSLVei5?)MBXh-Xr&=E6BYZ6FN;iDkD%DL2Zt(s z4(wX@YLjx3N&7}7s3Tcc-LA;k?hJBGuDQ}`Se2kF^#9(4ias)KS@6soNS0J36u=Bf z|0eJxzZi{3R7>!<^WDyI9b5UVANJw*O@NrDyMJH(NVtY$(xb!51o-#8Kot(N)Ed6< zyQRXUp%h3wqUnUFd?RBhF+1Kk7uf(83O~J3gyBoYu%*rwz4>??N2am?0E{S7hMI&` ozNd1ECKrA5UFXsSDZyM5C>#KQ@S8pYKDUX?*C7I#WM>*8xjGfi4gdfE literal 0 HcmV?d00001 diff --git a/resources/[standalone]/nordi_tdm/client.lua b/resources/[standalone]/nordi_tdm/client.lua index 60719f10b..b58076c1d 100644 --- a/resources/[standalone]/nordi_tdm/client.lua +++ b/resources/[standalone]/nordi_tdm/client.lua @@ -25,19 +25,6 @@ local function debugPrint(message) print("^2[TDM DEBUG]^7 " .. message) end --- Funktion zum Prüfen, ob eine Waffe eine Airsoft-Waffe ist -function isAirsoftWeapon(weaponHash) - return Config.airsoftWeapons[weaponHash] or Config.treatAllWeaponsAsAirsoft -end - --- Enhanced function to check if a player is in ragdoll state -function isPedInRagdoll(ped) - return IsPedRagdoll(ped) or - IsPedFalling(ped) or - IsPedDiving(ped) or - (not IsPedStill(ped) and IsPedOnFoot(ped) and not IsPedWalking(ped) and not IsPedRunning(ped) and not IsPedSprinting(ped)) -end - -- Helper function to dump tables for debugging function dumpTable(table, indent) if not indent then indent = 0 end @@ -52,6 +39,19 @@ function dumpTable(table, indent) end end +-- Funktion zum Prüfen, ob eine Waffe eine Airsoft-Waffe ist +function isAirsoftWeapon(weaponHash) + return Config.airsoftWeapons[weaponHash] or Config.treatAllWeaponsAsAirsoft +end + +-- Enhanced function to check if a player is in ragdoll state +function isPedInRagdoll(ped) + return IsPedRagdoll(ped) or + IsPedFalling(ped) or + IsPedDiving(ped) or + (not IsPedStill(ped) and IsPedOnFoot(ped) and not IsPedWalking(ped) and not IsPedRunning(ped) and not IsPedSprinting(ped)) +end + -- Events RegisterNetEvent('tdm:updateGamesList', function(games) activeGames = games @@ -791,570 +791,189 @@ CreateThread(function() if inTDM and not isHit then local ped = PlayerPedId() + -- Prüfe, ob der Spieler im Ragdoll-Zustand ist + if -- Prüfe, ob der Spieler im Ragdoll-Zustand ist if isPedInRagdoll(ped) then -- Bestimme den Angreifer (verwende den letzten Angreifer, wenn innerhalb von 3 Sekunden) local attacker = nil - if lastDamager and (GetGameTimer() - -local QBCore = exports['qb-core']:GetCoreObject() - --- Game Management -local activeGames = {} -local gameIdCounter = 1 - --- Debug-Funktion für Konsole -local function debugPrint(message) - print("^2[TDM SERVER]^7 " .. message) -end - --- Events -RegisterNetEvent('tdm:createGame', function(gameName, fieldId, gameType, password) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player then - debugPrint("Spielerstellung fehlgeschlagen - Spieler nicht gefunden: " .. src) - return - end - - -- Prüfen ob Spielfeld bereits belegt - for gameId, gameData in pairs(activeGames) do - if gameData.fieldId == fieldId then - debugPrint("Spielerstellung abgelehnt - Feld bereits belegt: " .. fieldId) - TriggerClientEvent('QBCore:Notify', src, 'Dieses Spielfeld ist bereits belegt!', 'error') - return - end - end - - local gameId = 'game_' .. gameIdCounter - gameIdCounter = gameIdCounter + 1 - - activeGames[gameId] = { - id = gameId, - name = gameName, - fieldId = fieldId, - admin = src, - adminName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname, - gameType = gameType, - password = password, - hasPassword = password ~= nil, - status = 'waiting', - team1 = {}, - team2 = {}, - score = {team1 = 0, team2 = 0}, - startTime = nil, - maxTime = Config.maxGameTime, - maxHits = Config.maxHits, - playerStats = {} -- Spieler-Statistiken initialisieren - } - - local typeText = gameType == 'public' and 'öffentliches' or 'privates' - TriggerClientEvent('QBCore:Notify', src, 'Dein ' .. typeText .. ' Spiel "' .. gameName .. '" wurde erstellt!', 'success') - - updateGamesListForAll() - debugPrint("Spiel erstellt: " .. gameId .. " von " .. Player.PlayerData.name .. " (Feld: " .. fieldId .. ")") -end) - -RegisterNetEvent('tdm:requestGamesList', function() - local src = source - debugPrint("Spiele-Liste angefordert von: " .. src) - TriggerClientEvent('tdm:updateGamesList', src, activeGames) -end) - -RegisterNetEvent('tdm:requestJoinGame', function(gameId, password) - local src = source - local Player = QBCore.Functions.GetPlayer(src) - - if not Player or not activeGames[gameId] then - debugPrint("Spielbeitritt fehlgeschlagen - Spieler oder Spiel nicht gefunden: " .. src .. ", " .. gameId) - return - end - - local game = activeGames[gameId] - local playerName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname - - debugPrint("Beitrittsanfrage von " .. playerName .. " (ID: " .. src .. ") für Spiel " .. gameId) - - -- Passwort prüfen falls vorhanden - if game.hasPassword and game.password ~= password then - debugPrint("Beitritt abgelehnt - Falsches Passwort") - TriggerClientEvent('QBCore:Notify', src, 'Falsches Passwort!', 'error') - return - end - - -- Spieler bereits im Spiel? - for _, playerId in ipairs(game.team1) do - if playerId == src then - debugPrint("Beitritt abgelehnt - Spieler bereits in Team 1") - TriggerClientEvent('QBCore:Notify', src, 'Du bist bereits in diesem Spiel!', 'error') - return - end - end - for _, playerId in ipairs(game.team2) do - if playerId == src then - debugPrint("Beitritt abgelehnt - Spieler bereits in Team 2") - TriggerClientEvent('QBCore:Notify', src, 'Du bist bereits in diesem Spiel!', 'error') - return - end - end - - -- Max Spieler erreicht? - local currentPlayers = #game.team1 + #game.team2 - local maxPlayers = Config.gameFields[game.fieldId].maxPlayers - - if currentPlayers >= maxPlayers then - debugPrint("Beitritt abgelehnt - Spiel ist voll") - TriggerClientEvent('QBCore:Notify', src, 'Spiel ist voll!', 'error') - return - end - - -- Prüfen ob Admin online ist (für private Spiele) - if game.gameType == 'private' then - local AdminPlayer = QBCore.Functions.GetPlayer(game.admin) - if not AdminPlayer then - debugPrint("Beitritt abgelehnt - Admin nicht online") - TriggerClientEvent('QBCore:Notify', src, 'Der Spiel-Admin ist nicht online!', 'error') - return - end - end - - -- Join Logic basierend auf Spiel Typ - if game.gameType == 'public' then - debugPrint("Öffentliches Spiel - Direkter Beitritt") - joinPlayerToGame(src, gameId) - TriggerClientEvent('QBCore:Notify', src, 'Du bist dem öffentlichen Spiel beigetreten!', 'success') - else - debugPrint("Privates Spiel - Sende Anfrage an Admin") - TriggerClientEvent('tdm:joinRequest', game.admin, gameId, playerName, src) - TriggerClientEvent('QBCore:Notify', src, 'Join-Anfrage gesendet an ' .. game.adminName, 'info') - end -end) - -RegisterNetEvent('tdm:approveJoinRequest', function(gameId, playerId, approved) - local src = source - local game = activeGames[gameId] - - if not game or game.admin ~= src then - debugPrint("Join-Anfrage Bearbeitung fehlgeschlagen - Ungültiges Spiel oder nicht Admin") - return - end - - if approved then - debugPrint("Join-Anfrage genehmigt für Spieler " .. playerId .. " in Spiel " .. gameId) - joinPlayerToGame(playerId, gameId) - TriggerClientEvent('tdm:joinRequestResult', playerId, true, game.name) - else - debugPrint("Join-Anfrage abgelehnt für Spieler " .. playerId .. " in Spiel " .. gameId) - TriggerClientEvent('tdm:joinRequestResult', playerId, false, game.name) - end -end) - -RegisterNetEvent('tdm:leaveGame', function() - local src = source - debugPrint("Spieler " .. src .. " möchte alle Spiele verlassen") - - for gameId, game in pairs(activeGames) do - removePlayerFromGame(src, gameId) - end - - TriggerClientEvent('tdm:leaveGame', src) -end) - -RegisterNetEvent('tdm:playerWasHit', function(gameId, victimTeam, attackerId) - local victim = source - - if not activeGames[gameId] then - debugPrint("Hit registriert, aber Spiel " .. gameId .. " existiert nicht!") - return - end - - local game = activeGames[gameId] - - debugPrint("Hit registriert - Opfer: " .. victim .. " (Team: " .. victimTeam .. "), Angreifer: " .. (attackerId or "Unbekannt")) - - -- Spieler Stats initialisieren falls nicht vorhanden - if not game.playerStats then - game.playerStats = {} - end - - if not game.playerStats[victim] then - game.playerStats[victim] = {hits = 0, deaths = 0} - end - - -- Wichtig: Nur wenn ein Angreifer identifiziert wurde, zähle den Kill - if attackerId then - if not game.playerStats[attackerId] then - game.playerStats[attackerId] = {hits = 0, deaths = 0} - end - - -- Stats updaten - game.playerStats[victim].deaths = (game.playerStats[victim].deaths or 0) + 1 - game.playerStats[attackerId].hits = (game.playerStats[attackerId].hits or 0) + 1 - - -- Benachrichtigung an den Angreifer senden - TriggerClientEvent('tdm:hitRegistered', attackerId) - debugPrint("Treffer von " .. attackerId .. " gegen " .. victim .. " registriert") - - -- Team Score erhöhen - if victimTeam == 'team1' then - game.score.team2 = game.score.team2 + 1 - debugPrint("Punkt für Team 2 - Neuer Score: " .. game.score.team2) + if lastDamager and (GetGameTimer() - lastDamageTime) < 3000 then + attacker = lastDamager + + -- Prüfe ob die Waffe eine Airsoft-Waffe ist + if isAirsoftWeapon(lastDamageWeapon) then + debugPrint("Ragdoll durch Airsoft-Waffe - Zählt als Tod durch: " .. attacker) + + -- Lokale Stats sofort updaten + playerStats.deaths = playerStats.deaths + 1 + + -- Treffer-Events auslösen + TriggerEvent('tdm:playerHit') + TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, attacker) + + -- Zurücksetzen des letzten Angreifers + lastDamager = nil + lastDamageWeapon = 0 + + -- Warten um mehrfache Auslösung zu verhindern + Wait(1000) + end + end + end else - game.score.team1 = game.score.team1 + 1 - debugPrint("Punkt für Team 1 - Neuer Score: " .. game.score.team1) - end - else - debugPrint("Treffer gegen " .. victim .. " von unbekanntem Angreifer registriert - Kein Punkt vergeben") - end - - TriggerClientEvent('tdm:deathRegistered', victim) - - -- Score an alle Spieler senden - updateScoreForGame(gameId) - - -- Spiel beenden prüfen - if game.score.team1 >= game.maxHits or game.score.team2 >= game.maxHits then - local winnerTeam = game.score.team1 >= game.maxHits and 'team1' or 'team2' - debugPrint("Max Punkte erreicht - Beende Spiel. Gewinner: " .. winnerTeam) - endGame(gameId, winnerTeam) - end -end) - -RegisterNetEvent('tdm:playerDied', function(gameId) - local src = source - debugPrint("Spieler " .. src .. " ist gestorben in Spiel " .. gameId) - removePlayerFromGame(src, gameId) -end) - -RegisterNetEvent('tdm:requestScoreUpdate', function(gameId) - local src = source - - if activeGames[gameId] then - debugPrint("Score-Update angefordert von " .. src .. " für Spiel " .. gameId) - updateScoreForGame(gameId) - else - debugPrint("Score-Update fehlgeschlagen - Spiel " .. gameId .. " nicht gefunden") - end -end) - -RegisterNetEvent('tdm:debugPlayerStats', function(gameId) - local src = source - if activeGames[gameId] and activeGames[gameId].playerStats and activeGames[gameId].playerStats[src] then - local stats = activeGames[gameId].playerStats[src] - debugPrint("Stats für Spieler " .. src .. ": Hits=" .. (stats.hits or 0) .. ", Deaths=" .. (stats.deaths or 0)) - TriggerClientEvent('QBCore:Notify', src, 'Server Stats - Hits: ' .. (stats.hits or 0) .. ', Deaths: ' .. (stats.deaths or 0), 'info') - else - debugPrint("Keine Stats gefunden für Spieler " .. src .. " in Spiel " .. gameId) - TriggerClientEvent('QBCore:Notify', src, 'Keine Stats gefunden!', 'error') - end -end) - --- Funktionen -function joinPlayerToGame(playerId, gameId) - local game = activeGames[gameId] - if not game then - debugPrint("Spielbeitritt fehlgeschlagen - Spiel nicht gefunden: " .. gameId) - return - end - - -- Team mit weniger Spielern wählen - local team = #game.team1 <= #game.team2 and 'team1' or 'team2' - - table.insert(game[team], playerId) - - -- Spieler-Stats initialisieren - if not game.playerStats then - game.playerStats = {} - end - - game.playerStats[playerId] = {hits = 0, deaths = 0} - - -- Spiel starten wenn mindestens 2 Spieler - if #game.team1 + #game.team2 >= 2 and game.status == 'waiting' then - game.status = 'active' - game.startTime = os.time() - debugPrint("Spiel " .. gameId .. " gestartet - Mindestens 2 Spieler erreicht") - - -- Game Timer starten - startGameTimer(gameId) - end - - TriggerClientEvent('tdm:joinGame', playerId, gameId, team, game.fieldId) - updateScoreForGame(gameId) - updateGamesListForAll() - - debugPrint("Spieler " .. playerId .. " ist Spiel " .. gameId .. " beigetreten (Team: " .. team .. ")") -end - -function removePlayerFromGame(playerId, gameId) - local game = activeGames[gameId] - if not game then return end - - -- Spieler aus Teams entfernen - local removed = false - - for i, id in ipairs(game.team1) do - if id == playerId then - table.remove(game.team1, i) - debugPrint("Spieler " .. playerId .. " aus Team 1 entfernt") - removed = true - break + Wait(500) end end - - if not removed then - for i, id in ipairs(game.team2) do - if id == playerId then - table.remove(game.team2, i) - debugPrint("Spieler " .. playerId .. " aus Team 2 entfernt") - removed = true - break - end - end - end - - if not removed then - debugPrint("Spieler " .. playerId .. " nicht in Spiel " .. gameId .. " gefunden") - return - end - - -- Wenn Admin das Spiel verlässt, Spiel beenden - if game.admin == playerId then - debugPrint("Admin hat das Spiel verlassen - Beende Spiel " .. gameId) - endGame(gameId, nil, 'Admin hat das Spiel verlassen') - return - end - - checkGameEnd(gameId) - updateGamesListForAll() -end +end) -function endGame(gameId, winnerTeam, reason) - local game = activeGames[gameId] - if not game then - debugPrint("Spielende fehlgeschlagen - Spiel nicht gefunden: " .. gameId) - return - end - - game.status = 'finished' - - local allPlayers = {} - for _, playerId in ipairs(game.team1) do - table.insert(allPlayers, playerId) - end - for _, playerId in ipairs(game.team2) do - table.insert(allPlayers, playerId) - end - - -- Game End Event an alle Spieler - for _, playerId in ipairs(allPlayers) do - debugPrint("Sende Spielende-Event an Spieler " .. playerId) - TriggerClientEvent('tdm:gameEnded', playerId, winnerTeam, game.score.team1, game.score.team2) - end - - -- Spiel nach 10 Sekunden löschen - SetTimeout(10000, function() - activeGames[gameId] = nil - updateGamesListForAll() - debugPrint("Spiel " .. gameId .. " aus der Liste entfernt") - end) - - if reason then - debugPrint("Spiel " .. gameId .. " beendet: " .. reason) - else - debugPrint("Spiel " .. gameId .. " beendet. Gewinner: " .. (winnerTeam or "Unentschieden")) - end -end - -function startGameTimer(gameId) - CreateThread(function() - local game = activeGames[gameId] - if not game then return end - - local maxTime = game.maxTime - local startTime = os.time() - - debugPrint("Timer für Spiel " .. gameId .. " gestartet. Maximale Zeit: " .. maxTime .. " Sekunden") - - while game and game.status == 'active' and (os.time() - startTime) < maxTime do - Wait(1000) - game = activeGames[gameId] -- Refresh game data +-- Direkter Waffen-Schaden Monitor für zusätzliche Zuverlässigkeit +CreateThread(function() + while true do + Wait(0) + if inTDM and not isHit then + local ped = PlayerPedId() - -- Alle 30 Sekunden Debug-Info - if (os.time() - startTime) % 30 == 0 then - debugPrint("Spiel " .. gameId .. " läuft seit " .. (os.time() - startTime) .. " Sekunden") + -- Prüfe auf Projektil-Treffer + if HasEntityBeenDamagedByWeapon(ped, 0, 2) then -- 2 = Projektilwaffen + debugPrint("Projektil-Treffer erkannt") + + -- Schaden zurücksetzen + ClearEntityLastDamageEntity(ped) + ClearPedLastWeaponDamage(ped) + SetEntityHealth(ped, GetEntityMaxHealth(ped)) + + -- Lokale Stats sofort updaten + playerStats.deaths = playerStats.deaths + 1 + + -- Treffer-Events auslösen + TriggerEvent('tdm:playerHit') + TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, lastDamager) + + -- Warten um mehrfache Auslösung zu verhindern + Wait(500) end + else + Wait(500) end - - -- Zeit abgelaufen - if game and game.status == 'active' then - local winnerTeam = game.score.team1 > game.score.team2 and 'team1' or - game.score.team2 > game.score.team1 and 'team2' or nil - debugPrint("Spielzeit abgelaufen - Beende Spiel " .. gameId) - endGame(gameId, winnerTeam, 'Zeit abgelaufen') - end - end) -end - -function checkGameEnd(gameId) - local game = activeGames[gameId] - if not game then return end - - local totalPlayers = #game.team1 + #game.team2 - - if totalPlayers < 2 and game.status == 'active' then - debugPrint("Zu wenig Spieler - Beende Spiel " .. gameId) - endGame(gameId, nil, 'Zu wenig Spieler') - elseif totalPlayers == 0 then - activeGames[gameId] = nil - updateGamesListForAll() - debugPrint("Spiel " .. gameId .. " gelöscht (keine Spieler)") - end -end - -function updateScoreForGame(gameId) - local game = activeGames[gameId] - if not game then - debugPrint("Score-Update fehlgeschlagen - Spiel nicht gefunden: " .. gameId) - return - end - - debugPrint("Score Update für Spiel " .. gameId .. ": Team1=" .. game.score.team1 .. ", Team2=" .. game.score.team2) - - for _, playerId in ipairs(game.team1) do - local playerStats = game.playerStats[playerId] or {hits = 0, deaths = 0} - TriggerClientEvent('tdm:updateScore', playerId, game.score.team1, game.score.team2, { - hits = playerStats.hits or 0, - deaths = playerStats.deaths or 0 - }) - end - - for _, playerId in ipairs(game.team2) do - local playerStats = game.playerStats[playerId] or {hits = 0, deaths = 0} - TriggerClientEvent('tdm:updateScore', playerId, game.score.team1, game.score.team2, { - hits = playerStats.hits or 0, - deaths = playerStats.deaths or 0 - }) - end -end - -function updateGamesListForAll() - local players = QBCore.Functions.GetPlayers() - for _, playerId in pairs(players) do - TriggerClientEvent('tdm:updateGamesList', playerId, activeGames) - end -end - --- Player Disconnect Handler -AddEventHandler('playerDropped', function() - local src = source - - for gameId, game in pairs(activeGames) do - removePlayerFromGame(src, gameId) - end - - debugPrint("Spieler " .. src .. " hat den Server verlassen - aus allen Spielen entfernt") -end) - --- Server Start - Games Liste leeren -AddEventHandler('onResourceStart', function(resourceName) - if GetCurrentResourceName() == resourceName then - activeGames = {} - gameIdCounter = 1 - debugPrint("TeamDeathmatch System gestartet!") end end) +-- Function to spawn NPCs for all fields +function spawnFieldNPCs() + for fieldId, fieldData in pairs(Config.gameFields) do + if fieldData.lobby and fieldData.lobby.npc then + local npcData = fieldData.lobby.npc + local model = GetHashKey(npcData.model) + + -- Request the model + RequestModel(model) + while not HasModelLoaded(model) do + Wait(10) + end + + -- Create the NPC + local npc = CreatePed(4, model, npcData.coords.x, npcData.coords.y, npcData.coords.z, npcData.coords.w, false, true) + SetEntityAsMissionEntity(npc, true, true) + SetBlockingOfNonTemporaryEvents(npc, true) + FreezeEntityPosition(npc, true) + SetEntityInvincible(npc, true) + + -- Add to spawned NPCs table + table.insert(spawnedNPCs, npc) + + -- Add target interaction + exports['qb-target']:AddTargetEntity(npc, { + options = { + { + type = "client", + event = "tdm:openMenu", + icon = "fas fa-gamepad", + label = "TeamDeathmatch Menu", + fieldId = fieldId + } + }, + distance = 2.0 + }) + + debugPrint("Spawned NPC for field: " .. fieldId) + end + end +end + +-- Register event handler for NPC interaction +RegisterNetEvent('tdm:openMenu', function(data) + if data.fieldId then + openMainMenu(data.fieldId) + end +end) + +-- Spawn NPCs when resource starts +CreateThread(function() + Wait(1000) -- Wait for everything to load + spawnFieldNPCs() +end) + +-- Function to create blips for all TDM lobbies +function createTDMBlips() + for fieldId, fieldData in pairs(Config.gameFields) do + if fieldData.lobby and fieldData.lobby.pos then + local blip = AddBlipForCoord(fieldData.lobby.pos.x, fieldData.lobby.pos.y, fieldData.lobby.pos.z) + SetBlipSprite(blip, 156) -- You can change this to any appropriate sprite + SetBlipDisplay(blip, 4) + SetBlipScale(blip, 0.8) + SetBlipColour(blip, 3) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString("TDM: " .. fieldData.name) + EndTextCommandSetBlipName(blip) + + table.insert(tdmBlips, blip) + debugPrint("Created blip for TDM field: " .. fieldId) + end + end +end + +-- Call this function when the resource starts +CreateThread(function() + Wait(2000) -- Wait for everything to load + createTDMBlips() +end) + +-- Cleanup function +function cleanupResources() + -- Remove NPCs + for _, npc in ipairs(spawnedNPCs) do + if DoesEntityExist(npc) then + DeleteEntity(npc) + end + end + spawnedNPCs = {} + + -- Remove blips + for _, blip in ipairs(tdmBlips) do + if DoesBlipExist(blip) then + RemoveBlip(blip) + end + end + tdmBlips = {} + + debugPrint("Cleaned up all NPCs and blips") +end + +-- Register cleanup when resource stops AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() == resourceName then - -- Alle Spieler aus TDM entfernen - for gameId, game in pairs(activeGames) do - local allPlayers = {} - for _, playerId in ipairs(game.team1) do - table.insert(allPlayers, playerId) - end - for _, playerId in ipairs(game.team2) do - table.insert(allPlayers, playerId) - end - - for _, playerId in ipairs(allPlayers) do - TriggerClientEvent('tdm:leaveGame', playerId) - end - end - - activeGames = {} - debugPrint("TeamDeathmatch System gestoppt!") + cleanupResources() end end) --- Admin-Befehle -RegisterCommand('tdmreset', function(source, args) - local src = source - if src > 0 then -- Spieler - local Player = QBCore.Functions.GetPlayer(src) - if not Player or not Player.PlayerData.job or Player.PlayerData.job.name ~= 'admin' then - TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung!', 'error') - return +-- Helper function for table.count +if not table.count then + table.count = function(tbl) + local count = 0 + for _, _ in pairs(tbl) do + count = count + 1 end + return count end - - -- Alle Spieler aus TDM entfernen - for gameId, game in pairs(activeGames) do - local allPlayers = {} - for _, playerId in ipairs(game.team1) do - table.insert(allPlayers, playerId) - end - for _, playerId in ipairs(game.team2) do - table.insert(allPlayers, playerId) - end - - for _, playerId in ipairs(allPlayers) do - TriggerClientEvent('tdm:leaveGame', playerId) - end - end - - activeGames = {} - gameIdCounter = 1 - - debugPrint("TeamDeathmatch System zurückgesetzt!") - - if src > 0 then - TriggerClientEvent('QBCore:Notify', src, 'TDM System zurückgesetzt!', 'success') - end -end, true) - --- Debug-Befehl für Server-Status -RegisterCommand('tdmstatus', function(source, args) - local src = source - if src > 0 then -- Spieler - local Player = QBCore.Functions.GetPlayer(src) - if not Player or not Player.PlayerData.job or Player.PlayerData.job.name ~= 'admin' then - TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung!', 'error') - return - end - end - - debugPrint("=== TDM STATUS ===") - debugPrint("Aktive Spiele: " .. table.count(activeGames)) - - for gameId, game in pairs(activeGames) do - debugPrint("Spiel: " .. gameId .. " - " .. game.name) - debugPrint(" Status: " .. game.status) - debugPrint(" Feld: " .. game.fieldId) - debugPrint(" Team 1: " .. #game.team1 .. " Spieler, Score: " .. game.score.team1) - debugPrint(" Team 2: " .. #game.team2 .. " Spieler, Score: " .. game.score.team2) - end - - if src > 0 then - TriggerClientEvent('QBCore:Notify', src, 'TDM Status in Server-Konsole', 'info') - end -end, true) - --- Hilfsfunktion für table.count -table.count = function(tbl) - local count = 0 - for _, _ in pairs(tbl) do - count = count + 1 - end - return count end diff --git a/resources/[standalone]/nordi_tdm/server.lua b/resources/[standalone]/nordi_tdm/server.lua index 1c4756156..0c52224f1 100644 --- a/resources/[standalone]/nordi_tdm/server.lua +++ b/resources/[standalone]/nordi_tdm/server.lua @@ -183,28 +183,30 @@ RegisterNetEvent('tdm:playerWasHit', function(gameId, victimTeam, attackerId) game.playerStats[victim] = {hits = 0, deaths = 0} end - if attackerId and not game.playerStats[attackerId] then - game.playerStats[attackerId] = {hits = 0, deaths = 0} - end - - -- Stats updaten - game.playerStats[victim].deaths = (game.playerStats[victim].deaths or 0) + 1 - + -- Wichtig: Nur wenn ein Angreifer identifiziert wurde, zähle den Kill if attackerId then + if not game.playerStats[attackerId] then + game.playerStats[attackerId] = {hits = 0, deaths = 0} + end + + -- Stats updaten + game.playerStats[victim].deaths = (game.playerStats[victim].deaths or 0) + 1 game.playerStats[attackerId].hits = (game.playerStats[attackerId].hits or 0) + 1 + + -- Benachrichtigung an den Angreifer senden TriggerClientEvent('tdm:hitRegistered', attackerId) debugPrint("Treffer von " .. attackerId .. " gegen " .. victim .. " registriert") + + -- Team Score erhöhen + if victimTeam == 'team1' then + game.score.team2 = game.score.team2 + 1 + debugPrint("Punkt für Team 2 - Neuer Score: " .. game.score.team2) + else + game.score.team1 = game.score.team1 + 1 + debugPrint("Punkt für Team 1 - Neuer Score: " .. game.score.team1) + end else - debugPrint("Treffer gegen " .. victim .. " von unbekanntem Angreifer registriert") - end - - -- Team Score erhöhen - if victimTeam == 'team1' then - game.score.team2 = game.score.team2 + 1 - debugPrint("Punkt für Team 2 - Neuer Score: " .. game.score.team2) - else - game.score.team1 = game.score.team1 + 1 - debugPrint("Punkt für Team 1 - Neuer Score: " .. game.score.team1) + debugPrint("Treffer gegen " .. victim .. " von unbekanntem Angreifer registriert - Kein Punkt vergeben") end TriggerClientEvent('tdm:deathRegistered', victim)