This has some work to do, so lets start from the beginning. You can use the CFrame value directly to tween the head and do not have to convert it into an Orientation.
Your code to find rawCF is wonky, and ill go over it.
local rawCF = (CFrame.new(npc.Head.Position,game.Players.LocalPlayer.Character.PrimaryPart.Position):Inverse() * npc.HumanoidRootPart.CFrame ).lookVector * -1
It finds the CFrame.LookAt between the npc’s head and the players HumanoidRootPart, then Inverses it (converts all positive numbers negative and all negative numbers positive in the CFrame), adds an offset to that, and then grabs a directional vector.
Instead, you can cut down almost all of it into a single CFrame
local rawCF = CFrame.new(npc.Head.Position, game.Players.LocalPlayer.Character.Head.Position)
This CFrame is the same constructor as CFrame.LookAt, which as the name might apply, transforms one CFrame to look at another. It also comes with a 3rd parameter that describes the UpVector, but thats defaulted to (0,1,0) which works in this case. It returns a CFrame that is rotated to look at the goal
then to add limits, you can use Vector3.new:Angle() to find an angle between the Original LookVector, and the one that it wants to go to.
local GoalCF = CFrame.new()
if Angle < DesiredAngle then
GoalCF = rawCF
else
GoalCF = npc.Head.CFrame--if it limits, then dont move the head
end
That does mean that the head travels in a cone instead of a limited x and y angle, but if you need a limited x and y Orientation, you can always convert a CFrame’s Rotation into Orientation using:
local x,y,z = CFrame.new:ToOrientation() --descibes the x,y,z angles that make up the rotation in radians.
so some psuedo code to put it all together goes like this:
--varibles
local MaxAngle = 45
local OrigonalCFrame = npc.Head.CFrame
--calculation
local rawCF = CFrame.new(npc.Head.Position, game.Players.LocalPlayer.Character.Head.Position)
local GoalCF = CFrame.new()
local Angle = OrigonalCFrame.LookVector:Angle(rawCF.LookVector)
if Angle < math.deg(Angle) then
GoalCF = rawCF
else
GoalCF = npc.Head.CFrame--if it limits, then dont move the head
end
game:GetService("TweenService"):Create(npc.Head, TweenInfo.new(0.3), {CFrame = GoalCF}):Play()
Sorry its sloppy, I wrote this in a rush. Hope this helps!