add sborka
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
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
|
||||
Reference in New Issue
Block a user