Visão geral do sistema de caminhão de reboque
Um sistema de caminhão de reboque e apreensão cria todo um ecossistema de trabalho em seu servidor FiveM, dando aos jogadores uma carreira civil que interage naturalmente com policiais, mecânicos e outros motoristas. O sistema envolve vários componentes interconectados: uma camada de gerenciamento de trabalho que controla a entrada e a saída, um sistema de despacho que encaminha solicitações de reboque para motoristas disponíveis, mecânica de fixação de veículos que simula um reboque realista, um lote de apreensão que armazena veículos apreendidos e abandonados e uma estrutura de taxas que determina quanto custa recuperar veículos apreendidos. Quando bem projetado, o trabalho de guincho se torna uma das funções mais socialmente envolventes no servidor porque cada chamada envolve interação direta com outro jogador que precisa de ajuda com seu veículo quebrado ou estacionado ilegalmente.
Configuração de trabalho e sistema de serviço
Comece definindo o trabalho do caminhão de reboque dentro do sistema de trabalho da sua estrutura. Os jogadores devem ser capazes de marcar ponto em um local de reboque, receber um caminhão de reboque da empresa e começar a aceitar chamadas de despacho. O sistema de serviço deve rastrear os condutores de reboque ativos para que o sistema de despacho saiba quem está disponível. Quando um jogador chega, crie um caminhão-plataforma de uma lista predefinida de modelos de veículos de reboque e atribua-o a esse jogador. Ao expirar, o veículo da empresa deverá ser devolvido ou apagado. Armazene o status de serviço do jogador no servidor para que ele persista corretamente e possa ser consultado pelo sistema de despacho:
Config.TowJob = {
jobName = 'tow',
clockInLocation = vector3(409.09, -1622.57, 29.29),
vehicleSpawn = vector4(405.87, -1631.14, 29.29, 228.35),
towVehicles = {
{model = 'flatbed', label = 'Flatbed Truck', rank = 0},
{model = 'towtruck', label = 'Tow Truck (Hook)', rank = 1},
{model = 'towtruck2', label = 'Heavy Tow Truck', rank = 2},
},
impoundLot = vector3(409.59, -1637.87, 29.29),
impoundReturn = vector4(403.12, -1640.23, 29.29, 138.92),
payPerTow = {min = 350, max = 750},
payPerImpound = 500,
}
RegisterNetEvent('tow:server:clockIn', function()
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
if Player.PlayerData.job.name ~= Config.TowJob.jobName then
TriggerClientEvent('QBCore:Notify', src, 'You are not a tow driver', 'error')
return
end
Player.Functions.SetJobDuty(true)
TowDrivers[src] = {
onDuty = true,
currentCall = nil,
vehicleNet = nil,
towedVehicle = nil,
}
TriggerClientEvent('QBCore:Notify', src, 'You are now on duty', 'success')
TriggerClientEvent('tow:client:spawnVehicle', src)
end)Mecânica de fixação de veículos
O principal desafio técnico de um sistema de reboque é prender um veículo a outro de uma forma que pareça realista e se comporte corretamente durante a condução. FiveM fornece o AttachEntityToEntity nativo para essa finalidade, mas acertar as posições de deslocamento requer calibração cuidadosa para cada modelo de caminhão de reboque. A abordagem de plataforma funciona melhor para a maioria dos veículos: o carro rebocado é colocado no topo da plataforma com deslocamentos apropriados para que fique naturalmente assentado na plataforma. Para guinchos tipo gancho, as rodas dianteiras do veículo rebocado são levantadas enquanto as rodas traseiras permanecem no solo. Cada estilo de anexo precisa de diferentes vetores de deslocamento e valores de rotação. Teste esses valores no jogo fazendo pequenos ajustes até que o veículo assente corretamente sem cortar o caminhão:
function AttachVehicleToFlatbed(towTruck, targetVehicle)
local towModel = GetEntityModel(towTruck)
-- Offsets calibrated for the flatbed model
local offsets = {
['flatbed'] = {x = 0.0, y = -0.5, z = 1.1, rx = 0.0, ry = 0.0, rz = 0.0},
}
local offset = offsets['flatbed']
if not offset then return false end
-- Freeze target vehicle physics
SetEntityCollision(targetVehicle, false, false)
FreezeEntityPosition(targetVehicle, true)
-- Attach to flatbed
AttachEntityToEntity(
targetVehicle, towTruck,
0, -- bone index
offset.x, offset.y, offset.z,
offset.rx, offset.ry, offset.rz,
false, false, false, false, 0, true
)
-- Store attachment state
TriggerServerEvent('tow:server:vehicleAttached', VehToNet(towTruck), VehToNet(targetVehicle))
return true
end
function DetachVehicleFromFlatbed(towTruck, targetVehicle)
DetachEntity(targetVehicle, true, true)
SetEntityCollision(targetVehicle, true, true)
FreezeEntityPosition(targetVehicle, false)
-- Place vehicle on ground behind the truck
local truckCoords = GetEntityCoords(towTruck)
local truckHeading = GetEntityHeading(towTruck)
local behind = GetOffsetFromEntityInWorldCoords(towTruck, 0.0, -12.0, 0.0)
SetEntityCoords(targetVehicle, behind.x, behind.y, behind.z)
SetEntityHeading(targetVehicle, truckHeading)
SetVehicleOnGroundProperly(targetVehicle)
TriggerServerEvent('tow:server:vehicleDetached', VehToNet(towTruck))
endUm detalhe importante é desabilitar a colisão no veículo acoplado para evitar falhas físicas. Quando dois veículos têm colisão ativa e estão acoplados, o motor do jogo pode produzir tremores violentos ou lançar os veículos no ar. Congelar a posição do veículo alvo e desabilitar sua colisão enquanto estiver conectado elimina totalmente esses problemas.
Sistema de despacho e solicitação de reboque
O sistema de despacho conecta jogadores que precisam de reboque com motoristas de guincho disponíveis. Os jogadores podem solicitar um reboque por meio de seu telefone, de um NPC de oficina mecânica ou de cabines telefônicas colocadas ao redor do mapa. Quando chega uma solicitação, o servidor verifica se há drivers de reboque disponíveis e encaminha a chamada para o mais próximo. Se nenhum motorista estiver de plantão, forneça um serviço de reboque NPC substituto que cobra uma taxa mais alta e teletransporta o veículo para o local de apreensão após um atraso. A notificação de despacho deve incluir a localização do solicitante, modelo do veículo e o motivo do reboque, como avaria, acidente ou estacionamento ilegal. Dê ao motorista de reboque a opção de aceitar ou recusar a chamada, com as chamadas recusadas sendo encaminhadas para o próximo motorista mais próximo:
RegisterNetEvent('tow:server:requestTow', function(reason)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local playerCoords = GetEntityCoords(GetPlayerPed(src))
local vehicle = GetVehiclePedIsIn(GetPlayerPed(src), true)
local vehicleModel = vehicle ~= 0 and GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) or 'Unknown'
-- Find nearest available tow driver
local nearestDriver = nil
local nearestDist = math.huge
for driverId, data in pairs(TowDrivers) do
if data.onDuty and not data.currentCall then
local driverCoords = GetEntityCoords(GetPlayerPed(driverId))
local dist = #(playerCoords - driverCoords)
if dist < nearestDist then
nearestDist = dist
nearestDriver = driverId
end
end
end
if nearestDriver then
TowDrivers[nearestDriver].currentCall = {
requesterId = src,
coords = playerCoords,
vehicle = vehicleModel,
reason = reason,
timestamp = os.time(),
}
TriggerClientEvent('tow:client:incomingCall', nearestDriver, {
coords = playerCoords,
vehicle = vehicleModel,
reason = reason,
playerName = Player.PlayerData.charinfo.firstname,
})
TriggerClientEvent('QBCore:Notify', src, 'Tow truck dispatched. ETA based on driver location.', 'success')
else
TriggerClientEvent('QBCore:Notify', src, 'No tow drivers available. NPC service will arrive shortly.', 'info')
StartNPCTowService(src, playerCoords, vehicle)
end
end)Gerenciamento de lotes apreendidos
O lote apreendido é onde os veículos rebocados e apreendidos são armazenados até que seus proprietários os recuperem. Projete o banco de dados de apreensões para rastrear quais veículos estão atualmente apreendidos, quem os apreendeu, quando foram apreendidos e a taxa exigida para liberação. A polícia deve poder apreender veículos diretamente durante as paragens de trânsito ou após perseguições, sendo que essas apreensões acarretam taxas mais elevadas ou tempos de retenção obrigatórios mais longos. A interface do lote apreendido deve exibir uma lista dos veículos apreendidos do jogador com detalhes como modelo do veículo, número da placa, data da apreensão, motivo e taxa de liberação. Os jogadores pagam a taxa no balcão do lote apreendido e seu veículo aparece em um ponto de retorno designado próximo:
CREATE TABLE IF NOT EXISTS impounded_vehicles (
id INT AUTO_INCREMENT PRIMARY KEY,
plate VARCHAR(10) NOT NULL,
citizenid VARCHAR(50) NOT NULL,
vehicle_data LONGTEXT NOT NULL,
impound_reason ENUM('tow', 'police', 'abandoned', 'parking') DEFAULT 'tow',
impound_fee INT DEFAULT 250,
impounded_by VARCHAR(50) DEFAULT NULL,
impounded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
hold_until TIMESTAMP DEFAULT NULL,
released_at TIMESTAMP DEFAULT NULL,
INDEX idx_plate (plate),
INDEX idx_citizen (citizenid),
INDEX idx_released (released_at)
);
-- Server-side: Impound a vehicle
RegisterNetEvent('tow:server:impoundVehicle', function(vehicleNet, reason)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local vehicle = NetworkGetEntityFromNetworkId(vehicleNet)
if not DoesEntityExist(vehicle) then return end
local plate = GetVehicleNumberPlateText(vehicle)
local vehicleData = QBCore.Functions.GetVehicleProperties(vehicle)
local fee = Config.ImpoundFees[reason] or 250
MySQL.insert('INSERT INTO impounded_vehicles (plate, citizenid, vehicle_data, impound_reason, impound_fee, impounded_by) VALUES (?, ?, ?, ?, ?, ?)', {
plate:trim(),
GetVehicleOwner(plate),
json.encode(vehicleData),
reason,
fee,
Player.PlayerData.citizenid,
})
DeleteEntity(vehicle)
local pay = math.random(Config.TowJob.payPerImpound - 100, Config.TowJob.payPerImpound + 100)
Player.Functions.AddMoney('bank', pay, 'tow-impound')
TriggerClientEvent('QBCore:Notify', src, 'Vehicle impounded. Earned $' .. pay, 'success')
end)Fiscalização de estacionamento automatizado
Adicione um sistema automatizado que detecte veículos estacionados em zonas restritas e os sinalize para reboque após um período de carência configurável. Defina zonas de estacionamento restritas em torno de hidrantes, pontos de ônibus, delegacias de polícia e entradas de emergência de hospitais. Quando um jogador estaciona em uma zona restrita e sai andando, inicie um cronômetro. Se o veículo permanecer sem vigilância após o término do período de carência, gere uma solicitação de reboque e notifique os motoristas de reboque disponíveis. Isso cria um trabalho orgânico para os motoristas de guincho, sem exigir que os jogadores chamem manualmente o serviço. O sistema deve sinalizar apenas veículos de propriedade do jogador porque os veículos NPC são gerenciados pelo mecanismo de jogo e desaparecem naturalmente. Inclua uma notificação ao proprietário do veículo de que seu carro foi sinalizado para reboque, dando-lhe a chance de devolvê-lo e movê-lo antes da chegada do guincho.
Receita e Progressão
Estruture o trabalho de reboque com um sistema de progressão que recompense o trabalho consistente. Novos guinchos começam na classificação zero com acesso ao caminhão-plataforma básico e ganham uma taxa básica por reboque concluído. À medida que acumulam reboques concluídos, eles se classificam e desbloqueiam veículos de reboque mais pesados, capazes de lidar com veículos maiores, como caminhões e ônibus, acesso a contratos de apreensão policial que pagam mais e um caminhão de reboque pessoal que podem personalizar. Acompanhe estatísticas como total de reboques concluídos, distância percorrida durante o reboque e tempo médio de resposta para despachar chamadas. Exiba essas estatísticas em um painel de trabalho acessível por meio de um NPC ou aplicativo de telefone. Considere a implementação de um sistema de reputação em que os motoristas de reboque que respondem rapidamente e concluem os trabalhos sem danificar os veículos ganham multiplicadores de bônus em seu salário, enquanto os motoristas que ignoram chamadas ou danificam os veículos durante o reboque enfrentam penalidades salariais. Isto cria um incentivo natural à qualidade que beneficia toda a comunidade de servidores, garantindo que os serviços de reboque sejam confiáveis e profissionais.
Lidando com casos extremos e redes
Os sistemas de reboque têm vários casos extremos complicados que precisam de manuseio cuidadoso. Quando um motorista de reboque se desconecta enquanto reboca um veículo, o servidor deve detectar a desconexão e desconectar e aterrar com segurança o veículo rebocado para que ele não flutue no ar ou caia no mapa. Se o proprietário do veículo fizer login enquanto seu carro estiver acoplado a um caminhão de reboque, ele deverá ver seu veículo marcado como sendo rebocado em sua lista de veículos com informações sobre o motorista do reboque. Trate a propriedade da entidade do veículo com cuidado porque o anexo nativo só funciona de forma confiável quando o cliente anexador possui a propriedade de ambas as entidades. Use NetworkRequestControlOfEntity para garantir que o cliente do motorista de reboque controle ambos os veículos antes de tentar a fixação. Para sincronização multijogador, transmita o estado do anexo para todos os jogadores próximos para que eles vejam o veículo rebocado na posição correta em suas telas. As bolsas de estado funcionam bem para isso porque são replicadas automaticamente para todos os clientes ao alcance da entidade.
