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

361 lines
12 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.active = PLUGIN.active or false
PLUGIN.start_time = PLUGIN.start_time or 0
PLUGIN.participants = PLUGIN.participants or {}
PLUGIN.eliminated_players = PLUGIN.eliminated_players or {}
PLUGIN.initiator = PLUGIN.initiator or nil
PLUGIN.tempSpawns = PLUGIN.tempSpawns or {} -- [factionUniqueID] = { [specDef or "default"] = { {pos, ang}, ... } }
-- Zone Data
PLUGIN.zone = PLUGIN.zone or {
pos1 = nil,
pos2 = nil,
min = nil,
max = nil,
shrinking = false,
center = nil,
shrink_speed = 1,
min_size = 500
}
-- Networking
util.AddNetworkString("events_system_notification")
util.AddNetworkString("events_system_hud_update")
util.AddNetworkString("events_system_zone_update")
-- Initialization
function PLUGIN:InitPostEntity()
SetGlobalBool("events_system_active", false)
SetGlobalInt("events_system_start_time", 0)
self.tempSpawns = self:GetData() or {}
end
function PLUGIN:SaveTempSpawns()
self:SetData(self.tempSpawns)
end
-- Permissions Check (using ix.config)
function PLUGIN:HasPermission(ply)
if (!IsValid(ply)) then return false end
if (ply:IsSuperAdmin()) then return true end
local allowed = string.Split(ix.config.Get("eventAllowedRanks", ""), ",")
local userGroup = ply:GetUserGroup()
for _, rank in ipairs(allowed) do
if (string.lower(userGroup) == string.lower(string.Trim(rank))) then
return true
end
end
return false
end
-- Zone Logic
function PLUGIN:SetZonePoint1(ply)
self.zone.pos1 = ply:GetPos()
self:SendMessage(ply, "✅ Точка зоны 1 установлена: " .. tostring(self.zone.pos1))
if (self.zone.pos2) then self:CalculateZoneBounds() end
end
function PLUGIN:SetZonePoint2(ply)
self.zone.pos2 = ply:GetPos()
self:SendMessage(ply, "✅ Точка зоны 2 установлена: " .. tostring(self.zone.pos2))
if (self.zone.pos1) then self:CalculateZoneBounds() end
end
function PLUGIN:CalculateZoneBounds()
if (!self.zone.pos1 or !self.zone.pos2) then return end
self.zone.min = Vector(
math.min(self.zone.pos1.x, self.zone.pos2.x),
math.min(self.zone.pos1.y, self.zone.pos2.y),
math.min(self.zone.pos1.z, self.zone.pos2.z)
)
self.zone.max = Vector(
math.max(self.zone.pos1.x, self.zone.pos2.x),
math.max(self.zone.pos1.y, self.zone.pos2.y),
math.max(self.zone.pos1.z, self.zone.pos2.z)
)
self:BroadcastZone()
local size = self.zone.max - self.zone.min
self:BroadcastMessage(string.format("🎯 Зона ивента установлена! Размер: %.0f x %.0f x %.0f", size.x, size.y, size.z))
end
function PLUGIN:BroadcastZone()
if (!self.zone.min or !self.zone.max) then return end
net.Start("events_system_zone_update")
net.WriteBool(true)
net.WriteVector(self.zone.min)
net.WriteVector(self.zone.max)
net.Broadcast()
end
function PLUGIN:ClearZone(ply)
self.zone.pos1 = nil
self.zone.pos2 = nil
self.zone.min = nil
self.zone.max = nil
self.zone.shrinking = false
if (timer.Exists("EventsSystem_ZoneShrink")) then
timer.Remove("EventsSystem_ZoneShrink")
end
net.Start("events_system_zone_update")
net.WriteBool(false)
net.Broadcast()
if (IsValid(ply)) then
self:SendMessage(ply, "🗑️ Зона ивента очищена.")
end
end
function PLUGIN:StartZoneShrinking(ply)
if (!self.zone.min or !self.zone.max) then
if (IsValid(ply)) then self:SendMessage(ply, "❌ Сначала установите зону!") end
return
end
if (self.zone.shrinking) then
if (IsValid(ply)) then self:SendMessage(ply, "⚠️ Зона уже сужается!") end
return
end
self.zone.center = (self.zone.min + self.zone.max) / 2
self.zone.shrinking = true
timer.Create("EventsSystem_ZoneShrink", 0.1, 0, function()
self:ShrinkZone()
end)
self:BroadcastMessage("🔴 ВНИМАНИЕ! Зона начала сужаться к центру!")
for _, player in ipairs(player.GetAll()) do
player:EmitSound("ambient/alarms/warningbell1.wav", 75, 100)
end
end
function PLUGIN:StopZoneShrinking(ply)
if (!self.zone.shrinking) then return end
self.zone.shrinking = false
timer.Remove("EventsSystem_ZoneShrink")
local size = self.zone.max - self.zone.min
self:BroadcastMessage(string.format("⏸️ Сужение зоны остановлено! Текущий размер: %.0f x %.0f", size.x, size.y))
end
function PLUGIN:ShrinkZone()
if (!self.zone.shrinking or !self.zone.min or !self.zone.max or !self.zone.center) then return end
local shrink_amount = self.zone.shrink_speed * 0.1
local new_min = Vector(self.zone.min.x + shrink_amount, self.zone.min.y + shrink_amount, self.zone.min.z)
local new_max = Vector(self.zone.max.x - shrink_amount, self.zone.max.y - shrink_amount, self.zone.max.z)
if (new_max.x - new_min.x <= self.zone.min_size or new_max.y - new_min.y <= self.zone.min_size) then
self:StopZoneShrinking(nil)
self:BroadcastMessage("❗ Зона достигла минимального размера!")
return
end
self.zone.min = new_min
self.zone.max = new_max
self:BroadcastZone()
end
function PLUGIN:IsInZone(pos)
if (!self.zone.min or !self.zone.max) then return true end
return pos.x >= self.zone.min.x and pos.x <= self.zone.max.x and
pos.y >= self.zone.min.y and pos.y <= self.zone.max.y and
pos.z >= self.zone.min.z and pos.z <= self.zone.max.z
end
function PLUGIN:CheckZoneBoundaries()
if (!self.active) then return end
for steamid, data in pairs(self.participants) do
local ply = player.GetBySteamID(steamid)
if (IsValid(ply) and ply:Alive()) then
if (!self:IsInZone(ply:GetPos())) then
ply:TakeDamage(10, ply, ply)
ply:Notify("⚠️ ВНИМАНИЕ! Вы вне зоны ивента! Немедленно вернитесь!")
ply:EmitSound("buttons/button10.wav", 75, 100)
end
end
end
end
-- Event Control
function PLUGIN:StartEvent(ply)
self.active = true
self.start_time = CurTime()
self.participants = {}
self.eliminated_players = {}
self.initiator = ply
SetGlobalBool("events_system_active", true)
SetGlobalInt("events_system_start_time", CurTime())
local initiator_name = IsValid(ply) and ply:Nick() or "Администратор"
local start_message = "⚔️ БОЕВОЙ ИВЕНТ НАЧАЛСЯ! ⚔️\nОрганизатор: " .. initiator_name .. "\nКак участвовать: Используйте /EventJoin\nЦель: Выжить и победить!"
self:BroadcastMessage(start_message)
for _, player in ipairs(player.GetAll()) do
player:EmitSound(self.Config.sounds.event_start, 75, 100, 0.8)
end
self:UpdateHUD()
timer.Create("EventsSystem_ZoneCheck", 1, 0, function() self:CheckZoneBoundaries() end)
end
function PLUGIN:EndEvent(ply, cancelled)
if (!self.active) then return end
local stats_lines = {
"╔═══════════════════════════════════════╗",
"║ 📊 ФИНАЛЬНАЯ СТАТИСТИКА 📊 ║",
"╚═══════════════════════════════════════╝",
string.format("👥 Всего участников: %d", table.Count(self.participants) + table.Count(self.eliminated_players))
}
self.active = false
SetGlobalBool("events_system_active", false)
SetGlobalInt("events_system_start_time", 0)
local end_message = cancelled and "🛑 ИВЕНТ ОТМЕНЕН АДМИНИСТРАТОРОМ" or "🏆 ИВЕНТ ЗАВЕРШЕН 🏆"
self:BroadcastMessage(end_message)
for _, line in ipairs(stats_lines) do self:BroadcastMessage(line) end
for _, player in ipairs(player.GetAll()) do
player:EmitSound(self.Config.sounds.event_end, 75, 100, 0.6)
end
self.participants = {}
self.eliminated_players = {}
timer.Remove("EventsSystem_ZoneCheck")
self:UpdateHUD()
end
function PLUGIN:AddParticipant(ply)
if (!self.active or !IsValid(ply)) then return end
self.participants[ply:SteamID()] = {
join_time = CurTime(),
kills = 0,
nick = ply:Nick()
}
ply:Notify("✅ ВЫ ПРИСОЕДИНИЛИСЬ К ИВЕНТУ!")
ply:EmitSound(self.Config.sounds.player_join, 50, 100, 0.5)
end
function PLUGIN:AddAllPlayers(admin_ply)
for _, ply in ipairs(player.GetAll()) do
self:AddParticipant(ply)
end
self:BroadcastMessage("📢 Все игроки добавлены в ивент!")
end
-- Hooks
function PLUGIN:PlayerDeath(victim, inflictor, attacker)
if (!self.active) then return end
local steamid = victim:SteamID()
local data = self.participants[steamid]
if (!data) then return end
if (IsValid(attacker) and attacker:IsPlayer()) then
local attacker_data = self.participants[attacker:SteamID()]
if (attacker_data) then attacker_data.kills = attacker_data.kills + 1 end
end
self.eliminated_players[steamid] = data
self.participants[steamid] = nil
victim:Notify("💀 Вы выбыли из ивента!")
self:BroadcastMessage(string.format("💀 %s выбыл! Убийств: %d", victim:Nick(), data.kills))
timer.Simple(0.5, function() self:CheckEventEnd() end)
end
function PLUGIN:CheckEventEnd()
if (!self.active) then return end
if (table.Count(self.participants) <= 1) then
self:EndEvent()
end
end
function PLUGIN:PlayerDisconnected(ply)
if (!self.active) then return end
self.participants[ply:SteamID()] = nil
end
-- Messaging
function PLUGIN:SendMessage(ply, message)
if (!IsValid(ply)) then return end
net.Start("events_system_notification")
net.WriteString(message)
net.Send(ply)
end
function PLUGIN:BroadcastMessage(message)
for _, ply in ipairs(player.GetAll()) do
ply:ChatPrint(message)
end
end
function PLUGIN:UpdateHUD()
net.Start("events_system_hud_update")
net.WriteBool(self.active)
net.WriteInt(self.start_time, 32)
net.Broadcast()
end
-- Temporary Spawns Implementation
function PLUGIN:AddTempSpawn(factionID, specDef, pos, ang)
specDef = specDef or "default"
self.tempSpawns[factionID] = self.tempSpawns[factionID] or {}
self.tempSpawns[factionID][specDef] = self.tempSpawns[factionID][specDef] or {}
table.insert(self.tempSpawns[factionID][specDef], {pos = pos, ang = ang})
self:SaveTempSpawns()
end
function PLUGIN:RemoveTempSpawns(pos, radius)
local count = 0
for factionID, specs in pairs(self.tempSpawns) do
for specDef, points in pairs(specs) do
for k, v in pairs(points) do
if (v.pos:Distance(pos) <= radius) then
points[k] = nil
count = count + 1
end
end
end
end
if (count > 0) then self:SaveTempSpawns() end
return count
end
function PLUGIN:PostPlayerLoadout(client)
if (!self.active) then return end
local char = client:GetCharacter()
if (!char) then return end
local faction = ix.faction.indices[client:Team()]
if (!faction) then return end
local factionID = faction.uniqueID
local podr = char:GetPodr()
local factionTable = ix.faction.Get(client:Team())
local specDef = (factionTable and factionTable.Podr and factionTable.Podr[podr]) and factionTable.Podr[podr].spec_def or "default"
local points = (self.tempSpawns[factionID] and self.tempSpawns[factionID][specDef]) or (self.tempSpawns[factionID] and self.tempSpawns[factionID]["default"])
if (points and !table.IsEmpty(points)) then
local point = table.Random(points)
client:SetPos(point.pos)
client:SetEyeAngles(point.ang)
end
end