
Key Required

-- ====================================================================
-- 8 Ball Duels - ULTIMATE ACCURATE GUIDELINE & AIMBOT ASSIST
-- 100% Akurat pake Geometri Fisika Asli Game:
-- - cushionToi: 18 segmen bantalan (termasuk facing miring & jaw)
-- - pocketToi: Deteksi lubang resmi game (MouthCentre, CommitDepth, Throat)
-- - sweepCircle: Deteksi tabrakan bola lain & ujung bantalan
-- - Indikator LUBANG HIJAU: Menyala terang kalau 100% PASTI MASUK!
-- - Peringatan SCRATCH: Garis putih berubah MERAH kalau bola putih kecemplung
-- ====================================================================
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Pool = ReplicatedStorage:WaitForChild("Libraries"):WaitForChild("GameSpecific"):WaitForChild("Pool")
local PoolConstants = require(Pool:WaitForChild("PoolConstants"))
local PoolGeometry = require(Pool:WaitForChild("PoolGeometry"))
local PoolPhysics = require(Pool:WaitForChild("PoolPhysics"))
local PoolAimOverlay = require(Pool:WaitForChild("PoolAimOverlay"))
local FrameWidth = PoolConstants.FrameWidth
local FrameHeight = PoolConstants.FrameHeight
local BallRadius = PoolConstants.BallRadius
local BallDiameter = PoolConstants.BallDiameter
-- Geometri resmi dari game
local cushions = PoolGeometry.GetCushions()
local pockets = PoolGeometry.GetPockets()
local jaws = PoolGeometry.GetJaws()
-- Warna
local COLOR_POCKET_IN = Color3.fromRGB(0, 255, 120) -- Hijau neon: 100% Masuk lubang
local COLOR_NORMAL = Color3.fromRGB(255, 140, 50) -- Oranye: Jalur bola biasa
local COLOR_BOUNCE = Color3.fromRGB(255, 200, 80) -- Kuning: Jalur pantulan
local COLOR_SCRATCH = Color3.fromRGB(255, 40, 40) -- Merah: Bahaya bola putih kecemplung
local COLOR_WHITE = Color3.fromRGB(255, 255, 255)
-- --------------------------------------------------------------------
-- Algoritma Fisika CCD Resmi Game
-- --------------------------------------------------------------------
local function sweepCircle(a1, a2, a3, a4)
local v1 = (a1:Dot(a1)) - a3 * a3
local v2 = 2 * a1:Dot(a2)
if v1 < 0 then
if v2 < 0 then return 0 end
return nil
end
if v2 >= 0 then return nil end
local v3 = a2:Dot(a2)
if v3 <= 1e-06 then return nil end
local v4 = v2 * v2 - 4 * v3 * v1
if v4 < 0 then return nil end
local v5 = (-v2 - math.sqrt(v4)) / (2 * v3)
if not (v5 < 0) and not (a4 < v5) then
return v5
end
return nil
end
local function cushionToi(a1, a2, a3, a4, a5)
local v4 = a5 or BallRadius
local Normal = a3.Normal
local v5 = a2:Dot(Normal)
if v5 >= -1e-06 then return nil end
local v6 = (a1 - a3.A):Dot(Normal)
if v6 < -v4 then return nil end
if v6 < v4 then return nil end
local v3 = (v4 - v6) / v5
if not (v3 < 0) and not (a4 < v3) then
local v1 = a3.B - a3.A
local Magnitude = v1.Magnitude
local v2 = (a1 + a2 * v3 - a3.A):Dot(v1 / Magnitude)
if not (v2 < 0) and not (Magnitude < v2) then
return v3
end
end
return nil
end
local function pocketToi(a1, a2, a3, a4)
local v1 = a2:Dot(a3.Normal)
if v1 <= 1e-06 then return nil end
local v2 = -(((a1 - a3.MouthCentre):Dot(a3.Normal)) - a3.CommitDepth) / v1
if not (v2 < 0) and not (a4 < v2) then
local v3 = Vector2.new(-a3.Normal.Y, a3.Normal.X)
local v4 = math.abs(((a1 + a2 * v2 - a3.MouthCentre):Dot(v3)))
if a3.Mouth / 2 < v4 then return nil end
return v2
end
return nil
end
-- --------------------------------------------------------------------
-- Raycast Bola Akurat (Semua Rintangan Meja)
-- --------------------------------------------------------------------
local function castBall(pos, dir, radius, ignoreNumber, balls)
radius = radius or BallRadius
local maxDist = 200
local closestT = maxDist
local hitKind = "None"
local hitObj = nil
local hitNormal = nil
-- 1. Cek tabrakan bola lain di meja
if balls then
for _, b in ipairs(balls) do
if b.Number ~= ignoreNumber and not b.Pocketed and not b.Sinking then
local t = sweepCircle(b.Position - pos, -dir, radius + (b.Radius or BallRadius), closestT)
if t and t > 0.001 and t < closestT then
closestT = t
hitKind = "Ball"
hitObj = b
end
end
end
end
-- 2. Cek lubang pocket resmi
for _, p in ipairs(pockets) do
local t = pocketToi(pos, dir, p, closestT)
if t and t > 0.001 and t < closestT then
closestT = t
hitKind = "Pocket"
hitObj = p
end
end
-- 3. Cek bantalan / cushion
for _, c in ipairs(cushions) do
local t = cushionToi(pos, dir, c, closestT, radius)
if t and t > 0.001 and t < closestT then
closestT = t
hitKind = "Cushion"
hitObj = c
hitNormal = c.Normal
end
end
-- 4. Cek sudut jaw tip bantalan
for _, j in ipairs(jaws) do
local t = sweepCircle(j.Position - pos, -dir, radius, closestT)
if t and t > 0.001 and t < closestT then
closestT = t
hitKind = "Tip"
hitObj = j
hitNormal = (pos + dir * t - j.Position).Unit
end
end
if hitKind == "None" or closestT >= maxDist then
return pos + dir * 80, "None", nil, nil
end
local hitPos = pos + dir * closestT
local reflectDir = nil
if hitKind == "Pocket" then
hitPos = hitObj.BowlCentre
elseif hitKind == "Cushion" or hitKind == "Tip" then
if hitNormal then
reflectDir = dir - 2 * (dir:Dot(hitNormal)) * hitNormal
if reflectDir.Magnitude > 0.01 then
reflectDir = reflectDir.Unit
end
end
elseif hitKind == "Ball" then
local toOther = (hitObj.Position - hitPos).Unit
reflectDir = toOther
end
return hitPos, hitKind, hitObj, reflectDir
end
-- Ambil daftar bola aktif dari Simulation atau dari GUI
local function getActiveBalls(overlay, sim)
if sim and sim.Balls then
local list = {}
for _, b in pairs(sim.Balls) do
if not b.Pocketed and not b.Sinking then
table.insert(list, b)
end
end
if #list > 0 then return list end
end
-- Fallback dari GUI Balls frame
local pt = overlay.Root and overlay.Root.Parent and overlay.Root.Parent.Parent
local bf = pt and pt:FindFirstChild("Balls")
if bf then
local list = {}
for _, b in ipairs(bf:GetChildren()) do
if b:IsA("Frame") and b.Visible then
local num = tonumber(b.Name:match("%d+")) or -1
local x = (b.Position.X.Scale - 0.5) * FrameWidth
local y = (0.5 - b.Position.Y.Scale) * FrameHeight
table.insert(list, {Number = num, Position = Vector2.new(x, y), Radius = BallRadius})
end
end
return list
end
return {}
end
-- --------------------------------------------------------------------
-- Renderer Garis & Indikator Lubang
-- --------------------------------------------------------------------
local function setLine(lineFrame, a2, a3, thickness, color)
local v1 = a3 - a2
local Magnitude = v1.Magnitude
if Magnitude < 0.001 then
lineFrame.Visible = false
return
end
local v2 = (a2 + a3) / 2
lineFrame.Position = UDim2.fromScale(0.5 + v2.X / FrameWidth, 0.5 - v2.Y / FrameHeight)
lineFrame.Size = UDim2.fromScale(Magnitude / FrameWidth, (thickness or 0.5) / FrameHeight)
lineFrame.Rotation = math.deg(math.atan2(-v1.Y, v1.X))
if color then
lineFrame.BackgroundColor3 = color
lineFrame.ImageColor3 = color
end
lineFrame.Visible = true
local Attribute = lineFrame:GetAttribute("SliceSourceHeight")
if Attribute and lineFrame.Parent then
local Y = lineFrame.Parent.AbsoluteSize.Y
if Y > 0 then
lineFrame.SliceScale = math.max((thickness or 0.5) / FrameHeight * Y / Attribute, 0.01)
end
end
end
local function getHelperLine(overlay, key, template, color, transparency)
if not overlay[key] or not overlay[key].Parent then
local line = template:Clone()
line.Name = key
line.BackgroundColor3 = color or template.BackgroundColor3
line.ImageColor3 = color or template.ImageColor3
line.BackgroundTransparency = transparency or 0.35
line.ImageTransparency = transparency or 0.35
line.Visible = false
line.Parent = overlay.Root
overlay[key] = line
end
return overlay[key]
end
local function getPocketMarker(overlay, name, color)
local marker = overlay.Root:FindFirstChild(name)
if not marker then
marker = Instance.new("Frame")
marker.Name = name
marker.AnchorPoint = Vector2.new(0.5, 0.5)
marker.Size = UDim2.fromScale(6.5 / FrameWidth, 6.5 / FrameHeight)
marker.BackgroundTransparency = 1
marker.ZIndex = overlay.AimLine.ZIndex + 10
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(1, 0)
corner.Parent = marker
local stroke = Instance.new("UIStroke")
stroke.Thickness = 3
stroke.Color = color or COLOR_POCKET_IN
stroke.Transparency = 0.05
stroke.Parent = marker
marker.Parent = overlay.Root
end
return marker
end
local function hideHelpers(overlay)
if overlay.ObjectLineBounce then overlay.ObjectLineBounce.Visible = false end
if overlay.CueLineBounce then overlay.CueLineBounce.Visible = false end
if overlay.AimBounce then overlay.AimBounce.Visible = false end
if overlay.AimBounce2 then overlay.AimBounce2.Visible = false end
if overlay.ComboLine then overlay.ComboLine.Visible = false end
local m1 = overlay.Root:FindFirstChild("TargetPocketMarker")
if m1 then m1.Visible = false end
local m2 = overlay.Root:FindFirstChild("ScratchMarker")
if m2 then m2.Visible = false end
end
-- --------------------------------------------------------------------
-- Hook PoolPhysics.PredictShot (Agar Simulation Selalu Terbawa)
-- --------------------------------------------------------------------
if not _G.OriginalPredictShot then
_G.OriginalPredictShot = PoolPhysics.PredictShot
end
PoolPhysics.PredictShot = function(sim, dir, power, spin, dt)
local res = _G.OriginalPredictShot(sim, dir, power, spin, dt)
if res then
res.Simulation = sim
end
return res
end
-- --------------------------------------------------------------------
-- Hook Utama: PoolAimOverlay.Update (Akurasi Penuh 100%)
-- --------------------------------------------------------------------
PoolAimOverlay.Update = function(a1, a2, a3, a4, a5, a6, a7)
if a3.Magnitude < 0.0001 then return end
local isLegal = a7 ~= false
local AimLineColour = if not isLegal then COLOR_SCRATCH else PoolConstants.AimLineColour
local DefaultAccent = if not isLegal then COLOR_SCRATCH else (a1.AccentColour or COLOR_NORMAL)
a1.AimLine.BackgroundColor3 = AimLineColour
a1.AimLine.ImageColor3 = AimLineColour
local UIStroke = a1.Ghost:FindFirstChildOfClass("UIStroke")
if UIStroke then
UIStroke.Color = AimLineColour
end
-- Stick & Pull back animation (Presisi Bawaan Game)
local Unit = a3.Unit
local v2 = a2 - Unit * (PoolConstants.BallRadius + (math.clamp(a5 or 0, 0, 1) * 9 + 1.6))
local v3 = v2 - Unit * 34
if not a1.StickEnabled then
a1.CueStick.Visible = false
else
setLine(a1.CueStick, v3, v2, 0.85)
end
if a4 and a4.Kind ~= "None" and a1.GuidesEnabled then
local Ghost = a4.Ghost
setLine(a1.AimLine, a2, Ghost, 0.5)
a1.Ghost.Position = UDim2.fromScale(0.5 + Ghost.X / FrameWidth, 0.5 - Ghost.Y / FrameHeight)
a1.Ghost.Visible = true
local balls = getActiveBalls(a1, a4.Simulation)
local targetMarker = getPocketMarker(a1, "TargetPocketMarker", COLOR_POCKET_IN)
targetMarker.Visible = false
local scratchMarker = getPocketMarker(a1, "ScratchMarker", COLOR_SCRATCH)
scratchMarker.Visible = false
if a4.Kind == "Ball" and a4.ObjectDirection then
-- Posisi awal bola target
local objStart = Ghost + a4.ObjectDirection * BallDiameter
local hitPos1, kind1, obj1, reflect1 = castBall(objStart, a4.ObjectDirection, BallRadius, a4.Other, balls)
-- 1. GARIS BOLA TARGET
if kind1 == "Pocket" then
-- 100% MASUK LUBANG! Warnai Hijau Terang & Nyalakan Indikator Lubang!
setLine(a1.ObjectLine, objStart, hitPos1, 0.6, COLOR_POCKET_IN)
targetMarker.Position = UDim2.fromScale(0.5 + obj1.MouthCentre.X / FrameWidth, 0.5 - obj1.MouthCentre.Y / FrameHeight)
targetMarker.Visible = true
if a1.ObjectLineBounce then a1.ObjectLineBounce.Visible = false end
else
-- Kena bantalan / tip
setLine(a1.ObjectLine, objStart, hitPos1, 0.55, DefaultAccent)
targetMarker.Visible = false
-- 2. GARIS PANTULAN (BOUNCE)
if reflect1 then
local hitPos2, kind2, obj2, _ = castBall(hitPos1, reflect1, BallRadius, a4.Other, balls)
local bounceLine = getHelperLine(a1, "ObjectLineBounce", a1.ObjectLine, COLOR_BOUNCE, 0.4)
if kind2 == "Pocket" then
-- Pantulan masuk lubang! (Bank shot masuk)
setLine(bounceLine, hitPos1, hitPos2, 0.5, COLOR_POCKET_IN)
targetMarker.Position = UDim2.fromScale(0.5 + obj2.MouthCentre.X / FrameWidth, 0.5 - obj2.MouthCentre.Y / FrameHeight)
targetMarker.Visible = true
else
setLine(bounceLine, hitPos1, hitPos2, 0.4, COLOR_BOUNCE)
end
elseif a1.ObjectLineBounce then
a1.ObjectLineBounce.Visible = false
end
end
-- 3. GARIS CUE BALL DEFLECTION (Arah Bola Putih)
if a4.CueDirection and a4.CueDirection.Magnitude > 0.01 then
local cueHit, cueKind, cueObj, cueReflect = castBall(Ghost, a4.CueDirection, BallRadius, 0, balls)
if cueKind == "Pocket" then
-- BAHAYA: BOLA PUTIH SCRATCH KECEMPLUNG!
setLine(a1.CueLine, Ghost, cueHit, 0.55, COLOR_SCRATCH)
scratchMarker.Position = UDim2.fromScale(0.5 + cueObj.MouthCentre.X / FrameWidth, 0.5 - cueObj.MouthCentre.Y / FrameHeight)
scratchMarker.Visible = true
if a1.CueLineBounce then a1.CueLineBounce.Visible = false end
else
setLine(a1.CueLine, Ghost, cueHit, 0.5, AimLineColour)
scratchMarker.Visible = false
if cueReflect then
local cueHit2, cueKind2, cueObj2, _ = castBall(cueHit, cueReflect, BallRadius, 0, balls)
local cueBounce = getHelperLine(a1, "CueLineBounce", a1.CueLine, AimLineColour, 0.5)
if cueKind2 == "Pocket" then
setLine(cueBounce, cueHit, cueHit2, 0.45, COLOR_SCRATCH)
scratchMarker.Position = UDim2.fromScale(0.5 + cueObj2.MouthCentre.X / FrameWidth, 0.5 - cueObj2.MouthCentre.Y / FrameHeight)
scratchMarker.Visible = true
else
setLine(cueBounce, cueHit, cueHit2, 0.35, AimLineColour)
end
elseif a1.CueLineBounce then
a1.CueLineBounce.Visible = false
end
end
else
a1.CueLine.Visible = false
if a1.CueLineBounce then a1.CueLineBounce.Visible = false end
end
if a1.AimBounce then a1.AimBounce.Visible = false end
if a1.AimBounce2 then a1.AimBounce2.Visible = false end
return
elseif a4.Kind == "Cushion" or a4.Kind == "Tip" then
-- Kick shot: Tembak langsung ke bantalan
a1.ObjectLine.Visible = false
a1.CueLine.Visible = false
if a1.ObjectLineBounce then a1.ObjectLineBounce.Visible = false end
if a1.CueLineBounce then a1.CueLineBounce.Visible = false end
local hit1, kind1, obj1, reflect1 = castBall(a2, Unit, BallRadius, 0, balls)
if reflect1 then
local hit2, kind2, obj2, reflect2 = castBall(Ghost, reflect1, BallRadius, 0, balls)
local aimBounce = getHelperLine(a1, "AimBounce", a1.AimLine, AimLineColour, 0.35)
if kind2 == "Pocket" then
setLine(aimBounce, Ghost, hit2, 0.5, COLOR_SCRATCH)
scratchMarker.Position = UDim2.fromScale(0.5 + obj2.MouthCentre.X / FrameWidth, 0.5 - obj2.MouthCentre.Y / FrameHeight)
scratchMarker.Visible = true
else
setLine(aimBounce, Ghost, hit2, 0.45, AimLineColour)
if reflect2 then
local hit3 = castBall(hit2, reflect2, BallRadius, 0, balls)
local aimBounce2 = getHelperLine(a1, "AimBounce2", a1.AimLine, AimLineColour, 0.55)
setLine(aimBounce2, hit2, hit3, 0.35, AimLineColour)
elseif a1.AimBounce2 then
a1.AimBounce2.Visible = false
end
end
elseif a1.AimBounce then
a1.AimBounce.Visible = false
if a1.AimBounce2 then a1.AimBounce2.Visible = false end
end
return
end
end
a1.AimLine.Visible = false
a1.Ghost.Visible = false
a1.ObjectLine.Visible = false
a1.CueLine.Visible = false
hideHelpers(a1)
end
-- Auto-hide pas ganti turn
local oldSetGuides = PoolAimOverlay.SetGuidesVisible
PoolAimOverlay.SetGuidesVisible = function(a1, a2)
oldSetGuides(a1, a2)
if not a2 then
hideHelpers(a1)
end
end
print("[Guideline] v4 PRO HOOK AKTIF! Akurasi Geometri CCD 100%!")No comments yet for this script.