Damage Module & Improved TagHumanoid function
Introduction
Hi! I’m about to release an open-sourced system for applying Status Effects which utilizes this function so I’m making this post first
You might have seen something called TagHumanoid in Roblox Gear scripts, which is used to indicate the last player who killed an enemy or another player. It does not let you know how much damage each player contributed if many people team up to fight.
This function allows you to replace the outdated method by Roblox. You can check not only the last hit, but also total damage contributed by each individual player. And BTW, the attacker can be either a player or an NPC, and the target can also be either.
On top of that, I have a function for dealing damage here, it automatically does the sanity checks as well as tagging the target upon dealing damage. You can even send info like ignoring defense or immunity or attack type (If the target has Defense or AttackType . . Immunity Attributes)
Usage:
- Create a new ModuleScript and place it somewhere accessible by other scripts
- Copy and paste the code below into it
- Use local DamageModule = require(Path_To_Module) and DamageModule:Damage(Args)
- Roblox Model: Damage Module & Updated TagHumanoid
Source code:
local Module = {}
local Players = game:GetService("Players")
function Module:TagForDamage(Attacker: Damage_Dealer, Target, Damage: number)
local Humanoid = Target:FindFirstChildOfClass("Humanoid")
if not Humanoid or Humanoid.Health <= 0 then return end
local PlayerTags = Target:FindFirstChild("PlayerTags")
if not PlayerTags then
PlayerTags = Instance.new("Configuration")
PlayerTags.Name = "PlayerTags"
PlayerTags.Parent = Target
end
if Attacker:IsA("Player") then
local ExistingTag = PlayerTags:GetAttribute(Attacker.UserId)
if ExistingTag then
PlayerTags:SetAttribute(Attacker.UserId, ExistingTag + Damage)
else
PlayerTags:SetAttribute(Attacker.UserId, Damage)
end
if Damage >= Humanoid.Health then
PlayerTags:SetAttribute("LastHit", Attacker.UserId)
end
elseif typeof(Attacker) == "Instance" then
local ExistingTag
for i, v in ipairs(PlayerTags:GetChildren()) do
if v and v:IsA("ObjectValue") and v.Value == Attacker then
ExistingTag = v
break
end
end
if ExistingTag then
ExistingTag:SetAttribute("Damage", ExistingTag:GetAttribute("Damage") + Damage)
else
local Tag = Instance.new("ObjectValue")
Tag.Value = Attacker
Tag.Name = Attacker.Name
Tag.Parent = PlayerTags
Tag:SetAttribute("Damage", Damage)
end
if Damage >= Humanoid.Health then
local LastHit
for i, v in ipairs(PlayerTags:GetChildren()) do
if v and v:IsA("ObjectValue") and v.Name == "LastHit" and v.Value ~= Attacker then
v:Destroy()
v = nil
end
end
local Tag = Instance.new("ObjectValue")
Tag.Name = "LastHit"
Tag.Value = Attacker
Tag.Parent = PlayerTags
end
end
end
function Module:Damage(Attacker: Damage_Dealer, Target: Target, Damage, IgnoreDefense, IgnoreImmunity, AttackType, Cooldown)
if Attacker and Target and Target:FindFirstChildOfClass("Humanoid") and tonumber(Damage) ~= nil then
local TargetHum = Target:FindFirstChildOfClass("Humanoid")
if TargetHum.Health <= 0 then return end
local AttackerIsPlayer = false
local Character
if Attacker:IsA("Player") then
AttackerIsPlayer = true
Character = Attacker.Character
elseif Players:GetPlayerFromCharacter(Attacker) then
AttackerIsPlayer = true
Attacker = Players:GetPlayerFromCharacter(Attacker)
Character = Attacker.Character
end
if Cooldown then
if AttackerIsPlayer then
if Target:GetAttribute(Attacker.UserId) then return end
Target:SetAttribute(Attacker.UserId, true)
task.delay(Cooldown, function()
Target:SetAttribute(Attacker.UserId, nil)
end)
else
if Target:GetAttribute(Attacker.Name) then return end
Target:SetAttribute(Attacker.Name, true)
task.delay(Cooldown, function()
Target:SetAttribute(Attacker.Name, nil)
end)
end
end
if Character and Character:GetAttribute("Voided") then
Damage = Damage / 2
end
if not IgnoreDefense and Target:GetAttribute("Defense") then
Damage = Damage - (Damage * Target:GetAttribute("Defense") / 100)
end
if not IgnoreImmunity and AttackType and Target:GetAttribute(AttackType .. "Immunity") then
Damage = Damage - Damage * (Target:GetAttribute(AttackType .. "Immunity") / 100)
end
Damage = math.clamp(Damage, 0, TargetHum.Health)
Module:TagForDamage(Attacker, Target, Damage)
TargetHum:TakeDamage(Damage)
return Damage
end
end
return Module
- The function used in gears (Quite outdated)
-- Don't use this, the DamageModule is above if you somehow missed it
Module.TagHumanoid = function(Humanoid, Player)
for i, v in pairs(Humanoid:GetChildren()) do
if v:IsA("ObjectValue") and string.lower(v.Name) == "creator" then
v:Destroy()
end
end
local Creator_Tag = Instance.new("ObjectValue")
Creator_Tag.Name = "creator"
Creator_Tag.Value = Player
Creator_Tag.Parent = Humanoid
end
Examples (These are also in the model I provided above)
Here are some examples to get you understand what this can be used for.
- 1: Reward all players who dealt damage equal to at least 15% of the target’s MaxHealth
local MinimumDamagePercentageForReward = 0.15
local Rewards = {
Coins = 100, BadgeID = 0
}
local Character = script.Parent
local Humanoid = Character:FindFirstChild("Humanoid")
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
local PlayerData = require(ServerScriptService.PlayerData.Manager) -- ProfileService datastore
local FunctionBank = require(Your_Badge_Rewarding_Function)
if Character:FindFirstChild("PlayerTags") then
local PlayerTags = Character:FindFirstChild("PlayerTags")
for UserId, Damage: number in PlayerTags:GetAttributes() do
UserId = tonumber(UserId)
if UserId then
local Player = Players:GetPlayerByUserId(UserId)
if not Player then continue end
local Percent = Damage / Humanoid.MaxHealth
if Percent >= MinimumDamagePercentageForReward then
local RewardMulti = Percent / 2
if RewardMulti > 0.5 then
RewardMulti = 0.5
end
local profile = PlayerData.Profiles[Player]
if profile then
PlayerData.AdjustStat(Player, profile, "Kills", 1)
PlayerData.AdjustStat(Player, profile, "Coins", math.round(Rewards.Coins * RewardMulti))
end
if Rewards.BadgeID then
FunctionBank.AwardBadge(Player, Rewards.BadgeID)
end
end
end
end
end
- 2: Touched damage in a Roblox sword
local DamageModule = require(game:GetService("ReplicatedStorage"):FindFirstChild("DamageModule"))
function Blow(Part)
Part.Touched:connect(function(Hit)
if not Hit or not Hit.Parent or not Equipped or not Character or not Character.Parent then
return
end
local character = Hit.Parent
local humanoid = character:FindFirstChild("Humanoid")
if character ~= Character and humanoid and humanoid.Health > 0 then
if not humanoid then return end
if humanoid.Parent == Tool then return end
if game:GetService("Players"):GetPlayerFromCharacter(Hit.Parent) then return end
DamageModule:Damage(Player, Hit.Parent, (Active and Damage.Slash) or Damage.Touch)
end
end)
end
- 3: Dealing damage from a script in a projectile or a minion created by a player
local DamageModule = require(game:GetService("ReplicatedStorage"):FindFirstChild("DamageModule"))
DamageModule:Damage(script:FindFirstChild("creator").Value, TempChar, Damage)
Features
- LastHit (The player/NPC who dealt the final hit)
- Damage contribution (You can reward players based on the damage dealt percentage)
- Defense/Immunity (Set Attributes to use this)
- Cooldown (Time to wait before the same player/NPC can attack again)