Загрузка данных
local Lighting = game:GetService("Lighting")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
-- ================= НАСТРОЙКИ =================
local Settings = {
WeatherEnabled = true,
StartWeather = "Clear",
MinSeconds = 180, -- минимум до смены погоды (секунды)
MaxSeconds = 420, -- максимум
Weights = { -- шанс выпадения (больше = чаще)
Clear = 30, Rain = 20, Storm = 10, Snow = 15,
Fog = 10, Hurricane = 5, Tornado = 5,
},
TornadoSpeed = 14,
TornadoHeight = 120,
TornadoStartDistance = 220,
}
-- =============================================
local FogByWeather = {
Clear = 0, Rain = 0.08, Storm = 0.15, Snow = 0.15,
Fog = 0.45, Hurricane = 0.15, Tornado = 0.2,
}
local WindByWeather = {
Clear = Vector3.new(4, 0, 2), Rain = Vector3.new(8, 0, 4),
Storm = Vector3.new(25, 0, 10), Snow = Vector3.new(6, 0, 3),
Fog = Vector3.new(2, 0, 1), Hurricane = Vector3.new(60, 0, 25),
Tornado = Vector3.new(30, 0, 10),
}
-- плавный туман (читает скрипт дня и ночи)
local fog, fogTarget = 0, 0
task.spawn(function()
while true do
task.wait(0.25)
fog += (fogTarget - fog) * 0.08
Lighting:SetAttribute("FogAdd", fog)
end
end)
-- ================= СМЕРЧ =================
local tornado
local layers = {}
local tornadoPos = Vector3.zero
local tornadoDir = Vector3.new(1, 0, 0)
local groundY = 0
local heartbeatConn
local function destroyTornado()
if heartbeatConn then heartbeatConn:Disconnect() heartbeatConn = nil end
if tornado then tornado:Destroy() tornado = nil end
layers = {}
end
local function findGround(x, z)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
local ignore = {}
for _, p in ipairs(Players:GetPlayers()) do
if p.Character then table.insert(ignore, p.Character) end
end
if tornado then table.insert(ignore, tornado) end
params.FilterDescendantsInstances = ignore
local r = workspace:Raycast(Vector3.new(x, 500, z), Vector3.new(0, -1000, 0), params)
return r and r.Position.Y or 0
end
local function spawnTornado()
destroyTornado()
local center = Vector3.zero
for _, p in ipairs(Players:GetPlayers()) do
local root = p.Character and p.Character:FindFirstChild("HumanoidRootPart")
if root then center = root.Position break end
end
local angle = math.random() * math.pi * 2
tornadoPos = center + Vector3.new(math.cos(angle), 0, math.sin(angle)) * Settings.TornadoStartDistance
local toCenter = (center - tornadoPos) * Vector3.new(1, 0, 1)
tornadoDir = toCenter.Magnitude > 1 and toCenter.Unit or Vector3.new(1, 0, 0)
groundY = findGround(tornadoPos.X, tornadoPos.Z)
tornado = Instance.new("Model")
tornado.Name = "Tornado"
local count = 14
local h = Settings.TornadoHeight / count
for i = 1, count do
local width = 10 + i * i * 0.45
local p = Instance.new("Part")
p.Name = "L" .. i
p.Shape = Enum.PartType.Cylinder
p.Size = Vector3.new(h + 1, width, width)
p.Anchored = true
p.CanCollide = false
p.CanQuery = false
p.CanTouch = false
p.CastShadow = false
p.Material = Enum.Material.SmoothPlastic
p.Color = Color3.fromRGB(95 + i * 3, 98 + i * 3, 100 + i * 3)
p.Transparency = 0.45
p.Parent = tornado
layers[i] = { part = p, y = (i - 0.5) * h, index = i }
end
-- пыль у основания
local dust = Instance.new("ParticleEmitter")
dust.Texture = "rbxasset://textures/particles/smoke_main.dds"
dust.Rate = 40
dust.Lifetime = NumberRange.new(2, 4)
dust.Speed = NumberRange.new(10, 25)
dust.SpreadAngle = Vector2.new(180, 180)
dust.Size = NumberSequence.new(10, 25)
dust.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.6),
NumberSequenceKeypoint.new(1, 1),
})
dust.Color = ColorSequence.new(Color3.fromRGB(120, 110, 95))
dust.Parent = layers[1].part
tornado.Parent = workspace
local t, groundTimer = 0, 0
heartbeatConn = RunService.Heartbeat:Connect(function(dt)
t += dt
groundTimer += dt
-- плавное блуждание направления
local drift = math.noise(t * 0.08, 0.5, 0.5) * 0.6 * dt
tornadoDir = CFrame.Angles(0, drift, 0):VectorToWorldSpace(tornadoDir)
tornadoPos += tornadoDir * Settings.TornadoSpeed * dt
if groundTimer > 0.3 then
groundTimer = 0
local gy = findGround(tornadoPos.X, tornadoPos.Z)
groundY += (gy - groundY) * 0.5
end
for _, l in ipairs(layers) do
local sway = l.index * 0.7
local ox = math.cos(t * 2 + l.index * 0.6) * sway
local oz = math.sin(t * 2 + l.index * 0.6) * sway
l.part.CFrame = CFrame.new(tornadoPos.X + ox, groundY + l.y, tornadoPos.Z + oz)
* CFrame.Angles(0, 0, math.pi / 2)
end
end)
end
-- ================= СМЕНА ПОГОДЫ =================
local function apply(name)
if not FogByWeather[name] then return end
fogTarget = FogByWeather[name]
workspace.GlobalWind = WindByWeather[name]
if name == "Tornado" then spawnTornado() else destroyTornado() end
end
local function pickWeather(current)
local total = 0
for name, w in pairs(Settings.Weights) do
if name ~= current then total += w end
end
local r = math.random() * total
for name, w in pairs(Settings.Weights) do
if name ~= current then
r -= w
if r <= 0 then return name end
end
end
return "Clear"
end
Lighting:SetAttribute("Weather", Settings.StartWeather)
apply(Settings.StartWeather)
Lighting:GetAttributeChangedSignal("Weather"):Connect(function()
apply(Lighting:GetAttribute("Weather"))
end)
task.spawn(function()
while Settings.WeatherEnabled do
task.wait(math.random(Settings.MinSeconds, Settings.MaxSeconds))
Lighting:SetAttribute("Weather", pickWeather(Lighting:GetAttribute("Weather")))
end
end)