Run it
Copy the script and paste it into your executor.
WindUI = (:())()
RunService = :()
PathfindingService = :()
UserInputService = :()
Players = :()
Player = Players.LocalPlayer
Workspace = :()
Debris = :()
Lighting = :()
PartHealth = {}
PartHealthMax = {}
(part)
PartHealth[part]
size = part.Size.Magnitude
health =
size <
health =
size <
health =
size <
health =
size <
health =
health =
PartHealth[part] = health
PartHealthMax[part] = health
(part, damage)
(part)
PartHealth[part] = PartHealth[part] - damage
PartHealth[part] <=
part:()
PartHealth[part] =
PartHealthMax[part] =
(part, efLevel)
(part)
currentHealth = PartHealth[part]
maxHealth = PartHealthMax[part]
healthPercent = maxHealth > (currentHealth / maxHealth)
healthMultiplier =
healthPercent <
healthMultiplier =
healthPercent <
healthMultiplier =
mass = part:()
size = part.Size.Magnitude
liftCapabilities = {
[] = {mass = , size = },
[] = {mass = , size = },
[] = {mass = , size = },
[] = {mass = , size = },
[] = {mass = , size = },
[] = {mass = , size = },
[] = {mass = , size = },
}
cap = liftCapabilities[efLevel] liftCapabilities[]
mass <= cap.mass * healthMultiplier size <= cap.size * healthMultiplier *
()
part, _ (PartHealth)
part part.Parent
PartHealth[part] =
PartHealthMax[part] =
PlayerPullData = {}
ActiveTornados = {}
TornadoCounter =
ShowTrail =
TrailParts = {}
WindfieldParts = {}
ShowWindfield =
AutoSpawnCoroutine =
AutoSpawnEnabled =
SupercellPart =
SupercellBlocks = {}
SupercellGridEnabled =
SupercellFogActive =
shrinkCoroutine =
shrinkTarget =
PreviewModel =
PreviewSegments = {}
PreviewBillboard =
PreviewInfoGUI =
originalFogStart = Lighting.FogStart
originalFogEnd = Lighting.FogEnd
originalFogColor = Lighting.FogColor
isNearTornado =
Settings = {
EF_Level = ,
EF_Scale = ,
TornadoHeight = ,
MaxWindSpeed = ,
BaseWidth = ,
WidthIncrement = ,
TornadoType = ,
Mutation = ,
PhysicsMode = ,
PullStrengthMult = ,
RadiusMult = ,
LiftSpeed = ,
RotationSpeed = ,
DebrisDensity = ,
FunnelTrans = ,
Color_R = ,
Color_G = ,
Color_B = ,
WanderSpeed = ,
WidthMultiplier = ,
ShowTrail = ,
TrailColor = .(, , ),
ShowMesonet = ,
ShowWindfield = ,
ShowHUD = ,
GrowDuration = ,
TornadoLifetime = ,
DirectionChangeSpeed = ,
OcclusionAngle = ,
SpawnPosX = ,
SpawnPosZ = ,
SatelliteCount = ,
SatelliteMaxWidth = ,
SatelliteOrbitMult = ,
GrowthBottomLagExponent = ,
IntroDuration = ,
OutroDuration = ,
AutoSpawnMinInterval = ,
AutoSpawnMaxInterval = ,
ShowPreview = ,
Special = {
MultiVortex = ,
Satellites = ,
},
}
EF_SCALES = {
[] = ,
[] = ,
[] = ,
[] = ,
[] = ,
[] = ,
[] = ,
}
SHAPE_FUNCTIONS = {
[] = (ratio) + * ratio ,
[] = (ratio) + * ratio ,
[] = (ratio) + * ratio ,
[] = (ratio) - .(ratio - ) * ,
[] = (ratio) + * .(ratio * .pi) ,
}
(SHAPE_FUNCTIONS, {__index = () SHAPE_FUNCTIONS[] })
(size, efLevel)
sizeRanges = {
[] = {, },
[] = {, },
[] = {, },
[] = {, },
[] = {, },
[] = {, },
[] = {, },
}
range = sizeRanges[efLevel]
range size >= range[] size <= range[]
WindUI:({
Name = ,
Accent = .(),
Background = .(),
Outline = .(),
Text = .(),
Placeholder = .(),
Button = .(),
Icon = .(),
})
Window = WindUI:({
Title = ,
Icon = ,
Author = ,
Folder = ,
Size = .(, ),
MinSize = .(, ),
MaxSize = .(, ),
Transparent = ,
Theme = ,
Resizable = ,
SideBarWidth = ,
BackgroundImageTransparency = ,
HideSearchBar = ,
ScrollBarEnabled = ,
User = {
Enabled = ,
Anonymous = ,
Callback = () () ,
},
})
Window:({
Title = ,
Icon = ,
CornerRadius = .(, ),
StrokeThickness = ,
Color = ColorSequence.(.(), .()),
OnlyMobile = ,
Enabled = ,
Draggable =
})
TabInfo = Window:({ Title = , Icon = })
Window:()
TabSpawn = Window:({ Title = , Icon = })
TabPhysics = Window:({ Title = , Icon = })
TabESP = Window:({ Title = , Icon = })
TabWarnings = Window:({ Title = , Icon = })
TabRadar = Window:({ Title = , Icon = })
Window:()
TabGive = Window:({ Title = , Icon = })
TabPlayer = Window:({ Title = , Icon = })
TabInfo:({ Title = })
TabInfo:({
Title = ,
Desc = ,
Color =
})
TabInfo:({ Title = })
TabInfo:({Title = , Desc = , Color = })
TabInfo:({Title = , Desc = , Color = })
TabInfo:({
Title = ,
Desc = ,
Callback = ()
().Rendering.QualityLevel = .QualityLevel.Level01
.Lighting.GlobalShadows =
.Lighting.FogEnd =
.Lighting.Brightness =
WindUI:({Title = , Content = , Duration = })
})
TabInfo:({
Title = ,
Desc = ,
Callback = ()
(:())()
})
TabSpawn:({ Title = })
SpawnButton = TabSpawn:({
Title = ,
Desc = ,
Callback = ()
()
})
DissipateButton = TabSpawn:({
Title = ,
Desc = ,
Callback = ()
state
char = Player.Character
root = char char:()
root
WindUI:({Title = , Content = , Duration = })
nearest =
minDist = .huge
_, t (ActiveTornados)
t.Active t.state ~= t.state ~=
dist = (t.Core.Position - root.Position).Magnitude
dist < minDist
minDist = dist
nearest = t
nearest
WindUI:({Title = , Content = , Duration = })
shrinkTarget = nearest
shrinkCoroutine = .(()
shrinkTarget shrinkTarget.Active shrinkTarget.state ~=
shrinkTarget.currentScale = .(, shrinkTarget.currentScale - )
(shrinkTarget)
shrinkTarget.currentScale <=
shrinkTarget.state =
shrinkTarget.outroStartTime = ()
shrinkTarget.outroDuration =
shrinkTarget =
.()
shrinkTarget =
shrinkCoroutine =
)
WindUI:({Title = , Content = , Duration = })
shrinkTarget
shrinkTarget =
shrinkCoroutine
.(shrinkCoroutine)
shrinkCoroutine =
WindUI:({Title = , Content = , Duration = })
})
TwinSpawnButton = TabSpawn:({
Title = ,
Desc = ,
Callback = ()
()
})
TabSpawn:({
Title = ,
Desc = ,
Callback = ()
()
})
TabSpawn:()
TabSpawn:({
Title = ,
Desc = ,
Callback = ()
()
})
TabSpawn:({
Title = ,
Desc = ,
Callback = ()
()
})
TabSpawn:({
Title = ,
Desc = ,
Callback = ()
.Lighting.GlobalShadows =
.Lighting.FogEnd =
.Lighting.Brightness =
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
AutoSpawnEnabled = state
state
AutoSpawnCoroutine
AutoSpawnCoroutine = .(()
AutoSpawnEnabled
delayTime = .(Settings.AutoSpawnMinInterval, Settings.AutoSpawnMaxInterval)
(delayTime)
AutoSpawnEnabled
()
AutoSpawnCoroutine =
)
AutoSpawnCoroutine
.(AutoSpawnCoroutine)
AutoSpawnCoroutine =
})
TabSpawn:()
TabSpawn:({ Title = })
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
state
SupercellGridEnabled
()
SupercellGridEnabled =
SupercellPart SupercellPart:()
SupercellPart = .()
SupercellPart.Name =
SupercellPart.Anchored =
SupercellPart.CanCollide =
SupercellPart.Size = .(, , )
SupercellPart.Position = .(, , )
SupercellPart.Color = .(, , )
SupercellPart.Material = .Material.SmoothPlastic
SupercellPart.Transparency =
SupercellPart.Parent = Workspace
WindUI:({Title = , Content = , Duration = })
SupercellPart
SupercellPart:()
SupercellPart =
WindUI:({Title = , Content = , Duration = })
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
SupercellGridEnabled = state
state
SupercellPart
SupercellPart:()
SupercellPart =
()
SupercellFogActive =
isNearTornado
Lighting.FogStart =
Lighting.FogEnd =
Lighting.FogColor = .(, , )
WindUI:({Title = , Content = , Duration = })
()
SupercellFogActive =
isNearTornado
Lighting.FogStart = originalFogStart
Lighting.FogEnd = originalFogEnd
Lighting.FogColor = originalFogColor
WindUI:({Title = , Content = , Duration = })
})
TabSpawn:()
TabSpawn:({ Title = })
TabSpawn:({
Title = ,
Values = {
{Title=}, {Title=}, {Title=}, {Title=},
{Title=}, {Title=}, {Title=}
},
Default = ,
Callback = (opt)
efNumber = (.(opt.Title, ))
Settings.EF_Level = efNumber
Settings.EF_Scale = EF_SCALES[efNumber]
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.TornadoHeight = v
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.MaxWindSpeed = v
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.WidthMultiplier = v
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.GrowDuration = v
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.IntroDuration = v
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.OutroDuration = v
_, t (ActiveTornados)
t.Active t.state ==
t.outroDuration = v
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.TornadoLifetime = v
_, t (ActiveTornados)
t.Active t.state ==
t.lifetime = v
t.outroTriggerTime = t.stableStartTime + v
})
TabSpawn:({
Title = ,
Value = {Min = -, Max = , Default = },
Callback = (v)
Settings.SpawnPosX = v
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Value = {Min = -, Max = , Default = },
Callback = (v)
Settings.SpawnPosZ = v
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Values = {
{Title=}, {Title=}, {Title=},
{Title=},
{Title=}, {Title=}, {Title=}
},
Default = ,
Callback = (opt)
Settings.Mutation = opt.Title
_, t (ActiveTornados)
(t)
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Values = {
{Title=},
{Title=},
{Title=},
{Title=},
{Title=}
},
Default = ,
Callback = (opt)
Settings.TornadoType = opt.Title
Settings.ShowPreview
()
()
})
SpecialDropdown = TabSpawn:({
Title = ,
Values = {
{Title=},
{Title=},
{Title=}
},
Default = ,
Multi = ,
Callback = (selected)
Settings.Special.MultiVortex =
Settings.Special.Satellites =
hasNone =
_, opt (selected)
opt.Title ==
hasNone =
opt.Title ==
Settings.Special.MultiVortex =
opt.Title ==
Settings.Special.Satellites =
hasNone
Settings.Special.MultiVortex =
Settings.Special.Satellites =
_, t (ActiveTornados)
t.Active t.state ==
Settings.Special.MultiVortex t.InnerVortices
_, v (t.InnerVortices)
_, seg (v.Segments)
seg.Part seg.Part:()
t.InnerVortices =
Settings.Special.Satellites t.Satellites
_, sat (t.Satellites)
sat sat.Active (sat)
t.Satellites = {}
(t)
(t)
(t)
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
Settings.ShowPreview = state
state
()
()
})
TabSpawn:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.SatelliteCount = v
Settings.ShowPreview
()
()
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
Settings.ShowTrail = state
ShowTrail = state
state
_, part (TrailParts)
part part:()
.(TrailParts)
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
Settings.ShowWindfield = state
ShowWindfield = state
state
_, part (WindfieldParts)
part part:()
.(WindfieldParts)
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
Settings.ShowMesonet = state
state
_, t (ActiveTornados)
t.MesonetBeams
_, beam (t.MesonetBeams) beam:()
t.MesonetBeams = {}
})
TabSpawn:({
Title = ,
Default = ,
Callback = (state)
Settings.ShowHUD = state
HudFrame
HudFrame.Visible = state
})
TabSpawn:()
TabWarnings:({ Title = })
GlobalWarningParagraph = TabWarnings:({
Title = ,
Desc = ,
Color =
})
()
char = Player.Character
root = char char:()
root
GlobalWarningParagraph:()
GlobalWarningParagraph:()
#ActiveTornados ==
GlobalWarningParagraph:()
GlobalWarningParagraph:()
highestEF =
nearestDist = .huge
inDanger =
_, tornado (ActiveTornados)
tornado.Active tornado.state ~=
effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
dangerRadius = effectiveTopWidth *
dist = (tornado.Core.Position - root.Position).Magnitude
dist <= dangerRadius
inDanger =
tornado.EF_Level > highestEF
highestEF = tornado.EF_Level
dist < nearestDist
nearestDist = dist
inDanger
GlobalWarningParagraph:()
GlobalWarningParagraph:()
title, desc = ,
highestEF <=
title =
desc =
highestEF ==
title =
desc =
highestEF <=
title =
desc =
highestEF <=
title =
desc =
title =
desc =
GlobalWarningParagraph:(title)
GlobalWarningParagraph:(.(, desc, .(nearestDist), highestEF))
TabRadar:({ Title = })
RadarGridParagraph = TabRadar:({
Title = ,
Desc = ,
Color =
})
RadarInfoParagraph = TabRadar:({
Title = ,
Desc = ,
Color =
})
SimulatedPartsDestroyed =
()
char = Player.Character
root = char char:()
#ActiveTornados ==
RadarInfoParagraph:()
nearestTornado =
minDist = .huge
_, tornado (ActiveTornados)
tornado.Active tornado.state ~=
dist = root (tornado.Core.Position - root.Position).Magnitude .huge
dist < minDist
minDist = dist
nearestTornado = tornado
nearestTornado
RadarInfoParagraph:()
efLevel = nearestTornado.EF_Level
maxWind = .(nearestTornado.MaxWindSpeed * ) /
actualWind = .((nearestTornado.MaxWindSpeed * nearestTornado.currentScale) * ) /
width = .(nearestTornado.targetFullTopWidth * nearestTornado.currentScale)
distance = root .(minDist)
SimulatedPartsDestroyed = SimulatedPartsDestroyed + .(, )
RadarInfoParagraph:(.(
,
efLevel, maxWind, actualWind, width, distance, SimulatedPartsDestroyed
))
()
char = Player.Character
root = char char:()
root
RadarGridParagraph:()
gridSize =
gridChars = {}
radarRadius =
i = , gridSize * gridSize gridChars[i] =
_, tornado (ActiveTornados)
tornado.Active tornado.state ~= tornado.state ~=
diff = tornado.Core.Position - root.Position
dist = diff.Magnitude
dist <= radarRadius
gridX = .(((diff.X / radarRadius) * + ) * (gridSize - )) +
gridZ = .(((diff.Z / radarRadius) * + ) * (gridSize - )) +
gridX >= gridX <= gridSize gridZ >= gridZ <= gridSize
idx = gridX + (gridZ - ) * gridSize
tornadoColorCode =
tornado.EF_Level <= tornadoColorCode =
tornado.EF_Level <= tornadoColorCode =
tornado.EF_Level <= tornadoColorCode =
tornado.EF_Level <= tornadoColorCode =
tornado.EF_Level <= tornadoColorCode =
tornadoColorCode =
gridChars[idx] = tornadoColorCode
displayString =
z = , gridSize -
x = , gridSize -
displayString = displayString .. gridChars[(x + ) + z * gridSize]
z < gridSize - displayString = displayString ..
RadarGridParagraph:(displayString)
.(()
()
()
.()
)
TabRadar:({ Title = })
TabGive:({ Title = })
TabGive:({
Title = ,
Desc = ,
Callback = ()
()
})
TabGive:({
Title = ,
Desc = ,
Callback = ()
i = ,
()
})
TabESP:({ Title = , Desc = , Color = })
TabPlayer:({ Title = })
TabPlayer:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
char = Player.Character
char char:()
char.Humanoid.WalkSpeed = v
})
TabPlayer:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
char = Player.Character
char char:()
char.Humanoid.JumpPower = v
})
TabPlayer:({
Title = ,
Desc = ,
Callback = ()
char = Player.Character
char char:()
char.Humanoid.WalkSpeed =
char.Humanoid.JumpPower =
WindUI:({Title = , Content = , Duration = })
})
TabPhysics:({ Title = })
TabPhysics:({
Title = ,
Values = {{Title=}, {Title=}, {Title=}},
Default = ,
Callback = (opt)
Settings.PhysicsMode = opt.Title
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.RadiusMult = v
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.PullStrengthMult = v
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.LiftSpeed = v
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.RotationSpeed = v
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.DirectionChangeSpeed = v
_, t (ActiveTornados)
t.directionChangeSpeed = v
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.OcclusionAngle = v
})
TabPhysics:()
TabPhysics:({ Title = })
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.FunnelTrans = v
_, t (ActiveTornados)
(t)
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v)
Settings.DebrisDensity = v
_, t (ActiveTornados)
t.DebrisEmitter
t.DebrisEmitter.Rate = v
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.Color_R = v; _, t (ActiveTornados) (t)
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.Color_G = v; _, t (ActiveTornados) (t)
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.Color_B = v; _, t (ActiveTornados) (t)
})
TabPhysics:({
Title = ,
Value = {Min = , Max = , Default = },
Callback = (v) Settings.WanderSpeed = v
})
()
char = Player.Character
char
backpack = Player:()
backpack
existing = backpack:()
existing existing:()
tool = .()
tool.Name =
tool.RequiresHandle =
tool.CanBeDropped =
tool.ManualActivationOnly =
tool.ToolTip =
tool.Activated:(()
humanoid = char:()
humanoid humanoid:()
tool.Parent =
(char)
tool:()
)
tool.Parent = backpack
WindUI:({Title = , Content = , Duration = })
(char)
root = char:()
root
probe = .()
probe.Name =
probe.Anchored =
probe.CanCollide =
probe.Size = .(, , )
probe.Material = .Material.Metal
probe.BrickColor = BrickColor.()
probe.Position = root.Position + .(, -, )
probe.Parent = Workspace
light = .(, probe)
light.Brightness =
light.Range =
light.Color = .(, , )
sound = .(, probe)
sound.SoundId =
sound.Volume =
sound:()
Debris:(probe, )
WindUI:({Title = , Content = , Duration = })
()
efLevel = .(, )
efScale = EF_SCALES[efLevel]
height = .(, )
maxWind = .(, )
baseWidth = .(, )
widthMult = .() * +
mutation = ({ , , , , , })[.(,)]
tornadoType = ({ , , , , })[.(,)]
physicsMode = ({ , , })[.(,)]
lifetime = .(, )
introDuration = .(, )
outroDuration = .(, )
wanderSpeed = .(, )
spawnX = .(-, )
spawnZ = .(-, )
oldSpecial = Settings.Special
oldSettings = {
EF_Level = Settings.EF_Level, EF_Scale = Settings.EF_Scale,
TornadoHeight = Settings.TornadoHeight, MaxWindSpeed = Settings.MaxWindSpeed,
BaseWidth = Settings.BaseWidth, WidthMultiplier = Settings.WidthMultiplier,
Mutation = Settings.Mutation, TornadoType = Settings.TornadoType,
PhysicsMode = Settings.PhysicsMode, TornadoLifetime = Settings.TornadoLifetime,
IntroDuration = Settings.IntroDuration, OutroDuration = Settings.OutroDuration,
WanderSpeed = Settings.WanderSpeed,
SpawnPosX = Settings.SpawnPosX, SpawnPosZ = Settings.SpawnPosZ,
Special = Settings.Special,
}
Settings.EF_Level = efLevel
Settings.EF_Scale = efScale
Settings.TornadoHeight = height
Settings.MaxWindSpeed = maxWind
Settings.BaseWidth = baseWidth
Settings.WidthMultiplier = widthMult
Settings.Mutation = mutation
Settings.TornadoType = tornadoType
Settings.PhysicsMode = physicsMode
Settings.TornadoLifetime = lifetime
Settings.IntroDuration = introDuration
Settings.OutroDuration = outroDuration
Settings.WanderSpeed = wanderSpeed
Settings.SpawnPosX = spawnX
Settings.SpawnPosZ = spawnZ
tornado = (, , .(spawnX, , spawnZ))
k, v (oldSettings) Settings[k] = v
tornado
(tornado)
Settings.TornadoType ==
_, data (tornado.Segments)
eye = data.Part:()
eye eye:()
tornado.ExtraEmitter
tornado.ExtraEmitter:()
tornado.ExtraEmitter =
col = .(, , )
_, data (tornado.Segments)
data.Part.Color = col
data.Part.Transparency =
tornado.MutationStrengthMult =
col
strengthMult =
extraEmitter =
tornado.ExtraEmitter
tornado.ExtraEmitter:()
tornado.ExtraEmitter =
Settings.Mutation ==
col = .(, , )
strengthMult =
_, data (tornado.Segments)
part = data.Part
part:()
decal = .(, part)
decal.Name =
decal.Texture =
decal.Face = .NormalId.Front
decal.Transparency =
Settings.Mutation ==
col = .(, , )
_, data (tornado.Segments)
eye = data.Part:()
eye eye:()
Settings.Mutation ==
col = .(, , )
_, data (tornado.Segments)
eye = data.Part:()
eye eye:()
Settings.Mutation ==
col = .(, , )
strengthMult =
attach = .(, tornado.Core)
emitter = .(, attach)
emitter.Texture =
emitter.Size = NumberSequence.(, )
emitter.Transparency = NumberSequence.(, )
emitter.Speed = NumberRange.(, )
emitter.Lifetime = NumberRange.(, )
emitter.Rate =
emitter.SpreadAngle = .(, )
emitter.Color = ColorSequence.(.(,,))
tornado.ExtraEmitter = emitter
Settings.Mutation ==
col = .(, , )
strengthMult =
attach = .(, tornado.Core)
emitter = .(, attach)
emitter.Texture =
emitter.Size = NumberSequence.(, )
emitter.Transparency = NumberSequence.(, )
emitter.Speed = NumberRange.(, )
emitter.Lifetime = NumberRange.(, )
emitter.Rate =
emitter.SpreadAngle = .(, )
emitter.Color = ColorSequence.(.(,,), .(,,))
tornado.ExtraEmitter = emitter
Settings.Mutation ==
col = .(, , )
strengthMult =
attach = .(, tornado.Core)
emitter = .(, attach)
emitter.Texture =
emitter.Size = NumberSequence.(, )
emitter.Transparency = NumberSequence.(, )
emitter.Speed = NumberRange.(, )
emitter.Lifetime = NumberRange.(, )
emitter.Rate =
emitter.SpreadAngle = .(, )
emitter.Color = ColorSequence.(.(,,), .(,,))
tornado.ExtraEmitter = emitter
col = .(Settings.Color_R, Settings.Color_G, Settings.Color_B)
_, data (tornado.Segments)
eye = data.Part:()
eye eye:()
_, data (tornado.Segments)
data.Part.Color = col
data.Part.Transparency = Settings.FunnelTrans
tornado.DebrisEmitter
tornado.DebrisEmitter.Color = ColorSequence.(col)
tornado.DebrisEmitter.Rate = Settings.DebrisDensity
tornado.MutationStrengthMult = strengthMult
(tornado)
Settings.TornadoType ==
col = .(, , )
_, data (tornado.Segments)
data.Part.Color = col
data.Part.Transparency =
col = .(Settings.Color_R, Settings.Color_G, Settings.Color_B)
_, data (tornado.Segments)
data.Part.Color = col
data.Part.Transparency = Settings.FunnelTrans
tornado.DebrisEmitter
tornado.DebrisEmitter.Color = ColorSequence.(col)
(effectiveWidth)
effectiveWidth <=
.(, , )
effectiveWidth <=
.(, , )
effectiveWidth <=
.(, , )
effectiveWidth <=
.(, , )
.(, , )
(tornado)
Settings.ShowTrail
effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
trailWidth = effectiveTopWidth *
trailColor = (effectiveTopWidth)
trailPart = .()
trailPart.Name =
trailPart.Shape = .PartType.Cylinder
trailPart.Anchored =
trailPart.CanCollide =
trailPart.CanTouch =
trailPart.CanQuery =
trailPart.Material = .Material.SmoothPlastic
trailPart.Color = trailColor
trailPart.Transparency =
trailPart.Size = .(, trailWidth * , trailWidth * )
trailPart.CFrame = .(tornado.Core.Position) * .(, , .())
trailPart.Parent = Workspace
.(TrailParts, trailPart)
Debris:(trailPart, )
#TrailParts >
oldestPart = .(TrailParts, )
oldestPart oldestPart:()
(tornado)
ShowWindfield
effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
windWidth = effectiveTopWidth *
windPart = .()
windPart.Name =
windPart.Shape = .PartType.Cylinder
windPart.Anchored =
windPart.CanCollide =
windPart.CanTouch =
windPart.CanQuery =
windPart.Material = .Material.SmoothPlastic
windPart.Color = .(, , )
windPart.Transparency =
windPart.Size = .(, windWidth * , windWidth * )
windPart.CFrame = .(tornado.Core.Position) * .(, , .())
windPart.Parent = Workspace
.(WindfieldParts, windPart)
Debris:(windPart, )
#WindfieldParts >
oldest = .(WindfieldParts, )
oldest oldest:()
(tornado)
tornado tornado.Active
tornado.InnerVortices
_, v (tornado.InnerVortices)
_, seg (v.Segments)
seg.Part seg.Part:()
tornado.InnerVortices =
numVortices =
radius = tornado.targetFullTopWidth * tornado.currentScale *
height = Settings.TornadoHeight *
vortexHeight = height *
vortexWidth = tornado.baseWidth *
vortices = {}
i = , numVortices
angle = (i-) * ( / numVortices)
segments = {}
j = ,
ratio = j /
h = ratio * vortexHeight
width = vortexWidth * ( + * ratio)
part = .()
part.Shape = .PartType.Cylinder
part.Anchored =
part.CanCollide =
part.CanTouch =
part.CanQuery =
part.Material = .Material.SmoothPlastic
part.Size = .(width, , width)
part.Transparency =
part.Color = .(, , )
part.Parent = tornado.Model
decal = .(, part)
decal.Texture =
decal.Transparency =
.(segments, {Part = part, Height = h})
.(vortices, {
Segments = segments,
Angle = angle,
OrbitSpeed = .(, ) /
})
tornado.InnerVortices = vortices
(tornado)
tornado tornado.Active
tornado.SatelliteVortices
_, v (tornado.SatelliteVortices)
_, seg (v.Segments)
seg.Part seg.Part:()
tornado.SatelliteVortices =
numSatellites = Settings.SatelliteCount
numSatellites ==
orbitRadius = tornado.targetFullTopWidth * tornado.currentScale *
vortexHeight = Settings.TornadoHeight *
vortexWidth = tornado.baseWidth *
satellites = {}
i = , numSatellites
angle = (i-) * ( / numSatellites) + .(-, )
segments = {}
j = ,
ratio = j /
h = ratio * vortexHeight
width = vortexWidth * ( + * ratio)
part = .()
part.Shape = .PartType.Cylinder
part.Anchored =
part.CanCollide =
part.CanTouch =
part.CanQuery =
part.Material = .Material.SmoothPlastic
part.Size = .(width, , width)
part.Transparency =
part.Color = .(, , )
part.Parent = tornado.Model
decal = .(, part)
decal.Texture =
decal.Transparency =
.(segments, {Part = part, Height = h})
.(satellites, {
Segments = segments,
Angle = angle,
OrbitSpeed = ( + .() * ),
OrbitRadius = orbitRadius * ( + .() * )
})
tornado.SatelliteVortices = satellites
(tornado)
tornado.SmokeEmitter
tornado.SmokeEmitter:()
tornado.SmokeEmitter =
attach = .(, tornado.Core)
emitter = .(, attach)
emitter.Texture =
emitter.Size = NumberSequence.(, )
emitter.Transparency = NumberSequence.(, )
emitter.Speed = NumberRange.(, )
emitter.Lifetime = NumberRange.(, )
emitter.Rate =
emitter.SpreadAngle = .(, )
emitter.RotSpeed = NumberRange.(-, )
emitter.Acceleration = .(, , )
emitter.VelocityInheritance =
emitter.Enabled =
tornado.SmokeEmitter = emitter
tornado.SmokeAttach = attach
tornado.SmokeParticles
_, p (tornado.SmokeParticles)
p p:()
tornado.SmokeParticles = {}
(tornado, dt)
tornado.SmokeEmitter tornado.Core
center = tornado.Core.Position
topWidth = tornado.targetFullTopWidth * tornado.currentScale
radius = topWidth *
.() <
angle = .() * * .pi
dist = radius * ( + .() * )
x = center.X + .(angle) * dist
z = center.Z + .(angle) * dist
y = center.Y + .() * topWidth *
particle = .()
particle.Name =
particle.Anchored =
particle.CanCollide =
particle.CanTouch =
particle.CanQuery =
particle.Material = .Material.SmoothPlastic
particle.Size = .(, , )
particle.BrickColor = BrickColor.()
particle.Transparency =
particle.Position = .(x, y, z)
particle.Parent = Workspace
att = .(, particle)
pEmitter = .(, att)
pEmitter.Texture =
pEmitter.Size = NumberSequence.(, )
pEmitter.Transparency = NumberSequence.(, )
pEmitter.Lifetime = NumberRange.(, )
pEmitter.Rate =
pEmitter.SpreadAngle = .(, )
pEmitter.VelocityInheritance =
pEmitter.Enabled =
tornado.SmokeParticles tornado.SmokeParticles = {}
.(tornado.SmokeParticles, particle)
dir = (center - particle.Position) * .(,,)
dir.Magnitude >
dir = dir.Unit
tangent = .(-dir.Z, , dir.X).Unit
particle.AssemblyLinearVelocity = dir * + tangent * + .(, , )
Debris:(particle, )
tornado.SmokeParticles
i = #tornado.SmokeParticles, , -
p = tornado.SmokeParticles[i]
p p.Parent
.(tornado.SmokeParticles, i)
dir = (center - p.Position) * .(,,)
dir.Magnitude >
unit = dir.Unit
tangent = .(-unit.Z, , unit.X).Unit
speed = .(, - dir.Magnitude * )
p.AssemblyLinearVelocity = p.AssemblyLinearVelocity + (unit * speed + tangent * ) * dt
life = p:()
p:(, life + dt)
p.Transparency = .(, + life * )
(tornado)
_, data (tornado.Segments)
data.Part:()
.(tornado.Segments)
totalHeight = Settings.TornadoHeight
baseWidth = Settings.BaseWidth * (Settings.WidthMultiplier )
targetFullTopWidth = baseWidth
Settings.TornadoType ==
totalHeight = totalHeight *
baseWidth = .(baseWidth, )
Settings.TornadoType ==
baseWidth = .(baseWidth, )
tornado.targetFullTopWidth = targetFullTopWidth
tornado.baseWidth = baseWidth
tornado.initialScale = .(, / targetFullTopWidth)
tornado.shrinkTargetScale = tornado.initialScale
numSegments = .(totalHeight / )
numSegments < numSegments =
shapeFunc = SHAPE_FUNCTIONS[Settings.TornadoType] SHAPE_FUNCTIONS[]
i = , numSegments
ratio = i / numSegments
h = ratio * totalHeight
shapeMult = (ratio)
p = .()
p.Shape = .PartType.Cylinder
p.Anchored =
p.CanCollide =
p.CanTouch =
p.CanQuery =
p.Material = .Material.SmoothPlastic
p.Size = .(, , )
p.Transparency =
p.Parent = tornado.Model
Settings.TornadoType ==
decal = .(, p)
decal.Texture =
decal.Transparency =
p.Material = .Material.Sand
decal = .(, p)
decal.Texture =
decal.Transparency =
.(tornado.Segments, {
Part = p,
Height = h,
Speed = * ( - ratio),
ratio = ratio,
shapeMult = shapeMult,
})
i % == .()
MAX_SEGMENT_WIDTH =
(tornado)
Settings.TornadoType ==
rootPos = tornado.Core.Position
local time = tick()
for _, seg in ipairs(tornado.Segments) do
local ratio = seg.ratio
local angle = math.sin(time * 2 + ratio * 10) * 0.5
local offsetX = math.sin(angle) * (seg.Part.Size.Y * 0.5)
local offsetZ = math.cos(angle) * (seg.Part.Size.Y * 0.5)
seg.Part.Position = rootPos + Vector3.new(offsetX, seg.Height, offsetZ)
seg.Part.Size = Vector3.new(4, seg.Part.Size.Y, seg.Part.Size.Z)
end
return
end
local effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
local baseWidth = tornado.baseWidth
local topScale = tornado.currentScale
local bottomScale = topScale
for _, seg in ipairs(tornado.Segments) do
local linearWidth = baseWidth * (bottomScale + (topScale - bottomScale) * seg.ratio)
local finalWidth = linearWidth * seg.shapeMult
finalWidth = math.min(finalWidth, MAX_SEGMENT_WIDTH)
seg.Part.Size = Vector3.new(5, finalWidth, finalWidth)
end
end
function SpawnTornado(mergeParent1, mergeParent2, overridePos)
local tornado = {
Model = Instance.new("Model", Workspace),
Segments = {},
Active = true,
Core = nil,
DebrisEmitter = nil,
EF_Scale = Settings.EF_Scale,
EF_Level = Settings.EF_Level,
targetFullTopWidth = 0,
baseWidth = 0,
currentScale = 0.1,
initialScale = 0,
shrinkTargetScale = 0,
state = "intro",
introStartTime = tick(),
introDuration = Settings.IntroDuration or 3,
outroStartTime = nil,
outroDuration = Settings.OutroDuration or 5,
growStartTime = nil,
stableStartTime = nil,
outroTriggerTime = nil,
fadeStartTime = nil,
growDuration = Settings.GrowDuration,
lifetime = Settings.TornadoLifetime,
fadeDuration = 5,
moveDirection = Vector3.new(math.random(-1,1), 0, math.random(-1,1)).Unit,
targetDirection = nil,
directionChangeSpeed = Settings.DirectionChangeSpeed,
lastOcclusionChange = 0,
occlusionInterval = 60,
WanderSpeed = Settings.WanderSpeed,
MutationStrengthMult = 50.0,
MaxWindSpeed = Settings.MaxWindSpeed * (EF_SCALES[Settings.EF_Level] or 1),
PhysicsMode = Settings.PhysicsMode,
PullStrengthMult = Settings.PullStrengthMult,
RadiusMult = Settings.RadiusMult,
LiftSpeed = Settings.LiftSpeed,
RotationSpeed = Settings.RotationSpeed,
LastTrailFrame = 0,
MesonetBeams = {},
InnerVortices = nil,
SatelliteVortices = nil, -- NOVO: vórtices internos usados como satélites
Satellites = {}, -- mantido para compatibilidade, mas não usado mais
SpawnTime = os.time(),
PendingSuck = {},
UnanchoredParts = {},
ProcessedAnchored = {},
IsTwin = false,
TwinPartner = nil,
TwinAngle = 0,
TwinOrbitRadius = 10,
TwinOrbitSpeed = 1,
TwinCommonCenter = nil,
WarningSection = nil,
WarningParagraph = nil,
PlayerInDanger = false,
ExtraEmitter = nil,
IsRapidAutoSpawn = false,
IsSatellite = false,
SmokeEmitter = nil, -- NOVO
SmokeAttach = nil,
SmokeParticles = {},
}
if mergeParent1 and mergeParent2 then
tornado.EF_Level = math.max(mergeParent1.EF_Level, mergeParent2.EF_Level) + 1
tornado.EF_Level = math.min(tornado.EF_Level, 6)
tornado.EF_Scale = EF_SCALES[tornado.EF_Level] or 40
tornado.MaxWindSpeed = mergeParent1.MaxWindSpeed + mergeParent2.MaxWindSpeed
tornado.targetFullTopWidth = mergeParent1.targetFullTopWidth + mergeParent2.targetFullTopWidth
tornado.baseWidth = mergeParent1.baseWidth + mergeParent2.baseWidth
tornado.Core.Position = (mergeParent1.Core.Position + mergeParent2.Core.Position) / 2
tornado.introDuration = 0.2
tornado.currentScale = 0
tornado.state = "intro"
else
TornadoCounter = TornadoCounter + 1
tornado.Model.Name = "Tornado_" .. TornadoCounter
local Core = Instance.new("Part", tornado.Model)
Core.Anchored = true
Core.CanCollide = false
Core.Transparency = 1
local pos = overridePos or Vector3.new(Settings.SpawnPosX, 5, Settings.SpawnPosZ)
Core.Position = pos
tornado.Core = Core
tornado.Model.PrimaryPart = Core
end
local Core = tornado.Core or Instance.new("Part", tornado.Model)
local attach = Instance.new("Attachment", Core)
local emitter = Instance.new("ParticleEmitter", attach)
emitter.Texture = "rbxassetid://258128463"
emitter.Size = NumberSequence.new({NumberSequenceKeypoint.new(0, 20), NumberSequenceKeypoint.new(1, 100)})
emitter.Transparency = NumberSequence.new(0.5, 1)
emitter.Speed = NumberRange.new(50, 150)
emitter.Lifetime = NumberRange.new(2, 4)
emitter.SpreadAngle = Vector2.new(180, 180)
emitter.Rate = Settings.DebrisDensity
tornado.DebrisEmitter = emitter
GenerateFunnel(tornado)
tornado.currentScale = tornado.initialScale
UpdateMutationEffects(tornado)
tornado.targetDirection = tornado.moveDirection
tornado.lastOcclusionChange = tick()
-- ===== CRIAÇÃO DE SATÉLITES COMO VÓRTICES INTERNOS =====
if Settings.Special.Satellites and not tornado.IsTwin and not tornado.IsSatellite then
CreateSatelliteVortices(tornado)
end
-- ===== CRIAÇÃO DO EMISSOR DE FUMAÇA =====
CreateSmokeParticles(tornado)
table.insert(ActiveTornados, tornado)
UpdateGlobalCloudPresence()
local section = TabWarnings:Section({ Title = tornado.Model.Name })
local paragraph = section:Paragraph({ Title = "", Desc = "Carregando..." })
tornado.WarningSection = section
tornado.WarningParagraph = paragraph
if SpawnButton then
SpawnButton:SetEnabled(#ActiveTornados == 0)
end
if TwinSpawnButton then
TwinSpawnButton:SetEnabled(#ActiveTornados == 0)
end
return tornado
end
function SpawnTwinTornados()
local pos = Vector3.new(Settings.SpawnPosX, 5, Settings.SpawnPosZ)
local t1 = SpawnTornado(nil, nil, pos)
local t2 = SpawnTornado(nil, nil, pos)
local separation = (t1.targetFullTopWidth + t2.targetFullTopWidth) * 1.5
local commonCenter = pos
local orbitSpeed = 0.5
t1.IsTwin = true
t2.IsTwin = true
t1.TwinPartner = t2
t2.TwinPartner = t1
t1.TwinCommonCenter = commonCenter
t2.TwinCommonCenter = commonCenter
t1.TwinOrbitRadius = separation / 35
t2.TwinOrbitRadius = separation / 35
t1.TwinOrbitSpeed = orbitSpeed
t2.TwinOrbitSpeed = orbitSpeed
t1.TwinAngle = 0
t2.TwinAngle = math.pi
t1.Core.Position = commonCenter + Vector3.new(math.cos(t1.TwinAngle) * t1.TwinOrbitRadius, 0, math.sin(t1.TwinAngle) * t1.TwinOrbitRadius)
t2.Core.Position = commonCenter + Vector3.new(math.cos(t2.TwinAngle) * t2.TwinOrbitRadius, 0, math.sin(t2.TwinAngle) * t2.TwinOrbitRadius)
t1.WanderSpeed = 0
t2.WanderSpeed = 0
t1.targetDirection = nil
t2.targetDirection = nil
end
local function DestroyTornado(tornado)
tornado.Active = false
if tornado.Satellites then
for _, sat in ipairs(tornado.Satellites) do
if sat and sat.Active then
DestroyTornado(sat)
end
end
tornado.Satellites = {}
end
-- Destroi os novos satélites (vórtices internos)
if tornado.SatelliteVortices then
for _, v in ipairs(tornado.SatelliteVortices) do
for _, seg in ipairs(v.Segments) do
if seg.Part then seg.Part:Destroy() end
end
end
tornado.SatelliteVortices = nil
end
if tornado.MesonetBeams then
for _, beam in ipairs(tornado.MesonetBeams) do beam:Destroy() end
end
if tornado.InnerVortices then
for _, v in ipairs(tornado.InnerVortices) do
for _, seg in ipairs(v.Segments) do
if seg.Part then seg.Part:Destroy() end
end
end
tornado.InnerVortices = nil
end
if tornado.ExtraEmitter then
tornado.ExtraEmitter:Destroy()
end
if tornado.SmokeEmitter then
tornado.SmokeEmitter:Destroy()
tornado.SmokeEmitter = nil
end
if tornado.SmokeParticles then
for _, p in ipairs(tornado.SmokeParticles) do
if p then p:Destroy() end
end
tornado.SmokeParticles = {}
end
if tornado.Model then
tornado.Model:Destroy()
end
if tornado.WarningSection then
tornado.WarningSection:Destroy()
tornado.WarningSection = nil
tornado.WarningParagraph = nil
end
for i, t in ipairs(ActiveTornados) do
if t == tornado then
table.remove(ActiveTornados, i)
break
end
end
if tornado.IsTwin and tornado.TwinPartner then
local partner = tornado.TwinPartner
partner.IsTwin = false
partner.TwinPartner = nil
partner.WanderSpeed = Settings.WanderSpeed
end
if tornado.IsRapidAutoSpawn then
for i, t in ipairs(RapidAutoSpawnTornados) do
if t == tornado then
table.remove(RapidAutoSpawnTornados, i)
break
end
end
end
if SupercellGridEnabled and #ActiveTornados == 0 then
SupercellFogActive = false
if not isNearTornado then
Lighting.FogStart = originalFogStart
Lighting.FogEnd = originalFogEnd
Lighting.FogColor = originalFogColor
end
end
UpdateGlobalCloudPresence()
if #ActiveTornados == 0 then
if SpawnButton then SpawnButton:SetEnabled(true) end
if TwinSpawnButton then TwinSpawnButton:SetEnabled(true) end
end
end
function DestroyAllTornados()
for _, t in ipairs(ActiveTornados) do
t.Active = false
if t.Satellites then
for _, sat in ipairs(t.Satellites) do
if sat and sat.Active then DestroyTornado(sat) end
end
t.Satellites = {}
end
if t.SatelliteVortices then
for _, v in ipairs(t.SatelliteVortices) do
for _, seg in ipairs(v.Segments) do
if seg.Part then seg.Part:Destroy() end
end
end
t.SatelliteVortices = nil
end
if t.MesonetBeams then
for _, beam in ipairs(t.MesonetBeams) do beam:Destroy() end
end
if t.InnerVortices then
for _, v in ipairs(t.InnerVortices) do
for _, seg in ipairs(v.Segments) do
if seg.Part then seg.Part:Destroy() end
end
end
t.InnerVortices = nil
end
if t.ExtraEmitter then
t.ExtraEmitter:Destroy()
end
if t.SmokeEmitter then
t.SmokeEmitter:Destroy()
end
if t.SmokeParticles then
for _, p in ipairs(t.SmokeParticles) do
if p then p:Destroy() end
end
t.SmokeParticles = {}
end
if t.WarningSection then
t.WarningSection:Destroy()
t.WarningSection = nil
t.WarningParagraph = nil
end
if t.Model then
t.Model:Destroy()
end
end
table.clear(ActiveTornados)
table.clear(RapidAutoSpawnTornados)
SupercellFogActive = false
if not isNearTornado then
Lighting.FogStart = originalFogStart
Lighting.FogEnd = originalFogEnd
Lighting.FogColor = originalFogColor
end
UpdateGlobalCloudPresence()
for _, part in ipairs(TrailParts) do
if part then part:Destroy() end
end
table.clear(TrailParts)
for _, part in ipairs(WindfieldParts) do
if part then part:Destroy() end
end
table.clear(WindfieldParts)
if SpawnButton then SpawnButton:SetEnabled(true) end
if TwinSpawnButton then TwinSpawnButton:SetEnabled(true) end
end
local function GetNearbyProbeCount(tornado)
local count = 0
local radius = tornado.targetFullTopWidth * tornado.currentScale * 2
for _, obj in ipairs(Workspace:GetChildren()) do
if obj.Name == "Probe" and obj:IsA("BasePart") then
local dist = (obj.Position - tornado.Core.Position).Magnitude
if dist <= radius then
count = count + 1
end
end
end
return count
end
local function UpdateMesonetVisuals(tornado)
if not Settings.ShowMesonet then
if tornado.MesonetBeams then
for _, beam in ipairs(tornado.MesonetBeams) do beam:Destroy() end
tornado.MesonetBeams = {}
end
return
end
if tornado.MesonetBeams then
for _, beam in ipairs(tornado.MesonetBeams) do beam:Destroy() end
tornado.MesonetBeams = {}
end
local corePos = tornado.Core.Position
local radius = tornado.targetFullTopWidth * tornado.currentScale * 2
for _, obj in ipairs(Workspace:GetChildren()) do
if obj.Name == "Probe" and obj:IsA("BasePart") then
local dist = (obj.Position - corePos).Magnitude
if dist <= radius then
local beam = Instance.new("Beam")
beam.Attachment0 = Instance.new("Attachment", tornado.Core)
beam.Attachment0.Position = Vector3.new(0,0,0)
beam.Attachment1 = Instance.new("Attachment", obj)
beam.Color = ColorSequence.new(Color3.fromRGB(0,255,255))
beam.Width0 = 0.2
beam.Width1 = 0.2
beam.Transparency = NumberSequence.new(0.5)
beam.Parent = tornado.Core
table.insert(tornado.MesonetBeams, beam)
end
end
end
end
-- =================[ HUD ]================
local HudFrame
local function CreateHUD()
if HudFrame then return end
local sg = Instance.new("ScreenGui")
sg.Name = "TornadoHUD"
sg.Parent = game:GetService("CoreGui")
local frame = Instance.new("Frame", sg)
frame.Size = UDim2.new(0, 200, 0, 110)
frame.Position = UDim2.new(1, -210, 0, 10)
frame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
frame.BackgroundTransparency = 0.6
frame.BorderSizePixel = 0
local layout = Instance.new("UIListLayout", frame)
layout.Padding = UDim.new(0, 4)
layout.HorizontalAlignment = Enum.HorizontalAlignment.Center
layout.VerticalAlignment = Enum.VerticalAlignment.Center
local text1 = Instance.new("TextLabel", frame)
text1.Size = UDim2.new(1, -10, 0, 25)
text1.Text = "Mesonet: 0"
text1.TextColor3 = Color3.fromRGB(255, 255, 255)
text1.BackgroundTransparency = 1
local textDist = Instance.new("TextLabel", frame)
textDist.Size = UDim2.new(1, -10, 0, 25)
textDist.Text = "Distância: 0"
textDist.TextColor3 = Color3.fromRGB(255, 255, 255)
textDist.BackgroundTransparency = 1
local text2 = Instance.new("TextLabel", frame)
text2.Size = UDim2.new(1, -10, 0, 25)
text2.Text = "EF: EF0"
text2.TextColor3 = Color3.fromRGB(255, 255, 255)
text2.BackgroundTransparency = 1
local text3 = Instance.new("TextLabel", frame)
text3.Size = UDim2.new(1, -10, 0, 25)
text3.Text = "Windspeed: 0"
text3.TextColor3 = Color3.fromRGB(255, 255, 255)
text3.BackgroundTransparency = 1
HudFrame = sg
HudFrame.Visible = Settings.ShowHUD
end
-- 🌫️ Controle do fog (agora usando raio do funil)
local function UpdateFogEffect()
local char = Player.Character
local root = char and char:FindFirstChild("HumanoidRootPart")
if not root then
if isNearTornado then
Lighting.FogStart = originalFogStart
Lighting.FogEnd = originalFogEnd
Lighting.FogColor = originalFogColor
isNearTornado = false
end
return
end
local newNear = false
for _, tornado in ipairs(ActiveTornados) do
if tornado.Active and tornado.state ~= "dead" then
local effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
-- Fog só ativa dentro do funil (0.4 do top width)
local fogRadius = effectiveTopWidth * 0.4
local dist = (tornado.Core.Position - root.Position).Magnitude
if dist <= fogRadius then
newNear = true
break
end
end
end
if newNear and not isNearTornado then
Lighting.FogStart = 40
Lighting.FogEnd = 100
Lighting.FogColor = Color3.fromRGB(50, 50, 50)
isNearTornado = true
elseif not newNear and isNearTornado then
if SupercellGridEnabled and SupercellFogActive then
Lighting.FogStart = 80
Lighting.FogEnd = 250
Lighting.FogColor = Color3.fromRGB(180, 190, 200)
else
Lighting.FogStart = originalFogStart
Lighting.FogEnd = originalFogEnd
Lighting.FogColor = originalFogColor
end
isNearTornado = false
end
end
local function UpdateSupercellFog()
if SupercellGridEnabled and SupercellFogActive and not isNearTornado then
Lighting.FogStart = 80
Lighting.FogEnd = 250
Lighting.FogColor = Color3.fromRGB(180, 190, 200)
elseif not SupercellGridEnabled and not isNearTornado then
Lighting.FogStart = originalFogStart
Lighting.FogEnd = originalFogEnd
Lighting.FogColor = originalFogColor
end
end
-- =================[ REDIRECIONAR ]================
function RedirectTornadosToPlayer()
local char = Player.Character
local root = char and char:FindFirstChild("HumanoidRootPart")
if not root then
WindUI:Notify({Title = "Erro", Content = "Personagem não encontrado!", Duration = 3, Icon = "alert"})
return
end
local playerPos = root.Position
local count = 0
for _, tornado in ipairs(ActiveTornados) do
if tornado.Active and tornado.state ~= "dead" and tornado.state ~= "outro" and tornado.state ~= "merging" then
if tornado.IsTwin then
tornado.IsTwin = false
if tornado.TwinPartner then
tornado.TwinPartner.IsTwin = false
tornado.TwinPartner.TwinPartner = nil
tornado.TwinPartner.WanderSpeed = Settings.WanderSpeed
end
tornado.TwinPartner = nil
tornado.WanderSpeed = Settings.WanderSpeed
tornado.TwinCommonCenter = nil
end
local toPlayer = playerPos - tornado.Core.Position
if toPlayer.Magnitude > 0.1 then
tornado.targetDirection = toPlayer.Unit
tornado.moveDirection = toPlayer.Unit
tornado.lastOcclusionChange = tick()
count = count + 1
end
end
end
if count > 0 then
WindUI:Notify({Title = "Redirecionando", Content = string.format("%d tornados estão perseguindo você!", count), Duration = 3, Icon = "target"})
else
WindUI:Notify({Title = "Redirecionar", Content = "Nenhum tornado ativo para redirecionar.", Duration = 3, Icon = "info"})
end
end
function RedirectTornadosToSpawn()
local spawnPos = Vector3.new(Settings.SpawnPosX, 5, Settings.SpawnPosZ)
local count = 0
for _, tornado in ipairs(ActiveTornados) do
if tornado.Active and tornado.state ~= "dead" and tornado.state ~= "outro" and tornado.state ~= "merging" then
if tornado.IsTwin then
tornado.IsTwin = false
if tornado.TwinPartner then
tornado.TwinPartner.IsTwin = false
tornado.TwinPartner.TwinPartner = nil
tornado.TwinPartner.WanderSpeed = Settings.WanderSpeed
end
tornado.TwinPartner = nil
tornado.WanderSpeed = Settings.WanderSpeed
tornado.TwinCommonCenter = nil
end
local toSpawn = spawnPos - tornado.Core.Position
if toSpawn.Magnitude > 0.1 then
tornado.targetDirection = toSpawn.Unit
tornado.moveDirection = toSpawn.Unit
tornado.lastOcclusionChange = tick()
count = count + 1
end
end
end
if count > 0 then
WindUI:Notify({Title = "Redirecionando", Content = string.format("%d tornados estão indo para o spawn!", count), Duration = 3})
else
WindUI:Notify({Title = "Redirecionar", Content = "Nenhum tornado ativo para redirecionar.", Duration = 3})
end
end
-- =================[ SUPER CÉLULA ]================
function CreateSupercellGrid()
RemoveSupercellGrid()
local gridSize = 128
local blockSize = 300
local height = 660
local startX = -gridSize/2 * blockSize
local startZ = -gridSize/2 * blockSize
for x = 0, gridSize-1 do
for z = 0, gridSize-1 do
local block = Instance.new("Part")
block.Name = "SupercellBlock"
block.Anchored = true
block.CanCollide = false
block.Size = Vector3.new(blockSize, 20, blockSize)
block.Position = Vector3.new(startX + x*blockSize + blockSize/2, height, startZ + z*blockSize + blockSize/2)
block.Color = Color3.fromRGB(128, 128, 128)
block.Material = Enum.Material.SmoothPlastic
block.Transparency = 0.0
block.Parent = Workspace
table.insert(SupercellBlocks, block)
end
end
end
function RemoveSupercellGrid()
for _, block in ipairs(SupercellBlocks) do
if block then block:Destroy() end
end
table.clear(SupercellBlocks)
end
-- =================[ FUNÇÕES DE PRÉ-VISUALIZAÇÃO ]================
local function CreatePreview()
DestroyPreview()
if not Settings.ShowPreview then return end
-- Modelo do preview
PreviewModel = Instance.new("Model", Workspace)
PreviewModel.Name = "TornadoPreview"
local totalHeight = Settings.TornadoHeight
local baseWidth = Settings.BaseWidth * (Settings.WidthMultiplier or 1.0)
local targetFullTopWidth = baseWidth
if Settings.TornadoType == "Dust Devil" then
totalHeight = totalHeight * 0.4
baseWidth = math.min(baseWidth, 8)
elseif Settings.TornadoType == "Rope" then
baseWidth = math.min(baseWidth, 12)
end
local numSegments = math.floor(totalHeight / 5)
if numSegments < 10 then numSegments = 10 end
local shapeFunc = SHAPE_FUNCTIONS[Settings.TornadoType] or SHAPE_FUNCTIONS["Cone"]
local pos = Vector3.new(Settings.SpawnPosX, 5, Settings.SpawnPosZ)
for i = 1, numSegments do
local ratio = i / numSegments
local h = ratio * totalHeight
local shapeMult = shapeFunc(ratio)
local linearWidth = baseWidth * shapeMult
local finalWidth = math.min(linearWidth, MAX_SEGMENT_WIDTH)
-- Parte interna (verde transparente)
local inner = Instance.new("Part")
inner.Name = "PreviewInner"
inner.Shape = Enum.PartType.Cylinder
inner.Anchored = true
inner.CanCollide = false
inner.CanTouch = false
inner.CanQuery = false
inner.Material = Enum.Material.SmoothPlastic
inner.Size = Vector3.new(5, finalWidth, finalWidth)
inner.Color = Color3.fromRGB(0, 255, 0)
inner.Transparency = 0.6
inner.Position = pos + Vector3.new(0, h, 0)
inner.CFrame = inner.CFrame * CFrame.Angles(0, 0, math.rad(90))
inner.Parent = PreviewModel
-- Outline (branco, ligeiramente maior)
local outline = Instance.new("Part")
outline.Name = "PreviewOutline"
outline.Shape = Enum.PartType.Cylinder
outline.Anchored = true
outline.CanCollide = false
outline.CanTouch = false
outline.CanQuery = false
outline.Material = Enum.Material.SmoothPlastic
local outlineWidth = finalWidth + 0.4
outline.Size = Vector3.new(5, outlineWidth, outlineWidth)
outline.Color = Color3.fromRGB(255, 255, 255)
outline.Transparency = 0.3
outline.Position = pos + Vector3.new(0, h, 0)
outline.CFrame = outline.CFrame * CFrame.Angles(0, 0, math.rad(90))
outline.Parent = PreviewModel
table.insert(PreviewSegments, {Inner = inner, Outline = outline})
end
-- Billboard com informações
local sg = Instance.new("ScreenGui")
sg.Name = "PreviewInfoGUI"
sg.Parent = game:GetService("CoreGui")
local mainFrame = Instance.new("Frame", sg)
mainFrame.Size = UDim2.new(0, 260, 0, 300)
mainFrame.Position = UDim2.new(1, -280, 0.5, -150)
mainFrame.BackgroundColor3 = Color3.fromRGB(20, 20, 30)
mainFrame.BackgroundTransparency = 0.2
mainFrame.BorderSizePixel = 0
local scroll = Instance.new("ScrollingFrame", mainFrame)
scroll.Size = UDim2.new(1, -10, 1, -10)
scroll.Position = UDim2.new(0, 5, 0, 5)
scroll.BackgroundTransparency = 1
scroll.ScrollBarThickness = 6
scroll.CanvasSize = UDim2.new(0, 0, 0, 0)
local layout = Instance.new("UIListLayout", scroll)
layout.Padding = UDim.new(0, 6)
layout.SortOrder = Enum.SortOrder.LayoutOrder
local function AddLabel(text, color)
local lbl = Instance.new("TextLabel", scroll)
lbl.Size = UDim2.new(1, 0, 0, 28)
lbl.BackgroundTransparency = 1
lbl.Text = text
lbl.TextColor3 = color or Color3.fromRGB(255,255,255)
lbl.TextSize = 16
lbl.TextXAlignment = Enum.TextXAlignment.Left
lbl.Font = Enum.Font.GothamBold
lbl.TextScaled = false
return lbl
end
-- Preencher informações
local info = {
{text = "📋 Tornado Preview", color = Color3.fromRGB(200, 200, 255)},
{text = string.format("EF: %d", Settings.EF_Level), color = Color3.fromRGB(255, 200, 100)},
{text = string.format("Tipo: %s", Settings.TornadoType), color = Color3.fromRGB(150, 255, 150)},
{text = string.format("Mutação: %s", Settings.Mutation), color = Color3.fromRGB(255, 150, 255)},
{text = string.format("Windspeed: %.1f Mph/h", Settings.MaxWindSpeed), color = Color3.fromRGB(100, 200, 255)},
{text = string.format("Largura máxima: %.1f studs", Settings.BaseWidth * Settings.WidthMultiplier), color = Color3.fromRGB(255, 200, 150)},
{text = string.format("Altura: %d studs", Settings.TornadoHeight), color = Color3.fromRGB(200, 200, 200)},
{text = string.format("Specials: %s", (Settings.Special.MultiVortex and "Multi-Vortex " or "") .. (Settings.Special.Satellites and "Satellites" or "")), color = Color3.fromRGB(255, 255, 100)},
{text = string.format("Satélites: %d", Settings.SatelliteCount), color = Color3.fromRGB(100, 255, 200)},
}
for _, item in ipairs(info) do
AddLabel(item.text, item.color)
end
-- Ajustar canvas
local function updateCanvas()
local totalHeight = 0
for _, child in ipairs(scroll:GetChildren()) do
if child:IsA("TextLabel") then
totalHeight = totalHeight + child.Size.Y.Offset + layout.Padding.Offset
end
end
scroll.CanvasSize = UDim2.new(0, 0, 0, totalHeight)
end
task.spawn(function()
wait(0.1)
updateCanvas()
end)
PreviewInfoGUI = sg
end
local function DestroyPreview()
if PreviewModel then
PreviewModel:Destroy()
PreviewModel = nil
end
if PreviewInfoGUI then
PreviewInfoGUI:Destroy()
PreviewInfoGUI = nil
end
PreviewSegments = {}
end
-- =================[ Loop principal ]================--
local frameCount = 0
local lastDamageTime = {}
local lastWarningUpdate = 0
RunService.Heartbeat:Connect(function(dt)
CleanupPartData()
frameCount = frameCount + 1
local char = Player.Character
local root = char and char:FindFirstChild("HumanoidRootPart")
local humanoid = char and char:FindFirstChild("Humanoid")
local camera = workspace.CurrentCamera
if not HudFrame then CreateHUD() end
if tick() - lastWarningUpdate > 2 then
UpdateWarnings()
lastWarningUpdate = tick()
end
UpdateSupercellFog()
-- Atualização de gêmeos
for _, tornado in ipairs(ActiveTornados) do
if tornado.IsTwin and tornado.Active then
tornado.TwinAngle = tornado.TwinAngle + tornado.TwinOrbitSpeed * dt
local cx, cz = tornado.TwinCommonCenter.X, tornado.TwinCommonCenter.Z
local nx = cx + math.cos(tornado.TwinAngle) * tornado.TwinOrbitRadius
local nz = cz + math.sin(tornado.TwinAngle) * tornado.TwinOrbitRadius
tornado.Core.Position = Vector3.new(nx, tornado.Core.Position.Y, nz)
tornado.moveDirection = Vector3.new(-math.sin(tornado.TwinAngle), 0, math.cos(tornado.TwinAngle)).Unit
tornado.targetDirection = nil
end
end
-- Fusão de tornados (IGNORA SATÉLITES)
for i = 1, #ActiveTornados do
local t1 = ActiveTornados[i]
if not t1.Active or t1.IsTwin or t1.state == "merging" or t1.state == "dead" or t1.IsSatellite then continue end
for j = i+1, #ActiveTornados do
local t2 = ActiveTornados[j]
if not t2.Active or t2.IsTwin or t2.state == "merging" or t2.state == "dead" or t2.IsSatellite then continue end
local dist = (t1.Core.Position - t2.Core.Position).Magnitude
local threshold = (t1.targetFullTopWidth * t1.currentScale + t2.targetFullTopWidth * t2.currentScale) * 0.8
if dist < threshold then
t1.state = "merging"
t2.state = "merging"
t1.fadeStartTime = tick()
t2.fadeStartTime = tick()
t1.fadeDuration = 0.4
t2.fadeDuration = 0.4
delay(0.4, function()
SpawnTornado(t1, t2)
end)
end
end
end
local nearestTornado = nil
local minDist = math.huge
for _, tornado in ipairs(ActiveTornados) do
if not tornado.Active then continue end
local now = tick()
local distToPlayer = root and (tornado.Core.Position - root.Position).Magnitude or math.huge
if distToPlayer < minDist then
minDist = distToPlayer
nearestTornado = tornado
end
-- Mutant: persegue jogador (exceto satélites)
if Settings.Mutation == "Mutant" and not tornado.IsTwin and not tornado.IsSatellite then
local nearestRoot = nil
local minDistPlr = math.huge
for _, plr in ipairs(Players:GetPlayers()) do
if plr.Character and plr.Character:FindFirstChild("HumanoidRootPart") then
local r = plr.Character.HumanoidRootPart
local d = (r.Position - tornado.Core.Position).Magnitude
if d < minDistPlr then
minDistPlr = d
nearestRoot = r
end
end
end
if nearestRoot then
local toPlayer = (nearestRoot.Position - tornado.Core.Position) * Vector3.new(1,0,1)
if toPlayer.Magnitude > 0.1 then
tornado.targetDirection = toPlayer.Unit
end
end
end
-- Estados do tornado
if tornado.state == "intro" then
local elapsed = now - tornado.introStartTime
local progress = math.clamp(elapsed / tornado.introDuration, 0, 1)
tornado.currentScale = tornado.initialScale + (1 - tornado.initialScale) * progress
UpdateFunnelSizes(tornado)
for _, seg in ipairs(tornado.Segments) do
seg.Part.Transparency = progress < (1 - seg.ratio) and 1 or 0
end
if progress >= 1 then
tornado.state = "stable"
tornado.stableStartTime = now
tornado.outroTriggerTime = now + tornado.lifetime
end
elseif tornado.state == "outro" then
local elapsed = now - tornado.outroStartTime
local progress = math.clamp(elapsed / tornado.outroDuration, 0, 1)
for _, seg in ipairs(tornado.Segments) do
seg.Part.Transparency = progress >= seg.ratio and 1 or 0
end
if progress >= 1 then
tornado.state = "dead"
end
elseif tornado.state == "stable" then
UpdateFunnelSizes(tornado)
if now >= tornado.outroTriggerTime then
tornado.state = "outro"
tornado.outroStartTime = now
end
elseif tornado.state == "merging" then
local elapsed = now - tornado.fadeStartTime
local progress = math.clamp(elapsed / 0.4, 0, 1)
for _, seg in ipairs(tornado.Segments) do
seg.Part.Transparency = progress
end
if progress >= 1 then
tornado.state = "dead"
end
elseif tornado.state == "dead" then
DestroyTornado(tornado)
continue
end
-- Movimento (direção) para tornados não gêmeos
if tornado.state ~= "dead" and tornado.state ~= "outro" and tornado.state ~= "merging" and not tornado.IsTwin then
-- Se Multi-Vortex ativo e estável, comportamento errático (muda direção mais rápido)
if Settings.Special.MultiVortex and tornado.state == "stable" and not tornado.IsSatellite then
tornado.occlusionInterval = math.random(10, 25) -- mais errático
-- Força extra (multiplica MutationStrengthMult por 1.5)
tornado.MutationStrengthMult = 50.0 * 1.5
else
-- Restaura força base se não estiver em Multi-Vortex
if not tornado.IsSatellite then
tornado.MutationStrengthMult = 50.0 -- base
end
end
if Settings.Mutation ~= "Mutant" then
if not tornado.targetDirection or (now - tornado.lastOcclusionChange) > tornado.occlusionInterval then
local currentDir = tornado.moveDirection
local maxAngleRad = math.rad(Settings.OcclusionAngle)
local deviation = (math.random() * 2 - 1) * maxAngleRad
local newDir = CFrame.Angles(0, deviation, 0) * currentDir
tornado.targetDirection = newDir.Unit
tornado.lastOcclusionChange = now
tornado.occlusionInterval = math.random(20, 60)
end
end
local angleDiff = math.acos(math.clamp(tornado.moveDirection:Dot(tornado.targetDirection), -1, 1))
if angleDiff > 0.01 then
local rotAxis = tornado.moveDirection:Cross(tornado.targetDirection).Unit
if rotAxis.Magnitude > 0 then
local maxAngle = tornado.directionChangeSpeed * dt
local angle = math.min(angleDiff, maxAngle)
tornado.moveDirection = CFrame.fromAxisAngle(rotAxis, angle) * tornado.moveDirection
else
tornado.moveDirection = tornado.targetDirection
end
end
local newPos = tornado.Core.Position + tornado.moveDirection * tornado.WanderSpeed * dt
tornado.Core.Position = newPos
end
-- ===== ATUALIZAÇÃO DE INNER VORTICES (MULTI-VORTEX) =====
if Settings.Special.MultiVortex and not tornado.IsSatellite and tornado.state == "stable" then
if not tornado.InnerVortices then
CreateInnerVortices(tornado)
else
local center = tornado.Core.Position
local radius = tornado.targetFullTopWidth * tornado.currentScale * 0.15
for _, vortex in ipairs(tornado.InnerVortices) do
vortex.Angle = vortex.Angle + vortex.OrbitSpeed * dt
local rad = math.rad(vortex.Angle)
local offset = Vector3.new(math.cos(rad), 0, math.sin(rad)) * radius
for _, seg in ipairs(vortex.Segments) do
local pos = center + offset + Vector3.new(0, seg.Height, 0)
seg.Part.Position = pos
-- Segmento deitado: manter Size, mas aplicar rotação para ficar horizontal
seg.Part.CFrame = CFrame.new(pos) * CFrame.Angles(math.rad(90), 0, 0)
end
end
end
elseif tornado.InnerVortices and (not Settings.Special.MultiVortex or tornado.state ~= "stable") then
-- Limpa InnerVortices se a opção for desativada ou tornado não está estável
for _, v in ipairs(tornado.InnerVortices) do
for _, seg in ipairs(v.Segments) do
if seg.Part then seg.Part:Destroy() end
end
end
tornado.InnerVortices = nil
end
-- ===== ATUALIZAÇÃO DE SATÉLITES (VÓRTICES INTERNOS ORBITANDO) =====
if tornado.SatelliteVortices and tornado.state == "stable" and not tornado.IsTwin then
local center = tornado.Core.Position
for _, sat in ipairs(tornado.SatelliteVortices) do
sat.Angle = sat.Angle + sat.OrbitSpeed * dt
local rad = math.rad(sat.Angle)
local radius = sat.OrbitRadius * tornado.currentScale
local offset = Vector3.new(math.cos(rad), 0, math.sin(rad)) * radius
for _, seg in ipairs(sat.Segments) do
local pos = center + offset + Vector3.new(0, seg.Height, 0)
seg.Part.Position = pos
seg.Part.CFrame = CFrame.new(pos) * CFrame.Angles(math.rad(90), 0, 0)
end
end
end
-- ===== ATUALIZAÇÃO DAS PARTÍCULAS DE FUMAÇA =====
UpdateSmokeParticles(tornado, dt)
-- ===== TRANSPARÊNCIA DINÂMICA DOS SEGMENTOS =====
if root and tornado.state == "stable" then
local playerPos = root.Position
local center = tornado.Core.Position
local topWidth = tornado.targetFullTopWidth * tornado.currentScale
local innerRadius = topWidth * 0.2 -- raio para transparência
for _, seg in ipairs(tornado.Segments) do
local segPos = seg.Part.Position
local distToPlayer = (segPos - playerPos).Magnitude
local baseTrans = Settings.FunnelTrans
if distToPlayer < innerRadius then
local factor = 1 - (distToPlayer / innerRadius)
local newTrans = math.min(1, baseTrans + factor * 0.5) -- fica mais transparente
seg.Part.Transparency = newTrans
else
seg.Part.Transparency = baseTrans
end
end
end
-- Trail e Windfield
local t = tick()
local rootPos = tornado.Core.Position
if Settings.ShowTrail and tornado.state ~= "dead" and tornado.state ~= "outro" and tornado.state ~= "merging" then
if frameCount % 2 == 0 then
CreateTrailPart(tornado)
end
end
if Settings.ShowWindfield and tornado.state ~= "dead" and tornado.state ~= "outro" and tornado.state ~= "merging" then
if frameCount % 4 == 0 then
CreateWindfieldPart(tornado)
end
end
UpdateMesonetVisuals(tornado)
-- Atualiza posição dos segmentos do funil principal
for _, seg in ipairs(tornado.Segments) do
local h = seg.Height
local rot = CFrame.Angles(0, t * seg.Speed * Settings.RotationSpeed * 0.7, 0)
local wobble = Vector3.new(math.sin(t*3 + h)*3, 0, math.cos(t*3 + h)*3)
seg.Part.CFrame = CFrame.new(rootPos + Vector3.new(0, h, 0) + wobble) * rot * CFrame.Angles(0, 0, math.rad(90))
end
-- Física e puxão (sem alterações)
if tornado.state ~= "dead" and tornado.state ~= "outro" and tornado.state ~= "merging" then
local totalHeight = Settings.TornadoHeight
local effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
local searchRadius = effectiveTopWidth * 2.5 * Settings.RadiusMult
local probeCount = GetNearbyProbeCount(tornado)
local baseStrength = tornado.MaxWindSpeed * tornado.MutationStrengthMult
local strength = baseStrength * (1 + probeCount * 0.2)
-- Dano ao jogador
if root and humanoid then
local dist = (root.Position - rootPos).Magnitude
local damageRadius = effectiveTopWidth * 1.5
if dist <= damageRadius then
local last = lastDamageTime[tornado] or 0
if tick() - last >= 2 then
humanoid:TakeDamage(1)
lastDamageTime[tornado] = tick()
if humanoid.Health > 0 then
local bp = Instance.new("BillboardGui", root)
bp.Adornee = root
bp.Size = UDim2.new(0, 50, 0, 30)
bp.StudsOffset = Vector3.new(0, 3, 0)
local label = Instance.new("TextLabel", bp)
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.TextColor3 = Color3.fromRGB(255, 50, 50)
label.Text = "-1 HP"
label.TextScaled = true
label.Font = Enum.Font.Bold
Debris:AddItem(bp, 1)
end
end
end
end
-- Puxar jogador com stun
if root and humanoid and tornado.state ~= "dead" and tornado.state ~= "outro" then
local distToPlayer = (rootPos - root.Position).Magnitude
local funnelRadius = effectiveTopWidth * 0.4
local innerRadius = effectiveTopWidth * 0.8
local outerRadius = effectiveTopWidth * 2.5
if distToPlayer <= outerRadius then
local strength_pull = 1 - (distToPlayer / outerRadius)
strength_pull = math.clamp(strength_pull, 0, 1)
if humanoid.PlatformStand == false then
humanoid.PlatformStand = true
end
if distToPlayer <= funnelRadius then
local upwardForce = Vector3.new(0, 50 * strength_pull + 20, 0)
root.AssemblyLinearVelocity = root.AssemblyLinearVelocity + upwardForce * dt * 20
local toCenter = (rootPos - root.Position) * Vector3.new(1, 0, 1)
if toCenter.Magnitude > 0.5 then
local tangent = Vector3.new(-toCenter.Z, 0, toCenter.X).Unit
local rotationSpeed = 30 + (1 - strength_pull) * 20
root.AssemblyLinearVelocity = root.AssemblyLinearVelocity + tangent * rotationSpeed * dt * 5
end
elseif distToPlayer <= innerRadius then
local upwardForce = Vector3.new(0, 15 * strength_pull + 5, 0)
root.AssemblyLinearVelocity = root.AssemblyLinearVelocity + upwardForce * dt * 10
local toCenter = (rootPos - root.Position) * Vector3.new(1, 0, 1)
if toCenter.Magnitude > 0.5 then
local tangent = Vector3.new(-toCenter.Z, 0, toCenter.X).Unit
local rotationSpeed = 15 + strength_pull * 15
root.AssemblyLinearVelocity = root.AssemblyLinearVelocity + tangent * rotationSpeed * dt * 3
end
end
else
if humanoid.PlatformStand == true then
humanoid.PlatformStand = false
end
end
end
-- Física de partes (mesmo código original)
local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {tornado.Model, Player.Character}
local partsInRadius = Workspace:GetPartBoundsInRadius(rootPos, searchRadius, params)
for _, p in ipairs(partsInRadius) do
if p:IsA("BasePart") then
local offset = p.Position - rootPos
local currentHeight = p.Position.Y - rootPos.Y
local horizontalDist = Vector3.new(offset.X, 0, offset.Z).Magnitude
local forceMult = strength * Settings.PullStrengthMult
local liftMult = Settings.LiftSpeed * (1 + probeCount * 0.15)
local efLevel = tornado.EF_Level
if Settings.PhysicsMode == "Original" or Settings.PhysicsMode == "Better" then
if not p.Anchored then
local canLift = false
local mass = p:GetMass()
if efLevel <= 1 then
canLift = mass < 50
elseif efLevel <= 2 then
canLift = mass < 100
elseif efLevel <= 4 then
canLift = mass < 1000
else
canLift = true
end
if canLift then
local tangent = Vector3.new(-offset.Z, 0, offset.X).Unit
local inward = -Vector3.new(offset.X, 0, offset.Z).Unit
local upward = Vector3.new(0, 1.8 * liftMult, 0)
if Settings.PhysicsMode == "Better" then
if currentHeight > totalHeight * 0.3 and math.random(1, 150) == 1 then
local throwDir = Vector3.new(math.random(-100, 100), math.random(-20, 50), math.random(-100, 100)).Unit
p.AssemblyLinearVelocity = throwDir * (forceMult * math.random(1.5, 3))
else
local distPercent = math.clamp(horizontalDist / searchRadius, 0, 1)
local liftPower = 1 - distPercent
if liftPower < 0.1 then liftPower = 0.1 end
local suckForce = inward * (forceMult * (1.5 - distPercent))
p.AssemblyLinearVelocity = (tangent * forceMult) + suckForce + (upward * forceMult)
end
else
if currentHeight > totalHeight * 0.95 then
p.AssemblyLinearVelocity = Vector3.new(math.random(-100, 100), 50, math.random(-100, 100)).Unit * (forceMult * 1.5)
else
p.AssemblyLinearVelocity = (tangent * forceMult) + (inward * (forceMult * 0.4)) + (upward * forceMult)
end
end
end
end
elseif Settings.PhysicsMode == "Realistic" then
if not p.Anchored then
InitializePartHealth(p)
if not tornado.PendingSuck[p] then
local health = PartHealth[p] or 1
local maxHealth = PartHealthMax[p] or 1
local healthRatio = health / maxHealth
local baseDelay = math.max(0.3, 5 - efLevel * 0.3)
local delayTime = baseDelay * (0.5 + (1 - healthRatio) * 0.5)
tornado.PendingSuck[p] = {firstSeen = now, delay = delayTime}
else
local data = tornado.PendingSuck[p]
if now - data.firstSeen >= data.delay then
if CanLiftPartByEF(p, efLevel) then
local distPercent = math.clamp(horizontalDist / searchRadius, 0, 1)
local liftPower = 1 - distPercent
if liftPower < 0.1 then liftPower = 0.1 end
local tangent = Vector3.new(-offset.Z, 0, offset.X).Unit
local inward = -Vector3.new(offset.X, 0, offset.Z).Unit
local upward = Vector3.new(0, 3 * liftMult * liftPower * (1 + (1 - PartHealth[p]/PartHealthMax[p]) * 0.5), 0)
local suckForce = inward * (forceMult * (1.5 - distPercent))
p.AssemblyLinearVelocity = (tangent * forceMult) + suckForce + (upward * forceMult)
if math.random(1, 50) == 1 then
local damage = math.random(1, 2)
if DamagePart(p, damage) then
local explosion = Instance.new("Explosion", workspace)
explosion.Position = p.Position
explosion.BlastRadius = 2
explosion.BlastPressure = 0
explosion.ExplosionType = Enum.ExplosionType.NoCraters
Debris:AddItem(explosion, 0.1)
end
end
end
tornado.PendingSuck[p] = nil
end
end
else
if not tornado.ProcessedAnchored[p] then
local size = p.Size.Magnitude
if IsSizeInEFRange(size, efLevel) then
local unanchorDuration = math.random(3, 15)
tornado.ProcessedAnchored[p] = {time = now, duration = unanchorDuration}
p.Anchored = false
local impulseDir = Vector3.new(math.random(-10,10), math.random(5,20), math.random(-10,10)).Unit
p.AssemblyLinearVelocity = impulseDir * 10
end
else
local data = tornado.ProcessedAnchored[p]
if now - data.time >= data.duration then
p.Anchored = true
tornado.ProcessedAnchored[p] = nil
else
if math.random(1, 30) == 1 then
if DamagePart(p, 1) then
p:Destroy()
tornado.ProcessedAnchored[p] = nil
end
end
end
end
end
end
end
end
if root and humanoid and humanoid.SeatPart then
local seat = humanoid.SeatPart
local vehicleModel = seat.Parent
if vehicleModel and vehicleModel:IsA("Model") then
local primaryPart = vehicleModel.PrimaryPart or seat
local vehicleDist = (primaryPart.Position - rootPos).Magnitude
if vehicleDist <= searchRadius * 1.5 then
primaryPart.AssemblyLinearVelocity = primaryPart.AssemblyLinearVelocity + Vector3.new(0, liftMult * baseStrength * 0.5, 0)
end
end
end
end
-- Atualiza o parágrafo de aviso do tornado
if tornado.WarningParagraph then
local currentWidth = tornado.targetFullTopWidth * tornado.currentScale
local maxWidth = tornado.targetFullTopWidth
local windspeed = tornado.MaxWindSpeed
local dateStr = os.date("%d/%m/%Y %H:%M:%S", tornado.SpawnTime)
local distanceStr = "N/A"
if root then
local dist = (tornado.Core.Position - root.Position).Magnitude
distanceStr = string.format("%.1f studs", dist)
end
local dirAngle = math.deg(math.atan2(tornado.moveDirection.Z, tornado.moveDirection.X))
local dirStr = string.format("%.1f°", dirAngle)
local coordsStr = string.format("(%.1f, %.1f)", tornado.Core.Position.X, tornado.Core.Position.Z)
local desc = string.format(
"Dia: %s\nEF: %d\nWidth Atual: %.1f studs\nMax Width: %.1f studs\nWindspeed: %.1f\nDistância: %s\nDireção: %s\nCoordenadas: %s",
dateStr, tornado.EF_Level, currentWidth, maxWidth, windspeed, distanceStr, dirStr, coordsStr
)
tornado.WarningParagraph:SetDesc(desc)
end
-- Notificação de perigo (mantém raio de aviso)
if root and tornado.Active and tornado.state ~= "dead" then
local effectiveTopWidth = tornado.targetFullTopWidth * tornado.currentScale
local dangerRadius = effectiveTopWidth * 6
local dist = (tornado.Core.Position - root.Position).Magnitude
if dist <= dangerRadius then
if not tornado.PlayerInDanger then
tornado.PlayerInDanger = true
WindUI:Notify({
Title = "⚠️ Perigo de Tornado!",
Content = string.format("Você entrou na zona de perigo de um tornado EF%d! Distância: %.0f studs.", tornado.EF_Level, dist),
Duration = 5,
Icon = "alert"
})
end
else
tornado.PlayerInDanger = false
if humanoid and humanoid.PlatformStand then
humanoid.PlatformStand = false
end
end
end
end
UpdateFogEffect()
-- HUD
local maxDisplayDist = 200
if nearestTornado then
maxDisplayDist = math.max(200, nearestTornado.targetFullTopWidth * nearestTornado.currentScale * 3)
end
if Settings.ShowHUD and HudFrame then
if nearestTornado and minDist <= maxDisplayDist then
HudFrame.Visible = true
local textLabels = HudFrame.Frame:GetChildren()
local mesonetText, distText, efText, windText
for _, child in ipairs(textLabels) do
if child:IsA("TextLabel") then
if child.Text:find("Mesonet") then mesonetText = child
elseif child.Text:find("Distância") then distText = child
elseif child.Text:find("EF") then efText = child
elseif child.Text:find("Windspeed") then windText = child
end
end
end
if nearestTornado then
local probeCount = GetNearbyProbeCount(nearestTornado)
if mesonetText then mesonetText.Text = "Mesonet: " .. probeCount end
if distText then distText.Text = "Distância: " .. math.floor(minDist) end
if efText then efText.Text = "EF: EF" .. nearestTornado.EF_Level end
if windText then
local windSpeed = math.floor(nearestTornado.MaxWindSpeed * 10) / 10
windText.Text = "Windspeed: " .. windSpeed
end
end
else
HudFrame.Visible = false
end
else
if HudFrame then HudFrame.Visible = false end
end
-- Shake da câmera
if root and camera then
local shakeMagnitude = 0
for _, tornado in ipairs(ActiveTornados) do
if tornado.Active and tornado.state ~= "dead" and tornado.state ~= "outro" then
local dist = (root.Position - tornado.Core.Position).Magnitude
local maxDist = tornado.targetFullTopWidth * tornado.currentScale * 3
if dist < maxDist then
shakeMagnitude = math.max(shakeMagnitude, 1 - (dist / maxDist))
end
end
end
if shakeMagnitude > 0 then
local shakeIntensity = shakeMagnitude * 1.0
local maxRoll = 60
local maxPitch = 30
local maxYaw = 15
local roll = math.rad(math.random(-maxRoll, maxRoll)) * shakeIntensity
local pitch = math.rad(math.random(-maxPitch, maxPitch)) * shakeIntensity
local yaw = math.rad(math.random(-maxYaw, maxYaw)) * shakeIntensity
local yOffset = math.random(-7, 7) * shakeIntensity
local xOffset = math.random(-3, 3) * shakeIntensity
local zOffset = math.random(-3, 3) * shakeIntensity
local shakeCF = CFrame.Angles(roll, yaw, pitch) * CFrame.new(xOffset, yOffset, zOffset)
camera.CFrame = camera.CFrame * shakeCF
end
end
end)
WindUI:Notify({
Title = "Tornado-Geek Carregado!",
Content = "V1.0 – EF0-EF6 • Stun ao ser puxado • Hitbox aumentada • Special multi • Auto spawn 160-230s",
Duration = 9,
Icon = "orbit",
})
WindUI:SetTheme("Default")Loads code from the internet. It downloads more code when it runs (loadstring + HttpGet), which can change after it was posted. From: github.com, raw.githubusercontent.com.
(AI slop, srry) 🇺🇸: Basically an Imagination free tornado script (it's in Portuguese) If some stuff is inaccurate, Tell me in the comments, Ill fix next upd. (Credits to VoidX5 for template "TornadoV6") (Blank thumbnail for Now)

Universal