Actual tornado V8.0
Key RequiredUniversal

Actual tornado V8.0

Universal ScriptVoidX5VoidX5 2.5K views 5.2K executesUpdated Jun 1, 2026
5.0/ 5(1 votes)
10100% Positive
Lua Script•667 lines (25244 chars)
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer

local ActiveDisasterType = "Tornado"
local ActiveStorms = {}

local Settings = {
    SpawnPlacement = "Original",
    WanderSpeed = 25,
    Color_R = 100,
    Color_G = 100,
    Color_B = 105,
    FunnelTrans = 0.6,
    DebrisDensity = 100,
    DebrisVis = 0.8,
    EF_Level = 0,
    PhysicsMode = "Original",
    TornadoShape = "Classic",
    LiftSpeed = 1.0,
    RotationSpeed = 1.0,
    RadiusMult = 1.0,
    PullStrengthMult = 1.0,
    MobileButtonPosition = "Top-Right"
}

local EF_STATS = {
    [-1] = {Radius = 25, Strength = 15, Height = 70, Scale = 0.4},
    [0] = {Radius = 40, Strength = 25, Height = 100, Scale = 0.6},
    [1] = {Radius = 60, Strength = 40, Height = 150, Scale = 1},
    [2] = {Radius = 100, Strength = 70, Height = 220, Scale = 1.6},
    [3] = {Radius = 160, Strength = 120, Height = 320, Scale = 2.4},
    [4] = {Radius = 240, Strength = 200, Height = 450, Scale = 3.5},
    [5] = {Radius = 350, Strength = 320, Height = 600, Scale = 5.0}
}

local ScreenGui = Instance.new("ScreenGui", Player:WaitForChild("PlayerGui"))
ScreenGui.Name = "StormOS_V8_Merged"
ScreenGui.ResetOnSpawn = false

local ChangelogFrame = Instance.new("Frame", ScreenGui)
ChangelogFrame.AnchorPoint = Vector2.new(0.5, 0.5)
ChangelogFrame.Size = UDim2.new(0.45, 0, 0.5, 0)
ChangelogFrame.Position = UDim2.new(0.5, 0, 0.5, 0)
ChangelogFrame.BackgroundColor3 = Color3.fromRGB(15, 15, 18)
ChangelogFrame.Visible = true
Instance.new("UICorner", ChangelogFrame).CornerRadius = UDim.new(0, 12)
Instance.new("UIStroke", ChangelogFrame).Color = Color3.fromRGB(80, 140, 255)

local CL_Title = Instance.new("TextLabel", ChangelogFrame)
CL_Title.Size = UDim2.new(1, 0, 0.1, 0)
CL_Title.Text = "STORM OS v8.0 - CHANGELOG"
CL_Title.Font = Enum.Font.GothamBlack
CL_Title.TextColor3 = Color3.new(1, 1, 1)
CL_Title.TextSize = 20
CL_Title.TextScaled = true
CL_Title.BackgroundTransparency = 1

local CL_CloseBtn = Instance.new("TextButton", ChangelogFrame)
CL_CloseBtn.Size = UDim2.new(0, 30, 0, 30)
CL_CloseBtn.Position = UDim2.new(1, -35, 0, 5)
CL_CloseBtn.Text = "X"
CL_CloseBtn.BackgroundColor3 = Color3.fromRGB(180, 50, 50)
CL_CloseBtn.TextColor3 = Color3.new(1, 1, 1)
CL_CloseBtn.Font = Enum.Font.GothamBold
CL_CloseBtn.TextScaled = true
Instance.new("UICorner", CL_CloseBtn).CornerRadius = UDim.new(0, 6)

local CL_Scroll = Instance.new("ScrollingFrame", ChangelogFrame)
CL_Scroll.Size = UDim2.new(1, -20, 0.8, 0)
CL_Scroll.Position = UDim2.new(0, 10, 0.15, 0)
CL_Scroll.BackgroundColor3 = Color3.fromRGB(22, 22, 26)
CL_Scroll.ScrollBarThickness = 3
CL_Scroll.CanvasSize = UDim2.new(0, 0, 0, 0)
Instance.new("UICorner", CL_Scroll)

local CL_Layout = Instance.new("UIListLayout", CL_Scroll)
CL_Layout.Padding = UDim.new(0, 10)
CL_Layout.HorizontalAlignment = Enum.HorizontalAlignment.Center
CL_Layout.SortOrder = Enum.SortOrder.LayoutOrder

local ChangelogEntries = {
    {
        Date = os.date("%Y-%m-%d %H:%M:%S"),
        Text = "Max 2 tornadoes – SPAWN creates first, SPAWN ADDITIONAL adds second. Mobile button positionable."
    },
    {
        Date = "2026-05-24 18:30",
        Text = "Tornado physics improved – player & held tools excluded from pull."
    },
    {
        Date = "2026-05-23 09:15",
        Text = "Initial V8 merge – tornado visual overhaul & responsive GUI."
    }
}

for i, entry in ipairs(ChangelogEntries) do
    local entryFrame = Instance.new("Frame", CL_Scroll)
    entryFrame.Size = UDim2.new(0.95, 0, 0, 45)
    entryFrame.BackgroundTransparency = 1

    local dateLbl = Instance.new("TextLabel", entryFrame)
    dateLbl.Size = UDim2.new(1, 0, 0.35, 0)
    dateLbl.Text = "[" .. entry.Date .. "]"
    dateLbl.Font = Enum.Font.GothamBold
    dateLbl.TextColor3 = Color3.fromRGB(80, 140, 255)
    dateLbl.TextSize = 12
    dateLbl.BackgroundTransparency = 1
    dateLbl.TextXAlignment = Enum.TextXAlignment.Left

    local descLbl = Instance.new("TextLabel", entryFrame)
    descLbl.Size = UDim2.new(1, 0, 0.65, 0)
    descLbl.Position = UDim2.new(0, 0, 0.35, 0)
    descLbl.Text = entry.Text
    descLbl.Font = Enum.Font.Gotham
    descLbl.TextColor3 = Color3.new(0.9, 0.9, 0.9)
    descLbl.TextSize = 14
    descLbl.BackgroundTransparency = 1
    descLbl.TextXAlignment = Enum.TextXAlignment.Left
    descLbl.TextWrapped = true
end

local LoadingFrame = Instance.new("Frame", ScreenGui)
LoadingFrame.Size = UDim2.new(1, 0, 1, 0)
LoadingFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
LoadingFrame.BackgroundTransparency = 0.3
LoadingFrame.Visible = false
LoadingFrame.ZIndex = 10

local LoadingLabel = Instance.new("TextLabel", LoadingFrame)
LoadingLabel.Size = UDim2.new(0.6, 0, 0.1, 0)
LoadingLabel.AnchorPoint = Vector2.new(0.5, 0.5)
LoadingLabel.Position = UDim2.new(0.5, 0, 0.4, 0)
LoadingLabel.Text = "Loading StormOS v8.0..."
LoadingLabel.Font = Enum.Font.GothamBlack
LoadingLabel.TextColor3 = Color3.new(1, 1, 1)
LoadingLabel.TextSize = 28
LoadingLabel.BackgroundTransparency = 1
LoadingLabel.TextScaled = true

local ProgressBar = Instance.new("Frame", LoadingFrame)
ProgressBar.Size = UDim2.new(0.4, 0, 0.03, 0)
ProgressBar.AnchorPoint = Vector2.new(0.5, 0.5)
ProgressBar.Position = UDim2.new(0.5, 0, 0.55, 0)
ProgressBar.BackgroundColor3 = Color3.fromRGB(40, 40, 45)
Instance.new("UICorner", ProgressBar)

local ProgressFill = Instance.new("Frame", ProgressBar)
ProgressFill.Size = UDim2.new(0, 0, 1, 0)
ProgressFill.BackgroundColor3 = Color3.fromRGB(80, 140, 255)
ProgressFill.BorderSizePixel = 0
Instance.new("UICorner", ProgressFill)

local ProgressText = Instance.new("TextLabel", ProgressBar)
ProgressText.Size = UDim2.new(1, 0, 1, 0)
ProgressText.Text = "0%"
ProgressText.Font = Enum.Font.GothamBold
ProgressText.TextColor3 = Color3.new(1, 1, 1)
ProgressText.TextSize = 14
ProgressText.BackgroundTransparency = 1
ProgressText.TextScaled = true

local MainFrame = Instance.new("Frame", ScreenGui)
MainFrame.AnchorPoint = Vector2.new(0.5, 0.5)
MainFrame.Size = UDim2.new(0.35, 0, 0.6, 0)
MainFrame.Position = UDim2.new(0.75, 0, 0.5, 0)
MainFrame.BackgroundColor3 = Color3.fromRGB(15, 15, 18)
MainFrame.Visible = false
MainFrame.Active = true
Instance.new("UICorner", MainFrame).CornerRadius = UDim.new(0, 12)
Instance.new("UIStroke", MainFrame).Color = Color3.fromRGB(80, 140, 255)

local dragging, dragStart, startPos
MainFrame.InputBegan:Connect(function(input)
    if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
        dragging = true
        dragStart = input.Position
        startPos = MainFrame.Position
        input.Changed:Connect(function()
            if input.UserInputState == Enum.UserInputState.End then dragging = false end
        end)
    end
end)
UserInputService.InputChanged:Connect(function(input)
    if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then
        local delta = input.Position - dragStart
        MainFrame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
    end
end)

local Title = Instance.new("TextLabel", MainFrame)
Title.Size = UDim2.new(0.5, 0, 0.08, 0)
Title.Text = "STORM OS v8.0"
Title.Position = UDim2.new(0, 10, 0, 0)
Title.Font = Enum.Font.GothamBlack
Title.TextColor3 = Color3.new(1, 1, 1)
Title.TextSize = 16
Title.TextScaled = true
Title.BackgroundTransparency = 1
Title.TextXAlignment = Enum.TextXAlignment.Left

local OpenBtn = Instance.new("TextButton", ScreenGui)
OpenBtn.Size = UDim2.new(0, 50, 0, 50)
OpenBtn.Text = "🌪️"
OpenBtn.TextSize = 28
OpenBtn.BackgroundColor3 = Color3.fromRGB(20, 20, 25)
OpenBtn.Visible = false
Instance.new("UICorner", OpenBtn).CornerRadius = UDim.new(1, 0)
Instance.new("UIStroke", OpenBtn).Color = Color3.fromRGB(80, 140, 255)

local CloseBtn = Instance.new("TextButton", MainFrame)
CloseBtn.Size = UDim2.new(0, 30, 0, 30)
CloseBtn.Position = UDim2.new(1, -35, 0, 5)
CloseBtn.Text = "X"
CloseBtn.BackgroundColor3 = Color3.fromRGB(180, 50, 50)
CloseBtn.TextColor3 = Color3.new(1, 1, 1)
CloseBtn.Font = Enum.Font.GothamBold
CloseBtn.TextScaled = true
Instance.new("UICorner", CloseBtn).CornerRadius = UDim.new(0, 6)

CloseBtn.MouseButton1Click:Connect(function()
    MainFrame.Visible = false
    OpenBtn.Visible = true
end)
OpenBtn.MouseButton1Click:Connect(function()
    MainFrame.Visible = true
    OpenBtn.Visible = false
end)

local ButtonPositions = {
    ["Top-Left"] = UDim2.new(0, 10, 0, 10),
    ["Top-Right"] = UDim2.new(1, -60, 0, 10),
    ["Bottom-Left"] = UDim2.new(0, 10, 1, -60),
    ["Bottom-Right"] = UDim2.new(1, -60, 1, -60),
    ["Center"] = UDim2.new(0.5, -25, 0.5, -25)
}

local function UpdateButtonPosition()
    local pos = ButtonPositions[Settings.MobileButtonPosition]
    if pos then
        OpenBtn.Position = pos
    end
end
UpdateButtonPosition()

local TabContainer = Instance.new("Frame", MainFrame)
TabContainer.Size = UDim2.new(1, -20, 0.07, 0)
TabContainer.Position = UDim2.new(0, 10, 0.08, 0)
TabContainer.BackgroundTransparency = 1
local TabLayout = Instance.new("UIListLayout", TabContainer)
TabLayout.FillDirection = Enum.FillDirection.Horizontal
TabLayout.Padding = UDim.new(0, 5)

local ContentArea = Instance.new("Frame", MainFrame)
ContentArea.Size = UDim2.new(1, -20, 0.8, 0)
ContentArea.Position = UDim2.new(0, 10, 0.17, 0)
ContentArea.BackgroundColor3 = Color3.fromRGB(22, 22, 26)
Instance.new("UICorner", ContentArea)

local Tabs = {}
local function CreateTab(name)
    local btn = Instance.new("TextButton", TabContainer)
    btn.Size = UDim2.new(0.31, 0, 1, 0)
    btn.Text = name
    btn.BackgroundColor3 = Color3.fromRGB(30, 30, 35)
    btn.TextColor3 = Color3.new(0.7, 0.7, 0.7)
    btn.Font = Enum.Font.GothamBold
    btn.TextSize = 12
    btn.TextScaled = true
    Instance.new("UICorner", btn)

    local frame = Instance.new("ScrollingFrame", ContentArea)
    frame.Size = UDim2.new(1, -10, 1, -10)
    frame.BackgroundTransparency = 1
    frame.Position = UDim2.new(0, 5, 0, 5)
    frame.ScrollBarThickness = 2
    frame.Visible = false
    frame.CanvasSize = UDim2.new(0, 0, 2, 0)

    local layout = Instance.new("UIListLayout", frame)
    layout.Padding = UDim.new(0, 8)
    layout.HorizontalAlignment = Enum.HorizontalAlignment.Center

    btn.MouseButton1Click:Connect(function()
        for _, t in pairs(Tabs) do
            t.Btn.BackgroundColor3 = Color3.fromRGB(30, 30, 35)
            t.Btn.TextColor3 = Color3.new(0.7, 0.7, 0.7)
            t.Frame.Visible = false
        end
        btn.BackgroundColor3 = Color3.fromRGB(80, 140, 255)
        btn.TextColor3 = Color3.new(1, 1, 1)
        frame.Visible = true
    end)

    table.insert(Tabs, {Btn = btn, Frame = frame})
    return frame
end

local TabControl = CreateTab("CONTROL")
local TabPhysics = CreateTab("PHYSICS")
local TabVisuals = CreateTab("VISUALS")

local function CreateSetting(parent, text, settingKey, min, max, step)
    local row = Instance.new("Frame", parent)
    row.Size = UDim2.new(0.95, 0, 0, 35)
    row.BackgroundTransparency = 1

    local lbl = Instance.new("TextLabel", row)
    lbl.Size = UDim2.new(0.5, 0, 1, 0)
    lbl.Text = text
    lbl.TextColor3 = Color3.new(0.9, 0.9, 0.9)
    lbl.BackgroundTransparency = 1
    lbl.Font = Enum.Font.Gotham
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.TextScaled = true

    local valLbl = Instance.new("TextLabel", row)
    valLbl.Size = UDim2.new(0.2, 0, 1, 0)
    valLbl.Position = UDim2.new(0.5, 0, 0, 0)
    valLbl.Text = tostring(Settings[settingKey])
    valLbl.BackgroundTransparency = 1
    valLbl.TextColor3 = Color3.fromRGB(80, 140, 255)
    valLbl.Font = Enum.Font.GothamBold
    valLbl.TextScaled = true

    local minus = Instance.new("TextButton", row)
    minus.Size = UDim2.new(0, 25, 0, 25)
    minus.Position = UDim2.new(0.75, 0, 0.5, -12)
    minus.Text = "-"
    minus.BackgroundColor3 = Color3.fromRGB(40, 40, 45)
    minus.TextColor3 = Color3.new(1, 1, 1)
    minus.Font = Enum.Font.GothamBold
    Instance.new("UICorner", minus)

    local plus = Instance.new("TextButton", row)
    plus.Size = UDim2.new(0, 25, 0, 25)
    plus.Position = UDim2.new(0.9, 0, 0.5, -12)
    plus.Text = "+"
    plus.BackgroundColor3 = Color3.fromRGB(40, 40, 45)
    plus.TextColor3 = Color3.new(1, 1, 1)
    plus.Font = Enum.Font.GothamBold
    Instance.new("UICorner", plus)

    local function update(change)
        Settings[settingKey] = math.clamp(Settings[settingKey] + change, min, max)
        valLbl.Text = tostring(math.floor(Settings[settingKey] * 10) / 10)
        if settingKey == "Color_R" or settingKey == "Color_G" or settingKey == "Color_B" or settingKey == "FunnelTrans" or settingKey == "DebrisVis" then
            UpdateVisuals()
        end
    end

    minus.MouseButton1Click:Connect(function() update(-step) end)
    plus.MouseButton1Click:Connect(function() update(step) end)
end

local function CreateDropdown(parent, text, settingKey, options)
    local row = Instance.new("Frame", parent)
    row.Size = UDim2.new(0.95, 0, 0, 35)
    row.BackgroundTransparency = 1

    local lbl = Instance.new("TextLabel", row)
    lbl.Size = UDim2.new(0.5, 0, 1, 0)
    lbl.Text = text
    lbl.TextColor3 = Color3.new(0.9, 0.9, 0.9)
    lbl.BackgroundTransparency = 1
    lbl.Font = Enum.Font.Gotham
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.TextScaled = true

    local dropBtn = Instance.new("TextButton", row)
    dropBtn.Size = UDim2.new(0.45, 0, 0.8, 0)
    dropBtn.Position = UDim2.new(0.5, 0, 0.1, 0)
    dropBtn.Text = Settings[settingKey]
    dropBtn.BackgroundColor3 = Color3.fromRGB(40, 40, 45)
    dropBtn.TextColor3 = Color3.new(1, 1, 1)
    dropBtn.Font = Enum.Font.GothamBold
    dropBtn.TextScaled = true
    Instance.new("UICorner", dropBtn)

    local currentIndex = 1
    for i, opt in ipairs(options) do
        if opt == Settings[settingKey] then
            currentIndex = i
            break
        end
    end

    dropBtn.MouseButton1Click:Connect(function()
        currentIndex = currentIndex + 1
        if currentIndex > #options then currentIndex = 1 end
        Settings[settingKey] = options[currentIndex]
        dropBtn.Text = options[currentIndex]
        if settingKey == "MobileButtonPosition" then
            UpdateButtonPosition()
        end
    end)
end

local function CreateActionBtn(parent, text, color, callback)
    local b = Instance.new("TextButton", parent)
    b.Size = UDim2.new(0.95, 0, 0, 40)
    b.BackgroundColor3 = color
    b.Text = text
    b.Font = Enum.Font.GothamBold
    b.TextColor3 = Color3.new(1, 1, 1)
    b.TextSize = 14
    b.TextScaled = true
    Instance.new("UICorner", b)
    b.MouseButton1Click:Connect(callback)
    return b
end

local function BuildUI_For_Disaster()
    TabControl:ClearAllChildren()
    TabPhysics:ClearAllChildren()
    TabVisuals:ClearAllChildren()

    Instance.new("UIListLayout", TabControl).Padding = UDim.new(0, 8)
    Instance.new("UIListLayout", TabPhysics).Padding = UDim.new(0, 8)
    Instance.new("UIListLayout", TabVisuals).Padding = UDim.new(0, 8)

    -- SPAWN TORNADO: only if none exist
    CreateActionBtn(TabControl, "SPAWN TORNADO", Color3.fromRGB(40, 180, 80), function()
        if #ActiveStorms == 0 then
            GenerateDisaster()
        end
    end)

    -- SPAWN ADDITIONAL: only if exactly 1 exists → total becomes 2
    CreateActionBtn(TabControl, "SPAWN ADDITIONAL", Color3.fromRGB(150, 100, 40), function()
        if #ActiveStorms == 1 then
            GenerateDisaster()
        end
    end)

    CreateActionBtn(TabControl, "CLEAR ALL", Color3.fromRGB(180, 50, 50), function() DestroyDisaster() end)

    CreateDropdown(TabControl, "Spawn Placement", "SpawnPlacement", {"Original", "Infront"})
    CreateSetting(TabControl, "Wander AI Speed", "WanderSpeed", 0, 100, 5)
    CreateSetting(TabControl, "EF Level (-1=EFU)", "EF_Level", -1, 5, 1)
    CreateDropdown(TabControl, "Tornado Shape", "TornadoShape", {"Classic", "Rope", "Wedge"})

    CreateDropdown(TabPhysics, "Vortex Mode", "PhysicsMode", {"Original", "Better"})
    CreateSetting(TabPhysics, "Pull Radius Mult", "RadiusMult", 0.5, 3.0, 0.1)
    CreateSetting(TabPhysics, "Suck Strength Mult", "PullStrengthMult", 0.5, 5.0, 0.1)
    CreateSetting(TabPhysics, "Updraft Lift Speed", "LiftSpeed", 0.5, 3.0, 0.1)
    CreateSetting(TabPhysics, "Rotation Speed Mult", "RotationSpeed", 0.1, 5.0, 0.2)

    CreateSetting(TabVisuals, "Cloud Transp.", "FunnelTrans", 0.1, 1.0, 0.1)
    CreateSetting(TabVisuals, "Debris Visibility", "DebrisVis", 0.0, 1.0, 0.1)
    CreateSetting(TabVisuals, "Debris Density", "DebrisDensity", 0, 500, 50)
    CreateSetting(TabVisuals, "Color: Red", "Color_R", 0, 255, 15)
    CreateSetting(TabVisuals, "Color: Green", "Color_G", 0, 255, 15)
    CreateSetting(TabVisuals, "Color: Blue", "Color_B", 0, 255, 15)
    CreateDropdown(TabVisuals, "Mobile Button Pos", "MobileButtonPosition",
        {"Top-Left", "Top-Right", "Bottom-Left", "Bottom-Right", "Center"})

    Tabs[1].Btn.BackgroundColor3 = Color3.fromRGB(80, 140, 255)
    Tabs[1].Btn.TextColor3 = Color3.new(1, 1, 1)
    TabControl.Visible = true
end

CL_CloseBtn.MouseButton1Click:Connect(function()
    ChangelogFrame.Visible = false
    LoadingFrame.Visible = true

    task.spawn(function()
        local duration = 20
        local startTime = tick()
        while true do
            local elapsed = tick() - startTime
            local percent = math.min(100, math.floor((elapsed / duration) * 100))
            ProgressFill.Size = UDim2.new(percent/100, 0, 1, 0)
            ProgressText.Text = percent .. "%"
            if elapsed >= duration then break end
            task.wait(0.1)
        end
        LoadingFrame.Visible = false
        BuildUI_For_Disaster()
        MainFrame.Visible = true
        OpenBtn.Visible = true
        UpdateButtonPosition()
    end)
end)

function UpdateVisuals()
    local col = Color3.fromRGB(Settings.Color_R, Settings.Color_G, Settings.Color_B)
    for _, stormData in ipairs(ActiveStorms) do
        if stormData.Emitter then
            stormData.Emitter.Rate = Settings.DebrisDensity
            stormData.Emitter.Color = ColorSequence.new(col)
            local t = math.clamp(1 - Settings.DebrisVis, 0, 1)
            stormData.Emitter.Transparency = NumberSequence.new({NumberSequenceKeypoint.new(0, t), NumberSequenceKeypoint.new(1, 1)})
        end
        for _, data in ipairs(stormData.Segments) do
            if data.Part then
                data.Part.Color = col
                data.Part.Transparency = Settings.FunnelTrans
            end
        end
    end
end

local function isDescendantOfPlayerCharacter(part)
    local ancestor = part.Parent
    while ancestor do
        if ancestor:IsA("Model") and ancestor:FindFirstChild("Humanoid") then
            local player = Players:GetPlayerFromCharacter(ancestor)
            if player then
                return true
            end
        end
        ancestor = ancestor.Parent
    end
    return false
end

function GenerateDisaster()
    local StormID = tick()
    local DisasterModel = Instance.new("Model", workspace)
    DisasterModel.Name = "Tornado_" .. math.floor(StormID)

    local Core = Instance.new("Part", DisasterModel)
    Core.Anchored = true
    Core.CanCollide = false
    Core.Transparency = 1

    if Settings.SpawnPlacement == "Infront" and Player.Character and Player.Character:FindFirstChild("HumanoidRootPart") then
        local rootPart = Player.Character.HumanoidRootPart
        Core.Position = rootPart.Position + (rootPart.CFrame.LookVector * 200) + Vector3.new(0, 5, 0)
    else
        Core.Position = Vector3.new(0, 5, 0)
    end

    DisasterModel.PrimaryPart = Core
    local attach = Instance.new("Attachment", Core)

    local StormData = {
        Model = DisasterModel,
        Core = Core,
        Emitter = nil,
        Segments = {},
        Path = {},
        Waypoint = 1,
        Active = true,
        NextScan = 0,
        CachedParts = {}
    }
    table.insert(ActiveStorms, StormData)

    task.spawn(function()
        local stats = EF_STATS[Settings.EF_Level]
        local segments = 20 + (Settings.EF_Level * 5)

        local DebrisEmitter = Instance.new("ParticleEmitter", attach)
        DebrisEmitter.Texture = "rbxassetid://258128463"
        DebrisEmitter.Size = NumberSequence.new({NumberSequenceKeypoint.new(0, 10), NumberSequenceKeypoint.new(1, 80)})
        DebrisEmitter.Lifetime = NumberRange.new(3, 6)
        DebrisEmitter.Speed = NumberRange.new(20, 50)
        DebrisEmitter.SpreadAngle = Vector2.new(360, 360)
        DebrisEmitter.Drag = 2
        DebrisEmitter.Acceleration = Vector3.new(0, 40, 0)
        DebrisEmitter.RotSpeed = NumberRange.new(-300, 300)
        DebrisEmitter.EmissionDirection = Enum.NormalId.Top
        StormData.Emitter = DebrisEmitter

        for i = 1, segments do
            local p = Instance.new("Part")
            p.Shape = Enum.PartType.Cylinder
            p.Anchored = true
            p.CanCollide = false

            local ratio = i / segments
            local width = (Settings.TornadoShape == "Rope" and (2 + (ratio ^ 3) * (40 * stats.Scale))) or
                          (Settings.TornadoShape == "Wedge" and (20 + (ratio ^ 1.5) * (300 * stats.Scale))) or
                          (5 + (ratio ^ 3) * (120 * stats.Scale))

            p.Size = Vector3.new((stats.Height/segments) * 1.5, width, width)
            p.Parent = DisasterModel
            table.insert(StormData.Segments, {Part = p, Height = i * (stats.Height/segments), Speed = 15 * (1.2 - ratio), IsHurricane = false})
        end
        UpdateVisuals()
    end)
    task.spawn(function()
        while StormData.Active do
            local target = Core.Position + Vector3.new(math.random(-500, 500), 0, math.random(-500, 500))
            local path = PathfindingService:CreatePath({AgentRadius = 10})
            path:ComputeAsync(Core.Position, target)
            if path.Status == Enum.PathStatus.Success then
                StormData.Path = path:GetWaypoints()
                StormData.Waypoint = 1
            end
            task.wait(8)
        end
    end)
end

function DestroyDisaster()
    for _, storm in ipairs(ActiveStorms) do
        storm.Active = false
        if storm.Model then storm.Model:Destroy() end
    end
    table.clear(ActiveStorms)
end

RunService.Heartbeat:Connect(function(dt)
    local t = tick()

    for idx, Storm in ipairs(ActiveStorms) do
        if not Storm.Active or not Storm.Model.PrimaryPart then continue end
        local rootPos = Storm.Core.Position

        if #Storm.Path > 0 and Storm.Waypoint <= #Storm.Path then
            local targetPos = Vector3.new(Storm.Path[Storm.Waypoint].Position.X, rootPos.Y, Storm.Path[Storm.Waypoint].Position.Z)
            local diff = targetPos - rootPos
            if diff.Magnitude < 5 then
                Storm.Waypoint += 1
            else
                Storm.Core.Position = rootPos + (diff.Unit * Settings.WanderSpeed * dt)
            end
        end

        for _, data in ipairs(Storm.Segments) do
            local rotAngle = t * data.Speed * Settings.RotationSpeed
            local wobbleX = math.sin(t * 2 + (data.Height * 0.05)) * (data.Height * 0.05)
            local wobbleZ = math.cos(t * 2 + (data.Height * 0.05)) * (data.Height * 0.05)
            data.Part.CFrame = CFrame.new(rootPos + Vector3.new(wobbleX, data.Height, wobbleZ)) * CFrame.Angles(0, rotAngle, 0) * CFrame.Angles(0, 0, math.rad(90))
        end

        local stats = EF_STATS[Settings.EF_Level]
        local searchRadius = stats.Radius * Settings.RadiusMult

        if t >= Storm.NextScan then
            Storm.NextScan = t + 0.25
            local parts = workspace:GetPartBoundsInRadius(rootPos, searchRadius)
            local validParts = {}

            for _, p in ipairs(parts) do
                if p:IsA("BasePart") and not p.Anchored and p.Size.Magnitude < 150 then
                    if not isDescendantOfPlayerCharacter(p) then
                        table.insert(validParts, p)
                    end
                end
            end
            Storm.CachedParts = validParts
        end

        for _, p in ipairs(Storm.CachedParts) do
            if p and p.Parent then
                local offset = p.Position - rootPos
                local horizontalDist = Vector3.new(offset.X, 0, offset.Z).Magnitude

                local suctionForce = math.clamp(1 - (horizontalDist / searchRadius), 0, 1)
                local baseStrength = stats.Strength * Settings.PullStrengthMult

                local tangent = Vector3.new(-offset.Z, 0, offset.X).Unit
                local pull = -offset.Unit * (baseStrength * 0.5)
                local lift = Vector3.new(0, (baseStrength * 1.5) * Settings.LiftSpeed, 0)
                local spin = tangent * (baseStrength * 2.5) * Settings.RotationSpeed

                p.AssemblyLinearVelocity = (spin + lift + pull) * suctionForce
            end
        end
    end
end)

Features & Information

Tornado V8.0

Community Comments (1)

ilikehacks200ilikehacks200Sep 26, 2026

is it normal that an ui is stuck on my screen saying "STORM OS v8.0 - CHANGELOG"

More Scripts for Universal Scripts