forked from Simnation/Main
ed
This commit is contained in:
parent
305602602e
commit
99aed29a52
4 changed files with 415 additions and 0 deletions
175
resources/[carscripts]/nordi_rental/client.lua
Normal file
175
resources/[carscripts]/nordi_rental/client.lua
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||||||
|
local spawnedNPCs = {}
|
||||||
|
local currentRental = nil
|
||||||
|
|
||||||
|
-- NPCs spawnen
|
||||||
|
CreateThread(function()
|
||||||
|
for i = 1, #Config.RentalLocations do
|
||||||
|
local location = Config.RentalLocations[i]
|
||||||
|
|
||||||
|
RequestModel(location.npc.model)
|
||||||
|
while not HasModelLoaded(location.npc.model) do
|
||||||
|
Wait(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local npc = CreatePed(4, location.npc.model, location.npc.coords.x, location.npc.coords.y, location.npc.coords.z - 1.0, location.npc.coords.w, false, true)
|
||||||
|
FreezeEntityPosition(npc, true)
|
||||||
|
SetEntityInvincible(npc, true)
|
||||||
|
SetBlockingOfNonTemporaryEvents(npc, true)
|
||||||
|
|
||||||
|
spawnedNPCs[location.id] = npc
|
||||||
|
|
||||||
|
-- QB-Target für NPC
|
||||||
|
exports['qb-target']:AddTargetEntity(npc, {
|
||||||
|
options = {
|
||||||
|
{
|
||||||
|
type = "client",
|
||||||
|
event = "vehiclerental:client:openMenu",
|
||||||
|
icon = "fas fa-car",
|
||||||
|
label = "Fahrzeug mieten",
|
||||||
|
locationId = location.id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "client",
|
||||||
|
event = "vehiclerental:client:returnVehicle",
|
||||||
|
icon = "fas fa-car-side",
|
||||||
|
label = "Fahrzeug zurückgeben",
|
||||||
|
locationId = location.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
distance = 2.0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Mietmenü öffnen
|
||||||
|
RegisterNetEvent('vehiclerental:client:openMenu', function(data)
|
||||||
|
local locationId = data.locationId
|
||||||
|
local location = nil
|
||||||
|
|
||||||
|
for i = 1, #Config.RentalLocations do
|
||||||
|
if Config.RentalLocations[i].id == locationId then
|
||||||
|
location = Config.RentalLocations[i]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not location then return end
|
||||||
|
|
||||||
|
local options = {}
|
||||||
|
for i = 1, #location.vehicles do
|
||||||
|
local vehicle = location.vehicles[i]
|
||||||
|
table.insert(options, {
|
||||||
|
title = vehicle.label,
|
||||||
|
description = '$' .. vehicle.price .. ' pro Stunde',
|
||||||
|
icon = 'car',
|
||||||
|
onSelect = function()
|
||||||
|
openRentalDialog(vehicle, location)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.registerContext({
|
||||||
|
id = 'vehicle_rental_menu',
|
||||||
|
title = location.name,
|
||||||
|
options = options
|
||||||
|
})
|
||||||
|
|
||||||
|
lib.showContext('vehicle_rental_menu')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Mietdialog
|
||||||
|
function openRentalDialog(vehicle, location)
|
||||||
|
local input = lib.inputDialog('Fahrzeug mieten', {
|
||||||
|
{
|
||||||
|
type = 'number',
|
||||||
|
label = 'Mietdauer (Stunden)',
|
||||||
|
description = 'Maximale Mietdauer: ' .. Config.MaxRentalTime .. ' Stunden',
|
||||||
|
required = true,
|
||||||
|
min = 1,
|
||||||
|
max = Config.MaxRentalTime
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if not input or not input[1] then return end
|
||||||
|
|
||||||
|
local hours = tonumber(input[1])
|
||||||
|
if not hours or hours < 1 or hours > Config.MaxRentalTime then
|
||||||
|
QBCore.Functions.Notify('Ungültige Mietdauer!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local totalCost = vehicle.price * hours
|
||||||
|
local plate = GeneratePlate()
|
||||||
|
|
||||||
|
QBCore.Functions.TriggerCallback('vehiclerental:server:rentVehicle', function(success)
|
||||||
|
if success then
|
||||||
|
spawnRentalVehicle(vehicle.model, location.spawnPoint, plate)
|
||||||
|
end
|
||||||
|
end, {
|
||||||
|
vehicleModel = vehicle.model,
|
||||||
|
pricePerHour = vehicle.price,
|
||||||
|
hours = hours,
|
||||||
|
locationId = location.id,
|
||||||
|
plate = plate
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Fahrzeug spawnen
|
||||||
|
function spawnRentalVehicle(model, spawnPoint, plate)
|
||||||
|
RequestModel(model)
|
||||||
|
while not HasModelLoaded(model) do
|
||||||
|
Wait(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local vehicle = CreateVehicle(model, spawnPoint.x, spawnPoint.y, spawnPoint.z, spawnPoint.w, true, false)
|
||||||
|
SetVehicleNumberPlateText(vehicle, plate)
|
||||||
|
SetEntityAsMissionEntity(vehicle, true, true)
|
||||||
|
TaskWarpPedIntoVehicle(PlayerPedId(), vehicle, -1)
|
||||||
|
|
||||||
|
TriggerEvent("vehiclekeys:client:SetOwner", plate)
|
||||||
|
SetModelAsNoLongerNeeded(model)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Fahrzeug zurückgeben
|
||||||
|
RegisterNetEvent('vehiclerental:client:returnVehicle', function(data)
|
||||||
|
local ped = PlayerPedId()
|
||||||
|
local vehicle = GetVehiclePedIsIn(ped, false)
|
||||||
|
|
||||||
|
if vehicle == 0 then
|
||||||
|
QBCore.Functions.Notify('Du musst in einem Fahrzeug sitzen!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local plate = GetVehicleNumberPlateText(vehicle)
|
||||||
|
|
||||||
|
QBCore.Functions.TriggerCallback('vehiclerental:server:returnVehicle', function(success)
|
||||||
|
if success then
|
||||||
|
DeleteVehicle(vehicle)
|
||||||
|
end
|
||||||
|
end, plate)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Kennzeichen generieren
|
||||||
|
function GeneratePlate()
|
||||||
|
local plate = ""
|
||||||
|
for i = 1, 8 do
|
||||||
|
if math.random(1, 2) == 1 then
|
||||||
|
plate = plate .. string.char(math.random(65, 90)) -- A-Z
|
||||||
|
else
|
||||||
|
plate = plate .. tostring(math.random(0, 9)) -- 0-9
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return plate
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleanup beim Ressourcen-Stop
|
||||||
|
AddEventHandler('onResourceStop', function(resourceName)
|
||||||
|
if GetCurrentResourceName() ~= resourceName then return end
|
||||||
|
|
||||||
|
for _, npc in pairs(spawnedNPCs) do
|
||||||
|
if DoesEntityExist(npc) then
|
||||||
|
DeleteEntity(npc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
62
resources/[carscripts]/nordi_rental/config.lua
Normal file
62
resources/[carscripts]/nordi_rental/config.lua
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
Config = {}
|
||||||
|
|
||||||
|
Config.MaxRentalTime = 24 -- Maximale Mietdauer in Stunden
|
||||||
|
Config.PenaltyPerHour = 100 -- Strafe pro Stunde Verspätung
|
||||||
|
Config.UseOkokBanking = true -- true für okokBanking, false für Bargeld
|
||||||
|
|
||||||
|
Config.RentalLocations = {
|
||||||
|
{
|
||||||
|
id = "rental_1",
|
||||||
|
name = "Downtown Autovermietung",
|
||||||
|
npc = {
|
||||||
|
model = "a_m_m_business_01",
|
||||||
|
coords = vector4(-56.79, -1096.58, 26.42, 25.0),
|
||||||
|
},
|
||||||
|
spawnPoint = vector4(-66.79, -1096.58, 26.42, 25.0),
|
||||||
|
returnPoint = vector4(-66.79, -1096.58, 26.42, 25.0),
|
||||||
|
vehicles = {
|
||||||
|
{
|
||||||
|
model = "blista",
|
||||||
|
label = "Blista",
|
||||||
|
price = 50, -- Preis pro Stunde
|
||||||
|
category = "compacts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model = "sultan",
|
||||||
|
label = "Sultan",
|
||||||
|
price = 75,
|
||||||
|
category = "sedans"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model = "baller",
|
||||||
|
label = "Baller",
|
||||||
|
price = 100,
|
||||||
|
category = "suvs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id = "rental_2",
|
||||||
|
name = "Sandy Shores Autovermietung",
|
||||||
|
npc = {
|
||||||
|
model = "a_m_m_business_01",
|
||||||
|
coords = vector4(1737.59, 3710.2, 34.14, 25.0),
|
||||||
|
},
|
||||||
|
spawnPoint = vector4(1727.59, 3710.2, 34.14, 25.0),
|
||||||
|
returnPoint = vector4(1727.59, 3710.2, 34.14, 25.0),
|
||||||
|
vehicles = {
|
||||||
|
{
|
||||||
|
model = "rebel",
|
||||||
|
label = "Rebel",
|
||||||
|
price = 80,
|
||||||
|
category = "offroad"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model = "sandking",
|
||||||
|
label = "Sandking",
|
||||||
|
price = 120,
|
||||||
|
category = "offroad"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
resources/[carscripts]/nordi_rental/fxmanifest.lua
Normal file
0
resources/[carscripts]/nordi_rental/fxmanifest.lua
Normal file
178
resources/[carscripts]/nordi_rental/server.lua
Normal file
178
resources/[carscripts]/nordi_rental/server.lua
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||||||
|
|
||||||
|
-- Datenbank Tabelle erstellen
|
||||||
|
MySQL.ready(function()
|
||||||
|
MySQL.Async.execute([[
|
||||||
|
CREATE TABLE IF NOT EXISTS vehicle_rentals (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
citizenid VARCHAR(50) NOT NULL,
|
||||||
|
vehicle_model VARCHAR(50) NOT NULL,
|
||||||
|
vehicle_plate VARCHAR(10) NOT NULL,
|
||||||
|
rental_location VARCHAR(50) NOT NULL,
|
||||||
|
start_time BIGINT NOT NULL,
|
||||||
|
end_time BIGINT NOT NULL,
|
||||||
|
price_per_hour INT NOT NULL,
|
||||||
|
returned BOOLEAN DEFAULT FALSE,
|
||||||
|
penalty_paid BOOLEAN DEFAULT FALSE,
|
||||||
|
INDEX(citizenid),
|
||||||
|
INDEX(vehicle_plate)
|
||||||
|
)
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Fahrzeug mieten
|
||||||
|
QBCore.Functions.CreateCallback('vehiclerental:server:rentVehicle', function(source, cb, data)
|
||||||
|
local Player = QBCore.Functions.GetPlayer(source)
|
||||||
|
if not Player then return cb(false) end
|
||||||
|
|
||||||
|
local totalCost = data.pricePerHour * data.hours
|
||||||
|
local currentTime = os.time()
|
||||||
|
local endTime = currentTime + (data.hours * 3600)
|
||||||
|
|
||||||
|
-- Zahlung prüfen und abziehen
|
||||||
|
local paymentSuccess = false
|
||||||
|
if Config.UseOkokBanking then
|
||||||
|
local bankMoney = exports['okokBanking']:GetAccount(Player.PlayerData.citizenid)
|
||||||
|
if bankMoney and bankMoney >= totalCost then
|
||||||
|
exports['okokBanking']:RemoveMoney(Player.PlayerData.citizenid, totalCost)
|
||||||
|
paymentSuccess = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Player.Functions.RemoveMoney('cash', totalCost) then
|
||||||
|
paymentSuccess = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not paymentSuccess then
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Nicht genug Geld!', 'error')
|
||||||
|
return cb(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fahrzeug in Datenbank eintragen
|
||||||
|
MySQL.Async.insert('INSERT INTO vehicle_rentals (citizenid, vehicle_model, vehicle_plate, rental_location, start_time, end_time, price_per_hour) VALUES (?, ?, ?, ?, ?, ?, ?)', {
|
||||||
|
Player.PlayerData.citizenid,
|
||||||
|
data.vehicleModel,
|
||||||
|
data.plate,
|
||||||
|
data.locationId,
|
||||||
|
currentTime,
|
||||||
|
endTime,
|
||||||
|
data.pricePerHour
|
||||||
|
})
|
||||||
|
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich gemietet für $' .. totalCost, 'success')
|
||||||
|
cb(true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Fahrzeug zurückgeben
|
||||||
|
QBCore.Functions.CreateCallback('vehiclerental:server:returnVehicle', function(source, cb, plate)
|
||||||
|
local Player = QBCore.Functions.GetPlayer(source)
|
||||||
|
if not Player then return cb(false) end
|
||||||
|
|
||||||
|
MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND vehicle_plate = ? AND returned = FALSE', {
|
||||||
|
Player.PlayerData.citizenid,
|
||||||
|
plate
|
||||||
|
}, function(result)
|
||||||
|
if not result[1] then
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Kein aktives Mietverhältnis für dieses Fahrzeug gefunden!', 'error')
|
||||||
|
return cb(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
local rental = result[1]
|
||||||
|
local currentTime = os.time()
|
||||||
|
local penalty = 0
|
||||||
|
|
||||||
|
-- Strafe berechnen wenn verspätet
|
||||||
|
if currentTime > rental.end_time then
|
||||||
|
local hoursLate = math.ceil((currentTime - rental.end_time) / 3600)
|
||||||
|
penalty = hoursLate * Config.PenaltyPerHour
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Strafe abziehen falls vorhanden
|
||||||
|
if penalty > 0 then
|
||||||
|
local penaltyPaid = false
|
||||||
|
if Config.UseOkokBanking then
|
||||||
|
local bankMoney = exports['okokBanking']:GetAccount(Player.PlayerData.citizenid)
|
||||||
|
if bankMoney and bankMoney >= penalty then
|
||||||
|
exports['okokBanking']:RemoveMoney(Player.PlayerData.citizenid, penalty)
|
||||||
|
penaltyPaid = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Player.Functions.RemoveMoney('cash', penalty) then
|
||||||
|
penaltyPaid = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if penaltyPaid then
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Verspätungsstrafe von $' .. penalty .. ' wurde abgezogen!', 'error')
|
||||||
|
else
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Nicht genug Geld für Verspätungsstrafe!', 'error')
|
||||||
|
return cb(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mietverhältnis als zurückgegeben markieren
|
||||||
|
MySQL.Async.execute('UPDATE vehicle_rentals SET returned = TRUE, penalty_paid = ? WHERE id = ?', {
|
||||||
|
penalty > 0,
|
||||||
|
rental.id
|
||||||
|
})
|
||||||
|
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Fahrzeug erfolgreich zurückgegeben!', 'success')
|
||||||
|
cb(true)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Mietzeit abfragen
|
||||||
|
QBCore.Functions.CreateCallback('vehiclerental:server:getRentalInfo', function(source, cb)
|
||||||
|
local Player = QBCore.Functions.GetPlayer(source)
|
||||||
|
if not Player then return cb(nil) end
|
||||||
|
|
||||||
|
MySQL.Async.fetchAll('SELECT * FROM vehicle_rentals WHERE citizenid = ? AND returned = FALSE', {
|
||||||
|
Player.PlayerData.citizenid
|
||||||
|
}, function(result)
|
||||||
|
if not result[1] then
|
||||||
|
return cb(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
local rentals = {}
|
||||||
|
for i = 1, #result do
|
||||||
|
local rental = result[i]
|
||||||
|
local currentTime = os.time()
|
||||||
|
local timeLeft = rental.end_time - currentTime
|
||||||
|
|
||||||
|
table.insert(rentals, {
|
||||||
|
vehicleModel = rental.vehicle_model,
|
||||||
|
plate = rental.vehicle_plate,
|
||||||
|
timeLeft = timeLeft,
|
||||||
|
isOverdue = timeLeft < 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
cb(rentals)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Befehl für Mietzeit
|
||||||
|
QBCore.Commands.Add('mietzeit', 'Zeige deine aktuelle Mietzeit an', {}, false, function(source, args)
|
||||||
|
QBCore.Functions.TriggerCallback('vehiclerental:server:getRentalInfo', source, function(rentals)
|
||||||
|
if not rentals or #rentals == 0 then
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, 'Du hast keine aktiven Mietverhältnisse!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #rentals do
|
||||||
|
local rental = rentals[i]
|
||||||
|
local timeText = ""
|
||||||
|
|
||||||
|
if rental.isOverdue then
|
||||||
|
local hoursOverdue = math.ceil(math.abs(rental.timeLeft) / 3600)
|
||||||
|
timeText = "Überfällig um " .. hoursOverdue .. " Stunden"
|
||||||
|
else
|
||||||
|
local hoursLeft = math.floor(rental.timeLeft / 3600)
|
||||||
|
local minutesLeft = math.floor((rental.timeLeft % 3600) / 60)
|
||||||
|
timeText = hoursLeft .. "h " .. minutesLeft .. "m verbleibend"
|
||||||
|
end
|
||||||
|
|
||||||
|
TriggerClientEvent('QBCore:Notify', source, rental.vehicleModel .. " (" .. rental.plate .. "): " .. timeText, rental.isOverdue and 'error' or 'primary')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
Loading…
Add table
Add a link
Reference in a new issue