local h=game:GetService("\082\117\110\083\101\114\118\105\099\101")local C=game:GetService("\080\108\097\121\101\114\115")local I=game:GetService("\067\111\108\108\101\099\116\105\111\110\083\101\114\118\105\099\101")local F=require(script.BouncePlatform)local b=require(script.BouncePlatformConstants)local O=b.BOUNCY_TAG local y=false local S=2 local n={Vector2.new(-1,.5),Vector2.new(0,.5);Vector2.new(1,.5),Vector2.new(0,0);Vector2.new(-1,-0.5),Vector2.new(0,-0.5),Vector2.new(1,-0.5)}local Q=C.LocalPlayer local function f(h)assert(h~=nil,"\080\097\114\116 \101\120\112\101\099\116\101\100 \103\111\116 \110\105\108")assert(typeof(h)=="\073\110\115\116\097\110\099\101","\101\120\112\101\099\116\101\100 \073\110\115\116\097\110\099\101 \103\111\116 "..typeof(h))assert(h.ClassName=="\077\101\115\104\080\097\114\116"or h.ClassName=="\080\097\114\116","\101\120\112\101\099\116\101\100 \077\101\115\104\080\097\114\116 \111\114 \080\097\114\116 \103\111\116 "..h.ClassName)end local function T(h)local C=0 for h,I in pairs(h:GetDescendants())do if I:IsA("\066\097\115\101\080\097\114\116")then C+=I:GetMass()end end return C end local s={ActiveBouncePlatforms={}}function s.new(h,C)f(h)local I=F.new(h,C)s.ActiveBouncePlatforms[I.platform]=I if y then I:_debug(I)end return I end function s._detect(I,h,C)local F={}local b=Q.Character.PrimaryPart.CFrame.LookVector local O=Q.Character.PrimaryPart.CFrame.RightVector for I,y in pairs(n)do local S=O*y.X local n=b*y.Y local f=(Q.Character.PrimaryPart.Position+n)+S local T=workspace:Raycast(f,Vector3.new(0,-C,0),h)if T and T.Instance then table.insert(F,T)end end return F end function s._checkForBouncePlatforms(h)if not Q.Character then return end if not Q.Character.PrimaryPart then return end local C=Q.Character:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if not C then return end local F=C.HipHeight+S local b=RaycastParams.new()b.FilterDescendantsInstances=I:GetTagged(O)b.FilterType=Enum.RaycastFilterType.Whitelist local y=s:_detect(b,F)for h,C in pairs(y)do local I=C.Instance local F=s:getBouncePlatformFromInstance(I)if not F or F and not F.enabled then return end local b=os.clock()if b-F.lastBouncedAt>=F.debounce then F.lastBouncedAt=b Q.Character.PrimaryPart.Velocity=Vector3.new(0,0,0)local h=(F.bounceVector*F.bounceForce)*Q.Character.PrimaryPart.AssemblyMass Q.Character.PrimaryPart:ApplyImpulse(h)F:_onBounced()return end end end function s.enable(C)if s.HeartbeatConnection then s:disable()end s.HeartbeatConnection=h.Heartbeat:Connect(function()s:_checkForBouncePlatforms()end)end function s.disable(h)if s.HeartbeatConnection then s.HeartbeatConnection:Disconnect()s.HeartbeatConnection=nil end end function s.getBouncePlatformFromInstance(C,h)return s.ActiveBouncePlatforms[h]end function s.getActiveBouncePlatforms(h)return s.ActiveBouncePlatforms end return s


local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")

local BouncePlatform = require(script.BouncePlatform)
local BouncePlatformConstants = require(script.BouncePlatformConstants)

local BOUNCY_TAG = BouncePlatformConstants.BOUNCY_TAG
local DEBUG = false
local HIPHEIGHT_BUFFER = 2
local RAYCAST_DETECTION_OFFSETS = {
	Vector2.new(-1, 0.5),
	Vector2.new(0, 0.5),
	Vector2.new(1, 0.5),
	Vector2.new(0, 0),
	Vector2.new(-1, -0.5),
	Vector2.new(0, -0.5),
	Vector2.new(1, -0.5),
}

local player = Players.LocalPlayer

local function _assert(part)
	assert(part ~= nil, "Part expected got nil")
	assert(typeof(part) == "Instance", "expected Instance got " .. typeof(part))
	assert(part.ClassName == "MeshPart" or part.ClassName == "Part", "expected MeshPart or Part got " .. part.ClassName)
end

local function getModelMass(model)
	local totalMass = 0

	for _, basepart in pairs(model:GetDescendants()) do
		if basepart:IsA("BasePart") then
			totalMass += basepart:GetMass()
		end
	end

	return totalMass
end

local BouncePlatforms = {
	ActiveBouncePlatforms = {},
}

function BouncePlatforms.new(part, config)
	_assert(part)

	local bouncePlatform = BouncePlatform.new(part, config)

	BouncePlatforms.ActiveBouncePlatforms[bouncePlatform.platform] = bouncePlatform

	if DEBUG then
		bouncePlatform:_debug(bouncePlatform)
	end

	return bouncePlatform
end

function BouncePlatforms:_detect(raycastParams, length)
	local raycastResults = {}

	local lookVector = player.Character.PrimaryPart.CFrame.LookVector
	local rightVector = player.Character.PrimaryPart.CFrame.RightVector

	for _, cell in pairs(RAYCAST_DETECTION_OFFSETS) do
		local offsetX = rightVector * cell.X
		local offsetZ = lookVector * cell.Y
		local origin = (player.Character.PrimaryPart.Position + offsetZ + offsetX)
		local raycastResult = workspace:Raycast(origin, Vector3.new(0, -length, 0), raycastParams)

		if raycastResult and raycastResult.Instance then
			table.insert(raycastResults, raycastResult)
		end
	end

	return raycastResults
end

function BouncePlatforms:_checkForBouncePlatforms()
	if not player.Character then
		return
	end

	if not player.Character.PrimaryPart then
		return
	end

	local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
	if not humanoid then
		return
	end
	
	local length = humanoid.HipHeight + HIPHEIGHT_BUFFER
	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = CollectionService:GetTagged(BOUNCY_TAG)
	raycastParams.FilterType = Enum.RaycastFilterType.Whitelist

	local raycastResults = BouncePlatforms:_detect(raycastParams, length)

	for _, raycastResult in pairs(raycastResults) do
		local platformInstance = raycastResult.Instance

		local bouncePlatform = BouncePlatforms:getBouncePlatformFromInstance(platformInstance)

		if not bouncePlatform or (bouncePlatform and not bouncePlatform.enabled) then
			return
		end

		local currentTime = os.clock()

		if currentTime - bouncePlatform.lastBouncedAt >= bouncePlatform.debounce then
			bouncePlatform.lastBouncedAt = currentTime

			player.Character.PrimaryPart.Velocity = Vector3.new(0, 0, 0)

			local momentum = bouncePlatform.bounceVector * bouncePlatform.bounceForce * player.Character.PrimaryPart.AssemblyMass

			player.Character.PrimaryPart:ApplyImpulse(momentum)

			bouncePlatform:_onBounced()

			return
		end
	end
end

function BouncePlatforms:enable()
	if BouncePlatforms.HeartbeatConnection then
		BouncePlatforms:disable()
	end

	BouncePlatforms.HeartbeatConnection = RunService.Heartbeat:Connect(function()
		BouncePlatforms:_checkForBouncePlatforms()
	end)
end

function BouncePlatforms:disable()
	if BouncePlatforms.HeartbeatConnection then
		BouncePlatforms.HeartbeatConnection:Disconnect()
		BouncePlatforms.HeartbeatConnection = nil
	end
end

function BouncePlatforms:getBouncePlatformFromInstance(platform)
	return BouncePlatforms.ActiveBouncePlatforms[platform]
end

function BouncePlatforms:getActiveBouncePlatforms()
	return BouncePlatforms.ActiveBouncePlatforms
end

return BouncePlatforms
