local q=require(script.Parent:WaitForChild("\066\097\115\101\067\097\109\101\114\097"))local w=setmetatable({},q)w.__index=w function w.new()local D=setmetatable(q.new(),w)D.lastUpdate=tick()D.changedSignalConnections={}D.refAzimuthRad=nil D.curAzimuthRad=nil D.minAzimuthAbsoluteRad=nil D.maxAzimuthAbsoluteRad=nil D.useAzimuthLimits=nil D.curElevationRad=nil D.minElevationRad=nil D.maxElevationRad=nil D.curDistance=nil D.minDistance=nil D.maxDistance=nil D.r3ButtonDown=false D.l3ButtonDown=false D.gamepadDollySpeedMultiplier=1 D.lastUserPanCamera=tick()D.externalProperties={}D.externalProperties.InitialDistance=25 D.externalProperties.MinDistance=10 D.externalProperties.MaxDistance=100 D.externalProperties.InitialElevation=35 D.externalProperties.MinElevation=35 D.externalProperties.MaxElevation=35 D.externalProperties.ReferenceAzimuth=-45 D.externalProperties.CWAzimuthTravel=90 D.externalProperties.CCWAzimuthTravel=90 D.externalProperties.UseAzimuthLimits=false D:LoadNumberValueParameters()return D end function w.LoadOrCreateNumberValueParameter(s,q,w,D)local e=script:FindFirstChild(q)if e and e:isA(w)then s.externalProperties[q]=e.Value elseif s.externalProperties[q]~=nil then e=Instance.new(w)e.Name=q e.Parent=script e.Value=s.externalProperties[q]else print("\101\120\116\101\114\110\097\108\080\114\111\112\101\114\116\105\101\115 \116\097\098\108\101 \104\097\115 \110\111 \101\110\116\114\121 \102\111\114 ",q)return end if D then if s.changedSignalConnections[q]then s.changedSignalConnections[q]:Disconnect()end s.changedSignalConnections[q]=e.Changed:Connect(function(w)s.externalProperties[q]=w D(s)end)end end function w.SetAndBoundsCheckAzimuthValues(q)q.minAzimuthAbsoluteRad=math.rad(q.externalProperties.ReferenceAzimuth)-math.abs(math.rad(q.externalProperties.CWAzimuthTravel))q.maxAzimuthAbsoluteRad=math.rad(q.externalProperties.ReferenceAzimuth)+math.abs(math.rad(q.externalProperties.CCWAzimuthTravel))q.useAzimuthLimits=q.externalProperties.UseAzimuthLimits if q.useAzimuthLimits then q.curAzimuthRad=math.max(q.curAzimuthRad,q.minAzimuthAbsoluteRad)q.curAzimuthRad=math.min(q.curAzimuthRad,q.maxAzimuthAbsoluteRad)end end function w.SetAndBoundsCheckElevationValues(q)local w=math.max(q.externalProperties.MinElevation,MIN_ALLOWED_ELEVATION_DEG)local D=math.min(q.externalProperties.MaxElevation,MAX_ALLOWED_ELEVATION_DEG)q.minElevationRad=math.rad(math.min(w,D))q.maxElevationRad=math.rad(math.max(w,D))q.curElevationRad=math.max(q.curElevationRad,q.minElevationRad)q.curElevationRad=math.min(q.curElevationRad,q.maxElevationRad)end function w.SetAndBoundsCheckDistanceValues(q)q.minDistance=q.externalProperties.MinDistance q.maxDistance=q.externalProperties.MaxDistance q.curDistance=math.max(q.curDistance,q.minDistance)q.curDistance=math.min(q.curDistance,q.maxDistance)end
--
local BaseCamera = require(script.Parent:WaitForChild("BaseCamera"))
local OrbitalCamera = setmetatable({}, BaseCamera)
OrbitalCamera.__index = OrbitalCamera


function OrbitalCamera.new()
	local self = setmetatable(BaseCamera.new(), OrbitalCamera)

	self.lastUpdate = tick()

	-- OrbitalCamera-specific members
	self.changedSignalConnections = {}
	self.refAzimuthRad = nil
	self.curAzimuthRad = nil
	self.minAzimuthAbsoluteRad = nil
	self.maxAzimuthAbsoluteRad = nil
	self.useAzimuthLimits = nil
	self.curElevationRad = nil
	self.minElevationRad = nil
	self.maxElevationRad = nil
	self.curDistance = nil
	self.minDistance = nil
	self.maxDistance = nil

	-- Gamepad
	self.r3ButtonDown = false
	self.l3ButtonDown = false
	self.gamepadDollySpeedMultiplier = 1

	self.lastUserPanCamera = tick()

	self.externalProperties = {}
	self.externalProperties["InitialDistance"] 	= 25
	self.externalProperties["MinDistance"] 		= 10
	self.externalProperties["MaxDistance"] 		= 100
	self.externalProperties["InitialElevation"] 	= 35
	self.externalProperties["MinElevation"] 		= 35
	self.externalProperties["MaxElevation"] 		= 35
	self.externalProperties["ReferenceAzimuth"] 	= -45	-- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
	self.externalProperties["CWAzimuthTravel"] 	= 90	-- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
	self.externalProperties["CCWAzimuthTravel"] 	= 90	-- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
	self.externalProperties["UseAzimuthLimits"] 	= false -- Full rotation around Y axis available by default
	self:LoadNumberValueParameters()

	return self
end

function OrbitalCamera:LoadOrCreateNumberValueParameter(name, valueType, updateFunction)
	local valueObj = script:FindFirstChild(name)

	if valueObj and valueObj:isA(valueType) then
		-- Value object exists and is the correct type, use its value
		self.externalProperties[name] = valueObj.Value
	elseif self.externalProperties[name] ~= nil then
		-- Create missing (or replace incorrectly-typed) valueObject with default value
		valueObj = Instance.new(valueType)
		valueObj.Name = name
		valueObj.Parent = script
		valueObj.Value = self.externalProperties[name]
	else
		print("externalProperties table has no entry for ",name)
		return
	end

	if updateFunction then
		if self.changedSignalConnections[name] then
			self.changedSignalConnections[name]:Disconnect()
		end
		self.changedSignalConnections[name] = valueObj.Changed:Connect(function(newValue)
			self.externalProperties[name] = newValue
			updateFunction(self)
		end)
	end
end

function OrbitalCamera:SetAndBoundsCheckAzimuthValues()
	self.minAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) - math.abs(math.rad(self.externalProperties["CWAzimuthTravel"]))
	self.maxAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) + math.abs(math.rad(self.externalProperties["CCWAzimuthTravel"]))
	self.useAzimuthLimits = self.externalProperties["UseAzimuthLimits"]
	if self.useAzimuthLimits then
		self.curAzimuthRad = math.max(self.curAzimuthRad, self.minAzimuthAbsoluteRad)
		self.curAzimuthRad = math.min(self.curAzimuthRad, self.maxAzimuthAbsoluteRad)
	end
end

function OrbitalCamera:SetAndBoundsCheckElevationValues()
	-- These degree values are the direct user input values. It is deliberate that they are
	-- ranged checked only against the extremes, and not against each other. Any time one
	-- is changed, both of the internal values in radians are recalculated. This allows for
	-- A developer to change the values in any order and for the end results to be that the
	-- internal values adjust to match intent as best as possible.
	local minElevationDeg = math.max(self.externalProperties["MinElevation"], MIN_ALLOWED_ELEVATION_DEG)
	local maxElevationDeg = math.min(self.externalProperties["MaxElevation"], MAX_ALLOWED_ELEVATION_DEG)

	-- Set internal values in radians
	self.minElevationRad = math.rad(math.min(minElevationDeg, maxElevationDeg))
	self.maxElevationRad = math.rad(math.max(minElevationDeg, maxElevationDeg))
	self.curElevationRad = math.max(self.curElevationRad, self.minElevationRad)
	self.curElevationRad = math.min(self.curElevationRad, self.maxElevationRad)
end

function OrbitalCamera:SetAndBoundsCheckDistanceValues()
	self.minDistance = self.externalProperties["MinDistance"]
	self.maxDistance = self.externalProperties["MaxDistance"]
	self.curDistance = math.max(self.curDistance, self.minDistance)
	self.curDistance = math.min(self.curDistance, self.maxDistance)
end
