Reflect a projectile at same orientation?

Hi, and thanks for reading in advance.

I know this is a dumb question, but CFrames have never been my strong suit. I’ve got a sort of reflection system set up for the projectiles in a fighting game I’m helping to code, but I’ve run into a small issue. My current method of reflection is simply to use CFrame.Angles and spin it around 180 degrees, but that only works if the projectile is facing straight ahead and isn’t angled up or down. A projectile that’s meant to be launched upwards will be re-launched downwards when reflected. I know that all I need do is simply rotate the projectile via the world axis, but the wiki doesn’t provide a decent enough explanation of how ToWorldSpace and ToObjectSpace work.

Sauce (careful, it’s long):

function Core:Projectile(owner, info)
	local ignore = {}; local check = nil
	local reflected = false
	
	local bullet = Instance.new("Part")
	bullet.Size = info.size or Vector3.new(1, 1, 1)
	bullet.Material = Enum.Material.Neon
	bullet.TopSurface = Enum.SurfaceType.Smooth
	bullet.TopSurface = bullet.BottomSurface
	bullet.Transparency = 1; bullet.CanCollide = false
	bullet.CFrame = info.cframe or CFrame.new()

	local mover = Instance.new("BodyVelocity", bullet)
	mover.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	mover.Velocity = bullet.CFrame.LookVector * (info.speed or 10)

	local reflector = Instance.new("BindableEvent")
	reflector.Name = "Reflect"; reflector.Parent = bullet

	local function ProcessHit(target, part)
		local Humanoid = target and target:FindFirstChild("Humanoid")
		local result

		if part ~= owner and part ~= workspace.Terrain and not part:IsDescendantOf(owner) then
			if game:GetService("CollectionService"):HasTag(part, "Reflector") then
				Core:Effect("Clear", info.id); check:Disconnect()
				
				reflected = true; bullet.Parent = nil
				info.cframe = bullet.CFrame * CFrame.Angles(0, math.rad(180), 0)
				game:GetService("Debris"):AddItem(bullet, 1)
				
				local newbullet = Core:Projectile(part, info)
				
				if info.OnReflect then
					info.OnReflect(bullet.CFrame, newbullet)
				end
				
			elseif info.damage then
				if Humanoid and Humanoid.Health > 0 and not ignore[target]
					and Core:FilterSingle(target, owner, info.targetgroup or "Enemies") then
					local hp, health = Core:CheckStat(target, "HP")

					if health and hp > 0 then local hitsound
						ignore[target] = true
						
						if info.sound then
							hitsound = {id = info.sound, v = 1,
								pos = target:FindFirstChild("HumanoidRootPart")	
							}
						end
						
						result = Core:Hurt(target, owner, info.damage, {Sound = hitsound})
						
						if info.OnResult then
							info.OnResult(target, result)
						end
					end
				end
			end
			
			if (result and not info.pierce)
				or (not Humanoid and part.CanCollide and not info.wallpierce and not Core:IsArmor(part)) then
				check:Disconnect(); Core:Effect("Clear", info.id)
				game:GetService("Debris"):AddItem(bullet, 1)
				bullet.Parent = nil
				
				if not result and info.breaksound then
					Core:Effect("Sound", {id = info.breaksound, pos = bullet.Position})
				end
			end
		end
	end

	bullet.Parent = workspace.ServerProjectiles
	bullet:SetNetworkOwner(nil)
	
	check = RS.Remotes.BulletCheck.OnServerEvent:Connect(function(player, target, part, key)
		if player == game.Players:GetPlayerFromCharacter(owner) and key == info.id then
			local ping = (player:FindFirstChild("Ping") or {Value = 50}).Value
			if ping < 200 then
				if Core:ConfirmProximity(target, bullet.Position, 5 + bullet.Size.Magnitude) then
					if not part:IsDescendantOf(workspace.ServerProjectiles) then
						ProcessHit(target, part)
					end
				end
			end
		end
	end)
	
	bullet.Touched:Connect(function(part)
		if not part:IsDescendantOf(workspace.ServerProjectiles) then
			ProcessHit(part.Parent, part)
		end
	end)

	reflector.Event:Connect(function(newowner)
		if bullet.Parent then
			Core:Effect("Clear", info.id); check:Disconnect()
			game:GetService("Debris"):AddItem(bullet, 1)
			bullet.Parent = nil
			
			info.cframe = bullet.CFrame * CFrame.Angles(0, math.rad(180), 0)
			Core:Projectile(newowner, info)
		end
	end)

	bullet.AncestryChanged:Connect(function()
		check:Disconnect(); if info.OnRemove then
			info.OnRemove(bullet.CFrame, reflected)
		end
	end)
	
	if info.acceleration then
		coroutine.wrap(function()
			while bullet.Parent and wait() do
				mover.Velocity = mover.Velocity + info.acceleration
			end
		end)()
	end

	delay(info.lifetime or 1, function()
		if bullet.Parent then bullet.Parent = nil
			game:GetService("Debris"):AddItem(bullet, 1)
		end
	end)
	
	if info.effect then
		Core:Effect(info.effect, {cf = bullet.CFrame, 
			col = info.color or Color3.fromRGB(255, 255, 255),
			speed = info.speed, acceleration = info.acceleration,
			size = bullet.Size, life = info.lifetime, key = info.id
		})
	end

	return bullet
end

Any help is appreciated.