How do I confirm a kill consistently?

So recently I’ve been working on a Shotgun. I currently need some help on confirming a kill.
Let me try to break this down:

My shotgun uses a for loop to shoot, as a shotgun shoots multiple pellets in once. I inserted a Value named “EliminatedBy” in a player’s folder. What I currently do is:

if TotalDMG - HitHumanoid.Health <= 0 then
-- Do elimination stuff (Add one elimination, show feed etc.)

But everything done above is all inside a for loop and I have to use a debounce to stop it, or else it will add multiple eliminations as it loops. Is there any more consistent way to achieve such behavior? I’m not sure is module script suitable for this case, but if it does, is there any simple examples that’s similar to this case? Thanks.

Please do let me know if you need more information regarding on this, I’m trying my best to explain this!

11 Likes

Any time you damage a player, create a “creator” tag.

local tag = Instance.new("ObjectValue")
tag.Value = plrChar -- this can either be the shooters character or plr object (adjust accordingly if you use character)
tag.Name = "creator"
tag.Parent = humanoid -- this is the enemies humanoid, again can be parented to their character (adjust to it if you use character)
game:GetService("Debris"):AddItem(tag,.5) -- destroys tag after half a second

And then make a died function for everyone’s humanoid to check if someone killed them:

humanoid.Died:connect(function()
local tag = humanoid:FindFirstChild("creator")
if tag then
local plr = tag.Value
-- do your magic
end
end)
32 Likes

This is probably the best way to handle it. However, if you don’t want to significantly change what you have, check if the humanoid has already died before running the logic to see if the bullet damage would reduce their health below 0.

5 Likes

Your method is kinda iffy to be honest. I agree with xuefei’s suggested method, but you could probably just handle it using a module script to save having to create instances inside characters. E.g

local HitModule = require(hitmodule)

function hit(from, part)
    -- Do hit stuff dmging etc
    HitModule.HitPart(from, part)
end

------------------------------

HitModule.PlayerKilled:Connect(function(lastHit, contributors)
    -- reward player who got lastHit, and rreward the contributors
end)

Inside the ModuleScript you would have something like this:

local hits = {}

-- whenever the module receives a hit, check for a character
-- if a character exists, make it a table in the dictionary "hits" e.g. hits[character] = {}
-- now check if there is already a hit from the player who shot the character already in the table, and if it exists merely update its timestamp
-- if a hit isn't there then do hits[character][from] = tick()

-- when a character dies from this table just loop through the hits and fire bindable event which can be picked up outside the modulescript.
-- clearup the table

-- of course if anything weird happens then remove the character from the table to save memory and not have  a giant huge dictionary full of useless information about dead characters or characters that no longer exist.
6 Likes

I’m new to module scripts, they works when being called on LocalScript right?
Also when it’s the part where it checks for character, it’s client or server sided?

Edit: They can’t be called from local scripts.
So do I have to FireServer and called the module? That sounds complicated…

Module scripts should be able to be called by a script or localscript, or another module script. Also, whichever type(global, local) requires the module will determine if it’s done client-side or server-side.

You should be able to call the module script from the client or the server, but you should really be handling this hit detection on the server anyways so this shouldn’t ever be called from the client.

The shotgun is using a for loop code, when it eliminates a player (humanoid.Died) and I’ll call the module script but it will fire few times, is there any ways to bring this out of the loop and only call the module script once?

I’d implement a debounce so just say something like this:

local debounce = false

function hit(part)
	if not debounce then
		debounce = true
		-- DO CODE
		wait(0.2)
		debounce = false
	end
end

I actually spent few hours on rewriting the code entirely, and I found a better way to do it.

When a player shoots another player, it will creates an ObjectValue. The value of it is the damaged player’s instance. The value’s parent is a Folder.
EliminatedBy is a Value stored in a folder, it’s value will change to the eliminator when TotalDMG - Health <= 0

HitStates.HitCharacters.ChildAdded:Connect(function(child)
	if child:IsA("ObjectValue") then
		if child.Value ~= "" then
			child.Value.Humanoid.Died:Connect(function()
				if players:GetPlayerFromCharacter(child.Value).Configs.EliminatedBy.Value == plr.Name then
					print("You killed him")
				else
					print("You assisted")
				end
				child:Destroy()
			end)
		end
	end
end)

For the module script, I’d like to include few things:

  • Shows the elimination/assist UI (Client)
  • Add one kill (Server)

How do I achieve both in a ModuleScript?

Here’s a more complex yet more secure derivative of a tagging system that gives you a little more flexibility at the end of the day. I derived this from the code I use in Redux and simplified it a lot so you don’t need to deal with all of my bloat. Steps:

When a player spawns:

clientdata:CreateCache(PlayerWhoSpawned)

When a player takes damage

clientdata:DamageDealt(PlayerWhoTookDamage, PlayerWhoDealtDamage, DamageAmount)

to get the player who killed someone

local killer, assistCountAsKill = clientdata:GetKiller(PlayerWhoDied)

I would compare killer and assistCountAsKill before awarding any points.

local clientdata = {}

function clientdata:CreateCache(client)
	clientdata.Cache[client.UserId]	= {
		['LifeDamage']		= {},
	}
end

function clientdata:DictionaryCount(dictionary)
	local value = 0
	for i,v in next, dictionary do
		value = value + 1
	end
	return value
end

function clientdata:DamageDealt(target, dealer, amount)
	if clientdata.Cache[target.UserId].LifeDamage[dealer.UserId] then
		clientdata.Cache[target.UserId].LifeDamage[dealer.UserId] = {clientdata.Cache[target.UserId].LifeDamage[dealer.UserId][1] + amount,tick()}
	else
		clientdata.Cache[target.UserId].LifeDamage[dealer.UserId] = {amount,tick()}
	end
end

function clientdata:GetKiller(player)
	local lifedamage = clientdata.Cache[player.UserId].LifeDamage
	if clientdata:DictionaryCount(lifedamage) >= 1 then
		local HighestDealer = {0,nil}
		local MostRecentDealer = {0,nil}
		for Dealer,Information in next, lifedamage do
			if Information[1] > HighestDealer[1] then
				HighestDealer = {Information[1],Dealer}
			end
			if Information[2] > MostRecentDealer[1] then
				MostRecentDealer = {Information[2],Dealer}
			end
		end
		HighestDealer = {HighestDealer[1], game.Players:GetPlayerByUserId(HighestDealer[2])}
		MostRecentDealer = {MostRecentDealer[1], game.Players:GetPlayerByUserId(MostRecentDealer[2])}
        return MostRecentDealer, HighestDealer
    end
end
12 Likes

Module scripts can require other module scripts.

1 Like

No they don’t @ZacharyFuzz

That’s only if they require each other and it’s called circular dependency. You also can’t do that, it won’t let you.

1 Like

Ah, gotcha. Did not know that. Thanks!