--<< Returns hit, position, normal, time >>


function raycast(model, start, vector, brickFunction)
	local hit, normal, time = raycastRecursive(model, start, vector, brickFunction, vector.unit, dot(start, vector.unit))
	if (dot(normal, vector) > 0) then
		normal = -normal
	end
	return hit, start + time * vector, normal.unit, time
end
function raycastRecursive(model, start, vector, brickFunction, unitVec, startDist)
	if (model.className == "Part") or (model.className == "Seat") or (model.className =="SpawnLocation") then
		local range = model.Size.magnitude / 2
		local dist = dot(model.Position, unitVec) - startDist
		if (dist + range > 0) and (dist - range < vector.magnitude) and ((dist * unitVec + start - model.Position).magnitude < range) and brickFunction(model) then
			local halfSize = model.Size / 2
			if (model.Shape == Enum.PartType.Ball) then
				local time, timeMax = getLineSphereCollide(start, vector, model.Position, halfSize.x)
				if (time < 1) and (time >= 0) then
					return model, (time * vector + start - model.Position), time
				else
					return nil, Vector3.new(0, 0, 0), 1
				end
			elseif (model.Shape == Enum.PartType.Block) then
				local time = 1
				local cf = model.CFrame - model.Position
				local xvec = cf * Vector3.new(1, 0, 0)
				local yvec = cf * Vector3.new(0, 1, 0)
				local zvec = cf * Vector3.new(0, 0, 1)
				local xspd = -dot(xvec, vector)
				local yspd = -dot(yvec, vector)
				local zspd = -dot(zvec, vector)
				local xmin, xmax, ymin, ymax, zmin, zmax = -1
				local dotProd = dot(xvec, start - model.Position)
				if (xspd ~= 0) then
					xmin = (dotProd - halfSize.x) / xspd
					xmax = (dotProd + halfSize.x) / xspd
					if (xmax < xmin) then
						local swap = xmin
						xmin = xmax
						xmax = swap
					end
				else
					if (math.abs(dotProd) < halfSize.x) then
						xmax = 1
						xmin = 0
					else
						return nil, Vector3.new(0, 0, 0), 1
					end
				end
				local dotProd = dot(yvec, start - model.Position)
				if (yspd ~= 0) then
					ymin = (dotProd - halfSize.y) / yspd
					ymax = (dotProd + halfSize.y) / yspd
					if (ymax < ymin) then
						local swap = ymin
						ymin = ymax
						ymax = swap
					end
				else
					if (math.abs(dotProd) < halfSize.y) then
						ymax = 1
						ymin = 0
					else
						return nil, Vector3.new(0, 0, 0), 1
					end
				end
				local dotProd = dot(zvec, start - model.Position)
				if (zspd ~= 0) then
					zmin = (dotProd - halfSize.z) / zspd
					zmax = (dotProd + halfSize.z) / zspd
					if (zmax < zmin) then
						local swap = zmin
						zmin = zmax
						zmax = swap
					end
				else
					if (math.abs(dotProd) < halfSize.z) then
						zmax = 1
						zmin = 0
					else
						return nil, Vector3.new(0, 0, 0), 1
					end
				end
				if (xmin <= ymax) and (xmax >= ymin) and (xmin <= zmax) and (xmax >= zmin) and (zmin <= ymax) and (zmax >= ymin) then
					local normal = xvec
					local min = xmin
					if (ymin > min) then
						min = ymin
						normal = yvec
					end
					if (zmin > min) then
						min = zmin
						normal = zvec
					end
					if (min >= 0) and (min < 1) then
						time = min
					elseif (xmax > 0) and (ymax > 0) and (zmax > 0) and (min < 0) then
						time = 0
						normal = Vector3.new(0, 0, 0)
					end
					return model, normal, time
				else
					return nil, Vector3.new(0, 0, 0), 1
				end
			else	--	Cylinder
				local time = 1
				local cf = model.CFrame - model.Position
				local xvec = cf * Vector3.new(1, 0, 0)
				local xspd = -dot(xvec, vector)
				local xmin, xmax = -1
				local dotProd = dot(xvec, start - model.Position)
				if (xspd ~= 0) then
					xmin = (dotProd - halfSize.x) / xspd
					xmax = (dotProd + halfSize.x) / xspd
					if (xmax < xmin) then
						local swap = xmin
						xmin = xmax
						xmax = swap
					end
				else
					if (math.abs(dotProd) < halfSize.x) then
						xmax = 1
						xmin = 0
					else
						return nil, Vector3.new(0, 0, 0), 1
					end
				end

				local relVec = cf:pointToObjectSpace(vector) * Vector3.new(0, 1, 1)
				local relPos = model.CFrame:pointToObjectSpace(start) * Vector3.new(0, 1, 1)
				local rmin, rmax = getLineSphereCollide(relPos, relVec, Vector3.new(0, 0, 0), halfSize.y)
				if (xmin <= rmax) and (xmax >= rmin) and (rmax > 0) then
					local normal = xvec
					local min = xmin
					if (rmin > min) then
						min = rmin
						normal = cf * (relPos + relVec * min)
					end
					if (min >= 0) and (min < 1) then
						time = min
					elseif (xmax > 0) and (rmax > 0) and (min < 0) then
						time = 0
						normal = Vector3.new(0, 0, 0)
					end
					return model, normal, time
				else
					return nil, Vector3.new(0, 0, 0), 1
				end
				return nil, Vector3.new(0, 0, 0), 1
			end
		end
		return nil, Vector3.new(0, 0, 0), 1
	elseif (model.className=="Model") or (model.className=="Workspace") or (model.className=="Hat") or (model.className == "Tool") then
		local children=model:GetChildren()
		local time=1
		local normal=Vector3.new(0, 0, 0)
		local hit=nil
		for n = 1, #children do
			if children[n]~= nil then
				local newHit, newNormal, newTime = raycastRecursive(children[n], start, vector, brickFunction, unitVec, startDist)
				if (newTime < time) then
					time = newTime
					hit = newHit
					normal = newNormal
				end
			end
		end
		return hit, normal, time
	else
		return nil, Vector3.new(0, 0, 0), 1
	end
end