Files
2026-03-31 10:27:04 +03:00

693 lines
25 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
local PLUGIN = PLUGIN
-- Таблица активных отрядов
PLUGIN.squads = PLUGIN.squads or {}
PLUGIN.playerSquads = PLUGIN.playerSquads or {} -- [SteamID] = squadID
PLUGIN.squadInvites = PLUGIN.squadInvites or {} -- [SteamID] = {squadID, inviterSteamID}
util.AddNetworkString("ixSquadCreate")
util.AddNetworkString("ixSquadDisband")
util.AddNetworkString("ixSquadInvite")
util.AddNetworkString("ixSquadAcceptInvite")
util.AddNetworkString("ixSquadDeclineInvite")
util.AddNetworkString("ixSquadKick")
util.AddNetworkString("ixSquadLeave")
util.AddNetworkString("ixSquadPromote")
util.AddNetworkString("ixSquadSync")
util.AddNetworkString("ixSquadNotify")
util.AddNetworkString("ixSquadUpdateMarkers")
util.AddNetworkString("ixSquadPlaceMarker")
util.AddNetworkString("ixSquadWorldMarker")
local function IsAdminMode(ply)
return ply.GetNetVar and ply:GetNetVar("AdminMode", false)
end
-- Генерация уникального ID отряда
function PLUGIN:GenerateSquadID()
local id
repeat
id = "squad_" .. os.time() .. "_" .. math.random(1000, 9999)
until not self.squads[id]
return id
end
-- Получить отряд игрока
function PLUGIN:GetPlayerSquad(client)
local steamID = client:SteamID()
local squadID = self.playerSquads[steamID]
if squadID then
return self.squads[squadID], squadID
end
return nil, nil
end
-- Получить фракцию игрока
function PLUGIN:GetPlayerFaction(client)
local character = client:GetCharacter()
if not character then return nil end
return character:GetFaction()
end
-- Создание отряда
function PLUGIN:CreateSquad(leader)
if IsAdminMode(leader) then
return false, "Администратор не может создавать отряд"
end
if not IsValid(leader) then
return false, "Недействительный игрок"
end
local steamID = leader:SteamID()
-- Проверяем, не состоит ли уже в отряде
if self.playerSquads[steamID] then
return false, "Вы уже состоите в отряде"
end
local faction = self:GetPlayerFaction(leader)
if not faction then
return false, "У вас нет активного персонажа"
end
local squadID = self:GenerateSquadID()
self.squads[squadID] = {
id = squadID,
leader = steamID,
faction = faction,
members = {steamID},
memberData = {
[steamID] = {
name = leader:Name(),
isLeader = true,
joinTime = os.time()
}
},
createdAt = os.time()
}
self.playerSquads[steamID] = squadID
self:SyncSquadToMembers(squadID)
self:StartSquadMarkers(squadID)
-- Логирование создания отряда
local serverlogsPlugin = ix.plugin.list["serverlogs"]
if (serverlogsPlugin) then
local factionName = ix.faction.Get(faction).name or tostring(faction)
local message = string.format("%s создал отряд (ID: %s, фракция: %s)", leader:Nick(), squadID, factionName)
serverlogsPlugin:AddLog("SQUAD_CREATE", message, leader, {
squadID = squadID,
faction = faction,
factionName = factionName
})
end
return true, "Отряд успешно создан"
end
-- Расформирование отряда
function PLUGIN:DisbandSquad(squadID)
local squad = self.squads[squadID]
if not squad then return false, "Отряд не найден" end
-- Логируем расформирование перед удалением
local serverlogsPlugin = ix.plugin.list["serverlogs"]
if (serverlogsPlugin) then
local leaderName = squad.memberData[squad.leader] and squad.memberData[squad.leader].name or "Unknown"
local factionName = ix.faction.Get(squad.faction).name or tostring(squad.faction)
local message = string.format("Отряд %s расформирован (лидер: %s, членов: %d, фракция: %s)",
squadID, leaderName, #squad.members, factionName)
serverlogsPlugin:AddLog("SQUAD_DELETE", message, nil, {
squadID = squadID,
leader = squad.leader,
leaderName = leaderName,
memberCount = #squad.members,
faction = squad.faction,
factionName = factionName
})
end
-- Останавливаем маркеры
self:StopSquadMarkers(squadID)
-- Очищаем данные на клиенте и уведомляем всех членов
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
-- Отправляем пустую таблицу для очистки данных на клиенте
net.Start("ixSquadSync")
net.WriteString("{}")
net.Send(member)
net.Start("ixSquadNotify")
net.WriteString("Отряд был расформирован")
net.Send(member)
end
self.playerSquads[memberSteamID] = nil
end
self.squads[squadID] = nil
return true, "Отряд расформирован"
end
-- Приглашение в отряд
function PLUGIN:InviteToSquad(inviter, target)
print("[SQUADS] InviteToSquad вызван: inviter=" .. (IsValid(inviter) and inviter:Name() or "NIL") .. ", target=" .. (IsValid(target) and target:Name() or "NIL"))
if IsAdminMode(inviter) then
return false, "Администратор не может приглашать в отряд"
end
if not IsValid(inviter) or not IsValid(target) then
print("[SQUADS] Ошибка: недействительный игрок")
return false, "Недействительный игрок"
end
local squad, squadID = self:GetPlayerSquad(inviter)
if not squad then
print("[SQUADS] Ошибка: инвайтер не в отряде")
return false, "Вы не состоите в отряде"
end
print("[SQUADS] Отряд найден: " .. squadID)
local inviterSteamID = inviter:SteamID()
if squad.leader ~= inviterSteamID then
print("[SQUADS] Ошибка: не лидер (leader=" .. squad.leader .. ", inviter=" .. inviterSteamID .. ")")
return false, "Только лидер может приглашать игроков"
end
local targetSteamID = target:SteamID()
-- Проверяем, не состоит ли уже в отряде
if self.playerSquads[targetSteamID] then
print("[SQUADS] Ошибка: target уже в отряде")
return false, target:Name() .. " уже состоит в отряде"
end
-- Проверяем лимит
if #squad.members >= ix.config.Get("squadMaxMembers", 16) then
print("[SQUADS] Ошибка: отряд заполнен")
return false, "Отряд заполнен (макс. " .. ix.config.Get("squadMaxMembers", 16) .. " человек)"
end
-- Проверяем фракцию
local targetFaction = self:GetPlayerFaction(target)
if targetFaction ~= squad.faction then
print("[SQUADS] Ошибка: разные фракции (squad=" .. squad.faction .. ", target=" .. (targetFaction or "NIL") .. ")")
return false, target:Name() .. " из другой фракции"
end
-- Проверяем, нет ли уже приглашения
if self.squadInvites[targetSteamID] then
print("[SQUADS] Ошибка: у target уже есть приглашение")
return false, target:Name() .. " уже имеет активное приглашение"
end
-- Сохраняем приглашение
self.squadInvites[targetSteamID] = {
squadID = squadID,
inviterSteamID = inviterSteamID,
inviterName = inviter:Name(),
time = CurTime()
}
print("[SQUADS] Приглашение сохранено для " .. target:Name())
-- Отправляем приглашение
net.Start("ixSquadInvite")
net.WriteString(squadID)
net.WriteString(inviter:Name())
net.Send(target)
print("[SQUADS] Приглашение отправлено клиенту " .. target:Name())
-- Уведомляем инвайтера
net.Start("ixSquadNotify")
net.WriteString("Приглашение отправлено " .. target:Name())
net.Send(inviter)
-- Логирование приглашения в отряд
local serverlogsPlugin = ix.plugin.list["serverlogs"]
if (serverlogsPlugin) then
local message = string.format("%s пригласил %s в отряд %s", inviter:Nick(), target:Nick(), squadID)
serverlogsPlugin:AddLog("SQUAD_INVITE", message, inviter, {
squadID = squadID,
targetSteamID = targetSteamID,
targetName = target:Nick()
})
end
-- Автоматическое удаление через 30 секунд
timer.Simple(30, function()
if self.squadInvites[targetSteamID] and self.squadInvites[targetSteamID].squadID == squadID then
self.squadInvites[targetSteamID] = nil
print("[SQUADS] Приглашение для " .. targetSteamID .. " истекло")
end
end)
return true, "Приглашение отправлено"
end
-- Принятие приглашения
function PLUGIN:AcceptSquadInvite(client)
local steamID = client:SteamID()
local invite = self.squadInvites[steamID]
if IsAdminMode(client) then
return false, "Администратор не может вступать в отряд"
end
if not invite then
return false, "У вас нет активных приглашений"
end
local squad = self.squads[invite.squadID]
if not squad then
self.squadInvites[steamID] = nil
return false, "Отряд больше не существует"
end
-- Проверяем лимит еще раз
if #squad.members >= ix.config.Get("squadMaxMembers", 16) then
self.squadInvites[steamID] = nil
return false, "Отряд уже заполнен"
end
-- Проверяем фракцию еще раз (на случай смены персонажа)
local currentFaction = self:GetPlayerFaction(client)
if currentFaction ~= squad.faction then
self.squadInvites[steamID] = nil
return false, "Вы больше не в той же фракции"
end
-- Добавляем в отряд
table.insert(squad.members, steamID)
squad.memberData[steamID] = {
name = client:Name(),
isLeader = false,
joinTime = os.time()
}
self.playerSquads[steamID] = invite.squadID
self.squadInvites[steamID] = nil
-- Уведомляем всех членов отряда
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
net.Start("ixSquadNotify")
net.WriteString(client:Name() .. " присоединился к отряду")
net.Send(member)
end
end
self:SyncSquadToMembers(invite.squadID)
-- Логирование вступления в отряд
local serverlogsPlugin = ix.plugin.list["serverlogs"]
if (serverlogsPlugin) then
local factionName = ix.faction.Get(squad.faction).name or tostring(squad.faction)
local message = string.format("%s присоединился к отряду %s (лидер: %s)",
client:Nick(), invite.squadID, squad.memberData[squad.leader].name)
serverlogsPlugin:AddLog("SQUAD_JOIN", message, client, {
squadID = invite.squadID,
inviterSteamID = invite.inviterSteamID,
inviterName = invite.inviterName,
faction = squad.faction,
factionName = factionName
})
end
return true, "Вы присоединились к отряду"
end
-- Исключение из отряда
function PLUGIN:KickFromSquad(kicker, targetSteamID)
if not IsValid(kicker) then return false, "Недействительный игрок" end
local squad, squadID = self:GetPlayerSquad(kicker)
if not squad then
return false, "Вы не состоите в отряде"
end
if squad.leader ~= kicker:SteamID() then
return false, "Только лидер может исключать игроков"
end
if targetSteamID == kicker:SteamID() then
return false, "Используйте расформирование отряда"
end
-- Удаляем из отряда
for i, memberSteamID in ipairs(squad.members) do
if memberSteamID == targetSteamID then
table.remove(squad.members, i)
break
end
end
local targetName = squad.memberData[targetSteamID].name
squad.memberData[targetSteamID] = nil
self.playerSquads[targetSteamID] = nil
-- Уведомляем исключенного игрока и очищаем его данные
local target = player.GetBySteamID(targetSteamID)
if IsValid(target) then
-- Очищаем данные отряда на клиенте
net.Start("ixSquadSync")
net.WriteString("{}")
net.Send(target)
net.Start("ixSquadNotify")
net.WriteString("Вы были исключены из отряда")
net.Send(target)
end
-- Уведомляем остальных
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
net.Start("ixSquadNotify")
net.WriteString(targetName .. " был исключен из отряда")
net.Send(member)
end
end
self:SyncSquadToMembers(squadID)
-- Логирование исключения из отряда
local serverlogsPlugin = ix.plugin.list["serverlogs"]
if (serverlogsPlugin) then
local message = string.format("%s исключил %s из отряда %s", kicker:Nick(), targetName, squadID)
serverlogsPlugin:AddLog("SQUAD_KICK", message, kicker, {
squadID = squadID,
targetSteamID = targetSteamID,
targetName = targetName
})
end
return true, targetName .. " исключен из отряда"
end
-- Выход из отряда
function PLUGIN:LeaveSquad(client)
local squad, squadID = self:GetPlayerSquad(client)
if not squad then
return false, "Вы не состоите в отряде"
end
local steamID = client:SteamID()
-- Если лидер - расформировываем отряд
if squad.leader == steamID then
return self:DisbandSquad(squadID)
end
-- Удаляем из отряда
for i, memberSteamID in ipairs(squad.members) do
if memberSteamID == steamID then
table.remove(squad.members, i)
break
end
end
squad.memberData[steamID] = nil
self.playerSquads[steamID] = nil
-- Уведомляем остальных
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
net.Start("ixSquadNotify")
net.WriteString(client:Name() .. " покинул отряд")
net.Send(member)
end
end
self:SyncSquadToMembers(squadID)
-- Логирование выхода из отряда
local serverlogsPlugin = ix.plugin.list["serverlogs"]
if (serverlogsPlugin) then
local message = string.format("%s покинул отряд %s", client:Nick(), squadID)
serverlogsPlugin:AddLog("SQUAD_LEAVE", message, client, {
squadID = squadID
})
end
return true, "Вы покинули отряд"
end
-- Система маркеров на компасе
function PLUGIN:StartSquadMarkers(squadID)
local timerName = "ixSquadMarkers_" .. squadID
timer.Create(timerName, ix.config.Get("squadMarkerUpdateRate", 0.5), 0, function()
local squad = self.squads[squadID]
if not squad then
timer.Remove(timerName)
return
end
-- Обновляем маркеры для каждого члена отряда
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
-- Получаем других членов отряда
local teammates = {}
for _, otherSteamID in ipairs(squad.members) do
if otherSteamID ~= memberSteamID then
local teammate = player.GetBySteamID(otherSteamID)
if IsValid(teammate) and teammate:Alive() then
table.insert(teammates, teammate)
end
end
end
-- Отправляем позиции союзников этому игроку
if #teammates > 0 then
net.Start("ixSquadUpdateMarkers")
net.WriteUInt(#teammates, 8)
for _, teammate in ipairs(teammates) do
net.WriteVector(teammate:GetPos())
net.WriteString(teammate:Name())
net.WriteEntity(teammate)
end
net.Send(member)
end
end
end
end)
end
function PLUGIN:StopSquadMarkers(squadID)
timer.Remove("ixSquadMarkers_" .. squadID)
end
-- Синхронизация данных отряда
function PLUGIN:SyncSquadToMembers(squadID)
local squad = self.squads[squadID]
if not squad then return end
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
net.Start("ixSquadSync")
net.WriteString(util.TableToJSON(squad))
net.Send(member)
end
end
end
function PLUGIN:SyncAllSquads()
for _, client in ipairs(player.GetAll()) do
local squad, squadID = self:GetPlayerSquad(client)
if squad then
net.Start("ixSquadSync")
net.WriteString(util.TableToJSON(squad))
net.Send(client)
end
end
end
-- Обработка отключения игрока
function PLUGIN:PlayerDisconnected(client)
if not IsValid(client) then return end
local steamID = client:SteamID()
local squad, squadID = self:GetPlayerSquad(client)
if squad then
-- Если это лидер - расформировываем отряд
if squad.leader == steamID then
print("[SQUADS] Лидер " .. client:Name() .. " отключился, расформирование отряда " .. squadID)
self:DisbandSquad(squadID)
else
-- Обычный участник - просто покидает отряд
print("[SQUADS] Игрок " .. client:Name() .. " отключился, удаление из отряда " .. squadID)
-- Удаляем из списка участников
for i, memberSteamID in ipairs(squad.members) do
if memberSteamID == steamID then
table.remove(squad.members, i)
break
end
end
squad.memberData[steamID] = nil
self.playerSquads[steamID] = nil
-- Уведомляем остальных участников
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
net.Start("ixSquadNotify")
net.WriteString(client:Name() .. " покинул отряд (отключение)")
net.Send(member)
end
end
-- Синхронизируем данные с оставшимися участниками
self:SyncSquadToMembers(squadID)
end
end
-- Удаляем приглашения
self.squadInvites[steamID] = nil
end
-- Сетевые обработчики
net.Receive("ixSquadCreate", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then
return
end
if IsAdminMode(client) then return end
local success, message = plugin:CreateSquad(client)
net.Start("ixSquadNotify")
net.WriteString(message)
net.Send(client)
end)
net.Receive("ixSquadDisband", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if IsAdminMode(client) then return end
local squad, squadID = plugin:GetPlayerSquad(client)
if squad and squad.leader == client:SteamID() then
plugin:DisbandSquad(squadID)
end
end)
net.Receive("ixSquadInvite", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if IsAdminMode(client) then return end
local targetSteamID = net.ReadString()
print("[SQUADS SERVER] Получен запрос на приглашение от " .. client:Name() .. " для SteamID: " .. targetSteamID)
local target = player.GetBySteamID(targetSteamID)
print("[SQUADS SERVER] Найден игрок: " .. (IsValid(target) and target:Name() or "NIL"))
if IsValid(target) then
local success, message = plugin:InviteToSquad(client, target)
print("[SQUADS SERVER] Результат приглашения: " .. tostring(success) .. " - " .. message)
net.Start("ixSquadNotify")
net.WriteString(message)
net.Send(client)
else
net.Start("ixSquadNotify")
net.WriteString("Игрок не найден")
net.Send(client)
end
end)
net.Receive("ixSquadAcceptInvite", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if IsAdminMode(client) then return end
local success, message = plugin:AcceptSquadInvite(client)
net.Start("ixSquadNotify")
net.WriteString(message)
net.Send(client)
end)
net.Receive("ixSquadDeclineInvite", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if IsAdminMode(client) then return end
local invite = plugin.squadInvites[client:SteamID()]
-- Уведомляем лидера об отклонении
if invite then
local inviter = player.GetBySteamID(invite.inviterSteamID)
if IsValid(inviter) then
net.Start("ixSquadNotify")
net.WriteString(client:Name() .. " отклонил приглашение")
net.Send(inviter)
end
end
plugin.squadInvites[client:SteamID()] = nil
net.Start("ixSquadNotify")
net.WriteString("Приглашение отклонено")
net.Send(client)
end)
net.Receive("ixSquadKick", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if IsAdminMode(client) then return end
local targetSteamID = net.ReadString()
local success, message = plugin:KickFromSquad(client, targetSteamID)
net.Start("ixSquadNotify")
net.WriteString(message)
net.Send(client)
end)
net.Receive("ixSquadLeave", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if IsAdminMode(client) then return end
local success, message = plugin:LeaveSquad(client)
net.Start("ixSquadNotify")
net.WriteString(message)
net.Send(client)
end)
net.Receive("ixSquadPlaceMarker", function(len, client)
local plugin = ix.plugin.Get("squads")
if not plugin then return end
if not IsValid(client) or not client:Alive() then return end
local squad, squadID = plugin:GetPlayerSquad(client)
if not squad then return end
local pos = net.ReadVector()
if not pos then return end
for _, memberSteamID in ipairs(squad.members) do
local member = player.GetBySteamID(memberSteamID)
if IsValid(member) then
net.Start("ixSquadWorldMarker")
net.WriteVector(pos)
net.WriteString(client:Name())
net.Send(member)
end
end
end)