How would I make it so that players on the same team cant kill each other

As the title says, i want to make it so that team members cant kill each other, currently that’s the only thing thats stopping me from releasing the game. I want to make it so that if the player shoots their teammate, the bullet/damage doesnt register, but nothing happens to the player who shot at their teammate. How can I do this?

(edit)
heres the damage script using raycast

local BULLET_SPEED = 150							
local BULLET_MAXDIST = 1000							
local BULLET_GRAVITY = Vector3.new(0, 1, 0)		
local MIN_BULLET_SPREAD_ANGLE = 0			
local MAX_BULLET_SPREAD_ANGLE = 	0	
local FIRE_DELAY = 0.30
local BULLETS_PER_SHOT = 1				
local PIERCE_DEMO = false	

-- Local Variables

local Tool = script.Parent
local ammo = Tool:WaitForChild("Ammo")
local RELOADINGINPROG = Tool:WaitForChild("Reloading")
local emptymag = Tool.Handle.clip_empty
local reload = Tool.Handle.reload
local clipsize = 15
local Handle = Tool.Handle
local MouseEvent = Tool.MouseEvent
local ReloadEvent = Tool.Reloading
local FirePointObject = Handle.GunFirePoint
local FastCast = require(Tool.FastCastRedux)
local FireSound = Handle.Fire
local ImpactParticle = Handle.ImpactParticle
local Debris = game:GetService("Debris")
local table = require(Tool.FastCastRedux.Table)
local PartCacheModule = require(Tool.PartCache)
local CanFire = true	
local reloading = false

local RNG = Random.new()							-- Set up a randomizer.
local TAU = math.pi * 2							-- Set up mathematical constant Tau (pi * 2)
FastCast.DebugLogging = DEBUG
FastCast.VisualizeCasts = DEBUG

---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
-- Cast Objects

-- Cosmetic bullet container
local CosmeticBulletsFolder = workspace:FindFirstChild("CosmeticBulletsFolder") or Instance.new("Folder", workspace)
CosmeticBulletsFolder.Name = "CosmeticBulletsFolder"

-- Now we set the caster values.
local Caster = FastCast.new() --Create a new caster object.

-- Make a base cosmetic bullet object. This will be cloned every time we fire off a ray.
local CosmeticBullet = Instance.new("Part")
CosmeticBullet.Material = Enum.Material.Neon
CosmeticBullet.Color = Color3.fromRGB(189, 189, 0)
CosmeticBullet.CanCollide = false
CosmeticBullet.Anchored = true
CosmeticBullet.Size = Vector3.new(0.2, 0.2, 2.4)

-- New raycast parameters.
local CastParams = RaycastParams.new()
CastParams.IgnoreWater = true
CastParams.FilterType = Enum.RaycastFilterType.Blacklist
CastParams.FilterDescendantsInstances = {}

-- NEW V13.1.0 - PartCache tie-in. If you use the PartCache module to create cosmetic bullets, you can now directly tie that in.
-- Ensure you're using the latest version of PartCache.
local CosmeticPartProvider = PartCacheModule.new(CosmeticBullet, 100, CosmeticBulletsFolder)

-- NEW v12.0.0: Casters now use a data packet which can be made like what follows.
-- Check the API for more information: https://etithespirit.github.io/FastCastAPIDocs/fastcast-objects/fcbehavior
local CastBehavior = FastCast.newBehavior()
CastBehavior.RaycastParams = CastParams
CastBehavior.MaxDistance = BULLET_MAXDIST
CastBehavior.HighFidelityBehavior = FastCast.HighFidelityBehavior.Default

-- CastBehavior.CosmeticBulletTemplate = CosmeticBullet -- Uncomment if you just want a simple template part and aren't using PartCache
CastBehavior.CosmeticBulletProvider = CosmeticPartProvider -- Comment out if you aren't using PartCache.

CastBehavior.CosmeticBulletContainer = CosmeticBulletsFolder
CastBehavior.Acceleration = BULLET_GRAVITY
CastBehavior.AutoIgnoreContainer = false -- We already do this! We don't need the default value of true (see the bottom of this script)

-- Bonus points: If you're going to be slinging a ton of bullets in a short period of time, you may see it fit to use PartCache.
-- https://devforum.roblox.com/t/partcache-for-all-your-quick-part-creation-needs/246641 

---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
-- Helper Functions

-- A function to play fire sounds.
function PlayFireSound()

	local NewSound = FireSound:Clone()
	NewSound.Parent = Handle
	Debris:AddItem(NewSound, NewSound.TimeLength)
	if reloading == false then
		NewSound:Play()
	else
		if reloading == true then
			NewSound:Stop()
			reload.Ended:Wait()
			NewSound:Play()
		end
	end
end

function ReloadGun()
	if reloading then return end
	reloading = true
	reload:Play()
	reload.Ended:Wait()
	ammo.Value = 10
	reloading = false
end

-- Create the spark effect for the bullet impact
function MakeParticleFX(position, normal)
	-- This is a trick I do with attachments all the time.
	-- Parent attachments to the Terrain - It counts as a part, and setting position/rotation/etc. of it will be in world space.
	-- UPD 11 JUNE 2019 - Attachments now have a "WorldPosition" value, but despite this, I still see it fit to parent attachments to terrain since its position never changes.
	local attachment = Instance.new("Attachment")
	attachment.CFrame = CFrame.new(position, position + normal)
	attachment.Parent = workspace.Terrain
	local particle = ImpactParticle:Clone()
	particle.Parent = attachment
	Debris:AddItem(attachment, particle.Lifetime.Max) -- Automatically delete the particle effect after its maximum lifetime.

	-- A potentially better option in favor of this would be to use the Emit method (Particle:Emit(numParticles)) though I prefer this since it adds some natural spacing between the particles.
	particle.Enabled = true
	wait(0.05)
	particle.Enabled = false
end

---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
-- Main Logic

local function Reflect(surfaceNormal, bulletNormal)
	return bulletNormal - (2 * bulletNormal:Dot(surfaceNormal) * surfaceNormal)
end

-- The pierce function can also be used for things like bouncing.
-- In reality, it's more of a function that the module uses to ask "Do I end the cast now, or do I keep going?"
-- Because of this, you can use it for logic such as ray reflection or other redirection methods.
-- A great example might be to pierce or bounce based on something like velocity or angle.
-- You can see this implementation further down in the OnRayPierced function.
function CanRayPierce(cast, rayResult, segmentVelocity)

	-- Let's keep track of how many times we've hit something.
	local hits = cast.UserData.Hits
	if (hits == nil) then
		-- If the hit data isn't registered, set it to 1 (because this is our first hit)
		cast.UserData.Hits = 1
	else
		-- If the hit data is registered, add 1.
		cast.UserData.Hits += 1
	end

	-- And if the hit count is over 3, don't allow piercing and instead stop the ray.
	if (cast.UserData.Hits > 3) then
		return false
	end

	-- Now if we make it here, we want our ray to continue.
	-- This is extra important! If a bullet bounces off of something, maybe we want it to do damage too!
	-- So let's implement that.
	local hitPart = rayResult.Instance
	if hitPart ~= nil and hitPart.Parent ~= nil then
		local humanoid = hitPart.Parent:FindFirstChild("Humanoid")
		if humanoid then
			humanoid:TakeDamage(14) -- Damage.
		end
	end

	-- And then lastly, return true to tell FC to continue simulating.
	return true

end

function Fire(direction)
	-- Called when we want to fire the gun.
	if Tool.Parent:IsA("Backpack") then return end -- Can't fire if it's not equipped.



	local directionalCF = CFrame.new(Vector3.new(), direction)
	-- Now, we can use CFrame orientation to our advantage.
	-- Overwrite the existing Direction value.
	local direction = (directionalCF * CFrame.fromOrientation(0, 0, RNG:NextNumber(0, TAU)) * CFrame.fromOrientation(math.rad(RNG:NextNumber(MIN_BULLET_SPREAD_ANGLE, MAX_BULLET_SPREAD_ANGLE)), 0, 0)).LookVector

	local humanoidRootPart = Tool.Parent:WaitForChild("HumanoidRootPart", 1)	-- Add a timeout to this.
	local myMovementSpeed = humanoidRootPart.Velocity							-- To do: It may be better to get this value on the clientside since the server will see this value differently due to ping and such.
	local modifiedBulletSpeed = (direction * BULLET_SPEED)-- + myMovementSpeed	-- We multiply our direction unit by the bullet speed. This creates a Vector3 version of the bullet's velocity at the given speed. We then add MyMovementSpeed to add our body's motion to the velocity.

	if PIERCE_DEMO then
		CastBehavior.CanPierceFunction = CanRayPierce
	end

	local simBullet = Caster:Fire(FirePointObject.WorldPosition, direction, modifiedBulletSpeed, CastBehavior)

	PlayFireSound()
end

function OnRayHit(cast, raycastResult, segmentVelocity, cosmeticBulletObject)
	-- This function will be connected to the Caster's "RayHit" event.
	local hitPart = raycastResult.Instance
	local hitPoint = raycastResult.Position
	local normal = raycastResult.Normal
	local plrTeam = hitPart.Parent
	if hitPart ~= nil and hitPart.Parent ~= nil then -- Test if we hit something
		local humanoid = hitPart.Parent:FindFirstChild("Humanoid") -- Is there a humanoid?
		if humanoid then
			humanoid:TakeDamage(32) -- Damage.
		end
		MakeParticleFX(hitPoint, normal) -- Particle FX
	end
end

function OnRayPierced(cast, raycastResult, segmentVelocity, cosmeticBulletObject)
	-- You can do some really unique stuff with pierce behavior - In reality, pierce is just the module's way of asking "Do I keep the bullet going, or do I stop it here?"
	-- You can make use of this unique behavior in a manner like this, for instance, which causes bullets to be bouncy.
	local position = raycastResult.Position
	local normal = raycastResult.Normal

	local newNormal = Reflect(normal, segmentVelocity.Unit)
	cast:SetVelocity(newNormal * segmentVelocity.Magnitude)

	-- It's super important that we set the cast's position to the ray hit position. Remember: When a pierce is successful, it increments the ray forward by one increment.
	-- If we don't do this, it'll actually start the bounce effect one segment *after* it continues through the object, which for thin walls, can cause the bullet to almost get stuck in the wall.
	cast:SetPosition(position)

	-- Generally speaking, if you plan to do any velocity modifications to the bullet at all, you should use the line above to reset the position to where it was when the pierce was registered.
end

function OnRayUpdated(cast, segmentOrigin, segmentDirection, length, segmentVelocity, cosmeticBulletObject)
	-- Whenever the caster steps forward by one unit, this function is called.
	-- The bullet argument is the same object passed into the fire function.
	if cosmeticBulletObject == nil then return end
	local bulletLength = cosmeticBulletObject.Size.Z / 2 -- This is used to move the bullet to the right spot based on a CFrame offset
	local baseCFrame = CFrame.new(segmentOrigin, segmentOrigin + segmentDirection)
	cosmeticBulletObject.CFrame = baseCFrame * CFrame.new(0, 0, -(length - bulletLength))
end

function OnRayTerminated(cast)
	local cosmeticBullet = cast.RayInfo.CosmeticBulletObject
	if cosmeticBullet ~= nil then
		-- This code here is using an if statement on CastBehavior.CosmeticBulletProvider so that the example gun works out of the box.
		-- In your implementation, you should only handle what you're doing (if you use a PartCache, ALWAYS use ReturnPart. If not, ALWAYS use Destroy.
		if CastBehavior.CosmeticBulletProvider ~= nil then
			CastBehavior.CosmeticBulletProvider:ReturnPart(cosmeticBullet)
		else
			cosmeticBullet:Destroy()
		end
	end
end

MouseEvent.OnServerEvent:Connect(function (clientThatFired, mousePoint)

	if not CanFire then
		return
	end

	CanFire = false

	local mouseDirection = (mousePoint - FirePointObject.WorldPosition).Unit

	if ammo.Value > 0 and not reloading then
		Fire(mouseDirection)
		ammo.Value -= 1
	else
		ReloadGun()
	end

	if FIRE_DELAY > 0.03 then wait(FIRE_DELAY) end

	CanFire = true

end)


Caster.RayHit:Connect(OnRayHit)
Caster.RayPierced:Connect(OnRayPierced)
Caster.LengthChanged:Connect(OnRayUpdated)
Caster.CastTerminating:Connect(OnRayTerminated)

ReloadEvent.OnServerEvent:Connect(ReloadGun)

Tool.Equipped:Connect(function ()
	CastParams.FilterDescendantsInstances = {Tool.Parent, CosmeticBulletsFolder}
end)


If you wanted that, in the damage script, simply add a check to make sure they aren’t on the same team:

if not target.Team == plr.Team then
    humanoid:TakeDamage(damage)
end
1 Like

how would i get the target? using hit.parent?

Ah, the target would be something like this:

local target = game.Players:GetPlayerFromCharacter(hit.Parent) or game.Players:GetPlayerFromCharacter(hit.Parent.Parent)

image
sorry for late reply but this is what i get,

Heres the script that i did

	local hitPoint = raycastResult.Position
	local normal = raycastResult.Normal
	local plrTeam = hitPart.Parent
	local target = game.Players:GetPlayerFromCharacter(hitPart.Parent) or game.Players:GetPlayerFromCharacter(hitPart.Parent.Parent)
	if hitPart ~= nil and hitPart.Parent ~= nil then -- Test if we hit something
		local humanoid = hitPart.Parent:FindFirstChild("Humanoid")
		-- Is there a humanoid?
		if humanoid and if not plrTeam == target.Team then 
			humanoid:TakeDamage(32)
		
			
		end```

you have an extra if.

if humanoid and not plrTeam == target.Team then

Also, you are getting that error because you can shorten the statement to just

if humanoid and plrTeam ~= target.Team then

Tried it out ingame, Still killing my teammate. Anything else im missing?

	local hitPoint = raycastResult.Position
	local normal = raycastResult.Normal
	local plrTeam = hitPart.Parent
	local target = game.Players:GetPlayerFromCharacter(hitPart.Parent) or game.Players:GetPlayerFromCharacter(hitPart.Parent.Parent)
	if hitPart ~= nil and hitPart.Parent ~= nil then -- Test if we hit something
		local humanoid = hitPart.Parent:FindFirstChild("Humanoid")
		-- Is there a humanoid?
		if humanoid and  plrTeam ~= target.Team then 
			humanoid:TakeDamage(32)
		
			
		end```

Well, after looking into your code a bit more than where you were getting the error, you put plrTeam as the hitPart’s parent?

Where is your script? If it’s underneath a tool in player backpack, use plrTeam = script.Parent.Parent.Parent.Team

Here is a quick example:

Tool.Unequipped:Connect(function ()
	CastParams.FilterDescendantsInstances = {CosmeticBulletsFolder}
end)

I did not try this code so this might not work!!!

Yeah, Where would I have put it instead?

The player attacking and the player attacking’s team would probably be like this:

local plr = script:FindFirstAncestorOfClass("Player") or game.Players:GetPlayerFromCharacter(script:FindFirstAncestorOfClass("Tool").Parent)
local plrTeam = plr.Team