local a=require(script.Parent:WaitForChild("\066\097\115\101\067\097\109\101\114\097"))local D=setmetatable({},a)D.__index=D function D.new()local r=setmetatable(a.new(),D)r.lastUpdate=tick()r.changedSignalConnections={}r.refAzimuthRad=nil r.curAzimuthRad=nil r.minAzimuthAbsoluteRad=nil r.maxAzimuthAbsoluteRad=nil r.useAzimuthLimits=nil r.curElevationRad=nil r.minElevationRad=nil r.maxElevationRad=nil r.curDistance=nil r.minDistance=nil r.maxDistance=nil r.r3ButtonDown=false r.l3ButtonDown=false r.gamepadDollySpeedMultiplier=1 r.lastUserPanCamera=tick()r.externalProperties={}r.externalProperties.InitialDistance=25 r.externalProperties.MinDistance=10 r.externalProperties.MaxDistance=100 r.externalProperties.InitialElevation=35 r.externalProperties.MinElevation=35 r.externalProperties.MaxElevation=35 r.externalProperties.ReferenceAzimuth=-45 r.externalProperties.CWAzimuthTravel=90 r.externalProperties.CCWAzimuthTravel=90 r.externalProperties.UseAzimuthLimits=false r:LoadNumberValueParameters()return r end function D.LoadOrCreateNumberValueParameter(s,a,D,r)local Y=script:FindFirstChild(a)if Y and Y:isA(D)then s.externalProperties[a]=Y.Value elseif s.externalProperties[a]~=nil then Y=Instance.new(D)Y.Name=a Y.Parent=script Y.Value=s.externalProperties[a]else return end if r then if s.changedSignalConnections[a]then s.changedSignalConnections[a]:Disconnect()end s.changedSignalConnections[a]=Y.Changed:Connect(function(D)s.externalProperties[a]=D r(s)end)end end function D.SetAndBoundsCheckAzimuthValues(a)a.minAzimuthAbsoluteRad=math.rad(a.externalProperties.ReferenceAzimuth)-math.abs(math.rad(a.externalProperties.CWAzimuthTravel))a.maxAzimuthAbsoluteRad=math.rad(a.externalProperties.ReferenceAzimuth)+math.abs(math.rad(a.externalProperties.CCWAzimuthTravel))a.useAzimuthLimits=a.externalProperties.UseAzimuthLimits if a.useAzimuthLimits then a.curAzimuthRad=math.max(a.curAzimuthRad,a.minAzimuthAbsoluteRad)a.curAzimuthRad=math.min(a.curAzimuthRad,a.maxAzimuthAbsoluteRad)end end function D.SetAndBoundsCheckElevationValues(a)local D=math.max(a.externalProperties.MinElevation,MIN_ALLOWED_ELEVATION_DEG)local r=math.min(a.externalProperties.MaxElevation,MAX_ALLOWED_ELEVATION_DEG)a.minElevationRad=math.rad(math.min(D,r))a.maxElevationRad=math.rad(math.max(D,r))a.curElevationRad=math.max(a.curElevationRad,a.minElevationRad)a.curElevationRad=math.min(a.curElevationRad,a.maxElevationRad)end function D.SetAndBoundsCheckDistanceValues(a)a.minDistance=a.externalProperties.MinDistance a.maxDistance=a.externalProperties.MaxDistance a.curDistance=math.max(a.curDistance,a.minDistance)a.curDistance=math.min(a.curDistance,a.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
		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
