add sborka
This commit is contained in:
923
garrysmod/gamemodes/militaryrp/plugins/vehicles/cl_plugin.lua
Normal file
923
garrysmod/gamemodes/militaryrp/plugins/vehicles/cl_plugin.lua
Normal file
@@ -0,0 +1,923 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
-- Хранение выбранного loadout для каждой техники
|
||||
local selectedLoadouts = {}
|
||||
|
||||
-- Цветовая схема (такая же как в арсенале)
|
||||
local COLOR_BG_DARK = Color(3, 5, 4)
|
||||
local COLOR_BG_MEDIUM = Color(8, 12, 10)
|
||||
local COLOR_BG_LIGHT = Color(12, 18, 14)
|
||||
local COLOR_PRIMARY = Color(27, 94, 32)
|
||||
local COLOR_PRIMARY_DARK = Color(15, 60, 18)
|
||||
local COLOR_ACCENT = Color(56, 102, 35)
|
||||
local COLOR_TEXT_PRIMARY = Color(165, 214, 167)
|
||||
local COLOR_TEXT_SECONDARY = Color(102, 187, 106)
|
||||
local COLOR_DANGER = Color(136, 14, 14)
|
||||
local COLOR_WARNING = Color(191, 130, 0)
|
||||
local COLOR_BORDER = Color(15, 60, 18, 60)
|
||||
|
||||
-- Вспомогательная функция для рисования кругов
|
||||
if not draw.Circle then
|
||||
function draw.Circle(x, y, radius, color)
|
||||
local segmentCount = math.max(16, radius)
|
||||
surface.SetDrawColor(color or color_white)
|
||||
|
||||
local circle = {}
|
||||
for i = 0, segmentCount do
|
||||
local angle = math.rad((i / segmentCount) * 360)
|
||||
table.insert(circle, {
|
||||
x = x + math.cos(angle) * radius,
|
||||
y = y + math.sin(angle) * radius
|
||||
})
|
||||
end
|
||||
surface.DrawPoly(circle)
|
||||
end
|
||||
end
|
||||
|
||||
if not surface.DrawCircle then
|
||||
function surface.DrawCircle(x, y, radius, color)
|
||||
draw.Circle(x, y, radius, color)
|
||||
end
|
||||
end
|
||||
|
||||
PLUGIN.clientFactionPoints = {}
|
||||
PLUGIN.selectedLoadouts = {} -- Глобальное хранилище выбранных loadout'ов
|
||||
|
||||
-- Проверка доступности техники для игрока
|
||||
function PLUGIN:CanPlayerAccessVehicle(vehicleConfig)
|
||||
local character = LocalPlayer():GetCharacter()
|
||||
if not character then return false end
|
||||
|
||||
local playerSubdivision = character:GetPodr()
|
||||
|
||||
-- Если нет ограничений по подразделениям - доступно всем
|
||||
if not vehicleConfig.allowedPodr or not istable(vehicleConfig.allowedPodr) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Проверяем доступность для подразделения игрока
|
||||
return vehicleConfig.allowedPodr[playerSubdivision] ~= nil
|
||||
end
|
||||
|
||||
function PLUGIN:GetFactions()
|
||||
if not self.cachedFactions then
|
||||
if not self.config then
|
||||
-- Fallback to global if self.config is missing for some reason
|
||||
self.config = PLUGIN.config or {}
|
||||
end
|
||||
if not self.config or not istable(self.config) then
|
||||
self.cachedFactions = {}
|
||||
else
|
||||
self.cachedFactions = self.config.getFactions and self.config.getFactions() or self.config.factions or {}
|
||||
end
|
||||
end
|
||||
return self.cachedFactions
|
||||
end
|
||||
|
||||
-- Сетевые сообщения
|
||||
net.Receive("LVS_SyncFactionPoints", function()
|
||||
local faction = net.ReadUInt(8)
|
||||
local points = net.ReadUInt(32)
|
||||
PLUGIN.clientFactionPoints[faction] = points
|
||||
end)
|
||||
|
||||
-- Открытие меню наземной техники
|
||||
net.Receive("LVS_OpenGroundMenu", function()
|
||||
PLUGIN:OpenVehicleMenu("ground")
|
||||
end)
|
||||
|
||||
-- Открытие меню воздушной техники
|
||||
net.Receive("LVS_OpenAirMenu", function()
|
||||
PLUGIN:OpenVehicleMenu("air")
|
||||
end)
|
||||
|
||||
local function UpdateVehicleInfo(vehicleConfig, vehicleID, infoContent, modelPanel, vehicleName, vehicleDesc, vehiclePrice, vehicleCooldown, spawnBtn, factionPoints, vehicleType, returnBtn, loadoutPanel, updateLoadoutCallback)
|
||||
if not vehicleConfig then return end
|
||||
|
||||
-- Инициализируем выбранный loadout если его нет (дефолтный = 1)
|
||||
if not PLUGIN.selectedLoadouts[vehicleID] then
|
||||
PLUGIN.selectedLoadouts[vehicleID] = 1
|
||||
end
|
||||
|
||||
local faction = LocalPlayer():Team()
|
||||
local factions = PLUGIN:GetFactions()
|
||||
local factionConfig = factions and factions[faction]
|
||||
local character = LocalPlayer():GetCharacter()
|
||||
local playerSubdivision = character and character:GetPodr() or 1
|
||||
|
||||
-- Проверяем доступность по подразделению
|
||||
local canSpawnBySubdivision = true
|
||||
local subdivisionMessage = ""
|
||||
|
||||
-- Проверяем разрешенные подразделения и специализации
|
||||
if vehicleConfig.allowedPodr and istable(vehicleConfig.allowedPodr) then
|
||||
local playerSpec = character and character:GetSpec() or 1
|
||||
local podrConfig = vehicleConfig.allowedPodr[playerSubdivision]
|
||||
|
||||
if podrConfig == nil then
|
||||
-- Подразделение не в списке
|
||||
canSpawnBySubdivision = false
|
||||
elseif podrConfig == true then
|
||||
-- Разрешено всем специализациям этого подразделения
|
||||
canSpawnBySubdivision = true
|
||||
elseif istable(podrConfig) then
|
||||
-- Проверяем специализацию
|
||||
canSpawnBySubdivision = table.HasValue(podrConfig, playerSpec)
|
||||
end
|
||||
|
||||
if not canSpawnBySubdivision then
|
||||
-- Получаем названия разрешенных подразделений и специализаций
|
||||
local podrNames = {}
|
||||
local factionTable = ix.faction.Get(faction)
|
||||
|
||||
if factionTable and factionTable.Podr and factionTable.Spec then
|
||||
for podrID, config in pairs(vehicleConfig.allowedPodr) do
|
||||
local podrData = factionTable.Podr[podrID]
|
||||
if podrData and podrData.name then
|
||||
if config == true then
|
||||
table.insert(podrNames, podrData.name)
|
||||
elseif istable(config) then
|
||||
local specNames = {}
|
||||
for _, specID in ipairs(config) do
|
||||
local specData = factionTable.Spec[specID]
|
||||
if specData and specData.name then
|
||||
table.insert(specNames, specData.name)
|
||||
end
|
||||
end
|
||||
if #specNames > 0 then
|
||||
table.insert(podrNames, podrData.name .. " (" .. table.concat(specNames, ", ") .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #podrNames > 0 then
|
||||
subdivisionMessage = "Только: " .. table.concat(podrNames, "; ")
|
||||
else
|
||||
subdivisionMessage = "Недоступно для вашего подразделения"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Показываем элементы
|
||||
modelPanel:SetVisible(true)
|
||||
vehicleName:SetVisible(true)
|
||||
vehicleDesc:SetVisible(true)
|
||||
vehiclePrice:SetVisible(true)
|
||||
vehicleCooldown:SetVisible(true)
|
||||
spawnBtn:SetVisible(true)
|
||||
returnBtn:SetVisible(true)
|
||||
|
||||
-- Устанавливаем модель
|
||||
modelPanel:SetModel(vehicleConfig.model)
|
||||
|
||||
local ent = modelPanel.Entity
|
||||
if IsValid(ent) then
|
||||
local center = ent:OBBCenter()
|
||||
local distance = ent:BoundingRadius() * 1.5
|
||||
modelPanel:SetLookAt(center)
|
||||
modelPanel:SetCamPos(center + Vector(distance, distance, distance))
|
||||
modelPanel:SetFOV(50)
|
||||
end
|
||||
|
||||
-- Устанавливаем текстовую информацию
|
||||
vehicleName:SetText(vehicleConfig.name)
|
||||
vehicleDesc:SetText(vehicleConfig.description)
|
||||
vehiclePrice:SetText("Стоимость: " .. vehicleConfig.price .. " очков техники")
|
||||
|
||||
-- Проверяем кд и доступность
|
||||
local vehiclesData = character and character:GetData("lvs_vehicles", {}) or {}
|
||||
local lastSpawn = vehiclesData[vehicleID]
|
||||
local cooldown = lastSpawn and lastSpawn > os.time() and (lastSpawn - os.time()) or 0
|
||||
|
||||
-- Проверяем, есть ли активная техника этого типа
|
||||
local hasActiveVehicle = PLUGIN:HasActiveVehicle(vehicleID, vehicleType)
|
||||
local refundAmount = math.floor(vehicleConfig.price * 0.8)
|
||||
|
||||
-- Настраиваем кнопку возврата
|
||||
if hasActiveVehicle then
|
||||
returnBtn:SetEnabled(true)
|
||||
returnBtn:SetText("")
|
||||
returnBtn.Paint = function(s, w, h)
|
||||
local col = s:IsHovered() and Color(185, 60, 60) or Color(165, 85, 60)
|
||||
draw.RoundedBox(6, 0, 0, w, h, col)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(255, 255, 255, 30)
|
||||
draw.RoundedBox(6, 2, 2, w-4, h-4, Color(255, 255, 255, 30))
|
||||
end
|
||||
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("◄ ВЕРНУТЬ (" .. refundAmount .. " очков)", "ixSmallFont", w/2, h/2, COLOR_TEXT_PRIMARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
-- Отправляем запрос на возврат техники при нажатии кнопки
|
||||
returnBtn.DoClick = function()
|
||||
local veh = PLUGIN:GetActiveVehicle(vehicleID, vehicleType)
|
||||
if IsValid(veh) then
|
||||
net.Start("LVS_RequestReturn")
|
||||
net.WriteUInt(veh:EntIndex(), 16)
|
||||
net.SendToServer()
|
||||
else
|
||||
LocalPlayer():Notify("Активная техника не найдена")
|
||||
end
|
||||
end
|
||||
else
|
||||
returnBtn:SetEnabled(false)
|
||||
returnBtn:SetText("")
|
||||
returnBtn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, Color(60, 60, 60))
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("НЕТ АКТИВНОЙ ТЕХНИКИ", "ixSmallFont", w/2, h/2, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
if not canSpawnBySubdivision then
|
||||
vehicleCooldown:SetText(subdivisionMessage)
|
||||
vehicleCooldown:SetTextColor(COLOR_DANGER)
|
||||
spawnBtn:SetEnabled(false)
|
||||
spawnBtn:SetText("")
|
||||
spawnBtn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, Color(60, 60, 60))
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("НЕДОСТУПНО", "ixSmallFont", w/2, h/2, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
elseif cooldown > 0 then
|
||||
local minutes = math.floor(cooldown / 60)
|
||||
local seconds = math.floor(cooldown % 60)
|
||||
vehicleCooldown:SetText("Перезарядка: " .. string.format("%02d:%02d", minutes, seconds))
|
||||
vehicleCooldown:SetTextColor(COLOR_WARNING)
|
||||
spawnBtn:SetEnabled(false)
|
||||
spawnBtn:SetText("")
|
||||
spawnBtn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, Color(60, 60, 60))
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("В КД", "ixSmallFont", w/2, h/2, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
elseif factionPoints < vehicleConfig.price then
|
||||
vehicleCooldown:SetText("Недостаточно очков техники")
|
||||
vehicleCooldown:SetTextColor(COLOR_WARNING)
|
||||
spawnBtn:SetEnabled(false)
|
||||
spawnBtn:SetText("")
|
||||
spawnBtn.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, Color(60, 60, 60))
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("НЕТ ОЧКОВ", "ixSmallFont", w/2, h/2, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
else
|
||||
vehicleCooldown:SetText("Готов к вызову")
|
||||
vehicleCooldown:SetTextColor(COLOR_ACCENT)
|
||||
spawnBtn:SetEnabled(true)
|
||||
spawnBtn:SetText("")
|
||||
spawnBtn.Paint = function(s, w, h)
|
||||
local col = s:IsHovered() and COLOR_ACCENT or COLOR_PRIMARY
|
||||
draw.RoundedBox(6, 0, 0, w, h, col)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(255, 255, 255, 30)
|
||||
draw.RoundedBox(6, 2, 2, w-4, h-4, Color(255, 255, 255, 30))
|
||||
end
|
||||
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("ВЫЗВАТЬ ТЕХНИКУ ►", "ixSmallFont", w/2, h/2, COLOR_TEXT_PRIMARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
spawnBtn.DoClick = function()
|
||||
local loadoutID = PLUGIN.selectedLoadouts[vehicleID] or 1
|
||||
|
||||
print("[LVS] Запрос спавна техники " .. vehicleConfig.name .. " с loadout ID: " .. loadoutID)
|
||||
PLUGIN:RequestSpawnVehicle(vehicleID, vehicleType, loadoutID)
|
||||
end
|
||||
end
|
||||
|
||||
-- Обновляем панель loadout если она передана
|
||||
if IsValid(loadoutPanel) and updateLoadoutCallback then
|
||||
updateLoadoutCallback(vehicleConfig, vehicleID, loadoutPanel)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function PLUGIN:GetActiveVehicle(vehicleID, vehicleType)
|
||||
local ply = LocalPlayer()
|
||||
if not IsValid(ply) then return nil end
|
||||
|
||||
for _, ent in ipairs(ents.GetAll()) do
|
||||
if IsValid(ent) then
|
||||
local nwID = ent:GetNWString("LVS_VehicleID", "")
|
||||
local nwType = ent:GetNWString("LVS_VehicleType", "")
|
||||
local nwOwner = ent:GetNWString("LVS_OwnerSteamID", "")
|
||||
|
||||
if nwID == tostring(vehicleID) and nwType == tostring(vehicleType) then
|
||||
if ply:IsAdmin() or ply:SteamID() == nwOwner then
|
||||
return ent
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Функция обновления кд
|
||||
function PLUGIN:UpdateVehicleCooldown(panel, vehicleID)
|
||||
local character = LocalPlayer():GetCharacter()
|
||||
if not character then return end
|
||||
|
||||
local vehicles = character:GetData("lvs_vehicles", {})
|
||||
local lastSpawn = vehicles[vehicleID]
|
||||
|
||||
if lastSpawn and lastSpawn > os.time() then
|
||||
panel.cooldown = lastSpawn - os.time()
|
||||
|
||||
timer.Create("LVS_Cooldown_" .. vehicleID, 1, 0, function()
|
||||
if not IsValid(panel) then
|
||||
timer.Remove("LVS_Cooldown_" .. vehicleID)
|
||||
return
|
||||
end
|
||||
|
||||
panel.cooldown = panel.cooldown - 1
|
||||
if panel.cooldown <= 0 then
|
||||
panel.cooldown = 0
|
||||
timer.Remove("LVS_Cooldown_" .. vehicleID)
|
||||
end
|
||||
end)
|
||||
else
|
||||
panel.cooldown = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Основная функция создания меню в стиле Military RP
|
||||
function PLUGIN:OpenVehicleMenu(vehicleType)
|
||||
-- Если окно уже открыто — закроем старое, чтобы не было дубликатов
|
||||
if IsValid(PLUGIN._vehicleMenuFrame) then
|
||||
PLUGIN._vehicleMenuFrame:Remove()
|
||||
PLUGIN._vehicleMenuFrame = nil
|
||||
end
|
||||
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
local faction = LocalPlayer():Team()
|
||||
local factions = PLUGIN:GetFactions()
|
||||
local factionConfig = factions and factions[faction]
|
||||
|
||||
if not factionConfig then
|
||||
return
|
||||
end
|
||||
|
||||
local vehicles = vehicleType == "ground" and factionConfig.groundVehicles or factionConfig.airVehicles
|
||||
|
||||
-- Фильтруем технику по доступности
|
||||
local availableVehicles = {}
|
||||
for vehicleID, vehicleConfig in pairs(vehicles) do
|
||||
if self:CanPlayerAccessVehicle(vehicleConfig) then
|
||||
availableVehicles[vehicleID] = vehicleConfig
|
||||
end
|
||||
end
|
||||
|
||||
-- Если нет доступной техники - не открываем меню
|
||||
if table.Count(availableVehicles) == 0 then
|
||||
LocalPlayer():Notify("Техника вам не доступна")
|
||||
return
|
||||
end
|
||||
|
||||
local frame = vgui.Create("DFrame")
|
||||
frame:SetSize(scrW, scrH)
|
||||
frame:SetPos(0, 0)
|
||||
frame:SetTitle("")
|
||||
frame:SetDraggable(false)
|
||||
frame:ShowCloseButton(false)
|
||||
frame:MakePopup()
|
||||
|
||||
local factionPoints = self.clientFactionPoints[faction] or 0
|
||||
|
||||
-- Градиентный фон с зеленым оттенком
|
||||
frame.Paint = function(s, w, h)
|
||||
-- Основной фон
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
-- Градиент сверху
|
||||
local gradHeight = 300
|
||||
for i = 0, gradHeight do
|
||||
local alpha = (1 - i/gradHeight) * 40
|
||||
surface.SetDrawColor(COLOR_PRIMARY.r, COLOR_PRIMARY.g, COLOR_PRIMARY.b, alpha)
|
||||
surface.DrawRect(0, i, w, 1)
|
||||
end
|
||||
|
||||
-- Верхняя панель
|
||||
surface.SetDrawColor(COLOR_BG_MEDIUM)
|
||||
surface.DrawRect(0, 0, w, 100)
|
||||
|
||||
-- Акцентная линия
|
||||
surface.SetDrawColor(COLOR_PRIMARY)
|
||||
surface.DrawRect(0, 100, w, 3)
|
||||
|
||||
-- Декоративные элементы
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
for i = 0, w, 200 do
|
||||
surface.DrawRect(i, 0, 1, 100)
|
||||
end
|
||||
|
||||
-- Заголовок
|
||||
local title = vehicleType == "ground" and "◆ НАЗЕМНАЯ ТЕХНИКА ◆" or "◆ ВОЗДУШНАЯ ТЕХНИКА ◆"
|
||||
local subtitle = vehicleType == "ground" and "СИСТЕМА ВЫЗОВА ТЕХНИКИ" or "СИСТЕМА ВЫЗОВА АВИАЦИИ"
|
||||
draw.SimpleText(title, "ixMenuButtonFont", w/2, 35, COLOR_ACCENT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText(subtitle, "ixSmallFont", w/2, 68, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
-- Кнопка закрытия
|
||||
local closeBtn = vgui.Create("DButton", frame)
|
||||
closeBtn:SetText("")
|
||||
closeBtn:SetSize(42, 42)
|
||||
closeBtn:SetPos(scrW - 60, 18)
|
||||
closeBtn.Paint = function(s, w, h)
|
||||
local col = s:IsHovered() and COLOR_DANGER or Color(COLOR_DANGER.r * 0.7, COLOR_DANGER.g * 0.7, COLOR_DANGER.b * 0.7)
|
||||
|
||||
draw.RoundedBox(4, 0, 0, w, h, col)
|
||||
surface.SetDrawColor(COLOR_BG_DARK)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
surface.SetDrawColor(COLOR_TEXT_PRIMARY)
|
||||
surface.DrawLine(w*0.3, h*0.3, w*0.7, h*0.7)
|
||||
surface.DrawLine(w*0.7, h*0.3, w*0.3, h*0.7)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(255, 255, 255, 30)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Color(255, 255, 255, 30))
|
||||
end
|
||||
end
|
||||
closeBtn.DoClick = function()
|
||||
frame:Close()
|
||||
end
|
||||
|
||||
-- Панель информации об очках
|
||||
local infoPanel = vgui.Create("DPanel", frame)
|
||||
infoPanel:SetSize(300, 70)
|
||||
infoPanel:SetPos(40, 115)
|
||||
infoPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG_LIGHT)
|
||||
surface.SetDrawColor(COLOR_PRIMARY)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
draw.SimpleText("ОЧКИ ТЕХНИКИ", "ixSmallFont", 20, 12, COLOR_TEXT_SECONDARY, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(factionPoints, "ixMenuButtonFont", 20, 32, COLOR_ACCENT, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
|
||||
-- Иконка
|
||||
surface.SetDrawColor(COLOR_PRIMARY)
|
||||
draw.NoTexture()
|
||||
surface.DrawCircle(w - 35, h/2, 12, COLOR_PRIMARY)
|
||||
end
|
||||
|
||||
local colWidth = (scrW - 120) / 2
|
||||
local startY = 205
|
||||
|
||||
-- Панель списка техники
|
||||
local vehiclesPanel = vgui.Create("DPanel", frame)
|
||||
vehiclesPanel:SetSize(colWidth, scrH - startY - 40)
|
||||
vehiclesPanel:SetPos(40, startY)
|
||||
vehiclesPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG_MEDIUM)
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
end
|
||||
|
||||
local vehiclesHeader = vgui.Create("DPanel", vehiclesPanel)
|
||||
vehiclesHeader:SetSize(vehiclesPanel:GetWide(), 45)
|
||||
vehiclesHeader:SetPos(0, 0)
|
||||
vehiclesHeader.Paint = function(s, w, h)
|
||||
draw.RoundedBoxEx(8, 0, 0, w, h, COLOR_PRIMARY_DARK, true, true, false, false)
|
||||
|
||||
draw.SimpleText("▣ ДОСТУПНАЯ ТЕХНИКА", "ixSmallFont", 15, h/2, COLOR_TEXT_PRIMARY, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
surface.SetDrawColor(COLOR_ACCENT)
|
||||
surface.DrawRect(0, h-2, w, 2)
|
||||
end
|
||||
|
||||
-- Панель информации о технике
|
||||
local infoPanel2 = vgui.Create("DPanel", frame)
|
||||
infoPanel2:SetSize(colWidth, scrH - startY - 40)
|
||||
infoPanel2:SetPos(scrW - colWidth - 40, startY)
|
||||
infoPanel2.Paint = function(s, w, h)
|
||||
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG_MEDIUM)
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
end
|
||||
|
||||
local infoHeader = vgui.Create("DPanel", infoPanel2)
|
||||
infoHeader:SetSize(infoPanel2:GetWide(), 45)
|
||||
infoHeader:SetPos(0, 0)
|
||||
infoHeader.Paint = function(s, w, h)
|
||||
draw.RoundedBoxEx(8, 0, 0, w, h, COLOR_PRIMARY_DARK, true, true, false, false)
|
||||
|
||||
draw.SimpleText("⬢ ИНФОРМАЦИЯ", "ixSmallFont", 15, h/2, COLOR_TEXT_PRIMARY, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
surface.SetDrawColor(COLOR_ACCENT)
|
||||
surface.DrawRect(0, h-2, w, 2)
|
||||
end
|
||||
|
||||
-- Список техники
|
||||
local vehiclesScroll = vgui.Create("DScrollPanel", vehiclesPanel)
|
||||
vehiclesScroll:SetSize(vehiclesPanel:GetWide() - 20, vehiclesPanel:GetTall() - 60)
|
||||
vehiclesScroll:SetPos(10, 50)
|
||||
|
||||
local sbar = vehiclesScroll:GetVBar()
|
||||
sbar:SetHideButtons(true)
|
||||
function sbar:Paint(w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, COLOR_BG_DARK)
|
||||
end
|
||||
function sbar.btnGrip:Paint(w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, COLOR_PRIMARY)
|
||||
end
|
||||
|
||||
-- Панель информации
|
||||
local infoContent = vgui.Create("DPanel", infoPanel2)
|
||||
infoContent:SetSize(infoPanel2:GetWide() - 20, infoPanel2:GetTall() - 60)
|
||||
infoContent:SetPos(10, 50)
|
||||
infoContent.selectedVehicle = nil
|
||||
infoContent.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, COLOR_BG_LIGHT)
|
||||
|
||||
if not s.selectedVehicle then
|
||||
draw.SimpleText("⚠ ВЫБЕРИТЕ ТЕХНИКУ", "ixSmallFont", w/2, h/2, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
-- Модель техники
|
||||
local modelPanel = vgui.Create("DModelPanel", infoContent)
|
||||
modelPanel:SetSize(400, 200)
|
||||
modelPanel:SetPos(10, 10)
|
||||
modelPanel:SetVisible(false)
|
||||
|
||||
-- Информация о технике
|
||||
local vehicleName = vgui.Create("DLabel", infoContent)
|
||||
vehicleName:SetFont("ixSmallFont")
|
||||
vehicleName:SetTextColor(color_white)
|
||||
vehicleName:SetPos(10, 220)
|
||||
vehicleName:SetSize(400, 20)
|
||||
vehicleName:SetVisible(false)
|
||||
|
||||
local vehicleDesc = vgui.Create("DLabel", infoContent)
|
||||
vehicleDesc:SetFont("ixSmallFont")
|
||||
vehicleDesc:SetTextColor(Color(200, 200, 200))
|
||||
vehicleDesc:SetPos(10, 245)
|
||||
vehicleDesc:SetSize(400, 40)
|
||||
vehicleDesc:SetVisible(false)
|
||||
|
||||
local vehiclePrice = vgui.Create("DLabel", infoContent)
|
||||
vehiclePrice:SetFont("ixSmallFont")
|
||||
vehiclePrice:SetTextColor(color_white)
|
||||
vehiclePrice:SetPos(10, 290)
|
||||
vehiclePrice:SetSize(400, 20)
|
||||
vehiclePrice:SetVisible(false)
|
||||
|
||||
local vehicleCooldown = vgui.Create("DLabel", infoContent)
|
||||
vehicleCooldown:SetFont("ixSmallFont")
|
||||
vehicleCooldown:SetTextColor(color_white)
|
||||
vehicleCooldown:SetPos(10, 315)
|
||||
vehicleCooldown:SetSize(400, 20)
|
||||
vehicleCooldown:SetVisible(false)
|
||||
|
||||
-- Кнопка вызова
|
||||
local spawnBtn = vgui.Create("DButton", infoContent)
|
||||
spawnBtn:SetSize(200, 35)
|
||||
spawnBtn:SetPos(10, 350)
|
||||
spawnBtn:SetText("")
|
||||
spawnBtn:SetVisible(false)
|
||||
|
||||
-- Кнопка возврата
|
||||
local returnBtn = vgui.Create("DButton", infoContent)
|
||||
returnBtn:SetSize(200, 35)
|
||||
returnBtn:SetPos(220, 350)
|
||||
returnBtn:SetText("")
|
||||
returnBtn:SetVisible(false)
|
||||
|
||||
-- Используем глобальное хранилище selectedLoadouts
|
||||
local selectedLoadouts = PLUGIN.selectedLoadouts
|
||||
|
||||
-- Создаем панель выбора loadout (скрыта по умолчанию)
|
||||
local loadoutPanel = vgui.Create("DPanel", infoContent)
|
||||
loadoutPanel:SetSize(infoContent:GetWide() - 20, 120)
|
||||
loadoutPanel:SetPos(10, 400)
|
||||
loadoutPanel:SetVisible(false)
|
||||
loadoutPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(6, 0, 0, w, h, ColorAlpha(COLOR_BG_DARK, 200))
|
||||
surface.SetDrawColor(COLOR_PRIMARY)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
draw.SimpleText("⚙ ВООРУЖЕНИЕ:", "ixSmallFont", 10, 8, COLOR_TEXT_PRIMARY, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
-- Функция обновления панели loadout
|
||||
local function UpdateLoadoutPanel(vehicleConfig, vehicleID, panel)
|
||||
if not IsValid(panel) then return end
|
||||
|
||||
for _, child in pairs(panel:GetChildren()) do
|
||||
child:Remove()
|
||||
end
|
||||
|
||||
local loadoutConfig = vehicleConfig.loadout
|
||||
if not istable(loadoutConfig) or table.Count(loadoutConfig) == 0 then
|
||||
panel:SetVisible(false)
|
||||
return
|
||||
end
|
||||
|
||||
panel:SetVisible(true)
|
||||
|
||||
local btnWidth = 130
|
||||
local btnHeight = 80
|
||||
local gap = 10
|
||||
local startX = 10
|
||||
local startY = 30
|
||||
|
||||
-- соберём пары в массив и отсортируем по ID, чтобы кнопки шли в правильном порядке
|
||||
local entries = {}
|
||||
for loadoutName, loadoutID in pairs(loadoutConfig) do
|
||||
if isnumber(loadoutID) then
|
||||
table.insert(entries, {name = loadoutName, id = loadoutID})
|
||||
end
|
||||
end
|
||||
table.sort(entries, function(a, b) return a.id < b.id end)
|
||||
|
||||
-- вставим пустые слоты для пропущенных ID, чтобы кнопки располагались на своих позициях
|
||||
local filled = {}
|
||||
local nextIdx = 1
|
||||
local maxID = entries[#entries] and entries[#entries].id or 0
|
||||
for id = 1, maxID do
|
||||
if entries[nextIdx] and entries[nextIdx].id == id then
|
||||
table.insert(filled, entries[nextIdx])
|
||||
nextIdx = nextIdx + 1
|
||||
else
|
||||
table.insert(filled, {name = "", id = id})
|
||||
end
|
||||
end
|
||||
|
||||
for idx, entry in ipairs(filled) do
|
||||
local btn = vgui.Create("DButton", panel)
|
||||
btn:SetSize(btnWidth, btnHeight)
|
||||
btn:SetPos(startX + (idx - 1) * (btnWidth + gap), startY)
|
||||
btn:SetText("")
|
||||
btn.variantIndex = idx
|
||||
btn.loadoutID = entry.id
|
||||
btn.loadoutName = tostring(entry.name)
|
||||
|
||||
btn.Paint = function(s, w, h)
|
||||
local isSelected = PLUGIN.selectedLoadouts[vehicleID] == s.loadoutID
|
||||
local bgColor = isSelected and COLOR_PRIMARY or COLOR_BG_MEDIUM
|
||||
|
||||
if s:IsHovered() and not isSelected then
|
||||
bgColor = ColorAlpha(COLOR_PRIMARY, 100)
|
||||
end
|
||||
|
||||
draw.RoundedBox(4, 0, 0, w, h, bgColor)
|
||||
|
||||
if isSelected then
|
||||
surface.SetDrawColor(COLOR_ACCENT)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 3)
|
||||
else
|
||||
surface.SetDrawColor(COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
end
|
||||
|
||||
draw.SimpleText(s.loadoutName or ("Вариант " .. s.variantIndex), "ixSmallFont", w/2, 12, COLOR_TEXT_PRIMARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText("ID: " .. tostring(s.loadoutID), "DermaDefault", w/2, 50, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER)
|
||||
|
||||
if isSelected then
|
||||
draw.SimpleText("✓", "ixMenuButtonFont", w/2, h - 20, COLOR_ACCENT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
btn.DoClick = function()
|
||||
print("[LVS] Пользователь выбрал loadout '" .. btn.loadoutName .. "' (ID=" .. btn.loadoutID .. ") для " .. vehicleConfig.name)
|
||||
PLUGIN.selectedLoadouts[vehicleID] = btn.loadoutID
|
||||
|
||||
for _, child in pairs(panel:GetChildren()) do
|
||||
child:Remove()
|
||||
end
|
||||
|
||||
UpdateLoadoutPanel(vehicleConfig, vehicleID, panel)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Привяжем frame к плагину, чтобы можно было предотвращать дубликаты
|
||||
PLUGIN._vehicleMenuFrame = frame
|
||||
|
||||
-- Функция создания строки техники
|
||||
local function CreateVehicleRow(parent, vehicleID, vehicleConfig)
|
||||
local row = vgui.Create("DButton", parent)
|
||||
row:SetSize(parent:GetWide() - 10, 70)
|
||||
row:SetText("")
|
||||
row.vehicleID = vehicleID
|
||||
row.vehicleConfig = vehicleConfig
|
||||
|
||||
local isHovered = false
|
||||
row.Paint = function(s, w, h)
|
||||
local isSelected = infoContent.selectedVehicle == vehicleID
|
||||
local canAfford = factionPoints >= vehicleConfig.price
|
||||
local cooldown = s.cooldown or 0
|
||||
local hasActive = PLUGIN:HasActiveVehicle(vehicleID, vehicleType)
|
||||
|
||||
-- Фон строки
|
||||
local bgColor = isSelected and COLOR_PRIMARY_DARK or COLOR_BG_LIGHT
|
||||
if isHovered and not isSelected then
|
||||
bgColor = Color(bgColor.r + 10, bgColor.g + 15, bgColor.b + 10)
|
||||
end
|
||||
draw.RoundedBox(6, 0, 0, w, h, bgColor)
|
||||
|
||||
-- Боковая полоска
|
||||
local statusColor = hasActive and Color(56, 142, 200) or (canAfford and COLOR_PRIMARY or COLOR_DANGER)
|
||||
draw.RoundedBoxEx(6, 0, 0, 5, h, statusColor, true, false, true, false)
|
||||
|
||||
-- Рамка
|
||||
surface.SetDrawColor(isSelected and COLOR_ACCENT or COLOR_BORDER)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
-- Акцентная линия при наведении
|
||||
if isHovered then
|
||||
surface.SetDrawColor(COLOR_ACCENT)
|
||||
surface.DrawRect(5, h-3, w-10, 3)
|
||||
end
|
||||
|
||||
-- Название техники
|
||||
draw.SimpleText(vehicleConfig.name, "ixSmallFont", 15, 12, COLOR_TEXT_PRIMARY, TEXT_ALIGN_LEFT)
|
||||
|
||||
-- Цена
|
||||
local priceColor = canAfford and COLOR_ACCENT or COLOR_WARNING
|
||||
draw.SimpleText(vehicleConfig.price .. " очков", "ixSmallFont", 15, 35, priceColor, TEXT_ALIGN_LEFT)
|
||||
|
||||
-- Статус
|
||||
local statusText = ""
|
||||
local statusTextColor = COLOR_TEXT_SECONDARY
|
||||
|
||||
if hasActive then
|
||||
statusText = "● АКТИВНА"
|
||||
statusTextColor = Color(100, 180, 255)
|
||||
elseif cooldown > 0 then
|
||||
local minutes = math.floor(cooldown / 60)
|
||||
local seconds = math.floor(cooldown % 60)
|
||||
statusText = "КД: " .. string.format("%02d:%02d", minutes, seconds)
|
||||
statusTextColor = COLOR_WARNING
|
||||
elseif not canAfford then
|
||||
statusText = "НЕТ ОЧКОВ"
|
||||
statusTextColor = COLOR_WARNING
|
||||
else
|
||||
statusText = "● ГОТОВ"
|
||||
statusTextColor = COLOR_ACCENT
|
||||
end
|
||||
|
||||
draw.SimpleText(statusText, "ixSmallFont", w - 15, h/2, statusTextColor, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
row.OnCursorEntered = function() isHovered = true end
|
||||
row.OnCursorExited = function() isHovered = false end
|
||||
|
||||
row.DoClick = function(s)
|
||||
infoContent.selectedVehicle = vehicleID
|
||||
UpdateVehicleInfo(vehicleConfig, vehicleID, infoContent, modelPanel, vehicleName, vehicleDesc, vehiclePrice, vehicleCooldown, spawnBtn, factionPoints, vehicleType, returnBtn, loadoutPanel, UpdateLoadoutPanel)
|
||||
end
|
||||
|
||||
-- Обновляем кд
|
||||
PLUGIN:UpdateVehicleCooldown(row, vehicleID)
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
-- Создаем строки с техникой (только доступной)
|
||||
for vehicleID, vehicleConfig in SortedPairsByMemberValue(availableVehicles, "price") do
|
||||
local row = CreateVehicleRow(vehiclesScroll, vehicleID, vehicleConfig)
|
||||
row:Dock(TOP)
|
||||
row:DockMargin(0, 0, 0, 5)
|
||||
end
|
||||
|
||||
-- Функция обновления интерфейса
|
||||
local function UpdateInterface()
|
||||
if not IsValid(frame) then return end
|
||||
|
||||
local currentPoints = PLUGIN.clientFactionPoints[faction] or 0
|
||||
factionPoints = currentPoints
|
||||
|
||||
-- Обновляем отображение очков
|
||||
if IsValid(pointsPanel) then
|
||||
pointsPanel.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(Color(23, 23, 23))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
|
||||
draw.SimpleText("ОЧКИ ТЕХНИКИ", "ixSmallFont", w/2, 11, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText(currentPoints, "ixSmallFont", w/2, 28, Color(100, 255, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
-- Обновляем информацию о выбранной технике
|
||||
if infoContent.selectedVehicle then
|
||||
local vehicleConfig = availableVehicles[infoContent.selectedVehicle]
|
||||
if vehicleConfig then
|
||||
UpdateVehicleInfo(vehicleConfig, infoContent.selectedVehicle, infoContent, modelPanel, vehicleName, vehicleDesc, vehiclePrice, vehicleCooldown, spawnBtn, currentPoints, vehicleType, returnBtn)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Таймер для обновления интерфейса
|
||||
timer.Create("LVS_InterfaceUpdate_" .. vehicleType, 1, 0, function()
|
||||
if not IsValid(frame) then
|
||||
timer.Remove("LVS_InterfaceUpdate_" .. vehicleType)
|
||||
return
|
||||
end
|
||||
UpdateInterface()
|
||||
end)
|
||||
|
||||
-- Автоматически выбираем первую технику
|
||||
timer.Simple(0.1, function()
|
||||
if IsValid(vehiclesScroll) and vehiclesScroll:GetCanvas():GetChildren()[1] then
|
||||
local firstRow = vehiclesScroll:GetCanvas():GetChildren()[1]
|
||||
if firstRow.DoClick then
|
||||
firstRow:DoClick()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
frame.OnCursorEntered = function(s)
|
||||
s:MoveToFront()
|
||||
end
|
||||
|
||||
frame.OnClose = function()
|
||||
timer.Remove("LVS_InterfaceUpdate_" .. vehicleType)
|
||||
if PLUGIN._vehicleMenuFrame == frame then
|
||||
PLUGIN._vehicleMenuFrame = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Запрос спавна техники
|
||||
function PLUGIN:RequestSpawnVehicle(vehicleID, vehicleType, loadoutVariant)
|
||||
print("[LVS] Отправка запроса спавна на сервер - vehicleID: " .. vehicleID .. ", loadoutVariant: " .. (loadoutVariant or 0))
|
||||
net.Start("LVS_RequestSpawn")
|
||||
net.WriteString(vehicleID)
|
||||
net.WriteString(vehicleType)
|
||||
net.WriteUInt(loadoutVariant or 0, 8)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
-- Запрос возврата техники
|
||||
function PLUGIN:RequestReturnVehicle(vehicleID, vehicleType)
|
||||
local vehicle = self:GetActiveVehicle(vehicleID, vehicleType)
|
||||
|
||||
if IsValid(vehicle) then
|
||||
net.Start("LVS_RequestReturn")
|
||||
net.WriteUInt(vehicle:EntIndex(), 16)
|
||||
net.SendToServer()
|
||||
else
|
||||
LocalPlayer():Notify("Активная техника не найдена")
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("LVS_SyncVehicleInfo", function()
|
||||
local vehicle = net.ReadEntity()
|
||||
local ownerSteamID = net.ReadString()
|
||||
local vehicleID = net.ReadString()
|
||||
local vehicleType = net.ReadString()
|
||||
|
||||
if IsValid(vehicle) then
|
||||
vehicle.lvsVehicleInfo = {
|
||||
ownerSteamID = ownerSteamID,
|
||||
vehicleID = vehicleID,
|
||||
vehicleType = vehicleType
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
-- Получаем таблицу моделей от сервера и подставляем в конфиг при отсутствии
|
||||
net.Receive("LVS_SyncVehicleModels", function()
|
||||
local mapping = net.ReadTable()
|
||||
if not istable(mapping) then return end
|
||||
|
||||
for factionID, data in pairs(mapping) do
|
||||
local factionConfig = PLUGIN:GetFactions()[factionID]
|
||||
if not factionConfig then continue end
|
||||
|
||||
if istable(data.ground) then
|
||||
for id, mdl in pairs(data.ground) do
|
||||
if factionConfig.groundVehicles and factionConfig.groundVehicles[id] then
|
||||
if not factionConfig.groundVehicles[id].model or factionConfig.groundVehicles[id].model == "" then
|
||||
factionConfig.groundVehicles[id].model = mdl
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if istable(data.air) then
|
||||
for id, mdl in pairs(data.air) do
|
||||
if factionConfig.airVehicles and factionConfig.airVehicles[id] then
|
||||
if not factionConfig.airVehicles[id].model or factionConfig.airVehicles[id].model == "" then
|
||||
factionConfig.airVehicles[id].model = mdl
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,35 @@
|
||||
include("shared.lua")
|
||||
|
||||
function ENT:Draw()
|
||||
self:DrawModel()
|
||||
|
||||
local pos = self:GetPos()
|
||||
local ang = self:GetAngles()
|
||||
|
||||
pos = pos + ang:Up() * 40
|
||||
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
ang:RotateAroundAxis(ang:Up(), 90)
|
||||
|
||||
cam.Start3D2D(pos, ang, 0.15)
|
||||
local terminalType = self:GetTerminalType()
|
||||
local title = "Терминал техники"
|
||||
|
||||
if terminalType == "ground" then
|
||||
title = "НАЗЕМНАЯ ТЕХНИКА"
|
||||
elseif terminalType == "air" then
|
||||
title = "ВОЗДУШНАЯ ТЕХНИКА"
|
||||
end
|
||||
|
||||
draw.SimpleTextOutlined(
|
||||
title,
|
||||
"DermaLarge",
|
||||
0, 0,
|
||||
Color(255, 255, 255),
|
||||
TEXT_ALIGN_CENTER,
|
||||
TEXT_ALIGN_CENTER,
|
||||
2,
|
||||
Color(0, 0, 0)
|
||||
)
|
||||
cam.End3D2D()
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
AddCSLuaFile("cl_init.lua")
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self:GetModel() or "models/props_c17/oildrum001.mdl")
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if phys:IsValid() then
|
||||
phys:Wake()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Use(activator, caller)
|
||||
if not IsValid(activator) or not activator:IsPlayer() then return end
|
||||
|
||||
local vehiclePlugin = ix.plugin.list["vehicles"]
|
||||
if vehiclePlugin and vehiclePlugin.GetFactionPoints then
|
||||
net.Start("LVS_SyncFactionPoints")
|
||||
net.WriteUInt(activator:Team(), 8)
|
||||
net.WriteUInt(vehiclePlugin:GetFactionPoints(activator:Team()) or 0, 32)
|
||||
net.Send(activator)
|
||||
end
|
||||
|
||||
local terminalType = self:GetTerminalType()
|
||||
if terminalType == "ground" then
|
||||
net.Start("LVS_OpenGroundMenu")
|
||||
net.Send(activator)
|
||||
elseif terminalType == "air" then
|
||||
net.Start("LVS_OpenAirMenu")
|
||||
net.Send(activator)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,129 @@
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_gmodentity"
|
||||
ENT.PrintName = "Vehicle Terminal"
|
||||
ENT.Author = "RefoselDev"
|
||||
ENT.Category = "[FT] Система техники"
|
||||
ENT.Spawnable = false
|
||||
ENT.AdminSpawnable = true
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("String", 0, "TerminalType")
|
||||
end
|
||||
|
||||
scripted_ents.Register({
|
||||
Type = "anim",
|
||||
Base = "base_gmodentity",
|
||||
PrintName = "Терминал наземной техники",
|
||||
Category = "[FT] Система техники",
|
||||
Author = "RefoselDev",
|
||||
Spawnable = true,
|
||||
AdminOnly = true,
|
||||
bNoPersist = true,
|
||||
Model = "models/props_c17/oildrum001.mdl",
|
||||
SetupDataTables = function(self)
|
||||
self:NetworkVar("String", 0, "TerminalType")
|
||||
end,
|
||||
Initialize = function(self)
|
||||
if (SERVER) then
|
||||
self:SetModel(self.Model)
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self:SetTerminalType("ground")
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if phys:IsValid() then
|
||||
phys:Wake()
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Use = function(self, activator, caller)
|
||||
if (SERVER) then
|
||||
if not IsValid(activator) or not activator:IsPlayer() then return end
|
||||
|
||||
local vehiclePlugin = ix.plugin.list["vehicles"]
|
||||
if vehiclePlugin and vehiclePlugin.GetFactionPoints then
|
||||
net.Start("LVS_SyncFactionPoints")
|
||||
net.WriteUInt(activator:Team(), 8)
|
||||
net.WriteUInt(vehiclePlugin:GetFactionPoints(activator:Team()) or 0, 32)
|
||||
net.Send(activator)
|
||||
end
|
||||
|
||||
net.Start("LVS_OpenGroundMenu")
|
||||
net.Send(activator)
|
||||
end
|
||||
end,
|
||||
Draw = function(self)
|
||||
self:DrawModel()
|
||||
|
||||
local pos = self:GetPos() + Vector(0, 0, 80)
|
||||
local ang = Angle(0, LocalPlayer():EyeAngles().y - 90, 90)
|
||||
|
||||
cam.Start3D2D(pos, ang, 0.15)
|
||||
draw.SimpleText("НАЗЕМНАЯ ТЕХНИКА", "DermaLarge", 0, 0, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
cam.End3D2D()
|
||||
end
|
||||
}, "ix_vehicle_terminal_ground")
|
||||
|
||||
scripted_ents.Register({
|
||||
Type = "anim",
|
||||
Base = "base_gmodentity",
|
||||
PrintName = "Терминал воздушной техники",
|
||||
Category = "[FT] Система техники",
|
||||
Author = "RefoselDev",
|
||||
Spawnable = true,
|
||||
AdminOnly = true,
|
||||
bNoPersist = true,
|
||||
Model = "models/props_c17/oildrum001.mdl",
|
||||
SetupDataTables = function(self)
|
||||
self:NetworkVar("String", 0, "TerminalType")
|
||||
end,
|
||||
Initialize = function(self)
|
||||
if (SERVER) then
|
||||
self:SetModel(self.Model)
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self:SetTerminalType("air")
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if phys:IsValid() then
|
||||
phys:Wake()
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Use = function(self, activator, caller)
|
||||
if (SERVER) then
|
||||
if not IsValid(activator) or not activator:IsPlayer() then return end
|
||||
|
||||
local vehiclePlugin = ix.plugin.list["vehicles"]
|
||||
if vehiclePlugin and vehiclePlugin.GetFactionPoints then
|
||||
net.Start("LVS_SyncFactionPoints")
|
||||
net.WriteUInt(activator:Team(), 8)
|
||||
net.WriteUInt(vehiclePlugin:GetFactionPoints(activator:Team()) or 0, 32)
|
||||
net.Send(activator)
|
||||
end
|
||||
|
||||
net.Start("LVS_OpenAirMenu")
|
||||
net.Send(activator)
|
||||
end
|
||||
end,
|
||||
Draw = function(self)
|
||||
self:DrawModel()
|
||||
|
||||
if (not CLIENT) then return end
|
||||
|
||||
local pos = self:GetPos() + self:GetUp() * 100
|
||||
local ang = LocalPlayer():EyeAngles()
|
||||
ang:RotateAroundAxis(ang:Right(), 90)
|
||||
ang:RotateAroundAxis(ang:Up(), -90)
|
||||
|
||||
cam.Start3D2D(pos, ang, 0.1)
|
||||
draw.SimpleText("ВОЗДУШНАЯ ТЕХНИКА", "DermaLarge", 0, 0, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
cam.End3D2D()
|
||||
end
|
||||
}, "ix_vehicle_terminal_air")
|
||||
5302
garrysmod/gamemodes/militaryrp/plugins/vehicles/sh_config.lua
Normal file
5302
garrysmod/gamemodes/militaryrp/plugins/vehicles/sh_config.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
PLUGIN.name = "LVS Vehicles System"
|
||||
PLUGIN.author = "RefoselDev"
|
||||
PLUGIN.description = "Система вызова LVS техники за очки"
|
||||
|
||||
ix.util.Include("sh_config.lua", "shared")
|
||||
ix.util.Include("cl_plugin.lua", "client")
|
||||
ix.util.Include("sv_plugin.lua", "server")
|
||||
|
||||
-- Функция проверки активной техники
|
||||
function PLUGIN:HasActiveVehicle(vehicleID, vehicleType)
|
||||
local ply = LocalPlayer()
|
||||
if not IsValid(ply) then return false end
|
||||
|
||||
-- Ищем активную технику игрока
|
||||
for _, ent in pairs(ents.GetAll()) do
|
||||
if IsValid(ent) and ent.lvsVehicleInfo then
|
||||
local info = ent.lvsVehicleInfo
|
||||
if info.vehicleID == vehicleID and info.vehicleType == vehicleType then
|
||||
-- Проверяем владельца или права админа
|
||||
if ply:IsAdmin() or ply:SteamID() == info.ownerSteamID then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- fallback: проверим NWVars если lvsVehicleInfo ещё не синхронизировалось
|
||||
if IsValid(ent) then
|
||||
local nwOwner = ent:GetNWString("LVS_OwnerSteamID", "")
|
||||
local nwID = ent:GetNWString("LVS_VehicleID", "")
|
||||
local nwType = ent:GetNWString("LVS_VehicleType", "")
|
||||
|
||||
if nwID == tostring(vehicleID) and nwType == tostring(vehicleType) and nwOwner ~= "" then
|
||||
if ply:IsAdmin() or ply:SteamID() == nwOwner then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
1052
garrysmod/gamemodes/militaryrp/plugins/vehicles/sv_plugin.lua
Normal file
1052
garrysmod/gamemodes/militaryrp/plugins/vehicles/sv_plugin.lua
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user