
Key Required VerifiedUniversal

-- BASKET ASSIST — RAYFIELD
-- Applies a normal ballistic launch after a genuine Shoot input.
-- It uses the game's own trajectory equation, then removes small horizontal
-- drift near the rim and prevents fast shots skipping the scoring detector.
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local VirtualInputManager = game:GetService("VirtualInputManager")
local StarterGui = game:GetService("StarterGui")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local basketballValue = ReplicatedStorage:WaitForChild("Basketball")
local enabled = true
local stopped = false
local shotToken = 0
local shotArmedUntil = 0
local appliedToken = 0
local qualifiedToken = 0
local connections = {}
local cleanupCallbacks = {}
local AIM_CONE_DEGREES = 18
local FINAL_APPROACH_HEIGHT = 22
local FINAL_APPROACH_RADIUS = 18
local autoBlockEnabled = true
local autoBlockPassesEnabled = true
local configuredBlockRange = 25
local autoStealEnabled = true
local configuredStealRange = 16
local autoDribbleEnabled = true
local configuredAutoDribbleRange = 24
local lastAutoDribbleAt = 0
local autoDribbleKeyDown = false
local autoDribbleHeldKey
-- Extra time after the game's dribble protection. At this first checkpoint the
-- script waits 3 ms more for a possible chained-dribble animation before stealing.
local configuredStealWaitMs = 190
local STEAL_RECHECK_MS = 3
local nextStealAt = 0
local dribbleArmedCarrier
local dribbleFallbackUntil = 0
local dribbleConfirmUntil = 0
local dribbleReleaseDeadline = 0
local lastBlockAt = 0
local blockDetectionToken = 0
local blockFlightHandled = false
local pumpSuppressedUntil = setmetatable({}, { __mode = "k" })
local unresolvedWindupUntil = setmetatable({}, { __mode = "k" })
local confirmedPumpAt = setmetatable({}, { __mode = "k" })
local lastBlockTarget
local Rayfield
local detectCarrierDribbleTrack
local sharedEnvironment = type(getgenv) == "function" and getgenv() or _G
local previous = sharedEnvironment.__BasketNaturalAim
if type(previous) == "table" and type(previous.Stop) == "function" then
pcall(previous.Stop)
end
local function connect(signal, callback)
local connection = signal:Connect(callback)
table.insert(connections, connection)
return connection
end
local function notify(text)
pcall(function()
StarterGui:SetCore("SendNotification", {
Title = "Basket Assist",
Text = text,
Duration = 4
})
end)
end
-- Read the player's real configured Shoot bind when Knit is available.
local keybindsController
local ballService
local ballController
local defenseController
local movementController
local networkController
local Zones
local Knit
pcall(function()
Knit = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("Knit"))
end)
local function resolveGameControllers()
if not Knit then return end
if not keybindsController then
pcall(function() keybindsController = Knit.GetController("KeybindsController") end)
end
if not ballController then
pcall(function() ballController = Knit.GetController("BallController") end)
end
if not defenseController then
pcall(function() defenseController = Knit.GetController("DefenseController") end)
end
if not movementController then
pcall(function() movementController = Knit.GetController("MovementController") end)
end
if not networkController then
pcall(function() networkController = Knit.GetController("Network") end)
end
if not ballService then
pcall(function() ballService = Knit.GetService("BallService") end)
end
end
resolveGameControllers()
pcall(function()
Zones = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("Tables"):WaitForChild("Zones"))
end)
local function isShootInput(input)
if keybindsController then
local ok, result = pcall(keybindsController.IsInputValid, input, "Shoot")
if ok then return result == true end
end
return input.UserInputType == Enum.UserInputType.MouseButton1
or input.KeyCode == Enum.KeyCode.ButtonR2
end
-- BallStateClient gives an accurate fallback when the executor does not
-- expose isnetworkowner(). No functions are replaced or hooked.
local ballState
pcall(function()
local ballController = ReplicatedStorage:WaitForChild("Controllers"):WaitForChild("BallController")
ballState = require(ballController:WaitForChild("BallStateClient"))
end)
local executorOwns = type(isnetworkowner) == "function" and isnetworkowner or nil
local function localOwnsBall(ball)
if executorOwns then
local ok, result = pcall(executorOwns, ball)
if ok then return result == true end
end
if ballState and type(ballState.localPlayerIsBallNetworkOwner) == "function" then
local ok, result = pcall(ballState.localPlayerIsBallNetworkOwner, ballState)
if ok then return result == true end
end
return false
end
local function localPossessesBall()
if ballState and type(ballState.localPlayerPossessesBall) == "function" then
local ok, result = pcall(ballState.localPlayerPossessesBall, ballState)
if ok then return result == true end
end
return false
end
local function getBallPlayer(last)
if not ballState then return nil end
local method = last and ballState.getLastPlayerToPossessBall
or ballState.getPlayerPossessingBall
if type(method) ~= "function" then return nil end
local ok, result = pcall(method, ballState)
return ok and result or nil
end
local function currentStealCooldown()
local zone = player:FindFirstChild("Zone")
if zone and Zones and Zones.StealCooldowns then
return Zones.StealCooldowns[zone.Value] or 2.35
end
return 2.35
end
local function releaseAutoDribbleKey()
if not autoDribbleKeyDown then return end
autoDribbleKeyDown = false
local dribbleKey = autoDribbleHeldKey or Enum.KeyCode.Q
autoDribbleHeldKey = nil
pcall(function()
VirtualInputManager:SendKeyEvent(false, dribbleKey, false, game)
end)
end
-- Use the same keyboard route as a real Q press. Calling BallController:Dribble
-- from an executor context can poison the controller's private integrity guard,
-- after which both manual dribbling and shooting silently stop working.
local function requestNativeDribbleInput()
if stopped or os.clock() - lastAutoDribbleAt < 0.12 then return false end
resolveGameControllers()
local dribbleKey = Enum.KeyCode.Q
pcall(function()
local inputs = keybindsController and keybindsController.GetInputs("Dribble")
if inputs and inputs.Keyboard and inputs.Keyboard.EnumType == Enum.KeyCode then
dribbleKey = inputs.Keyboard
end
end)
lastAutoDribbleAt = os.clock()
local pressed = pcall(function()
autoDribbleKeyDown = true
autoDribbleHeldKey = dribbleKey
VirtualInputManager:SendKeyEvent(true, dribbleKey, false, game)
end)
if not pressed then
autoDribbleKeyDown = false
autoDribbleHeldKey = nil
return false
end
task.defer(function()
RunService.Heartbeat:Wait()
releaseAutoDribbleKey()
end)
return true
end
local function horizontalDistanceToSegment(point, segmentStart, segmentEnd)
local point2 = Vector3.new(point.X, 0, point.Z)
local start2 = Vector3.new(segmentStart.X, 0, segmentStart.Z)
local finish2 = Vector3.new(segmentEnd.X, 0, segmentEnd.Z)
local segment = finish2 - start2
local lengthSquared = segment:Dot(segment)
if lengthSquared < 0.001 then
return (point2 - start2).Magnitude
end
local alpha = math.clamp((point2 - start2):Dot(segment) / lengthSquared, 0, 1)
return (point2 - (start2 + segment * alpha)).Magnitude
end
-- BallService.Steal is sent when the opponent commits to the steal, before the
-- roughly half-second lunge finishes. Request the player's configured Dribble
-- input so the game's untouched input connection performs every state check.
local function reactToEnemySteal(thiefCharacter, targetCFrame)
if not autoDribbleEnabled or stopped or os.clock() - lastAutoDribbleAt < 0.08 then return end
resolveGameControllers()
if not ballController then return end
local values = networkController and networkController.CharValues
if not localPossessesBall() and not (values and values.HasBall == true) then return end
if not thiefCharacter or thiefCharacter == player.Character then return end
local thief = Players:GetPlayerFromCharacter(thiefCharacter)
if not thief or thief == player or not thief.Team or not player.Team
or thief.Team == player.Team then return end
local myRoot = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
local thiefRoot = thiefCharacter:FindFirstChild("HumanoidRootPart")
if not myRoot or not thiefRoot then return end
local startPosition = thiefRoot.Position
local hasTarget = typeof(targetCFrame) == "CFrame"
local endPosition = hasTarget and targetCFrame.Position or startPosition
local startDistance = (Vector3.new(myRoot.Position.X, 0, myRoot.Position.Z)
- Vector3.new(startPosition.X, 0, startPosition.Z)).Magnitude
if hasTarget then
if startDistance > configuredAutoDribbleRange
or horizontalDistanceToSegment(myRoot.Position, startPosition, endPosition) > 7 then return end
elseif startDistance > configuredAutoDribbleRange then
return
end
if values and values.Dribbling == true then return end
requestNativeDribbleInput()
end
local function clearDribbleStealArm()
dribbleArmedCarrier = nil
dribbleFallbackUntil = 0
dribbleConfirmUntil = 0
dribbleReleaseDeadline = 0
end
local function enemyDribbleDuration(owner)
local zone = owner and owner:FindFirstChild("Zone")
-- BallController uses 0.55 seconds normally and 0.275 while the carrier's
-- Shadow zone is active.
return zone and zone.Value == "Shadow" and 0.275 or 0.55
end
local function armStealAfterEnemyDribble(owner)
if not autoStealEnabled or stopped or not owner or owner == player
or not owner.Team or not player.Team or owner.Team == player.Team then return end
resolveGameControllers()
local carrier
if ballController and type(ballController.GetPlayerPossessingBall) == "function" then
pcall(function() carrier = ballController:GetPlayerPossessingBall() end)
end
carrier = carrier or getBallPlayer(false)
if carrier ~= owner then return end
dribbleArmedCarrier = owner
local duration = enemyDribbleDuration(owner)
dribbleFallbackUntil = os.clock() + duration + configuredStealWaitMs / 1000
dribbleConfirmUntil = dribbleFallbackUntil + STEAL_RECHECK_MS / 1000
dribbleReleaseDeadline = dribbleConfirmUntil + 0.24
-- Only accepted dribbles create a new replicated animation. Every accepted
-- chained dribble therefore resets this deadline, including during the final
-- 190-to-193 ms confirmation gap.
end
local function tryAutoSteal()
if not autoStealEnabled or stopped then return end
resolveGameControllers()
if not defenseController or not ballController then return end
if not player.Team or player.Team.Name == "Visitor" then return end
local carrier
if type(ballController.GetPlayerPossessingBall) == "function" then
pcall(function() carrier = ballController:GetPlayerPossessingBall() end)
end
carrier = carrier or getBallPlayer(false)
if carrier and detectCarrierDribbleTrack then
detectCarrierDribbleTrack(carrier)
end
if not carrier or carrier == player or not carrier.Team
or carrier.Team == player.Team then
if dribbleArmedCarrier and carrier ~= dribbleArmedCarrier then
clearDribbleStealArm()
end
return
end
-- Merely approaching a carrier must never steal. Only a previously observed
-- dribble animation can arm this exact possession.
if dribbleArmedCarrier ~= carrier then return end
if os.clock() < nextStealAt then return end
local character = player.Character
local carrierCharacter = carrier.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
local carrierRoot = carrierCharacter and carrierCharacter:FindFirstChild("HumanoidRootPart")
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not root or not carrierRoot or not humanoid or humanoid.Health <= 0
or humanoid.FloorMaterial == Enum.Material.Air then return end
local ragdoll = character:FindFirstChild("IsRagdoll")
if ragdoll and ragdoll.Value then return end
local gameValues = ReplicatedStorage:FindFirstChild("GameValues")
local scoring = gameValues and gameValues:FindFirstChild("Scoring")
if scoring and scoring.Value then return end
local offset = Vector3.new(carrierRoot.Position.X - root.Position.X, 0,
carrierRoot.Position.Z - root.Position.Z)
local distance = offset.Magnitude
if distance < 0.05 or distance > configuredStealRange then
-- The end of this dribble was not a valid nearby opportunity. A later
-- approach cannot consume it; another real dribble must arm a new attempt.
if os.clock() >= dribbleFallbackUntil then
clearDribbleStealArm()
end
return
end
-- First checkpoint: do not commit yet. An accepted combo animation arriving
-- in the next 3 ms re-arms all three deadlines before this reaches 193 ms.
if os.clock() < dribbleFallbackUntil then return end
if os.clock() < dribbleConfirmUntil then return end
if os.clock() > dribbleReleaseDeadline then
clearDribbleStealArm()
return
end
pcall(function() defenseController:Steal() end)
local values = networkController and networkController.CharValues
local nativeStarted = values and values.Stealing == true
or movementController and movementController.States
and movementController.States.Stealing == true
if nativeStarted then
clearDribbleStealArm()
nextStealAt = os.clock() + currentStealCooldown()
else
-- Network jitter can move the server boundary by a frame. Retry only inside
-- this tiny post-window period, never indefinitely.
nextStealAt = os.clock() + 0.03
end
end
local function legitimateBlockRange()
local multiplier = 1
local values = networkController and networkController.CharValues
if values then
local zone = player:FindFirstChild("Zone")
if values.InZone and zone and Zones and Zones.BlockDistBuffZones then
multiplier += Zones.BlockDistBuffZones[zone.Value] or 0
end
if type(values.MalevolentVisionMode) == "number"
and values.MalevolentVisionMode >= workspace:GetServerTimeNow() then
multiplier += 0.7
end
if type(values.BlockingRangeBoost) == "number" and values.BlockingRangeBoost > 0 then
multiplier *= values.BlockingRangeBoost
end
end
pcall(function()
for _, emote in networkController.Player.Customization.Equipped.Emotes do
if emote == "Emr4n and Luck's Car" then
multiplier += 0.25
break
end
end
end)
return 25 * multiplier
end
local function enemyScoringHoop(shooter)
if not shooter or not shooter.Team or shooter.Team.Name == "Visitor" then return nil end
local hoops = workspace:FindFirstChild("Hoops")
local folder = hoops and hoops:FindFirstChild(shooter.Team.Name)
local hoop = folder and folder:FindFirstChild("Hoop")
return hoop and hoop:IsA("BasePart") and hoop or nil
end
local function shotIsHeadingToBasket(shooter, ball)
local velocity = ball.AssemblyLinearVelocity
if velocity.Magnitude < 32 or velocity.Y < 14 then return false end
local hoop = enemyScoringHoop(shooter)
if not hoop then return false end
local flatVelocity = Vector3.new(velocity.X, 0, velocity.Z)
local flatTarget = Vector3.new(hoop.Position.X - ball.Position.X, 0, hoop.Position.Z - ball.Position.Z)
if flatVelocity.Magnitude < 0.01 or flatTarget.Magnitude < 0.01 then return false end
local travel, toward = flatVelocity.Unit, flatTarget.Unit
return travel:Dot(toward) > 0.45
end
local function canAutoBlock(shooter, ball, preRelease)
if not autoBlockEnabled or stopped or os.clock() - lastBlockAt < 0.55 then return false end
if not shooter or shooter == player or not shooter.Team or not player.Team
or shooter.Team == player.Team or player.Team.Name == "Visitor" then return false end
local character = player.Character
local shooterCharacter = shooter.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
local shooterRoot = shooterCharacter and shooterCharacter:FindFirstChild("HumanoidRootPart")
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if not root or not shooterRoot or not humanoid or humanoid.Health <= 0
or humanoid.FloorMaterial == Enum.Material.Air or localPossessesBall() then return false end
local ragdoll = character:FindFirstChild("IsRagdoll")
if not ragdoll or ragdoll.Value then return false end
local range = math.min(configuredBlockRange, legitimateBlockRange())
local ballPosition = preRelease and shooterRoot.Position or ball.Position
return (ballPosition - root.Position).Magnitude <= range
and (shooterRoot.Position - root.Position).Magnitude <= range + 6
end
local function jumpImmediately(humanoid)
local root = humanoid.Parent and humanoid.Parent:FindFirstChild("HumanoidRootPart")
humanoid.Jump = true
pcall(humanoid.ChangeState, humanoid, Enum.HumanoidStateType.Jumping)
-- ChangeState normally applies the upward velocity on the next physics step.
-- Apply that same natural jump velocity now so a replicated pass cue does not
-- lose one additional frame locally.
if root and root:IsA("BasePart") then
pcall(function()
local jumpVelocity = humanoid.UseJumpPower and humanoid.JumpPower
or math.sqrt(2 * workspace.Gravity * humanoid.JumpHeight)
local velocity = root.AssemblyLinearVelocity
if velocity.Y < jumpVelocity then
root.AssemblyLinearVelocity = Vector3.new(velocity.X, jumpVelocity, velocity.Z)
end
end)
end
end
local function triggerAutoBlock(shooter, ball)
if (pumpSuppressedUntil[shooter] or 0) > os.clock() then return false end
if (unresolvedWindupUntil[shooter] or 0) > os.clock() then return false end
if not canAutoBlock(shooter, ball) or not shotIsHeadingToBasket(shooter, ball) then return false end
local humanoid = player.Character and player.Character:FindFirstChildOfClass("Humanoid")
if not humanoid then return false end
lastBlockAt = os.clock()
lastBlockTarget = shooter
jumpImmediately(humanoid)
return true
end
local function inspectEnemyRelease(shooter)
if not autoBlockEnabled or blockFlightHandled or not shooter or shooter == player then return end
blockDetectionToken += 1
local token = blockDetectionToken
task.spawn(function()
local deadline = os.clock() + 1.20
while autoBlockEnabled and not stopped and token == blockDetectionToken
and os.clock() < deadline do
local ball = basketballValue.Value
if ball and ball:IsA("BasePart") and ball.Parent
and triggerAutoBlock(shooter, ball) then
blockFlightHandled = true
return
end
RunService.Heartbeat:Wait()
end
end)
end
local function triggerPreReleaseBlock(shooter, confirmedPass)
if not confirmedPass and (pumpSuppressedUntil[shooter] or 0) > os.clock() then return false end
local ball = basketballValue.Value
if not ball or not ball:IsA("BasePart") or not ball.Parent
or not canAutoBlock(shooter, ball, true) then return false end
local humanoid = player.Character and player.Character:FindFirstChildOfClass("Humanoid")
if not humanoid then return false end
lastBlockAt = os.clock()
lastBlockTarget = shooter
blockFlightHandled = true
jumpImmediately(humanoid)
return true
end
local animationKind
local function inspectEnemyPass(passer, fromAnimation)
if not autoBlockEnabled or not autoBlockPassesEnabled or stopped
or not passer or passer == player then return end
local now = os.clock()
local confirmedAfterPump = fromAnimation and (pumpSuppressedUntil[passer] or 0) > now
if blockFlightHandled and not fromAnimation then return end
-- Pass blocking is intentionally passer-proximity only. Receiver proximity
-- and the later ball flight are not considered.
if fromAnimation then
pumpSuppressedUntil[passer] = 0
unresolvedWindupUntil[passer] = 0
confirmedPumpAt[passer] = 0
end
-- A confirmed pass immediately after PumpFake must not inherit a cooldown
-- created during the fake's small replication window.
if now - lastBlockAt < 0.55 and not confirmedAfterPump then return end
if fromAnimation then blockFlightHandled = false end
triggerPreReleaseBlock(passer, fromAnimation)
end
local function tryPostPumpPassCue(owner, animator, pumpAt)
if stopped or not autoBlockEnabled or not autoBlockPassesEnabled
or confirmedPumpAt[owner] ~= pumpAt
or getBallPlayer(false) ~= owner then return end
local okTracks, activeTracks = pcall(animator.GetPlayingAnimationTracks, animator)
if not okTracks then return end
for _, activeTrack in activeTracks do
local activeKind = animationKind(activeTrack)
if activeKind == "shot" or activeKind == "pump" then return end
end
inspectEnemyPass(owner, true)
end
-- Normal shots and pump fakes share the same opening animation. The game
-- replaces ShootF/L/R/B with PumpFake at 0.115 seconds only for a fake, so a
-- short confirmation after that branch is the earliest reliable block cue.
local SHOT_ANIMATION_NAMES = {
ShootF = true, ShootL = true, ShootR = true, ShootB = true,
FloaterL = true, FloaterR = true
}
local PUMP_ANIMATION_NAMES = { PumpFake = true, PumpFakeIdle = true }
local PASS_ANIMATION_NAMES = {
StraightPass1 = true, StraightPass2 = true,
Pass1R = true, Pass1L = true, Pass2R = true, Pass2L = true,
JumpPassF = true, JumpPassR = true, JumpPassL = true
}
local STEAL_ANIMATION_NAMES = { StealL = true, StealR = true }
local shotAnimationIds = {}
local pumpAnimationIds = {}
local passAnimationIds = {}
local stealAnimationIds = {}
local dribbleAnimationIds = {}
local dribbleAnimationNames = {}
local function animationIdDigits(id)
return type(id) == "string" and id:match("%d+") or nil
end
do
local assets = ReplicatedStorage:FindFirstChild("Assets")
if assets then
for _, object in assets:GetDescendants() do
if object:IsA("Animation") then
local id = animationIdDigits(object.AnimationId)
local dribbleAsset = false
local ancestor = object.Parent
while ancestor and ancestor ~= assets do
if ancestor.Name == "Dribbles" or ancestor.Name:match("Combo$") then
dribbleAsset = true
break
end
ancestor = ancestor.Parent
end
if id and SHOT_ANIMATION_NAMES[object.Name] then
shotAnimationIds[id] = object.Name
elseif id and PUMP_ANIMATION_NAMES[object.Name] then
pumpAnimationIds[id] = object.Name
elseif id and PASS_ANIMATION_NAMES[object.Name] then
passAnimationIds[id] = object.Name
elseif id and STEAL_ANIMATION_NAMES[object.Name] then
stealAnimationIds[id] = true
elseif id and dribbleAsset then
dribbleAnimationIds[id] = true
dribbleAnimationNames[object.Name] = true
end
end
end
end
end
animationKind = function(track)
if SHOT_ANIMATION_NAMES[track.Name] then return "shot" end
if PASS_ANIMATION_NAMES[track.Name] then return "pass" end
if STEAL_ANIMATION_NAMES[track.Name] then return "steal" end
if track.Name == "PumpFake" then return "pump" end
if track.Name == "PumpFakeIdle" then return "idle" end
if dribbleAnimationNames[track.Name] then return "dribble" end
local ok, animation = pcall(function() return track.Animation end)
local id = ok and animation and animationIdDigits(animation.AnimationId)
if id and shotAnimationIds[id] then return "shot" end
if id and passAnimationIds[id] then return "pass" end
if id and stealAnimationIds[id] then return "steal" end
if id and pumpAnimationIds[id] == "PumpFake" then return "pump" end
if id and pumpAnimationIds[id] == "PumpFakeIdle" then return "idle" end
if id and dribbleAnimationIds[id] then return "dribble" end
return nil
end
local observedDribbleTracks = setmetatable({}, { __mode = "k" })
detectCarrierDribbleTrack = function(owner)
local character = owner and owner.Character
if not character then return false end
local animators = {}
local humanoid = character:FindFirstChildOfClass("Humanoid")
local characterAnimator = humanoid and humanoid:FindFirstChildOfClass("Animator")
if characterAnimator then table.insert(animators, characterAnimator) end
local plrBall = character:FindFirstChild("PlrBall")
local anims = plrBall and plrBall:FindFirstChild("Anims")
local animationController = anims and anims:FindFirstChild("AnimationController")
local ballAnimator = animationController and animationController:FindFirstChildOfClass("Animator")
if ballAnimator then table.insert(animators, ballAnimator) end
for _, animator in animators do
local ok, tracks = pcall(animator.GetPlayingAnimationTracks, animator)
if ok then
for _, track in tracks do
if not observedDribbleTracks[track] and animationKind(track) == "dribble" then
observedDribbleTracks[track] = true
armStealAfterEnemyDribble(owner)
return true
end
end
end
end
return false
end
local animationStates = setmetatable({}, { __mode = "k" })
local watchedCharacters = setmetatable({}, { __mode = "k" })
local function watchEnemyAnimations(owner, character)
if owner == player or watchedCharacters[character] then return end
watchedCharacters[character] = true
task.spawn(function()
local humanoid = character:WaitForChild("Humanoid", 8)
local animator = humanoid and humanoid:WaitForChild("Animator", 8)
if stopped or not animator then return end
local state = { serial = 0 }
animationStates[owner] = state
connect(animator.AnimationPlayed, function(track)
if stopped or (not autoBlockEnabled and not autoStealEnabled and not autoDribbleEnabled)
or not owner.Team or not player.Team
or owner.Team == player.Team or owner.Team.Name == "Visitor" then return end
local kind = animationKind(track)
if not kind then return end
if kind == "steal" then
reactToEnemySteal(character)
return
end
if kind == "dribble" then
observedDribbleTracks[track] = true
armStealAfterEnemyDribble(owner)
return
end
if not autoBlockEnabled then return end
if kind == "idle" then
-- PassController stops all of the post-pump tracks before passing.
-- AnimationController then restarts PumpFakeIdle about 70 ms before
-- the possession handoff. A second shot also restarts it, but its new
-- Shoot/PumpFake track is already active and is rejected immediately.
local pumpAt = confirmedPumpAt[owner] or 0
if autoBlockPassesEnabled and pumpAt > 0 then
tryPostPumpPassCue(owner, animator, pumpAt)
end
return
end
state.serial += 1
local serial = state.serial
if kind == "pump" then
-- This is the game's definitive fake cancellation signal. Suppress
-- looser possession/velocity fallbacks for this specific actor.
local now = os.clock()
confirmedPumpAt[owner] = now
pumpSuppressedUntil[owner] = now + 0.70
unresolvedWindupUntil[owner] = 0
blockDetectionToken += 1
-- A pass calls AnimationController:StopAll(), cutting PumpFake off
-- before its natural end. Defer only until the current synchronous
-- action finishes so a replacement Shoot track can veto the guess.
local stoppedConnection
stoppedConnection = track.Stopped:Connect(function()
if stoppedConnection then stoppedConnection:Disconnect() end
local okTiming, timePosition, length = pcall(function()
return track.TimePosition, track.Length
end)
if not okTiming or length <= 0 or timePosition >= length - 0.02 then return end
task.defer(tryPostPumpPassCue, owner, animator, now)
end)
table.insert(connections, stoppedConnection)
-- If a fallback jumped in the tiny replication window before PumpFake
-- arrived, do not let that stale cooldown suppress the following pass.
if lastBlockTarget == owner and os.clock() - lastBlockAt < 0.35 then
lastBlockAt = -math.huge
blockFlightHandled = false
end
return
end
-- Ignore unrelated distant animations and animation previews. While the
-- track begins, the actor should still be the carrier or beside the ball.
local ball = basketballValue.Value
local currentCarrier = getBallPlayer(false)
local shooterRoot = character:FindFirstChild("HumanoidRootPart")
if not ball or not shooterRoot or (currentCarrier ~= owner
and (ball.Position - shooterRoot.Position).Magnitude > 12) then return end
-- A valid new shooting wind-up starts a fresh flight. This also prevents
-- the previous shot's handled flag from suppressing a rapid next attempt.
blockFlightHandled = false
if kind == "pass" then
unresolvedWindupUntil[owner] = 0
if autoBlockPassesEnabled then inspectEnemyPass(owner, true) end
return
end
-- A new real shooting attempt after a fake is independent of that fake.
pumpSuppressedUntil[owner] = 0
confirmedPumpAt[owner] = 0
-- Until the game's 0.115-second branch resolves, velocity and possession
-- are deliberately forbidden from guessing whether this is a fake.
unresolvedWindupUntil[owner] = os.clock() + 0.22
-- The script cannot decide sooner: the shooter's own client does not
-- convert the shared wind-up into PumpFake until exactly 0.115 seconds.
task.delay(0.20, function()
if stopped or not autoBlockEnabled or state.serial ~= serial
or blockFlightHandled then return end
local okPlaying, playing = pcall(function() return track.IsPlaying end)
if not okPlaying or not playing then return end
-- Defensively scan active tracks too. This catches a PumpFake event
-- delivered on the same frame as the confirmation callback.
local okTracks, activeTracks = pcall(animator.GetPlayingAnimationTracks, animator)
if okTracks then
for _, activeTrack in activeTracks do
if animationKind(activeTrack) == "pump" then return end
end
end
-- A steal/pass during the confirmation window cancels it.
-- nil is allowed here because a very fast genuine shot may just have released.
local confirmedCarrier = getBallPlayer(false)
if confirmedCarrier and confirmedCarrier ~= owner then return end
triggerPreReleaseBlock(owner)
end)
end)
end)
end
local function watchEnemyPlayer(owner)
if owner == player then return end
if owner.Character then watchEnemyAnimations(owner, owner.Character) end
connect(owner.CharacterAdded, function(character)
watchEnemyAnimations(owner, character)
end)
end
for _, owner in Players:GetPlayers() do watchEnemyPlayer(owner) end
connect(Players.PlayerAdded, watchEnemyPlayer)
local function scoringHoop()
local team = player.Team
if not team or team.Name == "Visitor" then return nil end
local hoops = workspace:FindFirstChild("Hoops")
local teamHoop = hoops and hoops:FindFirstChild(team.Name)
local hoop = teamHoop and teamHoop:FindFirstChild("Hoop")
return hoop and hoop:IsA("BasePart") and hoop or nil
end
-- The visible/aiming Hoop is not the actual scoring volume. NewGoalCount is
-- the narrow channel immediately below the rim; GoalCount is the older,
-- broader fallback lower down. Passing through NewGoalCount's centre also
-- leaves the ball comfortably inside GoalCount's footprint.
local function scoringChannel(hoop)
local channel = hoop and hoop.Parent and hoop.Parent:FindFirstChild("NewGoalCount")
return channel and channel:IsA("BasePart") and channel or hoop
end
local function horizontalUnit(vector)
local flat = Vector3.new(vector.X, 0, vector.Z)
return flat.Magnitude > 0.001 and flat.Unit or nil
end
local function isAimingAtScoringHoop(hoop)
local camera = workspace.CurrentCamera
if not camera then return false end
local towardHoop = horizontalUnit(hoop.Position - camera.CFrame.Position)
local cameraDirection = horizontalUnit(camera.CFrame.LookVector)
if not towardHoop or not cameraDirection then return false end
return cameraDirection:Dot(towardHoop) >= math.cos(math.rad(AIM_CONE_DEGREES))
end
local function ballIsTravellingTowardHoop(ball, hoop)
local velocity = horizontalUnit(ball.AssemblyLinearVelocity)
local towardHoop = horizontalUnit(hoop.Position - ball.Position)
return velocity ~= nil and towardHoop ~= nil and velocity:Dot(towardHoop) > 0.55
end
local function applyNaturalArc(ball, hoop)
local channel = scoringChannel(hoop)
local destination = channel.Position - Vector3.new(0, 0.12, 0)
local displacement = destination - ball.Position
local distance = displacement.Magnitude
local flightTime
if distance >= 115 then
-- The game's own FullCourt ability uses this taller equation. The guarded
-- final passage below prevents its faster descent skipping the detector.
flightTime = math.log(3.5 + distance * 0.055)
else
flightTime = math.log(1.75 + distance * 0.045)
end
if flightTime <= 0 then return false end
-- Initial velocity under normal workspace gravity. The final-approach guard
-- below preserves vertical speed and only removes small horizontal drift.
ball.AssemblyLinearVelocity = displacement / flightTime
+ Vector3.new(0, workspace.Gravity * flightTime * 0.5, 0)
return true
end
-- Normal client prediction can drift slightly over a long flight. Once an
-- assisted shot is already descending through the rim area, continuously
-- remove only horizontal drift. At the final instant, make a tiny two-frame
-- downward passage through the thin scoring slab so a fast ball cannot tunnel
-- across it between server physics samples.
local function guideFinalApproach(ball, hoop, token)
task.spawn(function()
local deadline = os.clock() + 7
local possessionGrace = os.clock() + 0.35
while enabled and not stopped and token == shotToken and os.clock() < deadline do
if not ball.Parent or basketballValue.Value ~= ball then
return
end
if localPossessesBall() then
if os.clock() > possessionGrace then return end
task.wait()
continue
end
if not localOwnsBall(ball) then
task.wait()
continue
end
local channel = scoringChannel(hoop)
if not channel or not channel.Parent then return end
local position = ball.Position
local velocity = ball.AssemblyLinearVelocity
local height = position.Y - channel.Position.Y
local horizontalError = (Vector3.new(position.X, 0, position.Z)
- Vector3.new(channel.Position.X, 0, channel.Position.Z)).Magnitude
if height < -2 then return end
if velocity.Y < -1 and height > 0.25
and height <= FINAL_APPROACH_HEIGHT
and horizontalError <= FINAL_APPROACH_RADIUS then
-- Solve the current vertical motion for the time at which the ball
-- reaches the scoring slab, then make X/Z arrive at its centre too.
-- Re-evaluating each frame compensates for replication drift without
-- altering the shot's vertical arc.
local gravity = workspace.Gravity
local discriminant = velocity.Y * velocity.Y + 2 * gravity * height
local timeToChannel = (velocity.Y + math.sqrt(math.max(0, discriminant))) / gravity
if timeToChannel > 0.025 and timeToChannel < 0.65 then
local target = channel.Position - Vector3.new(0, 0.12, 0)
local horizontal = Vector3.new(target.X - position.X, 0, target.Z - position.Z)
/ timeToChannel
ball.AssemblyLinearVelocity = Vector3.new(horizontal.X, velocity.Y, horizontal.Z)
end
end
-- Far shots can move several studs per physics sample. Once the ball
-- is already on the qualified scoring line, explicitly show the server
-- a short downward crossing instead of allowing it to skip the slab.
if velocity.Y < -1 and height <= 2.4 and height > -0.8
and horizontalError <= 3.5 then
local rotation = ball.CFrame.Rotation
for _, yOffset in { 0.18, -0.18 } do
if stopped or token ~= shotToken or not ball.Parent
or basketballValue.Value ~= ball or not localOwnsBall(ball) then
return
end
ball.CFrame = CFrame.new(channel.Position + Vector3.new(0, yOffset, 0)) * rotation
ball.AssemblyLinearVelocity = Vector3.new(0, -18, 0)
RunService.Heartbeat:Wait()
end
-- Leave the upper channel on a natural downward path through the
-- game's larger legacy GoalCount detector as well.
local lower = hoop.Parent:FindFirstChild("GoalCount")
if lower and lower:IsA("BasePart") and ball.Parent and localOwnsBall(ball) then
local start = ball.Position
local fall = math.max(start.Y - lower.Position.Y, 0.1)
local exitY = -28
local exitTime = (exitY + math.sqrt(exitY * exitY + 2 * workspace.Gravity * fall))
/ workspace.Gravity
local horizontal = Vector3.new(lower.Position.X - start.X, 0, lower.Position.Z - start.Z)
/ math.max(exitTime, 0.05)
ball.AssemblyLinearVelocity = Vector3.new(horizontal.X, exitY, horizontal.Z)
end
return
end
task.wait()
end
end)
end
local function armShot()
if not enabled or stopped then return end
local hoop = scoringHoop()
if not hoop then return end
-- Capture intent before the game's aim assist turns the character. If the
-- scoring basket is not actually near the camera crosshair, leave the shot
-- fully natural—including shots aimed at the defensive basket or a wall.
if not isAimingAtScoringHoop(hoop) then return end
shotToken += 1
local token = shotToken
qualifiedToken = token
shotArmedUntil = os.clock() + 1.6
task.spawn(function()
local deadline = os.clock() + 1.4
while enabled and not stopped and token == shotToken and os.clock() < deadline do
-- Prefer the BallService.Throw acknowledgment below. This delayed path
-- is only a fallback if that service signal is unavailable.
if ballService and os.clock() < deadline - 0.75 then
task.wait()
continue
end
local ball = basketballValue.Value
if ball and ball:IsA("BasePart") and ball.Parent
and localOwnsBall(ball) and not localPossessesBall()
and ball.AssemblyLinearVelocity.Magnitude > 6 then
-- Resolve again in case the player's team changed during the wind-up.
local currentHoop = scoringHoop()
if currentHoop and qualifiedToken == token and appliedToken ~= token
and ballIsTravellingTowardHoop(ball, currentHoop)
and applyNaturalArc(ball, currentHoop) then
appliedToken = token
guideFinalApproach(ball, currentHoop, token)
return
end
end
task.wait()
end
end)
end
-- The possession remote reaches every client and is the reliable defender-side
-- signal. It exposes both ordinary carrier>nil releases and the carrier>teammate
-- direct handoff used by some short/post-pump passes.
if ballState and type(ballState.onPlayerPossessingBallChangedConnect) == "function" then
local previousPossessor = getBallPlayer(false)
local ok, disconnect = pcall(ballState.onPlayerPossessingBallChangedConnect, ballState,
function(currentPossessor)
local releasedBy = previousPossessor
previousPossessor = currentPossessor
if releasedBy and currentPossessor and currentPossessor ~= releasedBy
and releasedBy.Team and currentPossessor.Team
and releasedBy.Team == currentPossessor.Team then
-- Short/fast passes can replicate as passer>receiver directly, with
-- neither a nil possession gap nor a visible pass animation.
inspectEnemyPass(releasedBy, true)
elseif releasedBy and currentPossessor == nil then
inspectEnemyRelease(releasedBy)
confirmedPumpAt[releasedBy] = 0
pumpSuppressedUntil[releasedBy] = 0
unresolvedWindupUntil[releasedBy] = 0
elseif releasedBy and currentPossessor ~= releasedBy then
-- A steal or other cross-team transfer ends the old carrier's pump.
confirmedPumpAt[releasedBy] = 0
pumpSuppressedUntil[releasedBy] = 0
unresolvedWindupUntil[releasedBy] = 0
end
end, false)
if ok and type(disconnect) == "function" then
table.insert(cleanupCallbacks, disconnect)
end
end
-- Continuous fallback: defender replication can deliver the velocity well
-- after the release event. Polling the already-replicated state removes that
-- race without firing or hooking any remotes.
do
local observedPossessor = getBallPlayer(false)
local lastCarrier = observedPossessor
connect(RunService.Heartbeat, function()
local currentPossessor = getBallPlayer(false)
if currentPossessor then
if currentPossessor ~= observedPossessor then
blockFlightHandled = false
end
observedPossessor = currentPossessor
lastCarrier = currentPossessor
return
end
if observedPossessor then
lastCarrier = observedPossessor
observedPossessor = nil
end
if not autoBlockEnabled or blockFlightHandled or stopped then return end
local ball = basketballValue.Value
if not ball or not ball:IsA("BasePart") or not ball.Parent then return end
-- Only an actual carrier may be treated as the shooter. A nearest-player
-- guess can become the receiver during a pass and is intentionally avoided.
local shooter = getBallPlayer(true) or lastCarrier
if shooter and triggerAutoBlock(shooter, ball) then
blockFlightHandled = true
end
end)
end
-- Check every physics frame so the attempt lands immediately after the game's
-- real dribble window plus the user's adjustable safety delay.
connect(RunService.Heartbeat, tryAutoSteal)
-- The server first acknowledges a valid shot by firing BallService.Throw with
-- its chosen landing point. The native client then writes that trajectory.
-- Since this connection is added later, waiting one Heartbeat guarantees our
-- one-time natural arc is applied after that native write instead of before it.
if ballService and ballService.Throw then
connect(ballService.Throw, function(serverTarget)
inspectEnemyRelease(getBallPlayer(true))
if not enabled or stopped or serverTarget == nil
or os.clock() > shotArmedUntil then return end
local token = shotToken
task.spawn(function()
RunService.Heartbeat:Wait()
if not enabled or stopped or token ~= shotToken or qualifiedToken ~= token
or appliedToken == token then return end
local ball = basketballValue.Value
local hoop = scoringHoop()
if ball and ball:IsA("BasePart") and ball.Parent and hoop
and localOwnsBall(ball) and not localPossessesBall()
and ballIsTravellingTowardHoop(ball, hoop)
and applyNaturalArc(ball, hoop) then
appliedToken = token
guideFinalApproach(ball, hoop, token)
end
end)
end)
end
-- Pass is replicated to every client before the pass physics settle. The
-- game's last-possession record identifies the passer; the current/nearest
-- player may already be the receiver and must never be substituted.
if ballService and ballService.Pass then
connect(ballService.Pass, function()
if not autoBlockPassesEnabled then return end
local passer = getBallPlayer(true)
inspectEnemyPass(passer, true)
end)
end
-- Unlike animation guessing, this is the same replicated cue the game's own
-- DefenseController uses to animate an enemy's committed steal lunge.
if ballService and ballService.Steal then
connect(ballService.Steal, function(thiefCharacter, targetCFrame)
reactToEnemySteal(thiefCharacter, targetCFrame)
end)
end
local function stop()
if stopped then return end
stopped = true
enabled = false
autoBlockEnabled = false
autoStealEnabled = false
autoDribbleEnabled = false
releaseAutoDribbleKey()
shotToken += 1
blockDetectionToken += 1
for _, connection in ipairs(connections) do
pcall(function() connection:Disconnect() end)
end
for _, callback in ipairs(cleanupCallbacks) do
pcall(callback)
end
if Rayfield then
pcall(function() Rayfield:Destroy() end)
end
sharedEnvironment.__BasketNaturalAim = nil
end
sharedEnvironment.__BasketNaturalAim = { Stop = stop }
connect(UserInputService.InputBegan, function(input, processed)
if processed or not isShootInput(input) then return end
armShot()
end)
local shotToggle
local blockToggle
local passToggle
local stealToggle
local dribbleToggle
local function setShotAssist(value, announce)
enabled = value == true
shotToken += 1
if announce then
notify(enabled and "Perfect Shot enabled" or "Perfect Shot disabled — shots are untouched")
end
end
local function setAutoBlock(value, announce)
autoBlockEnabled = value == true
blockDetectionToken += 1
blockFlightHandled = false
if announce then
notify(autoBlockEnabled and "Auto Block enabled" or "Auto Block disabled")
end
end
local function setAutoSteal(value, announce)
autoStealEnabled = value == true
nextStealAt = 0
clearDribbleStealArm()
if announce then
notify(autoStealEnabled and "Auto Steal enabled" or "Auto Steal disabled")
end
end
local function setAutoDribble(value, announce)
autoDribbleEnabled = value == true
lastAutoDribbleAt = 0
if not autoDribbleEnabled then releaseAutoDribbleKey() end
if announce then
notify(autoDribbleEnabled and "Auto Dribble enabled" or "Auto Dribble disabled")
end
end
local rayfieldOk, rayfieldResult = pcall(function()
return loadstring(game:HttpGet("https://sirius.menu/rayfield"))()
end)
if rayfieldOk and rayfieldResult then
Rayfield = rayfieldResult
local Window = Rayfield:CreateWindow({
Name = "Basket Assist",
Icon = "dribbble",
LoadingTitle = "Basket Assist",
LoadingSubtitle = "Natural shots + reactive defense",
ShowText = "Basket",
Theme = "Default",
-- Mouse clicks use KeyCode.Unknown. A harmless keyboard-only placeholder
-- prevents Rayfield's native window toggle from reacting to every click.
ToggleUIKeybind = Enum.KeyCode.World95,
DisableRayfieldPrompts = false,
DisableBuildWarnings = false,
ConfigurationSaving = {
Enabled = true,
FolderName = "BasketAssist",
FileName = "Settings"
},
Discord = { Enabled = false },
KeySystem = false
})
local ShotTab = Window:CreateTab("Perfect Shot", "crosshair")
ShotTab:CreateSection("Automatic Swish")
shotToggle = ShotTab:CreateToggle({
Name = "Perfect Shot Assist",
CurrentValue = true,
Flag = "PerfectShotEnabled",
Callback = function(value)
setShotAssist(value, true)
end
})
ShotTab:CreateKeybind({
Name = "Toggle Perfect Shot",
CurrentKeybind = "P",
HoldToInteract = false,
Flag = "PerfectShotKeybind",
Callback = function()
setShotAssist(not enabled, true)
if shotToggle then pcall(function() shotToggle:Set(enabled) end) end
end
})
ShotTab:CreateLabel("Only assists when you genuinely aim toward the enemy basket.", "target")
ShotTab:CreateLabel("Shots toward your own net or elsewhere remain untouched.", "shield-check")
local DefenseTab = Window:CreateTab("Auto Block", "shield")
DefenseTab:CreateSection("Reactive Defense")
blockToggle = DefenseTab:CreateToggle({
Name = "Auto Block Enemy Shots",
CurrentValue = true,
Flag = "AutoBlockEnabled",
Callback = function(value)
setAutoBlock(value, true)
end
})
passToggle = DefenseTab:CreateToggle({
Name = "Auto Block Enemy Passes",
CurrentValue = true,
Flag = "AutoBlockPassesEnabled",
Callback = function(value)
autoBlockPassesEnabled = value == true
notify(autoBlockPassesEnabled
and "Pass blocking enabled"
or "Pass blocking disabled")
end
})
DefenseTab:CreateKeybind({
Name = "Toggle Auto Block",
CurrentKeybind = "B",
HoldToInteract = false,
Flag = "AutoBlockKeybind",
Callback = function()
setAutoBlock(not autoBlockEnabled, true)
if blockToggle then pcall(function() blockToggle:Set(autoBlockEnabled) end) end
end
})
DefenseTab:CreateSlider({
Name = "Maximum Reaction Range",
Range = { 5, 50 },
Increment = 1,
Suffix = " studs",
CurrentValue = 25,
Flag = "AutoBlockRange",
Callback = function(value)
configuredBlockRange = tonumber(value) or 25
end
})
DefenseTab:CreateLabel("The game still enforces its real 25-stud range and legitimate boosts.", "ruler")
DefenseTab:CreateLabel("Waits for the real shot decision; PumpFake and PumpFakeIdle never trigger a jump.", "shield-check")
DefenseTab:CreateLabel("Passes trigger only when you are close to the passer, never merely the receiver.", "activity")
local StealTab = Window:CreateTab("Auto Steal", "hand")
StealTab:CreateSection("Protected Timing")
stealToggle = StealTab:CreateToggle({
Name = "Auto Steal Ball Carriers",
CurrentValue = true,
Flag = "AutoStealEnabled",
Callback = function(value)
setAutoSteal(value, true)
end
})
StealTab:CreateKeybind({
Name = "Toggle Auto Steal",
CurrentKeybind = "N",
HoldToInteract = false,
Flag = "AutoStealKeybind",
Callback = function()
setAutoSteal(not autoStealEnabled, true)
if stealToggle then pcall(function() stealToggle:Set(autoStealEnabled) end) end
end
})
StealTab:CreateSlider({
Name = "Activation Range",
Range = { 6, 18 },
Increment = 1,
Suffix = " studs",
CurrentValue = 16,
Flag = "AutoStealRange",
Callback = function(value)
configuredStealRange = tonumber(value) or 16
end
})
StealTab:CreateInput({
Name = "First Steal Check (ms)",
CurrentValue = "190",
PlaceholderText = "Enter 0-500",
RemoveTextAfterFocusLost = false,
Flag = "AutoStealFirstCheckMs",
Callback = function(value)
local milliseconds = tonumber(value)
if milliseconds then
configuredStealWaitMs = math.clamp(math.round(milliseconds), 0, 500)
end
end
})
StealTab:CreateLabel("Only a real enemy dribble arms one steal attempt.", "scan")
StealTab:CreateLabel("Each accepted chained dribble resets the timer; rejected Q spam is ignored.", "shield-check")
StealTab:CreateLabel("First check: typed value. Final check: exactly 3 ms later.", "activity")
StealTab:CreateLabel("Default 190 ms → final steal checkpoint at 193 ms.", "timer")
local DribbleTab = Window:CreateTab("Auto Dribble", "shield-check")
DribbleTab:CreateSection("Steal Protection")
dribbleToggle = DribbleTab:CreateToggle({
Name = "Dribble Against Enemy Steals",
CurrentValue = true,
Flag = "AutoDribbleEnabled",
Callback = function(value)
setAutoDribble(value, true)
end
})
DribbleTab:CreateKeybind({
Name = "Toggle Auto Dribble",
CurrentKeybind = "M",
HoldToInteract = false,
Flag = "AutoDribbleKeybind",
Callback = function()
setAutoDribble(not autoDribbleEnabled, true)
if dribbleToggle then pcall(function() dribbleToggle:Set(autoDribbleEnabled) end) end
end
})
DribbleTab:CreateSlider({
Name = "Confirmed Steal Range",
Range = { 8, 26 },
Increment = 1,
Suffix = " studs",
CurrentValue = 24,
Flag = "AutoDribbleReactionRange",
Callback = function(value)
configuredAutoDribbleRange = tonumber(value) or 24
end
})
DribbleTab:CreateLabel("Triggers only after a confirmed enemy steal event or StealL/StealR animation.", "scan")
DribbleTab:CreateLabel("Uses your configured keyboard Dribble bind and never reacts to proximity alone.", "shield-check")
local SettingsTab = Window:CreateTab("Settings", "settings")
SettingsTab:CreateSection("Interface")
SettingsTab:CreateKeybind({
Name = "Show / Hide Window",
CurrentKeybind = "RightShift",
HoldToInteract = false,
Flag = "WindowKeybind",
Callback = function()
local visible = true
pcall(function() visible = Rayfield:IsVisible() end)
pcall(function() Rayfield:SetVisibility(not visible) end)
end
})
SettingsTab:CreateLabel("Only the selected keyboard key can show or hide this window.", "keyboard")
pcall(function() Rayfield:LoadConfiguration() end)
sharedEnvironment.__BasketNaturalAim.Rayfield = Rayfield
Rayfield:Notify({
Title = "Basket Assist ready",
Content = "Perfect Shot, Auto Block, Auto Steal, and Auto Dribble are enabled.",
Duration = 5,
Image = "shield-check"
})
else
warn("Basket Assist: Rayfield failed to load: " .. tostring(rayfieldResult))
notify("Rayfield failed to load; shot and block assistance remain enabled")
end



use this to get instant ban for 3 days
@Hu2s3y I'm sorry to hear that. It's probably because of the executor. You need one that isn't as easily detected. I'm using Pot*ium, and I haven't had any issues so far.
Great script. Didn't have any issues with it
theres really only one thing you need to fix is the lock on its very blatent
nvm the perfect shoots really *ing blatent
THIS **** GOT PATCHED