local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local UserInputService = game:GetService("UserInputService") local TweenService = game:GetService("TweenService") local player = Players.LocalPlayer if not player then warn("TowerHub must run as a LocalScript.") return end local playerGui = player:WaitForChild("PlayerGui", 15) if not playerGui then warn("TowerHub: PlayerGui was not found.") return end print("TowerHub: script started") local oldGui = playerGui:FindFirstChild("TowerHub") if oldGui then oldGui:Destroy() end --================================================== -- FIND UNIT / TOWER FOLDERS --================================================== local folderNames = { tower = true, towers = true, unit = true, units = true, troop = true, troops = true, character = true, characters = true, npc = true, npcs = true, unitsfolder = true, towerfolder = true, } local function isUnitTowerFolder(object) return object:IsA("Folder") and folderNames[object.Name:lower()] == true end local function getAllUnitsAndTowers() local objects = {} local seen = {} for _, folder in ipairs(ReplicatedStorage:GetDescendants()) do if isUnitTowerFolder(folder) then for _, object in ipairs(folder:GetChildren()) do if not seen[object] then seen[object] = true table.insert(objects, object) end end end end table.sort(objects, function(a, b) return a.Name:lower() < b.Name:lower() end) return objects end --================================================== -- GUI --================================================== local gui = Instance.new("ScreenGui") gui.Name = "TowerHub" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.DisplayOrder = 999 gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.Parent = playerGui local main = Instance.new("Frame") main.Name = "Main" main.AnchorPoint = Vector2.new(0.5, 0.5) main.Position = UDim2.fromScale(0.5, 0.5) main.Size = UDim2.fromOffset(560, 400) main.BackgroundColor3 = Color3.fromRGB(17, 18, 24) main.BorderSizePixel = 0 main.Visible = true main.Parent = gui local mainCorner = Instance.new("UICorner") mainCorner.CornerRadius = UDim.new(0, 16) mainCorner.Parent = main local mainStroke = Instance.new("UIStroke") mainStroke.Color = Color3.fromRGB(62, 65, 80) mainStroke.Thickness = 1 mainStroke.Parent = main --================================================== -- RESPONSIVE SCALE --================================================== local uiScale = Instance.new("UIScale") uiScale.Scale = 1 uiScale.Parent = main local function updateScale() local camera = workspace.CurrentCamera if not camera then return end local viewport = camera.ViewportSize local width = viewport.X local height = viewport.Y local scaleX = (width - 20) / 560 local scaleY = (height - 20) / 400 local screenScale = math.min(scaleX, scaleY) if width <= 390 then uiScale.Scale = math.min(screenScale, 0.82) elseif width <= 600 then uiScale.Scale = math.min(screenScale, 0.90) else uiScale.Scale = math.min(screenScale, 1) end if uiScale.Scale <= 0 then uiScale.Scale = 0.5 end end local function connectCamera() local camera = workspace.CurrentCamera if not camera then return end camera:GetPropertyChangedSignal("ViewportSize"):Connect(updateScale) updateScale() end connectCamera() workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function() task.defer(connectCamera) end) --================================================== -- TOP BAR --================================================== local top = Instance.new("Frame") top.Name = "TopBar" top.Size = UDim2.new(1, 0, 0, 50) top.BackgroundColor3 = Color3.fromRGB(25, 27, 35) top.BorderSizePixel = 0 top.Parent = main local topCorner = Instance.new("UICorner") topCorner.CornerRadius = UDim.new(0, 16) topCorner.Parent = top local title = Instance.new("TextLabel") title.BackgroundTransparency = 1 title.Position = UDim2.fromOffset(14, 0) title.Size = UDim2.new(1, -65, 1, 0) title.Text = "universe spawner tower" title.TextColor3 = Color3.fromRGB(255, 255, 255) title.TextSize = 18 title.Font = Enum.Font.GothamBold title.TextXAlignment = Enum.TextXAlignment.Left title.TextTruncate = Enum.TextTruncate.AtEnd title.Parent = top local hideButton = Instance.new("TextButton") hideButton.Size = UDim2.fromOffset(38, 38) hideButton.Position = UDim2.new(1, -45, 0, 6) hideButton.BackgroundColor3 = Color3.fromRGB(47, 49, 61) hideButton.Text = "×" hideButton.TextColor3 = Color3.fromRGB(255, 255, 255) hideButton.TextSize = 23 hideButton.Font = Enum.Font.GothamBold hideButton.AutoButtonColor = false hideButton.Parent = top local hideCorner = Instance.new("UICorner") hideCorner.CornerRadius = UDim.new(0, 9) hideCorner.Parent = hideButton --================================================== -- LEFT PANEL --================================================== local left = Instance.new("Frame") left.Name = "UnitTowerPanel" left.Position = UDim2.fromOffset(8, 58) left.Size = UDim2.new(0.42, -8, 1, -66) left.BackgroundColor3 = Color3.fromRGB(24, 26, 34) left.BorderSizePixel = 0 left.Parent = main local leftCorner = Instance.new("UICorner") leftCorner.CornerRadius = UDim.new(0, 12) leftCorner.Parent = left local listTitle = Instance.new("TextLabel") listTitle.BackgroundTransparency = 1 listTitle.Position = UDim2.fromOffset(11, 7) listTitle.Size = UDim2.new(1, -22, 0, 28) listTitle.Text = "UNITS + TOWERS" listTitle.TextColor3 = Color3.fromRGB(225, 227, 235) listTitle.TextSize = 11 listTitle.Font = Enum.Font.GothamBold listTitle.TextXAlignment = Enum.TextXAlignment.Left listTitle.TextTruncate = Enum.TextTruncate.AtEnd listTitle.Parent = left local towerList = Instance.new("ScrollingFrame") towerList.Name = "UnitTowerList" towerList.Position = UDim2.fromOffset(7, 39) towerList.Size = UDim2.new(1, -14, 1, -46) towerList.BackgroundTransparency = 1 towerList.BorderSizePixel = 0 towerList.ScrollBarThickness = 5 towerList.ScrollBarImageColor3 = Color3.fromRGB(90, 94, 112) towerList.ScrollBarImageTransparency = 0.15 towerList.ScrollingDirection = Enum.ScrollingDirection.Y towerList.ScrollingEnabled = true towerList.ElasticBehavior = Enum.ElasticBehavior.Always towerList.AutomaticCanvasSize = Enum.AutomaticSize.Y towerList.CanvasSize = UDim2.new(0, 0, 0, 0) towerList.Parent = left local towerPadding = Instance.new("UIPadding") towerPadding.PaddingTop = UDim.new(0, 3) towerPadding.PaddingBottom = UDim.new(0, 10) towerPadding.PaddingLeft = UDim.new(0, 2) towerPadding.PaddingRight = UDim.new(0, 2) towerPadding.Parent = towerList local towerLayout = Instance.new("UIListLayout") towerLayout.Padding = UDim.new(0, 6) towerLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center towerLayout.SortOrder = Enum.SortOrder.LayoutOrder towerLayout.Parent = towerList --================================================== -- RIGHT PANEL --================================================== local right = Instance.new("Frame") right.Name = "StatsPanel" right.Position = UDim2.new(0.42, 4, 0, 58) right.Size = UDim2.new(0.58, -12, 1, -66) right.BackgroundColor3 = Color3.fromRGB(20, 21, 29) right.BorderSizePixel = 0 right.Parent = main local rightCorner = Instance.new("UICorner") rightCorner.CornerRadius = UDim.new(0, 12) rightCorner.Parent = right local selectedTitle = Instance.new("TextLabel") selectedTitle.BackgroundTransparency = 1 selectedTitle.Position = UDim2.fromOffset(13, 8) selectedTitle.Size = UDim2.new(1, -26, 0, 28) selectedTitle.Text = "SELECT A UNIT" selectedTitle.TextColor3 = Color3.fromRGB(255, 255, 255) selectedTitle.TextSize = 16 selectedTitle.Font = Enum.Font.GothamBold selectedTitle.TextXAlignment = Enum.TextXAlignment.Left selectedTitle.TextTruncate = Enum.TextTruncate.AtEnd selectedTitle.Parent = right local subtitle = Instance.new("TextLabel") subtitle.BackgroundTransparency = 1 subtitle.Position = UDim2.fromOffset(14, 34) subtitle.Size = UDim2.new(1, -28, 0, 16) subtitle.Text = "Select an existing unit or tower" subtitle.TextColor3 = Color3.fromRGB(125, 130, 145) subtitle.TextSize = 9 subtitle.Font = Enum.Font.Gotham subtitle.TextXAlignment = Enum.TextXAlignment.Left subtitle.TextTruncate = Enum.TextTruncate.AtEnd subtitle.Parent = right --================================================== -- STAT ALIASES --================================================== local aliases = { Damage = { "Damage", "AttackDamage", "Attack", "DMG" }, Range = { "Range", "AttackRange", "Radius" }, Cooldown = { "Cooldown", "AttackCooldown", "FireRate", "AttackSpeed" }, Price = { "Price", "Cost", "Cash" }, DPS = { "DPS", "DamagePerSecond", "DamagePerSec" }, } local function readValueObject(object) if object:IsA("NumberValue") or object:IsA("IntValue") or object:IsA("StringValue") or object:IsA("BoolValue") then return object.Value end return nil end local function findStatInObject(object, statName) if not object then return nil end local attribute = object:GetAttribute(statName) if attribute ~= nil then return attribute end local direct = object:FindFirstChild(statName) if direct then local value = readValueObject(direct) if value ~= nil then return value end end for _, alias in ipairs(aliases[statName] or {}) do local child = object:FindFirstChild(alias) if child then local value = readValueObject(child) if value ~= nil then return value end end local attr = object:GetAttribute(alias) if attr ~= nil then return attr end end for _, descendant in ipairs(object:GetDescendants()) do local attr = descendant:GetAttribute(statName) if attr ~= nil then return attr end local value = readValueObject(descendant) if value ~= nil then if descendant.Name:lower() == statName:lower() then return value end for _, alias in ipairs(aliases[statName] or {}) do if descendant.Name:lower() == alias:lower() then return value end end end end return nil end local function findStatInModule(moduleScript, statName) if not moduleScript or not moduleScript:IsA("ModuleScript") then return nil end local success, data = pcall(require, moduleScript) if not success or type(data) ~= "table" then return nil end if data[statName] ~= nil then return data[statName] end for _, alias in ipairs(aliases[statName] or {}) do if data[alias] ~= nil then return data[alias] end end for key, value in pairs(data) do if type(key) == "string" then if key:lower() == statName:lower() then return value end for _, alias in ipairs(aliases[statName] or {}) do if key:lower() == alias:lower() then return value end end end end return nil end local function findStat(unit, statName) local result = findStatInObject(unit, statName) if result ~= nil then return result end for _, configName in ipairs({ "Config", "Configuration", "Stats", "Data", "TowerData", "UnitData" }) do local config = unit:FindFirstChild(configName) if config then result = findStatInObject(config, statName) if result ~= nil then return result end end end for _, object in ipairs(unit:GetDescendants()) do if object:IsA("ModuleScript") then result = findStatInModule(object, statName) if result ~= nil then return result end end end return "N/A" end --================================================== -- STATS --================================================== local statsScroll = Instance.new("ScrollingFrame") statsScroll.Name = "StatsScroll" statsScroll.Position = UDim2.fromOffset(7, 56) statsScroll.Size = UDim2.new(1, -14, 1, -116) statsScroll.BackgroundTransparency = 1 statsScroll.BorderSizePixel = 0 statsScroll.ScrollBarThickness = 5 statsScroll.ScrollBarImageColor3 = Color3.fromRGB(85, 90, 110) statsScroll.ScrollBarImageTransparency = 0.15 statsScroll.ScrollingDirection = Enum.ScrollingDirection.Y statsScroll.ScrollingEnabled = true statsScroll.ElasticBehavior = Enum.ElasticBehavior.Always statsScroll.AutomaticCanvasSize = Enum.AutomaticSize.Y statsScroll.CanvasSize = UDim2.new(0, 0, 0, 0) statsScroll.Parent = right local statsPadding = Instance.new("UIPadding") statsPadding.PaddingTop = UDim.new(0, 3) statsPadding.PaddingBottom = UDim.new(0, 10) statsPadding.PaddingLeft = UDim.new(0, 4) statsPadding.PaddingRight = UDim.new(0, 4) statsPadding.Parent = statsScroll local statsLayout = Instance.new("UIListLayout") statsLayout.Padding = UDim.new(0, 7) statsLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center statsLayout.SortOrder = Enum.SortOrder.LayoutOrder statsLayout.Parent = statsScroll local statCards = {} local function createStatCard(name, icon, color) local card = Instance.new("Frame") card.Name = name .. "Card" card.Size = UDim2.new(1, 0, 0, 50) card.BackgroundColor3 = Color3.fromRGB(31, 33, 43) card.BorderSizePixel = 0 card.Parent = statsScroll local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 9) corner.Parent = card local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(45, 47, 59) stroke.Thickness = 1 stroke.Parent = card local iconLabel = Instance.new("TextLabel") iconLabel.Position = UDim2.fromOffset(6, 6) iconLabel.Size = UDim2.fromOffset(38, 38) iconLabel.BackgroundColor3 = color iconLabel.Text = icon iconLabel.TextColor3 = Color3.new(1, 1, 1) iconLabel.TextSize = 17 iconLabel.Font = Enum.Font.GothamBold iconLabel.Parent = card local iconCorner = Instance.new("UICorner") iconCorner.CornerRadius = UDim.new(0, 8) iconCorner.Parent = iconLabel local nameLabel = Instance.new("TextLabel") nameLabel.BackgroundTransparency = 1 nameLabel.Position = UDim2.fromOffset(52, 5) nameLabel.Size = UDim2.new(1, -62, 0, 14) nameLabel.Text = name:upper() nameLabel.TextColor3 = Color3.fromRGB(140, 145, 160) nameLabel.TextSize = 8 nameLabel.Font = Enum.Font.GothamBold nameLabel.TextXAlignment = Enum.TextXAlignment.Left nameLabel.Parent = card local valueLabel = Instance.new("TextLabel") valueLabel.BackgroundTransparency = 1 valueLabel.Position = UDim2.fromOffset(52, 18) valueLabel.Size = UDim2.new(1, -62, 0, 26) valueLabel.Text = "--" valueLabel.TextColor3 = Color3.fromRGB(248, 248, 252) valueLabel.TextSize = 16 valueLabel.Font = Enum.Font.GothamBold valueLabel.TextXAlignment = Enum.TextXAlignment.Left valueLabel.TextTruncate = Enum.TextTruncate.AtEnd valueLabel.Parent = card statCards[name] = { Card = card, Value = valueLabel } end createStatCard("Damage", "⚔", Color3.fromRGB(190, 60, 70)) createStatCard("Range", "◎", Color3.fromRGB(60, 125, 205)) createStatCard("Cooldown", "◷", Color3.fromRGB(145, 90, 205)) createStatCard("Price", "$", Color3.fromRGB(50, 165, 95)) createStatCard("DPS", "⚡", Color3.fromRGB(220, 150, 45)) --================================================== -- SELECTION --================================================== local selectedUnit = nil local selectedUnitName = nil local towerButtons = {} local function animateStat(name, value) local data = statCards[name] if not data then return end data.Value.Text = tostring(value) data.Value.TextTransparency = 1 TweenService:Create( data.Value, TweenInfo.new( 0.2, Enum.EasingStyle.Quint, Enum.EasingDirection.Out ), { TextTransparency = 0 } ):Play() end local function calculateDPS(unit) local directDPS = findStat(unit, "DPS") if directDPS ~= "N/A" then return directDPS end local damage = findStat(unit, "Damage") local cooldown = findStat(unit, "Cooldown") if type(damage) == "number" and type(cooldown) == "number" and cooldown > 0 then return math.round((damage / cooldown) * 100) / 100 end return "N/A" end local function showUnitStats(unit) selectedUnit = unit selectedUnitName = unit.Name selectedTitle.Text = unit.Name subtitle.Text = "EXISTING UNIT / TOWER" animateStat("Damage", findStat(unit, "Damage")) animateStat("Range", findStat(unit, "Range")) animateStat("Cooldown", findStat(unit, "Cooldown")) animateStat("Price", findStat(unit, "Price")) animateStat("DPS", calculateDPS(unit)) statsScroll.CanvasPosition = Vector2.new(0, 0) for button, buttonUnit in pairs(towerButtons) do if buttonUnit == unit then button.BackgroundColor3 = Color3.fromRGB(62, 100, 175) else button.BackgroundColor3 = Color3.fromRGB(40, 42, 53) end end end local function createTowerButton(unit) local button = Instance.new("TextButton") button.Name = unit.Name button.Size = UDim2.new(1, -4, 0, 42) button.BackgroundColor3 = Color3.fromRGB(40, 42, 53) button.BorderSizePixel = 0 button.Text = unit.Name button.TextColor3 = Color3.fromRGB(240, 240, 245) button.TextSize = 11 button.Font = Enum.Font.GothamMedium button.TextTruncate = Enum.TextTruncate.AtEnd button.AutoButtonColor = false button.Parent = towerList local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 7) corner.Parent = button towerButtons[button] = unit button.MouseEnter:Connect(function() if selectedUnit ~= unit then TweenService:Create( button, TweenInfo.new(0.1), { BackgroundColor3 = Color3.fromRGB(50, 53, 66) } ):Play() end end) button.MouseLeave:Connect(function() if selectedUnit ~= unit then TweenService:Create( button, TweenInfo.new(0.1), { BackgroundColor3 = Color3.fromRGB(40, 42, 53) } ):Play() end end) button.Activated:Connect(function() showUnitStats(unit) end) end local function refreshTowers() for button in pairs(towerButtons) do if button and button.Parent then button:Destroy() end end table.clear(towerButtons) local objects = getAllUnitsAndTowers() if #objects == 0 then listTitle.Text = "NO TOWERS FOUND" return end for _, object in ipairs(objects) do createTowerButton(object) end listTitle.Text = "TOWERS • " .. tostring(#objects) task.defer(function() towerList.CanvasPosition = Vector2.new(0, 0) end) end refreshTowers() --================================================== -- AUTO REFRESH --================================================== local function isInsideUnitTowerFolder(object) local current = object while current and current ~= ReplicatedStorage do if isUnitTowerFolder(current) then return true end current = current.Parent end return false end ReplicatedStorage.DescendantAdded:Connect(function(object) if isUnitTowerFolder(object) or isInsideUnitTowerFolder(object) then task.defer(refreshTowers) end end) ReplicatedStorage.DescendantRemoving:Connect(function(object) if isUnitTowerFolder(object) or isInsideUnitTowerFolder(object) then task.defer(refreshTowers) end end) --================================================== -- SPAWN AREA --================================================== local spawnArea = Instance.new("Frame") spawnArea.Name = "SpawnArea" spawnArea.AnchorPoint = Vector2.new(0, 1) spawnArea.Position = UDim2.new(0, 7, 1, -7) spawnArea.Size = UDim2.new(1, -14, 0, 50) spawnArea.BackgroundColor3 = Color3.fromRGB(25, 27, 36) spawnArea.BorderSizePixel = 0 spawnArea.Parent = right local spawnAreaCorner = Instance.new("UICorner") spawnAreaCorner.CornerRadius = UDim.new(0, 9) spawnAreaCorner.Parent = spawnArea local spawnButton = Instance.new("TextButton") spawnButton.Name = "SpawnButton" spawnButton.Position = UDim2.fromOffset(6, 6) spawnButton.Size = UDim2.new(1, -12, 1, -12) spawnButton.BackgroundColor3 = Color3.fromRGB(55, 165, 95) spawnButton.BorderSizePixel = 0 spawnButton.Text = "SPAWN SELECTED" spawnButton.TextColor3 = Color3.new(1, 1, 1) spawnButton.TextSize = 11 spawnButton.Font = Enum.Font.GothamBold spawnButton.AutoButtonColor = false spawnButton.Parent = spawnArea local spawnCorner = Instance.new("UICorner") spawnCorner.CornerRadius = UDim.new(0, 8) spawnCorner.Parent = spawnButton --================================================== -- SPAWN NAMES --================================================== local spawnNames = { "SpawnTower", "spawnTower", "SPAWNTOWER", "PlaceTower", "placeTower", "PLACETOWER", "SpawnUnit", "spawnUnit", "SPAWNUNIT", "PlaceUnit", "placeUnit", "PLACEUNIT", "DeployTower", "deployTower", "DEPLOYTOWER", "DeployUnit", "deployUnit", "DEPLOYUNIT", "CreateTower", "createTower", "CREATETOWER", "CreateUnit", "createUnit", "CREATEUNIT", "BuildTower", "buildTower", "BUILDTOWER", "BuildUnit", "buildUnit", "BUILDUNIT", "TowerPlace", "towerPlace", "TOWERPLACE", "UnitPlace", "unitPlace", "UNITPLACE", "TowerSpawn", "towerSpawn", "TOWERSPAWN", "UnitSpawn", "unitSpawn", "UNITSPAWN", "TroopSpawn", "troopSpawn", "TROOPSPAWN", "SpawnTroop", "spawnTroop", "SPAWNTROOP", "PlaceTroop", "placeTroop", "PLACETROOP", "DeployTroop", "deployTroop", "DEPLOYTROOP", "CreateTroop", "createTroop", "CREATETROOP", "BuildTroop", "buildTroop", "BUILDTROOP", "RequestSpawn", "requestSpawn", "REQUESTSPAWN", "SpawnRequest", "spawnRequest", "SPAWNREQUEST", "RequestTower", "requestTower", "REQUESTTOWER", "RequestUnit", "requestUnit", "REQUESTUNIT", "RequestTroop", "requestTroop", "REQUESTTROOP", "SummonTower", "summonTower", "SUMMONTOWER", "SummonUnit", "summonUnit", "SUMMONUNIT", "SummonTroop", "summonTroop", "SUMMONTROOP", "SpawnRequestTower", "spawnRequestTower", "SPAWNREQUESTTOWER", "SpawnRequestUnit", "spawnRequestUnit", "SPAWNREQUESTUNIT", "RequestSpawnTower", "requestSpawnTower", "REQUESTSPAWNTOWER", "RequestSpawnUnit", "requestSpawnUnit", "REQUESTSPAWNUNIT", "Spawn_Tower", "spawn_tower", "Spawn_Unit", "spawn_unit", "SpawnRequest_Tower", "spawnRequest_Tower", "SpawnRequest_Unit", "spawnRequest_Unit", } local function findSpawnRemote() local containers = {} local functionsFolder = ReplicatedStorage:FindFirstChild("Functions") if functionsFolder then table.insert(containers, functionsFolder) end local remoteFunctions = ReplicatedStorage:FindFirstChild("RemoteFunctions") if remoteFunctions then table.insert(containers, remoteFunctions) end local remoteEvents = ReplicatedStorage:FindFirstChild("RemoteEvents") if remoteEvents then table.insert(containers, remoteEvents) end table.insert(containers, ReplicatedStorage) for _, container in ipairs(containers) do for _, wantedName in ipairs(spawnNames) do local remote = container:FindFirstChild(wantedName, true) if remote and ( remote:IsA("RemoteFunction") or remote:IsA("RemoteEvent") ) then return remote end end end return nil end --================================================== -- SPAWN SELECTED --================================================== spawnButton.Activated:Connect(function() if not selectedUnitName then selectedTitle.Text = "SELECT A UNIT OR TOWER" return end local character = player.Character if not character then character = player.CharacterAdded:Wait() end local root = character:FindFirstChild("HumanoidRootPart") if not root then selectedTitle.Text = "CHARACTER NOT READY" return end local spawnRemote = findSpawnRemote() if not spawnRemote then warn("No tower/unit spawn RemoteFunction or RemoteEvent was found.") selectedTitle.Text = "SPAWN REMOTE NOT FOUND" return end local success, result = pcall(function() if spawnRemote:IsA("RemoteFunction") then return spawnRemote:InvokeServer( selectedUnitName, root.CFrame ) elseif spawnRemote:IsA("RemoteEvent") then spawnRemote:FireServer( selectedUnitName, root.CFrame ) return true end end) if not success then warn("Spawn request failed:", result) selectedTitle.Text = "SPAWN REQUEST FAILED" else print( "Spawn request sent:", spawnRemote:GetFullName(), selectedUnitName ) end end) --================================================== -- SPAWN BUTTON ANIMATION --================================================== spawnButton.MouseEnter:Connect(function() TweenService:Create( spawnButton, TweenInfo.new(0.1), { BackgroundColor3 = Color3.fromRGB(65, 185, 105) } ):Play() end) spawnButton.MouseLeave:Connect(function() TweenService:Create( spawnButton, TweenInfo.new(0.1), { BackgroundColor3 = Color3.fromRGB(55, 165, 95) } ):Play() end) --================================================== -- HIDE / SHOW --================================================== local showButton = Instance.new("TextButton") showButton.Name = "ShowHub" showButton.AnchorPoint = Vector2.new(0, 0.5) showButton.Position = UDim2.new(0, 10, 0.5, 0) showButton.Size = UDim2.fromOffset(52, 52) showButton.BackgroundColor3 = Color3.fromRGB(35, 38, 49) showButton.BorderSizePixel = 0 showButton.Text = "☰" showButton.TextColor3 = Color3.new(1, 1, 1) showButton.TextSize = 22 showButton.Font = Enum.Font.GothamBold showButton.Visible = false showButton.Parent = gui local showCorner = Instance.new("UICorner") showCorner.CornerRadius = UDim.new(1, 0) showCorner.Parent = showButton local showStroke = Instance.new("UIStroke") showStroke.Color = Color3.fromRGB(65, 68, 82) showStroke.Parent = showButton hideButton.Activated:Connect(function() main.Visible = false showButton.Visible = true end) showButton.Activated:Connect(function() main.Visible = true showButton.Visible = false end) --================================================== -- PC + MOBILE DRAGGING --================================================== local dragging = false local dragStart = nil local startPosition = nil local dragInput = nil top.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true dragStart = input.Position startPosition = main.Position dragInput = input end end) top.InputEnded:Connect(function(input) if input == dragInput or input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = false dragInput = nil end end) UserInputService.InputChanged:Connect(function(input) if not dragging then return end if input.UserInputType ~= Enum.UserInputType.MouseMovement and input.UserInputType ~= Enum.UserInputType.Touch then return end if not dragStart or not startPosition then return end local delta = input.Position - dragStart main.Position = UDim2.new( startPosition.X.Scale, startPosition.X.Offset + delta.X, startPosition.Y.Scale, startPosition.Y.Offset + delta.Y ) end) --================================================== -- FINALIZE --================================================== main.Visible = true gui.Enabled = true task.defer(function() updateScale() end) print("TowerHub loaded.")