-- Tools --

function Tools:RotateVector(Vector : Vector3, Rotation : Vector3 | CFrame)
	
	if typeof(Rotation) == 'Vector3' then
		Rotation = Tools:Vector3ToCFrameAngles(Rotation)
	end
	
	
	local RotatedVector = Rotation:VectorToWorldSpace(Vector)
	
	
	return RotatedVector
end
function Tools:GetObjectByAddress(Address, StartInput)
	local Current
	if StartInput then
		Current = StartInput
	else
		Current = game
	end
	for i, k in pairs(Address) do
		Current = Current:FindFirstChild(k)
		if Current == nil then return end
	end
	return Current
end
function Tools:Waitf(frames : number)
	
	if frames == 0 then
		return 0
	end
	local start = os.clock()
	--Tools:SuperWait(frames / 60)
	task.wait(frames / 60)
	return os.clock() - start
end
function Tools:Vector3ToCFrameAngles(vector3)
	return CFrame.Angles(math.rad(vector3.X), math.rad(vector3.Y), math.rad(vector3.Z))
end
function Tools:GetCFrameOrientation(c : CFrame)
	local Orientation = Vector3.new(c:ToOrientation())
	

	Orientation = Tools:Vector3InDegrees(Orientation)
	
	
	return Orientation
end


local AvailableAnimations = {}
local LoadedAnimations = {}

function Tools:PlayAnim(
	Character : Model, 
	Animation : string | number | Animation, 
	PlayArguments : {fadeTime : number, weight : number, speed : number}
)
	local Humanoid = Character:FindFirstChildWhichIsA('Humanoid')
	if Animation == nil then return end
	if Humanoid:IsAncestorOf(workspace) == nil then warn('No animation argument') return end
	
	local Animator : Animator = Humanoid:FindFirstChild('Animator') or Instance.new('Animator', Humanoid)
	
	if typeof(Animation) == 'Animation' then
		-- shouldn't need to do anything for this
		
	elseif typeof(Animation) == 'string' then
		local AnimationInstance = Instance.new('Animation', Animator)
		AnimationInstance.Name = Animation
		AnimationInstance.AnimationId = Animation
		Animation = AnimationInstance
	elseif typeof(Animation) == 'number' then
		local AnimationInstance = Instance.new('Animation', Animator)
		AnimationInstance.Name = Animation
		AnimationInstance.AnimationId = 'rbxassetid://'.. tostring(Animation)
		Animation = AnimationInstance
		
		
	end
	
	
	local Anim : AnimationTrack
	local AnimatorLoadedAnimations = LoadedAnimations[Animator]
	if AnimatorLoadedAnimations == nil then
		LoadedAnimations[Animator] = {}
		AnimatorLoadedAnimations = LoadedAnimations[Animator]
		
		local KillCheck
		KillCheck = Humanoid.StateChanged:Connect(function(old, new)
			if new == Enum.HumanoidStateType.Dead then
				LoadedAnimations[Animator] = nil
				KillCheck:Disconnect()
			end
		end)
	end


	Anim = Animator:LoadAnimation(Animation)
	Animation:Destroy()
	table.insert(LoadedAnimations[Animator], Anim)
	
	if PlayArguments then
		Anim:Play(table.unpack(PlayArguments))
	else
		Anim:AdjustWeight(0)
		Anim:Play()
	end
	local Ended, Destroyed
	
	Ended = Anim.Ended:Once(function()
		Ended:Disconnect()
		Anim:Destroy()
	end)
	
	Destroyed = Anim.Destroying:Once(function()
		Ended:Disconnect()
	end)
	return Anim
end
function Tools:GetPlayerByRig(rig) 
	local PotentialPlayer = Players:FindFirstChild(rig.Name)
	if PotentialPlayer then
		if PotentialPlayer.Character == rig then
			return PotentialPlayer
		end
	end
	return nil
end
function Tools:Vector3InRadians(v)
	return Vector3.new(math.rad(v.x), math.rad(v.y), math.rad(v.z))
end
function Tools:Vector3InDegrees(vector3)
	
	return Vector3.new(
		math.deg(vector3.x), 
		math.deg(vector3.y),
		math.deg(vector3.z)
	)
	
end
function Tools:SuperWait(sec)
	local Start = time()
	
	repeat
		task.desynchronize()
		task.synchronize()
	until time() - Start >= sec
	
end
function Tools:DictionaryConcat(a1 : {}, a2 : {})
	local new = a1
	for i, k in pairs(a2) do
		new[i] = k
	end
	return new
end
function Tools:GetChildrenNames(instance : Instance)
	local Children = instance:GetChildren()
	for i, k in ipairs(Children) do
		table.insert(Children, i, k.Name)
		table.remove(Children, i + 1)
	end
	return Children
end

function Tools:GetCameraAimAngle()
	local Player = Players.LocalPlayer
	local Character = Player.Character
	local Mouse = Player:GetMouse()
	local Camera = workspace.CurrentCamera

	local CameraAngle
	if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then
		-- if in shiftlock
		CameraAngle = Camera.CFrame.Rotation

	else
		-- if not in shiftlock
		local RootPart = Character:FindFirstChild('HumanoidRootPart')
		
		if RootPart == nil then return nil end
		CameraAngle = CFrame.lookAt(RootPart.Position, Mouse.Hit.Position).Rotation
	end

	return CameraAngle
end

function Tools:WaitUntilAnimationTrackLoaded(Animator : Animator, AnimationTrackId : string | number) 
	if typeof(AnimationTrackId) == 'number' then
		AnimationTrackId = 'rbxassetid://'.. tostring(AnimationTrackId)
	end
	
	local AnimationLoaded = false
	local Animation = nil
	
	local function CheckIfInAnimator()
		
		local InAnimator = false
		for i, AnimationTrack : AnimationTrack in ipairs(Animator:GetPlayingAnimationTracks()) do

			if AnimationTrack.Animation.AnimationId == AnimationTrackId then
				Animation = AnimationTrack
				InAnimator = true
				break
			end
		end
		
		return InAnimator
	end
	
	AnimationLoaded = CheckIfInAnimator()
	
	if not AnimationLoaded then 
		repeat
			task.wait(0)

			AnimationLoaded = CheckIfInAnimator()
		until AnimationLoaded
	end
	
	if Animation.Length > 0 then return end
	repeat 
		task.wait(0)
		
	until Animation.Length > 0
end

function Tools:BeamArc(Angle : number, Radius : number) 
	
	local BeamPart = Instance.new('Part', workspace)
	BeamPart.Transparency = 1
	BeamPart.CanCollide = false
	BeamPart.CanTouch = false
	BeamPart.CanQuery = false
	BeamPart.Size = Vector3.one
	BeamPart.Anchored = true
	
	local Attachment0 = Instance.new('Attachment', BeamPart)
	Attachment0.Name = 'Attachment0'
	Attachment0.CFrame = CFrame.new(0, 0, Radius)
	
	local Attachment1 = Instance.new('Attachment', BeamPart)
	Attachment1.Name = 'Attachment1'
	Attachment1.CFrame = (CFrame.Angles(0, math.rad(Angle), 0) * CFrame.new(0, 0, Radius))
	Attachment1.CFrame *= CFrame.Angles(0, math.rad(180), 0)
	
	
	local Beam = Instance.new('Beam', BeamPart)
	Beam.Attachment0 = Attachment0
	Beam.Attachment1 = Attachment1
	Beam.CurveSize0 = (4/3) * math.tan(math.pi/8) * Radius * (Angle / 90)
	Beam.CurveSize1 = -Beam.CurveSize0
		
	
	
	return BeamPart
	
end

function Tools:DeepCopy(Original)
	local Copy = {}
	for k, v in pairs(Original) do
		if type(v) == "table" then
			v = Tools:DeepCopy(v)
		end
		Copy[k] = v
	end
	return Copy
end
return Tools