-- Load clean UI-free TrickshotAssist if _G.TrickshotAssistCleanup then pcall(_G.TrickshotAssistCleanup) end local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local LocalPlayer = Players.LocalPlayer local PlayerGui = LocalPlayer:WaitForChild("PlayerGui") local PoolFolder = ReplicatedStorage:WaitForChild("Libraries"):WaitForChild("GameSpecific"):WaitForChild("Pool") local PoolGeometry = require(PoolFolder:WaitForChild("PoolGeometry")) local PoolConstants = require(PoolFolder:WaitForChild("PoolConstants")) local PoolTableRenderer2D = require(PoolFolder:WaitForChild("PoolTableRenderer2D")) local PoolAimOverlay = require(PoolFolder:WaitForChild("PoolAimOverlay")) local DefaultTable = PoolGeometry.Default local FrameSize = PoolTableRenderer2D.GetFrameSize() local FrameWidth = FrameSize.X local FrameHeight = FrameSize.Y local BallRadius = PoolConstants.BallRadius or 1.125 local BallDiameter = BallRadius * 2 local CushionById = {} for _, c in ipairs(DefaultTable.Cushions) do CushionById[c.Id] = c end local PoolGameUI = PlayerGui:WaitForChild("PoolGameUI") local TableFolder = PoolGameUI:WaitForChild("Table") local PoolTableGui = TableFolder:WaitForChild("PoolTable") local TableOverlay = PoolTableGui:WaitForChild("Overlay") local old = TableOverlay:FindFirstChild("TrickshotOverlayNoUI") if old then old:Destroy() end local Container = Instance.new("Frame") Container.Name = "TrickshotOverlayNoUI" Container.BackgroundTransparency = 1 Container.BorderSizePixel = 0 Container.Size = UDim2.fromScale(1, 1) Container.Position = UDim2.fromScale(0, 0) Container.ZIndex = 40 Container.Parent = TableOverlay local function toScale(pos) return UDim2.fromScale(0.5 + pos.X / FrameWidth, 0.5 - pos.Y / FrameHeight) end local function createLine(name, color, thickness, zIndex) local line = Instance.new("Frame") line.Name = name line.AnchorPoint = Vector2.new(0.5, 0.5) line.BorderSizePixel = 0 line.BackgroundColor3 = color line.ZIndex = zIndex or 42 line.Visible = false line.Parent = Container local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = line return line end local function setLine(lineObj, p1, p2, thickness, color, transparency) local delta = p2 - p1 local len = delta.Magnitude if len < 0.001 then lineObj.Visible = false return end local mid = (p1 + p2) / 2 lineObj.Position = UDim2.fromScale(0.5 + mid.X / FrameWidth, 0.5 - mid.Y / FrameHeight) lineObj.Size = UDim2.fromScale(len / FrameWidth, thickness / FrameHeight) lineObj.Rotation = math.deg(math.atan2(-delta.Y, delta.X)) if color then lineObj.BackgroundColor3 = color end lineObj.BackgroundTransparency = transparency or 0 lineObj.Visible = true end local Visuals = { CueLine = createLine("CueLine", Color3.fromRGB(255, 255, 255), 0.45, 42), KickBounceLine = createLine("KickBounceLine", Color3.fromRGB(255, 215, 0), 0.45, 42), KickGhostBall = Instance.new("Frame"), CueDeflect = createLine("CueDeflect", Color3.fromRGB(255, 220, 100), 0.35, 41), ObjLeg1 = createLine("ObjLeg1", Color3.fromRGB(0, 255, 130), 0.55, 44), ObjLeg2 = createLine("ObjLeg2", Color3.fromRGB(0, 225, 255), 0.5, 44), ObjLeg3 = createLine("ObjLeg3", Color3.fromRGB(255, 65, 220), 0.45, 44), GhostBall = Instance.new("Frame"), PocketRings = {} } do local gb = Visuals.GhostBall gb.Name = "GhostBall" gb.AnchorPoint = Vector2.new(0.5, 0.5) gb.Size = UDim2.fromScale(BallDiameter / FrameWidth, BallDiameter / FrameHeight) gb.BackgroundTransparency = 0.85 gb.BackgroundColor3 = Color3.fromRGB(255, 255, 255) gb.BorderSizePixel = 0 gb.ZIndex = 45 gb.Visible = false gb.Parent = Container local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = gb local stroke = Instance.new("UIStroke") stroke.Name = "Stroke" stroke.Thickness = 2 stroke.Color = Color3.fromRGB(0, 255, 130) stroke.Transparency = 0.1 stroke.Parent = gb end do local kgb = Visuals.KickGhostBall kgb.Name = "KickGhostBall" kgb.AnchorPoint = Vector2.new(0.5, 0.5) kgb.Size = UDim2.fromScale(BallDiameter / FrameWidth, BallDiameter / FrameHeight) kgb.BackgroundTransparency = 0.85 kgb.BackgroundColor3 = Color3.fromRGB(255, 215, 0) kgb.BorderSizePixel = 0 kgb.ZIndex = 45 kgb.Visible = false kgb.Parent = Container local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = kgb local stroke = Instance.new("UIStroke") stroke.Name = "Stroke" stroke.Thickness = 2 stroke.Color = Color3.fromRGB(255, 215, 0) stroke.Transparency = 0.1 stroke.Parent = kgb end for _, pocket in ipairs(DefaultTable.Pockets) do local ring = Instance.new("Frame") ring.Name = "PocketRing_" .. pocket.Id ring.AnchorPoint = Vector2.new(0.5, 0.5) local mouthSize = pocket.Mouth ring.Size = UDim2.fromScale(mouthSize / FrameWidth * 1.15, mouthSize / FrameHeight * 1.15) ring.Position = toScale(pocket.MouthCentre) ring.BackgroundTransparency = 0.85 ring.BackgroundColor3 = Color3.fromRGB(0, 255, 130) ring.BorderSizePixel = 0 ring.ZIndex = 43 ring.Visible = false ring.Parent = Container local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(1, 0) corner.Parent = ring local stroke = Instance.new("UIStroke") stroke.Name = "Stroke" stroke.Thickness = 2.5 stroke.Color = Color3.fromRGB(0, 255, 130) stroke.Parent = ring Visuals.PocketRings[pocket.Id] = ring end local function hideAll() Visuals.CueLine.Visible = false Visuals.KickBounceLine.Visible = false Visuals.KickGhostBall.Visible = false Visuals.CueDeflect.Visible = false Visuals.ObjLeg1.Visible = false Visuals.ObjLeg2.Visible = false Visuals.ObjLeg3.Visible = false Visuals.GhostBall.Visible = false for _, ring in pairs(Visuals.PocketRings) do ring.Visible = false end end local function rayCircle(origin, dir, center, radius) local offset = center - origin local proj = offset:Dot(dir) if proj <= 0 then return nil end local distSq = offset:Dot(offset) - proj * proj local radSq = radius * radius if distSq > radSq then return nil end local d = math.sqrt(math.max(0, radSq - distSq)) local hitDist = proj - d if hitDist < 0 then return nil end return hitDist, origin + dir * hitDist end local function rayCushion(origin, dir, cushion, r) local normal = cushion.Normal local d = dir:Dot(normal) if d >= -1e-5 then return nil end local dist = (r - (origin - cushion.A):Dot(normal)) / d if dist < 0 then return nil end local hitPt = origin + dir * dist local ab = cushion.B - cushion.A local abLen = ab.Magnitude local abDir = ab / abLen local proj = (hitPt - cushion.A):Dot(abDir) if proj >= -0.15 and proj <= abLen + 0.15 then return dist, hitPt, normal end return nil end local function checkPocket(origin, dir, pocket, r) local d = dir:Dot(pocket.Normal) if d <= 1e-4 then return nil end local offset = pocket.MouthCentre - origin local dist = offset:Dot(pocket.Normal) / d if dist <= 0 then return nil end local hitMouth = origin + dir * dist local sideVec = Vector2.new(-pocket.Normal.Y, pocket.Normal.X) local lat = math.abs((hitMouth - pocket.MouthCentre):Dot(sideVec)) if lat <= (pocket.Mouth / 2 - 0.05) then return dist, hitMouth end return nil end local function getActiveBalls(ignoreBallNum) local balls = {} pcall(function() local ballsFolder = PoolGameUI.Table.PoolTable.Balls for _, b in ipairs(ballsFolder:GetChildren()) do if b:IsA("Frame") and b.Visible then local num = tonumber(string.match(b.Name, "%d+")) if num and num ~= 0 and num ~= ignoreBallNum then local x = (b.Position.X.Scale - 0.5) * FrameWidth local y = (0.5 - b.Position.Y.Scale) * FrameHeight table.insert(balls, {num = num, pos = Vector2.new(x, y)}) end end end end) return balls end local function traceBallTrajectory(startPos, initialDir, ignoreNum) local legs = {} local potPocket = nil local otherBalls = getActiveBalls(ignoreNum) local curPos = startPos local curDir = initialDir for bounce = 1, 3 do local closestDist = math.huge local closestHit = nil local closestNorm = nil local closestType = nil local targetPocket = nil for _, p in ipairs(DefaultTable.Pockets) do local pDist, pHit = checkPocket(curPos, curDir, p, BallRadius) if pDist and pDist < closestDist then closestDist = pDist closestHit = pHit closestType = "Pocket" targetPocket = p end end if closestType ~= "Pocket" then for _, b in ipairs(otherBalls) do local bDist, bHit = rayCircle(curPos, curDir, b.pos, BallDiameter) if bDist and bDist < closestDist then closestDist = bDist closestHit = bHit closestType = "Ball" end end end if closestType ~= "Pocket" and closestType ~= "Ball" then for _, c in ipairs(DefaultTable.Cushions) do local cDist, cHit, cNorm = rayCushion(curPos, curDir, c, BallRadius) if cDist and cDist < closestDist then closestDist = cDist closestHit = cHit closestNorm = cNorm closestType = "Cushion" end end end if closestHit then table.insert(legs, { Bounce = bounce, From = curPos, To = closestHit, Type = closestType, Pocket = targetPocket }) if closestType == "Pocket" then potPocket = targetPocket break elseif closestType == "Cushion" and closestNorm then curPos = closestHit + closestNorm * 0.05 curDir = (curDir - 2 * (curDir:Dot(closestNorm)) * closestNorm).Unit else break end else break end end return legs, potPocket end local oldPaoUpdate = PoolAimOverlay.Update local oldPaoHide = PoolAimOverlay.HideGuides local oldPaoSetGuides = PoolAimOverlay.SetGuidesVisible PoolAimOverlay.Update = function(self, cuePos, aimDir, hitResult, power, guideLength, isLegal) if self.ObjectLine then self.ObjectLine.Visible = false end if self.CueLine then self.CueLine.Visible = false end if self.AimLine then self.AimLine.Visible = false end hideAll() if not hitResult or hitResult.Kind == "None" or not PoolGameUI.Enabled then return oldPaoUpdate(self, cuePos, aimDir, hitResult, power, guideLength, isLegal) end if hitResult.Kind == "Ball" and hitResult.Ghost and hitResult.ObjectDirection then local ghost = hitResult.Ghost local objDir = hitResult.ObjectDirection.Unit local cueDir = hitResult.CueDirection and hitResult.CueDirection.Unit setLine(Visuals.CueLine, cuePos, ghost, 0.45, Color3.fromRGB(255, 255, 255), 0.1) local gb = Visuals.GhostBall gb.Position = toScale(ghost) gb.Visible = true local objStart = ghost + objDir * BallDiameter local legs, potPocket = traceBallTrajectory(objStart, objDir, hitResult.Other) local leg1 = legs[1] local leg2 = legs[2] local leg3 = legs[3] if leg1 then local c1 = (potPocket and #legs == 1) and Color3.fromRGB(0, 255, 130) or Color3.fromRGB(255, 255, 255) setLine(Visuals.ObjLeg1, leg1.From, leg1.To, 0.55, c1, 0.05) end if leg2 then local c2 = (potPocket and #legs == 2) and Color3.fromRGB(0, 230, 255) or Color3.fromRGB(120, 210, 255) setLine(Visuals.ObjLeg2, leg2.From, leg2.To, 0.5, c2, 0.1) end if leg3 then local c3 = (potPocket and #legs == 3) and Color3.fromRGB(255, 65, 220) or Color3.fromRGB(200, 100, 255) setLine(Visuals.ObjLeg3, leg3.From, leg3.To, 0.45, c3, 0.15) end if cueDir then local deflectEnd = ghost + cueDir * 18 local scratches = false for _, p in ipairs(DefaultTable.Pockets) do local sDist, sHit = checkPocket(ghost, cueDir, p, BallRadius) if sDist and sDist < 25 then deflectEnd = sHit scratches = true break end end local defColor = scratches and Color3.fromRGB(255, 45, 45) or Color3.fromRGB(255, 220, 100) setLine(Visuals.CueDeflect, ghost, deflectEnd, 0.35, defColor, scratches and 0 or 0.3) end if potPocket then local ring = Visuals.PocketRings[potPocket.Id] if ring then local stroke = ring:FindFirstChild("Stroke") local ringColor = Color3.fromRGB(0, 255, 130) if #legs == 2 then ringColor = Color3.fromRGB(0, 230, 255) elseif #legs == 3 then ringColor = Color3.fromRGB(255, 65, 220) end ring.BackgroundColor3 = ringColor if stroke then stroke.Color = ringColor end ring.Visible = true end end elseif hitResult.Kind == "Cushion" and hitResult.Ghost then local cushionPt = hitResult.Ghost local cushion = hitResult.CushionId and CushionById[hitResult.CushionId] local norm = cushion and cushion.Normal if not norm then local bestDist = math.huge for _, c in ipairs(DefaultTable.Cushions) do local d = math.abs((cushionPt - c.A):Dot(c.Normal)) if d < bestDist then bestDist = d norm = c.Normal end end end setLine(Visuals.CueLine, cuePos, cushionPt, 0.45, Color3.fromRGB(255, 255, 255), 0.1) if norm then local bounceDir = (aimDir - 2 * (aimDir:Dot(norm)) * norm).Unit local bounceOrigin = cushionPt + norm * 0.05 local kickDist = math.huge local kickHitPos = nil local kickBall = nil local otherBalls = getActiveBalls(nil) for _, b in ipairs(otherBalls) do local dist, pt = rayCircle(bounceOrigin, bounceDir, b.pos, BallDiameter) if dist and dist < kickDist then kickDist = dist kickHitPos = pt kickBall = b end end if kickBall and kickHitPos then setLine(Visuals.KickBounceLine, bounceOrigin, kickHitPos, 0.45, Color3.fromRGB(255, 215, 0), 0.1) local kgb = Visuals.KickGhostBall kgb.Position = toScale(kickHitPos) kgb.Visible = true local kickObjDir = (kickBall.pos - kickHitPos).Unit local kickStart = kickHitPos + kickObjDir * BallDiameter local legs, potPocket = traceBallTrajectory(kickStart, kickObjDir, kickBall.num) if legs[1] then local c1 = potPocket and Color3.fromRGB(0, 255, 130) or Color3.fromRGB(255, 255, 255) setLine(Visuals.ObjLeg1, legs[1].From, legs[1].To, 0.55, c1, 0.05) end if legs[2] then local c2 = potPocket and Color3.fromRGB(0, 230, 255) or Color3.fromRGB(120, 210, 255) setLine(Visuals.ObjLeg2, legs[2].From, legs[2].To, 0.5, c2, 0.1) end if potPocket then local ring = Visuals.PocketRings[potPocket.Id] if ring then ring.BackgroundColor3 = Color3.fromRGB(255, 215, 0) local stroke = ring:FindFirstChild("Stroke") if stroke then stroke.Color = Color3.fromRGB(255, 215, 0) end ring.Visible = true end end else local c2Dist = math.huge local c2Hit = nil for _, c in ipairs(DefaultTable.Cushions) do local dist, pt = rayCushion(bounceOrigin, bounceDir, c, BallRadius) if dist and dist < c2Dist then c2Dist = dist c2Hit = pt end end if c2Hit then setLine(Visuals.KickBounceLine, bounceOrigin, c2Hit, 0.35, Color3.fromRGB(255, 255, 255), 0.4) end end end end return oldPaoUpdate(self, cuePos, aimDir, hitResult, power, guideLength, isLegal) end PoolAimOverlay.HideGuides = function(self) hideAll() return oldPaoHide(self) end PoolAimOverlay.SetGuidesVisible = function(self, visible) if not visible then hideAll() end return oldPaoSetGuides(self, visible) end _G.TrickshotAssistCleanup = function() pcall(function() PoolAimOverlay.Update = oldPaoUpdate PoolAimOverlay.HideGuides = oldPaoHide PoolAimOverlay.SetGuidesVisible = oldPaoSetGuides Container:Destroy() end) _G.TrickshotAssistCleanup = nil end print("🎱 [Trickshot Assist] Activated! 100% aligned, multi-bounce trickshots enabled, NO UI.")