add sborka
This commit is contained in:
751
garrysmod/gamemodes/militaryrp/plugins/arsenal/sv_plugin.lua
Normal file
751
garrysmod/gamemodes/militaryrp/plugins/arsenal/sv_plugin.lua
Normal file
@@ -0,0 +1,751 @@
|
||||
if SERVER then
|
||||
util.AddNetworkString("ixArsenalOpen")
|
||||
util.AddNetworkString("ixArsenalAction")
|
||||
util.AddNetworkString("ixWardrobeOpen")
|
||||
util.AddNetworkString("ixWardrobeApply")
|
||||
end
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
local config = PLUGIN.config
|
||||
|
||||
|
||||
|
||||
local function IsRecruit(ply)
|
||||
local char = ply:GetCharacter()
|
||||
if not char then return false end
|
||||
local spec = char.GetSpec and char:GetSpec() or 0
|
||||
return spec == 1
|
||||
end
|
||||
|
||||
-- Helpers to store complex data as JSON strings because SetData/GetData may not accept tables
|
||||
local function _loadJSONData(plugin, key)
|
||||
-- plugin:GetData() returns the whole plugin store (or default table). We keep subkeys inside that store.
|
||||
local store = plugin:GetData() or {}
|
||||
if type(store) ~= "table" then return {} end
|
||||
local raw = store[key]
|
||||
if raw == nil then return {} end
|
||||
if type(raw) == "table" then return raw end
|
||||
if type(raw) == "string" then
|
||||
local ok, tbl = pcall(util.JSONToTable, raw)
|
||||
if ok and istable(tbl) then return tbl end
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local function _saveJSONData(plugin, key, tbl)
|
||||
if type(tbl) ~= "table" then tbl = {} end
|
||||
local store = plugin:GetData() or {}
|
||||
if type(store) ~= "table" then store = {} end
|
||||
-- store the table directly under the subkey; ix.data.Set will serialize the whole store
|
||||
store[key] = tbl
|
||||
plugin:SetData(store)
|
||||
end
|
||||
|
||||
-- Faction supply storage helpers
|
||||
function PLUGIN:GetFactionSupply(faction)
|
||||
local store = _loadJSONData(self, "factionSupply") or {}
|
||||
local key = tostring(faction)
|
||||
local val = store[faction]
|
||||
if val == nil then val = store[key] end
|
||||
if val == nil then
|
||||
-- fallback to configured startSupply or 0
|
||||
return config.startSupply[faction] or 0
|
||||
end
|
||||
local maxSupply = (self.config and self.config.maxSupply) or 20000
|
||||
return math.Clamp(tonumber(val) or 0, 0, maxSupply)
|
||||
end
|
||||
|
||||
-- Returns remaining cooldown seconds for free weapons for a specific player (per-player persistent)
|
||||
function PLUGIN:GetFreeWeaponCooldownForPlayer(client)
|
||||
if not IsValid(client) or not client:IsPlayer() then return 0 end
|
||||
local stamps = _loadJSONData(self, "freeWeaponTimestamps") or {}
|
||||
local steam = client:SteamID() or tostring(client:SteamID64() or "unknown")
|
||||
local last = tonumber(stamps[steam]) or 0
|
||||
local now = os.time()
|
||||
local cd = tonumber(config.freeWeaponCooldown) or 0
|
||||
if cd <= 0 then return 0 end
|
||||
local remain = cd - (now - last)
|
||||
if remain < 0 then remain = 0 end
|
||||
return math.floor(remain)
|
||||
end
|
||||
|
||||
-- Returns remaining cooldown seconds for donate weapons (10 minutes = 600 seconds)
|
||||
function PLUGIN:GetDonateWeaponCooldown(client, weaponClass)
|
||||
if not IsValid(client) or not client:IsPlayer() then return 0 end
|
||||
local stamps = _loadJSONData(self, "donateWeaponTimestamps") or {}
|
||||
local steam = client:SteamID() or tostring(client:SteamID64() or "unknown")
|
||||
if not stamps[steam] then return 0 end
|
||||
local last = tonumber(stamps[steam][weaponClass]) or 0
|
||||
local now = os.time()
|
||||
local cd = 600 -- 10 минут
|
||||
local remain = cd - (now - last)
|
||||
if remain < 0 then remain = 0 end
|
||||
return math.floor(remain)
|
||||
end
|
||||
|
||||
function PLUGIN:SetDonateWeaponCooldown(client, weaponClass)
|
||||
if not IsValid(client) or not client:IsPlayer() then return end
|
||||
local stamps = _loadJSONData(self, "donateWeaponTimestamps") or {}
|
||||
local steam = client:SteamID() or tostring(client:SteamID64() or "unknown")
|
||||
if not stamps[steam] then stamps[steam] = {} end
|
||||
stamps[steam][weaponClass] = os.time()
|
||||
_saveJSONData(self, "donateWeaponTimestamps", stamps)
|
||||
end
|
||||
|
||||
function PLUGIN:SetFactionSupply(faction, amount)
|
||||
local store = _loadJSONData(self, "factionSupply") or {}
|
||||
local key = tostring(faction)
|
||||
local value = math.max(tonumber(amount) or 0, 0)
|
||||
store[key] = value
|
||||
store[faction] = value
|
||||
_saveJSONData(self, "factionSupply", store)
|
||||
end
|
||||
|
||||
-- Compatibility helper: add (or subtract) supply for a faction
|
||||
function PLUGIN:AddFactionSupply(faction, delta)
|
||||
local amount = tonumber(delta) or 0
|
||||
local cur = tonumber(self:GetFactionSupply(faction)) or 0
|
||||
local maxSupply = (self.config and self.config.maxSupply) or 20000
|
||||
local new = math.Clamp(cur + amount, 0, maxSupply)
|
||||
|
||||
if new == cur then return end -- Если ничего не изменилось (например, уперлись в лимит)
|
||||
|
||||
self:SetFactionSupply(faction, new)
|
||||
|
||||
-- Логирование изменения снабжения
|
||||
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, new)
|
||||
serverlogsPlugin:AddLog("FACTION_SUPPLY_ADD", message, nil, {
|
||||
faction = faction,
|
||||
factionName = factionName,
|
||||
amount = amount,
|
||||
total = new
|
||||
})
|
||||
elseif amount < 0 then
|
||||
local message = string.format("Фракция '%s' потратила %d очков снабжения (итого: %d)", factionName, math.abs(amount), new)
|
||||
serverlogsPlugin:AddLog("FACTION_SUPPLY_USE", message, nil, {
|
||||
faction = faction,
|
||||
factionName = factionName,
|
||||
amount = math.abs(amount),
|
||||
total = new
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return new
|
||||
end
|
||||
|
||||
-- Получить список доступного оружия для персонажа (заглушка)
|
||||
-- Рекомендуется реализовать: читать FACTION.Spec и FACTION.Podr из ix.faction.Get(faction)
|
||||
function PLUGIN:GetAvailableWeapons(char)
|
||||
local weapons = {}
|
||||
local faction = char:GetFaction()
|
||||
local factionTable = ix.faction.Get(faction)
|
||||
if not factionTable then
|
||||
return weapons
|
||||
end
|
||||
|
||||
local allowed = {}
|
||||
local specIndex = tonumber(char:GetSpec()) or 1
|
||||
if factionTable.Spec and factionTable.Spec[specIndex] and istable(factionTable.Spec[specIndex].weapons) then
|
||||
for _, c in ipairs(factionTable.Spec[specIndex].weapons) do
|
||||
allowed[c] = true
|
||||
end
|
||||
end
|
||||
|
||||
local podrIndex = tonumber(char:GetPodr()) or 1
|
||||
if factionTable.Podr and factionTable.Podr[podrIndex] and istable(factionTable.Podr[podrIndex].preset) then
|
||||
for _, c in ipairs(factionTable.Podr[podrIndex].preset) do
|
||||
allowed[c] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- donate weapons from character data (with expiration check)
|
||||
local donateWeaponsTimed = char:GetData("donate_weapons_timed", {})
|
||||
local donateTable = {}
|
||||
if donateWeaponsTimed and istable(donateWeaponsTimed) then
|
||||
local now = os.time()
|
||||
for wepClass, wepData in pairs(donateWeaponsTimed) do
|
||||
if istable(wepData) and wepData.expires and tonumber(wepData.expires) > now then
|
||||
donateTable[wepClass] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Поддержка старой системы без срока (если есть)
|
||||
local donateList = char:GetData("donate_weapons", false)
|
||||
if donateList and istable(donateList) then
|
||||
for _, c in ipairs(donateList) do donateTable[c] = true end
|
||||
end
|
||||
|
||||
-- IGS integration: check if player has any weapons bought via IGS
|
||||
local client = char:GetPlayer()
|
||||
if IsValid(client) then
|
||||
if IGS then
|
||||
if isfunction(IGS.GetItems) then
|
||||
local items = IGS.GetItems()
|
||||
for _, ITEM in pairs(items) do
|
||||
local uid = (isfunction(ITEM.GetUID) and ITEM:GetUID()) or ITEM.uid
|
||||
if not uid then continue end
|
||||
|
||||
local has = false
|
||||
if isfunction(client.HasPurchase) then
|
||||
has = client:HasPurchase(uid)
|
||||
elseif isfunction(IGS.PlayerHasItem) then
|
||||
has = IGS.PlayerHasItem(client, uid)
|
||||
end
|
||||
|
||||
if has then
|
||||
-- Try all possible ways IGS stores the weapon class
|
||||
local weaponClass = nil
|
||||
local weaponCandidates = {
|
||||
(isfunction(ITEM.GetWeapon) and ITEM:GetWeapon()),
|
||||
(isfunction(ITEM.GetMeta) and ITEM:GetMeta("weapon")),
|
||||
ITEM.weapon,
|
||||
ITEM.weapon_class,
|
||||
ITEM.class,
|
||||
ITEM.uniqueID,
|
||||
ITEM.val,
|
||||
uid
|
||||
}
|
||||
for _, candidate in ipairs(weaponCandidates) do
|
||||
if candidate and candidate ~= "" then
|
||||
weaponClass = candidate
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if weaponClass and type(weaponClass) ~= "string" then
|
||||
weaponClass = tostring(weaponClass)
|
||||
end
|
||||
|
||||
if not weaponClass then
|
||||
weaponClass = uid
|
||||
end
|
||||
|
||||
if weaponClass then
|
||||
-- Check if this class or a similar one exists in config
|
||||
if config.weapons[weaponClass] then
|
||||
donateTable[weaponClass] = true
|
||||
else
|
||||
for configClass, _ in pairs(config.weapons) do
|
||||
if configClass:find(weaponClass, 1, true) then
|
||||
donateTable[configClass] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Build result from config only if class is allowed and exists in config
|
||||
local supply = tonumber(self:GetFactionSupply(faction)) or 0
|
||||
for class, _ in pairs(allowed) do
|
||||
local data = config.weapons[class]
|
||||
if not data then continue end -- skip if not configured
|
||||
|
||||
-- Проверяем есть ли оружие в донате у игрока
|
||||
local hasDonate = donateTable[class]
|
||||
|
||||
if hasDonate then
|
||||
-- Если куплено в донате - показываем как донатное (бесплатно, с кулдауном)
|
||||
local donateData = table.Copy(data)
|
||||
donateData.isDonateVersion = true -- флаг что это донатная версия
|
||||
donateData.supplyPrice = 0
|
||||
donateData.moneyPrice = 0
|
||||
weapons[class] = donateData
|
||||
elseif data.donate_only then
|
||||
-- Оружие только для доната, но не куплено - не показываем
|
||||
continue
|
||||
else
|
||||
-- Обычное оружие из spec/podr - показываем с ценой из конфига
|
||||
if supply <= 0 then
|
||||
if (data.supplyPrice or 0) == 0 then weapons[class] = data end
|
||||
else
|
||||
weapons[class] = data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Добавляем оружие которое есть ТОЛЬКО в донате (не в spec/podr)
|
||||
for wepClass, _ in pairs(donateTable) do
|
||||
if not allowed[wepClass] then
|
||||
local data = config.weapons[wepClass]
|
||||
if data then
|
||||
local donateData = table.Copy(data)
|
||||
donateData.isDonateVersion = true
|
||||
donateData.supplyPrice = 0
|
||||
donateData.moneyPrice = 0
|
||||
weapons[wepClass] = donateData
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return weapons
|
||||
end
|
||||
|
||||
-- Заглушки для действий (реализуйте логику по необходимости)
|
||||
function PLUGIN:BuyWeapon(client, weaponClass, payMethod)
|
||||
local char = client:GetCharacter()
|
||||
if not char then return false, "Нет персонажа" end
|
||||
local available = self:GetAvailableWeapons(char)
|
||||
local data = available[weaponClass]
|
||||
if not data then return false, "Оружие недоступно для покупки" end
|
||||
payMethod = payMethod or "supply"
|
||||
local faction = char:GetFaction()
|
||||
|
||||
-- Option A: pay by money (Helix)
|
||||
if payMethod == "money" then
|
||||
local price = data.moneyPrice or 0
|
||||
if price == 0 then return false, "Это оружие нельзя купить за деньги" end
|
||||
if not char.HasMoney or not char:HasMoney(price) then return false, "Недостаточно денег" end
|
||||
char:TakeMoney(price)
|
||||
client:Give(weaponClass)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check if this is a donate weapon version
|
||||
local isDonateWeapon = (data.isDonateVersion == true)
|
||||
|
||||
if isDonateWeapon then
|
||||
-- Проверяем что оружие уже есть у игрока
|
||||
if client:HasWeapon(weaponClass) then
|
||||
return false, "У вас уже есть это оружие"
|
||||
end
|
||||
|
||||
-- Проверяем кулдаун для донат-оружия (10 минут)
|
||||
local cooldown = self:GetDonateWeaponCooldown(client, weaponClass)
|
||||
if cooldown > 0 then
|
||||
local minutes = math.floor(cooldown / 60)
|
||||
local seconds = cooldown % 60
|
||||
return false, string.format("Вы недавно брали это оружие. Подождите %02d:%02d", minutes, seconds)
|
||||
end
|
||||
|
||||
-- Проверяем категорию (донат оружие тоже может иметь категорию)
|
||||
local newCat = data.category or ""
|
||||
if newCat ~= "" and not newCat:find("^tool") then
|
||||
for _, wep in ipairs(client:GetWeapons()) do
|
||||
if not IsValid(wep) then continue end
|
||||
local wclass = wep:GetClass()
|
||||
local wdata = config.weapons[wclass]
|
||||
if wdata and wdata.category == newCat then
|
||||
return false, "Вы уже имеете оружие этой категории: " .. newCat
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check max ammo limit for launchers
|
||||
if data.maxAmmo then
|
||||
local totalAmmo = 0
|
||||
if data.ammoType == "PanzerFaust3 Rocket" or data.ammoType == "rpg_round" then
|
||||
totalAmmo = client:GetAmmoCount("rpg_round") + client:GetAmmoCount("PanzerFaust3 Rocket")
|
||||
else
|
||||
totalAmmo = client:GetAmmoCount(data.ammoType or "rpg_round")
|
||||
end
|
||||
if totalAmmo >= data.maxAmmo then
|
||||
return false, "Вы достигли максимального количества снарядов для этого типа оружия (" .. tostring(data.maxAmmo) .. ")"
|
||||
end
|
||||
end
|
||||
|
||||
-- Выдаем оружие и ставим кулдаун
|
||||
client:Give(weaponClass)
|
||||
|
||||
-- Reset ammo for launchers to prevent extra ammo
|
||||
if data.ammoType then
|
||||
client:SetAmmo(0, data.ammoType)
|
||||
end
|
||||
|
||||
self:SetDonateWeaponCooldown(client, weaponClass)
|
||||
|
||||
-- Логирование выдачи донат-оружия
|
||||
end
|
||||
|
||||
-- Default: pay by supply
|
||||
local supplyCost = data.supplyPrice or 0
|
||||
local factionSupply = self:GetFactionSupply(faction)
|
||||
|
||||
-- If weapon is free (supplyCost == 0), enforce per-player persistent cooldown
|
||||
if supplyCost == 0 then
|
||||
local stamps = _loadJSONData(self, "freeWeaponTimestamps") or {}
|
||||
local steam = client:SteamID() or tostring(client:SteamID64() or "unknown")
|
||||
local last = tonumber(stamps[steam]) or 0
|
||||
local now = os.time()
|
||||
local cd = tonumber(config.freeWeaponCooldown) or 0
|
||||
if cd > 0 and (now - last) < cd then
|
||||
local remain = cd - (now - last)
|
||||
return false, "Вы уже брали бесплатное оружие недавно. Подождите " .. tostring(remain) .. " сек."
|
||||
end
|
||||
-- don't write timestamp yet: write after successful give
|
||||
else
|
||||
if supplyCost > 0 then
|
||||
if factionSupply < supplyCost then return false, "Недостаточно очков снабжения у фракции" end
|
||||
self:AddFactionSupply(faction, -supplyCost)
|
||||
end
|
||||
end
|
||||
-- Check category conflict: prevent two weapons from same category
|
||||
local newCat = data.category or ""
|
||||
if newCat ~= "" and not newCat:find("^tool") then
|
||||
for _, wep in ipairs(client:GetWeapons()) do
|
||||
if not IsValid(wep) then continue end
|
||||
local wclass = wep:GetClass()
|
||||
local wdata = config.weapons[wclass]
|
||||
if wdata and wdata.category == newCat then
|
||||
return false, "Вы уже имеете оружие этой категории: " .. newCat
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check max ammo limit for launchers
|
||||
if data.maxAmmo then
|
||||
local totalAmmo = client:GetAmmoCount(data.ammoType or "rpg_round")
|
||||
if totalAmmo >= data.maxAmmo then
|
||||
return false, "Вы достигли максимального количества снарядов для этого типа оружия (" .. tostring(data.maxAmmo) .. ")"
|
||||
end
|
||||
end
|
||||
-- Check max count limit for weapons
|
||||
if data.maxCount and client:HasWeapon(weaponClass) then
|
||||
return false, "У вас уже есть это оружие"
|
||||
end
|
||||
|
||||
client:Give(weaponClass)
|
||||
|
||||
-- Reset ammo for launchers to prevent extra ammo
|
||||
if data.ammoType then
|
||||
client:SetAmmo(0, data.ammoType)
|
||||
end
|
||||
-- If free weapon granted, save timestamp per-player
|
||||
if supplyCost == 0 then
|
||||
local stamps = _loadJSONData(self, "freeWeaponTimestamps") or {}
|
||||
local steam = client:SteamID() or tostring(client:SteamID64() or "unknown")
|
||||
stamps[steam] = os.time()
|
||||
_saveJSONData(self, "freeWeaponTimestamps", stamps)
|
||||
end
|
||||
|
||||
-- Логирование выдачи оружия из арсенала
|
||||
local serverlogsPlugin = ix.plugin.list["serverlogs"]
|
||||
if (serverlogsPlugin) then
|
||||
local weaponName = data.name or weaponClass
|
||||
local factionName = ix.faction.Get(faction).name or tostring(faction)
|
||||
local message = string.format("%s получил оружие '%s' из арсенала (стоимость: %d снабжения)", client:Nick(), weaponName, supplyCost)
|
||||
serverlogsPlugin:AddLog("WEAPON_SPAWN", message, client, {
|
||||
weaponClass = weaponClass,
|
||||
weaponName = weaponName,
|
||||
supplyCost = supplyCost,
|
||||
faction = faction,
|
||||
factionName = factionName
|
||||
})
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:BuyAmmo(client, ammoType)
|
||||
local char = client:GetCharacter()
|
||||
if not char then return false, "Нет персонажа" end
|
||||
|
||||
-- Запрет на покупку патронов для ракетниц
|
||||
if ammoType == "rpg_round" or ammoType == "PanzerFaust3 Rocket" then
|
||||
return false, "Патроны для этого типа оружия недоступны для покупки"
|
||||
end
|
||||
|
||||
local ammoData = config.ammo[ammoType]
|
||||
if not ammoData then return false, "Тип патронов не найден" end
|
||||
|
||||
local faction = char:GetFaction()
|
||||
local supply = self:GetFactionSupply(faction)
|
||||
|
||||
local price = ammoData.price or 0
|
||||
if price > 0 then
|
||||
if supply < price then return false, "Недостаточно очков снабжения у фракции" end
|
||||
self:AddFactionSupply(faction, -price)
|
||||
end
|
||||
|
||||
client:GiveAmmo(ammoData.amount or 30, ammoType, true)
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:ReturnWeapon(client, weaponClass)
|
||||
local char = client:GetCharacter()
|
||||
if not char then return false, "Нет персонажа" end
|
||||
local data = config.weapons[weaponClass]
|
||||
if not data then return false, "Оружие не найдено" end
|
||||
|
||||
local faction = char:GetFaction()
|
||||
-- Remove weapon
|
||||
client:StripWeapon(weaponClass)
|
||||
|
||||
-- Reset ammo for launchers to prevent extra ammo
|
||||
if data.ammoType then
|
||||
client:SetAmmo(0, data.ammoType)
|
||||
end
|
||||
|
||||
-- Если это донат-оружие, сбрасываем кулдаун
|
||||
if data.donate == true then
|
||||
local stamps = _loadJSONData(self, "donateWeaponTimestamps") or {}
|
||||
local steam = client:SteamID() or tostring(client:SteamID64() or "unknown")
|
||||
if stamps[steam] and stamps[steam][weaponClass] then
|
||||
stamps[steam][weaponClass] = nil
|
||||
_saveJSONData(self, "donateWeaponTimestamps", stamps)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local refund = math.floor((data.supplyPrice or 0) * 0.8)
|
||||
if refund > 0 then self:AddFactionSupply(faction, refund) end
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
local data = self:GetData() or {}
|
||||
data.entities = {}
|
||||
|
||||
for _, entity in ipairs(ents.FindByClass("ix_arsenal")) do
|
||||
data.entities[#data.entities + 1] = {
|
||||
class = "ix_arsenal",
|
||||
pos = entity:GetPos(),
|
||||
angles = entity:GetAngles()
|
||||
}
|
||||
end
|
||||
|
||||
for _, entity in ipairs(ents.FindByClass("ix_ammobox")) do
|
||||
data.entities[#data.entities + 1] = {
|
||||
class = "ix_ammobox",
|
||||
pos = entity:GetPos(),
|
||||
angles = entity:GetAngles()
|
||||
}
|
||||
end
|
||||
|
||||
for _, entity in ipairs(ents.FindByClass("ix_wardrobe")) do
|
||||
data.entities[#data.entities + 1] = {
|
||||
class = "ix_wardrobe",
|
||||
pos = entity:GetPos(),
|
||||
angles = entity:GetAngles()
|
||||
}
|
||||
end
|
||||
|
||||
self:SetData(data)
|
||||
end
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
local data = self:GetData() or {}
|
||||
local entities = data.entities or data -- fallback to compatibility if no 'entities' key
|
||||
|
||||
for _, v in ipairs(entities) do
|
||||
if not v.class then continue end
|
||||
local entity = ents.Create(v.class or "ix_arsenal")
|
||||
entity:SetPos(v.pos)
|
||||
entity:SetAngles(v.angles)
|
||||
entity:Spawn()
|
||||
|
||||
local phys = entity:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Обработчик сетевых действий от клиента
|
||||
if SERVER then
|
||||
net.Receive("ixArsenalAction", function(len, ply)
|
||||
local action = net.ReadString()
|
||||
local id = net.ReadString()
|
||||
local plugin = ix.plugin.list["arsenal"]
|
||||
if not plugin then return end
|
||||
|
||||
if IsRecruit(ply) then
|
||||
ply:Notify("Новоприбывший не может пользоваться арсеналом.")
|
||||
return
|
||||
end
|
||||
|
||||
if action == "buy_weapon" then
|
||||
local method = net.ReadString()
|
||||
local ok, msg = plugin:BuyWeapon(ply, id, method)
|
||||
if not ok then
|
||||
ply:Notify(msg or "Ошибка покупки оружия")
|
||||
end
|
||||
elseif action == "buy_ammo" then
|
||||
local ok, msg = plugin:BuyAmmo(ply, id)
|
||||
if not ok then
|
||||
ply:Notify(msg or "Ошибка покупки патронов")
|
||||
end
|
||||
elseif action == "return_weapon" then
|
||||
local ok, msg = plugin:ReturnWeapon(ply, id)
|
||||
if not ok then
|
||||
ply:Notify(msg or "Ошибка возврата оружия")
|
||||
end
|
||||
elseif action == "buy_armor" then
|
||||
local method = net.ReadString()
|
||||
local ok, msg = plugin:BuyArmor(ply, id, method)
|
||||
if not ok then
|
||||
ply:Notify(msg or "Ошибка покупки брони")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Buy armor implementation
|
||||
function PLUGIN:BuyArmor(client, armorClass, payMethod)
|
||||
local char = client:GetCharacter()
|
||||
if not char then return false, "Нет персонажа" end
|
||||
local aData = config.armor[armorClass]
|
||||
if not aData then return false, "Броня не найдена" end
|
||||
payMethod = payMethod or "supply"
|
||||
|
||||
-- Check current armor: Armor() is the player's current armor value
|
||||
local curArmor = tonumber(client:Armor()) or 0
|
||||
local giveAmount = tonumber(aData.amount) or 0
|
||||
if curArmor >= giveAmount then
|
||||
return false, "Вы не можете купить броню — у вас уже есть броня этого уровня или выше"
|
||||
end
|
||||
|
||||
local faction = char:GetFaction()
|
||||
|
||||
-- money path
|
||||
if payMethod == "money" then
|
||||
local price = aData.moneyPrice or 0
|
||||
if price <= 0 then return false, "Эту броню нельзя купить за деньги" end
|
||||
if not char.HasMoney or not char:HasMoney(price) then return false, "Недостаточно денег" end
|
||||
char:TakeMoney(price)
|
||||
client:SetArmor(giveAmount)
|
||||
return true
|
||||
end
|
||||
|
||||
-- supply path
|
||||
local supplyCost = aData.supplyPrice or 0
|
||||
local factionSupply = self:GetFactionSupply(faction)
|
||||
if supplyCost > 0 then
|
||||
if factionSupply < supplyCost then return false, "Недостаточно очков снабжения у фракции" end
|
||||
self:AddFactionSupply(faction, -supplyCost)
|
||||
end
|
||||
|
||||
client:SetArmor(giveAmount)
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, character, currentChar)
|
||||
if not IsValid(client) then return end
|
||||
timer.Simple(0.5, function()
|
||||
if not IsValid(client) or not client:GetCharacter() then return end
|
||||
local char = client:GetCharacter()
|
||||
local bodygroups = char:GetData("bodygroups", {})
|
||||
local skin = char:GetData("skin", 0)
|
||||
local patch = char:GetData("patchIndex", 1)
|
||||
for idx, value in pairs(bodygroups) do
|
||||
client:SetBodygroup(idx, value)
|
||||
end
|
||||
client:SetSkin(skin)
|
||||
if self.config.patchIndex and self.config.patchMaterials then
|
||||
local pMat = self.config.patchMaterials[patch]
|
||||
if pMat then
|
||||
client:SetSubMaterial(self.config.patchIndex, pMat)
|
||||
else
|
||||
client:SetSubMaterial(self.config.patchIndex, "")
|
||||
end
|
||||
end
|
||||
hook.Run("PostPlayerLoadout", client)
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:PostPlayerLoadout(client)
|
||||
local char = client:GetCharacter()
|
||||
if not char then return end
|
||||
local model = client:GetModel()
|
||||
local bgs = char:GetData("bodygroups", {})
|
||||
local speedMod = 1
|
||||
local maxArmor = 100
|
||||
local ammoMod = 1
|
||||
local stats = self.config.wardrobeStats and self.config.wardrobeStats[model]
|
||||
if stats then
|
||||
for idx, val in pairs(bgs) do
|
||||
if stats[idx] and stats[idx][val] then
|
||||
local st = stats[idx][val]
|
||||
if st.speed then speedMod = speedMod * st.speed end
|
||||
if st.armor then maxArmor = maxArmor + st.armor end
|
||||
if st.ammo then ammoMod = ammoMod * st.ammo end
|
||||
end
|
||||
end
|
||||
end
|
||||
client:SetWalkSpeed(ix.config.Get("walkSpeed") * speedMod)
|
||||
client:SetRunSpeed(ix.config.Get("runSpeed") * speedMod)
|
||||
client:SetMaxArmor(maxArmor)
|
||||
char:SetData("ammoMod", ammoMod)
|
||||
end
|
||||
|
||||
function PLUGIN:EntityTakeDamage(target, dmginfo)
|
||||
if not target:IsPlayer() then return end
|
||||
local char = target:GetCharacter()
|
||||
if not char then return end
|
||||
local model = target:GetModel()
|
||||
local bgs = char:GetData("bodygroups", {})
|
||||
local resist = 1
|
||||
local stats = self.config.wardrobeStats and self.config.wardrobeStats[model]
|
||||
if stats then
|
||||
for idx, val in pairs(bgs) do
|
||||
if stats[idx] and stats[idx][val] then
|
||||
local st = stats[idx][val]
|
||||
if st.dmgResist then
|
||||
resist = resist * st.dmgResist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if resist ~= 1 then
|
||||
dmginfo:ScaleDamage(resist)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:InitPostEntity()
|
||||
-- Уменьшаем задержку до 2 секунд для более быстрой очистки при старте
|
||||
timer.Simple(2, function()
|
||||
-- Загружаем сырые данные из стора напрямую
|
||||
local currentData = _loadJSONData(self, "factionSupply") or {}
|
||||
|
||||
-- Перебираем все записи в сторе. Это надежнее, чем идти по индексам фракций,
|
||||
-- так как это затронет любые старые или "кривые" ключи в базе.
|
||||
local changed = false
|
||||
for key, value in pairs(currentData) do
|
||||
local factionID = tonumber(key)
|
||||
local rawVal = tonumber(value) or 0
|
||||
|
||||
if factionID and rawVal > 5000 then
|
||||
local startVal = (self.config.startSupply and self.config.startSupply[factionID]) or 2000
|
||||
|
||||
-- Устанавливаем новое значение
|
||||
currentData[key] = startVal
|
||||
currentData[factionID] = startVal
|
||||
changed = true
|
||||
|
||||
print(string.format("[Arsenal] Автосброс при старте: фракция %s, было %d, стало %d", key, rawVal, startVal))
|
||||
end
|
||||
end
|
||||
|
||||
if changed then
|
||||
_saveJSONData(self, "factionSupply", currentData)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSetHandsModel(client, ent)
|
||||
local char = client:GetCharacter()
|
||||
if not char then return end
|
||||
local model = client:GetModel()
|
||||
local bgs = char:GetData("bodygroups", {})
|
||||
local chands = self.config.wardrobeCHands and self.config.wardrobeCHands[model]
|
||||
if chands then
|
||||
for idx, val in pairs(bgs) do
|
||||
if chands[idx] and chands[idx][val] then
|
||||
local hData = chands[idx][val]
|
||||
ent:SetModel(hData.model or "models/weapons/c_arms_cstrike.mdl")
|
||||
ent:SetSkin(hData.skin or 0)
|
||||
ent:SetBodyGroups(hData.bodygroups or "0000000")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user