FiveM 抢劫与劫案系统:银行、商店与金库
奖励FiveM抢劫和劫案的脚本。包括Fleeca、Paleto、珠宝店、警察以及适合硬核角色扮演服务器的最佳劫案脚本。
Agency Scripts
Agency Scripts 创始人兼首席开发者
抢劫系统架构概述
抢劫和劫案是任何FiveM角色扮演服务器中最刺激的游戏循环之一。它们汇聚了策划复杂行动的罪犯、战术精准响应的警察、管理紧张对峙的人质谈判员以及被卷入其中的平民。一个设计良好的抢劫系统远不止简单的交互提示给玩家金钱。它需要分层架构来处理地点管理、前置条件检查、渐进式小游戏阶段、战利品分配、警察调度集成、冷却管理和防作弊保护。系统应由三层核心组成:定义每个抢劫地点及其要求和奖励的配置层、跟踪活跃抢劫并执行所有规则的服务器端状态机,以及渲染小游戏、动画和NUI界面的客户端展示层。所有关键决策必须在服务器端完成,因为客户端逻辑极易被利用。
配置抢劫地点和等级
您的抢劫系统应支持多种地点类型,具有递增难度等级。便利店代表入门级,需最少警力和基础工具。Fleeca 银行分支处于中间级,具备中等安全层和更高警力需求。Pacific Standard 保险库是顶级抢劫,需完整团队、高级设备和大量执法人员在线。每个地点需有配置条目,定义其坐标、所需物品、最低警力、冷却时间、奖励范围及必须击败的安全层序列:
Config.Robberies = {
['store_1'] = {
label = 'LTD Gasoline - Davis',
type = 'store',
coords = vector3(-47.02, -1757.23, 29.42),
register = vector3(-43.43, -1748.60, 29.42),
tier = 1,
cooldown = 1800, -- 30 minutes
minPolice = 2,
requiredItems = {'weapon_pistol'},
reward = {min = 2500, max = 6000, type = 'cash'},
securityLayers = {
{type = 'intimidate', time = 30},
{type = 'grab_cash', time = 15},
},
},
['fleeca_legion'] = {
label = 'Fleeca Bank - Legion Square',
type = 'bank',
coords = vector3(149.73, -1042.65, 29.37),
vault = vector3(144.87, -1044.16, 29.37),
tier = 2,
cooldown = 7200, -- 2 hours
minPolice = 4,
requiredItems = {'electronickit', 'thermite'},
reward = {min = 45000, max = 90000, type = 'markedbills'},
securityLayers = {
{type = 'hack', difficulty = 'medium', time = 25},
{type = 'thermite', time = 10},
{type = 'drill', time = 40},
},
},
['pacific_standard'] = {
label = 'Pacific Standard Bank',
type = 'vault',
coords = vector3(255.85, 225.60, 101.88),
vault = vector3(262.20, 222.10, 101.68),
tier = 3,
cooldown = 14400, -- 4 hours
minPolice = 6,
requiredItems = {'electronickit', 'thermite', 'advancedlaptop', 'duffelbag'},
reward = {min = 250000, max = 500000, type = 'markedbills'},
securityLayers = {
{type = 'hack', difficulty = 'hard', time = 20},
{type = 'hack', difficulty = 'hard', time = 20},
{type = 'thermite', time = 8},
{type = 'drill', time = 60},
{type = 'hack', difficulty = 'expert', time = 15},
},
},
}
每个等级不仅应在奖励上递增,也应在复杂度和风险上递增。商店抢劫是快速的砸抢行动,持续不到两分钟,而Pacific Standard抢劫是多阶段行动,可能需十五分钟或更长时间完成,给予警方充足时间设立警戒线。
服务器端状态机
抢劫状态机是系统核心。它跟踪每个抢劫地点的生命周期状态:空闲、进行中、冷却和锁定。当玩家发起抢劫时,服务器验证所有先决条件,然后将地点状态从空闲转为进行中。在进行中状态,服务器跟踪罪犯当前处于哪个安全层、参与玩家及经过时间。如果所有玩家离开区域或被逮捕,抢劫自动失败并进入缩短的冷却期。完成后,地点进入配置的冷却状态,冷却结束后重新开放:
local RobberyState = {}
function InitializeRobbery(robberyId)
RobberyState[robberyId] = {
status = 'idle', -- idle | active | cooldown | locked
participants = {},
currentLayer = 0,
startedAt = 0,
cooldownUntil = 0,
}
end
RegisterNetEvent('robbery:server:startRobbery', function(robberyId)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local config = Config.Robberies[robberyId]
local state = RobberyState[robberyId]
if not config or not state then return end
-- Validate state
if state.status ~= 'idle' then
TriggerClientEvent('QBCore:Notify', src, 'This location is not available', 'error')
return
end
-- Check cooldown
if os.time() < state.cooldownUntil then
TriggerClientEvent('QBCore:Notify', src, 'Location on cooldown', 'error')
return
end
-- Check minimum police online
local policeCount = GetOnlinePolicCount()
if policeCount < config.minPolice then
TriggerClientEvent('QBCore:Notify', src, 'Not enough police online', 'error')
return
end
-- Check required items
for _, item in ipairs(config.requiredItems) do
if not Player.Functions.GetItemByName(item) then
TriggerClientEvent('QBCore:Notify', src, 'Missing required equipment', 'error')
return
end
end
-- Start robbery
state.status = 'active'
state.participants = {src}
state.currentLayer = 1
state.startedAt = os.time()
-- Alert police dispatch
TriggerEvent('dispatch:server:notify', {
type = 'robbery',
coords = config.coords,
message = 'Silent alarm triggered at ' .. config.label,
tier = config.tier,
})
-- Send first security layer to client
TriggerClientEvent('robbery:client:startLayer', src, robberyId, config.securityLayers[1])
end)
状态机还应处理诸如服务器重启时正在进行的抢劫等边缘情况。服务器启动时,将所有抢劫状态重置为空闲,并清除所有活跃参与者。将冷却时间戳存储在持久表中,以便重启后仍然有效。
小游戏集成
小游戏将抢劫从被动等待转变为主动技能挑战。每种安全层类型应对应不同风格的小游戏,测试玩家不同能力。黑客小游戏可使用记忆匹配谜题、电路连接挑战或快速数字序列。热焰炸药放置需精准时机,在炸药熄灭前安放。钻孔机制考验玩家稳健的鼠标控制,需引导钻头穿过锁具机制且不使其过热。设计原则是更高难度层级应使用更复杂的小游戏变体,拥有更紧的时间窗口、更多元素需跟踪或更快速度:
-- Client-side minigame dispatcher
function StartMinigame(layerType, difficulty, callback)
if layerType == 'hack' then
local config = HackDifficulty[difficulty]
exports['ps-ui']:Circle(function(success)
callback(success)
end, config.circles, config.speed)
elseif layerType == 'thermite' then
exports['ps-ui']:Thermite(function(success)
callback(success)
end, 10, 5, 3)
elseif layerType == 'drill' then
exports['drilling']:StartDrill(function(success)
callback(success)
end)
elseif layerType == 'intimidate' then
-- Store clerk intimidation: aim weapon at NPC
StartClerkIntimidation(function(success)
callback(success)
end)
end
end
RegisterNetEvent('robbery:client:startLayer', function(robberyId, layer)
local ped = PlayerPedId()
-- Play appropriate animation
if layer.type == 'hack' then
PlayHackingAnimation(ped)
elseif layer.type == 'drill' then
PlayDrillingAnimation(ped)
end
StartMinigame(layer.type, layer.difficulty, function(success)
ClearPedTasks(ped)
TriggerServerEvent('robbery:server:layerResult', robberyId, success)
end)
end)
失败的小游戏尝试应有后果。一次失败的黑客攻击可能触发额外警报,减少剩余时间,或暂时锁定玩家以施加惩罚。对于高阶抢劫,最终黑客失败可能完全锁定金库,迫使团队放弃抢劫,只能带走部分战利品。这些失败状态增加紧张感,使成功完成真正令人满意。
警察调度与响应集成
抢劫系统若无警察集成则不完整。抢劫开始时,调度系统应根据抢劫等级发送分级警报。便利店抢劫触发简单的静默警报通知并标明店铺位置。Fleeca 分行触发银行警报,包含分行名称和建议响应级别。Pacific Standard 抢劫激活全面紧急广播,建议所有可用单位响应。为警察提供抢劫进展的实时更新:罪犯突破新安全层、建筑内开火或试图逃跑时。这些更新帮助警察协调响应,创造动态场景,警员可决定立即突入或设立包围圈并谈判。实现人质机制,罪犯可劫持 NPC 银行柜员作为人质,赋予警察谈判角色,并为罪犯争取延迟警察响应的筹码。人质系统应跟踪人质数量、是否有人受伤,并通过罪犯与响应警员的电话谈判提示提供交互。
战利品分配与洗钱
抢劫中玩家获得的物品与抢劫本身同样重要。与其直接将干净现金存入银行账户,不如奖励罪犯带有标记的钞票或赃物,这些物品需要额外步骤才能转换为可用货币。此设计将犯罪玩法循环延伸至抢劫之外,并创建互联系统。标记钞票可以在地图特定地点以一定手续费洗钱,玩家需权衡何时何地洗钱的风险与回报。赃物如珠宝或电子产品需通过 NPC 交易商出售,价格根据物品稀有度和当前市场状况浮动。实现战利品袋系统,玩家必须携带实物战利品,限制移动速度并禁止使用武器,为逃脱阶段增加战术考虑:
RegisterNetEvent('robbery:server:distributeLoot', function(robberyId)
local src = source
local config = Config.Robberies[robberyId]
local state = RobberyState[robberyId]
if not config or not state then return end
if state.status ~= 'active' then return end
local rewardAmount = math.random(config.reward.min, config.reward.max)
local participants = state.participants
local perPlayer = math.floor(rewardAmount / #participants)
for _, playerId in ipairs(participants) do
local Player = QBCore.Functions.GetPlayer(playerId)
if Player then
if config.reward.type == 'markedbills' then
local billStacks = math.ceil(perPlayer / 5000)
for i = 1, billStacks do
local amount = math.min(5000, perPlayer - ((i - 1) * 5000))
Player.Functions.AddItem('markedbills', 1, nil, {
worth = amount,
source = config.label,
})
end
elseif config.reward.type == 'cash' then
Player.Functions.AddMoney('cash', perPlayer, 'robbery-' .. robberyId)
end
TriggerClientEvent('inventory:client:ItemBox', playerId, QBCore.Shared.Items['markedbills'], 'add')
end
end
-- Set cooldown
state.status = 'cooldown'
state.cooldownUntil = os.time() + config.cooldown
state.participants = {}
-- Log the robbery for admin review
LogRobbery(robberyId, participants, rewardAmount)
end)
反利用与冷却管理
抢劫系统因产生财富而吸引作弊者。实施多层保护以防止常见作弊。服务器端距离检查确保玩家在每个阶段始终物理存在于抢劫地点。速率限制防止玩家频繁触发抢劫开始事件。参与者跟踪确保只有注册参与者能通过安全层或获得战利品。全局冷却限制服务器同时活跃抢劫数量,防止刷抢劫。每玩家冷却防止单个玩家连续抢劫多个地点。记录每次抢劫尝试的时间戳、参与者标识、成功或失败状态及分配奖励,便于管理员调查异常行为。监控不可能完成时间,防止玩家以物理上不可能的速度完成所有安全层。实施声望系统,过度抢劫活动会引起更多警察关注和更严厉后果,形成自然节流,鼓励玩家分散犯罪活动。
逃生路线和动态事件
逃脱阶段是抢劫真正不可预测的部分。抢劫物品安全后,罪犯必须携带沉重的行李袋逃离区域,行动受限。设计系统支持多种逃脱方式:停在外面的地面车辆、近岸码头的船只(适用于滨水银行)、或高端抢劫的屋顶直升机撤离。逃脱阶段添加动态事件,如警察部署的轮胎钉带、无线电通话更新披露嫌疑车辆描述,以及关键路口的随机路障。服务器应跟踪罪犯是否成功离开抢劫区域(定义为银行周围可配置半径),仅在此后标记抢劫完全完成。若所有参与者在清除区域前被击毙或逮捕,抢劫物应由警察作为证据回收。这为双方创造有意义的抢劫后玩法,确保逃脱与抢劫本身一样具有挑战性和奖励性。