local S={}function S.Create(S,z)local J={Item=z;Equipped=false;Connections={}}local U={}local K=z.Parent.Parent local t=z:WaitForChild("\072\097\110\100\108\101")local v=t:WaitForChild("\077\117\122\122\108\101")local Q=CONFIG:GetConfig(z)local l=true local o=false local y=false local N=false local V=0 local R=(z:WaitForChild("\065\109\109\111")).Value local F=false local function a()return J.Equipped and(l and R>0)end local function M()if not y and(J.Equipped and R<Q.Magazine)then y=true N=false local S=K.Ammo[Q.Size].Value if S>0 then MOUSE.Reticle="\082\101\108\111\097\100\105\110\103"REMOTES.Reload:FireServer(z)EFFECTS:Effect("\082\101\108\111\097\100",z)U.Reload:Play(.1,1,1/Q.ReloadTime)local K=tick()local t=0 repeat t=tick()-K RunService.Stepped:wait()until t>=Q.ReloadTime or N or not J.Equipped U.Reload:Stop()if J.Equipped then if t>=Q.ReloadTime then local z=Q.Magazine local J=z-R if S>=J then R=R+J else R=R+S end end MOUSE.Reticle=Q.Reticle or"\071\117\110"EVENTS.Gun:Fire("\085\112\100\097\116\101",R)end end y=false end end local function Y()R=R-1 local S=v.WorldPosition local J={}for U=1,Q.ShotSize,1 do local t=Q.Spread*10 local v=CFrame.new(S,MOUSE.WorldPosition)if Q.SpreadPattern then local S,z=Q.SpreadPattern[U][1],Q.SpreadPattern[U][2]v=v*CFrame.Angles(math.rad((t*z)/50),math.rad((t*S)/50),0)else v=v*CFrame.Angles(math.rad(math.random(-t,t)/50),math.rad(math.random(-t,t)/50),0)end local l=v.lookVector table.insert(J,l)local o,y,N,V=Raycast(S,l*Q.Range,{K})if o and V then if DAMAGE:PlayerCanDamage(PLAYER,V)then local J=DAMAGE:Calculate(z,o,S)local K=V:FindFirstChild("\065\114\109\111\114")and V.Armor.Value>0 EVENTS.Hitmarker:Fire(y,J,K,o.Name=="\072\101\097\100")REMOTES.Hit:FireServer(o,U)end end end if F then U.AimShoot:Play(0,math.random(5,10)/10,1)else U.Shoot:Play(0,math.random(5,10)/10,1)end EVENTS.Gun:Fire("\085\112\100\097\116\101",R)EFFECTS:Effect("\083\104\111\111\116",z,S,J,R)REMOTES.Shoot:FireServer(z,S,J)EVENTS.Recoil:Fire(Vector3.new(math.random(-Q.Recoil,Q.Recoil)/4,0,math.random(Q.Recoil/2,Q.Recoil)))end function J.Connect(S)local J=PLAYER.Character local K=J:WaitForChild("\072\117\109\097\110\111\105\100")for S,z in pairs((S.Item:WaitForChild("\065\110\105\109\097\116\105\111\110\115")):GetChildren())do U[z.Name]=K:LoadAnimation(z)end table.insert(S.Connections,INPUT.ActionBegan:connect(function(z,J)if S.Equipped and not J then if z=="\082\101\108\111\097\100"then M()end end end))table.insert(S.Connections,z.Attachments.ChildAdded:connect(function()Q=CONFIG:GetConfig(z)if S.Equipped then EVENTS.Zoom:Fire(Q.Zoom)EVENTS.Scope:Fire(Q.Scope)end end))table.insert(S.Connections,z.Attachments.ChildRemoved:connect(function()Q=CONFIG:GetConfig(z)if S.Equipped then EVENTS.Zoom:Fire(Q.Zoom)EVENTS.Scope:Fire(Q.Scope)end end))table.insert(S.Connections,EVENTS.Aim.Event:connect(function(z)F=z if S.Equipped then if F then U.Idle:Stop()U.Aim:Play()else U.Aim:Stop()U.Idle:Play()end end end))end function J.Disconnect(S)for S,z in pairs(S.Connections)do z:Disconnect()end S.Connections={}end function J.Equip(S)EVENTS.Zoom:Fire(Q.Zoom)EVENTS.Scope:Fire(Q.Scope)MOUSE.Reticle=Q.Reticle or"\071\117\110"if F then U.Aim:Play()else U.Idle:Play()end U.Equip:Play(0,1,1)R=z.Ammo.Value EVENTS.Gun:Fire("\069\110\097\098\108\101",Q.Size,R)S.Equipped=true V=tick()if R==0 then spawn(function()M()end)end end function J.Unequip(S)EVENTS.Zoom:Fire()EVENTS.Scope:Fire(false)MOUSE.Reticle="\068\101\102\097\117\108\116"for S,z in pairs(U)do z:Stop()end EVENTS.Gun:Fire("\068\105\115\097\098\108\101",Q.Size,R)S.Equipped=false end function J.Activate(S)o=true if tick()-V>=EQUIP_COOLDOWN then if Q.FireMode=="\083\101\109\105"then if a()then N=true l=false Y()wait(1/Q.FireRate)l=true end elseif Q.FireMode=="\065\117\116\111"then while o and a()do N=true l=false Y()wait(1/Q.FireRate)l=true end elseif Q.FireMode=="\066\117\114\115\116"then while o and a()do l=false for S=1,Q.BurstAmount,1 do if o and R>0 then N=true Y()wait(1/Q.BurstRate)else break end end wait(1/Q.FireRate)l=true end end if R==0 then M()end end end function J.Deactivate(S)o=false end return J end return S
-- modules

local module = {}

function module.Create(self, item)
	local itemModule = {
		Item = item,
		Equipped = false,
		Connections = {},
	}

	-- variables

	local animations = {}

	local character = item.Parent.Parent
	local handle = item:WaitForChild("Handle")
	local muzzle = handle:WaitForChild("Muzzle")

	local config = CONFIG:GetConfig(item)
	local canShoot = true
	local clicking = false
	local reloading = false
	local rCancelled = false
	local equipTime = 0

	local ammo = item:WaitForChild("Ammo").Value

	local aiming = false

	-- functions

	local function CanShoot()
		return itemModule.Equipped and canShoot and ammo > 0
	end

	local function Reload()
		if (not reloading) and itemModule.Equipped and ammo < config.Magazine then
			reloading = true
			rCancelled = false

			local storedAmmo = character.Ammo[config.Size].Value
			if storedAmmo > 0 then
				MOUSE.Reticle = "Reloading"
				REMOTES.Reload:FireServer(item)
				EFFECTS:Effect("Reload", item)
				animations.Reload:Play(0.1, 1, 1 / config.ReloadTime)

				local start = tick()
				local elapsed = 0
				repeat
					elapsed = tick() - start
					RunService.Stepped:wait()
				until elapsed >= config.ReloadTime or rCancelled or not itemModule.Equipped

				animations.Reload:Stop()

				if itemModule.Equipped then
					if elapsed >= config.ReloadTime then
						local magazine = config.Magazine
						local needed = magazine - ammo

						if storedAmmo >= needed then
							ammo = ammo + needed
						else
							ammo = ammo + storedAmmo
						end
					end

					MOUSE.Reticle = config.Reticle or "Gun"
					EVENTS.Gun:Fire("Update", ammo)
				end
			end
			reloading = false
		end
	end

	local function Shoot()
		ammo = ammo - 1
		local position = muzzle.WorldPosition
		local directions = {}

		for i = 1, config.ShotSize do
			local spread = config.Spread * 10
			local cframe = CFrame.new(position, MOUSE.WorldPosition)

			if config.SpreadPattern then
				local x, y = config.SpreadPattern[i][1], config.SpreadPattern[i][2]
				cframe = cframe * CFrame.Angles(math.rad(spread * y / 50), math.rad(spread * x / 50), 0)
			else
				cframe = cframe
					* CFrame.Angles(
						math.rad(math.random(-spread, spread) / 50),
						math.rad(math.random(-spread, spread) / 50),
						0
					)
			end

			local direction = cframe.lookVector
			table.insert(directions, direction)

			local hit, pos, normal, humanoid = Raycast(position, direction * config.Range, { character })

			if hit and humanoid then
				if DAMAGE:PlayerCanDamage(PLAYER, humanoid) then
					local damage = DAMAGE:Calculate(item, hit, position)
					local armor = (humanoid:FindFirstChild("Armor") and humanoid.Armor.Value > 0)
					EVENTS.Hitmarker:Fire(pos, damage, armor, hit.Name == "Head")
					REMOTES.Hit:FireServer(hit, i)
				end
			end
		end

		if aiming then
			animations.AimShoot:Play(0, math.random(5, 10) / 10, 1)
		else
			animations.Shoot:Play(0, math.random(5, 10) / 10, 1)
		end

		EVENTS.Gun:Fire("Update", ammo)
		EFFECTS:Effect("Shoot", item, position, directions, ammo)
		REMOTES.Shoot:FireServer(item, position, directions)

		EVENTS.Recoil:Fire(
			Vector3.new(
				math.random(-config.Recoil, config.Recoil) / 4,
				0,
				math.random(config.Recoil / 2, config.Recoil)
			)
		)
	end

	-- module functions

	function itemModule.Connect(self)
		local character = PLAYER.Character
		local humanoid = character:WaitForChild("Humanoid")

		for _, animation in pairs(self.Item:WaitForChild("Animations"):GetChildren()) do
			animations[animation.Name] = humanoid:LoadAnimation(animation)
		end

		table.insert(
			self.Connections,
			INPUT.ActionBegan:connect(function(action, processed)
				if self.Equipped and not processed then
					if action == "Reload" then
						Reload()
					end
				end
			end)
		)

		table.insert(
			self.Connections,
			item.Attachments.ChildAdded:connect(function()
				config = CONFIG:GetConfig(item)
				if self.Equipped then
					EVENTS.Zoom:Fire(config.Zoom)
					EVENTS.Scope:Fire(config.Scope)
				end
			end)
		)

		table.insert(
			self.Connections,
			item.Attachments.ChildRemoved:connect(function()
				config = CONFIG:GetConfig(item)
				if self.Equipped then
					EVENTS.Zoom:Fire(config.Zoom)
					EVENTS.Scope:Fire(config.Scope)
				end
			end)
		)

		table.insert(
			self.Connections,
			EVENTS.Aim.Event:connect(function(a)
				aiming = a

				if self.Equipped then
					if aiming then
						animations.Idle:Stop()
						animations.Aim:Play()
					else
						animations.Aim:Stop()
						animations.Idle:Play()
					end
				end
			end)
		)
	end

	function itemModule.Disconnect(self)
		for _, connection in pairs(self.Connections) do
			connection:Disconnect()
		end
		self.Connections = {}
	end

	function itemModule.Equip(self)
		EVENTS.Zoom:Fire(config.Zoom)
		EVENTS.Scope:Fire(config.Scope)
		MOUSE.Reticle = config.Reticle or "Gun"
		if aiming then
			animations.Aim:Play()
		else
			animations.Idle:Play()
		end
		animations.Equip:Play(0, 1, 1)

		ammo = item.Ammo.Value

		EVENTS.Gun:Fire("Enable", config.Size, ammo)

		self.Equipped = true
		equipTime = tick()

		if ammo == 0 then
			spawn(function()
				Reload()
			end)
		end
	end

	function itemModule.Unequip(self)
		EVENTS.Zoom:Fire()
		EVENTS.Scope:Fire(false)
		MOUSE.Reticle = "Default"

		for _, animation in pairs(animations) do
			animation:Stop()
		end

		EVENTS.Gun:Fire("Disable", config.Size, ammo)

		self.Equipped = false
	end

	function itemModule.Activate(self)
		clicking = true

		if tick() - equipTime >= EQUIP_COOLDOWN then
			if config.FireMode == "Semi" then
				if CanShoot() then
					rCancelled = true
					canShoot = false
					Shoot()
					wait(1 / config.FireRate)
					canShoot = true
				end
			elseif config.FireMode == "Auto" then
				while clicking and CanShoot() do
					rCancelled = true
					canShoot = false
					Shoot()
					wait(1 / config.FireRate)
					canShoot = true
				end
			elseif config.FireMode == "Burst" then
				while clicking and CanShoot() do
					canShoot = false
					for i = 1, config.BurstAmount do
						if clicking and ammo > 0 then
							rCancelled = true
							Shoot()
							wait(1 / config.BurstRate)
						else
							break
						end
					end
					wait(1 / config.FireRate)
					canShoot = true
				end
			end
			if ammo == 0 then
				Reload()
			end
		end
	end

	function itemModule.Deactivate(self)
		clicking = false
	end

	return itemModule
end

return module
