--Optimization--

local rad = math.rad
local random = math.random
local NewCFrame = CFrame.new
local Angles = CFrame.Angles
local NewVector = Vector3.new
local CFLookAt = CFrame.lookAt
local NewInstance = Instance.new

local function IsHeadAccessory(Handle)
	if Handle:FindFirstChild("HatAttachment") or Handle:FindFirstChild("HairAttachment") or Handle:FindFirstChild("NeckAttachment") or Handle:FindFirstChild("FaceFrontAttachment") then
		return true
	else
		return false
	end
end

local function OnLengthChanged(Cast, LastPoint, Direction, Length, Velocity, Bullet)
	if Bullet then
		local BulletLength = Bullet.Size.Z/2
		local Offset = NewCFrame(0,0,-(Length - BulletLength))
		Bullet.CFrame = CFLookAt(LastPoint, LastPoint + Direction):ToWorldSpace(Offset)
	end
end

local function OnHit(Cast, Result, Velocity, Bullet)
	local HitCoroutine = coroutine.create(function()
		local Hit = Result.Instance
		local Damage = random(MinimumDamage,MaximumDamage)

		if Hit.Parent:IsA("Model") and not core.isAlly(Hit.Parent) then
			if Hit.Parent:FindFirstChild("Humanoid") then
				local Humanoid = Hit:FindFirstAncestorWhichIsA("Model"):FindFirstChild("Humanoid")

				if Hit.Name == "Head" then
					Humanoid:TakeDamage(Damage * headshotmultiplier)
				else
					Humanoid:TakeDamage(Damage)
				end

				local HitSound = NewInstance("Sound",Hit)
				HitSound.PlayOnRemove = true
				HitSound.SoundId = HitFleshSound
				HitSound:Destroy()
			else
				local newBulletHole = combat.getBulletHole()
				newBulletHole.CFrame = NewCFrame(Result.Position,Result.Position + Result.Normal)
				
				for i = 1, random(2,3) do
					local chunk = NewInstance("Part")
					chunk.Color = Result.Instance.Color
					chunk.Material = Result.Instance.Material
					local size = random(5)/20
					chunk.Size = NewVector(size,size,size)
					chunk.CFrame = NewCFrame(Result.Position)
					chunk.Anchored = false
					chunk.Velocity = NewVector(random(-20,20),random(10,20),random(-20,20))
					chunk.Parent = workspace.IgnoreFolder
					chunk:SetNetworkOwner(nil)
					
					debrisService:AddItem(chunk,1)
				end
				
				local soundattachment = NewInstance("Part",workspace.IgnoreFolder)
				soundattachment.Size = NewVector(0,0,0)
				soundattachment.CanCollide = false
				soundattachment.CFrame = NewCFrame(Result.Position)
				soundattachment.Anchored = true
				
				local HitSound = NewInstance("Sound",soundattachment)
				HitSound.SoundId = HitWallSounds[random(1,#HitWallSounds)]
				HitSound.Volume = 1
				HitSound:Play()
				
				debrisService:AddItem(soundattachment,1.5)
				debrisService:AddItem(newBulletHole,5)
			end
		elseif Hit.Parent:IsA("Accessory") then
			local IsAHeadAccessory = IsHeadAccessory(Hit)
			if Hit.Parent.Parent:IsA("Model") and not core.isAlly(Hit.Parent.Parent) then
				local Model = Hit.Parent.Parent
				if Model:FindFirstChild("Humanoid") then
					if IsAHeadAccessory then
						Model.Humanoid:TakeDamage(Damage * headshotmultiplier)
					else
						Model.Humanoid:TakeDamage(Damage)
					end

					local HitSound = NewInstance("Sound",Hit)
					HitSound.PlayOnRemove = true
					HitSound.SoundId = HitFleshSound
					HitSound:Destroy()
					
				end
			end
		end

	end)
	
	coroutine.resume(HitCoroutine)
	
	debrisService:AddItem(Bullet,0.7)
	
	if Bullet:FindFirstChild("Whiz") then
		Bullet.Whiz:Stop()
	end
end

function combat.getBulletHole()
	local c = game.ServerStorage:WaitForChild("Hole"):Clone()
	c.Parent = workspace.IgnoreFolder
	
	return c
end

function combat.getBullet()
	local c = game.ServerStorage:WaitForChild("Bullet"):Clone()
	c.Parent = workspace.IgnoreFolder
	
	return c
end

function combat.getCase()
	local c = game.ServerStorage:WaitForChild("Case"):Clone()
	c.Parent = workspace.IgnoreFolder
	
	return c
end

function pushBullet(bullet)
	-- replaced all the code in this function
	
	local origin = barrel.WorldPosition
	local direction = (NewCFrame(origin,(barrel.WorldCFrame * NewCFrame(0,0,-5)).Position) * Angles(rad(random(-spread,spread)),rad(random(-spread,spread)),rad(random(-spread,spread)))).LookVector
	
	local NewVelocity = direction * bulletspeed
	
	NewCaster:Fire(origin, direction, NewVelocity, Behaviour)

	local MaxLength = 0 -- value starts at zero and works its way up to the maximum distance as the bullet travels (used to detect how far the cosmetic bullet travels before being destroyed if it reaches max distance)

	NewCaster.LengthChanged:Connect(function(Cast,LastPoint,NewDirection,Length,NewVelocity,Bullet)
		OnLengthChanged(Cast, LastPoint, NewDirection, Length, NewVelocity, Bullet)
		MaxLength += Length

		if MaxLength >= maxdistance and Bullet ~= nil then
			Bullet:Destroy()
		end
	end)

	NewCaster.RayHit:Connect(OnHit)
end

local flashIndex = 1
function combat.shoot(target)
	if status:get("weaponCool") and not status:get("reloading") then
		status:set("weaponCool",false)
		
		local shot
		if core.checkDist(target,myRoot) > 60 then
			shot = 1
		else
			shot = burstamount
		end
		
		if not fullauto then
			for i = 1, shot do
				if myHuman.Health <= 0 or target.Parent.Humanoid.Health <= 0 then break end
				
				if core.checkDist(myRoot,target) < 7 then break end
				
				wait(firerate)
				status:set("mag",status:get("mag")-1)
				
				--flash:Emit(1)
				local FlashCoroutine = coroutine.create(function()
					barrel.MuzzleFlash:Emit(20)
					barrel.Dust:Emit(40)
					lightFlash.Enabled = true 
					wait(0.1) 
					lightFlash.Enabled = false 
				end)

				coroutine.resume(FlashCoroutine)
				--bullet.CFrame = m4.CFrame * NewCFrame(0,0.2,-2.5) --z: 0.5
				
				if shotgunpellets > 1 then
					for i = 1,shotgunpellets,1 do
						spawn(function()
							local bullet = combat.getBullet()
							pushBullet(bullet)
							bullet:Destroy()
						end)
					end
				else
					local bullet = combat.getBullet()
					pushBullet(bullet)
					bullet:Destroy()
				end
				
				local case = combat.getCase()
				case.CFrame = chamber.WorldCFrame
				case.Velocity = case.CFrame.RightVector * -20 * NewVector(1,-3,1)
				case:SetNetworkOwner(nil)
				core.spawn(function() 
					wait(0.05) 
					case.CanCollide = true 
					wait(2)
					case:Destroy()
				end)

				fireSound:Play()

				--Kick
				local original = m4.HingeAttach1.Position
				m4.HingeAttach1.Position = original - NewVector(0,0,0.2)
				tweenService:Create(m4.HingeAttach1, TweenInfo.new(0.1),{Position = original}):Play()
				
			end
		else
			local Params = RaycastParams.new()
			Params.FilterType = Enum.RaycastFilterType.Blacklist
			Params.FilterDescendantsInstances = {marine}
			
			local Success,Error = pcall(function() -- gonna make sure if something strange happens the whole script wont freeze
				for i = 1, status:get("mag") do
					if myHuman.Health <= 0 or target.Parent.Humanoid.Health <= 0 then break end
					
					local cansee = false
					local eyesight = workspace:Raycast(myHead.Position,(target.Parent.Head.Position - myHead.Position).Unit * detectionrange,Params)
					pcall(function()
						if eyesight.Instance and eyesight.Instance:IsDescendantOf(target.Parent) then
							cansee = true
						end
					end)
					
					if not cansee then break end
					
					wait(firerate)
					status:set("mag",status:get("mag")-1)

					--flash:Emit(1)
					local FlashCoroutine = coroutine.create(function()
						barrel.MuzzleFlash:Emit(20)
						barrel.Dust:Emit(40)
						lightFlash.Enabled = true 
						wait(0.1) 
						lightFlash.Enabled = false 
					end)

					coroutine.resume(FlashCoroutine)

					if shotgunpellets > 1 then
						for i = 1,shotgunpellets,1 do
							spawn(function()
								local bullet = combat.getBullet()
								pushBullet(bullet)
								bullet:Destroy()
							end)
						end
					else
						local bullet = combat.getBullet()
						pushBullet(bullet)
						bullet:Destroy()
					end

					local case = combat.getCase()
					case.CFrame = chamber.WorldCFrame
					case.Velocity = case.CFrame.RightVector * -20 * NewVector(1,-3,1)
					core.spawn(function() 
						wait(0.05) 
						case.CanCollide = true 
						wait(2)
						case:Destroy()
					end)

					fireSound:Play()

					--Kick
					local original = m4.HingeAttach1.Position
					m4.HingeAttach1.Position = original - NewVector(0,0,0.2)
					tweenService:Create(m4.HingeAttach1, TweenInfo.new(0.1),{Position = original}):Play()

				end
			end)
			
			if not Success then
				warn(Error)
			end
		end
		if status:get("mag") <= 0 then
			wait(delaybeforereloading)
			actions.reload()
		end

		--New thread here before
		wait(mySettings.M4.Delay.Value)
		status:set("weaponCool",true)
	end
end

function combat.throwGrenade(target)
	if status:get("weaponCool") and status:get("grenadeCool") then
		status:set("weaponCool",false)
		status:set("grenadeCool",false)
		actions.yieldWeapons()
		
		local g = marine.Grenade:Clone()
		g.Boom.PlayOnRemove = true
		g.Parent = workspace
		g.CanCollide = true
		g.CFrame = marine["Right Arm"].CFrame * NewCFrame(0,-1.3,0) * Angles(0,0,rad(90))
		g:SetNetworkOwner(nil)
		debrisService:AddItem(g,5)
		
		marine.Grenade.Transparency = 1
		
		local w = NewInstance("WeldConstraint",g)
		w.Part0 = marine["Right Arm"]
		w.Part1 = g
		
		throwAnimation:Play()
		marine.Grenade.Pin:Play()
		
		--Rotate and throw
		myHuman.AutoRotate = false
		for i=1,4 do
			wait(0.1)
			myRoot.CFrame = CFLookAt(NewVector(myRoot.Position.X,myRoot.Position.Y,myRoot.Position.Z),NewVector(target.Position.X,myRoot.Position.Y,target.Position.Z))
		end
		myHuman.AutoRotate = true
		
		if myHuman.Health <= 0 then
			return
		end
		
		throwAnimation:Stop()
		
		w.Part1 = nil
		local targetPos = target.Position + (target.Velocity)
		troubleshoot.mark(targetPos)
		local dist = core.checkDist(myRoot,target)
		local aimCFrame = CFLookAt(myRoot.Position,targetPos)
		--g.Velocity = (aimCFrame.LookVector + NewVector(0,1.1,0)) * NewVector(dist,dist*1.5,dist)
		g.Velocity = (myRoot.CFrame.LookVector + NewVector(0,1,0)) * NewVector(dist,dist*1.5,dist)
		
		--Wait until grenade is thrown before it can be primed
		touched = g.Touched:Connect(function(obj)
			if not obj:IsDescendantOf(marine) then
				touched:Disconnect()
				g.HitWall:Play()
				wait(0.5)
				local x = NewInstance("Explosion",workspace)
				x.Position = g.Position
				x.BlastRadius = 16
				x.BlastPressure = 0
				x.Hit:Connect(function(obj,dist)
					local human = obj.Parent:FindFirstChild("Humanoid")
					if human then
						core.applyDamage(human,20-dist)
					end
				end)
				g:Destroy()
				debrisService:AddItem(x,2)
			end
		end)
		
		local attach0 = g.Attach0
		local attach1 = g.Attach1
		local t = NewInstance("Trail",g)
		t.Attachment0 = attach0
		t.Attachment1 = attach1
		t.Lifetime = 0.5
		t.Color = ColorSequence.new(Color3.fromRGB(150,150,150))
		t.WidthScale = NumberSequence.new(1,0)
		
		core.spawn(function()
			wait(1)
			status:set("weaponCool",true)
			wait(5)
			status:set("grenadeCool",true)
			marine.Grenade.Transparency = 0
		end)
	end
end

local knife = marine.Knife
function combat.stab()
	
	local canStab = false
	for i,v in pairs(status:get("currentTarget").Parent:GetChildren()) do
		if v:IsA("BasePart") and core.checkDist(v,myRoot) < 7 then
			canStab = true
		end
	end
	if canStab then
		myHuman:MoveTo(status:get("currentTarget").Position)
		
		knife.Stab:Play()
		knife.Attack:Play()
		
		if random(2) == 1 then
			stabAnimation:Play(0.1,1,2)
		else
			stabPunchAnimation:Play()
		end
		
		local human = status:get("currentTarget").Parent.Humanoid
		core.applyDamage(human,random(MinimumDamage,MaximumDamage))
		if human.Health <= 0 then
			targeting.findTarget()
		end
		
		--stabAnimation.Stopped:Wait()
		wait(0.5)
	else
		wait(0.2)
	end
end

function combat.checkCluster(target)
	--Check for nearby allies
	for i,v in ipairs(status:get("activeAllies")) do
		if core.checkDist(target,v) < 30 then
			return false
		end
	end
	--Check if enemies are paired close together
	for i,v in ipairs(status:get("potentialTargets")) do
		if v ~= target then
			if core.checkDist(target,v) < 15 then
				return true
			end
		end
	end
	return false
end

return combat
