first commit
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
local activeRobberies = {}
|
||||
local prepSessions = {}
|
||||
local globalCooldownUntil = 0
|
||||
|
||||
local function nearLocation(source, coords, maximum)
|
||||
local ped = GetPlayerPed(source)
|
||||
if not ped or ped <= 0 then return false end
|
||||
local playerCoords = GetEntityCoords(ped)
|
||||
return #(playerCoords - vector3(coords.x, coords.y, coords.z)) <= maximum
|
||||
end
|
||||
|
||||
local function findPrep(key)
|
||||
for _, entry in ipairs(DevXCrimeConfig.PrepLocations) do
|
||||
if entry.key == key then return entry end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function getProgress(characterId)
|
||||
local row = MySQL.single.await([[
|
||||
SELECT drill_done AS drillDone, card_done AS cardDone, vehicle_done AS vehicleDone
|
||||
FROM devx_heist_progress WHERE character_id = ? LIMIT 1
|
||||
]], { characterId })
|
||||
if not row then
|
||||
MySQL.insert.await('INSERT IGNORE INTO devx_heist_progress (character_id) VALUES (?)', { characterId })
|
||||
row = { drillDone = 0, cardDone = 0, vehicleDone = 0 }
|
||||
end
|
||||
return {
|
||||
drill = tonumber(row.drillDone) == 1,
|
||||
card = tonumber(row.cardDone) == 1,
|
||||
vehicle = tonumber(row.vehicleDone) == 1
|
||||
}
|
||||
end
|
||||
|
||||
local function leavePrep(source)
|
||||
prepSessions[source] = nil
|
||||
SetPlayerRoutingBucket(source, 0)
|
||||
end
|
||||
|
||||
MySQL.ready(function()
|
||||
MySQL.query.await([[
|
||||
CREATE TABLE IF NOT EXISTS devx_heist_progress (
|
||||
character_id BIGINT UNSIGNED NOT NULL,
|
||||
drill_done TINYINT(1) NOT NULL DEFAULT 0,
|
||||
card_done TINYINT(1) NOT NULL DEFAULT 0,
|
||||
vehicle_done TINYINT(1) NOT NULL DEFAULT 0,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (character_id),
|
||||
CONSTRAINT fk_devx_heist_character FOREIGN KEY (character_id)
|
||||
REFERENCES devx_characters (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
]])
|
||||
end)
|
||||
|
||||
DevXRegisterCallback('crime:status', function(source)
|
||||
local player = exports.devx_core:GetPlayer(source)
|
||||
if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end
|
||||
return {
|
||||
ok = true,
|
||||
progress = getProgress(player.characterId),
|
||||
cooldownSeconds = math.max(0, globalCooldownUntil - os.time()),
|
||||
active = activeRobberies[source] ~= nil
|
||||
}
|
||||
end)
|
||||
|
||||
DevXRegisterCallback('crime:enterPrep', function(source, payload)
|
||||
local player = exports.devx_core:GetPlayer(source)
|
||||
local prep = findPrep(tostring(payload.key or ''))
|
||||
if not player or not prep then return { ok = false, error = 'Ungültige Vorbereitungsmission.' } end
|
||||
if not nearLocation(source, prep.exterior, 6.0) then return { ok = false, error = 'Du bist nicht am Lagereingang.' } end
|
||||
|
||||
local bucket = DevXCrimeConfig.MissionBucketBase + tonumber(source)
|
||||
prepSessions[source] = { key = prep.key, exterior = prep.exterior, bucket = bucket }
|
||||
SetRoutingBucketPopulationEnabled(bucket, false)
|
||||
SetRoutingBucketEntityLockdownMode(bucket, 'relaxed')
|
||||
SetPlayerRoutingBucket(source, bucket)
|
||||
return { ok = true, interior = DevXCrimeConfig.PrepInterior, prep = prep }
|
||||
end)
|
||||
|
||||
DevXRegisterCallback('crime:leavePrep', function(source)
|
||||
local session = prepSessions[source]
|
||||
if not session then return { ok = false, error = 'Keine Vorbereitungsmission aktiv.' } end
|
||||
local exterior = session.exterior
|
||||
leavePrep(source)
|
||||
return { ok = true, exterior = exterior }
|
||||
end)
|
||||
|
||||
DevXRegisterCallback('crime:completePrep', function(source, payload)
|
||||
local player = exports.devx_core:GetPlayer(source)
|
||||
local key = tostring(payload.key or '')
|
||||
local prep = findPrep(key)
|
||||
local session = prepSessions[source]
|
||||
if not player or not prep or not session or session.key ~= key then
|
||||
return { ok = false, error = 'Keine passende Vorbereitungsmission aktiv.' }
|
||||
end
|
||||
if not nearLocation(source, prep.objective, 5.0) then return { ok = false, error = 'Du bist nicht am Missionsziel.' } end
|
||||
|
||||
local column = ({ drill = 'drill_done', card = 'card_done', vehicle = 'vehicle_done' })[key]
|
||||
MySQL.query.await('INSERT IGNORE INTO devx_heist_progress (character_id) VALUES (?)', { player.characterId })
|
||||
MySQL.update.await(('UPDATE devx_heist_progress SET %s = 1 WHERE character_id = ?'):format(column), { player.characterId })
|
||||
|
||||
local exterior = session.exterior
|
||||
leavePrep(source)
|
||||
return { ok = true, progress = getProgress(player.characterId), exterior = exterior }
|
||||
end)
|
||||
|
||||
DevXRegisterCallback('crime:startRobbery', function(source)
|
||||
local player = exports.devx_core:GetPlayer(source)
|
||||
if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end
|
||||
if not nearLocation(source, DevXCrimeConfig.Bank, 6.0) then return { ok = false, error = 'Du bist nicht im Tresorbereich.' } end
|
||||
if activeRobberies[source] then return { ok = false, error = 'Der Raub läuft bereits.' } end
|
||||
if os.time() < globalCooldownUntil then
|
||||
return { ok = false, error = ('Die Bank ist noch %s Minuten gesperrt.'):format(math.ceil((globalCooldownUntil - os.time()) / 60)) }
|
||||
end
|
||||
|
||||
local progress = getProgress(player.characterId)
|
||||
if not progress.drill or not progress.card or not progress.vehicle then
|
||||
return { ok = false, error = 'Schließe zuerst alle drei Vorbereitungsmissionen ab.' }
|
||||
end
|
||||
|
||||
local duration = DevXCrimeConfig.RobberyDurationSeconds
|
||||
activeRobberies[source] = { characterId = player.characterId, completeAt = os.time() + duration }
|
||||
globalCooldownUntil = os.time() + DevXCrimeConfig.GlobalCooldownSeconds
|
||||
return { ok = true, duration = duration }
|
||||
end)
|
||||
|
||||
DevXRegisterCallback('crime:finishRobbery', function(source)
|
||||
local robbery = activeRobberies[source]
|
||||
local player = exports.devx_core:GetPlayer(source)
|
||||
if not robbery or not player or robbery.characterId ~= player.characterId then
|
||||
return { ok = false, error = 'Kein aktiver Bankraub.' }
|
||||
end
|
||||
if os.time() < robbery.completeAt then return { ok = false, error = 'Der Tresor ist noch nicht geöffnet.' } end
|
||||
if not nearLocation(source, DevXCrimeConfig.Bank, 12.0) then
|
||||
activeRobberies[source] = nil
|
||||
return { ok = false, error = 'Du hast den Bankraubbereich verlassen.' }
|
||||
end
|
||||
|
||||
local reward = math.random(DevXCrimeConfig.MinimumReward, DevXCrimeConfig.MaximumReward)
|
||||
exports.devx_core:AddCash(source, reward, 'Fleeca-Bankraub')
|
||||
exports.devx_core:AddMetadataNumber(source, 'rpXp', 75)
|
||||
MySQL.update.await([[
|
||||
UPDATE devx_heist_progress
|
||||
SET drill_done = 0, card_done = 0, vehicle_done = 0
|
||||
WHERE character_id = ?
|
||||
]], { player.characterId })
|
||||
activeRobberies[source] = nil
|
||||
return { ok = true, reward = reward, xp = 75 }
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function()
|
||||
activeRobberies[source] = nil
|
||||
leavePrep(source)
|
||||
end)
|
||||
|
||||
AddEventHandler('onResourceStop', function(resourceName)
|
||||
if resourceName ~= GetCurrentResourceName() then return end
|
||||
for _, playerId in ipairs(GetPlayers()) do SetPlayerRoutingBucket(tonumber(playerId), 0) end
|
||||
end)
|
||||
|
||||
RegisterCommand('resetheist', function(source)
|
||||
if source ~= 0 and not IsPlayerAceAllowed(source, 'devx.admin') then return end
|
||||
globalCooldownUntil = 0
|
||||
activeRobberies = {}
|
||||
end, false)
|
||||
Reference in New Issue
Block a user