• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!
  • 2026 staff recruitment is open! Check it out and consider applying!

How to Retrieve All Players Involved in a Kill in TFS 1.x?

otadmin

New Member
Joined
Mar 13, 2019
Messages
1
Reaction score
0
Hi everyone, can someone help me understand this?

In TFS 0.4, the function onKill(cid, target, lastHit) returned the list of all participants involved in the kill. In TFS 1.x, it no longer does that? Considering it only has the function onKill(creature, target).

In the example script:
LUA:
function onKill(creature, target)
    if target:isPlayer() and creature:getLevel() > 250 and target:getLevel() > 250 then
        Game.broadcastMessage(
            "PvP Fight: " .. creature:getName() .. " (Level: " .. creature:getLevel() ..
            ") Killed " .. target:getName() .. " (Level: " .. target:getLevel() .. ").",
            MESSAGE_STATUS_WARNING
        )
    end

    return true
end

If 2, 3, or more players take part in the kill, the message only shows one player. Is it possible to return all players who participated in the kill on broadcast?

Thanks.
 
Hi everyone, can someone help me understand this?

In TFS 0.4, the function onKill(cid, target, lastHit) returned the list of all participants involved in the kill. In TFS 1.x, it no longer does that? Considering it only has the function onKill(creature, target).

In the example script:
LUA:
function onKill(creature, target)
    if target:isPlayer() and creature:getLevel() > 250 and target:getLevel() > 250 then
        Game.broadcastMessage(
            "PvP Fight: " .. creature:getName() .. " (Level: " .. creature:getLevel() ..
            ") Killed " .. target:getName() .. " (Level: " .. target:getLevel() .. ").",
            MESSAGE_STATUS_WARNING
        )
    end

    return true
end

If 2, 3, or more players take part in the kill, the message only shows one player. Is it possible to return all players who participated in the kill on broadcast?

Thanks.

OnKill in tfs 1.X, will trigger on last hitter and the most damage dealer.

To use onKill to track all player you need to make sure it only trigger once and use monster:getDamageMap() to get the players that has dealt damage to the monster.

And use that playerlist to give kill track. You must not use the player object from onKill, that will give double point to the mostDamageDealer and lasthitter. This will also happen if u do not make sure that onKill triggers once only.
 
Here is my announce boss kill script, feel free to use. Tested in production. Edit to your own liking and you probably want to remove the db logging.

LUA:
local announced = {}

function onKill(player, target, lastHit)
    if not target or not target:isMonster() then
        return true
    end

    local monsterType = target:getType()
    if not monsterType or not monsterType:isBoss() then
        return true
    end

    -- only run once per monster death
    local targetId = target:getId()
    if announced[targetId] then
        return true
    end
    announced[targetId] = true
    addEvent(function() announced[targetId] = nil end, 2000) -- cleanup

    -- collect unique player names
    local killersSet, killersList = {}, {}
    for attackerId, _ in pairs(target:getDamageMap()) do
        local attacker = Creature(attackerId)
        if attacker and attacker:isPlayer() then
            local name = attacker:getName()
            if not killersSet[name] then
                killersSet[name] = true
                table.insert(killersList, name)
            end
        end
    end

    local total = #killersList
    if total == 0 then
        return true
    end

    table.sort(killersList)

    local shownNames = {}
    for i = 1, math.min(3, total) do
        table.insert(shownNames, killersList[i])
    end

    local message
    if total == 1 then
        message = shownNames[1]
    elseif #shownNames == 2 then
        message = shownNames[1] .. " and " .. shownNames[2]
    else
        message = table.concat(shownNames, ", ", 1, #shownNames - 1) .. " and " .. shownNames[#shownNames]
    end
    if total > 3 then
        message = message .. " and " .. (total - 3) .. " others"
    end

    broadcastMessage(target:getName() .. " has been killed by " .. message .. ".", MESSAGE_STATUS_WARNING)

    -- DB logging
    local killTime = os.time()
    local monsterName = db.escapeString(target:getName())
    for _, killerName in ipairs(killersList) do
        db.asyncQuery(
            "INSERT INTO `player_bosskills` (`monster_name`, `killed_at`, `player_name`) " ..
            "VALUES (" .. monsterName .. ", " .. killTime .. ", " .. db.escapeString(killerName) .. ")"
        )
    end

    return true
end
 
Here is my announce boss kill script, feel free to use. Tested in production. Edit to your own liking and you probably want to remove the db logging.

LUA:
local announced = {}

function onKill(player, target, lastHit)
    if not target or not target:isMonster() then
        return true
    end

    local monsterType = target:getType()
    if not monsterType or not monsterType:isBoss() then
        return true
    end

    -- only run once per monster death
    local targetId = target:getId()
    if announced[targetId] then
        return true
    end
    announced[targetId] = true
    addEvent(function() announced[targetId] = nil end, 2000) -- cleanup

    -- collect unique player names
    local killersSet, killersList = {}, {}
    for attackerId, _ in pairs(target:getDamageMap()) do
        local attacker = Creature(attackerId)
        if attacker and attacker:isPlayer() then
            local name = attacker:getName()
            if not killersSet[name] then
                killersSet[name] = true
                table.insert(killersList, name)
            end
        end
    end

    local total = #killersList
    if total == 0 then
        return true
    end

    table.sort(killersList)

    local shownNames = {}
    for i = 1, math.min(3, total) do
        table.insert(shownNames, killersList[i])
    end

    local message
    if total == 1 then
        message = shownNames[1]
    elseif #shownNames == 2 then
        message = shownNames[1] .. " and " .. shownNames[2]
    else
        message = table.concat(shownNames, ", ", 1, #shownNames - 1) .. " and " .. shownNames[#shownNames]
    end
    if total > 3 then
        message = message .. " and " .. (total - 3) .. " others"
    end

    broadcastMessage(target:getName() .. " has been killed by " .. message .. ".", MESSAGE_STATUS_WARNING)

    -- DB logging
    local killTime = os.time()
    local monsterName = db.escapeString(target:getName())
    for _, killerName in ipairs(killersList) do
        db.asyncQuery(
            "INSERT INTO `player_bosskills` (`monster_name`, `killed_at`, `player_name`) " ..
            "VALUES (" .. monsterName .. ", " .. killTime .. ", " .. db.escapeString(killerName) .. ")"
        )
    end

    return true
end
Why do you have to paste a whole script if one function call is the answer?
This support board struggles with cargo cult programming and the inability to break down tasks into smaller components.

@otadmin
creature:getDamageMap() will return a map you can use similarly to 0.4's killers list.
Also, ensure that your event is executed only once. If I recall correctly, at least on some TFS's, onKill will be executed for every killer.
 
Why do you have to paste a whole script if one function call is the answer?
This support board struggles with cargo cult programming and the inability to break down tasks into smaller components.

@otadmin
creature:getDamageMap() will return a map you can use similarly to 0.4's killers list.
Also, ensure that your event is executed only once. If I recall correctly, at least on some TFS's, onKill will be executed for every killer.
Because it’s easier to remove code from an already working script than to remake it from scratch. If the op can’t figure out how to make it work then that’s his problem. Either way. Go back to the giga dev cave.
 
Because it’s easier to remove code from an already working script than to remake it from scratch. If the op can’t figure out how to make it work then that’s his problem. Either way. Go back to the giga dev cave.
No one asked you for a script, tho. OP asked how he gets access to the list of players who dealt damage.
If you are too busy to post something thought out, maybe just wait till you have time?
 
Last edited:
No one asked you for a script, tho. OP asked how he gets access to the list of players who dealt damage.
If you are too busy to post something thought out, maybe just wait till you have time?
The OP literally posted his own version of the same script I shared. With a few small edits he can drop it straight into his server.

There's no prize for being intentionally vague. If someone asks how to use getDamageMap(), the most useful answer is a complete implementation.

If your goal is to gatekeep information in the name of "training" people, that's your prerogative. I don't care. I can give you a thousand better ways to do that.
 
The OP literally posted his own version of the same script I shared. With a few small edits he can drop it straight into his server.

There's no prize for being intentionally vague. If someone asks how to use getDamageMap(), the most useful answer is a complete implementation.

If your goal is to gatekeep information in the name of "training" people, that's your prerogative. I don't care. I can give you a thousand better ways to do that.
Seems like you are confused, as gatekeeping is the least of my intentions.

The script is trivial for you, because you already understand the context, but anyone else has to either read it or copy paste it.
First requires focus and attention, which is very scarce these days, later, even if this time successful, will lead you on to having a bad time, and this is why I nitpick on it.

While, in this case, if you isolate the method out, it’s effortless to understand.
I am not saying an example of usage is a bad thing, but yours was polluted with sorting, logging and whatnot - I am sure we both agree as you pointed it out yourself.

This little effort makes the difference.
 
I don't doubt in the ability of @otadmin, he is a conusor of open tibia programming after all, and he will accomplish his task with utmost certainty, with or without my code. Including removing the unneeded parts.
 
@otadmin consider the case where player's summon is the killer, rather than the player itself. You might want to add such check to your if branch.
Yup, check for master and if it exists, fall back to it, then check if it’s a player.
Interestingly enough, some summons might (very briefly) exist without their master, so that nil check is not always reliable, but this shouldn’t be an issue in this case.
 
Back
Top