local Table1 = workspace.Tables.Table1
local HitTrajectory = Table1.Guides.HitTrajectory
-- Enhanced configuration
local config = {
maxSegments = 8,
segmentLength = 6,
maxBounces = 3,
ballRadius = 1.2, -- Approximate ball radius for collision
powerMultiplier = 1.5, -- Adjust trajectory length based on power
fadeIntensity = true, -- Whether segments fade with distance
showBouncePoints = true -- Show markers where trajectory bounces
}
local trajectoryParts = {}
local bounceMarkers = {}
local currentPower = 1.0
-- Material colors for visual appeal
local colors = {
primary = BrickColor.new("Bright green"),
secondary = BrickColor.new("Bright blue"),
bounce = BrickColor.new("Bright yellow"),
fade = BrickColor.new("Dark green")
}
-- Physics constants
local BALL_MASS = 1.0
local FRICTION = 0.98
local MIN_VELOCITY = 0.1
-- Create a better looking trajectory segment
local function createTrajectorySegment(position, direction, length, alpha, isBounce)
local part = Instance.new("Part")
part.Size = Vector3.new(0.15, 0.15, length)
part.CFrame = CFrame.lookAt(position, position + direction) * CFrame.new(0, 0, -length / 2)
part.Anchored = true
part.CanCollide = false
part.CastShadow = false
-- Visual customization
local material = isBounce and Enum.Material.Neon or Enum.Material.SmoothPlastic
part.Material = material
local color = isBounce and colors.bounce or colors.primary
if config.fadeIntensity then
-- Blend towards fade color based on alpha
local lerpValue = 1 - alpha
part.BrickColor = color:lerp(colors.fade, lerpValue)
else
part.BrickColor = color
end
part.Transparency = isBounce and 0.2 or alpha * 0.5
part.Parent = workspace
return part
end
-- Create bounce marker
local function createBounceMarker(position)
if not config.showBouncePoints then return end
local marker = Instance.new("Part")
marker.Size = Vector3.new(0.8, 0.8, 0.8)
marker.Shape = Enum.PartType.Ball
marker.CFrame = CFrame.new(position)
marker.BrickColor = colors.bounce
marker.Material = Enum.Material.Neon
marker.Anchored = true
marker.CanCollide = false
marker.Transparency = 0.3
marker.Parent = workspace
return marker
end
-- Clear all trajectory visuals
local function clearTrajectory()
for _, part in pairs(trajectoryParts) do
if part and part.Parent then
part:Destroy()
end
end
trajectoryParts = {}
for _, marker in pairs(bounceMarkers) do
if marker and marker.Parent then
marker:Destroy()
end
end
bounceMarkers = {}
end
-- Raycast with ball collision detection
local function raycastWithBallCollision(startPos, direction, ignoreList)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Blacklist
params.FilterDescendantsInstances = ignoreList or {}
params.IgnoreWater = true
local ray = workspace:Raycast(startPos, direction * config.segmentLength, params)
if ray then
-- Check if we hit a ball (approximate by size and name)
local hit = ray.Instance
if hit and (hit.Name:lower():find("ball") or hit.Size.Magnitude < 3) then
-- Calculate reflection for ball collision (simplified)
local normal = (ray.Position - hit.Position).Unit
local reflection = direction - 2 * direction:Dot(normal) * normal
return ray.Position, reflection, true, hit
else
-- Standard wall collision
local reflection = direction - 2 * direction:Dot(ray.Normal) * ray.Normal
return ray.Position, reflection, false, hit
end
end
return startPos + direction * config.segmentLength, direction, false, nil
end
-- Calculate cue ball physics
local function calculateBallPhysics(startPos, direction, power)
local velocity = direction * power * config.powerMultiplier
local positions = {}
local bounces = 0
local currentPos = startPos
local currentVel = velocity
for i = 1, config.maxSegments do
if bounces >= config.maxBounces then break end
-- Apply friction
currentVel = currentVel * FRICTION
-- Stop if velocity is too low
if currentVel.Magnitude < MIN_VELOCITY then break end
local nextPos, newDirection, didBounce, hitObject = raycastWithBallCollision(
currentPos,
currentVel.Unit,
{workspace.Tables.Table1} -- Ignore table parts
)
-- Calculate segment length
local segmentLength = (nextPos - currentPos).Magnitude
-- Create trajectory segment with fade effect
local alpha = (i - 1) / config.maxSegments
local segment = createTrajectorySegment(currentPos, currentVel.Unit, segmentLength, alpha, didBounce)
table.insert(trajectoryParts, segment)
if didBounce then
bounces = bounces + 1
if config.showBouncePoints then
local marker = createBounceMarker(nextPos)
table.insert(bounceMarkers, marker)
end
-- If we hit a ball, we might want to stop or change trajectory significantly
if hitObject and hitObject.Name:lower():find("ball") then
-- For ball hits, we could add special effects or stop the trajectory
currentVel = currentVel * 0.7 -- Reduce velocity after ball collision
end
end
currentPos = nextPos
currentVel = newDirection * currentVel.Magnitude
table.insert(positions, currentPos)
end
return positions
end
-- Main trajectory update function
local function updateTrajectory()
clearTrajectory()
local startPos = HitTrajectory.Position
local direction = HitTrajectory.CFrame.LookVector.Unit
-- Calculate trajectory with physics
calculateBallPhysics(startPos, direction, currentPower)
end
-- Power control functions
local function setPower(power)
currentPower = math.clamp(power, 0.1, 2.0)
updateTrajectory()
end
local function increasePower()
setPower(currentPower + 0.1)
end
local function decreasePower()
setPower(currentPower - 0.1)
end
-- Connect events
HitTrajectory:GetPropertyChangedSignal("CFrame"):Connect(updateTrajectory)
-- Optional: Add power control bindings (uncomment if you want keyboard controls)
-- game:GetService("UserInputService").InputBegan:Connect(function(input)
-- if input.KeyCode == Enum.KeyCode.E then
-- increasePower()
-- elseif input.KeyCode == Enum.KeyCode.Q then
-- decreasePower()
-- end
-- end)
-- Initialize
updateTrajectory()
-- Return API for external control
return {
updateTrajectory = updateTrajectory,
setPower = setPower,
increasePower = increasePower,
decreasePower = decreasePower,
clearTrajectory = clearTrajectory,
getCurrentPower = function() return currentPower end
}