local function F(F,H)return math_atan2(H.X*F.Z-H.Z*F.X,H.X*F.X+H.Z*F.Z)end local function H(F,H)local T=script:FindFirstChild(F)if T then return T.Value end return H end local function T(F,H,T)local C=script:FindFirstChild(F)if C and C:isA(H)then externalProperties[F]=C.Value elseif externalProperties[F]~=nil then C=Instance.new(H)C.Name=F C.Parent=script C.Value=externalProperties[F]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 ",F)return end if T then if changedSignalConnections[F]then changedSignalConnections[F]:disconnect()end changedSignalConnections[F]=C.Changed:connect(function(H)externalProperties[F]=H T()end)end end local function C()minAzimuthAbsoluteRad=math.rad(externalProperties.ReferenceAzimuth)-math.abs(math.rad(externalProperties.CWAzimuthTravel))maxAzimuthAbsoluteRad=math.rad(externalProperties.ReferenceAzimuth)+math.abs(math.rad(externalProperties.CCWAzimuthTravel))useAzimuthLimits=externalProperties.UseAzimuthLimits if useAzimuthLimits then curAzimuthRad=math.max(curAzimuthRad,minAzimuthAbsoluteRad)curAzimuthRad=math.min(curAzimuthRad,maxAzimuthAbsoluteRad)end end local function b()local F=math.max(externalProperties.MinElevation,MIN_ALLOWED_ELEVATION_DEG)local H=math.min(externalProperties.MaxElevation,MAX_ALLOWED_ELEVATION_DEG)minElevationRad=math.rad(math.min(F,H))maxElevationRad=math.rad(math.max(F,H))curElevationRad=math.max(curElevationRad,minElevationRad)curElevationRad=math.min(curElevationRad,maxElevationRad)end local function A()minDistance=externalProperties.MinDistance maxDistance=externalProperties.MaxDistance curDistance=math.max(curDistance,minDistance)curDistance=math.min(curDistance,maxDistance)end
-- May return NaN or inf or -inf
-- This is a way of finding the angle between the two vectors:
local function findAngleBetweenXZVectors(vec2, vec1)
	return math_atan2(vec1.X * vec2.Z - vec1.Z * vec2.X, vec1.X * vec2.X + vec1.Z * vec2.Z)
end

local function GetValueObject(name, defaultValue)
	local valueObj = script:FindFirstChild(name)
	if valueObj then
		return valueObj.Value
	end
	return defaultValue
end

local function 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
		externalProperties[name] = valueObj.Value
	elseif 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 = externalProperties[name]
	else
		print("externalProperties table has no entry for ", name)
		return
	end

	if updateFunction then
		if changedSignalConnections[name] then
			changedSignalConnections[name]:disconnect()
		end
		changedSignalConnections[name] = valueObj.Changed:connect(function(newValue)
			externalProperties[name] = newValue
			updateFunction()
		end)
	end
end

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

local function 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(externalProperties["MinElevation"], MIN_ALLOWED_ELEVATION_DEG)
	local maxElevationDeg = math.min(externalProperties["MaxElevation"], MAX_ALLOWED_ELEVATION_DEG)

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

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