forked from Simnation/Main
ed
This commit is contained in:
parent
b743002467
commit
f192af7af6
3 changed files with 941 additions and 0 deletions
499
resources/[standalone]/nordi_tdm/client.lua
Normal file
499
resources/[standalone]/nordi_tdm/client.lua
Normal file
|
@ -0,0 +1,499 @@
|
|||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
local inTDM = false
|
||||
local currentTeam = nil
|
||||
local currentGameId = nil
|
||||
local currentField = nil
|
||||
local tdmBlip = nil
|
||||
local teamZoneBlips = {}
|
||||
local isHit = false
|
||||
local activeGames = {}
|
||||
|
||||
-- Events
|
||||
RegisterNetEvent('tdm:updateGamesList', function(games)
|
||||
activeGames = games
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:joinGame', function(gameId, team, fieldId)
|
||||
currentGameId = gameId
|
||||
currentTeam = team
|
||||
currentField = fieldId
|
||||
inTDM = true
|
||||
isHit = false
|
||||
|
||||
local fieldConfig = Config.gameFields[fieldId]
|
||||
|
||||
-- Teleport zu Team Spawn
|
||||
local spawnPoints = fieldConfig.teamSpawns[team]
|
||||
local randomSpawn = spawnPoints[math.random(#spawnPoints)]
|
||||
|
||||
SetEntityCoords(PlayerPedId(), randomSpawn.x, randomSpawn.y, randomSpawn.z)
|
||||
|
||||
-- Team Maske setzen
|
||||
setTeamMask(team)
|
||||
|
||||
-- Team Zone Blip erstellen
|
||||
createTeamZoneBlip(team, fieldConfig)
|
||||
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Du bist dem Spiel beigetreten! Team: ' .. team,
|
||||
type = 'success'
|
||||
})
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:leaveGame', function()
|
||||
inTDM = false
|
||||
currentTeam = nil
|
||||
currentGameId = nil
|
||||
currentField = nil
|
||||
isHit = false
|
||||
|
||||
-- Zurück zur Lobby
|
||||
SetEntityCoords(PlayerPedId(), Config.lobbyPos.x, Config.lobbyPos.y, Config.lobbyPos.z)
|
||||
|
||||
-- Maske entfernen
|
||||
SetPedComponentVariation(PlayerPedId(), 1, 0, 0, 0)
|
||||
|
||||
-- Zone Blips entfernen
|
||||
removeTeamZoneBlips()
|
||||
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Du hast das Spiel verlassen!',
|
||||
type = 'error'
|
||||
})
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:joinRequest', function(gameId, playerName, playerId)
|
||||
local alert = lib.alertDialog({
|
||||
header = 'Join Anfrage',
|
||||
content = playerName .. ' möchte deinem Spiel beitreten.\n\nErlauben?',
|
||||
centered = true,
|
||||
cancel = true,
|
||||
labels = {
|
||||
cancel = 'Ablehnen',
|
||||
confirm = 'Erlauben'
|
||||
}
|
||||
})
|
||||
|
||||
if alert == 'confirm' then
|
||||
TriggerServerEvent('tdm:approveJoinRequest', gameId, playerId, true)
|
||||
else
|
||||
TriggerServerEvent('tdm:approveJoinRequest', gameId, playerId, false)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:joinRequestResult', function(approved, gameName)
|
||||
if approved then
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Deine Anfrage wurde angenommen!',
|
||||
type = 'success'
|
||||
})
|
||||
else
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Deine Anfrage für "' .. gameName .. '" wurde abgelehnt!',
|
||||
type = 'error'
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:playerHit', function()
|
||||
if not inTDM or isHit then return end
|
||||
|
||||
isHit = true
|
||||
local ped = PlayerPedId()
|
||||
|
||||
-- Arme hochnehmen Animation
|
||||
RequestAnimDict("random@mugging3")
|
||||
while not HasAnimDictLoaded("random@mugging3") do
|
||||
Wait(1)
|
||||
end
|
||||
|
||||
TaskPlayAnim(ped, "random@mugging3", "handsup_standing_base", 8.0, -8.0, -1, 50, 0, false, false, false)
|
||||
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Du wurdest getroffen! Gehe zurück zu deiner Team Zone!',
|
||||
type = 'error'
|
||||
})
|
||||
|
||||
-- Zone Marker hervorheben
|
||||
highlightTeamZone(currentTeam)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score)
|
||||
lib.showTextUI('[Team 1: ' .. team1Score .. '] VS [Team 2: ' .. team2Score .. ']', {
|
||||
position = "top-center",
|
||||
icon = 'crosshairs'
|
||||
})
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:gameEnded', function(winnerTeam, team1Score, team2Score)
|
||||
lib.hideTextUI()
|
||||
|
||||
local alert = lib.alertDialog({
|
||||
header = 'Spiel beendet!',
|
||||
content = 'Team ' .. winnerTeam .. ' hat gewonnen!\n\nTeam 1: ' .. team1Score .. '\nTeam 2: ' .. team2Score,
|
||||
centered = true,
|
||||
cancel = false
|
||||
})
|
||||
|
||||
Wait(5000)
|
||||
TriggerServerEvent('tdm:leaveGame')
|
||||
end)
|
||||
|
||||
-- Funktionen
|
||||
function setTeamMask(team)
|
||||
local ped = PlayerPedId()
|
||||
local maskData = Config.teamMasks[team]
|
||||
|
||||
if maskData then
|
||||
SetPedComponentVariation(ped, maskData.component, maskData.drawable, maskData.texture, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function createTeamZoneBlip(team, fieldConfig)
|
||||
local zone = fieldConfig.teamZones[team]
|
||||
|
||||
local blip = AddBlipForRadius(zone.center.x, zone.center.y, zone.center.z, zone.radius)
|
||||
SetBlipHighDetail(blip, true)
|
||||
SetBlipColour(blip, team == 'team1' and 1 or 3)
|
||||
SetBlipAlpha(blip, 128)
|
||||
|
||||
teamZoneBlips[team] = blip
|
||||
end
|
||||
|
||||
function removeTeamZoneBlips()
|
||||
for team, blip in pairs(teamZoneBlips) do
|
||||
if DoesBlipExist(blip) then
|
||||
RemoveBlip(blip)
|
||||
end
|
||||
end
|
||||
teamZoneBlips = {}
|
||||
end
|
||||
|
||||
function highlightTeamZone(team)
|
||||
if teamZoneBlips[team] and DoesBlipExist(teamZoneBlips[team]) then
|
||||
SetBlipFlashes(teamZoneBlips[team], true)
|
||||
end
|
||||
end
|
||||
|
||||
function openMainMenu()
|
||||
TriggerServerEvent('tdm:requestGamesList')
|
||||
|
||||
Wait(100) -- Kurz warten für Server Response
|
||||
|
||||
local options = {
|
||||
{
|
||||
title = 'Neues Spiel erstellen',
|
||||
description = 'Erstelle ein neues TeamDeathmatch Spiel',
|
||||
icon = 'plus',
|
||||
onSelect = function()
|
||||
openCreateGameMenu()
|
||||
end
|
||||
},
|
||||
{
|
||||
title = 'Spiel beitreten',
|
||||
description = 'Trete einem laufenden Spiel bei',
|
||||
icon = 'users',
|
||||
onSelect = function()
|
||||
openJoinGameMenu()
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
if inTDM then
|
||||
table.insert(options, {
|
||||
title = 'Aktuelles Spiel verlassen',
|
||||
description = 'Verlasse das laufende Spiel',
|
||||
icon = 'door-open',
|
||||
iconColor = 'red',
|
||||
onSelect = function()
|
||||
TriggerServerEvent('tdm:leaveGame')
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'tdm_main_menu',
|
||||
title = 'TeamDeathmatch',
|
||||
options = options
|
||||
})
|
||||
|
||||
lib.showContext('tdm_main_menu')
|
||||
end
|
||||
|
||||
function openCreateGameMenu()
|
||||
local fieldOptions = {}
|
||||
|
||||
-- Spielfelder zu Options hinzufügen
|
||||
for fieldId, fieldData in pairs(Config.gameFields) do
|
||||
table.insert(fieldOptions, {
|
||||
value = fieldId,
|
||||
label = fieldData.name .. ' (Max: ' .. fieldData.maxPlayers .. ')'
|
||||
})
|
||||
end
|
||||
|
||||
local input = lib.inputDialog('Neues Spiel erstellen', {
|
||||
{
|
||||
type = 'input',
|
||||
label = 'Spiel Name',
|
||||
description = 'Gib deinem Spiel einen Namen',
|
||||
required = true,
|
||||
max = 30
|
||||
},
|
||||
{
|
||||
type = 'select',
|
||||
label = 'Spielfeld',
|
||||
description = 'Wähle ein Spielfeld',
|
||||
required = true,
|
||||
options = fieldOptions
|
||||
},
|
||||
{
|
||||
type = 'select',
|
||||
label = 'Spiel Typ',
|
||||
description = 'Wähle den Spiel Typ',
|
||||
required = true,
|
||||
options = {
|
||||
{value = 'public', label = 'Öffentlich (Jeder kann beitreten)'},
|
||||
{value = 'private', label = 'Privat (Nur mit Genehmigung)'}
|
||||
}
|
||||
},
|
||||
{
|
||||
type = 'input',
|
||||
label = 'Passwort (Optional)',
|
||||
description = 'Passwort für das Spiel (leer lassen für kein Passwort)',
|
||||
password = true
|
||||
}
|
||||
})
|
||||
|
||||
if not input then return end
|
||||
|
||||
local gameName = input[1]
|
||||
local fieldId = input[2]
|
||||
local gameType = input[3] -- 'public' oder 'private'
|
||||
local password = input[4] and input[4] ~= '' and input[4] or nil
|
||||
|
||||
if gameName and fieldId and gameType then
|
||||
TriggerServerEvent('tdm:createGame', gameName, fieldId, gameType, password)
|
||||
end
|
||||
end
|
||||
|
||||
function openJoinGameMenu()
|
||||
TriggerServerEvent('tdm:requestGamesList')
|
||||
|
||||
Wait(200)
|
||||
|
||||
local options = {}
|
||||
|
||||
for gameId, gameData in pairs(activeGames) do
|
||||
local playerCount = #gameData.team1 + #gameData.team2
|
||||
local maxPlayers = Config.gameFields[gameData.fieldId].maxPlayers
|
||||
local fieldName = Config.gameFields[gameData.fieldId].name
|
||||
|
||||
local statusText = gameData.status == 'waiting' and 'Wartend' or 'Läuft'
|
||||
local typeText = gameData.gameType == 'public' and '🌐 Öffentlich' or '🔒 Privat'
|
||||
local passwordIcon = gameData.hasPassword and ' 🔑' or ''
|
||||
|
||||
table.insert(options, {
|
||||
title = gameData.name .. passwordIcon,
|
||||
description = typeText .. ' | Feld: ' .. fieldName .. ' | Spieler: ' .. playerCount .. '/' .. maxPlayers .. ' | Status: ' .. statusText,
|
||||
icon = gameData.gameType == 'public' and 'globe' or 'lock',
|
||||
iconColor = gameData.gameType == 'public' and 'green' or 'orange',
|
||||
args = {
|
||||
gameId = gameId,
|
||||
hasPassword = gameData.hasPassword,
|
||||
gameType = gameData.gameType
|
||||
},
|
||||
onSelect = function(args)
|
||||
if args.hasPassword then
|
||||
local input = lib.inputDialog('Passwort eingeben', {
|
||||
{
|
||||
type = 'input',
|
||||
label = 'Passwort',
|
||||
description = 'Gib das Spiel-Passwort ein',
|
||||
required = true,
|
||||
password = true
|
||||
}
|
||||
})
|
||||
|
||||
if input and input[1] then
|
||||
TriggerServerEvent('tdm:requestJoinGame', args.gameId, input[1])
|
||||
end
|
||||
else
|
||||
TriggerServerEvent('tdm:requestJoinGame', args.gameId)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
if #options == 0 then
|
||||
table.insert(options, {
|
||||
title = 'Keine Spiele verfügbar',
|
||||
description = 'Erstelle ein neues Spiel',
|
||||
icon = 'info',
|
||||
disabled = true
|
||||
})
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = 'tdm_join_menu',
|
||||
title = 'Spiel beitreten',
|
||||
menu = 'tdm_main_menu',
|
||||
options = options
|
||||
})
|
||||
|
||||
lib.showContext('tdm_join_menu')
|
||||
end
|
||||
|
||||
|
||||
-- Zone Checker Thread
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(500)
|
||||
|
||||
if inTDM and isHit and currentTeam and currentField then
|
||||
local ped = PlayerPedId()
|
||||
local playerPos = GetEntityCoords(ped)
|
||||
local zone = Config.gameFields[currentField].teamZones[currentTeam]
|
||||
|
||||
local distance = #(playerPos - zone.center)
|
||||
|
||||
if distance <= zone.radius then
|
||||
isHit = false
|
||||
ClearPedTasks(ped)
|
||||
|
||||
if teamZoneBlips[currentTeam] and DoesBlipExist(teamZoneBlips[currentTeam]) then
|
||||
SetBlipFlashes(teamZoneBlips[currentTeam], false)
|
||||
end
|
||||
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Du bist wieder im Spiel!',
|
||||
type = 'success'
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Zone Marker Renderer
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(0)
|
||||
|
||||
if inTDM and currentTeam and currentField then
|
||||
local zone = Config.gameFields[currentField].teamZones[currentTeam]
|
||||
local color = zone.color
|
||||
|
||||
DrawMarker(
|
||||
1,
|
||||
zone.center.x, zone.center.y, zone.center.z - 1.0,
|
||||
0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0,
|
||||
zone.radius * 2, zone.radius * 2, 1.0,
|
||||
color.r, color.g, color.b, color.a,
|
||||
false, true, 2, false, nil, nil, false
|
||||
)
|
||||
|
||||
if isHit then
|
||||
DrawMarker(
|
||||
2,
|
||||
zone.center.x, zone.center.y, zone.center.z + 5.0,
|
||||
0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0,
|
||||
1.0, 1.0, 1.0,
|
||||
255, 255, 0, 200,
|
||||
true, true, 2, false, nil, nil, false
|
||||
)
|
||||
end
|
||||
else
|
||||
Wait(1000)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Damage Handler
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(100)
|
||||
|
||||
if inTDM and not isHit then
|
||||
local ped = PlayerPedId()
|
||||
|
||||
if HasEntityBeenDamagedByAnyPed(ped) then
|
||||
ClearEntityLastDamageEntity(ped)
|
||||
TriggerEvent('tdm:playerHit')
|
||||
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Death Handler
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(1000)
|
||||
|
||||
if inTDM then
|
||||
local ped = PlayerPedId()
|
||||
|
||||
if IsEntityDead(ped) then
|
||||
TriggerServerEvent('tdm:playerDied', currentGameId)
|
||||
|
||||
lib.notify({
|
||||
title = 'TeamDeathmatch',
|
||||
description = 'Du bist ausgeschieden!',
|
||||
type = 'error'
|
||||
})
|
||||
|
||||
Wait(3000)
|
||||
TriggerServerEvent('tdm:leaveGame')
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- NPC Setup
|
||||
CreateThread(function()
|
||||
-- Blip erstellen
|
||||
tdmBlip = AddBlipForCoord(Config.lobbyPos.x, Config.lobbyPos.y, Config.lobbyPos.z)
|
||||
SetBlipSprite(tdmBlip, 432)
|
||||
SetBlipDisplay(tdmBlip, 4)
|
||||
SetBlipScale(tdmBlip, 0.8)
|
||||
SetBlipColour(tdmBlip, 1)
|
||||
SetBlipAsShortRange(tdmBlip, true)
|
||||
BeginTextCommandSetBlipName("STRING")
|
||||
AddTextComponentString("TeamDeathmatch")
|
||||
EndTextCommandSetBlipName(tdmBlip)
|
||||
|
||||
-- NPC erstellen
|
||||
RequestModel(GetHashKey(Config.gameNPC.model))
|
||||
while not HasModelLoaded(GetHashKey(Config.gameNPC.model)) do
|
||||
Wait(1)
|
||||
end
|
||||
|
||||
local npc = CreatePed(4, GetHashKey(Config.gameNPC.model), Config.gameNPC.coords.x, Config.gameNPC.coords.y, Config.gameNPC.coords.z, Config.gameNPC.coords.w, false, true)
|
||||
SetEntityInvincible(npc, true)
|
||||
FreezeEntityPosition(npc, true)
|
||||
SetBlockingOfNonTemporaryEvents(npc, true)
|
||||
end)
|
||||
|
||||
-- Target für NPC
|
||||
exports['qb-target']:AddTargetModel(Config.gameNPC.model, {
|
||||
options = {
|
||||
{
|
||||
type = "client",
|
||||
event = "tdm:openMainMenu",
|
||||
icon = "fas fa-crosshairs",
|
||||
label = "TeamDeathmatch",
|
||||
},
|
||||
},
|
||||
distance = 2.5
|
||||
})
|
||||
|
||||
RegisterNetEvent('tdm:openMainMenu', function()
|
||||
openMainMenu()
|
||||
end)
|
95
resources/[standalone]/nordi_tdm/config.lua
Normal file
95
resources/[standalone]/nordi_tdm/config.lua
Normal file
|
@ -0,0 +1,95 @@
|
|||
Config = {}
|
||||
|
||||
-- Lobby Position
|
||||
Config.lobbyPos = vector3(-1042.4, -2745.8, 21.4)
|
||||
|
||||
-- NPC für Game Management
|
||||
Config.gameNPC = {
|
||||
model = 's_m_y_cop_01',
|
||||
coords = vector4(-1042.4, -2745.8, 20.4, 0.0)
|
||||
}
|
||||
|
||||
-- Spielfelder Konfiguration
|
||||
Config.gameFields = {
|
||||
field1 = {
|
||||
name = "Arena 1",
|
||||
maxPlayers = 20,
|
||||
|
||||
-- Team Spawn Punkte
|
||||
teamSpawns = {
|
||||
team1 = {
|
||||
vector3(-1000.0, -2700.0, 25.0),
|
||||
vector3(-1005.0, -2705.0, 25.0),
|
||||
vector3(-995.0, -2695.0, 25.0)
|
||||
},
|
||||
team2 = {
|
||||
vector3(-1100.0, -2800.0, 25.0),
|
||||
vector3(-1105.0, -2805.0, 25.0),
|
||||
vector3(-1095.0, -2795.0, 25.0)
|
||||
}
|
||||
},
|
||||
|
||||
-- Team Lager Zonen (Kreismarker)
|
||||
teamZones = {
|
||||
team1 = {
|
||||
center = vector3(-980.0, -2680.0, 25.0),
|
||||
radius = 10.0,
|
||||
color = {r = 255, g = 0, b = 0, a = 100}
|
||||
},
|
||||
team2 = {
|
||||
center = vector3(-1120.0, -2820.0, 25.0),
|
||||
radius = 10.0,
|
||||
color = {r = 0, g = 0, b = 255, a = 100}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
field2 = {
|
||||
name = "Arena 2",
|
||||
maxPlayers = 16,
|
||||
|
||||
teamSpawns = {
|
||||
team1 = {
|
||||
vector3(-2000.0, -2700.0, 25.0),
|
||||
vector3(-2005.0, -2705.0, 25.0),
|
||||
vector3(-1995.0, -2695.0, 25.0)
|
||||
},
|
||||
team2 = {
|
||||
vector3(-2100.0, -2800.0, 25.0),
|
||||
vector3(-2105.0, -2805.0, 25.0),
|
||||
vector3(-2095.0, -2795.0, 25.0)
|
||||
}
|
||||
},
|
||||
|
||||
teamZones = {
|
||||
team1 = {
|
||||
center = vector3(-1980.0, -2680.0, 25.0),
|
||||
radius = 10.0,
|
||||
color = {r = 255, g = 0, b = 0, a = 100}
|
||||
},
|
||||
team2 = {
|
||||
center = vector3(-2120.0, -2820.0, 25.0),
|
||||
radius = 10.0,
|
||||
color = {r = 0, g = 0, b = 255, a = 100}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Team Masken
|
||||
Config.teamMasks = {
|
||||
team1 = {
|
||||
component = 1,
|
||||
drawable = 52,
|
||||
texture = 0
|
||||
},
|
||||
team2 = {
|
||||
component = 1,
|
||||
drawable = 54,
|
||||
texture = 0
|
||||
}
|
||||
}
|
||||
|
||||
-- Game Settings
|
||||
Config.maxGameTime = 600 -- 10 Minuten
|
||||
Config.maxHits = 30 -- Spiel endet bei 30 Treffern
|
347
resources/[standalone]/nordi_tdm/server.lua
Normal file
347
resources/[standalone]/nordi_tdm/server.lua
Normal file
|
@ -0,0 +1,347 @@
|
|||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
-- Game Management
|
||||
local activeGames = {}
|
||||
local gameIdCounter = 1
|
||||
|
||||
-- Events
|
||||
RegisterNetEvent('tdm:createGame', function(gameName, fieldId, gameType, password)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
|
||||
-- Prüfen ob Spielfeld bereits belegt
|
||||
for gameId, gameData in pairs(activeGames) do
|
||||
if gameData.fieldId == fieldId then
|
||||
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, -- 'public' oder 'private'
|
||||
password = password,
|
||||
hasPassword = password ~= nil,
|
||||
status = 'waiting', -- waiting, active, finished
|
||||
team1 = {},
|
||||
team2 = {},
|
||||
score = {team1 = 0, team2 = 0},
|
||||
startTime = nil,
|
||||
maxTime = Config.maxGameTime,
|
||||
maxHits = Config.maxHits
|
||||
}
|
||||
|
||||
local typeText = gameType == 'public' and 'öffentliches' or 'privates'
|
||||
TriggerClientEvent('QBCore:Notify', src, 'Dein ' .. typeText .. ' Spiel "' .. gameName .. '" wurde erstellt!', 'success')
|
||||
|
||||
-- Games Liste an alle senden
|
||||
updateGamesListForAll()
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:requestJoinGame', function(gameId, password)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player or not activeGames[gameId] then return end
|
||||
|
||||
local game = activeGames[gameId]
|
||||
local playerName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
|
||||
|
||||
-- Passwort prüfen falls vorhanden
|
||||
if game.hasPassword and game.password ~= password then
|
||||
TriggerClientEvent('QBCore:Notify', src, 'Falsches Passwort!', 'error')
|
||||
return
|
||||
end
|
||||
|
||||
-- Spieler bereits im Spiel?
|
||||
for _, playerId in ipairs(game.team1) do
|
||||
if playerId == src then
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
-- Öffentliches Spiel - automatisch beitreten
|
||||
joinPlayerToGame(src, gameId)
|
||||
TriggerClientEvent('QBCore:Notify', src, 'Du bist dem öffentlichen Spiel beigetreten!', 'success')
|
||||
else
|
||||
-- Privates Spiel - Join Request an Admin senden
|
||||
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 return end
|
||||
|
||||
if approved then
|
||||
joinPlayerToGame(playerId, gameId)
|
||||
TriggerClientEvent('tdm:joinRequestResult', playerId, true, game.name)
|
||||
else
|
||||
TriggerClientEvent('tdm:joinRequestResult', playerId, false, game.name)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:leaveGame', function()
|
||||
local src = source
|
||||
|
||||
-- Spieler aus allen Spielen entfernen
|
||||
for gameId, game in pairs(activeGames) do
|
||||
removePlayerFromGame(src, gameId)
|
||||
end
|
||||
|
||||
TriggerClientEvent('tdm:leaveGame', src)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:playerWasHit', function(gameId, victimTeam)
|
||||
local src = source
|
||||
local game = activeGames[gameId]
|
||||
|
||||
if not game then return end
|
||||
|
||||
-- Punkt für das andere Team
|
||||
local scoringTeam = victimTeam == 'team1' and 'team2' or 'team1'
|
||||
game.score[scoringTeam] = game.score[scoringTeam] + 1
|
||||
|
||||
-- Score Update an alle Spieler
|
||||
updateScoreForGame(gameId)
|
||||
|
||||
-- Prüfen ob Spiel gewonnen
|
||||
if game.score[scoringTeam] >= game.maxHits then
|
||||
endGame(gameId, scoringTeam)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('tdm:playerDied', function(gameId)
|
||||
local src = source
|
||||
removePlayerFromGame(src, gameId)
|
||||
checkGameEnd(gameId)
|
||||
end)
|
||||
|
||||
-- Funktionen
|
||||
function joinPlayerToGame(playerId, gameId)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return end
|
||||
|
||||
-- Team mit weniger Spielern wählen
|
||||
local team = #game.team1 <= #game.team2 and 'team1' or 'team2'
|
||||
|
||||
table.insert(game[team], playerId)
|
||||
|
||||
TriggerClientEvent('tdm:joinGame', playerId, gameId, team, game.fieldId)
|
||||
|
||||
-- Spiel starten wenn genug Spieler
|
||||
checkGameStart(gameId)
|
||||
|
||||
updateGamesListForAll()
|
||||
updateScoreForGame(gameId)
|
||||
end
|
||||
|
||||
function removePlayerFromGame(playerId, gameId)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return end
|
||||
|
||||
-- Aus Team 1 entfernen
|
||||
for i, id in ipairs(game.team1) do
|
||||
if id == playerId then
|
||||
table.remove(game.team1, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Aus Team 2 entfernen
|
||||
for i, id in ipairs(game.team2) do
|
||||
if id == playerId then
|
||||
table.remove(game.team2, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Wenn Admin das Spiel verlässt, Spiel beenden
|
||||
if game.admin == playerId then
|
||||
endGame(gameId, nil, 'Admin hat das Spiel verlassen')
|
||||
return
|
||||
end
|
||||
|
||||
checkGameEnd(gameId)
|
||||
updateGamesListForAll()
|
||||
end
|
||||
|
||||
function checkGameStart(gameId)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return end
|
||||
|
||||
local totalPlayers = #game.team1 + #game.team2
|
||||
|
||||
if totalPlayers >= 2 and game.status == 'waiting' then
|
||||
startGame(gameId)
|
||||
end
|
||||
end
|
||||
|
||||
function startGame(gameId)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return end
|
||||
|
||||
game.status = 'active'
|
||||
game.startTime = os.time()
|
||||
game.score = {team1 = 0, team2 = 0}
|
||||
|
||||
-- Nachricht an alle Spieler
|
||||
for _, playerId in ipairs(game.team1) do
|
||||
TriggerClientEvent('QBCore:Notify', playerId, 'Spiel gestartet!', 'success')
|
||||
end
|
||||
for _, playerId in ipairs(game.team2) do
|
||||
TriggerClientEvent('QBCore:Notify', playerId, 'Spiel gestartet!', 'success')
|
||||
end
|
||||
|
||||
updateScoreForGame(gameId)
|
||||
updateGamesListForAll()
|
||||
|
||||
-- Game Timer
|
||||
CreateThread(function()
|
||||
while activeGames[gameId] and activeGames[gameId].status == 'active' do
|
||||
Wait(1000)
|
||||
|
||||
local currentTime = os.time()
|
||||
local elapsed = currentTime - game.startTime
|
||||
|
||||
if elapsed >= game.maxTime then
|
||||
local winnerTeam = game.score.team1 > game.score.team2 and 'team1' or 'team2'
|
||||
endGame(gameId, winnerTeam, 'Zeit abgelaufen')
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function endGame(gameId, winnerTeam, reason)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return end
|
||||
|
||||
game.status = 'finished'
|
||||
|
||||
local message = reason or (winnerTeam and ('Team ' .. winnerTeam .. ' hat gewonnen!') or 'Spiel beendet')
|
||||
|
||||
-- Nachricht an alle Spieler
|
||||
for _, playerId in ipairs(game.team1) do
|
||||
if winnerTeam then
|
||||
TriggerClientEvent('tdm:gameEnded', playerId, winnerTeam, game.score.team1, game.score.team2)
|
||||
else
|
||||
TriggerClientEvent('QBCore:Notify', playerId, message, 'error')
|
||||
TriggerClientEvent('tdm:leaveGame', playerId)
|
||||
end
|
||||
end
|
||||
for _, playerId in ipairs(game.team2) do
|
||||
if winnerTeam then
|
||||
TriggerClientEvent('tdm:gameEnded', playerId, winnerTeam, game.score.team1, game.score.team2)
|
||||
else
|
||||
TriggerClientEvent('QBCore:Notify', playerId, message, 'error')
|
||||
TriggerClientEvent('tdm:leaveGame', playerId)
|
||||
end
|
||||
end
|
||||
|
||||
-- Spiel nach 15 Sekunden löschen
|
||||
CreateThread(function()
|
||||
Wait(15000)
|
||||
activeGames[gameId] = nil
|
||||
updateGamesListForAll()
|
||||
end)
|
||||
end
|
||||
|
||||
function checkGameEnd(gameId)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return 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
|
||||
endGame(gameId, nil, 'Zu wenig Spieler')
|
||||
elseif totalPlayers == 0 then
|
||||
-- Spiel löschen wenn keine Spieler mehr da sind
|
||||
activeGames[gameId] = nil
|
||||
updateGamesListForAll()
|
||||
end
|
||||
end
|
||||
|
||||
function updateScoreForGame(gameId)
|
||||
local game = activeGames[gameId]
|
||||
if not game then return end
|
||||
|
||||
-- Score an alle Spieler des Spiels senden
|
||||
for _, playerId in ipairs(game.team1) do
|
||||
TriggerClientEvent('tdm:updateScore', playerId, game.score.team1, game.score.team2)
|
||||
end
|
||||
for _, playerId in ipairs(game.team2) do
|
||||
TriggerClientEvent('tdm:updateScore', playerId, game.score.team1, game.score.team2)
|
||||
end
|
||||
end
|
||||
|
||||
function updateGamesListForAll()
|
||||
-- Games Liste an alle Spieler senden
|
||||
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
|
||||
|
||||
-- Spieler aus allen Spielen entfernen
|
||||
for gameId, game in pairs(activeGames) do
|
||||
removePlayerFromGame(src, gameId)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Server Start - Games Liste leeren
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if GetCurrentResourceName() == resourceName then
|
||||
activeGames = {}
|
||||
gameIdCounter = 1
|
||||
end
|
||||
end)
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue