How do I make it so If the Player kills a NPC's they get Experience in Return

So I have a script where if the Player touches a Part with this script inside they will get Exp, But i’m trying to get it so when the player kills a NPC not another player they will get exp, Where should I put the script? And if I have a combat script should I do something with that?

robloxapp-20241226-0819039.wmv (4.0 MB)

I tried multiple different methods of doing this before but none of them worked, maybe because I don’t understand what I should do to make them work. I did try looking at other forms but I either didn’t understand them or the information in that form didn’t work for me.

Give Player Experience When they Touch Part:

local serverModules = game.ServerStorage:WaitForChild("ModuleScripts")
local PlayerStats = require(serverModules:WaitForChild("PlayerStats"))

local part = script.Parent

local db = {}
local dbTime = 0.6

part.Touched:Connect(function(touch)
	
	local player = game.Players:GetPlayerFromCharacter(touch.Parent)
	
	if player and not db[player] then
		
		db[player] = true
		
		local randomExpToGive = math.random(10, 60)
		
		PlayerStats.AddExp(player, randomExpToGive)
		
		task.wait(dbTime)
		db[player] = nil
	end
end)
5 Likes

I tried to give a video footage but it sent as a download link because I used the studio record feature

3 Likes

You need to set up a system that tracks the player who last damaged the NPC, and awards that player XP when that NPC dies. A primitive implementation would insert an ObjectValue within each NPC so that the game’s weapons can assign their wielders to that value. When the NPC dies, it will read the current state of the value. The player stored at this time would be the player who last attacked the NPC. There are more advanced solutions that work entirely through middleware. You can have a look at my system for handling FPS kill-tracking

2 Likes

I looked though the kill tracking script but I don’t understand how much of it actually is detecting when someone, or a NPC dies and who killed them, can you give a short example on how to use the Objective Value, I’ll keep looking at the script though to see if I’ll find anything.

2 Likes

You should keep track of which player damaged each NPC last through a table (your combat system should write to this table), and simply index the NPC when it dies to reward exp to that player

2 Likes

can you give an example on how to do this, I’m not good with tables, also is there any way to detect if what the player killed actually is a npc or just another player?

2 Likes

I don’t exactly know how your combat system works, but lets say you have a function that you run each time the player does damage to an NPC

local function attackCharacter(attacker: Player, character: Model)

all you have to do with this is make a table that lists out every NPC paired with a player, which gets updated whenever a player attacks an NPC

local npcAttackerMap: {[Model]: Player} = {}

and update the table whenever the function to attack one of your NPCs or characters is ran

local function attackCharacter(attacker: Player, character: Model)
        npcAttackerMap[character] = attacker

        -- if you'd like, you can also make this value expire after, for example, 2 minutes
        task.delay(120, function()
                npcAttackerMap[character] = nil
        end)
end

From this table, you can now decide which player to grant experience whenever an NPC dies

local function onCharacterDied(character: Model)
        local murderer = npcAttackerMap[character]

        -- It's important to check that the character was even murdered by anyone in the first place, as they might have died from things that weren't caused by an attack
        if murderer ~= nil then
                grantExperienceTo(murderer)

                -- Get rid of the value to not leak memory
                npcAttackerMap[character] = nil
        end
end

Also, you can tell if a character is a player or an NPC by using Players:GetPlayerFromCharacter(character) ~= nil, if it outputs true, it means it is a player, false otherwise

1 Like

do I put it in like this or do I have to do it a different way?

				hit.Parent.Humanoid:TakeDamage(5)
				local npcAttackerMap: {[Model]: Player} = {}
				local function attackCharacter(attacker: Player, character: Model)
					npcAttackerMap[character] = attacker
					
					task.delay(120, function()
						npcAttackerMap[character] = nil
					end)
				end
				
				local function onCharacterDied(character: Model)
					local murderer = npcAttackerMap[character]

					if murderer ~= nil then
						local randomExpToGive = math.random(10, 60)
						PlayerStats.AddExp(murderer, randomExpToGive)

						npcAttackerMap[character] = nil
					end
				end

I put it in wrong I already can tell

1 Like

Yes, you aren’t exactly supposed to simply copy and paste what I wrote into your script

ok, i’ll try to figure out how I should do this

So it turns out I’m dumb, the exp scrip needed a player to give it to, I forgot that the damage script already gave the player who damaged the npc or player, I used that and it worked, thanks though. Although is it possible to do a if statement for if the humanoid that was killed was a humanoid or npc so it only gives exp when the kill was a npc.

basically how to I make this into a if statement.

Players:GetPlayerFromCharacter(character) ~= nil

It starts with KillsService.trackDamage. This function takes in a Player instance and initializes a damage history for that player. A damage history is a chronological collection of damage tokens, where each token contains the following information:

type DamageToken = {
	Dealer : Player?,
	Damage : number,
	Cause  : string,
}

Here, you can see the damage token contains a “dealer”. This is a reference to the Player instance of the client who dealt the damage. KillsService.dealDamage is the middle-man that handles the creation of these tokens, dealing of the damage, and recording of said damage. Further within KillsService.trackDamage, events are set up for detecting the changing health of a player and their ultimate death. As the player gains health, we look at the oldest damage record and reduce its value by the health gain, ultimately erasing the damage record when the gain exceeds its recorded damage. This ensures players who’ve dealt damage to another aren’t considered if that damage is healed away. This also provides an opportunity to record any damage from outside forces.

When the player dies, buildDeathSummary is called, which is a function that evaluates the damage history of the tracked player. The same principle with the ObjectValue is applied here, in that the latest damage record will reveal the player who killed the other. The remaining tokens are processed for rewarding those who helped in slaying the tracked player along the way. This system provides a very powerful summary of how a tracked player died, yielding the following information:

export type DeathSummary = {
	Assists             : {Player},
	AssistCountAsKiller : Player?,
	Killer              : Player?,
	Time                : number,
	Cause               : string,
	Location            : Vector3?
}
1 Like
if Players:GetPlayerFromCharacter(character) == nil then
        print("Character is an NPC!")
else
        print("Character is not an NPC!")
end
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.