Raycasting bouncing physics not working properly

Hey, just wanted to ask a question on raycasting and the creation of bouncing physics. Now I took inspiration from the Fastcast module (wanted to design my own one for flexibility)

My issue currently is that the projectile doesn’t seem to be bouncing properly, I tried copying the Fastcast module and taking their approach but I am getting stuck on the actual ray piercing. Currently, what happens is, whenever the projectile is fired right next to the character, the pojectile appears to be bouncing correctly (although only once), here is a clip:
https://gyazo.com/eaae6c294d81814a8605ed0c826cc086

But whenever I start to aim further away from the character, it sort of just teleports the projectile without creating a smooth animation:
https://gyazo.com/c5ba71e1c077891389a67215e2225304

So I am not sure if the approach I took was wrong, whether my calculations were wrong, just need some help and advice on this, here’s my code:

-- other -- 
local replicatedStorage = game:GetService('ReplicatedStorage')

local replicatedAssets = replicatedStorage:WaitForChild('Assets')


local ballLauncher = {

	image = 'rbxassetid://13977761430',
	name = 'ball launcher',

	combatType = 'ranged', -- key to allow system to check what type of weapon use --
	cooldown = 1,
	
	Gravity = 1,
	Whitelist = {},
	Wind = Vector3.new(0,0,0),
	DespawnTime = 7 ,
	Visualize = true,
	Params = RaycastParams.new(),
	
	raycast_settings = {
		acceleration = Vector3.new(0, -100, 0);
		force = 125;
		bounce = .1 -- Default height is .1
	};

}



function ballLauncher:getPositionAtTime(t, origin, initialVelocity, acceleration)
	local force = Vector3.new((acceleration.X * time^2) / 2,(acceleration.Y * time^2) / 2, (acceleration.Z * time^2) / 2)
	return origin + (initialVelocity * t) + force
end








function ballLauncher:new(character)
	
	local newProjectile = {}
	
	newProjectile.Gravity = self.Gravity
	newProjectile.Whitelist = self.Whitelist
	newProjectile.Wind = self.Wind
	newProjectile.DespawnTime = self.DespawnTime
	newProjectile.Visualize = self.Visualize
	newProjectile.RaycastParams = self.Params
	
	
	newProjectile.RaycastParams.FilterType = Enum.RaycastFilterType.Exclude
	newProjectile.RaycastParams.FilterDescendantsInstances = {character}
	
	function newProjectile:cast(start, destination, force, item)
		
		-- origin is start --
		local conversion = 196.2 / 9.8;
		local maxTime = 3;
		
		local direction = destination.Unit;
		local speed = force;
		local initialVelocity = direction * speed;
		local origin = start;
		
		local gravity = self.Gravity;
		local acceleration = Vector3.new(self.Wind.X, self.Wind.Y - self.Gravity * 9.8, self.Wind.Z) * conversion
		local raycastParams = self.Params;
		
		local segmentTimeLength;
		local connection;
		
		-- in function variables --
		local lastPosition;
		local rayResult;
		local visualPart;
		
		-- static data types --
		local completed = false;
		local simTime = 0;
		
		
		
		-- functions --
		local function reflect(surfaceNormal, bulletNormal)
			return bulletNormal - (2 * bulletNormal:Dot(surfaceNormal) * surfaceNormal)
		end
		
		
		
		local function getPositionAtTime(timeAmount, origin, initialVelocity, acceleration)
			local force = Vector3.new((acceleration.X * timeAmount^2) / 2,(acceleration.Y * timeAmount^2) / 2, (acceleration.Z * timeAmount^2) / 2)
			return origin + (initialVelocity * timeAmount) + force
		end
		
		
		local function getVelocityAtTime(timeAmount, initialVelocity, acceleration)
			return initialVelocity + acceleration * timeAmount
		end
		
		
		
		local function setVelocity(vel)
			initialVelocity = vel
		end
		
		
		local function setPosition(val)
			origin = val
		end
		
		
		
		local function performBounce(rayResult, segmentVelocity)
			local position = rayResult.Position
			local normal = rayResult.Normal
			local newNormal = reflect(normal, segmentVelocity.Unit)

			setVelocity(newNormal * segmentVelocity.Magnitude)
			setPosition(position)

		end
		
		
		
		local function lengthChanged(segmentOrigin, segmentDirection, length, segmentVelocity)
			return segmentOrigin + (segmentDirection * length)
		end
		
		
		
		
		
		connection = game:GetService('RunService').Heartbeat:Connect(function(deltaTime)
			
		
			
			-- last --
			local lastTarget = getPositionAtTime(simTime, origin, initialVelocity, acceleration)
			local lastVelocity = getVelocityAtTime(simTime, initialVelocity, acceleration)

			simTime += deltaTime

			-- new --
			local currentTarget = getPositionAtTime(simTime, origin, initialVelocity, acceleration)
			local segmentVelocity = getVelocityAtTime(simTime, initialVelocity, acceleration)
			local displacement = currentTarget - lastTarget
			
			

			-- calcs --
			local rayDirection = displacement.Unit * segmentVelocity.Magnitude * deltaTime
			local rayResult = workspace:Raycast(lastTarget, rayDirection, raycastParams)
			
			local point = currentTarget
			if rayResult then
				point = rayResult.Position
			end	
			local rayDisplacement = (point - lastTarget).Magnitude
			

			-- length changed --
			local finalPosition = lengthChanged(lastTarget, rayDirection.Unit, rayDisplacement, segmentVelocity)
	
			if rayResult and rayResult.Instance ~= visualPart then
				performBounce(rayResult, segmentVelocity)
			end
	
	
			
			if self.Visualize then

				if not visualPart then
					visualPart = replicatedAssets.Projectile:Clone()
					visualPart.Parent = game.Workspace
				end

				visualPart.Position = finalPosition

			end
			
			
	
			
			
			
			
			-- end if time runs out --
			if simTime >= maxTime then
				print('ended')
				connection:Disconnect()
			end
			
		end)
		
		
		
		
	end
	
	
	return newProjectile
	
end



return ballLauncher

I understand it seems all over the place, wanted to get to the bottom of this issue before fixing it up lol