local PLUGIN = PLUGIN PLUGIN.factionPoints = PLUGIN.factionPoints or {} PLUGIN.spawnPoints = PLUGIN.spawnPoints or {} PLUGIN.activeVehicles = PLUGIN.activeVehicles or {} -- Таблица для отслеживания активной техники function PLUGIN:LoadData() local data = self:GetData() or {} -- Загрузка очков фракций if type(data) == "table" and data.factionPoints then self.factionPoints = data.factionPoints elseif type(data) == "table" and not data.factionPoints then -- Старый формат (только очки) self.factionPoints = data else self.factionPoints = {} end -- Загрузка терминалов if data.terminals then for _, v in ipairs(data.terminals) do local terminal = ents.Create(v.class or "ix_vehicle_terminal_ground") if IsValid(terminal) then terminal:SetPos(v.pos) terminal:SetAngles(v.angles) terminal:Spawn() local phys = terminal:GetPhysicsObject() if IsValid(phys) then phys:EnableMotion(false) end end end end -- Загрузка точек спавна из конфига local mapName = game.GetMap() local spawnPointsForMap = self.config.spawnPoints[mapName] if spawnPointsForMap then self.spawnPoints = type(spawnPointsForMap) == "function" and spawnPointsForMap() or spawnPointsForMap else self.spawnPoints = {} end local factions = self.config.getFactions and self.config.getFactions() or self.config.factions or {} for factionID, factionConfig in pairs(factions) do if not self.factionPoints[factionID] then self.factionPoints[factionID] = factionConfig.points or 1000 end end -- Прекеширование LVS классов техники: создаём и сразу удаляем каждый класс, -- чтобы LVS инициализировал их заранее и при первом спавне игроком всё работало timer.Simple(5, function() local precached = {} for _, factionConfig in pairs(factions) do for _, vehicleList in pairs({factionConfig.groundVehicles or {}, factionConfig.airVehicles or {}}) do for _, vehConfig in pairs(vehicleList) do local class = vehConfig.class if class and not precached[class] then local ent = ents.Create(class) if IsValid(ent) then ent:SetPos(Vector(0, 0, -10000)) -- далеко под картой ent:Spawn() ent:Activate() SafeRemoveEntityDelayed(ent, 1) -- удаляем через секунду после инициализации precached[class] = true print("[LVS] Прекешировано: " .. class) end end end end end print("[LVS] Прекеширование завершено, классов: " .. table.Count(precached)) end) end function PLUGIN:GetFactions() if not self.cachedFactions then self.cachedFactions = self.config.getFactions and self.config.getFactions() or self.config.factions or {} end return self.cachedFactions end function PLUGIN:SaveData() local data = { terminals = {} } -- Сохраняем все терминалы for _, entity in ipairs(ents.FindByClass("ix_vehicle_terminal_ground")) do if IsValid(entity) then data.terminals[#data.terminals + 1] = { class = "ix_vehicle_terminal_ground", pos = entity:GetPos(), angles = entity:GetAngles() } end end for _, entity in ipairs(ents.FindByClass("ix_vehicle_terminal_air")) do if IsValid(entity) then data.terminals[#data.terminals + 1] = { class = "ix_vehicle_terminal_air", pos = entity:GetPos(), angles = entity:GetAngles() } end end self:SetData(data) end function PLUGIN:GetFactionPoints(faction) local points = self.factionPoints[faction] or 0 local maxPoints = (self.config and self.config.maxPoints) or 20000 return math.min(points, maxPoints) end function PLUGIN:AddFactionPoints(faction, amount) if not self.factionPoints[faction] then self.factionPoints[faction] = 0 end local maxPoints = (self.config and self.config.maxPoints) or 20000 self.factionPoints[faction] = math.Clamp(self.factionPoints[faction] + amount, 0, maxPoints) self:SyncFactionPoints(faction) -- Логирование изменения очков техники local serverlogsPlugin = ix.plugin.list["serverlogs"] if (serverlogsPlugin) then local factionName = ix.faction.Get(faction).name or tostring(faction) if amount > 0 then local message = string.format("Фракция '%s' получила +%d очков техники (итого: %d)", factionName, amount, self.factionPoints[faction]) serverlogsPlugin:AddLog("VEHICLE_POINTS_ADD", message, nil, { faction = faction, factionName = factionName, amount = amount, total = self.factionPoints[faction] }) elseif amount < 0 then local message = string.format("Фракция '%s' потратила %d очков техники (итого: %d)", factionName, math.abs(amount), self.factionPoints[faction]) serverlogsPlugin:AddLog("VEHICLE_POINTS_USE", message, nil, { faction = faction, factionName = factionName, amount = math.abs(amount), total = self.factionPoints[faction] }) end end return self.factionPoints[faction] end function PLUGIN:SyncFactionPoints(faction) net.Start("LVS_SyncFactionPoints") net.WriteUInt(faction, 8) net.WriteUInt(self.factionPoints[faction] or 0, 32) net.Broadcast() end function PLUGIN:GetSpawnPoints(faction, vehicleType, podr) local validPoints = {} local podrPoints = {} -- Точки, специфичные для подразделения for _, point in pairs(self.spawnPoints) do if istable(point) and point.factions and point.factions[faction] then -- Проверяем тип техники local validType = false if point.vehicleTypes then for _, allowedType in ipairs(point.vehicleTypes) do if allowedType == vehicleType then validType = true break end end else validType = true end if validType then -- Если у точки есть привязка к подразделениям if point.allowedPodr then if podr and point.allowedPodr[podr] then table.insert(podrPoints, point) end else -- Точка без привязки к подразделению — доступна всем table.insert(validPoints, point) end end end end -- Если есть точки для конкретного подразделения — используем их -- Иначе — используем общие точки фракции if #podrPoints > 0 then return podrPoints end return validPoints end function PLUGIN:FindSpawnPosition(player, spawnSettings, vehicleType) local faction = player:Team() if spawnSettings.useSpawnPoints then local spawnPoints = self:GetSpawnPoints(faction, vehicleType) if #spawnPoints > 0 then local point = spawnPoints[math.random(1, #spawnPoints)] local angle = math.random(0, 360) local distance = math.random(0, spawnSettings.spawnPointRadius) local offset = Vector( math.cos(angle) * distance, math.sin(angle) * distance, 0 ) local spawnPos = point.pos + offset + Vector(0, 0, spawnSettings.spawnHeight) local traceCheck = util.TraceHull({ start = spawnPos, endpos = spawnPos, mins = Vector(-130, -130, 20), maxs = Vector(130, 130, 180), filter = function(ent) if ent == player or ent:GetClass() == "lvs_vehicle_repair" then return false end return true end }) if not traceCheck.Hit then return spawnPos, point.ang end end end local trace = util.TraceLine({ start = player:GetShootPos(), endpos = player:GetShootPos() + player:GetAimVector() * spawnSettings.maxDistance, filter = player }) if trace.Hit then local spawnPos = trace.HitPos + Vector(0, 0, spawnSettings.spawnHeight) local traceCheck = util.TraceHull({ start = spawnPos, endpos = spawnPos, mins = Vector(-130, -130, 20), maxs = Vector(130, 130, 180), filter = function(ent) if ent == player or ent:GetClass() == "lvs_vehicle_repair" then return false end return true end }) if not traceCheck.Hit then return spawnPos, player:GetAngles() end end return nil end function PLUGIN:SpawnVehicle(player, vehicleID, vehicleType, loadoutVariant) if not IsValid(player) then print("[LVS DEBUG] FAIL: player not valid") return false, "Игрок не валиден" end local character = player:GetCharacter() if not character then print("[LVS DEBUG] FAIL: no character") return false, "Персонаж не найден" end local faction = player:Team() local factionConfig = PLUGIN:GetFactions()[faction] print("[LVS DEBUG] Player:", player:Nick(), "Faction:", faction, "VehicleID:", vehicleID, "Type:", vehicleType) print("[LVS DEBUG] FactionConfig exists:", factionConfig ~= nil) loadoutVariant = loadoutVariant or 0 if not factionConfig then print("[LVS DEBUG] FAIL: no factionConfig for faction", faction) print("[LVS DEBUG] Available factions:") for k, v in pairs(PLUGIN:GetFactions()) do print("[LVS DEBUG] faction key:", k, "name:", v.name) end return false, "Ваша фракция не может вызывать технику" end -- Проверка подразделения local playerSubdivision = character:GetPodr() print("[LVS DEBUG] PlayerSubdivision:", playerSubdivision) -- Проверяем массив разрешенных подразделений для конкретной техники local vehicles = vehicleType == "ground" and factionConfig.groundVehicles or factionConfig.airVehicles local vehicleConfig = vehicles[vehicleID] if not vehicleConfig then print("[LVS DEBUG] FAIL: vehicleConfig not found for ID:", vehicleID) print("[LVS DEBUG] Available vehicles:") for k, v in pairs(vehicles) do print("[LVS DEBUG] ", k, "->", v.name) end return false, "Техника не найдена" end if vehicleConfig.allowedPodr and istable(vehicleConfig.allowedPodr) then if not vehicleConfig.allowedPodr[playerSubdivision] then -- Получаем названия разрешенных подразделений local podrNames = {} local factionTable = ix.faction.Get(faction) if factionTable and factionTable.Podr then for podrID, _ in pairs(vehicleConfig.allowedPodr) do local podrData = factionTable.Podr[podrID] if podrData and podrData.name then table.insert(podrNames, podrData.name) end end end if #podrNames > 0 then return false, "Эту технику могут вызывать только: " .. table.concat(podrNames, ", ") else return false, "Эта техника недоступна для вашего подразделения" end end end local factionPoints = self:GetFactionPoints(faction) if factionPoints < vehicleConfig.price then print("[LVS DEBUG] FAIL: not enough points. Has:", factionPoints, "Need:", vehicleConfig.price) return false, "Недостаточно очков техники у фракции" end local vehiclesData = character:GetData("lvs_vehicles", {}) local lastSpawn = vehiclesData[vehicleID] if lastSpawn and lastSpawn > os.time() then local remaining = lastSpawn - os.time() local minutes = math.floor(remaining / 60) local seconds = math.floor(remaining % 60) return false, "Техника в кд: " .. string.format("%02d:%02d", minutes, seconds) end local spawnSettings = PLUGIN.config.spawnSettings[vehicleType] or PLUGIN.config.spawnSettings.ground -- Попытаемся взять точку спавна из конфигурации (если такие точки заданы для карты) local spawnPos, spawnAng = nil, nil local spawnPointUsed = nil local validPoints = self:GetSpawnPoints(faction, vehicleType, playerSubdivision) print("[LVS DEBUG] SpawnPoints found:", #validPoints, "for faction:", faction, "type:", vehicleType, "podr:", tostring(playerSubdivision)) -- Требуем, чтобы для карты были настроены точки спавна if not validPoints or #validPoints == 0 then print("[LVS DEBUG] FAIL: no spawn points!") print("[LVS DEBUG] Total spawnPoints loaded:", table.Count(self.spawnPoints)) for i, sp in pairs(self.spawnPoints) do if istable(sp) then print("[LVS DEBUG] point", i, "id:", sp.id, "factions:", table.ToString(sp.factions or {}), "types:", table.ToString(sp.vehicleTypes or {}), "podr:", table.ToString(sp.allowedPodr or {})) end end return false, "На этой карте не настроены точки спавна техники" end local effectiveSpawnHeight = spawnSettings.spawnHeight if vehicleType == "air" then effectiveSpawnHeight = 20 end local function IsPosClear(pos) local traceCheck = util.TraceHull({ start = pos, endpos = pos, mins = Vector(-70, -70, 30), -- Еще меньше, чтобы танки с длинными дулами или широкие машины не цепляли стены maxs = Vector(70, 70, 100), mask = MASK_SOLID, filter = function(ent) if ent == player then return false end local class = ent:GetClass() -- Игнорируем ремонтные станции и терминалы if class == "lvs_vehicle_repair" or class:find("ix_vehicle_terminal") then return false end return true end }) if traceCheck.Hit then if IsValid(traceCheck.Entity) then print("[LVS DEBUG] Точка спавна заблокирована энтити: " .. traceCheck.Entity:GetClass()) else print("[LVS DEBUG] Точка спавна заблокирована картой (World/Brush)!") end end return not traceCheck.Hit and not traceCheck.StartSolid end -- Сначала пробуем найти свободную точку среди всех доступных for _, p in ipairs(validPoints) do -- 1. Сначала пробуем ровно в центре точки (без смещения) local testPos = p.pos + Vector(0, 0, effectiveSpawnHeight) if IsPosClear(testPos) then spawnPos = testPos spawnAng = p.ang or player:GetAngles() spawnPointUsed = p break end -- 2. Если центр занят, пробуем 5 случайных смещений в радиусе local radius = spawnSettings.spawnPointRadius or 0 if radius > 0 then for i = 1, 5 do local angle = math.random(0, 360) local distance = math.random(50, radius) local offset = Vector(math.cos(angle) * distance, math.sin(angle) * distance, 0) local offsetPos = p.pos + offset + Vector(0, 0, effectiveSpawnHeight) if IsPosClear(offsetPos) then spawnPos = offsetPos spawnAng = p.ang or player:GetAngles() spawnPointUsed = p break end end end if spawnPos then break end end if not spawnPos then return false, "Все точки спавна заняты или заблокированы препятствиями" end -- Создаём технику (проверка класса через IsValid после создания) local vehicle = ents.Create(vehicleConfig.class) if not IsValid(vehicle) then return false, "Ошибка создания техники" end vehicle:SetPos(spawnPos) vehicle:SetAngles(spawnAng or player:GetAngles()) vehicle:Spawn() -- Для LVS транспорта нужен Activate после Spawn if vehicle.LVS then vehicle:Activate() end timer.Simple(1, function() if not IsValid(vehicle) then return end if vehicle.Loadouts and loadoutVariant then local loadoutID = tonumber(loadoutVariant) or 1 print("[LVS] Применяю loadout ID=" .. loadoutID .. " для техники " .. vehicleConfig.name) vehicle:SetLoadout(loadoutID) end end) local chosenSkin = nil if istable(vehicleConfig.skin) and next(vehicleConfig.skin or {}) ~= nil then if vehicleConfig.skin[faction] ~= nil then chosenSkin = vehicleConfig.skin[faction] else for k, v in pairs(vehicleConfig.skin) do if tostring(k) == tostring(faction) then chosenSkin = v break end end end end if chosenSkin == nil and vehicleConfig.default_skin ~= nil then chosenSkin = vehicleConfig.default_skin end if chosenSkin ~= nil then pcall(function() vehicle:SetSkin(chosenSkin) end) end if istable(vehicleConfig.default_bodygroups) then for _, bg in ipairs(vehicleConfig.default_bodygroups) do if bg and bg.id and bg.value then pcall(function() vehicle:SetBodygroup(bg.id, bg.value) end) end end end if istable(vehicleConfig.bodygroups) then for k, v in pairs(vehicleConfig.bodygroups) do if isnumber(k) and istable(v) and v.id and v.value then pcall(function() vehicle:SetBodygroup(v.id, v.value) end) elseif isnumber(k) and isnumber(v) then pcall(function() vehicle:SetBodygroup(k, v) end) end end end -- Сохраняем информацию о технике для возможности возврата local vehicleInfo = { owner = player, ownerSteamID = player:SteamID(), vehicleID = vehicleID, vehicleType = vehicleType, vehicleConfig = vehicleConfig, spawnTime = os.time(), faction = faction, appliedSkin = chosenSkin, appliedBodygroups = vehicleConfig.default_bodygroups, appliedLoadout = loadoutVariant, lastUse = CurTime() } -- Сохраняем использованную точку спавна (если была) if spawnPointUsed and spawnPointUsed.pos then vehicleInfo.spawnPoint = spawnPointUsed.pos vehicleInfo.spawnPointAng = spawnPointUsed.ang vehicleInfo.spawnPointID = spawnPointUsed.id end vehicle.lvsVehicleInfo = vehicleInfo self.activeVehicles[vehicle] = vehicleInfo -- Устанавливаем сетевые переменные на сущности, чтобы клиенты могли узнать данные даже если net сообщение не доспело pcall(function() vehicle:SetNWString("LVS_OwnerSteamID", vehicleInfo.ownerSteamID or "") vehicle:SetNWString("LVS_VehicleID", tostring(vehicleInfo.vehicleID or "")) vehicle:SetNWString("LVS_VehicleType", tostring(vehicleInfo.vehicleType or "")) end) -- Синхронизируем информацию с клиентом self:SyncVehicleInfo(vehicle) self:AddFactionPoints(faction, -vehicleConfig.price) vehiclesData[vehicleID] = os.time() + vehicleConfig.cooldown character:SetData("lvs_vehicles", vehiclesData) self:NotifyFaction(faction, player:Name() .. " вызвал " .. vehicleConfig.name .. " за " .. vehicleConfig.price .. " очков") -- Логирование вызова техники local serverlogsPlugin = ix.plugin.list["serverlogs"] if (serverlogsPlugin) then local factionName = ix.faction.Get(faction).name or tostring(faction) local message = string.format("%s вызвал технику '%s' (стоимость: %d очков техники)", player:Nick(), vehicleConfig.name, vehicleConfig.price) serverlogsPlugin:AddLog("VEHICLE_SPAWN", message, player, { vehicleID = vehicleID, vehicleName = vehicleConfig.name, vehicleClass = vehicleConfig.class, vehicleType = vehicleType, price = vehicleConfig.price, faction = faction, factionName = factionName }) end print(player:Name() .. " вызвал " .. vehicleConfig.name .. " (" .. vehicleType .. ")") return true, vehicleConfig.name .. " успешно вызвана!" end -- Функция возврата техники function PLUGIN:ReturnVehicle(vehicle, player) if not IsValid(vehicle) or not self.activeVehicles[vehicle] then return false, "Техника не найдена" end local vehicleInfo = self.activeVehicles[vehicle] -- Проверка: Можно ли вернуть технику по месту спавна (если точка сохранена) local maxDist = PLUGIN.config.returnSettings and PLUGIN.config.returnSettings.maxReturnDistance or 200 if vehicleInfo.spawnPoint then local curPos = vehicle:GetPos() local dist = curPos:DistToSqr(vehicleInfo.spawnPoint) if dist > (maxDist * maxDist) and not player:IsAdmin() then return false, "Техника находится слишком далеко от точки возврата" end end -- Проверяем, может ли игрок вернуть эту технику if not player:IsAdmin() and player:SteamID() ~= vehicleInfo.ownerSteamID then return false, "Вы не можете вернуть чужую технику" end local faction = vehicleInfo.faction local vehicleConfig = vehicleInfo.vehicleConfig local refundAmount = math.floor(vehicleConfig.price * 0.8) -- Возвращаем 80% стоимости -- Возвращаем очки фракции self:AddFactionPoints(faction, refundAmount) -- Сбрасываем кд для владельца if IsValid(vehicleInfo.owner) then local character = vehicleInfo.owner:GetCharacter() if character then local vehiclesData = character:GetData("lvs_vehicles", {}) vehiclesData[vehicleInfo.vehicleID] = nil character:SetData("lvs_vehicles", vehiclesData) vehicleInfo.owner:Notify("Вы вернули " .. vehicleConfig.name .. ". Возвращено " .. refundAmount .. " очков техники") end end -- Удаляем технику vehicle:Remove() self.activeVehicles[vehicle] = nil -- Уведомляем фракцию local returnMessage = player:Name() .. " вернул " .. vehicleConfig.name .. ". Возвращено " .. refundAmount .. " очков техники" self:NotifyFaction(faction, returnMessage) -- Логирование возврата техники local serverlogsPlugin = ix.plugin.list["serverlogs"] if (serverlogsPlugin) then local factionName = ix.faction.Get(faction).name or tostring(faction) local message = string.format("%s вернул технику '%s' (возврат: %d очков техники)", player:Nick(), vehicleConfig.name, refundAmount) serverlogsPlugin:AddLog("VEHICLE_RETURN", message, player, { vehicleID = vehicleInfo.vehicleID, vehicleName = vehicleConfig.name, vehicleClass = vehicleConfig.class, refundAmount = refundAmount, faction = faction, factionName = factionName }) end print(returnMessage) return true, vehicleConfig.name .. " успешно возвращена. Возвращено " .. refundAmount .. " очков" end -- Функция автоматической очистки уничтоженной техники function PLUGIN:VehicleDestroyed(vehicle) if self.activeVehicles[vehicle] then self.activeVehicles[vehicle] = nil end end -- Функция синхронизации информации о технике function PLUGIN:SyncVehicleInfo(vehicle) if not IsValid(vehicle) or not vehicle.lvsVehicleInfo then return end local info = vehicle.lvsVehicleInfo -- Отправляем информацию всем клиентам net.Start("LVS_SyncVehicleInfo") net.WriteEntity(vehicle) net.WriteString(info.ownerSteamID or "") net.WriteString(info.vehicleID or "") net.WriteString(info.vehicleType or "") net.Broadcast() end function PLUGIN:NotifyFaction(faction, message) for _, v in ipairs(player.GetAll()) do if v:Team() == faction then v:Notify(message) end end end util.AddNetworkString("LVS_SyncFactionPoints") util.AddNetworkString("LVS_OpenGroundMenu") util.AddNetworkString("LVS_OpenAirMenu") util.AddNetworkString("LVS_RequestSpawn") util.AddNetworkString("LVS_RequestReturn") util.AddNetworkString("LVS_SyncVehicleInfo") util.AddNetworkString("LVS_SyncVehicleModels") net.Receive("LVS_RequestSpawn", function(len, player) local vehicleID = net.ReadString() local vehicleType = net.ReadString() local loadoutVariant = net.ReadUInt(8) print("[LVS] Запрос спавна от " .. player:Name() .. " - ID: " .. vehicleID .. ", Тип: " .. vehicleType .. ", LoadoutID: " .. loadoutVariant) local success, message = PLUGIN:SpawnVehicle(player, vehicleID, vehicleType, loadoutVariant) player:Notify(message) end) -- Обработка запроса на возврат техники net.Receive("LVS_RequestReturn", function(len, player) local vehicleIndex = net.ReadUInt(16) -- Логирование для отладки print("[LVS] Received LVS_RequestReturn from ", player:Nick(), " (SteamID: ", player:SteamID(), ") with index:", vehicleIndex) local vehicle = Entity(vehicleIndex) if not IsValid(vehicle) then player:Notify("Техника не найдена (индекс: " .. tostring(vehicleIndex) .. ")") print("[LVS] Entity is not valid for index:", vehicleIndex) return end local success, message = PLUGIN:ReturnVehicle(vehicle, player) -- Сообщаем игроку подробное сообщение if success then player:Notify(message) else player:Notify(message or "Не удалось вернуть технику") end end) -- Хук для удаления уничтоженной техники hook.Add("EntityRemoved", "LVS_VehicleCleanup", function(ent) if PLUGIN.activeVehicles[ent] then PLUGIN:VehicleDestroyed(ent) end end) -- Хук автоудаления обломков взорванной техники через 5 минут local WRECK_LIFETIME = 300 -- 5 минут в секундах hook.Add("OnEntityCreated", "LVS_WreckAutoRemove", function(ent) -- Даем энтити немного времени на инициализацию класса timer.Simple(0.1, function() if IsValid(ent) and ent:GetClass() == "lvs_destruction" then timer.Simple(WRECK_LIFETIME, function() if IsValid(ent) then ent:Remove() print("[LVS] Обломки уничтоженной техники очищены (прошло 5 минут)") end end) end end) end) -- Система автоудаления неиспользуемой техники (10 минут) timer.Create("LVS_UnusedVehicleCleanup", 30, 0, function() local currentTime = CurTime() local idleLimit = 600 -- 10 минут for vehicle, info in pairs(PLUGIN.activeVehicles) do if not IsValid(vehicle) then PLUGIN.activeVehicles[vehicle] = nil continue end -- Если в технике есть кто-то, обновляем время использования local hasOccupant = false if IsValid(vehicle:GetDriver()) then hasOccupant = true else if istable(vehicle.pSeats) then for _, seat in pairs(vehicle.pSeats) do if IsValid(seat) and IsValid(seat:GetDriver()) then hasOccupant = true break end end end end if hasOccupant then info.lastUse = currentTime else -- Если техника не занята, проверяем время простоя if (currentTime - (info.lastUse or currentTime)) >= idleLimit then local faction = info.faction local vehicleConfig = info.vehicleConfig local refundAmount = vehicleConfig.price -- Возвращаем полную стоимость при автоудалении -- Возвращаем очки фракции PLUGIN:AddFactionPoints(faction, refundAmount) -- Сбрасываем кд для владельца if IsValid(info.owner) then local character = info.owner:GetCharacter() if character then local vehiclesData = character:GetData("lvs_vehicles", {}) vehiclesData[info.vehicleID] = nil character:SetData("lvs_vehicles", vehiclesData) info.owner:Notify("Ваша техника " .. vehicleConfig.name .. " была удалена из-за простоя. Очки возвращены.") end end -- Удаляем технику vehicle:Remove() PLUGIN.activeVehicles[vehicle] = nil -- Уведомляем фракцию PLUGIN:NotifyFaction(faction, "Техника " .. vehicleConfig.name .. " удалена из-за 10-минутного простоя. Очки возвращены.") -- Логирование local serverlogsPlugin = ix.plugin.list["serverlogs"] if (serverlogsPlugin) then local factionName = ix.faction.Get(faction).name or tostring(faction) local message = string.format("Техника '%s' удалена автоматически (простой 10 мин). Возвращено %d очков.", vehicleConfig.name, refundAmount) serverlogsPlugin:AddLog("VEHICLE_AUTO_REMOVE", message, nil, { vehicleName = vehicleConfig.name, refundAmount = refundAmount, faction = faction }) end print("[LVS] Техника " .. vehicleConfig.name .. " удалена автоматически за простой.") end end end end) -- Команда для возврата техники ix.command.Add("returnvehicle", { description = "Вернуть технику и получить часть очков обратно", arguments = { ix.type.number }, OnRun = function(self, client, vehicleIndex) local vehicle = Entity(tonumber(vehicleIndex)) if not IsValid(vehicle) then return "Техника с индексом " .. vehicleIndex .. " не найдена" end local success, message = PLUGIN:ReturnVehicle(vehicle, client) return message end }) -- Команда для просмотра активной техники ix.command.Add("listvehicles", { description = "Показать активную технику", adminOnly = true, OnRun = function(self, client) local count = 0 local result = "Активная техника:\n" for vehicle, info in pairs(PLUGIN.activeVehicles) do if IsValid(vehicle) then count = count + 1 local ownerName = IsValid(info.owner) and info.owner:Name() or "Неизвестно" result = result .. count .. ". " .. info.vehicleConfig.name .. " (ID: " .. vehicle:EntIndex() .. ") - Владелец: " .. ownerName .. "\n" else PLUGIN.activeVehicles[vehicle] = nil end end if count == 0 then return "Активной техники нет" end return result end }) ix.command.Add("GiveVehiclePoints", { description = "Выдать очки техники фракции", adminOnly = true, arguments = { ix.type.string, ix.type.number }, OnRun = function(self, client, factionName, amount) local targetFaction for factionID, config in pairs(PLUGIN:GetFactions()) do if string.lower(config.name) == string.lower(factionName) then targetFaction = factionID break end end if not targetFaction then return "Фракция не найдена. Доступные: Россия, Украина" end local newPoints = PLUGIN:AddFactionPoints(targetFaction, amount) local factionData = PLUGIN:GetFactions()[targetFaction] PLUGIN:NotifyFaction(targetFaction, "Администратор выдал " .. amount .. " очков техники фракции. Теперь: " .. newPoints) return "Выдано " .. amount .. " очков фракции " .. factionData.name .. ". Теперь: " .. newPoints end }) ix.command.Add("SetVehiclePoints", { description = "Установить очки техники фракции", adminOnly = true, arguments = { ix.type.string, ix.type.number }, OnRun = function(self, client, factionName, amount) local targetFaction for factionID, config in pairs(PLUGIN.config.factions) do if string.lower(config.name) == string.lower(factionName) then targetFaction = factionID break end end if not targetFaction then return "Фракция не найдена. Доступные: Россия, Украина" end PLUGIN.factionPoints[targetFaction] = amount PLUGIN:SyncFactionPoints(targetFaction) local factionData = PLUGIN:GetFactions()[targetFaction] PLUGIN:NotifyFaction(targetFaction, "Администратор установил очки техники на " .. amount) return "Установлено " .. amount .. " очков фракции " .. factionData.name end }) ix.command.Add("CheckVehiclePoints", { description = "Проверить очки всех фракций", adminOnly = true, OnRun = function(self, client) local result = "Очки техники фракций:\n" for factionID, config in pairs(PLUGIN:GetFactions()) do local points = PLUGIN:GetFactionPoints(factionID) result = result .. config.name .. ": " .. points .. " очков\n" end return result end }) ix.command.Add("ResetVehicleCooldowns", { description = "Сбросить кд техники для игрока", adminOnly = true, arguments = { ix.type.player }, OnRun = function(self, client, target) local character = target:GetCharacter() if character then character:SetData("lvs_vehicles", {}) target:Notify("Ваши кд техники были сброшены администратором") return "Кд техники сброшены для " .. target:Name() end return "Ошибка сброса кд" end }) ix.command.Add("ResetAllVehicleCooldowns", { description = "Сбросить кд техники для всех игроков", adminOnly = true, OnRun = function(self, client) for _, ply in ipairs(player.GetAll()) do local character = ply:GetCharacter() if character then character:SetData("lvs_vehicles", {}) ply:Notify("Кд техники сброшены администратором") end end return "Кд техники сброшены для всех игроков" end }) function PLUGIN:InitializedPlugins() timer.Simple(5, function() for factionID, _ in pairs(self:GetFactions()) do self:SyncFactionPoints(factionID) end -- Синхронизируем существующую технику timer.Simple(2, function() for vehicle, _ in pairs(self.activeVehicles) do if IsValid(vehicle) then self:SyncVehicleInfo(vehicle) else self.activeVehicles[vehicle] = nil end end end) -- Подготовим таблицу моделей для клиентов: если в конфиге нет поля model, попробуем получить его из класса timer.Simple(1, function() local mapping = {} for factionID, factionConfig in pairs(self:GetFactions()) do mapping[factionID] = { ground = {}, air = {} } -- ground if factionConfig.groundVehicles then for id, v in pairs(factionConfig.groundVehicles) do local mdl = v.model if not mdl or mdl == "" then -- Попробуем создать временную сущность указанного класса, чтобы прочитать модель if v.class then local ok, ent = pcall(function() return ents.Create(v.class) end) if ok and IsValid(ent) then mdl = ent:GetModel() or "" ent:Remove() else mdl = "" end else mdl = "" end end mapping[factionID].ground[id] = mdl or "" end end -- air if factionConfig.airVehicles then for id, v in pairs(factionConfig.airVehicles) do local mdl = v.model if not mdl or mdl == "" then if v.class then local ok, ent = pcall(function() return ents.Create(v.class) end) if ok and IsValid(ent) then mdl = ent:GetModel() or "" ent:Remove() else mdl = "" end else mdl = "" end end mapping[factionID].air[id] = mdl or "" end end end net.Start("LVS_SyncVehicleModels") net.WriteTable(mapping) net.Broadcast() end) end) end function PLUGIN:InitPostEntity() timer.Simple(10, function() for _, factionID in ipairs(ix.faction.indices) do local cur = self:GetFactionPoints(factionID) or 0 if cur >= 50000 then -- If it's stuck on old limit/bug self.factionPoints[factionID] = 1000 self:SyncFactionPoints(factionID) end end end) end