Auto-sync 2026-04-15 22:45
This commit is contained in:
parent
a3d5af24ec
commit
f148a720da
@ -136,33 +136,30 @@ end)
|
|||||||
|
|
||||||
-- Admin: Aktuelle Position erfassen
|
-- Admin: Aktuelle Position erfassen
|
||||||
RegisterNUICallback('capturePos', function(data, cb)
|
RegisterNUICallback('capturePos', function(data, cb)
|
||||||
|
local field = data.field or 'npc'
|
||||||
ClosePanel()
|
ClosePanel()
|
||||||
Citizen.Wait(300)
|
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")
|
exports['hex_4_hud']:ShowHelpNotify("Position erfassen", "E")
|
||||||
|
|
||||||
Citizen.CreateThread(function()
|
Citizen.CreateThread(function()
|
||||||
while true do
|
while true do
|
||||||
Citizen.Wait(0)
|
Citizen.Wait(0)
|
||||||
if IsControlJustPressed(0, 38) then -- E
|
if IsControlJustPressed(0, 38) then
|
||||||
exports['hex_4_hud']:HideHelpNotify()
|
exports['hex_4_hud']:HideHelpNotify()
|
||||||
local c = GetEntityCoords(PlayerPedId())
|
local coords = GetEntityCoords(PlayerPedId())
|
||||||
local h = GetEntityHeading(PlayerPedId())
|
local heading = GetEntityHeading(PlayerPedId())
|
||||||
-- Panel wieder öffnen mit erfassten Koordinaten
|
|
||||||
SetNuiFocus(true, true)
|
SetNuiFocus(true, true)
|
||||||
exports['hex_4_hud']:HideHud(true)
|
exports['hex_4_hud']:HideHud(true)
|
||||||
PanelOpen = true
|
PanelOpen = true
|
||||||
SendNUIMessage({
|
SendNUIMessage({
|
||||||
action = "OPEN_ADMIN",
|
action = "OPEN_ADMIN",
|
||||||
isAdmin = IsAdmin,
|
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
|
break
|
||||||
elseif IsControlJustPressed(0, 322) then -- ESC
|
elseif IsControlJustPressed(0, 322) then
|
||||||
exports['hex_4_hud']:HideHelpNotify()
|
exports['hex_4_hud']:HideHelpNotify()
|
||||||
OpenPanel()
|
OpenPanel()
|
||||||
break
|
break
|
||||||
@ -218,27 +215,47 @@ end)
|
|||||||
|
|
||||||
-- ── Fahrrad spawnen ────────────────────────────────────────────
|
-- ── 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)
|
local model = GetHashKey(bikeModel)
|
||||||
|
if model == 0 then
|
||||||
|
Notify("Unbekanntes Fahrrad-Modell: " .. tostring(bikeModel), "error")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
RequestModel(model)
|
RequestModel(model)
|
||||||
local t = GetGameTimer() + 5000
|
local t = GetGameTimer() + 8000
|
||||||
while not HasModelLoaded(model) do
|
while not HasModelLoaded(model) do
|
||||||
if GetGameTimer() > t then
|
if GetGameTimer() > t then
|
||||||
Notify("Fahrrad konnte nicht gespawnt werden.", "error")
|
Notify("Fahrrad-Modell konnte nicht geladen werden.", "error")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
Citizen.Wait(100)
|
Citizen.Wait(100)
|
||||||
end
|
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 ped = PlayerPedId()
|
||||||
local coords = GetEntityCoords(ped)
|
local coords = GetEntityCoords(ped)
|
||||||
local heading = GetEntityHeading(ped)
|
local heading = GetEntityHeading(ped)
|
||||||
local rad = math.rad(heading)
|
local rad = math.rad(heading)
|
||||||
local spawnX = coords.x + math.sin(-rad) * Config.SpawnOffset
|
sx = coords.x + math.sin(-rad) * Config.SpawnOffset
|
||||||
local spawnY = coords.y + math.cos(-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)
|
print(string.format('[mercyv-bike] Spawn bei %.1f, %.1f, %.1f', sx, sy, sz))
|
||||||
local tw = GetGameTimer() + 3000
|
|
||||||
|
local bike = CreateVehicle(model, sx, sy, sz, sh, true, false)
|
||||||
|
local tw = GetGameTimer() + 4000
|
||||||
while not DoesEntityExist(bike) do
|
while not DoesEntityExist(bike) do
|
||||||
if GetGameTimer() > tw then break end
|
if GetGameTimer() > tw then break end
|
||||||
Citizen.Wait(100)
|
Citizen.Wait(100)
|
||||||
@ -248,12 +265,11 @@ RegisterNetEvent('mercyv-bike:doSpawn', function(bikeModel)
|
|||||||
SetEntityAsMissionEntity(bike, true, true)
|
SetEntityAsMissionEntity(bike, true, true)
|
||||||
SetVehicleEngineOn(bike, true, true, false)
|
SetVehicleEngineOn(bike, true, true, false)
|
||||||
SetModelAsNoLongerNeeded(model)
|
SetModelAsNoLongerNeeded(model)
|
||||||
-- Schlüssel geben
|
TriggerServerEvent('vehicles_keys:selfGiveVehicleKeys', GetVehicleNumberPlateText(bike))
|
||||||
local ks = Config.VehicleKeySystem
|
Notify("Fahrrad erhalten! Viel Spaß!", "success")
|
||||||
if ks == 'jaksam' then
|
print('[mercyv-bike] Fahrrad erfolgreich gespawnt.')
|
||||||
TriggerServerEvent('vehicles_keys:selfGiveVehicleKeys',
|
else
|
||||||
GetVehicleNumberPlateText(bike))
|
Notify("Fahrrad konnte nicht gespawnt werden.", "error")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@ -285,8 +285,21 @@ body {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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()">
|
<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>
|
</button>
|
||||||
|
|
||||||
<div class="mb-admin-hint">
|
<div class="mb-admin-hint">
|
||||||
@ -358,7 +371,10 @@ var app = new Vue({
|
|||||||
postNUI('close');
|
postNUI('close');
|
||||||
},
|
},
|
||||||
capturePos: function() {
|
capturePos: function() {
|
||||||
postNUI('capturePos', {});
|
postNUI('capturePos', { field: 'npc' });
|
||||||
|
},
|
||||||
|
captureSpawn: function() {
|
||||||
|
postNUI('capturePos', { field: 'spawn' });
|
||||||
},
|
},
|
||||||
saveNPC: function() {
|
saveNPC: function() {
|
||||||
postNUI('saveNPC', {
|
postNUI('saveNPC', {
|
||||||
@ -387,10 +403,17 @@ window.addEventListener('message', function(event) {
|
|||||||
app.showAdmin = true;
|
app.showAdmin = true;
|
||||||
app.isAdmin = true;
|
app.isAdmin = true;
|
||||||
if (msg.captured) {
|
if (msg.captured) {
|
||||||
app.npc.x = Math.round(msg.captured.x * 100) / 100;
|
var rx = Math.round(msg.captured.x * 100) / 100;
|
||||||
app.npc.y = Math.round(msg.captured.y * 100) / 100;
|
var ry = Math.round(msg.captured.y * 100) / 100;
|
||||||
app.npc.z = Math.round(msg.captured.z * 100) / 100;
|
var rz = Math.round(msg.captured.z * 100) / 100;
|
||||||
app.npc.heading = Math.round(msg.captured.heading * 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;
|
break;
|
||||||
case 'CLOSE':
|
case 'CLOSE':
|
||||||
|
|||||||
@ -45,7 +45,8 @@ end)
|
|||||||
|
|
||||||
-- ── NPC-Position synchronisieren ──────────────────────────────
|
-- ── 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)
|
AddEventHandler('onResourceStart', function(res)
|
||||||
if res ~= GetCurrentResourceName() then return end
|
if res ~= GetCurrentResourceName() then return end
|
||||||
@ -58,13 +59,20 @@ AddEventHandler('onResourceStart', function(res)
|
|||||||
`x` FLOAT DEFAULT 0,
|
`x` FLOAT DEFAULT 0,
|
||||||
`y` FLOAT DEFAULT 0,
|
`y` FLOAT DEFAULT 0,
|
||||||
`z` 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
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||||
]])
|
]])
|
||||||
-- Dann lesen
|
-- Dann lesen
|
||||||
local r = MySQL.query.await("SELECT * FROM mercyv_bike_npc LIMIT 1")
|
local r = MySQL.query.await("SELECT * FROM mercyv_bike_npc LIMIT 1")
|
||||||
if r and r[1] then
|
if r and r[1] then
|
||||||
NPCData = r[1]
|
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
|
else
|
||||||
NPCData = {
|
NPCData = {
|
||||||
model = Config.NPC.model,
|
model = Config.NPC.model,
|
||||||
@ -117,7 +125,7 @@ RegisterNetEvent('mercyv-bike:claim', function(bikeModel)
|
|||||||
)
|
)
|
||||||
|
|
||||||
-- Spawn-Signal
|
-- Spawn-Signal
|
||||||
TriggerClientEvent('mercyv-bike:doSpawn', src, bikeModel)
|
TriggerClientEvent('mercyv-bike:doSpawn', src, bikeModel, SpawnData)
|
||||||
Config.ServerNotification(src, Config.Notify.CLAIMED, "success")
|
Config.ServerNotification(src, Config.Notify.CLAIMED, "success")
|
||||||
print(string.format('[mercyv-bike] %s hat %s erhalten.', identifier, bikeModel))
|
print(string.format('[mercyv-bike] %s hat %s erhalten.', identifier, bikeModel))
|
||||||
end)
|
end)
|
||||||
@ -148,15 +156,22 @@ RegisterNetEvent('mercyv-bike:saveNPC', function(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
NPCData = data
|
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
|
-- In DB speichern
|
||||||
local existing = MySQL.query.await('SELECT id FROM mercyv_bike_npc LIMIT 1')
|
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
|
if existing and existing[1] then
|
||||||
MySQL.update('UPDATE mercyv_bike_npc SET model=?, x=?, y=?, z=?, heading=? WHERE 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, existing[1].id })
|
{ data.model, data.x, data.y, data.z, data.heading, sx, sy, sz, sh, existing[1].id })
|
||||||
else
|
else
|
||||||
MySQL.insert('INSERT INTO mercyv_bike_npc (model, x, y, z, heading) VALUES (?, ?, ?, ?, ?)',
|
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 })
|
{ data.model, data.x, data.y, data.z, data.heading, sx, sy, sz, sh })
|
||||||
end
|
end
|
||||||
|
|
||||||
TriggerClientEvent('mercyv-bike:syncNPC', -1, NPCData)
|
TriggerClientEvent('mercyv-bike:syncNPC', -1, NPCData)
|
||||||
|
|||||||
@ -703,7 +703,7 @@ AddEventHandler('mercyv-deathscreen:client:npcReviveTeleport', function()
|
|||||||
local ped = PlayerPedId()
|
local ped = PlayerPedId()
|
||||||
|
|
||||||
-- Kurz warten, damit der Spieler nach dem Revive sicher am Leben ist
|
-- 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)
|
-- Bildschirm langsam schwarz ausblenden (Dauer: 1000 Millisekunden = 1 Sekunde)
|
||||||
DoScreenFadeOut(1000)
|
DoScreenFadeOut(1000)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user