local PLUGIN = PLUGIN -- Координаты баз для запрета авиаударов local BASE_RF = { min = Vector(-12686, 4350, -10000), max = Vector(-8714, 12126, 10000) } local BASE_UK = { min = Vector(9778, 2972, -10000), max = Vector(13576, 10852, 10000) } local function IsInBase(pos) if (pos:WithinAABox(BASE_RF.min, BASE_RF.max)) then return true end if (pos:WithinAABox(BASE_UK.min, BASE_UK.max)) then return true end return false end function PLUGIN:SpawnAirdrop(weapon) local center = Vector(0, 0, 0) local players = player.GetAll() if (#players > 0) then local targetPlayer = players[math.random(#players)] center = targetPlayer:GetPos() end local angle = math.random(0, 360) local radian = math.rad(angle) local altitude = math.random(2000, 2500) local startPos = center + Vector(math.cos(radian) * 15000, math.sin(radian) * 15000, 0) startPos.z = altitude local endPos = center - Vector(math.cos(radian) * 15000, math.sin(radian) * 15000, 0) endPos.z = altitude util.PrecacheModel(ix.config.Get("airdropPlaneModel", "models/gunkov2056/su25.mdl")) local plane = ents.Create("prop_dynamic") plane:SetModel(ix.config.Get("airdropPlaneModel", "models/gunkov2056/su25.mdl")) plane:SetPos(startPos) local angles = (endPos - startPos):Angle() plane:SetAngles(angles) plane:SetMoveType(MOVETYPE_FLY) plane:Spawn() plane:Activate() print("[Airdrop] Спавн самолета: " .. plane:GetModel() .. " на " .. tostring(startPos)) local speed = 2500 local distance = startPos:Distance(endPos) local duration = distance / speed local dropFrac = math.Rand(0.45, 0.55) local dropped = false local startTime = CurTime() plane:EmitSound("su25/su25.wav", 150, 100, 1, CHAN_AUTO) timer.Create("AirdropPlaneMove_" .. plane:EntIndex(), 0.05, 0, function() if (!IsValid(plane)) then return end local elapsed = CurTime() - startTime local frac = elapsed / duration if (frac >= 1) then plane:StopSound("su25/su25.wav") plane:Remove() timer.Remove("AirdropPlaneMove_" .. plane:EntIndex()) return end plane:SetPos(LerpVector(frac, startPos, endPos)) if (!dropped and frac >= dropFrac) then dropped = true self:DropCrate(plane:GetPos(), weapon) end end) end function PLUGIN:SpawnAirstrike() ix.util.Notify("Внимание! Обнаружена воздушная угроза. Авиаудар через 30 секунд!", nil, "all") timer.Simple(30, function() local targetPos = Vector(0, 0, 0) local players = player.GetAll() local attempts = 0 -- Ищем валидную точку для удара (не в базе) repeat if (#players > 0) then targetPos = players[math.random(#players)]:GetPos() else targetPos = Vector(math.random(-10000, 10000), math.random(-10000, 10000), 0) end attempts = attempts + 1 until (!IsInBase(targetPos) or attempts > 10) if (IsInBase(targetPos)) then return end -- Отмена если не нашли безопасную точку local angle = math.random(0, 360) local radian = math.rad(angle) local altitude = math.random(2000, 2500) local startPos = targetPos + Vector(math.cos(radian) * 15000, math.sin(radian) * 15000, 0) startPos.z = altitude local endPos = targetPos - Vector(math.cos(radian) * 15000, math.sin(radian) * 15000, 0) endPos.z = altitude util.PrecacheModel(ix.config.Get("airstrikePlaneModel", "models/gunkov2056/su25.mdl")) local plane = ents.Create("prop_dynamic") plane:SetModel(ix.config.Get("airstrikePlaneModel", "models/gunkov2056/su25.mdl")) plane:SetPos(startPos) plane:SetAngles((endPos - startPos):Angle()) plane:SetMoveType(MOVETYPE_FLY) plane:Spawn() plane:Activate() print("[Airstrike] Спавн самолета: " .. plane:GetModel() .. " на " .. tostring(startPos)) local speed = 3000 local duration = startPos:Distance(endPos) / speed local dropped = false local startTime = CurTime() plane:EmitSound("su25/su25.wav", 150, 100, 1, CHAN_AUTO) timer.Create("AirstrikePlaneMove_" .. plane:EntIndex(), 0.05, 0, function() if (!IsValid(plane)) then return end local elapsed = CurTime() - startTime local frac = elapsed / duration if (frac >= 1) then plane:StopSound("su25/su25.wav") plane:Remove() timer.Remove("AirstrikePlaneMove_" .. plane:EntIndex()) return end plane:SetPos(LerpVector(frac, startPos, endPos)) if (!dropped and frac >= 0.5) then dropped = true self:DropBomb(plane:GetPos(), plane:GetForward() * speed) end end) end) end function PLUGIN:DropBomb(pos, velocity) local bomb = ents.Create("sw_bomb_fab250m62_v3") if (!IsValid(bomb)) then -- Если энтити нет, создадим обычный взрыв для теста local exp = ents.Create("env_explosion") exp:SetPos(pos) exp:SetKeyValue("iMagnitude", "300") exp:Spawn() exp:Fire("Explode", 0, 0) return end bomb:SetPos(pos) bomb:SetAngles(velocity:Angle()) bomb:Spawn() bomb:Activate() local phys = bomb:GetPhysicsObject() if (IsValid(phys)) then phys:SetVelocity(velocity) end end function PLUGIN:DropCrate(pos, weapon) if (!weapon) then local weaponsConfig = ix.config.Get("airdropWeapons", "") local weapons = {} if (type(weaponsConfig) == "string" and weaponsConfig:Trim() != "") then for _, v in pairs(string.Split(weaponsConfig, "\n")) do local s = v:Trim() if (s != "") then table.insert(weapons, s) end end end -- Если конфиг пустой или старый, берем список по умолчанию if (#weapons == 0) then weapons = { "tacrp_io_degala", "tacrp_io_fiveseven", "tacrp_mr96", "tacrp_pdw", "tacrp_superv", "tacrp_sd_aac_hb", "tacrp_ak_ak12", "tacrp_ak_an94", "tacrp_sg551", "tacrp_io_xm8car", "tacrp_hk417", "tacrp_io_scarh", "tacrp_mg4", "tacrp_io_xm8lmg", "tacrp_io_sg550r", "tacrp_io_sl8", "tacrp_io_sg550", "tacrp_io_vss", "tacrp_as50", "tacrp_ex_hecate", "tacrp_civ_m320" } end weapon = weapons[math.random(#weapons)] end local crate = ents.Create("ix_airdrop") if (!IsValid(crate)) then print("[Airdrop] ОШИБКА: Не удалось создать энтити ix_airdrop!") return end crate:SetPos(pos) crate:SetAngles(Angle(0, math.random(0, 360), 0)) crate:Spawn() crate.ixWeapon = weapon -- Создаем парашют util.PrecacheModel("models/jessev92/bf2/parachute.mdl") local parachute = ents.Create("prop_dynamic") parachute:SetModel("models/jessev92/bf2/parachute.mdl") parachute:SetPos(crate:GetPos() + Vector(0, 0, 50)) parachute:SetParent(crate) parachute:Spawn() crate.ixParachute = parachute local phys = crate:GetPhysicsObject() if (IsValid(phys)) then phys:Wake() phys:SetVelocity(Vector(0, 0, -200)) -- Скорость падения phys:SetDragCoefficient(5) phys:SetAngleDragCoefficient(100) end local function StartSmoke() if (!IsValid(crate)) then return end local smoke = ents.Create("env_smokestack") smoke:SetPos(crate:GetPos()) smoke:SetAngles(Angle(-90, 0, 0)) smoke:SetKeyValue("InitialState", "1") smoke:SetKeyValue("rendercolor", "255 100 0") smoke:SetKeyValue("renderamt", "255") smoke:SetKeyValue("SmokeMaterial", "particle/smokesprites_0001.vmt") smoke:SetKeyValue("BaseSpread", "20") smoke:SetKeyValue("SpreadSpeed", "10") smoke:SetKeyValue("Speed", "80") smoke:SetKeyValue("StartSize", "30") smoke:SetKeyValue("EndSize", "120") smoke:SetKeyValue("Rate", "40") smoke:SetKeyValue("JetLength", "300") smoke:Spawn() smoke:Activate() smoke:SetParent(crate) crate.ixSmoke = smoke ix.util.Notify("Аирдроп приземлился! Ищите оранжевый дым.", nil, "all") end -- Принудительно держим ящик ровно во время падения timer.Create("AirdropUpright_" .. crate:EntIndex(), 0.1, 0, function() if (!IsValid(crate)) then timer.Remove("AirdropUpright_" .. crate:EntIndex()) return end local phys = crate:GetPhysicsObject() if (IsValid(phys) and phys:IsMotionEnabled()) then crate:SetAngles(Angle(0, crate:GetAngles().y, 0)) phys:SetAngleVelocity(Vector(0, 0, 0)) else timer.Remove("AirdropUpright_" .. crate:EntIndex()) end end) timer.Create("AirdropLand_" .. crate:EntIndex(), 0.2, 0, function() if (!IsValid(crate)) then timer.Remove("AirdropLand_" .. crate:EntIndex()) return end local phys = crate:GetPhysicsObject() if (!IsValid(phys)) then return end -- Если скорость мала или мы коснулись земли local tr = util.TraceLine({ start = crate:GetPos() + Vector(0, 0, 10), endpos = crate:GetPos() - Vector(0, 0, 40), filter = crate }) if (tr.Hit or phys:GetVelocity():Length() < 5) then phys:EnableMotion(false) crate:SetPos(tr.HitPos) crate:SetAngles(Angle(0, crate:GetAngles().y, 0)) if (IsValid(crate.ixParachute)) then crate.ixParachute:Remove() end StartSmoke() timer.Remove("AirdropLand_" .. crate:EntIndex()) timer.Remove("AirdropUpright_" .. crate:EntIndex()) end end) timer.Simple(900, function() if (IsValid(crate)) then if (IsValid(crate.ixSmoke)) then crate.ixSmoke:Remove() end crate:Remove() end end) end function PLUGIN:Initialize() self:SetupAirdropTimer() self:SetupAirstrikeTimer() end function PLUGIN:SetupAirdropTimer() local delay = math.random(ix.config.Get("airdropMinInterval", 3600), ix.config.Get("airdropMaxInterval", 7200)) timer.Create("ixAirdropTimer", delay, 1, function() self:SpawnAirdrop() self:SetupAirdropTimer() end) end function PLUGIN:SetupAirstrikeTimer() local delay = math.random(ix.config.Get("airstrikeMinInterval", 1800), ix.config.Get("airstrikeMaxInterval", 3600)) timer.Create("ixAirstrikeTimer", delay, 1, function() self:SpawnAirstrike() self:SetupAirstrikeTimer() end) end