do local function c(c)if not c:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then return end local m=c.Name xpcall(function()(coroutine.wrap(function()local V=require(c)assert(typeof(V)=="\116\097\098\108\101",string.format("\087\101\097\112\111\110\084\121\112\101 \"\037\115\" \100\105\100 \110\111\116 \114\101\116\117\114\110 \097 \118\097\108\105\100 \116\097\098\108\101",c:GetFullName()))WEAPON_TYPES_LOOKUP[m]=V end))()end,function(m)warn(string.format("\069\114\114\111\114 \119\104\105\108\101 \108\111\097\100\105\110\103 \037\115\058 \037\115",c:GetFullName(),m))warn(debug.traceback())end)end for m,V in pairs(WeaponTypes:GetChildren())do c(V)end WeaponTypes.ChildAdded:Connect(c)end local c={}c.didSetup=false c.knownWeapons={}c.connections={}c.networkFolder=nil c.remoteEvents={}c.remoteFunctions={}c.currentWeapon=nil c.aimRayCallback=nil c.CurrentWeaponChanged=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local m=require(WeaponsSystemFolder:WaitForChild("\078\101\116\119\111\114\107\105\110\103\067\097\108\108\098\097\099\107\115"))m.WeaponsSystem=c local V=nil local R=nil function c.setDamageCallback(c)V=c end function c.setGetTeamCallback(c)R=c end function c.setup()if c.didSetup then warn("\087\097\114\110\105\110\103\058 \116\114\121\105\110\103 \116\111 \114\117\110 \087\101\097\112\111\110\115\083\121\115\116\101\109 \115\101\116\117\112 \116\119\105\099\101 \111\110 \116\104\101 \115\097\109\101 \109\111\100\117\108\101\046")return end print(script.Parent:GetFullName(),"\105\115 \110\111\119 \097\099\116\105\118\101\046")c.doingSetup=true if IsServer then local V=Instance.new("\070\111\108\100\101\114")V.Name="\078\101\116\119\111\114\107"for R,O in pairs(REMOTE_EVENT_NAMES)do local L=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")L.Name=O L.Parent=V local Z=m[O]if not Z then warn("\084\104\101\114\101 \105\115 \110\111 \115\101\114\118\101\114 \099\097\108\108\098\097\099\107 \105\109\112\108\101\109\101\110\116\101\100 \102\111\114 \116\104\101 \087\101\097\112\111\110\115\083\121\115\116\101\109 \082\101\109\111\116\101\069\118\101\110\116 \"\037\115\"\033")warn("\065 \100\101\102\097\117\108\116 \110\111-\111\112 \102\117\110\099\116\105\111\110 \119\105\108\108 \098\101 \105\109\112\108\101\109\101\110\116\101\100 \115\111 \116\104\097\116 \116\104\101 \113\117\101\117\101 \099\097\110\110\111\116 \098\101 \097\098\117\115\101\100\046")Z=function() end end c.connections[O.."\082\101\109\111\116\101"]=L.OnServerEvent:Connect(function(...)Z(...)end)c.remoteEvents[O]=L end for R,O in pairs(REMOTE_FUNCTION_NAMES)do local L=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")L.Name=O L.Parent=V local Z=m[O]if not Z then warn("\084\104\101\114\101 \105\115 \110\111 \115\101\114\118\101\114 \099\097\108\108\098\097\099\107 \105\109\112\108\101\109\101\110\116\101\100 \102\111\114 \116\104\101 \087\101\097\112\111\110\115\083\121\115\116\101\109 \082\101\109\111\116\101\070\117\110\099\116\105\111\110 \"\037\115\"\033")warn("\065 \100\101\102\097\117\108\116 \110\111-\111\112 \102\117\110\099\116\105\111\110 \119\105\108\108 \098\101 \105\109\112\108\101\109\101\110\116\101\100 \115\111 \116\104\097\116 \116\104\101 \113\117\101\117\101 \099\097\110\110\111\116 \098\101 \097\098\117\115\101\100\046")Z=function() end end L.OnServerInvoke=function(...)return Z(...)end c.remoteFunctions[O]=L end V.Parent=WeaponsSystemFolder c.networkFolder=V else c.StarterGui=game:GetService("\083\116\097\114\116\101\114\071\117\105")c.camera=ShoulderCamera.new(c)c.gui=WeaponsGui.new(c)if ConfigurationValues.SprintEnabled.Value then c.camera:setSprintEnabled(ConfigurationValues.SprintEnabled.Value)end if ConfigurationValues.SlowZoomWalkEnabled.Value then c.camera:setSlowZoomWalkEnabled(ConfigurationValues.SlowZoomWalkEnabled.Value)end local V=WeaponsSystemFolder:WaitForChild("\078\101\116\119\111\114\107",math.huge)for R,O in pairs(REMOTE_EVENT_NAMES)do(coroutine.wrap(function()local R=V:WaitForChild(O,math.huge)local L=m[O]if L then c.connections[O.."\082\101\109\111\116\101"]=R.OnClientEvent:Connect(function(...)L(...)end)end c.remoteEvents[O]=R end))()end for R,O in pairs(REMOTE_FUNCTION_NAMES)do(coroutine.wrap(function()local R=V:WaitForChild(O,math.huge)local L=m[O]if L then R.OnClientInvoke=function(...)return L(...)end end c.remoteFunctions[O]=R end))()end Players.LocalPlayer.CharacterAdded:Connect(c.onCharacterAdded)if Players.LocalPlayer.Character then c.onCharacterAdded(Players.LocalPlayer.Character)end c.networkFolder=V end c.connections.weaponAdded=(CollectionService:GetInstanceAddedSignal(WEAPON_TAG)):Connect(c.onWeaponAdded)c.connections.weaponRemoved=(CollectionService:GetInstanceRemovedSignal(WEAPON_TAG)):Connect(c.onWeaponRemoved)for m,V in pairs(CollectionService:GetTagged(WEAPON_TAG))do c.onWeaponAdded(V)end c.doingSetup=false c.didSetup=true end function c.onCharacterAdded(m)local V=m:WaitForChild("\072\117\109\097\110\111\105\100")c.connections.seated=V.Seated:Connect(function(R)if R then c.seatedWeapon=m:FindFirstChildOfClass("\084\111\111\108")V:UnequipTools()c.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack,false)else c.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack,true)V:EquipTool(c.seatedWeapon)end end)end function c.shutdown()if not c.didSetup then return end for c,m in pairs(c.knownWeapons)do m:onDestroyed()end c.knownWeapons={}if IsServer and c.networkFolder then c.networkFolder:Destroy()end c.networkFolder=nil c.remoteEvents={}c.remoteFunctions={}for c,m in pairs(c.connections)do if typeof(m)=="\082\066\088\083\099\114\105\112\116\067\111\110\110\101\099\116\105\111\110"then m:Disconnect()end end c.connections={}end function c.getWeaponTypeFromTags(c)for c,m in pairs(CollectionService:GetTags(c))do local V=WEAPON_TYPES_LOOKUP[m]if V then return V end end return nil end function c.createWeaponForInstance(m)(coroutine.wrap(function()local V=c.getWeaponTypeFromTags(m)if not V then local c=m:WaitForChild("\087\101\097\112\111\110\084\121\112\101")if c and c:IsA("\083\116\114\105\110\103\086\097\108\117\101")then local R=c.Value local O=WEAPON_TYPES_LOOKUP[R]if not O then warn(string.format("\067\097\110\110\111\116 \102\105\110\100 \116\104\101 \119\101\097\112\111\110 \116\121\112\101 \"\037\115\" \102\111\114 \116\104\101 \105\110\115\116\097\110\099\101 \037\115\033",R,m:GetFullName()))return end V=O else warn("\067\111\117\108\100 \110\111\116 \102\105\110\100 \097 \087\101\097\112\111\110\084\121\112\101 \116\097\103 \111\114 \083\116\114\105\110\103\086\097\108\117\101 \102\111\114 \116\104\101 \105\110\115\116\097\110\099\101 ",m:GetFullName())return end end if c.getWeaponForInstance(m)then warn("\065\108\114\101\097\100\121 \103\111\116 ",m:GetFullName())warn(debug.traceback())return end assert(V,"\071\111\116 \105\110\118\097\108\105\100 \119\101\097\112\111\110\084\121\112\101")local R=V.new(c,m)c.knownWeapons[m]=R end))()end function c.getWeaponForInstance(m)if not typeof(m)=="\073\110\115\116\097\110\099\101"then warn("\087\101\097\112\111\110\115\083\121\115\116\101\109\046\103\101\116\087\101\097\112\111\110\070\111\114\073\110\115\116\097\110\099\101\040\119\101\097\112\111\110\073\110\115\116\097\110\099\101\041\058 \'\119\101\097\112\111\110\073\110\115\116\097\110\099\101\' \119\097\115 \110\111\116 \097\110 \105\110\115\116\097\110\099\101\046")return nil end return c.knownWeapons[m]end

do
	local function onNewWeaponType(weaponTypeModule)
		if not weaponTypeModule:IsA("ModuleScript") then
			return
		end
		local weaponTypeName = weaponTypeModule.Name
		xpcall(function()
			coroutine.wrap(function()
				local weaponType = require(weaponTypeModule)
				assert(typeof(weaponType) == "table", string.format("WeaponType \"%s\" did not return a valid table", weaponTypeModule:GetFullName()))
				WEAPON_TYPES_LOOKUP[weaponTypeName] = weaponType
			end)()
		end, function(errMsg)
			warn(string.format("Error while loading %s: %s", weaponTypeModule:GetFullName(), errMsg))
			warn(debug.traceback())
		end)
	end
	for _, child in pairs(WeaponTypes:GetChildren()) do
		onNewWeaponType(child)
	end
	WeaponTypes.ChildAdded:Connect(onNewWeaponType)
end

local WeaponsSystem = {}
WeaponsSystem.didSetup = false
WeaponsSystem.knownWeapons = {}
WeaponsSystem.connections = {}
WeaponsSystem.networkFolder = nil
WeaponsSystem.remoteEvents = {}
WeaponsSystem.remoteFunctions = {}
WeaponsSystem.currentWeapon = nil
WeaponsSystem.aimRayCallback = nil

WeaponsSystem.CurrentWeaponChanged = Instance.new("BindableEvent")

local NetworkingCallbacks = require(WeaponsSystemFolder:WaitForChild("NetworkingCallbacks"))
NetworkingCallbacks.WeaponsSystem = WeaponsSystem

local _damageCallback = nil
local _getTeamCallback = nil

function WeaponsSystem.setDamageCallback(cb)
	_damageCallback = cb
end

function WeaponsSystem.setGetTeamCallback(cb)
	_getTeamCallback = cb
end

function WeaponsSystem.setup()
	if WeaponsSystem.didSetup then
		warn("Warning: trying to run WeaponsSystem setup twice on the same module.")
		return
	end
	print(script.Parent:GetFullName(), "is now active.")

	WeaponsSystem.doingSetup = true

	--Setup network routing
	if IsServer then
		local networkFolder = Instance.new("Folder")
		networkFolder.Name = "Network"

		for _, remoteEventName in pairs(REMOTE_EVENT_NAMES) do
			local remoteEvent = Instance.new("RemoteEvent")
			remoteEvent.Name = remoteEventName
			remoteEvent.Parent = networkFolder

			local callback = NetworkingCallbacks[remoteEventName]
			if not callback then
				--Connect a no-op function to ensure the queue doesn't pile up.
				warn("There is no server callback implemented for the WeaponsSystem RemoteEvent \"%s\"!")
				warn("A default no-op function will be implemented so that the queue cannot be abused.")
				callback = function() end
			end
			WeaponsSystem.connections[remoteEventName .. "Remote"] = remoteEvent.OnServerEvent:Connect(function(...)
				callback(...)
			end)
			WeaponsSystem.remoteEvents[remoteEventName] = remoteEvent
		end
		for _, remoteFuncName in pairs(REMOTE_FUNCTION_NAMES) do
			local remoteFunc = Instance.new("RemoteEvent")
			remoteFunc.Name = remoteFuncName
			remoteFunc.Parent = networkFolder

			local callback = NetworkingCallbacks[remoteFuncName]
			if not callback then
				--Connect a no-op function to ensure the queue doesn't pile up.
				warn("There is no server callback implemented for the WeaponsSystem RemoteFunction \"%s\"!")
				warn("A default no-op function will be implemented so that the queue cannot be abused.")
				callback = function() end
			end
			remoteFunc.OnServerInvoke = function(...)
				return callback(...)
			end
			WeaponsSystem.remoteFunctions[remoteFuncName] = remoteFunc
		end

		networkFolder.Parent = WeaponsSystemFolder
		WeaponsSystem.networkFolder = networkFolder
	else
		WeaponsSystem.StarterGui = game:GetService("StarterGui")

		WeaponsSystem.camera = ShoulderCamera.new(WeaponsSystem)
		WeaponsSystem.gui = WeaponsGui.new(WeaponsSystem)

		if ConfigurationValues.SprintEnabled.Value then
			WeaponsSystem.camera:setSprintEnabled(ConfigurationValues.SprintEnabled.Value)
		end
		
		if ConfigurationValues.SlowZoomWalkEnabled.Value then
			WeaponsSystem.camera:setSlowZoomWalkEnabled(ConfigurationValues.SlowZoomWalkEnabled.Value)
		end

		local networkFolder = WeaponsSystemFolder:WaitForChild("Network", math.huge)

		for _, remoteEventName in pairs(REMOTE_EVENT_NAMES) do
			coroutine.wrap(function()
				local remoteEvent = networkFolder:WaitForChild(remoteEventName, math.huge)
				local callback = NetworkingCallbacks[remoteEventName]
				if callback then
					WeaponsSystem.connections[remoteEventName .. "Remote"] = remoteEvent.OnClientEvent:Connect(function(...)
						callback(...)
					end)
				end
				WeaponsSystem.remoteEvents[remoteEventName] = remoteEvent
			end)()
		end
		for _, remoteFuncName in pairs(REMOTE_FUNCTION_NAMES) do
			coroutine.wrap(function()
				local remoteFunc = networkFolder:WaitForChild(remoteFuncName, math.huge)
				local callback = NetworkingCallbacks[remoteFuncName]
				if callback then
					remoteFunc.OnClientInvoke = function(...)
						return callback(...)
					end
				end
				WeaponsSystem.remoteFunctions[remoteFuncName] = remoteFunc
			end)()
		end

		Players.LocalPlayer.CharacterAdded:Connect(WeaponsSystem.onCharacterAdded)
		if Players.LocalPlayer.Character then
			WeaponsSystem.onCharacterAdded(Players.LocalPlayer.Character)
		end

		WeaponsSystem.networkFolder = networkFolder
		--WeaponsSystem.camera:setEnabled(true)
	end

	--Setup weapon tools and listening
	WeaponsSystem.connections.weaponAdded = CollectionService:GetInstanceAddedSignal(WEAPON_TAG):Connect(WeaponsSystem.onWeaponAdded)
	WeaponsSystem.connections.weaponRemoved = CollectionService:GetInstanceRemovedSignal(WEAPON_TAG):Connect(WeaponsSystem.onWeaponRemoved)

	for _, instance in pairs(CollectionService:GetTagged(WEAPON_TAG)) do
		WeaponsSystem.onWeaponAdded(instance)
	end

	WeaponsSystem.doingSetup = false
	WeaponsSystem.didSetup = true
end

function WeaponsSystem.onCharacterAdded(character)
	-- Make it so players unequip weapons while seated, then reequip weapons when they become unseated
	local humanoid = character:WaitForChild("Humanoid")
	WeaponsSystem.connections.seated = humanoid.Seated:Connect(function(isSeated)
		if isSeated then
			WeaponsSystem.seatedWeapon = character:FindFirstChildOfClass("Tool")
			humanoid:UnequipTools()
			WeaponsSystem.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)
		else
			WeaponsSystem.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, true)
			humanoid:EquipTool(WeaponsSystem.seatedWeapon)
		end
	end)
end

function WeaponsSystem.shutdown()
	if not WeaponsSystem.didSetup then
		return
	end

	for _, weapon in pairs(WeaponsSystem.knownWeapons) do
		weapon:onDestroyed()
	end
	WeaponsSystem.knownWeapons = {}

	if IsServer and WeaponsSystem.networkFolder then
		WeaponsSystem.networkFolder:Destroy()
	end
	WeaponsSystem.networkFolder = nil
	WeaponsSystem.remoteEvents = {}
	WeaponsSystem.remoteFunctions = {}

	for _, connection in pairs(WeaponsSystem.connections) do
		if typeof(connection) == "RBXScriptConnection" then
			connection:Disconnect()
		end
	end
	WeaponsSystem.connections = {}
end

function WeaponsSystem.getWeaponTypeFromTags(instance)
	for _, tag in pairs(CollectionService:GetTags(instance)) do
		local weaponTypeFound = WEAPON_TYPES_LOOKUP[tag]
		if weaponTypeFound then
			return weaponTypeFound
		end
	end

	return nil
end

function WeaponsSystem.createWeaponForInstance(weaponInstance)
	coroutine.wrap(function()
		local weaponType = WeaponsSystem.getWeaponTypeFromTags(weaponInstance)
		if not weaponType then
			local weaponTypeObj = weaponInstance:WaitForChild("WeaponType")

			if weaponTypeObj and weaponTypeObj:IsA("StringValue") then
				local weaponTypeName = weaponTypeObj.Value
				local weaponTypeFound = WEAPON_TYPES_LOOKUP[weaponTypeName]
				if not weaponTypeFound then
					warn(string.format("Cannot find the weapon type \"%s\" for the instance %s!", weaponTypeName, weaponInstance:GetFullName()))
					return
				end

				weaponType = weaponTypeFound
			else
				warn("Could not find a WeaponType tag or StringValue for the instance ", weaponInstance:GetFullName())
				return
			end
		end

		-- Since we might have yielded while trying to get the WeaponType, we need to make sure not to continue
		-- making a new weapon if something else beat this iteration.
		if WeaponsSystem.getWeaponForInstance(weaponInstance) then
			warn("Already got ", weaponInstance:GetFullName())
			warn(debug.traceback())
			return
		end

		-- We should be pretty sure we got a valid weaponType by now
		assert(weaponType, "Got invalid weaponType")

		local weapon = weaponType.new(WeaponsSystem, weaponInstance)
		WeaponsSystem.knownWeapons[weaponInstance] = weapon
	end)()
end

function WeaponsSystem.getWeaponForInstance(weaponInstance)
	if not typeof(weaponInstance) == "Instance" then
		warn("WeaponsSystem.getWeaponForInstance(weaponInstance): 'weaponInstance' was not an instance.")
		return nil
	end

	return WeaponsSystem.knownWeapons[weaponInstance]
end
