--Setting up Collision--

local BulletCases = "BulletCases"

local Success,Error = pcall(function()
	physicsService:CreateCollisionGroup(BulletCases)
end)

if Success then
	print("Created new collision group")
else
	warn("Collision group already exists")
end

physicsService:CollisionGroupSetCollidable(BulletCases,BulletCases,false)

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()
				
				impact.WorldPosition = Result.Position
				impact.BloodCloud:Emit(30)
				impact.BloodParticles:Emit(30)
			else
				local newBulletHole = combat.getBulletHole(Hit)
				newBulletHole.CFrame = NewCFrame(newBulletHole.Position, newBulletHole.Position + Result.Normal)
				newBulletHole.Position = Result.Position
				
				local debrisAttachment = newBulletHole.DebrisAttachment
				debrisAttachment.TinyDustParticles:Emit(15)
				debrisAttachment.MainDustEffect:Emit(15)
				debrisAttachment.SmokeEffect:Emit(15)
				
				debrisService:AddItem(newBulletHole,5)
				
				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)
					
					physicsService:SetPartCollisionGroup(chunk,BulletCases)
					debrisService:AddItem(chunk,1)
				end
				
				local HitSound = newBulletHole.ImpactSound
				HitSound.SoundId = HitWallSounds[random(1,#HitWallSounds)]
				HitSound.Volume = 1
				HitSound:Play()
			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()
					
					impact.WorldPosition = Result.Position
					impact.BloodCloud:Emit(30)
					impact.BloodParticles:Emit(30)
				end
			end
		end

	end)
	
	coroutine.resume(HitCoroutine)
end

function combat.getBulletHole(partHit)
	local bulletHole = bulletHoleCopy:Clone()
	bulletHole.Parent = workspace.IgnoreFolder
	
	local debrisAttachment = bulletHole.DebrisAttachment
	local decal = bulletHole.Decal
	
	decal.Color3 = partHit.Color
	debrisAttachment.TinyDustParticles.Color = NewColorSequence(partHit.Color)
	debrisAttachment.MainDustEffect.Color = NewColorSequence(partHit.Color)
	debrisAttachment.SmokeEffect.Color = NewColorSequence(partHit.Color)

	return bulletHole
end

function combat.destroyBulletRelatedCopies()
	bulletCopy:Destroy()
	bulletCaseCopy:Destroy()
	bulletHoleCopy:Destroy()
end

function combat.getBullet() -- only reason im not creating bullet here anymore is because too many attachments and trails and im lazy.
	local bullet = bulletCopy:Clone()
	bullet.Parent = workspace.IgnoreFolder

	return bullet
end

function combat.getCase() -- same thing as bullet
	local case = bulletCaseCopy:Clone()
	case.Parent = workspace.IgnoreFolder
	
	physicsService:SetPartCollisionGroup(case,BulletCases)
	
	return case
end

function pushBullet(bullet)
	-- replaced all the code in this function
	bullet.CFrame = barrel.WorldCFrame * Angles(0,-rad(90),0)
	
	runService.Heartbeat:Wait()
	
	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
	
	local NewCaster = FastCast.new()
	local Behaviour = FastCast.newBehavior()

	Behaviour.Acceleration = bulletacceleration
	Behaviour.RaycastParams = Params
	Behaviour.AutoIgnoreFolder = false
	Behaviour.CosmeticBulletContainer = workspace.IgnoreFolder
	Behaviour.MaxDistance = maxdistance
	
	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 bullet then
			local BulletLength = bullet.Size.Z/2
			local Offset = NewCFrame(0,0,-(Length - BulletLength))
			bullet.CFrame = CFLookAt(LastPoint, LastPoint + NewDirection):ToWorldSpace(Offset) * Angles(0,-rad(90),0)
		end

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

	NewCaster.RayHit:Connect(function(Cast, Result, Velocity, Bullet)
		OnHit(Cast, Result, Velocity, Bullet)
		
		debrisService:AddItem(bullet,0.2)
		bullet.Transparency = 1
	end)
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 SemiAtLongRange then
			if core.checkDist(target,myRoot) > 60 then
				shot = 1
			else
				shot = burstamount
			end
		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 not SemiAtLongRange then
					if core.checkDist(myRoot,target) < 7 then break end
				end
				
				wait(firerate)
				status:set("mag",status:get("mag")-1)
				
				--flash:Emit(1)
				local FlashCoroutine = coroutine.create(function()
					barrel.MuzzleFlashEffect:Emit(15)
					barrel.MuzzleFlashInnerEffect:Emit(15)
					barrel.SmokeEffect:Emit(3)
					barrel.SparkEffect:Emit(20)
					lightFlash.Enabled = true 
					wait(0.05)
					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)
						end)
					end
				else
					local bullet = combat.getBullet()
					pushBullet(bullet)
				end
				
				local case = combat.getCase()
				case.CFrame = chamber.WorldCFrame
				case.Velocity = case.CFrame.RightVector * -20 * NewVector(1,-3,1)
				case.RotVelocity = NewVector(random(-0.2,0.2),random(-0.2,0.2),random(-0.2,0.2))
				case:SetNetworkOwner(nil)
				debrisService:AddItem(case,2.5)

				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
					
					if core.checkDist(myRoot,target) < 7 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.MuzzleFlashEffect:Emit(15)
						barrel.MuzzleFlashInnerEffect:Emit(15)
						barrel.SmokeEffect:Emit(3)
						barrel.SparkEffect:Emit(20)
						lightFlash.Enabled = true 
						wait(0.05)
						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)
							end)
						end
					else
						local bullet = combat.getBullet()
						pushBullet(bullet)
					end

					local case = combat.getCase()
					case.CFrame = chamber.WorldCFrame
					case.Velocity = case.CFrame.RightVector * -20 * NewVector(1,-3,1)
					case.RotVelocity = NewVector(random(-0.2,0.2),random(-0.2,0.2),random(-0.2,0.2))
					case:SetNetworkOwner(nil)
					debrisService:AddItem(case,2.5)

					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 = NewColorSequence(Color3.fromRGB(150,150,150))
		t.WidthScale = NewNumberSequence(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()
		knife.KnifeTrail.Enabled = true
		
		if random(2) == 1 then
			stabAnimation:Play(0.1,1,2)
		else
			stabPunchAnimation:Play()
		end
		
		local targetcharacter = status:get("currentTarget").Parent
		local human = targetcharacter.Humanoid
		local torso = nil
		
		if targetcharacter:FindFirstChild("Torso") then
			torso = targetcharacter.Torso
		elseif targetcharacter:FindFirstChild("UpperTorso") then
			torso = targetcharacter.UpperTorso
		end
		
		core.applyDamage(human,random(MinimumDamage,MaximumDamage))
		if human.Health <= 0 then
			targeting.findTarget()
		end
		
		if torso then
			impact.WorldPosition = torso.Position + NewVector(random(-0.5,0.5),random(-0.5,0.5),random(-0.5,0.5))
			impact.BloodCloud:Emit(30)
			impact.BloodParticles:Emit(30)
		end
		
		--stabAnimation.Stopped:Wait()
		wait(0.5)
		knife.KnifeTrail.Enabled = false
	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
