function getBaseDamage(N)local T=S.damageSettings.Start.Damage local g=S.damageSettings.Start.Dist local r=S.damageSettings.End.Damage local s=S.damageSettings.End.Dist return N<g*S.bulletSettings.Range and T or(N>=g*S.bulletSettings.Range and N<s*S.bulletSettings.Range)and numLerp(T,r,Map(N/S.bulletSettings.Range,g,s,0,1))or N>=s*S.bulletSettings.Range and r end function Damage(N,T,g,r,s,J)local d=S.damageSettings.Multipliers.Head local R=S.damageSettings.Multipliers.Chest local M=S.damageSettings.Multipliers.Limbs local Q=getBaseDamage(s)if Humanoid.Health~=0 then local g=nil if N.Parent:IsA("\072\097\116")then table.insert(J,N)local d=Ray.new(T-r*.1,r*((S.bulletSettings.Range-s)+.1))local R,M,Q=workspace:FindPartOnRayWithIgnoreList(d,J)if R then g=Damage(R,M,Q,r,s+(M-T).magnitude,J)end else g=findFirstClass(N.Parent,"\072\117\109\097\110\111\105\100")if g and(g.Health>0 and isEnemy(g))then local T=Instance.new("\079\098\106\101\099\116\086\097\108\117\101")T.Value=Player T.Name="\099\114\101\097\116\111\114"T.Parent=g DS:AddItem(T,.3)local r=0 if N.Name=="\072\101\097\100"then r=Q*RAND(d,d+.1,.01)elseif N.Name=="\084\111\114\115\111"then r=Q*RAND(R,R+.1,.01)else r=Q*RAND(M,M+.1,.01)end g:TakeDamage(r)markHit()end end return g end end function isWallIgnored(N)return N.Transparency>=S.penetrationSettings.transparencyThreshold or S.penetrationSettings.ignoreNonCanCollide and not N.CanCollide or isIgnored(N,S.penetrationSettings.ignoreCustom)end function penetrateWall(N,T,g,r,s,J,d,R)local M=isWallIgnored(N)local Q=N.Parent:IsA("\072\097\116")and findFirstClass(N.Parent.Parent,"\072\117\109\097\110\111\105\100")or findFirstClass(N.Parent,"\072\117\109\097\110\111\105\100")local t=nil if Q and Q~=R then R=Q t=Damage(N,T,r,g,d,{Char;ignoreModel})else R=nil end local c=Q and(N.Parent:IsA("\072\097\116")and N.Parent.Parent or N.Parent)or N table.insert(s,c)local h=S.bulletSettings.Range-d local e=Ray.new(T,g*(S.bulletSettings.Range-d))local P,G,m=workspace:FindPartOnRayWithIgnoreList(e,s)local O=removeElement(s,c)local E=Ray.new(G+g*.1,-g*(h+1))local a,z,v=workspace:FindPartOnRayWithIgnoreList(E,s)local o=J+(M and 0 or(getNearestPoint(G,z,T)-T).magnitude)local j=d+(G-T).magnitude local W=Round(o,.001)>S.penetrationSettings.Dist or Round(j,.001)>S.bulletSettings.Range if not M then createBulletImpact:FireServer(N,T,r,g,Q,gunIgnore,S)if not Q then createShockwave:FireServer(T,S.shockwaveSettings.Radius,gunIgnore,S)end end if Q and(Q.Health>0 and(isEnemy(Q)and Q==t))then createBlood:FireServer(N,z,g,gunIgnore,S)end if W or not P then if not W and not M then createBulletImpact:FireServer(N,z,v,g,Q,gunIgnore,S)if not Q then createShockwave:FireServer(z,S.shockwaveSettings.Radius,gunIgnore,S)end end return N,T else if N==a and not M then createBulletImpact:FireServer(N,z,v,g,Q,gunIgnore,S)if not Q then createShockwave:FireServer(z,S.shockwaveSettings.Radius,gunIgnore,S)end end return penetrateWall(P,G,g,m,s,o,j,R)end end function PenetrateWall(N,T,g,r,s,J)local d=(N-r).magnitude local R,M=nil,nil local Q,t=nil,nil local c={unpack(Ignore)}for g=1,10,1 do local r=Ray.new(N-T*.1,T*S.Penetration)local s,J=game.Workspace:FindPartOnRayWithIgnoreList(r,c)if s then local N=nil if s.Parent.ClassName=="\072\097\116"then N=findFirstClass(s.Parent.Parent,"\072\117\109\097\110\111\105\100")else N=findFirstClass(s.Parent,"\072\117\109\097\110\111\105\100")end if N and g~=1 then Q,t=s,J break else R,M=s,J table.insert(c,s)end else break end end if R then if S.InstantHit then if Q then Damage(Q.Parent:FindFirstChild("\072\101\097\100"),t)return t else local Q,t=nil,nil if g then Q,t=AdvRayCast(M,T,S.BulletRange-d,{R;g.Parent;unpack(Ignore)})else Q,t=AdvRayCast(M,T,S.BulletRange-d,{R;unpack(Ignore)})end Damage(Q,t)local c=J+(M-N).magnitude local h=t if c<S.Penetration and Q then h=PenetrateWall(t,T,g,r,s,J+c)end return h end else local c=M local h=0 spawn(function()if Q then Damage(Q.Parent:FindFirstChild("\072\101\097\100"),t)return t else while true do RS.RenderStepped:wait()if h>=S.BulletRange-d then s:Destroy()break end local Q=(s.Position-c).magnitude local t=(s.Position-c).unit local e,P=nil,nil if g then e,P=AdvRayCast(c,t,Q,{R,g.Parent;unpack(Ignore)})else e,P=AdvRayCast(c,t,Q,{R,unpack(Ignore)})end if e then Damage(e,P)local d=J+(M-N).magnitude local R=P if d<S.Penetration and e then R=PenetrateWall(P,T,g,r,s,J+d)else s:Destroy()end return R else c=s.Position h=h+Q end end end end)end else if s then s:Destroy()end return N end end function isEnemy(N)local T=game.Players:GetPlayerFromCharacter(N.Parent)if not T then return S.CanDamageNPCs end return S.AllowFriendlyFire or T.TeamColor~=Player.TeamColor or T.Neutral end


function getBaseDamage(Dist)
	local startDmg = S.damageSettings.Start.Damage
	local startDist = S.damageSettings.Start.Dist
	local endDmg = S.damageSettings.End.Damage
	local endDist = S.damageSettings.End.Dist
	return (
		(
			Dist < startDist * S.bulletSettings.Range
		) and startDmg or
		(
			Dist >= startDist * S.bulletSettings.Range and
			Dist < endDist * S.bulletSettings.Range
		) and numLerp(startDmg, endDmg, Map(Dist / S.bulletSettings.Range, startDist, endDist, 0, 1)) or
		(
			Dist >= endDist * S.bulletSettings.Range
		) and endDmg
	)
end

function Damage(H, P, N, D, Dist, customIgnore)
	local hVal = S.damageSettings.Multipliers.Head
	local cVal = S.damageSettings.Multipliers.Chest
	local lVal = S.damageSettings.Multipliers.Limbs
	local baseDamage = getBaseDamage(Dist)
	if Humanoid.Health ~= 0 then
		local hitHumanoid = nil
		if H.Parent:IsA("Hat") then
			table.insert(customIgnore, H)
			local newRay = Ray.new(P - D * 0.1, D * (S.bulletSettings.Range - Dist + 0.1))
			local newH, newP, newN = workspace:FindPartOnRayWithIgnoreList(newRay, customIgnore)
			if newH then
				hitHumanoid = Damage(newH, newP, newN, D, Dist + (newP - P).magnitude, customIgnore)
			end
		else
			hitHumanoid = findFirstClass(H.Parent, "Humanoid")
			if hitHumanoid and hitHumanoid.Health > 0 and isEnemy(hitHumanoid) then
				local Tag = Instance.new("ObjectValue")
				Tag.Value = Player
				Tag.Name = "creator"
				Tag.Parent = hitHumanoid
				DS:AddItem(Tag, 0.3)
				local chosenDamage = 0
				if H.Name == "Head" then
					chosenDamage = baseDamage  * RAND(hVal, hVal + 0.1, 0.01)
				elseif H.Name == "Torso" then
					chosenDamage = baseDamage  * RAND(cVal, cVal + 0.1, 0.01)
				else
					chosenDamage = baseDamage  * RAND(lVal, lVal + 0.1, 0.01)
				end
				hitHumanoid:TakeDamage(chosenDamage)
				markHit()
			end
		end
		return hitHumanoid
	end
end

function isWallIgnored(Wall)
	return (
		Wall.Transparency >= S.penetrationSettings.transparencyThreshold or
		(S.penetrationSettings.ignoreNonCanCollide and (not Wall.CanCollide)) or
		isIgnored(Wall, S.penetrationSettings.ignoreCustom)
	)
end

function penetrateWall(Wall, hitPos, Direction, Normal, Ignore, totalPDist, totalBDist, lastDamagedHumanoid)
	local wallIgnore = isWallIgnored(Wall)
	local hitHumanoid = (Wall.Parent:IsA("Hat") and findFirstClass(Wall.Parent.Parent, "Humanoid") or findFirstClass(Wall.Parent, "Humanoid"))
	local damagedHumanoid = nil
	if hitHumanoid and hitHumanoid ~= lastDamagedHumanoid then
		lastDamagedHumanoid = hitHumanoid
		damagedHumanoid = Damage(Wall, hitPos, Normal, Direction, totalBDist, {Char, ignoreModel})
	else
		lastDamagedHumanoid = nil
	end
	local ignoreObject = hitHumanoid and (Wall.Parent:IsA("Hat") and Wall.Parent.Parent or Wall.Parent) or Wall
	table.insert(Ignore, ignoreObject)
	local rayLength = S.bulletSettings.Range - totalBDist
	local testRay = Ray.new(hitPos, Direction * (S.bulletSettings.Range - totalBDist))
	local H1, P1, N1 = workspace:FindPartOnRayWithIgnoreList(testRay, Ignore)
	local newIgnore = removeElement(Ignore, ignoreObject)
	local wallRay = Ray.new(P1 + Direction * 0.1, -Direction * (rayLength + 1))
	local H2, P2, N2 = workspace:FindPartOnRayWithIgnoreList(wallRay, Ignore)
	local newPDist = totalPDist + (wallIgnore and 0 or (getNearestPoint(P1, P2, hitPos) - hitPos).magnitude)
	local newBDist = totalBDist + (P1 - hitPos).magnitude
	local outOfRange = Round(newPDist, 0.001) > S.penetrationSettings.Dist or Round(newBDist, 0.001) > S.bulletSettings.Range
	if (not wallIgnore) then
		createBulletImpact:FireServer(Wall, hitPos, Normal, Direction, hitHumanoid, gunIgnore, S)
		if (not hitHumanoid) then
			createShockwave:FireServer(hitPos, S.shockwaveSettings.Radius, gunIgnore, S)
		end
	end
	if hitHumanoid and hitHumanoid.Health > 0 and isEnemy(hitHumanoid) and hitHumanoid == damagedHumanoid then
		createBlood:FireServer(Wall, P2, Direction, gunIgnore, S)
	end
	if outOfRange or (not H1) then
		if (not outOfRange) and (not wallIgnore) then
			createBulletImpact:FireServer(Wall, P2, N2, Direction, hitHumanoid, gunIgnore, S)
			if (not hitHumanoid) then
				createShockwave:FireServer(P2, S.shockwaveSettings.Radius, gunIgnore, S)
			end
		end
		return Wall, hitPos
	else
		if Wall == H2 and (not wallIgnore) then
			createBulletImpact:FireServer(Wall, P2, N2, Direction, hitHumanoid, gunIgnore, S)
			if (not hitHumanoid) then
				createShockwave:FireServer(P2, S.shockwaveSettings.Radius, gunIgnore, S)
			end
		end
		return penetrateWall(H1, P1, Direction, N1, Ignore, newPDist, newBDist, lastDamagedHumanoid)
	end
end

function PenetrateWall(HitPos, Direction, HitHumanoid, OriginPos, Bullet, CurrentPDist)
	local HitDist = (HitPos - OriginPos).magnitude
	local Wall, WallHitPos = nil, nil
	local Hum, HumHitPos = nil, nil
	local CustomIgnore = {unpack(Ignore)}
	for i = 1, 10 do
		local WallRay = Ray.new(HitPos - (Direction * 0.1), Direction * S.Penetration)
		local H, P = game.Workspace:FindPartOnRayWithIgnoreList(WallRay, CustomIgnore)
		if H then
			local HitHumanoid = nil
			if H.Parent.ClassName == "Hat" then
				HitHumanoid = findFirstClass(H.Parent.Parent, "Humanoid")
			else
				HitHumanoid = findFirstClass(H.Parent, "Humanoid")
			end
			if HitHumanoid and i ~= 1 then
				Hum, HumHitPos = H, P
				break
			else
				Wall, WallHitPos = H, P
				table.insert(CustomIgnore, H)
			end
		else
			break
		end
	end
	if Wall then
		if S.InstantHit then
			if Hum then
				Damage(Hum.Parent:FindFirstChild("Head"), HumHitPos)
				return HumHitPos
			else
				local HitObj2, HitPos2 = nil, nil
				if HitHumanoid then
					HitObj2, HitPos2 = AdvRayCast(WallHitPos, Direction, S.BulletRange - HitDist, {Wall, HitHumanoid.Parent, unpack(Ignore)})
				else
					HitObj2, HitPos2 = AdvRayCast(WallHitPos, Direction, S.BulletRange - HitDist, {Wall, unpack(Ignore)})
				end
				Damage(HitObj2, HitPos2)
				
				local NewPDist = CurrentPDist + (WallHitPos - HitPos).magnitude
				local NewHitPos2 = HitPos2
				if NewPDist < S.Penetration and HitObj2 then
					NewHitPos2 = PenetrateWall(HitPos2, Direction, HitHumanoid, OriginPos, Bullet, CurrentPDist + NewPDist)
				end
				return NewHitPos2
			end
		else
			local LastPos = WallHitPos
			local TotalDistTraveled = 0
			spawn(function()
				if Hum then
					Damage(Hum.Parent:FindFirstChild("Head"), HumHitPos)
					return HumHitPos
				else
					while true do
						RS.RenderStepped:wait()
						if TotalDistTraveled >= S.BulletRange - HitDist then
							Bullet:Destroy()
							break
						end
						local DistTraveled = (Bullet.Position - LastPos).magnitude
						local NewDirection = (Bullet.Position - LastPos).unit
						local TempHitObj, TempHitPos = nil, nil
						if HitHumanoid then
							TempHitObj, TempHitPos = AdvRayCast(LastPos, NewDirection, DistTraveled, {Wall, HitHumanoid.Parent, unpack(Ignore)})
						else
							TempHitObj, TempHitPos = AdvRayCast(LastPos, NewDirection, DistTraveled, {Wall, unpack(Ignore)})
						end
						if TempHitObj then
							Damage(TempHitObj, TempHitPos)
							
							local NewPDist = CurrentPDist + (WallHitPos - HitPos).magnitude
							local NewTempPos = TempHitPos
							if NewPDist < S.Penetration and TempHitObj then
								NewTempPos = PenetrateWall(TempHitPos, Direction, HitHumanoid, OriginPos, Bullet, CurrentPDist + NewPDist)
							else
								Bullet:Destroy()
							end
							return NewTempPos
						else
							LastPos = Bullet.Position
							TotalDistTraveled = TotalDistTraveled + DistTraveled
						end
					end
				end
			end)
		end
	else
		if Bullet then Bullet:Destroy() end
		return HitPos
	end
end

function isEnemy(Human)
	local Plyr = game.Players:GetPlayerFromCharacter(Human.Parent)
	if (not Plyr) then return S.CanDamageNPCs end
	return S.AllowFriendlyFire or (Plyr.TeamColor ~= Player.TeamColor or Plyr.Neutral)
end
