DRS Zone Wars

Guerra de territórios: começa a dominação no NPC da zona, aguenta a área e desbloqueia uma loja exclusiva de armas, coletes e munições.

v1.9.2 QBCoreQboxESX drs_zonewars
Ver na loja

Guerra de territórios para organizações. Cada zona tem um NPC: um gang fala com ele para começar a dominação, a zona pisca no mapa de todos e o grupo tem de aguentar a posição. Quem domina a zona ganha acesso à loja desse NPC, onde se fazem armas, coletes e munições.

#Requisitos

FrameworkQBCore, Qbox ou ESX (deteção automática)
Inventárioox_inventory, qb-inventory ou o inventário do ESX
Base de dadosNão precisa (os donos ficam guardados no próprio servidor)

#Instalação

  1. Coloca a pasta drs_zonewars em resources.
  2. Adiciona ao server.cfg, depois do framework e do inventário:
server.cfg
ensure drs_zonewars
  1. Define as zonas, os NPCs e as lojas no config.lua (secções abaixo).
  2. ESX: como o ESX não tem gangs, é usado o emprego — coloca os empregos das organizações em Config.AllowedGroups.

#Como funciona

  1. Um membro de um gang aproxima-se do NPC da zona e carrega em E — a dominação começa.
  2. A zona passa a piscar a vermelho no mapa de todos os jogadores e o gang dono é avisado.
  3. Com o gang atacante dentro da zona e sem rivais, o progresso sobe até 100%.
  4. Se entrar outro gang, a zona fica disputada e o progresso para.
  5. Se os atacantes saírem todos ou morrerem, a dominação falha ao fim de Config.AbandonTime segundos e esse gang espera Config.RetryCooldown minutos para tentar de novo.
  6. Aos 100%, o gang passa a ser dono da zona, que fica protegida durante Config.ProtectMinutes.
  7. O gang dono usa o mesmo NPC (E) para abrir a loja. A loja fecha enquanto a zona está a ser atacada.

Nota

Polícia e INEM (Config.BlockedJobs) nunca dominam zonas. Jogadores mortos não contam para a captura.

#Zonas

Cada entrada de Config.Zones é uma zona:

config.lua
{
    id = 'docks', label = 'Docas', coords = vector3(1034.0, -3104.0, 5.9), radius = 120.0,
    npc = { model = 'g_m_m_chicold_01', coords = vector4(1034.0, -3104.0, 5.9, 90.0), scenario = 'WORLD_HUMAN_SMOKING' },
    shop = 'armas',
},
CampoDescrição
idIdentificador único (usado nos exports e para guardar o dono)
labelNome mostrado no mapa, no HUD e nos avisos
coords / radiusCentro e raio da zona, em metros
npc.model / npc.coordsModelo e posição (com direção) do NPC
npc.scenarioAnimação do NPC (opcional)
shopNome da loja em Config.Shops que o dono recebe

Dica

Para obter coordenadas no jogo usa um comando de coordenadas do teu framework (ex.: /coords no QBCore) em cima do sítio onde queres o NPC.

#Lojas

Cada loja em Config.Shops tem uma lista de itens. Cada item pode custar dinheiro, materiais ou os dois, e demora time segundos a fazer:

config.lua
armas = {
    label = 'Armeiro',
    items = {
        { item = 'weapon_pistol', label = 'Pistola', price = 5000, time = 20,
          materials = { { item = 'metalscrap', count = 40 }, { item = 'steel', count = 25 } } },
    },
},
  • count (opcional) — quantidade entregue por fabrico (por defeito 1).
  • O dinheiro sai de Config.ShopAccount (cash, bank ou black).
  • Se o inventário estiver cheio no fim, o dinheiro e os materiais são devolvidos.
  • As imagens dos itens vêm do teu inventário automaticamente; podes forçar outra pasta com Config.ImagePath.

#Opções principais

OpçãoPor defeitoDescrição
Config.AllowedGroups{}Gangs que podem dominar (vazio = todos)
Config.BlockedJobspolice, sheriff, ambulanceEmpregos que nunca dominam
Config.MinMembers1Membros dentro da zona para começar e avançar
Config.CaptureTime300Segundos para dominar
Config.AbandonTime60Segundos sem atacantes até a dominação falhar
Config.RetryCooldown10Minutos até o mesmo gang tentar de novo
Config.ProtectMinutes30Proteção depois de dominar
Config.Reward3000 a cada 15 minRecompensa ao dono, dividida pelos membros online (amount = 0 desliga)
Config.PromptPositionright-centerOnde aparece o aviso do NPC
Config.DiscordWebhook''Logs de ataques, conquistas e fabricos

#Configuração completa

config.lua
Config = {}

-- Idioma / Language: 'pt' ou 'en'
Config.Locale = 'pt'

-- Framework: 'auto' deteta Qbox/QBCore/ESX
-- QBCore/Qbox: usa o gang do jogador. ESX (não tem gangs): usa o emprego — mete os empregos das organizações em AllowedGroups.
-- QBCore/Qbox: uses the player's gang. ESX (no gangs): uses the job — list your organisation jobs in AllowedGroups.
Config.Framework = 'auto'

-- Grupos que podem dominar (vazio = qualquer gang) / Groups allowed to capture (empty = any gang)
Config.AllowedGroups = {}
-- Empregos que nunca dominam / Jobs that never capture
Config.BlockedJobs = { 'police', 'sheriff', 'ambulance' }

-- ------------------------------------------------------------------ dominação / capture
-- A dominação só começa quando um membro de um grupo fala com o NPC da zona.
-- A capture only starts when a group member talks to the zone's NPC.

-- Tecla para falar com o NPC (38 = E) e distância / Key to talk to the NPC (38 = E) and distance
Config.InteractKey = 38
Config.InteractKeyLabel = 'E'
Config.InteractDistance = 2.5
-- Onde aparece o aviso do NPC: 'right-center' | 'left-center' | 'center' / Where the NPC prompt shows
Config.PromptPosition = 'right-center'

-- Membros do grupo que têm de estar dentro da zona para começar e para o progresso avançar
-- Group members that must be inside the zone to start and for progress to advance
Config.MinMembers = 1
-- Tempo para dominar (segundos) com o grupo atacante dentro da zona e sem rivais
-- Capture time (seconds) with the attacking group inside and no rivals
Config.CaptureTime = 300
-- Se os atacantes saírem todos (ou morrerem), a dominação falha ao fim deste tempo (segundos)
-- If every attacker leaves (or dies), the capture fails after this long (seconds)
Config.AbandonTime = 60
-- Depois de falhar, o mesmo grupo espera este tempo para voltar a tentar nessa zona (minutos)
-- After a failed capture the same group waits this long to try that zone again (minutes)
Config.RetryCooldown = 10
-- Depois de dominada, quanto tempo fica protegida (minutos) / Protection after a capture (minutes)
Config.ProtectMinutes = 30

-- Recompensa para o grupo dono, dividida pelos membros online (amount = 0 desliga)
-- Owner reward, shared by online members (amount = 0 turns it off)
Config.Reward = { account = 'cash', amount = 3000, everyMinutes = 15 }

-- Avisar todos os jogadores quando uma dominação começa, falha ou termina
-- Announce to everyone when a capture starts, fails or succeeds
Config.Announce = true

-- ------------------------------------------------------------------ zonas / zones
-- npc.coords: onde fica o NPC (vector4 com a direção) / where the NPC stands (vector4 with heading)
-- shop: loja que o grupo dono passa a ter neste NPC (ver Config.Shops) / shop the owner gets at this NPC
Config.Zones = {
    {
        id = 'docks', label = 'Docas', coords = vector3(1034.0, -3104.0, 5.9), radius = 120.0,
        npc = { model = 'g_m_m_chicold_01', coords = vector4(1034.0, -3104.0, 5.9, 90.0), scenario = 'WORLD_HUMAN_SMOKING' },
        shop = 'armas',
    },
    {
        id = 'grove', label = 'Grove Street', coords = vector3(-156.0, -1611.0, 33.6), radius = 90.0,
        npc = { model = 'g_m_y_famca_01', coords = vector4(-156.0, -1611.0, 33.6, 180.0), scenario = 'WORLD_HUMAN_DRUG_DEALER' },
        shop = 'protecao',
    },
    {
        id = 'sandy', label = 'Sandy Shores', coords = vector3(1692.0, 3761.0, 34.7), radius = 110.0,
        npc = { model = 'a_m_m_hillbilly_01', coords = vector4(1692.0, 3761.0, 34.7, 220.0), scenario = 'WORLD_HUMAN_STAND_IMPATIENT' },
        shop = 'municoes',
    },
}

-- Blips no mapa (a zona pisca enquanto está a ser dominada) / Map blips (the zone flashes while under attack)
Config.Blips = true
Config.BlipSprite = 310

-- ------------------------------------------------------------------ lojas / shops
-- Só o grupo que domina a zona tem acesso à loja do NPC.
-- Cada item pode custar dinheiro (price), materiais (materials) ou os dois, e demora "time" segundos a fazer.
-- Only the group that owns the zone can use the NPC's shop.
-- Each item can cost money (price), materials (materials) or both, and takes "time" seconds to make.

-- Conta usada para pagar: 'cash' | 'bank' | 'black' (ESX) / Account used to pay
Config.ShopAccount = 'cash'

-- Pasta das imagens dos itens: nil = automático (ox_inventory ou qb-inventory)
-- Item image folder: nil = automatic (ox_inventory or qb-inventory)
Config.ImagePath = nil

Config.Shops = {
    armas = {
        label = 'Armeiro',
        items = {
            { item = 'weapon_snspistol',    label = 'Pistola SNS',     price = 3500,  time = 15, materials = { { item = 'metalscrap', count = 30 }, { item = 'steel', count = 15 } } },
            { item = 'weapon_pistol',       label = 'Pistola',         price = 5000,  time = 20, materials = { { item = 'metalscrap', count = 40 }, { item = 'steel', count = 25 }, { item = 'plastic', count = 15 } } },
            { item = 'weapon_microsmg',     label = 'Micro SMG',       price = 15000, time = 35, materials = { { item = 'metalscrap', count = 80 }, { item = 'steel', count = 60 }, { item = 'aluminum', count = 30 } } },
            { item = 'weapon_compactrifle', label = 'Rifle Compacta',  price = 30000, time = 50, materials = { { item = 'metalscrap', count = 120 }, { item = 'steel', count = 90 }, { item = 'aluminum', count = 50 }, { item = 'rubber', count = 20 } } },
        },
    },
    protecao = {
        label = 'Proteção',
        items = {
            { item = 'armor',        label = 'Colete',         price = 1500, time = 10, materials = { { item = 'rubber', count = 10 }, { item = 'plastic', count = 10 } } },
            { item = 'heavyarmor',   label = 'Colete Pesado',  price = 4000, time = 20, materials = { { item = 'rubber', count = 20 }, { item = 'steel', count = 15 }, { item = 'plastic', count = 15 } } },
            { item = 'weapon_knife', label = 'Faca',           price = 800,  time = 8,  materials = { { item = 'steel', count = 5 } } },
        },
    },
    municoes = {
        label = 'Munições',
        items = {
            { item = 'pistol_ammo',  label = 'Munição de Pistola', price = 400,  time = 6,  materials = { { item = 'copper', count = 5 }, { item = 'metalscrap', count = 5 } } },
            { item = 'smg_ammo',     label = 'Munição de SMG',     price = 700,  time = 8,  materials = { { item = 'copper', count = 8 }, { item = 'metalscrap', count = 8 } } },
            { item = 'rifle_ammo',   label = 'Munição de Rifle',   price = 1000, time = 10, materials = { { item = 'copper', count = 12 }, { item = 'metalscrap', count = 10 } } },
            { item = 'shotgun_ammo', label = 'Munição de Shotgun', price = 600,  time = 8,  materials = { { item = 'copper', count = 6 }, { item = 'iron', count = 6 } } },
        },
    },
}

-- Webhook do Discord ('' = desligado) / Discord webhook
Config.DiscordWebhook = ''

#Para programadores

server
exports.drs_zonewars:GetZoneOwner('docks')       -- nome do gang dono ou nil
exports.drs_zonewars:IsZoneUnderAttack('docks')  -- true / false

O estado de todas as zonas está em GlobalState.drsZones (cliente e servidor).

#Resolução de problemas

"Com o emprego X não podes dominar zonas." — o teu emprego está em Config.BlockedJobs. Muda de emprego ou sai de serviço.

"Não pertences a nenhum grupo que possa dominar." — a personagem não tem gang (QBCore/Qbox) ou o gang/emprego não está em Config.AllowedGroups.

O NPC está enterrado ou dentro de um objeto — ajusta npc.coords para um ponto livre.