Auto-sync 2026-04-15 22:45

This commit is contained in:
root 2026-04-15 22:45:02 +02:00
parent a3d5af24ec
commit f148a720da
4 changed files with 100 additions and 46 deletions

View File

@ -136,33 +136,30 @@ end)
-- Admin: Aktuelle Position erfassen
RegisterNUICallback('capturePos', function(data, cb)
local field = data.field or 'npc'
ClosePanel()
Citizen.Wait(300)
local ped = PlayerPedId()
local coords = GetEntityCoords(ped)
local heading = GetEntityHeading(ped)
Notify("Geh zur NPC-Position und drücke E.", "info")
Notify("Geh zur Position und drücke E.", "info")
exports['hex_4_hud']:ShowHelpNotify("Position erfassen", "E")
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
if IsControlJustPressed(0, 38) then -- E
if IsControlJustPressed(0, 38) then
exports['hex_4_hud']:HideHelpNotify()
local c = GetEntityCoords(PlayerPedId())
local h = GetEntityHeading(PlayerPedId())
-- Panel wieder öffnen mit erfassten Koordinaten
local coords = GetEntityCoords(PlayerPedId())
local heading = GetEntityHeading(PlayerPedId())
SetNuiFocus(true, true)
exports['hex_4_hud']:HideHud(true)
PanelOpen = true
SendNUIMessage({
action = "OPEN_ADMIN",
isAdmin = IsAdmin,
captured = { x = c.x, y = c.y, z = c.z, heading = h },
captured = { x = coords.x, y = coords.y, z = coords.z, heading = heading, field = field },
})
break
elseif IsControlJustPressed(0, 322) then -- ESC
elseif IsControlJustPressed(0, 322) then
exports['hex_4_hud']:HideHelpNotify()
OpenPanel()
break
@ -218,27 +215,47 @@ end)
-- ── Fahrrad spawnen ────────────────────────────────────────────
RegisterNetEvent('mercyv-bike:doSpawn', function(bikeModel)
RegisterNetEvent('mercyv-bike:doSpawn', function(bikeModel, spawnData)
print('[mercyv-bike] Spawne Fahrrad: ' .. tostring(bikeModel))
local model = GetHashKey(bikeModel)
if model == 0 then
Notify("Unbekanntes Fahrrad-Modell: " .. tostring(bikeModel), "error")
return
end
RequestModel(model)
local t = GetGameTimer() + 5000
local t = GetGameTimer() + 8000
while not HasModelLoaded(model) do
if GetGameTimer() > t then
Notify("Fahrrad konnte nicht gespawnt werden.", "error")
Notify("Fahrrad-Modell konnte nicht geladen werden.", "error")
return
end
Citizen.Wait(100)
end
-- Spawn-Position: vom Server gesetzte Koordinaten oder vor Spieler
local sx, sy, sz, sh
if spawnData and spawnData.x and spawnData.x ~= 0 then
sx = spawnData.x
sy = spawnData.y
sz = spawnData.z
sh = spawnData.heading or 0
else
local ped = PlayerPedId()
local coords = GetEntityCoords(ped)
local heading = GetEntityHeading(ped)
local rad = math.rad(heading)
local spawnX = coords.x + math.sin(-rad) * Config.SpawnOffset
local spawnY = coords.y + math.cos(-rad) * Config.SpawnOffset
sx = coords.x + math.sin(-rad) * Config.SpawnOffset
sy = coords.y + math.cos(-rad) * Config.SpawnOffset
sz = coords.z
sh = heading
end
local bike = CreateVehicle(model, spawnX, spawnY, coords.z, heading, true, false)
local tw = GetGameTimer() + 3000
print(string.format('[mercyv-bike] Spawn bei %.1f, %.1f, %.1f', sx, sy, sz))
local bike = CreateVehicle(model, sx, sy, sz, sh, true, false)
local tw = GetGameTimer() + 4000
while not DoesEntityExist(bike) do
if GetGameTimer() > tw then break end
Citizen.Wait(100)
@ -248,12 +265,11 @@ RegisterNetEvent('mercyv-bike:doSpawn', function(bikeModel)
SetEntityAsMissionEntity(bike, true, true)
SetVehicleEngineOn(bike, true, true, false)
SetModelAsNoLongerNeeded(model)
-- Schlüssel geben
local ks = Config.VehicleKeySystem
if ks == 'jaksam' then
TriggerServerEvent('vehicles_keys:selfGiveVehicleKeys',
GetVehicleNumberPlateText(bike))
end
TriggerServerEvent('vehicles_keys:selfGiveVehicleKeys', GetVehicleNumberPlateText(bike))
Notify("Fahrrad erhalten! Viel Spaß!", "success")
print('[mercyv-bike] Fahrrad erfolgreich gespawnt.')
else
Notify("Fahrrad konnte nicht gespawnt werden.", "error")
end
end)

View File

@ -285,8 +285,21 @@ body {
</button>
</div>
<div class="mb-form-section">
<div class="mb-form-label"><i class="fas fa-car"></i> Fahrrad Spawn-Position</div>
<div class="mb-pos-row">
<input type="number" v-model.number="npc.spawn_x" placeholder="X" class="mb-input mb-input-sm">
<input type="number" v-model.number="npc.spawn_y" placeholder="Y" class="mb-input mb-input-sm">
<input type="number" v-model.number="npc.spawn_z" placeholder="Z" class="mb-input mb-input-sm">
<input type="number" v-model.number="npc.spawn_heading" placeholder="Heading" class="mb-input mb-input-sm">
</div>
<button class="mb-capture-btn" v-on:click="captureSpawn()">
<i class="fas fa-crosshairs"></i> Spawn-Position erfassen
</button>
</div>
<button class="mb-save-btn" v-on:click="saveNPC()">
<i class="fas fa-save"></i> NPC speichern
<i class="fas fa-save"></i> Speichern
</button>
<div class="mb-admin-hint">
@ -358,7 +371,10 @@ var app = new Vue({
postNUI('close');
},
capturePos: function() {
postNUI('capturePos', {});
postNUI('capturePos', { field: 'npc' });
},
captureSpawn: function() {
postNUI('capturePos', { field: 'spawn' });
},
saveNPC: function() {
postNUI('saveNPC', {
@ -387,10 +403,17 @@ window.addEventListener('message', function(event) {
app.showAdmin = true;
app.isAdmin = true;
if (msg.captured) {
app.npc.x = Math.round(msg.captured.x * 100) / 100;
app.npc.y = Math.round(msg.captured.y * 100) / 100;
app.npc.z = Math.round(msg.captured.z * 100) / 100;
app.npc.heading = Math.round(msg.captured.heading * 100) / 100;
var rx = Math.round(msg.captured.x * 100) / 100;
var ry = Math.round(msg.captured.y * 100) / 100;
var rz = Math.round(msg.captured.z * 100) / 100;
var rh = Math.round(msg.captured.heading * 100) / 100;
if (msg.captured.field === 'spawn') {
app.npc.spawn_x = rx; app.npc.spawn_y = ry;
app.npc.spawn_z = rz; app.npc.spawn_heading = rh;
} else {
app.npc.x = rx; app.npc.y = ry;
app.npc.z = rz; app.npc.heading = rh;
}
}
break;
case 'CLOSE':

View File

@ -45,7 +45,8 @@ end)
-- ── NPC-Position synchronisieren ──────────────────────────────
local NPCData = nil -- wird aus DB oder Config geladen
local NPCData = nil
local SpawnData = nil -- separate Spawn-Position für Fahrräder
AddEventHandler('onResourceStart', function(res)
if res ~= GetCurrentResourceName() then return end
@ -58,13 +59,20 @@ AddEventHandler('onResourceStart', function(res)
`x` FLOAT DEFAULT 0,
`y` FLOAT DEFAULT 0,
`z` FLOAT DEFAULT 0,
`heading` FLOAT DEFAULT 0
`heading` FLOAT DEFAULT 0,
`spawn_x` FLOAT DEFAULT 0,
`spawn_y` FLOAT DEFAULT 0,
`spawn_z` FLOAT DEFAULT 0,
`spawn_heading` FLOAT DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
]])
-- Dann lesen
local r = MySQL.query.await("SELECT * FROM mercyv_bike_npc LIMIT 1")
if r and r[1] then
NPCData = r[1]
if r[1].spawn_x and r[1].spawn_x ~= 0 then
SpawnData = { x = r[1].spawn_x, y = r[1].spawn_y, z = r[1].spawn_z, heading = r[1].spawn_heading or 0 }
end
else
NPCData = {
model = Config.NPC.model,
@ -117,7 +125,7 @@ RegisterNetEvent('mercyv-bike:claim', function(bikeModel)
)
-- Spawn-Signal
TriggerClientEvent('mercyv-bike:doSpawn', src, bikeModel)
TriggerClientEvent('mercyv-bike:doSpawn', src, bikeModel, SpawnData)
Config.ServerNotification(src, Config.Notify.CLAIMED, "success")
print(string.format('[mercyv-bike] %s hat %s erhalten.', identifier, bikeModel))
end)
@ -148,15 +156,22 @@ RegisterNetEvent('mercyv-bike:saveNPC', function(data)
end
NPCData = data
if data.spawn_x and data.spawn_x ~= 0 then
SpawnData = { x = data.spawn_x, y = data.spawn_y, z = data.spawn_z, heading = data.spawn_heading or 0 }
end
-- In DB speichern
local existing = MySQL.query.await('SELECT id FROM mercyv_bike_npc LIMIT 1')
local sx = data.spawn_x or 0
local sy = data.spawn_y or 0
local sz = data.spawn_z or 0
local sh = data.spawn_heading or 0
if existing and existing[1] then
MySQL.update('UPDATE mercyv_bike_npc SET model=?, x=?, y=?, z=?, heading=? WHERE id=?',
{ data.model, data.x, data.y, data.z, data.heading, existing[1].id })
MySQL.update('UPDATE mercyv_bike_npc SET model=?, x=?, y=?, z=?, heading=?, spawn_x=?, spawn_y=?, spawn_z=?, spawn_heading=? WHERE id=?',
{ data.model, data.x, data.y, data.z, data.heading, sx, sy, sz, sh, existing[1].id })
else
MySQL.insert('INSERT INTO mercyv_bike_npc (model, x, y, z, heading) VALUES (?, ?, ?, ?, ?)',
{ data.model, data.x, data.y, data.z, data.heading })
MySQL.insert('INSERT INTO mercyv_bike_npc (model, x, y, z, heading, spawn_x, spawn_y, spawn_z, spawn_heading) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
{ data.model, data.x, data.y, data.z, data.heading, sx, sy, sz, sh })
end
TriggerClientEvent('mercyv-bike:syncNPC', -1, NPCData)

View File

@ -703,7 +703,7 @@ AddEventHandler('mercyv-deathscreen:client:npcReviveTeleport', function()
local ped = PlayerPedId()
-- Kurz warten, damit der Spieler nach dem Revive sicher am Leben ist
Wait(500)
Wait(200)
-- Bildschirm langsam schwarz ausblenden (Dauer: 1000 Millisekunden = 1 Sekunde)
DoScreenFadeOut(1000)