--[[
Attempts to solve footplanting.
This code is heavily "zombified" off of a project by
Stravant, and is taken from Nexus VR Character Model V1
with minimal changes. It really needs to be replaced by
someone who better unstands foot placement. No automated
tests are done on this code.
--]]

local FootPlanter = {}

local CFnew,CFAngles = CFrame.new,CFrame.Angles
local V3new = Vector3.new
local rad,atan2,acos = math.rad,math.atan2,math.acos
local min,max,abs,log = math.min,math.max,math.abs,math.log

function FootPlanter:CreateSolver(CenterPart,ScaleValue)
	--Heavily modified code from Stravant
	local FootPlanterClass = {}

	local ignoreModel = CenterPart.Parent

	local LEG_GAP = 1.2
	--
	local STRIDE_FORWARD = 1.7 / 2
	local STRIDE_BACKWARD = 3.3 / 2
	local STRIDE_HEIGHT = 0.6
	local STRIDE_RESTING = 1
	--
	local WALK_SPEED_VR_THRESHOLD = 2
	local FOOT_MAX_SPEED_FACTOR = 2
	--
	local WALK_CYCLE_POWER = 1
	--
	local FOOT_ANGLE = rad(5)

	local function flatten(CF)
		local X,Y,Z = CF.X,CF.Y,CF.Z
		local LX,LZ = CF.lookVector.X,CF.lookVector.Z

		return CFnew(X,Y,Z) * CFAngles(0,atan2(LX,LZ),0)
	end

	local lastPosition,lastPollTime
	local overrideVelocityWithZero = false
	local function getVelocity(CF)
		if overrideVelocityWithZero then
			overrideVelocityWithZero = true

			local curTime = tick()
			lastPosition = CF.p

			return V3new()
		end

		if lastPollTime then
			local curTime = tick()
			local newPosition = CF.p

			local velocity = (newPosition - lastPosition) * 1/(curTime - lastPollTime)
			lastPollTime = curTime
			lastPosition = newPosition

			return velocity
		else
			lastPollTime = tick()
			lastPosition = CF.p

			return V3new()
		end
	end

	local function getWalkSpeed(velocity)
		return V3new(velocity.x,0,velocity.z).magnitude
	end

	local function getWalkDirection(velocity)
		if velocity.magnitude > 0. then
			return velocity.unit
		else
			return V3new(0,0,-1)
		end
	end

	local function isWalking(velocity)
		return getWalkSpeed(velocity) > WALK_SPEED_VR_THRESHOLD
	end

	local CurrentScale = 1
	local function getBaseCFrame()
		return CenterPart.CFrame * CFnew(0,-CenterPart.Size.Y/2 - (CurrentScale * 2),0)
	end

	local function getBaseRotationY()
		local lookVector = getBaseCFrame().lookVector
		return atan2(lookVector.X,lookVector.Z)
	end

	local FindCollidablePartOnRay = require(script.Parent.Parent:WaitForChild("Util"):WaitForChild("FindCollidablePartOnRay"))
	local function FindPartOnRay(ray,ignore)
		return FindCollidablePartOnRay(ray.Origin,ray.Direction,ignore,CenterPart)
	end

	-- Leg data
	local mRightLeg, mLeftLeg, mRightArm, mLeftArm;
	local mLegs = {}
	local lastCF
	local function initLegs()
		local cf = flatten(getBaseCFrame())
		lastCF = cf
		mRightLeg = {
			OffsetModifier = CFnew(-LEG_GAP/2, 0, 0);
			Side = -1;
			--
			StepCycle = 0;
			FootPosition = cf*CFnew(-LEG_GAP/2, 0, 0).p;
			LastStepTo = cf*CFnew(-LEG_GAP/2, 0, 0).p;
			Takeoff = cf*CFnew(-LEG_GAP/2, 0, 0).p;
		}
		mLeftLeg = {
			OffsetModifier = CFnew( LEG_GAP/2, 0, 0);
			Side = 1;
			--
			StepCycle = 0;
			FootPosition = cf*CFnew( LEG_GAP/2, 0, 0).p;
			LastStepTo = cf*CFnew(-LEG_GAP/2, 0, 0).p;
			Takeoff = cf*CFnew(-LEG_GAP/2, 0, 0).p;
		}
		mRightLeg.OtherLeg = mLeftLeg
		mLeftLeg.OtherLeg = mRightLeg
		mLegs = {mLeftLeg, mRightLeg}
	end

	local LastScale = 1
	local function UpdateScaling()
		local CurrentScaleValue = (ScaleValue and ScaleValue.Value or 1)
		local Multiplier = CurrentScaleValue / LastScale
		LastScale = CurrentScaleValue

		LEG_GAP = LEG_GAP * Multiplier
		STRIDE_FORWARD = STRIDE_FORWARD * Multiplier
		STRIDE_BACKWARD = STRIDE_BACKWARD * Multiplier
		STRIDE_HEIGHT = STRIDE_HEIGHT * Multiplier
		STRIDE_RESTING = STRIDE_RESTING * Multiplier

		mRightLeg.OffsetModifier = CFnew(-LEG_GAP/2, 0, 0)
		--[[mRightLeg.FootPosition
		mRightLeg.LastStepTo
		mRightLeg.Takeoff
			 = ;
			Side = -1;
			--
			StepCycle = 0;
			 = lastCF*CFnew(-LEG_GAP/2, 0, 0).p;
			 = lastCF*CFnew(-LEG_GAP/2, 0, 0).p;
			 = lastCF*CFnew(-LEG_GAP/2, 0, 0).p;
		}
		mLeftLeg = {
			OffsetModifier = CFnew( LEG_GAP/2, 0, 0);
			Side = 1;
			--
			StepCycle = 0;
			FootPosition = lastCF*CFnew( LEG_GAP/2, 0, 0).p;
			LastStepTo = lastCF*CFnew(-LEG_GAP/2, 0, 0).p;
			Takeoff = lastCF*CFnew(-LEG_GAP/2, 0, 0).p;
		}]]
	end
	if ScaleValue then
		ScaleValue.Changed:Connect(function()
			if mRightLeg then
				UpdateScaling()
			end
		end)
	end

	local mAirborneFraction = 0
	local mLandedFraction = 0
	local mCurrentSpeed = 1
	local mStableStanding = false

	local function getStrideForward()
		if mCurrentSpeed > 0 then
			return STRIDE_FORWARD + STRIDE_FORWARD*1*mCurrentSpeed
		else
			return STRIDE_FORWARD + STRIDE_FORWARD*0.5*mCurrentSpeed
		end
	end

	local function getStrideFull()
		if mCurrentSpeed > 0 then
			return (STRIDE_FORWARD + STRIDE_BACKWARD) + (STRIDE_FORWARD + STRIDE_BACKWARD)*1.5*mCurrentSpeed
		else
			return (STRIDE_FORWARD + STRIDE_BACKWARD) + (STRIDE_FORWARD + STRIDE_BACKWARD)*0.5*mCurrentSpeed
		end
	end

	local function snapDown(pos)
		local orig, dir = (pos + V3new(0, 2, 0)), V3new(0, -500, 0)
		local hit, at = FindPartOnRay(Ray.new(orig, dir), ignoreModel)
		if hit then
			local hit1, at1 = FindPartOnRay(Ray.new(orig + V3new(0,    0,  0.01), dir), ignoreModel)
			local hit2, at2 = FindPartOnRay(Ray.new(orig + V3new(0,    0, -0.01), dir), ignoreModel)
			local hit3, at3 = FindPartOnRay(Ray.new(orig + V3new(0.01, 0,  0   ), dir), ignoreModel)
			local norm;

			if hit1 and hit2 and hit3 then
				norm = (at1 - at2):Cross(at2 - at3).unit
				if norm.Y < 0 then
					norm = -norm
				end
			end

			return at, norm
		else
			return pos, V3new(0, 1, 0)
		end
	end

	local function fixFeetPositionsY()
		for _,leg in pairs(mLegs) do
			local targetPos,norm = snapDown(leg.FootPosition)
			leg.FootPosition = targetPos
		end
	end

	local lastTime
	function FootPlanterClass:GetFeetCFrames()
		if not mLeftLeg then
			initLegs()
			UpdateScaling()
		end

		local curTime = tick()
		if not lastTime then lastTime = curTime end
		local dt = curTime - lastTime
		lastTime = curTime

		local velocity = getVelocity(CenterPart.CFrame)
		local speed = getWalkSpeed(velocity)
		local realBaseCF = flatten(CenterPart.CFrame)
		local baseCF = realBaseCF
		local baseAxis = baseCF.lookVector
		local baseAxisPerp = baseAxis:Cross(V3new(0, 1, 0))
		local walkAxis = getWalkDirection(velocity)
		local walkAxisPerp = walkAxis:Cross(V3new(0, 1, 0))

		--
		mCurrentSpeed = max(-1, min(1, log(speed/16)/log(2)))
		--
		local leftStepping = mLeftLeg.StepCycle > 0
		local rightStepping = mRightLeg.StepCycle > 0
		--

		local function spline(t2, n, p)
			if n == 1 then
				return p[1]
			else
				local t1 = (1 - t2)
				for i = 1, n-1 do
					p[i] = t1*p[i] + t2*p[i+1]
				end
				return spline(t2, n-1, p)
			end
		end

		local function positionFootByCycle(leg, stepTarget)
			local baseVector = stepTarget - leg.Takeoff
			local sideVector = walkAxisPerp*leg.Side
			local baseLen = baseVector.magnitude
			local towardsSide = sideVector*(LEG_GAP/2)*0.3
			local towardsTop = V3new(0, 1.3*STRIDE_HEIGHT*(1 / leg.StepSpeedMod), 0)
			local topPoint = leg.Takeoff + baseVector * 1/2 + towardsTop + towardsSide
			local nextPoint = leg.Takeoff + baseVector * 0.9 + towardsTop + towardsSide
			--local a, b, c = leg.Takeoff, topPoint, stepTarget
			--local fb = leg.StepCycle
			--fb = fb^(_G.A or WALK_CYCLE_POWER)
			--local fa = 1-fb
			--
			--local footDesiredPos = fa*fa*a + 2*fa*fb*b + fb*fb*c
			local fb = leg.StepCycle^(_G.A or WALK_CYCLE_POWER)
			local footDesiredPos = spline(fb, 4, {leg.Takeoff, topPoint, nextPoint, stepTarget})
			--makeP(CFnew(footDesiredPos))
			if (footDesiredPos - leg.FootPosition).magnitude > dt*speed*FOOT_MAX_SPEED_FACTOR then
				local forcePos = (1 - leg.StepCycle) * leg.FootPosition + leg.StepCycle * footDesiredPos
				local movePos = leg.FootPosition + (footDesiredPos - leg.FootPosition).unit * dt*speed*FOOT_MAX_SPEED_FACTOR
				if (forcePos - footDesiredPos).magnitude < (movePos - footDesiredPos).magnitude then
					footDesiredPos = forcePos
				else
					footDesiredPos = movePos
				end
			end
			leg.FootPosition = footDesiredPos
			leg.LastStepTo = stepTarget
		end
		--
		local isCharacterWalking = isWalking(velocity)
		if isCharacterWalking then
			mStableStanding = false
		end
		--
		if isCharacterWalking then
			-- Get the desired ahead step
			local centeringMod = walkAxisPerp * (LEG_GAP/2) * 0.5
			local rightDesiredAheadStep = (baseCF * mRightLeg.OffsetModifier + getStrideForward()*walkAxis - mRightLeg.Side*centeringMod).p
			local leftDesiredAheadStep  = (baseCF *  mLeftLeg.OffsetModifier + getStrideForward()*walkAxis - mLeftLeg.Side*centeringMod).p
			local rightNorm, leftNorm;
			rightDesiredAheadStep, rightNorm = snapDown(rightDesiredAheadStep)
			leftDesiredAheadStep, leftNorm = snapDown(leftDesiredAheadStep)
			if not rightStepping or not mRightLeg.AheadStep or (rightDesiredAheadStep - mRightLeg.AheadStep).magnitude < dt*speed then
				mRightLeg.AheadStep = rightDesiredAheadStep
			else
				mRightLeg.AheadStep = mRightLeg.AheadStep + (rightDesiredAheadStep - mRightLeg.AheadStep).unit * dt*speed*2
			end
			mRightLeg.NormalHint = rightNorm
			if not leftStepping or not mLeftLeg.AheadStep or (leftDesiredAheadStep - mLeftLeg.AheadStep).magnitude < dt*speed then
				mLeftLeg.AheadStep = leftDesiredAheadStep
			else
				mLeftLeg.AheadStep = mLeftLeg.AheadStep + (leftDesiredAheadStep - mLeftLeg.AheadStep).unit * dt*speed*2
			end
			mLeftLeg.NormalHint = leftNorm

			local strideFactor = 0.9 - 0.3*max(0, mCurrentSpeed)
			local stepSpeed = speed / getStrideFull() * strideFactor

			-- Which legs are stepping?
			if not leftStepping and not rightStepping then
				-- Neither leg is stepping pick up the closer leg into the step
				if mAirborneFraction < 0.8 then -- don't pick up feet if we just landed
					if (mLeftLeg.FootPosition - mLeftLeg.AheadStep).magnitude < (mRightLeg.FootPosition - mRightLeg.AheadStep).magnitude then
						-- step p1
						local fracThere = min(0.9, max(0, (mLeftLeg.FootPosition - mLeftLeg.AheadStep).magnitude / getStrideFull()))
						mLeftLeg.StepSpeedMod = 1 / (1 - fracThere)
						mLeftLeg.StepCycle = dt
						mLeftLeg.Takeoff = mLeftLeg.FootPosition
					else
						-- step p2
						local fracThere = min(0.9, max(0, (mRightLeg.FootPosition - mRightLeg.AheadStep).magnitude / getStrideFull()))
						mRightLeg.StepSpeedMod = 1 / (1 - fracThere)
						mRightLeg.StepCycle = dt
						mRightLeg.Takeoff = mRightLeg.FootPosition
					end
				end
			elseif leftStepping and rightStepping then
				-- Both legs are stepping
				-- just step both legs
				-- The leg closer to |aheadStep| should step there, and the
				-- other leg should
				for _, leg in pairs(mLegs) do
					leg.StepCycle = min(1, leg.StepCycle + dt*stepSpeed*leg.StepSpeedMod)
					positionFootByCycle(leg, leg.AheadStep)
					if leg.StepCycle == 1 then
						leg.StepCycle = 0
					end
				end
			else
				-- One leg is stepping.
				-- Step the one leg, and see if the other needs to enter a step
				for _, leg in pairs(mLegs) do
					if leg.StepCycle > 0 then
						-- Step this leg
						leg.StepCycle = min(1, leg.StepCycle + dt*stepSpeed*leg.StepSpeedMod)
						positionFootByCycle(leg, leg.AheadStep)

						-- Check if leg.Other needs to start a step
						if leg.StepCycle > strideFactor then
							leg.OtherLeg.StepSpeedMod = 1
							leg.OtherLeg.StepCycle = dt
							leg.OtherLeg.Takeoff = leg.OtherLeg.FootPosition
							positionFootByCycle(leg.OtherLeg, leg.AheadStep)
						end

						if leg.StepCycle == 1 then
							leg.StepCycle = 0
						end

						break
					end
				end
			end
		else
			local stepSpeed = 2

			-- Not walking, we need to try to get to a suitable base position
			if leftStepping or rightStepping then
				for _, leg in pairs(mLegs) do
					if leg.StepCycle > 0 then
						leg.StepCycle = min(1, leg.StepCycle + dt*stepSpeed)
						local restingPos = (baseCF * leg.OffsetModifier).p
						local toVec = (leg.LastStepTo - restingPos)
						local targetPos;
						if toVec.magnitude > STRIDE_RESTING then
							targetPos = restingPos + (toVec.unit * STRIDE_RESTING)
						else
							targetPos = leg.LastStepTo
						end
						local norm;
						targetPos, norm = snapDown(targetPos)
						leg.AheadStep = targetPos
						leg.NormalHint = norm
						positionFootByCycle(leg, targetPos)
						if leg.StepCycle == 1 then
							leg.StepCycle = 0
						end
					else

					end
				end
			else
				fixFeetPositionsY()
			end

			-- Now, we stepped both legs. If both legs are resting now. See if
			-- they are on roughly opposite offsets from where they should be.
			if mRightLeg.StepCycle == 0 and mLeftLeg.StepCycle == 0 then
				local rightResting = (baseCF * mRightLeg.OffsetModifier).p
				local leftResting = (baseCF * mLeftLeg.OffsetModifier).p
				local rightSep = mRightLeg.FootPosition - rightResting
				local leftSep = mLeftLeg.FootPosition - leftResting
				--
				local tooFar = abs(rightSep:Dot(baseAxis) - leftSep:Dot(baseAxis)) > 3
				local thetaBetweenFeet = acos(min(1, max(-1, rightSep.unit:Dot(leftSep.unit))))
				local distBetweenFeet = abs(rightSep.magnitude - leftSep.magnitude)
				--
				if rightSep:Dot(baseAxisPerp) > LEG_GAP/4 then
					mStableStanding = false
					mRightLeg.Takeoff = mRightLeg.FootPosition
					mRightLeg.StepCycle = dt
					--mRightLeg.LastStepTo = rightResting - baseAxisPerp*0.5
					local modLeftSep = leftSep.unit * 0.5
					if leftSep.magnitude == 0 then
						modLeftSep = -baseAxisPerp*0.5
					elseif leftSep:Dot(baseAxisPerp) > 0 then
						modLeftSep = (leftSep - 2*baseAxisPerp*leftSep:Dot(baseAxisPerp)).unit*0.5
					end
					mRightLeg.LastStepTo = rightResting + modLeftSep
					if (mRightLeg.LastStepTo - mRightLeg.Takeoff).magnitude < 0.5 then
						mRightLeg.StepCycle = 0
					end
					local fracThere = min(0.9, max(0, (mRightLeg.FootPosition - mRightLeg.LastStepTo).magnitude / getStrideFull()))
					mRightLeg.StepSpeedMod = 1 / (1 - fracThere)
				elseif leftSep:Dot(baseAxisPerp) < -LEG_GAP/4 then
					mStableStanding = false
					mLeftLeg.Takeoff = mLeftLeg.FootPosition
					mLeftLeg.StepCycle = dt
					--mLeftLeg.LastStepTo = leftResting + baseAxisPerp*0.5
					local modRightSep = rightSep.unit * 0.5
					if rightSep.magnitude == 0 then
						modRightSep = baseAxisPerp*0.5
					elseif rightSep:Dot(baseAxisPerp) < 0 then
						modRightSep = (rightSep - 2*baseAxisPerp*rightSep:Dot(baseAxisPerp)).unit*0.5
					end
					mLeftLeg.LastStepTo = leftResting + modRightSep
					if (mRightLeg.LastStepTo - mRightLeg.Takeoff).magnitude < 0.5 then
						mRightLeg.StepCycle = 0
					end
					local fracThere = min(0.9, max(0, (mLeftLeg.FootPosition - mLeftLeg.LastStepTo).magnitude / getStrideFull()))
					mLeftLeg.StepSpeedMod = 1 / (1 - fracThere)
				elseif not mStableStanding and (thetaBetweenFeet < rad(150) or distBetweenFeet > 0.2 or tooFar) and mAirborneFraction < 0.5 then
					mStableStanding = true
					-- Step the foot further from the rest pos
					local furtherLeg, furtherResting, otherSep;
					if rightSep.magnitude > leftSep.magnitude then
						furtherLeg = mRightLeg
						furtherResting = rightResting
						otherSep = leftSep
					else
						furtherLeg = mLeftLeg
						furtherResting = leftResting
						otherSep = rightSep
					end
					--
					furtherLeg.StepCycle = dt
					furtherLeg.Takeoff = furtherLeg.FootPosition
					furtherLeg.StepSpeedMod = 1
					if tooFar then
						furtherLeg.LastStepTo = furtherResting - 0.5*otherSep
					else
						furtherLeg.LastStepTo = furtherResting - otherSep
					end
					if (furtherLeg.Takeoff - furtherLeg.LastStepTo).magnitude < 0.2 then
						furtherLeg.StepCycle = 0
					end
				end
			end
			fixFeetPositionsY()
		end

		local leftFootPosition,rightFootPosition = mLeftLeg.FootPosition,mRightLeg.FootPosition
		local footAngle = getBaseRotationY()
		local leftFootRotation = CFAngles(0,FOOT_ANGLE + footAngle,0)
		local rightFootRotation = CFAngles(0,-FOOT_ANGLE + footAngle,0)
		return CFnew(leftFootPosition) * leftFootRotation,CFnew(rightFootPosition) * rightFootRotation
	end

	function FootPlanterClass:OffsetFeet(Offset)
		overrideVelocityWithZero = true
		for _,leg in pairs(mLegs) do
			leg.FootPosition = leg.FootPosition + Offset
			leg.LastStepTo = leg.LastStepTo + Offset
			if leg.Takeoff then leg.Takeoff = leg.Takeoff + Offset end
			if leg.AheadStep then leg.AheadStep = leg.AheadStep + Offset end
		end
		overrideVelocityWithZero = true
	end

	return FootPlanterClass
end

return FootPlanter


--Stores information about the head of a character.
--!strict

local NexusVRCharacterModel = script.Parent.Parent
local NexusAppendage = require(script.Parent.Parent:WaitForChild("Packages"):WaitForChild("NexusAppendage"))
local Settings = require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("Settings")).GetInstance()

local Limb = NexusAppendage.Limb

local Head = {}
Head.__index = Head
setmetatable(Head, Limb)

export type Head = {
    Head: BasePart,
    LastNeckRotationGlobal: number?,
} & typeof(setmetatable({}, Head)) & NexusAppendage.Limb

--[[
Creates a head.
--]]
function Head.new(HeadPart: BasePart): Head
    local self = setmetatable(Limb.new() :: any, Head) :: Head
    self.Head = HeadPart
    return self
end

--[[
Returns the offset from the head to
the location of the eyes.
--]]
function Head.GetEyesOffset(self: Head): CFrame
    return (self:GetAttachmentCFrame(self.Head, "FaceFrontAttachment") :: CFrame) * CFrame.new(0, (self.Head :: BasePart).Size.Y / 4, 0)
end

--[[
Returns the head CFrame for the
given VR input in global world space.
--]]
function Head.GetHeadCFrame(self: Head, VRHeadCFrame: CFrame): CFrame
    return VRHeadCFrame * self:GetEyesOffset():Inverse()
end

--[[
Returns the neck CFrame for the
given VR input in global world space.
--]]
function Head.GetNeckCFrame(self: Head, VRHeadCFrame: CFrame, TargetAngle: number?): CFrame
    --Get the base neck CFrame and angles.
    local BaseNeckCFrame = (self:GetHeadCFrame(VRHeadCFrame) :: CFrame) * (self:GetAttachmentCFrame(self.Head, "NeckRigAttachment") :: CFrame)
    local BaseNeckLookVector = BaseNeckCFrame.LookVector
    local BaseNeckLook,BaseNeckTilt = math.atan2(BaseNeckLookVector.X, BaseNeckLookVector.Z) + math.pi, math.asin(BaseNeckLookVector.Y)

    --Clamp the new neck tilt.
    local NewNeckTilt = 0
    local MaxNeckTilt = Settings:GetSetting("Appearance.MaxNeckTilt") or math.rad(60)
    if BaseNeckTilt > MaxNeckTilt then
        NewNeckTilt = BaseNeckTilt - MaxNeckTilt
    elseif BaseNeckTilt < -MaxNeckTilt then
        NewNeckTilt = BaseNeckTilt + MaxNeckTilt
    end

    --Clamp the neck rotation if it is turning.
    if TargetAngle then
        --Determine the minimum angle difference.
        --Modulus is not used as it guarentees a positive answer, not the minimum answer, which can be negative.
        local RotationDifference = (BaseNeckLook - TargetAngle)
        while RotationDifference > math.pi do RotationDifference = RotationDifference - (2 * math.pi) end
        while RotationDifference < -math.pi do RotationDifference = RotationDifference + (2 * math.pi) end

        --Set the angle based on if it is over the limit or not.
        local MaxNeckSeatedRotation = Settings:GetSetting("Appearance.MaxNeckSeatedRotation") or math.rad(60)
        if RotationDifference > MaxNeckSeatedRotation then
            BaseNeckLook = RotationDifference - MaxNeckSeatedRotation
        elseif RotationDifference < -MaxNeckSeatedRotation then
            BaseNeckLook = RotationDifference + MaxNeckSeatedRotation
        else
            BaseNeckLook = 0
        end
    else
        local MaxNeckRotation = Settings:GetSetting("Appearance.MaxNeckRotation") or math.rad(35)
        local LastNeckRotationGlobal = self.LastNeckRotationGlobal
        if LastNeckRotationGlobal then
            --Determine the minimum angle difference.
            --Modulus is not used as it guarentees a positive answer, not the minimum answer, which can be negative.
            local RotationDifference = (BaseNeckLook - LastNeckRotationGlobal)
            while RotationDifference > math.pi do RotationDifference = RotationDifference - (2 * math.pi) end
            while RotationDifference < -math.pi do RotationDifference = RotationDifference + (2 * math.pi) end

            --Set the angle based on if it is over the limit or not.
            --Ignore if there is no previous stored rotation or if the rotation is "big" (like teleporting).
            if math.abs(RotationDifference) < 1.5 * MaxNeckRotation then
                if RotationDifference > MaxNeckRotation then
                    BaseNeckLook = BaseNeckLook - MaxNeckRotation
                elseif RotationDifference < -MaxNeckRotation then
                    BaseNeckLook = BaseNeckLook + MaxNeckRotation
                else
                    BaseNeckLook = LastNeckRotationGlobal
                end
            end
        end
    end
    self.LastNeckRotationGlobal = BaseNeckLook

    --Return the new neck CFrame.
    return CFrame.new(BaseNeckCFrame.Position) * CFrame.Angles(0, BaseNeckLook, 0) * CFrame.Angles(NewNeckTilt, 0, 0)
end

return Head


 --Stores information about the torso of a character.
--!strict

local NexusVRCharacterModel = script.Parent.Parent
local NexusAppendage = require(script.Parent.Parent:WaitForChild("Packages"):WaitForChild("NexusAppendage"))
local Settings = require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("Settings")).GetInstance()

local Limb = NexusAppendage.Limb

local Torso = {}
Torso.__index = Torso
setmetatable(Torso, Limb)

export type Torso = {
    LowerTorso: BasePart,
    UpperTorso: BasePart,
} & typeof(setmetatable({}, Torso)) & NexusAppendage.Limb

--[[
Creates a torso.
--]]
function Torso.new(LowerTorso: BasePart, UpperTorso: BasePart): Torso
    local self = setmetatable(Limb.new() :: any, Torso) :: Torso
    self.LowerTorso = LowerTorso
    self.UpperTorso = UpperTorso
    return self
end

--[[
Returns the lower and upper torso CFrames
for the given neck CFrame in global world space.
--]]
function Torso.GetTorsoCFrames(self: Torso, NeckCFrame: CFrame): (CFrame, CFrame)
    --Determine the upper torso CFrame.
    local UpperTorsoCFrame = NeckCFrame * self:GetAttachmentCFrame(self.UpperTorso, "NeckRigAttachment"):Inverse()

    --Determine the center CFrame with bending.
    local MaxTorsoBend = Settings:GetSetting("Appearance.MaxTorsoBend") or math.rad(10)
    local NeckTilt = math.asin(NeckCFrame.LookVector.Y)
    local LowerTorsoAngle = math.sign(NeckTilt) * math.min(math.abs(NeckTilt), MaxTorsoBend)
    local TorsoCenterCFrame = UpperTorsoCFrame * self:GetAttachmentCFrame(self.UpperTorso, "WaistRigAttachment") * CFrame.Angles(-LowerTorsoAngle, 0, 0)

    --Return the lower and upper CFrames.
    return TorsoCenterCFrame * self:GetAttachmentCFrame(self.LowerTorso, "WaistRigAttachment"):Inverse(), UpperTorsoCFrame
end

--[[
Returns the CFrames of the joints for
the appendages.
--]]
function Torso.GetAppendageJointCFrames(self: Torso, LowerTorsoCFrame: CFrame, UpperTorsoCFrame: CFrame): {[string]: CFrame}
    return {
        RightShoulder = UpperTorsoCFrame * self:GetAttachmentCFrame(self.UpperTorso, "RightShoulderRigAttachment"),
        LeftShoulder = UpperTorsoCFrame * self:GetAttachmentCFrame(self.UpperTorso, "LeftShoulderRigAttachment"),
        LeftHip = LowerTorsoCFrame * self:GetAttachmentCFrame(self.LowerTorso, "LeftHipRigAttachment"),
        RightHip = LowerTorsoCFrame * self:GetAttachmentCFrame(self.LowerTorso, "RightHipRigAttachment"),
    }
end

return Torso


--Manipulates a character model.
--!strict

local SMOOTHING_DURATION_SECONDS = 1 / 30

local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local VRService = game:GetService("VRService")

local NexusVRCharacterModel = script.Parent
local Head = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Head"))
local Torso = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Torso"))
local AppendageLegacy = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Appendage"))
local FootPlanter = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("FootPlanter"))
local EnigmaService = require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("EnigmaService")).GetInstance()
local Settings = require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("Settings")).GetInstance()
local VRInputService = require(NexusVRCharacterModel:WaitForChild("State"):WaitForChild("VRInputService")).GetInstance()
local NexusAppendage = require(NexusVRCharacterModel:WaitForChild("Packages"):WaitForChild("NexusAppendage"))
local UpdateInputs = NexusVRCharacterModel:WaitForChild("UpdateInputs") :: UnreliableRemoteEvent

local Appendage = NexusAppendage.Appendage

local Character = {}
Character.__index = Character

export type Character = {
    CharacterModel: Model,
    Humanoid: Humanoid,
    Head: Head.Head,
    Torso: Torso.Torso,
    LeftArm: NexusAppendage.Appendage,
    RightArm: NexusAppendage.Appendage,
    LeftLeg: NexusAppendage.Appendage,
    RightLeg: NexusAppendage.Appendage,
    FootPlanter: any,
    CurrentWalkspeed: number,
    TweenComponents: boolean,
    UseIKControl: boolean,
    PreventArmDisconnection: boolean,
    Parts: {[string]: BasePart},
    Motors: {[string]: Motor6D},
    Attachments: {[string]: {[string]: Attachment}},
    CurrentMotor6DTransforms: {[Motor6D]: CFrame},
    LastMotor6DTransforms: {[Motor6D]: CFrame},
    LastRefreshTime: number,
    ReplicationCFrames: {[string]: CFrame}?,
    LastReplicationCFrames: {[string]: CFrame}?,
    ReplicationTrackerData: {[string]: CFrame}?,
    LastReplicationTrackerData: {[string]: CFrame}?,
    AppearanceChangedConnection: RBXScriptConnection?,
} & typeof(setmetatable({}, Character))

--[[
Creates a character.
--]]
function Character.new(CharacterModel: Model): Character
    local self = setmetatable({
        CharacterModel = CharacterModel,
        TweenComponents = (CharacterModel ~= Players.LocalPlayer.Character),
        UseIKControl = Settings:GetSetting("Extra.TEMPORARY_UseIKControl"),
    }, Character) :: Character

    --Determine if the arms can be disconnected.
    --Checking for the setting to be explicitly false is done in case the setting is undefined (default is true).
    local PreventArmDisconnection = false
    if Players.LocalPlayer and Players.LocalPlayer.Character == CharacterModel then
        local Setting = Settings:GetSetting("Appearance.LocalAllowArmDisconnection")
        if Setting == false then
            PreventArmDisconnection = true
        end
    else
        local Setting = Settings:GetSetting("Appearance.NonLocalAllowArmDisconnection")
        if Setting == false then
            PreventArmDisconnection = true
        end
    end

    --Store the body parts.
    self.Humanoid = CharacterModel:WaitForChild("Humanoid") :: Humanoid
    self.CurrentWalkspeed = 0
    self.Humanoid.Running:Connect(function(WalkSpeed)
        if VRService.AvatarGestures and VRInputService:GetThumbstickPosition(Enum.KeyCode.Thumbstick1).Magnitude < 0.2 then
            WalkSpeed = 0
        end
        self.CurrentWalkspeed = WalkSpeed
    end)

    --Set up the character parts.
    self.PreventArmDisconnection = PreventArmDisconnection
    self:SetUpVRParts()

    --Set up a connection for Character Appearance changes.
    self.AppearanceChangedConnection = nil :: RBXScriptConnection?
    self:SetUpAppearanceChanged()

    self.CurrentMotor6DTransforms = {}
    self.LastMotor6DTransforms = {}
    self.LastRefreshTime = tick()

    --Set up replication at 30hz.
    if Players.LocalPlayer and Players.LocalPlayer.Character == CharacterModel then
        task.spawn(function()
            while (self.Humanoid :: Humanoid).Health > 0 do
                --Send the new CFrames if the CFrames changed.
                local ReplicationCFrames = self.ReplicationCFrames
                local ReplicationTrackerData = self.ReplicationTrackerData
                if ReplicationCFrames and self.LastReplicationCFrames ~= ReplicationCFrames and self.LastReplicationTrackerData ~= ReplicationTrackerData then
                    self.LastReplicationCFrames = ReplicationCFrames
                    self.LastReplicationTrackerData = ReplicationTrackerData
                    local NewTrackerData = {
                        UpdateTime = tick(),
                        CurrentWalkspeed = self.CurrentWalkspeed,
                        LeftFootCFrame = ReplicationTrackerData and ReplicationTrackerData.LeftFoot,
                        RightFootCFrame = ReplicationTrackerData and ReplicationTrackerData.RightFoot,
                    }
                    if not VRService.AvatarGestures then
                        NewTrackerData.HeadCFrame = ReplicationCFrames.HeadCFrame
                        NewTrackerData.LeftHandCFrame = ReplicationCFrames.LeftHandCFrame
                        NewTrackerData.RightHandCFrame = ReplicationCFrames.RightHandCFrame
                    end
                    UpdateInputs:FireServer(NewTrackerData)
                end

                --Wait 1/30th of a second to send the next set of CFrames.
                task.wait(1 / 30)
            end
        end)
    end

    return self
end

--[[
Set up Character Parts for VR.
This is also used, to refresh character parts.
--]]
function Character.SetUpVRParts(self: Character): ()
    local CharacterModel = self.CharacterModel
    local PreventArmDisconnection = self.PreventArmDisconnection

    self.Parts = {
        Head = CharacterModel:WaitForChild("Head") :: BasePart,
        UpperTorso = CharacterModel:WaitForChild("UpperTorso") :: BasePart,
        LowerTorso = CharacterModel:WaitForChild("LowerTorso") :: BasePart,
        HumanoidRootPart = CharacterModel:WaitForChild("HumanoidRootPart") :: BasePart,
        RightUpperArm = CharacterModel:WaitForChild("RightUpperArm") :: BasePart,
        RightLowerArm = CharacterModel:WaitForChild("RightLowerArm") :: BasePart,
        RightHand = CharacterModel:WaitForChild("RightHand") :: BasePart,
        LeftUpperArm = CharacterModel:WaitForChild("LeftUpperArm") :: BasePart,
        LeftLowerArm = CharacterModel:WaitForChild("LeftLowerArm") :: BasePart,
        LeftHand = CharacterModel:WaitForChild("LeftHand") :: BasePart,
        RightUpperLeg = CharacterModel:WaitForChild("RightUpperLeg") :: BasePart,
        RightLowerLeg = CharacterModel:WaitForChild("RightLowerLeg") :: BasePart,
        RightFoot = CharacterModel:WaitForChild("RightFoot") :: BasePart,
        LeftUpperLeg = CharacterModel:WaitForChild("LeftUpperLeg") :: BasePart,
        LeftLowerLeg = CharacterModel:WaitForChild("LeftLowerLeg") :: BasePart,
        LeftFoot = CharacterModel:WaitForChild("LeftFoot") :: BasePart,
    }
    self.Motors = {
        Neck = self.Parts.Head:WaitForChild("Neck") :: Motor6D,
        Waist = self.Parts.UpperTorso:WaitForChild("Waist") :: Motor6D,
        Root = self.Parts.LowerTorso:WaitForChild("Root") :: Motor6D,
        RightShoulder = self.Parts.RightUpperArm:WaitForChild("RightShoulder") :: Motor6D,
        RightElbow = self.Parts.RightLowerArm:WaitForChild("RightElbow") :: Motor6D,
        RightWrist = self.Parts.RightHand:WaitForChild("RightWrist") :: Motor6D,
        LeftShoulder = self.Parts.LeftUpperArm:WaitForChild("LeftShoulder") :: Motor6D,
        LeftElbow = self.Parts.LeftLowerArm:WaitForChild("LeftElbow") :: Motor6D,
        LeftWrist = self.Parts.LeftHand:WaitForChild("LeftWrist") :: Motor6D,
        RightHip = self.Parts.RightUpperLeg:WaitForChild("RightHip") :: Motor6D,
        RightKnee = self.Parts.RightLowerLeg:WaitForChild("RightKnee") :: Motor6D,
        RightAnkle = self.Parts.RightFoot:WaitForChild("RightAnkle") :: Motor6D,
        LeftHip = self.Parts.LeftUpperLeg:WaitForChild("LeftHip") :: Motor6D,
        LeftKnee = self.Parts.LeftLowerLeg:WaitForChild("LeftKnee") :: Motor6D,
        LeftAnkle = self.Parts.LeftFoot:WaitForChild("LeftAnkle") :: Motor6D,
    }
    self.Attachments = {
        Head = {
            NeckRigAttachment = self.Parts.Head:WaitForChild("NeckRigAttachment") :: Attachment,
        },
        UpperTorso = {
            NeckRigAttachment = self.Parts.UpperTorso:WaitForChild("NeckRigAttachment") :: Attachment,
            LeftShoulderRigAttachment = self.Parts.UpperTorso:WaitForChild("LeftShoulderRigAttachment") :: Attachment,
            RightShoulderRigAttachment = self.Parts.UpperTorso:WaitForChild("RightShoulderRigAttachment") :: Attachment,
            WaistRigAttachment = self.Parts.UpperTorso:WaitForChild("WaistRigAttachment") :: Attachment,
        },
        LowerTorso = {
            WaistRigAttachment = self.Parts.LowerTorso:WaitForChild("WaistRigAttachment") :: Attachment,
            LeftHipRigAttachment = self.Parts.LowerTorso:WaitForChild("LeftHipRigAttachment") :: Attachment,
            RightHipRigAttachment = self.Parts.LowerTorso:WaitForChild("RightHipRigAttachment") :: Attachment,
            RootRigAttachment = self.Parts.LowerTorso:WaitForChild("RootRigAttachment") :: Attachment,
        },
        HumanoidRootPart = {
            RootRigAttachment = self.Parts.HumanoidRootPart:WaitForChild("RootRigAttachment") :: Attachment,
        },
        RightUpperArm = {
            RightShoulderRigAttachment = self.Parts.RightUpperArm:WaitForChild("RightShoulderRigAttachment") :: Attachment,
            RightElbowRigAttachment = self.Parts.RightUpperArm:WaitForChild("RightElbowRigAttachment") :: Attachment,
        },
        RightLowerArm = {
            RightElbowRigAttachment = self.Parts.RightLowerArm:WaitForChild("RightElbowRigAttachment") :: Attachment,
            RightWristRigAttachment = self.Parts.RightLowerArm:WaitForChild("RightWristRigAttachment") :: Attachment,
        },
        RightHand = {
            RightWristRigAttachment = self.Parts.RightHand:WaitForChild("RightWristRigAttachment") :: Attachment,
        },
        LeftUpperArm = {
            LeftShoulderRigAttachment = self.Parts.LeftUpperArm:WaitForChild("LeftShoulderRigAttachment") :: Attachment,
            LeftElbowRigAttachment = self.Parts.LeftUpperArm:WaitForChild("LeftElbowRigAttachment") :: Attachment,
        },
        LeftLowerArm = {
            LeftElbowRigAttachment = self.Parts.LeftLowerArm:WaitForChild("LeftElbowRigAttachment") :: Attachment,
            LeftWristRigAttachment = self.Parts.LeftLowerArm:WaitForChild("LeftWristRigAttachment") :: Attachment,
        },
        LeftHand = {
            LeftWristRigAttachment = self.Parts.LeftHand:WaitForChild("LeftWristRigAttachment") :: Attachment,
        },
        RightUpperLeg = {
            RightHipRigAttachment = self.Parts.RightUpperLeg:WaitForChild("RightHipRigAttachment") :: Attachment,
            RightKneeRigAttachment = self.Parts.RightUpperLeg:WaitForChild("RightKneeRigAttachment") :: Attachment,
        },
        RightLowerLeg = {
            RightKneeRigAttachment = self.Parts.RightLowerLeg:WaitForChild("RightKneeRigAttachment") :: Attachment,
            RightAnkleRigAttachment = self.Parts.RightLowerLeg:WaitForChild("RightAnkleRigAttachment") :: Attachment,
        },
        RightFoot = {
            RightAnkleRigAttachment = self.Parts.RightFoot:WaitForChild("RightAnkleRigAttachment") :: Attachment,
            RightFootAttachment = self.Parts.RightFoot:FindFirstChild("RightFootAttachment") :: Attachment,
        },
        LeftUpperLeg = {
            LeftHipRigAttachment = self.Parts.LeftUpperLeg:WaitForChild("LeftHipRigAttachment") :: Attachment,
            LeftKneeRigAttachment = self.Parts.LeftUpperLeg:WaitForChild("LeftKneeRigAttachment") :: Attachment,
        },
        LeftLowerLeg = {
            LeftKneeRigAttachment = self.Parts.LeftLowerLeg:WaitForChild("LeftKneeRigAttachment") :: Attachment,
            LeftAnkleRigAttachment = self.Parts.LeftLowerLeg:WaitForChild("LeftAnkleRigAttachment") :: Attachment,
        },
        LeftFoot = {
            LeftAnkleRigAttachment = self.Parts.LeftFoot:WaitForChild("LeftAnkleRigAttachment") :: Attachment,
            LeftFootAttachment = self.Parts.LeftFoot:FindFirstChild("LeftFootAttachment") :: Attachment,
        },
    }

    --Force IKControl when AnimationConstraints is active.
    if not self.Motors.Neck:IsA("Motor6D") then
        self.UseIKControl = true
    end

    --Add the missing attachments that not all rigs have.
    if not self.Attachments.RightFoot.RightFootAttachment then
        local NewAttachment = Instance.new("Attachment")
        NewAttachment.Position = Vector3.new(0, -(self.Parts.RightFoot :: BasePart).Size.Y / 2, 0)
        NewAttachment.Name = "RightFootAttachment"

        local OriginalPositionValue = Instance.new("Vector3Value")
        OriginalPositionValue.Name = "OriginalPosition"
        OriginalPositionValue.Value = NewAttachment.Position
        OriginalPositionValue.Parent = NewAttachment
        NewAttachment.Parent = self.Parts.RightFoot
        self.Attachments.RightFoot.RightFootAttachment = NewAttachment
    end
    if not self.Attachments.LeftFoot.LeftFootAttachment then
        local NewAttachment = Instance.new("Attachment")
        NewAttachment.Position = Vector3.new(0, -(self.Parts.LeftFoot :: BasePart).Size.Y / 2, 0)
        NewAttachment.Name = "LeftFootAttachment"

        local OriginalPositionValue = Instance.new("Vector3Value")
        OriginalPositionValue.Name = "OriginalPosition"
        OriginalPositionValue.Value = NewAttachment.Position
        OriginalPositionValue.Parent = NewAttachment
        NewAttachment.Parent = self.Parts.LeftFoot
        self.Attachments.LeftFoot.LeftFootAttachment = NewAttachment
    end

    --Store the limbs.
    self.Head = Head.new(self.Parts.Head :: BasePart)
    self.Torso = Torso.new(self.Parts.LowerTorso :: BasePart, self.Parts.UpperTorso :: BasePart)
    if self.UseIKControl then
        self.LeftArm = Appendage.FromPreset("LeftArm", CharacterModel, not PreventArmDisconnection, self.TweenComponents and 0.1 or 0)
        self.RightArm = Appendage.FromPreset("RightArm", CharacterModel, not PreventArmDisconnection, self.TweenComponents and 0.1 or 0)
        self.LeftLeg = Appendage.FromPreset("LeftLeg", CharacterModel, false, self.TweenComponents and 0.1 or 0)
        self.RightLeg = Appendage.FromPreset("RightLeg", CharacterModel, false, self.TweenComponents and 0.1 or 0)
    else
        local LeftArm = AppendageLegacy.new(CharacterModel:WaitForChild("LeftUpperArm") :: BasePart, CharacterModel:WaitForChild("LeftLowerArm") :: BasePart, CharacterModel:WaitForChild("LeftHand") :: BasePart, "LeftShoulderRigAttachment", "LeftElbowRigAttachment", "LeftWristRigAttachment", "LeftGripAttachment", PreventArmDisconnection)
        local RightArm = AppendageLegacy.new(CharacterModel:WaitForChild("RightUpperArm") :: BasePart, CharacterModel:WaitForChild("RightLowerArm") :: BasePart, CharacterModel:WaitForChild("RightHand") :: BasePart, "RightShoulderRigAttachment", "RightElbowRigAttachment", "RightWristRigAttachment", "RightGripAttachment", PreventArmDisconnection)
        local LeftLeg = AppendageLegacy.new(CharacterModel:WaitForChild("LeftUpperLeg") :: BasePart, CharacterModel:WaitForChild("LeftLowerLeg") :: BasePart, CharacterModel:WaitForChild("LeftFoot") :: BasePart, "LeftHipRigAttachment", "LeftKneeRigAttachment", "LeftAnkleRigAttachment", "LeftFootAttachment", true)
        LeftLeg.InvertBendDirection = true
        local RightLeg = AppendageLegacy.new(CharacterModel:WaitForChild("RightUpperLeg") :: BasePart, CharacterModel:WaitForChild("RightLowerLeg") :: BasePart, CharacterModel:WaitForChild("RightFoot") :: BasePart, "RightHipRigAttachment", "RightKneeRigAttachment", "RightAnkleRigAttachment", "RightFootAttachment", true)
        RightLeg.InvertBendDirection = true
        self.LeftArm = LeftArm :: any
        self.RightArm = RightArm :: any
        self.LeftLeg = LeftLeg :: any
        self.RightLeg = RightLeg :: any
    end
    self.FootPlanter = FootPlanter:CreateSolver(CharacterModel:WaitForChild("LowerTorso"), self.Humanoid:FindFirstChild("BodyHeightScale"))
end

--[[
This sets up a connection that fires when HumanoidDescription is added
under a Humanoid, to listen for appearance changes to refresh the character parts.
--]]
function Character.SetUpAppearanceChanged(self: Character): ()
    local CharacterModel = self.CharacterModel
    local Humanoid = CharacterModel:WaitForChild("Humanoid") :: Humanoid

    --Reset connection if it already exists
    if self.AppearanceChangedConnection then
        self.AppearanceChangedConnection:Disconnect()
        self.AppearanceChangedConnection = nil
    end

    self.AppearanceChangedConnection = Humanoid.ChildAdded:Connect(function(Child)
        --If a new HumanoidDescription appeared, then something changed on the avatar.
        --We should re-ensure that everything is still connected to NexusVR.
        if Child:IsA("HumanoidDescription") then
            --Refresh character parts
            self:SetUpVRParts()
        end
    end)
end

--[[
Returns the scale value of the humanoid, or the default value.
--]]
function Character.GetHumanoidScale(self: Character, ScaleName: string): number
    local Value = self.Humanoid:FindFirstChild(ScaleName) :: NumberValue
    if Value then
        return Value.Value
    end
    return (ScaleName == "BodyTypeScale" and 0 or 1)
end

--[[
Returns the SeatPart of the humanoid.
SeatPart is not replicated to new players, which results in
strange movements of character.
https://devforum.roblox.com/t/seat-occupant-and-humanoid-seatpart-not-replicating-to-new-players-to-a-server/261545
--]]
function Character.GetHumanoidSeatPart(self: Character): BasePart?
    --Return nil if the Humanoid is not sitting.
    if not self.Humanoid.Sit then
        return nil
    end

    --Return if the seat part is defined.
    if self.Humanoid.SeatPart then
        return self.Humanoid.SeatPart
    end

    --Iterated through the connected parts and return if a seat exists.
    --While SeatPart may not be set, a SeatWeld does exist.
    for _, ConnectedPart in self.Parts.HumanoidRootPart:GetConnectedParts() do
        if ConnectedPart:IsA("Seat") or ConnectedPart:IsA("VehicleSeat") then
            return ConnectedPart
        end
    end
    return nil
end

--[[
Sets a property. The property will either be
set instantly or tweened depending on how
it is configured.
--]]
function Character.SetCFrameProperty(self: Character, Object: Instance, PropertyName: string, PropertyValue: any): ()
    if self.TweenComponents and PropertyName ~= "Transform" then
        TweenService:Create(
            Object,
            TweenInfo.new(SMOOTHING_DURATION_SECONDS, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
            {
                [PropertyName] = PropertyValue,
            }
        ):Play()
    else
        (Object :: any)[PropertyName] = PropertyValue
    end
    if PropertyName == "Transform" then
        self.CurrentMotor6DTransforms[Object :: Motor6D] = PropertyValue
    end
end

--[[
Sets the transform of a motor.
--]]
function Character.SetTransform(self: Character, MotorName: string, AttachmentName: string, StartLimbName: string, EndLimbName: string, StartCFrame: CFrame, EndCFrame: CFrame): ()
    self:SetCFrameProperty(self.Motors[MotorName], "Transform", (StartCFrame * self.Attachments[StartLimbName][AttachmentName].CFrame):Inverse() * (EndCFrame * self.Attachments[EndLimbName][AttachmentName].CFrame))
end

--[[
Refreshes the Motor6D Transforms.
Intended to be run for the local character after Stepped to override the animations.
--]]
function Character.RefreshCharacter(self: Character): ()
    if self.TweenComponents then
        local CurrentRefreshTime = tick()
        local SmoothRatio = math.min((CurrentRefreshTime - self.LastRefreshTime) / SMOOTHING_DURATION_SECONDS, 1)
        for Motor6D, Transform in self.CurrentMotor6DTransforms do
            local LastTransform = self.LastMotor6DTransforms[Motor6D]
            if LastTransform then
                Motor6D.Transform = LastTransform:Lerp(Transform, SmoothRatio)
            else
                Motor6D.Transform = Transform
            end
        end
    else
        for Motor6D, Transform in self.CurrentMotor6DTransforms do
            Motor6D.Transform = Transform
        end
    end
end

--[[
Updates the character from the inputs.
--]]
function Character.UpdateFromInputs(self: Character, HeadControllerCFrame: CFrame?, LeftHandControllerCFrame: CFrame?, RightHandControllerCFrame: CFrame?, CurrentWalkspeed: number?, TrackerData: {[string]: CFrame}?): ()
    --Return if the humanoid is dead.
    if self.Humanoid.Health <= 0 then
        return
    end

    --Call the other method if there is a SeatPart.
    --The math below is not used while in seats due to assumptions made while standing.
    --The CFrames will already be in local space from the replication.
    local SeatPart = self:GetHumanoidSeatPart()
    if SeatPart then
        self:UpdateFromInputsSeated(HeadControllerCFrame, LeftHandControllerCFrame, RightHandControllerCFrame)
        return
    end

    --Store the current Motor6D transforms.
    for Motor6D, _ in self.CurrentMotor6DTransforms do
        self.LastMotor6DTransforms[Motor6D] = Motor6D.Transform
    end
    self.LastRefreshTime = tick()

    --Get the CFrames.
    local CurrentHeadControllerCFrame = HeadControllerCFrame or (self.Parts.Head.CFrame * self.Head:GetEyesOffset())
    local HeadCFrame = self.Head:GetHeadCFrame(CurrentHeadControllerCFrame)
    local NeckCFrame = self.Head:GetNeckCFrame(CurrentHeadControllerCFrame)
    local LowerTorsoCFrame: CFrame, UpperTorsoCFrame = self.Torso:GetTorsoCFrames(NeckCFrame)
    local JointCFrames = self.Torso:GetAppendageJointCFrames(LowerTorsoCFrame, UpperTorsoCFrame)

    --Get the tracker CFrames from Enigma and fallback feet CFrames.
    local IsLocalCharacter = (Players.LocalPlayer and Players.LocalPlayer.Character == self.CharacterModel)
    local LeftFoot: CFrame, RightFoot: CFrame = self.FootPlanter:GetFeetCFrames()
    local LeftFootTrackerActive, RightFootTrackerActive = false, false
    if TrackerData and TrackerData.LeftFoot then
        LeftFoot = TrackerData.LeftFoot
        LeftFootTrackerActive = true
    else
        LeftFoot = LeftFoot * CFrame.Angles(0, math.pi, 0)
    end
    if TrackerData and TrackerData.RightFoot then
        RightFoot = TrackerData.RightFoot
        RightFootTrackerActive = true
    else
        RightFoot = RightFoot * CFrame.Angles(0, math.pi, 0)
    end
    if IsLocalCharacter then
        local NewTrackerCFrames = EnigmaService:GetCFrames(self)
        if NewTrackerCFrames.LeftFoot then
            LeftFoot = NewTrackerCFrames.LeftFoot
            LeftFootTrackerActive = true
        end
        if NewTrackerCFrames.RightFoot then
            RightFoot = NewTrackerCFrames.RightFoot
            RightFootTrackerActive = true
        end
        self.ReplicationTrackerData = {
            LeftFoot = NewTrackerCFrames.LeftFoot,
            RightFoot = NewTrackerCFrames.RightFoot,
        } :: {[string]: CFrame}
    end

    --Set the character CFrames.
    --HumanoidRootParts must always face up. This makes the math more complicated.
    --Setting the CFrame directly to something not facing directly up will result in the physics
    --attempting to correct that within the next frame, causing the character to appear to move.
    local AvatarGesturesEnabled = VRService.AvatarGestures
    local IsWalking = ((CurrentWalkspeed or self.CurrentWalkspeed) > 0.1)
    local TargetHumanoidRootPartCFrame = LowerTorsoCFrame * self.Attachments.LowerTorso.RootRigAttachment.CFrame * self.Attachments.HumanoidRootPart.RootRigAttachment.CFrame:Inverse()
    local ActualHumanoidRootPartCFrame: CFrame = self.Parts.HumanoidRootPart.CFrame
    local HumanoidRootPartHeightDifference = ActualHumanoidRootPartCFrame.Y - TargetHumanoidRootPartCFrame.Y
    local NewTargetHumanoidRootPartCFrame = CFrame.new(TargetHumanoidRootPartCFrame.Position) * CFrame.Angles(0, math.atan2(TargetHumanoidRootPartCFrame.LookVector.X, TargetHumanoidRootPartCFrame.LookVector.Z) + math.pi, 0)
    if not AvatarGesturesEnabled then
        self:SetCFrameProperty(self.Parts.HumanoidRootPart, "CFrame", CFrame.new(0, HumanoidRootPartHeightDifference, 0) * NewTargetHumanoidRootPartCFrame)
        self:SetCFrameProperty(self.Motors.Root, "Transform", CFrame.new(0, -HumanoidRootPartHeightDifference, 0) * (NewTargetHumanoidRootPartCFrame * self.Attachments.HumanoidRootPart.RootRigAttachment.CFrame):Inverse() * LowerTorsoCFrame * self.Attachments.LowerTorso.RootRigAttachment.CFrame)
        self:SetTransform("Neck", "NeckRigAttachment", "UpperTorso", "Head", UpperTorsoCFrame, HeadCFrame)
        self:SetTransform("Waist", "WaistRigAttachment", "LowerTorso", "UpperTorso", LowerTorsoCFrame, UpperTorsoCFrame)
    end
    if self.UseIKControl then
        if not AvatarGesturesEnabled and LeftHandControllerCFrame and RightHandControllerCFrame then --CFrames aren't send with AvatarGestures. Checks included for typing.
            self.LeftArm:MoveToWorld(LeftHandControllerCFrame)
            self.RightArm:MoveToWorld(RightHandControllerCFrame)
        end
        if not IsWalking and (not AvatarGesturesEnabled or LeftFootTrackerActive) then
            self.LeftLeg:MoveToWorld(LeftFoot)
            self.LeftLeg:Enable()
        else
            self.LeftLeg:Disable()
        end
        if not IsWalking and (not AvatarGesturesEnabled or RightFootTrackerActive) then
            self.RightLeg:MoveToWorld(RightFoot)
            self.RightLeg:Enable()
        else
            self.RightLeg:Disable()
        end
    else
        if not AvatarGesturesEnabled then
            local LeftUpperArmCFrame, LeftLowerArmCFrame, LeftHandCFrame = (self.LeftArm :: any):GetAppendageCFrames(JointCFrames["LeftShoulder"], LeftHandControllerCFrame)
            local RightUpperArmCFrame, RightLowerArmCFrame, RightHandCFrame = (self.RightArm :: any):GetAppendageCFrames(JointCFrames["RightShoulder"], RightHandControllerCFrame)
            self:SetTransform("RightShoulder", "RightShoulderRigAttachment", "UpperTorso", "RightUpperArm", UpperTorsoCFrame, RightUpperArmCFrame)
            self:SetTransform("RightElbow", "RightElbowRigAttachment", "RightUpperArm", "RightLowerArm", RightUpperArmCFrame, RightLowerArmCFrame)
            self:SetTransform("RightWrist", "RightWristRigAttachment", "RightLowerArm", "RightHand", RightLowerArmCFrame, RightHandCFrame)
            self:SetTransform("LeftShoulder", "LeftShoulderRigAttachment", "UpperTorso", "LeftUpperArm", UpperTorsoCFrame, LeftUpperArmCFrame)
            self:SetTransform("LeftElbow", "LeftElbowRigAttachment", "LeftUpperArm", "LeftLowerArm", LeftUpperArmCFrame, LeftLowerArmCFrame)
            self:SetTransform("LeftWrist", "LeftWristRigAttachment", "LeftLowerArm", "LeftHand", LeftLowerArmCFrame, LeftHandCFrame)
        end
        if not IsWalking and (not AvatarGesturesEnabled or LeftFootTrackerActive) then
            local LeftUpperLegCFrame, LeftLowerLegCFrame, LeftFootCFrame = (self.LeftLeg :: any):GetAppendageCFrames(JointCFrames["LeftHip"], LeftFoot)
            self:SetTransform("LeftHip", "LeftHipRigAttachment", "LowerTorso", "LeftUpperLeg", LowerTorsoCFrame, LeftUpperLegCFrame)
            self:SetTransform("LeftKnee", "LeftKneeRigAttachment", "LeftUpperLeg", "LeftLowerLeg", LeftUpperLegCFrame, LeftLowerLegCFrame)
            self:SetTransform("LeftAnkle", "LeftAnkleRigAttachment", "LeftLowerLeg", "LeftFoot", LeftLowerLegCFrame, LeftFootCFrame)
        else
            self.CurrentMotor6DTransforms[self.Motors.LeftHip] = nil
            self.CurrentMotor6DTransforms[self.Motors.LeftKnee] = nil
            self.CurrentMotor6DTransforms[self.Motors.LeftAnkle] = nil
        end
        if not IsWalking and (not AvatarGesturesEnabled or RightFootTrackerActive) then
            local RightUpperLegCFrame, RightLowerLegCFrame, RightFootCFrame = (self.RightLeg :: any):GetAppendageCFrames(JointCFrames["RightHip"], RightFoot)
            self:SetTransform("RightHip", "RightHipRigAttachment", "LowerTorso", "RightUpperLeg", LowerTorsoCFrame, RightUpperLegCFrame)
            self:SetTransform("RightKnee", "RightKneeRigAttachment", "RightUpperLeg", "RightLowerLeg", RightUpperLegCFrame, RightLowerLegCFrame)
            self:SetTransform("RightAnkle", "RightAnkleRigAttachment", "RightLowerLeg", "RightFoot", RightLowerLegCFrame, RightFootCFrame)
        else
            self.CurrentMotor6DTransforms[self.Motors.RightHip] = nil
            self.CurrentMotor6DTransforms[self.Motors.RightKnee] = nil
            self.CurrentMotor6DTransforms[self.Motors.RightAnkle] = nil
        end
    end

    --Replicate the changes to the server.
    if IsLocalCharacter then
        self.ReplicationCFrames = {
            HeadCFrame = HeadControllerCFrame :: CFrame,
            LeftHandCFrame = LeftHandControllerCFrame :: CFrame,
            RightHandCFrame = RightHandControllerCFrame :: CFrame,
        }
    end
end

--[[
Updates the character from the inputs while seated.
The CFrames are in the local space instead of global space
since the seat maintains the global space.
--]]
function Character.UpdateFromInputsSeated(self: Character, HeadControllerCFrame: CFrame?, LeftHandControllerCFrame: CFrame?, RightHandControllerCFrame: CFrame?): ()
    --Return if the humanoid is dead.
    if self.Humanoid.Health <= 0 then
        return
    end
    if VRService.AvatarGestures or not HeadControllerCFrame or not LeftHandControllerCFrame or not RightHandControllerCFrame then --CFrames aren't send with AvatarGestures. Checks included for typing.
        return
    end

    --Get the CFrames.
    local HeadCFrame = self.Head:GetHeadCFrame(HeadControllerCFrame)
    local NeckCFrame = self.Head:GetNeckCFrame(HeadControllerCFrame,0)
    local LowerTorsoCFrame, UpperTorsoCFrame = self.Torso:GetTorsoCFrames(NeckCFrame)
    local JointCFrames = self.Torso:GetAppendageJointCFrames(LowerTorsoCFrame,UpperTorsoCFrame)
    local EyesOffset = self.Head:GetEyesOffset()
    local HeightOffset = CFrame.new(0, (CFrame.new(0, EyesOffset.Y, 0) * (HeadControllerCFrame * EyesOffset:Inverse())).Y, 0)

    --Set the head, toros, and arm CFrames.
    self:SetCFrameProperty(self.Motors.Root, "Transform", HeightOffset * CFrame.new(0, -LowerTorsoCFrame.Y, 0) * LowerTorsoCFrame)
    self:SetTransform("Neck", "NeckRigAttachment", "UpperTorso", "Head", UpperTorsoCFrame, HeadCFrame)
    self:SetTransform("Waist", "WaistRigAttachment", "LowerTorso", "UpperTorso", LowerTorsoCFrame, UpperTorsoCFrame)
    if self.UseIKControl then
        local HeadWorldSpaceCFrame = (self.Parts.Head.CFrame :: CFrame) * EyesOffset
        self.LeftArm:MoveToWorld(HeadWorldSpaceCFrame * HeadControllerCFrame:Inverse() * LeftHandControllerCFrame)
        self.RightArm:MoveToWorld(HeadWorldSpaceCFrame * HeadControllerCFrame:Inverse() * RightHandControllerCFrame)
        self.LeftLeg:Disable()
        self.RightLeg:Disable()
    else
        local LeftUpperArmCFrame, LeftLowerArmCFrame, LeftHandCFrame = (self.LeftArm:: any):GetAppendageCFrames(JointCFrames["LeftShoulder"], LeftHandControllerCFrame)
        local RightUpperArmCFrame, RightLowerArmCFrame, RightHandCFrame = (self.RightArm:: any):GetAppendageCFrames(JointCFrames["RightShoulder"], RightHandControllerCFrame)
        self:SetTransform("RightShoulder", "RightShoulderRigAttachment", "UpperTorso", "RightUpperArm", UpperTorsoCFrame, RightUpperArmCFrame)
        self:SetTransform("RightElbow", "RightElbowRigAttachment", "RightUpperArm", "RightLowerArm", RightUpperArmCFrame, RightLowerArmCFrame)
        self:SetTransform("RightWrist", "RightWristRigAttachment", "RightLowerArm", "RightHand", RightLowerArmCFrame, RightHandCFrame)
        self:SetTransform("LeftShoulder", "LeftShoulderRigAttachment", "UpperTorso", "LeftUpperArm", UpperTorsoCFrame, LeftUpperArmCFrame)
        self:SetTransform("LeftElbow", "LeftElbowRigAttachment", "LeftUpperArm", "LeftLowerArm", LeftUpperArmCFrame, LeftLowerArmCFrame)
        self:SetTransform("LeftWrist", "LeftWristRigAttachment", "LeftLowerArm", "LeftHand", LeftLowerArmCFrame, LeftHandCFrame)
    end

    --Reset the leg transforms to allow for animations.
    self.CurrentMotor6DTransforms[self.Motors.RightHip] = nil
    self.CurrentMotor6DTransforms[self.Motors.LeftHip] = nil
    self.CurrentMotor6DTransforms[self.Motors.RightKnee] = nil
    self.CurrentMotor6DTransforms[self.Motors.LeftKnee] = nil
    self.CurrentMotor6DTransforms[self.Motors.RightAnkle] = nil
    self.CurrentMotor6DTransforms[self.Motors.LeftAnkle] = nil

    --Replicate the changes to the server.
    if Players.LocalPlayer and Players.LocalPlayer.Character == self.CharacterModel then
        self.ReplicationCFrames = {
            HeadCFrame = HeadControllerCFrame,
            LeftHandCFrame = LeftHandControllerCFrame,
            RightHandCFrame = RightHandControllerCFrame,
        }
    end
end

return Character


--Manages the local camera.
--!strict

local NexusVRCharacterModel = script.Parent.Parent
local CommonCamera = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Camera"):WaitForChild("CommonCamera"))
local DefaultCamera = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Camera"):WaitForChild("DefaultCamera"))
local ThirdPersonTrackCamera = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Camera"):WaitForChild("ThirdPersonTrackCamera"))

local CameraService = {}
CameraService.__index = CameraService
local StaticInstance = nil

export type CameraService = {
    ActiveCamera: string,
    CurrentCamera: CameraInterface?,
    RegisteredCameras: {[string]: CameraInterface},
} & typeof(setmetatable({}, CameraService))

export type CameraInterface = {
    Enable: (self: CameraInterface) -> (),
    Disable: (self: CameraInterface) -> (),
    UpdateCamera: (self: CameraInterface, HeadsetCFrameWorld: CFrame) -> (),
}

--[[
Creates a camera service.
--]]
function CameraService.new(): CameraService
    --Create the object.
    local self = setmetatable({
        RegisteredCameras = {},
    }, CameraService) :: CameraService

    --Register the default controllers.
    self:RegisterCamera("Default", DefaultCamera.new() :: any)
    self:RegisterCamera("ThirdPersonTrack", ThirdPersonTrackCamera.new() :: any)
    self:RegisterCamera("Disabled", CommonCamera.new() :: any)

    --Return the object.
    return self
end

--[[
Returns a singleton instance of the camera service.
--]]
function CameraService.GetInstance(): CameraService
    if not StaticInstance then
        StaticInstance = CameraService.new()
    end
    return StaticInstance
end

--[[
Registers a camera.
--]]
function CameraService.RegisterCamera(self: CameraService, Name: string, Camera: CameraInterface): ()
    self.RegisteredCameras[Name] = Camera
end

--[[
Sets the active camera.
--]]
function CameraService.SetActiveCamera(self: CameraService, Name: string): ()
    --Return if the camera didn't change.
    if self.ActiveCamera == Name then return end
    self.ActiveCamera = Name

    --Disable the current camera.
    if self.CurrentCamera then
        self.CurrentCamera:Disable()
    end

    --Enable the new camera.
    self.CurrentCamera = self.RegisteredCameras[Name]
    if self.CurrentCamera then
        self.CurrentCamera:Enable()
    elseif Name ~= nil then
        warn(`Nexus VR Character Model camera \"{Name}\" is not registered.`)
    end
end

--[[
Updates the local camera.
--]]
function CameraService.UpdateCamera(self: CameraService,HeadsetCFrameWorld: CFrame): ()
    if self.CurrentCamera then
        self.CurrentCamera:UpdateCamera(HeadsetCFrameWorld)
    end
end

return CameraService


--Manages VR characters.
--!strict

local Players = game:GetService("Players")

local NexusVRCharacterModel = script.Parent.Parent
local Character = require(NexusVRCharacterModel:WaitForChild("Character"))

local CharacterService = {}
CharacterService.__index = CharacterService
local StaticInstance = nil

export type CharacterService = {
    Characters: {[Player]: {
        Character: Model,
        VRCharacter: Character.Character,
    }},
} & typeof(setmetatable({}, CharacterService))

--[[
Creates a character service.
--]]
function CharacterService.new(): CharacterService
    --Create the object.
    local self = setmetatable({
        Characters = {},
    }, CharacterService) :: CharacterService

    --Connect clearing players.
    Players.PlayerRemoving:Connect(function(Player)
        self.Characters[Player] = nil
    end)

    --Return the object.
    return self
end

--[[
Returns a singleton instance of the character service.
--]]
function CharacterService.GetInstance(): CharacterService
    if not StaticInstance then
        StaticInstance = CharacterService.new()
    end
    return StaticInstance
end

--[[
Returns the VR character for a player.
--]]
function CharacterService.GetCharacter(self: CharacterService, Player: Player): Character.Character?
    --Return if the character is nil.
    if not Player.Character or not Player.Character:FindFirstChild("Head") then
        return nil
    end

    --Create the VR character if it isn't valid.
    local PlayerCharacter = self.Characters[Player]
    if not PlayerCharacter or PlayerCharacter.Character ~= Player.Character then
        self.Characters[Player] = {
            Character = Player.Character,
            VRCharacter = Character.new(Player.Character :: Model),
        }
    end

    --Return the stored character.
    return self.Characters[Player].VRCharacter
end

--[[
Refreshes all the characters.
--]]
function CharacterService.RefreshAllCharacters(self: CharacterService): ()
    for _, Character in self.Characters do
        Character.VRCharacter:RefreshCharacter()
    end
end

return CharacterService


--Manages controlling the local characters.
--!strict

local NexusVRCharacterModel = script.Parent.Parent
local BaseController = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Controller"):WaitForChild("BaseController"))
local TeleportController = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Controller"):WaitForChild("TeleportController"))
local SmoothLocomotionController = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Controller"):WaitForChild("SmoothLocomotionController"))

local ControlService = {}
ControlService.__index = ControlService
local StaticInstance = nil

export type ControlService = {
    RegisteredControllers: {[string]: ControllerInterface},
    ActiveController: string,
    CurrentController: ControllerInterface?,
} & typeof(setmetatable({}, ControlService))

export type ControllerInterface = {
    Disable: (self: ControllerInterface) -> (),
    Enable: (self: ControllerInterface) -> (),
    UpdateCharacter: (self: ControllerInterface) -> (),
}

--[[
Creates a control service.
--]]
function ControlService.new(): ControlService
    --Create the object.
    local self = setmetatable({
        RegisteredControllers = {},
    }, ControlService) :: ControlService

    --Register the default controllers.
    local EmptyController = BaseController.new()
    EmptyController.ActionsToLock = {Enum.KeyCode.Thumbstick1, Enum.KeyCode.Thumbstick2, Enum.KeyCode.ButtonR3, Enum.KeyCode.ButtonA}
    self:RegisterController("None", EmptyController :: any)
    self:RegisterController("Teleport", TeleportController.new() :: any)
    self:RegisterController("SmoothLocomotion", SmoothLocomotionController.new() :: any)

    --Return the object.
    return self
end

--[[
Returns a singleton instance of the character service.
--]]
function ControlService.GetInstance(): ControlService
    if not StaticInstance then
        StaticInstance = ControlService.new()
    end
    return StaticInstance
end

--[[
Registers a controller.
--]]
function ControlService.RegisterController(self: ControlService, Name: string, Controller: ControllerInterface): ()
    self.RegisteredControllers[Name] = Controller
end

--[[
Sets the active controller.
--]]
function ControlService.SetActiveController(self: ControlService, Name: string): ()
    --Return if the controller didn't change.
    if self.ActiveController == Name then return end
    self.ActiveController = Name

    --Disable the current controller.
    if self.CurrentController then
        self.CurrentController:Disable()
    end

    --Enable the new controller.
    self.CurrentController = self.RegisteredControllers[Name]
    if self.CurrentController then
        self.CurrentController:Enable()
    elseif Name ~= nil then
        warn(`Nexus VR Character Model controller \"{Name}\" is not registered.`)
    end
end

--[[
Updates the local character.
--]]
function ControlService:UpdateCharacter()
    if self.CurrentController then
        self.CurrentController:UpdateCharacter()
    end
end

return ControlService


--Manages reeading UserCframes from Enigma.
--!strict

local THUMBSTICK_DEADZONE = 0.2

local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")

local NexusVRCharacterModel = script.Parent.Parent
local Enigma = require(NexusVRCharacterModel:WaitForChild("Packages"):WaitForChild("Enigma"))
local Head = require(NexusVRCharacterModel:WaitForChild("Character"):WaitForChild("Head"))

local EnigmaService = {}
EnigmaService.__index = EnigmaService
local StaticInstance = nil

export type EnigmaService = {
    Offsets: {
        LeftFoot: CFrame?,
        RightFoot: CFrame?,
    },
} & typeof(setmetatable({}, EnigmaService))
export type TrackerData = {
    LeftFoot: CFrame?,
    RightFoot: CFrame?,
}

--[[
Creates an Enigma service.
--]]
function EnigmaService.new(): EnigmaService
    return setmetatable({
        Offsets = {},
    }, EnigmaService) :: EnigmaService
end

--[[
Returns a singleton instance of the Enigma service.
--]]
function EnigmaService.GetInstance(): EnigmaService
    if not StaticInstance then
        StaticInstance = EnigmaService.new()
    end
    return StaticInstance
end

--[[
Returns the CFrames for the trackers.
--]]
function EnigmaService.GetCFrames(self: EnigmaService, Character: any): TrackerData
    if not Enigma.Enabled then return {} end
    local HeadsetWorldCFrame = Character.Parts.Head.CFrame * Character.Head:GetEyesOffset()
    local OriginWorldCFrame = HeadsetWorldCFrame * UserInputService:GetUserCFrame(Enum.UserCFrame.Head):Inverse()

    local LeftFootTrackerCFrame = Enigma:GetUserCFrame("LeftFoot")
    local RightFootTrackerCFrame = Enigma:GetUserCFrame("RightFoot")
    local Offsets = {}
    if LeftFootTrackerCFrame and self.Offsets.LeftFoot then
        Offsets.LeftFoot = OriginWorldCFrame * LeftFootTrackerCFrame * self.Offsets.LeftFoot
    end
    if RightFootTrackerCFrame and self.Offsets.RightFoot then
        Offsets.RightFoot = OriginWorldCFrame * RightFootTrackerCFrame * self.Offsets.RightFoot
    end
    return Offsets
end

--[[
Calibrates any inputs based on the current inputs.
--]]
function EnigmaService.Calibrate(self: EnigmaService, Character: any): ()
    if not Enigma.Enabled then return end
    local LeftFootTrackerCFrame = Enigma:GetUserCFrame("LeftFoot")
    local RightFootTrackerCFrame = Enigma:GetUserCFrame("RightFoot")
    if not LeftFootTrackerCFrame and not RightFootTrackerCFrame then return end
    local Attachments = Character.Attachments
    local HeadsetWorldCFrame = Workspace.CurrentCamera:GetRenderCFrame()
    local OriginWorldCFrame = HeadsetWorldCFrame * UserInputService:GetUserCFrame(Enum.UserCFrame.Head):Inverse()
    local FloorWorldCFrame = OriginWorldCFrame * UserInputService:GetUserCFrame(Enum.UserCFrame.Floor)

    local NewCharacterHead = Head.new(Character.Parts.Head)
    local HeadWorldCFrame = NewCharacterHead:GetHeadCFrame(HeadsetWorldCFrame)
    local NeckWorldCFrame = NewCharacterHead:GetNeckCFrame(HeadWorldCFrame)
    local LowerTorsoCFrame = NeckWorldCFrame * Attachments.UpperTorso.NeckRigAttachment.CFrame:Inverse() * Attachments.UpperTorso.WaistRigAttachment.CFrame * Attachments.LowerTorso.WaistRigAttachment.CFrame:Inverse()

    if LeftFootTrackerCFrame then
        local LeftFootTrackerWorldCFrame = OriginWorldCFrame * LeftFootTrackerCFrame
        local LeftUpperLegCFrame = LowerTorsoCFrame * Attachments.LowerTorso.LeftHipRigAttachment.CFrame * Attachments.LeftUpperLeg.LeftHipRigAttachment.CFrame:Inverse()
        local LeftLowerLegCFrame = LeftUpperLegCFrame * Attachments.LeftUpperLeg.LeftKneeRigAttachment.CFrame * Attachments.LeftLowerLeg.LeftKneeRigAttachment.CFrame:Inverse()
        local LeftFootCFrame = LeftLowerLegCFrame * Attachments.LeftLowerLeg.LeftAnkleRigAttachment.CFrame * Attachments.LeftFoot.LeftAnkleRigAttachment.CFrame:Inverse()
        local LeftFootBottomCFrame = LeftFootCFrame * Attachments.LeftFoot.LeftFootAttachment.CFrame
        LeftFootCFrame = CFrame.new(0, FloorWorldCFrame.Y - LeftFootBottomCFrame.Y, 0) * LeftFootCFrame
        self.Offsets.LeftFoot = LeftFootTrackerWorldCFrame:Inverse() * LeftFootCFrame
    end
    if RightFootTrackerCFrame then
        local RightFootTrackerWorldCFrame = OriginWorldCFrame * RightFootTrackerCFrame
        local RightUpperLegCFrame = LowerTorsoCFrame * Attachments.LowerTorso.RightHipRigAttachment.CFrame * Attachments.RightUpperLeg.RightHipRigAttachment.CFrame:Inverse()
        local RightLowerLegCFrame = RightUpperLegCFrame * Attachments.RightUpperLeg.RightKneeRigAttachment.CFrame * Attachments.RightLowerLeg.RightKneeRigAttachment.CFrame:Inverse()
        local RightFootCFrame = RightLowerLegCFrame * Attachments.RightLowerLeg.RightAnkleRigAttachment.CFrame * Attachments.RightFoot.RightAnkleRigAttachment.CFrame:Inverse()
        local RightFootBottomCFrame = RightFootCFrame * Attachments.RightFoot.RightFootAttachment.CFrame
        RightFootCFrame = CFrame.new(0, FloorWorldCFrame.Y - RightFootBottomCFrame.Y, 0) * RightFootCFrame
        self.Offsets.RightFoot = RightFootTrackerWorldCFrame:Inverse() * RightFootCFrame
    end
end

--[[
Enables Enigma.
--]]
function EnigmaService.Enable(self: EnigmaService): ()
    --Enable Enigma.
    Enigma:Enable()

    --Implement the inputs.
    --Pasting data interrupts the gamepad inputs.
    local LastLeftThumbstick = Vector3.zero
    local ButtonADown = false
    UserInputService.InputBegan:Connect(function(Input)
        if Input.KeyCode == Enum.KeyCode.ButtonA then
            ButtonADown = true
        end
    end)
    UserInputService.InputChanged:Connect(function(Input)
        if Input.KeyCode == Enum.KeyCode.Thumbstick1 then
            if Input.Position.Magnitude > THUMBSTICK_DEADZONE then
                LastLeftThumbstick = Input.Position
            else
                LastLeftThumbstick = Vector3.zero
            end
        end
    end)
    UserInputService.InputEnded:Connect(function(Input)
        if Input.KeyCode == Enum.KeyCode.Thumbstick1 then
            LastLeftThumbstick = Vector3.zero
        elseif Input.KeyCode == Enum.KeyCode.ButtonA then
            ButtonADown = false
        end
    end)

    RunService:BindToRenderStep("EnigmaCustomMovement", Enum.RenderPriority.Input.Value + 1, function()
        --Return if Enigma isn't active.
        if not Enigma:IsActive() then return end

        --Return if the character is invalid.
        local Character = Players.LocalPlayer.Character
        if not Character then return end
        local Humanoid = Character:FindFirstChildOfClass("Humanoid")
        if not Humanoid or Humanoid.Health <= 0 then return end

        --Move the character.
        local MovementReferenceWorldCFrame = Workspace.CurrentCamera:GetRenderCFrame()
        local MovementReferenceRotation = CFrame.new(-MovementReferenceWorldCFrame.Position) * MovementReferenceWorldCFrame
        local MoveDirection = (MovementReferenceRotation * CFrame.new(LastLeftThumbstick.X, 0, -LastLeftThumbstick.Y)).Position
        if MoveDirection.Magnitude > 0.01 or not VRService.AvatarGestures then --Move is not overriden with AvatarGestures since it does continous movements when not walking.
            Players.LocalPlayer:Move(MoveDirection, false)
        end
        if ButtonADown then
            Humanoid.Jump = true
        end
    end)
end

return EnigmaService


--Stores settings.
--!strict

local NexusVRCharacterModel = script.Parent.Parent
local NexusInstance = require(NexusVRCharacterModel:WaitForChild("Packages"):WaitForChild("NexusInstance"))

local TypedEvent = NexusInstance.TypedEvent

local Settings ={}
Settings.__index = Settings
local StaticInstance = nil

export type Settings ={
    Defaults: any,
    Overrides: any,
    SettingsChangeEvents: {[string]: NexusInstance.TypedEvent<>},
    SettingsCache: {[string]: any},
} & typeof(setmetatable({}, Settings))

--[[
Creates a settings object.
--]]
function Settings.new(): Settings
    return setmetatable({
        Defaults = {},
        Overrides = {},
        SettingsChangeEvents = {},
        SettingsCache = {},
    }, Settings) :: Settings
end

--[[
Returns a singleton instance of settings.
--]]
function Settings.GetInstance(): Settings
    if not StaticInstance then
        StaticInstance = Settings.new()
    end
    return StaticInstance
end

--[[
Returns the value of a setting.
--]]
function Settings.GetSetting(self: Settings, Setting: string): any
    --Return a cached entry if one exists.
    if self.SettingsCache[Setting] ~= nil then
        return self.SettingsCache[Setting]
    end

    --Get the table containing the setting.
    local Defaults, Overrides = self.Defaults, self.Overrides
    local SplitSettingNames = string.split(Setting, ".")
    for i = 1, #SplitSettingNames - 1 do
        Defaults = Defaults[SplitSettingNames[i]] or {}
        Overrides = Overrides[SplitSettingNames[i]] or {}
    end

    --Return the value.
    local Value = Overrides[SplitSettingNames[#SplitSettingNames]]
    if Value == nil then
        Value = Defaults[SplitSettingNames[#SplitSettingNames]]
    end
    self.SettingsCache[Setting] = Value
    return Value
end

--[[
Sets the value of a setting.
--]]
function Settings.SetSetting(self: Settings, Setting: string, Value: any): ()
    --Set the setting.
    local Overrides = self.Overrides
    local SplitSettingNames = string.split(Setting,".")
    for i = 1, #SplitSettingNames - 1 do
        if not Overrides[SplitSettingNames[i]] then
            Overrides[SplitSettingNames[i]] = {}
        end
        Overrides = Overrides[SplitSettingNames[i]]
    end
    Overrides[SplitSettingNames[#SplitSettingNames]] = Value
    self.SettingsCache[Setting] = Value

    --Fire the changed signal.
    local Event = self.SettingsChangeEvents[string.lower(Setting)]
    if Event then
        Event:Fire()
    end
end

--[[
Sets all the defaults.
--]]
function Settings.SetDefaults(self: Settings, Defaults: {[string]: any}): ()
    --Set the defaults.
    self.Defaults = Defaults
    self.SettingsCache = {}

    --Fire all the event changes.
    for _, Event in self.SettingsChangeEvents do
        (Event :: NexusInstance.TypedEvent<>):Fire()
    end
end

--[[
Sets all the overrides.
--]]
function Settings.SetOverrides(self: Settings, Overrides: {[string]: any}): ()
    --Set the overrides.
    self.Overrides = Overrides
    self.SettingsCache = {}

    --Fire all the event changes.
    for _, Event in self.SettingsChangeEvents do
        (Event :: NexusInstance.TypedEvent<>):Fire()
    end
end

--[[
Returns a changed signal for a setting.
--]]
function Settings.GetSettingsChangedSignal(self: Settings, Overrides: string): NexusInstance.TypedEvent<>
    Overrides = string.lower(Overrides)

    --Create the event if none exists.
    if not self.SettingsChangeEvents[Overrides] then
        self.SettingsChangeEvents[Overrides] = TypedEvent.new()
    end

    --Return the event.
    return self.SettingsChangeEvents[Overrides]
end

--[[
Destroys the settings.
--]]
function Settings.Destroy(self: Settings): ()
    --Disconnect the settings.
    for _,Event in self.SettingsChangeEvents do
        (Event :: NexusInstance.TypedEvent<>):Destroy()
    end
    self.SettingsChangeEvents = {}
end

return Settings


--Manages VR inputs. This normalizes the inputs from
--the headsets as the Y position of the inputs is arbitrary,
--meaning it can be the floor, eye level, or random.
--!strict

local NexusVRCharacterModel = script.Parent.Parent
local NexusInstance = require(NexusVRCharacterModel:WaitForChild("Packages"):WaitForChild("NexusInstance"))

local TypedEvent = NexusInstance.TypedEvent

local VRInputService = {}
VRInputService.__index = VRInputService
local StaticInstance = nil

export type VRInputService = {
    RecenterOffset: CFrame,
    ManualNormalHeadLevel: number?,
    HighestHeadHeight: number?,
    ThumbstickValues: {[Enum.KeyCode]: Vector3},
    VRService: VRService,
    UserInputService: UserInputService,
    Recentered: NexusInstance.TypedEvent<>,
    EyeLevelSet: NexusInstance.TypedEvent<>,
} & typeof(setmetatable({}, VRInputService))

--[[
Creates a settings object.
--]]
function VRInputService.new(VRService: VRService?, UserInputService: UserInputService?): VRInputService
    --Create the object.
    local self = setmetatable({
        RecenterOffset = CFrame.identity,
        ThumbstickValues = {
            [Enum.KeyCode.Thumbstick1] = Vector3.zero,
            [Enum.KeyCode.Thumbstick2] = Vector3.zero,
        },
        VRService = VRService or game:GetService("VRService"),
        UserInputService = UserInputService or game:GetService("UserInputService"),
        Recentered = TypedEvent.new(),
        EyeLevelSet = TypedEvent.new(),
    }, VRInputService) :: VRInputService

    --Connect updating the thumbsticks.
    self.UserInputService.InputEnded:Connect(function(Input)
        if self.ThumbstickValues[Input.KeyCode] then
            self.ThumbstickValues[Input.KeyCode] = Vector3.zero
        end
    end)
    self.UserInputService.InputChanged:Connect(function(Input)
        if self.ThumbstickValues[Input.KeyCode] then
            self.ThumbstickValues[Input.KeyCode] = Input.Position
        end
    end)

    --Return the object.
    return self
end

--[[
Returns a singleton instance of the VR input service.
--]]
function VRInputService.GetInstance(): VRInputService
    if not StaticInstance then
        StaticInstance = VRInputService.new()
    end
    return StaticInstance
end

--[[
Returns the VR inputs to use. The inputs are normalized
so that 0 is the head height.
--]]
function VRInputService.GetVRInputs(self: VRInputService): {[Enum.UserCFrame]: CFrame}
    --Get the head input.
    local VRInputs = {
        [Enum.UserCFrame.Head] = self.VRService:GetUserCFrame(Enum.UserCFrame.Head),
    } :: {[Enum.UserCFrame]: CFrame}

    --Get the hand inputs.
    if self.VRService:GetUserCFrameEnabled(Enum.UserCFrame.LeftHand) then
        VRInputs[Enum.UserCFrame.LeftHand] = self.VRService:GetUserCFrame(Enum.UserCFrame.LeftHand)
    else
        VRInputs[Enum.UserCFrame.LeftHand] = VRInputs[Enum.UserCFrame.Head] * CFrame.new(-1, -2.5, 0.5)
    end
    if self.VRService:GetUserCFrameEnabled(Enum.UserCFrame.RightHand) then
        VRInputs[Enum.UserCFrame.RightHand] = self.VRService:GetUserCFrame(Enum.UserCFrame.RightHand)
    else
        VRInputs[Enum.UserCFrame.RightHand] = VRInputs[Enum.UserCFrame.Head] * CFrame.new(1, -2.5, 0.5)
    end

    --Determine the height offset.
    local HeightOffset = 0
    if self.ManualNormalHeadLevel then
        --Adjust to normalize the height around the set value.
        HeightOffset = -self.ManualNormalHeadLevel
    else
        --Adjust to normalize the height around the highest value.
        --The head CFrame is moved back 0.5 studs for when the headset suddenly goes up (like putting on and taking off).
        local CurrentVRHeadHeight = (VRInputs[Enum.UserCFrame.Head] * CFrame.new(0, 0, 0.5)).Y
        if not self.HighestHeadHeight or CurrentVRHeadHeight > self.HighestHeadHeight then
            self.HighestHeadHeight = CurrentVRHeadHeight
        end
        HeightOffset = -self.HighestHeadHeight :: number
    end

    --Normalize the CFrame heights.
    --A list of enums is used instead of VRInputs because modifying a table stops pairs().
    for _, InputEnum in {Enum.UserCFrame.Head, Enum.UserCFrame.LeftHand, Enum.UserCFrame.RightHand} do
        VRInputs[InputEnum] = CFrame.new(0, HeightOffset, 0) * self.RecenterOffset * VRInputs[InputEnum]
    end

    --Return the CFrames.
    return VRInputs
end

--[[
Recenters the service.
Does not alter the Y axis.
--]]
function VRInputService.Recenter(self: VRInputService): ()
    local HeadCFrame = self.VRService:GetUserCFrame(Enum.UserCFrame.Head)
    self.RecenterOffset = CFrame.Angles(0, -math.atan2(-HeadCFrame.LookVector.X, -HeadCFrame.LookVector.Z), 0) * CFrame.new(-HeadCFrame.X, 0, -HeadCFrame.Z)
    self.Recentered:Fire()
end

--[[
Sets the eye level.
--]]
function VRInputService.SetEyeLevel(self: VRInputService): ()
    self.ManualNormalHeadLevel = self.VRService:GetUserCFrame(Enum.UserCFrame.Head).Y
    self.EyeLevelSet:Fire()
end

--[[
Returns the current value for a thumbstick.
--]]
function VRInputService.GetThumbstickPosition(self: VRInputService, Thumbsick: Enum.KeyCode): Vector3
    return self.ThumbstickValues[Thumbsick] or Vector3.zero
end

return VRInputService