Web-Entwickler mit Schwerpunkt auf Full-Stack Applikationen, Server-Infrastruktur und RP-Tools โ und dem Willen, auch im FiveM Scripting zu wachsen.
Ich bin Felix Fรถrster aus Kรถln und entwickle seit mehreren Jahren Software. Mein Hauptfokus liegt auf Web-Entwicklung โ von kompletten Next.js Applikationen รผber REST APIs bis hin zur Server-Infrastruktur.
Im FiveM Bereich bringe ich bereits Erfahrung mit: Ich habe auf mehreren Servern als Supporter gearbeitet und kenne daher die Ablรคufe, Strukturen und hรคufigen Probleme aus der Praxis โ nicht nur aus Spielersicht.
Technisch habe ich eigenstรคndig ein Tablet/Phone UI Script in Lua entwickelt und mit dem Transporter-Script auf dieser Seite bewiesen, dass ich auch komplexere Systeme umsetzen kann.
Meine Kernstรคrke liegt in Web-Systemen die nahtlos mit FiveM zusammenarbeiten โ Panels, Dashboards, APIs. Genau das ist oft der fehlende Baustein bei RP-Servern.
Vom FiveM Lua Script รผber Next.js Webapps bis zur Server-Konfiguration โ ich bin Full-Stack aufgestellt.
Eine Auswahl meiner Projekte โ alle live auf meinem eigenen VPS gehostet.
Ein vollstรคndiges FiveM Ressource-Script โ einsatzbereit fรผr ESX und QBCore.
Config = {}
-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-- Allgemeine Einstellungen
-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Config.Cooldown = 45 -- Minuten bis der nรคchste Transporter spawnt
Config.MinPoliceOnline = 2 -- Mindestanzahl Polizisten die online sein mรผssen
Config.DrillTime = 90 -- Sekunden zum Aufbohren des Tresors
Config.LootTime = 8 -- Sekunden um einen Gegenstand einzusammeln
Config.StoppedSpeed = 5.0 -- km/h โ gilt als "gestoppt"
Config.StopDuration = 4 -- Sekunden die der Truck still stehen muss
Config.MaxLootBags = 4 -- Maximale Anzahl an Loot-Taschen im Truck
-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-- Spawn-Routen
-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Config.Routes = {
{
start = vector4( 1035.3, -651.6, 58.2, 270.0),
target = vector3( 240.7, 214.0, 105.9),
label = "Downtown Los Santos"
},
{
start = vector4( -120.5, 6411.4, 31.5, 180.0),
target = vector3( -247.0, 6329.4, 32.4),
label = "Paleto Bay"
},
{
start = vector4( 1685.4, 4920.3, 42.1, 90.0),
target = vector3( 1854.0, 3687.0, 34.2),
label = "Sandy Shores"
},
}
-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-- Beute
-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Config.Loot = {
{ item = "money_bag", label = "Geldtasche", min = 5000, max = 15000, chance = 100 },
{ item = "gold_bar", label = "Goldbarren", min = 1, max = 3, chance = 35 },
{ item = "diamond", label = "Diamant", min = 1, max = 2, chance = 15 },
}
Config.PoliceJobs = { "police", "sheriff", "bcso" }
-- Client โ Gepanzerter Transporter
-- motofo20
local truck, truckBlip, truckDriver = nil, nil, nil
local isStopped, isDrilling = false, false
local vaultBags, bagProps = {}, {}
local stopTimer = 0
-- โโ Spawn โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RegisterNetEvent('transporter:client:startRun', function(route)
spawnTruck(route)
end)
function spawnTruck(route)
local model = `stockade`
RequestModel(model)
while not HasModelLoaded(model) do Wait(100) end
truck = CreateVehicle(model,
route.start.x, route.start.y, route.start.z, route.start.w,
true, false)
SetVehicleNumberPlateText(truck, "SICHER")
SetVehicleEngineOn(truck, true, true, false)
SetVehicleDoorsLocked(truck, 2)
-- NPC Fahrer
local driverModel = `s_m_y_armoured_01`
RequestModel(driverModel)
while not HasModelLoaded(driverModel) do Wait(100) end
truckDriver = CreatePedInsideVehicle(truck, 26, driverModel, -1, true, false)
SetBlockingOfNonTemporaryEvents(truckDriver, true)
SetPedFleeAttributes(truckDriver, 0, false)
TaskVehicleDriveToCoordLongrange(
truckDriver, truck,
route.target.x, route.target.y, route.target.z,
45.0, 786603, 5.0)
-- Karten-Blip
truckBlip = AddBlipForEntity(truck)
SetBlipSprite(truckBlip, Config.Blip.sprite)
SetBlipColour(truckBlip, Config.Blip.color)
SetBlipFlashes(truckBlip, true)
CreateThread(monitorTruck)
end
-- โโ Monitor โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function monitorTruck()
while truck and DoesEntityExist(truck) do
Wait(500)
local speed = GetEntitySpeed(truck) * 3.6
local engineHP = GetVehicleEngineHealth(truck)
if not isStopped and speed < Config.StoppedSpeed and engineHP < 100 then
stopTimer = stopTimer + 0.5
if stopTimer >= Config.StopDuration then
isStopped = true
TriggerServerEvent('transporter:server:truckStopped')
showDrillPrompt()
end
elseif speed >= Config.StoppedSpeed then
stopTimer = 0
end
-- Ziel erreicht oder Truck zerstรถrt
local task = GetScriptTaskStatus(truckDriver, 0x6F0783C3)
if task == 7 or GetEntityHealth(truck) <= 0 then
TriggerServerEvent('transporter:server:endRun')
break
end
end
end
-- โโ Drill โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function showDrillPrompt()
CreateThread(function()
while truck and DoesEntityExist(truck) and isStopped and not isDrilling do
Wait(0)
local dist = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(truck))
if dist < 4.0 then
lib.showTextUI('[E] Tresor aufbohren', {
position = 'left-center', icon = 'screwdriver-wrench'
})
if IsControlJustReleased(0, 38) then
lib.hideTextUI()
beginDrill()
end
else
lib.hideTextUI()
end
end
lib.hideTextUI()
end)
end
function beginDrill()
if isDrilling then return end
isDrilling = true
local success = lib.progressBar({
duration = Config.DrillTime * 1000,
label = 'Tresor wird aufgebohrt โฆ',
useWhileDead = false,
canCancel = true,
disable = { car = true },
anim = { dict = 'anim@heists@fleeca_bank@drilling', clip = 'drill_idle' },
})
isDrilling = false
if success then
TriggerServerEvent('transporter:server:drillComplete')
for door = 0, 5 do SetVehicleDoorOpen(truck, door, false, false) end
lib.notify({ title = 'โ
Tresor geรถffnet!', type = 'success', duration = 6000 })
end
end
-- โโ Loot โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RegisterNetEvent('transporter:client:openVault', function(bags)
vaultBags = bags
for i, bag in ipairs(bags) do
local offset = GetOffsetFromEntityInWorldCoords(truck, (i-1)*0.6-0.9, -3.0, 0.0)
local prop = CreateObject(`prop_cash_bag_01`, offset.x, offset.y, offset.z, true, true, false)
PlaceObjectOnGroundProperly(prop)
bagProps[i] = prop
end
CreateThread(function()
while #bagProps > 0 do
Wait(0)
local playerCoords = GetEntityCoords(PlayerPedId())
local nearestDist, nearestIdx = 99.0, nil
for i, prop in ipairs(bagProps) do
if prop and DoesEntityExist(prop) then
local d = #(playerCoords - GetEntityCoords(prop))
if d < nearestDist then nearestDist = d; nearestIdx = i end
end
end
if nearestIdx and nearestDist < 2.5 then
lib.showTextUI(('[E] %s einsammeln'):format(vaultBags[nearestIdx].label))
if IsControlJustReleased(0, 38) then
lib.hideTextUI()
collectBag(nearestIdx, bagProps[nearestIdx])
end
else lib.hideTextUI() end
end
end)
end)
function collectBag(idx, prop)
local success = lib.progressBar({
duration = Config.LootTime * 1000,
label = ('Sammle %s ein โฆ'):format(vaultBags[idx].label),
canCancel = false,
disable = { move = true },
anim = { dict = 'anim@heists@box_carry@', clip = 'idle' },
})
if success then
TriggerServerEvent('transporter:server:collectBag', idx, vaultBags)
DeleteObject(prop)
bagProps[idx] = nil; vaultBags[idx] = nil
end
end
-- Server โ Gepanzerter Transporter
-- motofo20
local Core = nil
local lastSpawn = 0
local activeRun = false
CreateThread(function()
Wait(500)
if Framework == 'ESX' then
Core = exports['es_extended']:getSharedObject()
elseif Framework == 'QBCore' then
Core = exports['qb-core']:GetCoreObject()
end
end)
-- Polizei-Anzahl ermitteln
local function getPoliceCount()
local count = 0
for _, player in ipairs(GetPlayers()) do
local src = tonumber(player)
if Framework == 'ESX' then
local xPlayer = Core.GetPlayerFromId(src)
if xPlayer then
local job = xPlayer.getJob()
for _, j in ipairs(Config.PoliceJobs) do
if job.name == j then count = count + 1; break end
end
end
elseif Framework == 'QBCore' then
local Player = Core.Functions.GetPlayer(src)
if Player then
for _, j in ipairs(Config.PoliceJobs) do
if Player.PlayerData.job.name == j then count = count + 1; break end
end
end
end
end
return count
end
-- Loot auswรผrfeln
local function rollLoot()
for _, loot in ipairs(Config.Loot) do
if math.random(1, 100) <= loot.chance then
return { item = loot.item, label = loot.label,
amount = math.random(loot.min, loot.max) }
end
end
return { item = "money_bag", label = "Geldtasche", amount = math.random(2000, 8000) }
end
-- Item geben (ESX & QBCore)
local function giveItem(src, item, amount)
if Framework == 'ESX' then
local xPlayer = Core.GetPlayerFromId(src)
if xPlayer then xPlayer.addInventoryItem(item, amount) end
elseif Framework == 'QBCore' then
local Player = Core.Functions.GetPlayer(src)
if Player then Player.Functions.AddItem(item, amount) end
end
end
-- โโ Spawn-Loop โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CreateThread(function()
Wait(10000) -- Server warm-up
while true do
local elapsed = (os.time() - lastSpawn) / 60
if not activeRun
and elapsed >= Config.Cooldown
and #GetPlayers() >= 1
and getPoliceCount() >= Config.MinPoliceOnline
then
local route = Config.Routes[math.random(1, #Config.Routes)]
lastSpawn = os.time()
activeRun = true
TriggerClientEvent('transporter:client:startRun', -1, route)
TriggerClientEvent('transporter:client:policeDispatch', -1, route.label, route.start)
end
Wait(60000)
end
end)
-- โโ Events โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RegisterNetEvent('transporter:server:truckStopped', function()
TriggerClientEvent('transporter:client:truckStoppedAlert', -1)
end)
RegisterNetEvent('transporter:server:drillComplete', function()
local src = source
local bags = {}
for i = 1, Config.MaxLootBags do bags[i] = rollLoot() end
TriggerClientEvent('transporter:client:openVault', src, bags)
end)
RegisterNetEvent('transporter:server:collectBag', function(bagIndex, bags)
local src = source
local bag = bags[bagIndex]
if not bag then return end
giveItem(src, bag.item, bag.amount)
MySQL.insert(
'INSERT INTO transporter_logs (player_id, item, amount, timestamp) VALUES (?, ?, ?, NOW())',
{ src, bag.item, bag.amount }
)
end)
RegisterNetEvent('transporter:server:endRun', function()
activeRun = false
TriggerClientEvent('transporter:client:cleanup', -1)
end)
-- motofo20-transporter โ Datenbank-Schema
-- Importieren mit: mysql -u root -p dein_db < transporter.sql
CREATE TABLE IF NOT EXISTS `transporter_logs` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`player_id` INT(11) NOT NULL COMMENT 'Server-ID des Spielers beim Einsammeln',
`item` VARCHAR(64) NOT NULL COMMENT 'Item-Name aus der Config',
`amount` INT(11) NOT NULL COMMENT 'Menge des erbeuteten Items',
`timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_timestamp` (`timestamp`),
INDEX `idx_player` (`player_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='Loggt alle Beute-Ereignisse des Transporter-Scripts';
-- Optionaler View: Beute-Statistik pro Spieler
CREATE OR REPLACE VIEW `transporter_stats` AS
SELECT
player_id,
item,
COUNT(*) AS anzahl_erbeutungen,
SUM(amount) AS gesamtmenge,
MAX(timestamp) AS zuletzt_aktiv
FROM `transporter_logs`
GROUP BY player_id, item
ORDER BY gesamtmenge DESC;
fx_version 'cerulean'
game 'gta5'
author 'motofo20'
description 'Gepanzerter Transporter รberfall โ ESX & QBCore kompatibel'
version '1.0.0'
shared_scripts {
'@ox_lib/init.lua',
'shared/framework.lua',
'config.lua'
}
client_scripts {
'client/main.lua'
}
server_scripts {
'@oxmysql/lib/MySQL.lua',
'server/main.lua'
}
dependencies {
'ox_lib',
'oxmysql'
}
Am schnellsten erreichst du mich รผber Discord. Ich freue mich รผber jede Rรผckmeldung zur Bewerbung.
motofo20