• 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!

TFS 1.X+ Solo player unable to get credit for kill tasks.

deadsmelly

New Member
Joined
Apr 5, 2009
Messages
8
Reaction score
4
Hello all, I'm running a TFS 1.3 10.98 server.

I was trying to figure out how to get onkill task credit to players in a party and found this thread where Sarah Wesker solved the problem.

Using Sarah Wesker's script I was able to configure party task credit sharing for my killing in the name of kill.lua script, but now I have the opposite problem. I can't seem to get credit if a player is not in a party, or if they are in a party and multiple players don't attack. Sarah says in her post that it should work for solo players or party so I know I am doing something wrong but I just can't figure out what it is.

Any help would be greatly appreciated. I am a lua noob and have been learning as I go.


Lua:
local function getKillers(creature, party)
    local killers = {}
    local timeNow = os.mtime()
    local inFightTicks = configManager.getNumber(configKeys.PZ_LOCKED)
    for uid, cb in pairs(creature:getDamageMap()) do
        local attacker = Player(uid)
        if (attacker and attacker ~= creature and timeNow - cb.ticks <= inFightTicks) then
            local p = attacker:getParty()
            if p and p == party then
                killers[#killers +1] = attacker
            end
        end
    end
    return killers
end

function onKill(player, target)
    if target:isPlayer() or target:getMaster() then
        return true
    end

   
    local targetName, startedTasks, taskId = target:getName():lower(), player:getStartedTasks()
    for i = 1, #startedTasks do
        taskId = startedTasks[i]
        if isInArray(tasks[taskId].creatures, targetName) then
        local killers = getKillers(target, player:getParty())
        for k, member in pairs(killers) do
            local killAmount = member:getStorageValue(KILLSSTORAGE_BASE + taskId)
            if killAmount < tasks[taskId].killsRequired then
                -- Set max kills to adm
                --if (player:getAccountType() >= ACCOUNT_TYPE_GOD) then
                    --player:setStorageValue(KILLSSTORAGE_BASE + taskId, tasks[taskId].killsRequired)
                    --return true
                --end

                member:setStorageValue(KILLSSTORAGE_BASE + taskId, killAmount + 1)
                if member:getStorageValue(KILLSSTORAGE_BASE + taskId, killAmount) == tasks[taskId].killsRequired then -- if player has completed kill amount
                    member:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have culled the required amount of" .. targetName .. ". Report back to Grizzly Adams.")
                elseif member:getStorageValue(KILLSSTORAGE_BASE + taskId, killAmount) < tasks[taskId].killsRequired then -- is on quest, killed a creature, report how many
                        member:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have killed " .. member:getStorageValue(KILLSSTORAGE_BASE + taskId, killAmount) .. " out of " .. tasks[taskId].killsRequired .. " " .. targetName .. ".")
                end
            end
        end
    end

    if(isMonster(target)) then
        local killAmount = player:getStorageValue(Storage.KillingInTheNameOf.LugriNecromancerCount)
        if(string.lower(getCreatureName(target)) == "necromancer") and killAmount < 4000 and player:getStorageValue(Storage.KillingInTheNameOf.LugriNecromancers) == 1 then
            player:setStorageValue(Storage.KillingInTheNameOf.LugriNecromancerCount, killAmount + 1)

        elseif(string.lower(getCreatureName(target)) == "priestess") and killAmount < 4000 and player:getStorageValue(Storage.KillingInTheNameOf.LugriNecromancers) == 1 then
            player:setStorageValue(Storage.KillingInTheNameOf.LugriNecromancerCount, killAmount + 1)

        elseif(string.lower(getCreatureName(target)) == "blood priest") and killAmount < 4000 and player:getStorageValue(Storage.KillingInTheNameOf.LugriNecromancers) == 1 then
            player:setStorageValue(Storage.KillingInTheNameOf.LugriNecromancerCount, killAmount + 1)

        elseif(string.lower(getCreatureName(target)) == "blood hand") and killAmount < 4000 and player:getStorageValue(Storage.KillingInTheNameOf.LugriNecromancers) == 1 then
            player:setStorageValue(Storage.KillingInTheNameOf.LugriNecromancerCount, killAmount + 1)

        elseif(string.lower(getCreatureName(target)) == "shadow pupil") and killAmount < 4000 and player:getStorageValue(Storage.KillingInTheNameOf.LugriNecromancers) == 1 then
            player:setStorageValue(Storage.KillingInTheNameOf.LugriNecromancerCount, killAmount + 1)
        end
    end
    return true
end
end
 
It isn't perfect, but I got it working well enough. It sometimes gives double credit to a party member and I haven't worked out how to fix it, but it works for solo or party.


Lua:
local function getKillers(creature, player)
    local killers = {}
    local timeNow = os.mtime()
    local inFightTicks = configManager.getNumber(configKeys.PZ_LOCKED)
    for uid, cb in pairs(creature:getDamageMap()) do
        local attacker = Player(uid)
        if (attacker and attacker ~= creature and timeNow - cb.ticks <= inFightTicks) then
            killers[#killers +1] = attacker       
        end
    end
    return killers
end

function onKill(player, target)
    if target:isPlayer() or target:getMaster() then
        return true
    end

    local targetName, startedTasks, taskId = target:getName():lower(), player:getStartedTasks()
    for i = 1, #startedTasks do
        taskId = startedTasks[i]
        if isInArray(tasks[taskId].creatures, targetName) then
            local killers = getKillers(target)
    
            for k, member in pairs(killers) do
                    local killAmount = player:getStorageValue(KILLSSTORAGE_BASE + taskId)
            if killAmount < tasks[taskId].killsRequired then
                -- Set max kills to adm
                --if (player:getAccountType() >= ACCOUNT_TYPE_GOD) then
                    --player:setStorageValue(KILLSSTORAGE_BASE + taskId, tasks[taskId].killsRequired)
                    --return true
                --end

                player:setStorageValue(KILLSSTORAGE_BASE + taskId, killAmount + 1)
            end
            end
        end
    end

That is the relevant top portion, left the necromancer kill portion below it alone.
 
It isn't perfect, but I got it working well enough. It sometimes gives double credit to a party member and I haven't worked out how to fix it, but it works for solo or party.


Lua:
local function getKillers(creature, player)
    local killers = {}
    local timeNow = os.mtime()
    local inFightTicks = configManager.getNumber(configKeys.PZ_LOCKED)
    for uid, cb in pairs(creature:getDamageMap()) do
        local attacker = Player(uid)
        if (attacker and attacker ~= creature and timeNow - cb.ticks <= inFightTicks) then
            killers[#killers +1] = attacker       
        end
    end
    return killers
end

function onKill(player, target)
    if target:isPlayer() or target:getMaster() then
        return true
    end

    local targetName, startedTasks, taskId = target:getName():lower(), player:getStartedTasks()
    for i = 1, #startedTasks do
        taskId = startedTasks[i]
        if isInArray(tasks[taskId].creatures, targetName) then
            local killers = getKillers(target)
    
            for k, member in pairs(killers) do
                    local killAmount = player:getStorageValue(KILLSSTORAGE_BASE + taskId)
            if killAmount < tasks[taskId].killsRequired then
                -- Set max kills to adm
                --if (player:getAccountType() >= ACCOUNT_TYPE_GOD) then
                    --player:setStorageValue(KILLSSTORAGE_BASE + taskId, tasks[taskId].killsRequired)
                    --return true
                --end

                player:setStorageValue(KILLSSTORAGE_BASE + taskId, killAmount + 1)
            end
            end
        end
    end

That is the relevant top portion, left the necromancer kill portion below it alone.
Thanks for sharing the solution bro =)
 
It isn't perfect, but I got it working well enough. It sometimes gives double credit to a party member and I haven't worked out how to fix it, but it works for solo or party.


Lua:
local function getKillers(creature, player)
    local killers = {}
    local timeNow = os.mtime()
    local inFightTicks = configManager.getNumber(configKeys.PZ_LOCKED)
    for uid, cb in pairs(creature:getDamageMap()) do
        local attacker = Player(uid)
        if (attacker and attacker ~= creature and timeNow - cb.ticks <= inFightTicks) then
            killers[#killers +1] = attacker      
        end
    end
    return killers
end

function onKill(player, target)
    if target:isPlayer() or target:getMaster() then
        return true
    end

    local targetName, startedTasks, taskId = target:getName():lower(), player:getStartedTasks()
    for i = 1, #startedTasks do
        taskId = startedTasks[i]
        if isInArray(tasks[taskId].creatures, targetName) then
            local killers = getKillers(target)
   
            for k, member in pairs(killers) do
                    local killAmount = player:getStorageValue(KILLSSTORAGE_BASE + taskId)
            if killAmount < tasks[taskId].killsRequired then
                -- Set max kills to adm
                --if (player:getAccountType() >= ACCOUNT_TYPE_GOD) then
                    --player:setStorageValue(KILLSSTORAGE_BASE + taskId, tasks[taskId].killsRequired)
                    --return true
                --end

                player:setStorageValue(KILLSSTORAGE_BASE + taskId, killAmount + 1)
            end
            end
        end
    end

That is the relevant top portion, left the necromancer kill portion below it alone.
Thanks for sharing the solution bro =)


Quick and dirty fix.

Basically we loop through the damage map of the creature like you were already doing to find all players who damaged the creature..
Then we loop through all of those players and add them to a list of players who have been 'checked'.

By checking if the player has been credited for that kill previously, we get no 'overlap', and no possibility of a player getting credited for a kill twice.

This way, even though onKill can get triggered multiple times, everyone only get's credit for the kill once.

So tldr;
I added a table to verify if a player received credit for a kill, and fixed some of the weird logic in your code to ensure the person who dealt the killing blow won't get double credit.

Lua:
local creatures = {}

local function resetCreatures(targetId)
    creatures[targetId] = nil
end

local function getKillers(creature, player)
    local killers = {}
    local timeNow = os.mtime()
    local inFightTicks = configManager.getNumber(configKeys.PZ_LOCKED)
    for uid, cb in pairs(creature:getDamageMap()) do
        local attacker = Player(uid)
        if (attacker and attacker ~= creature and timeNow - cb.ticks <= inFightTicks) then
            killers[#killers +1] = attacker       
        end
    end
    return killers
end

function onKill(player, target)
    if target:isPlayer() or target:getMaster() then
        return true
    end
    
    -- locate all damage dealers
    local killers = getKillers(target)
    
    -- add creature to table if they aren't there.
    local targetId = target:getId()
    if not creatures[targetId] then
        creatures[targetId] = {}
        addEvent(resetCreatures, 1000, targetId) -- clear table entry, to avoid a memory leak
    end
    
    -- loop through all damage dealers
    for k, member in pairs(killers) do
        local killer = Player(member)
        if member then
            killerId = killer:getId()
            if not table.contains(creatures[targetId], killerId) then -- check if player has been credited for this kill yet
                table.insert(creatures[targetId], killerId) -- player has not been credited for this kill yet, so add to table.
                
                -- check for tasks for this player
                local targetName, startedTasks, taskId = target:getName():lower(), killer:getStartedTasks()
                for i = 1, #startedTasks do
                    taskId = startedTasks[i]
                    if isInArray(tasks[taskId].creatures, targetName) then
                        local killAmount = killer:getStorageValue(KILLSSTORAGE_BASE + taskId)
                        if killAmount < tasks[taskId].killsRequired then
                            -- Set max kills to adm
                            --if (player:getAccountType() >= ACCOUNT_TYPE_GOD) then
                                --player:setStorageValue(KILLSSTORAGE_BASE + taskId, tasks[taskId].killsRequired)
                                --return true
                            --end
                
                            killer:setStorageValue(KILLSSTORAGE_BASE + taskId, killAmount + 1)
                        end
                    end
                end
            end
        end
    end
end
 
I just added the kill messages back
Lua:
local creatures = {}

local function resetCreatures(targetId)
    creatures[targetId] = nil
end

local function getKillers(creature, player)
    local killers = {}
    local timeNow = os.mtime()
    local inFightTicks = configManager.getNumber(configKeys.PZ_LOCKED)
    for uid, cb in pairs(creature:getDamageMap()) do
        local attacker = Player(uid)
        if (attacker and attacker ~= creature and timeNow - cb.ticks <= inFightTicks) then
            killers[#killers +1] = attacker       
        end
    end
    return killers
end

function onKill(player, target)
    if target:isPlayer() or target:getMaster() then
        return true
    end
    
    -- locate all damage dealers
    local killers = getKillers(target)
    
    -- add creature to table if they aren't there.
    local targetId = target:getId()
    if not creatures[targetId] then
        creatures[targetId] = {}
        addEvent(resetCreatures, 1000, targetId) -- clear table entry, to avoid a memory leak
    end
    
    -- loop through all damage dealers
    for k, member in pairs(killers) do
        local killer = Player(member)
        if member then
            killerId = killer:getId()
            if not table.contains(creatures[targetId], killerId) then -- check if player has been credited for this kill yet
                table.insert(creatures[targetId], killerId) -- player has not been credited for this kill yet, so add to table.
                
                -- check for tasks for this player
                local targetName, startedTasks, taskId = target:getName():lower(), killer:getStartedTasks()
                for i = 1, #startedTasks do
                    taskId = startedTasks[i]
                    if isInArray(tasks[taskId].creatures, targetName) then
                        local killAmount = killer:getStorageValue(KILLSSTORAGE_BASE + taskId)
                        if killAmount < tasks[taskId].killsRequired then
                            -- Set max kills to adm
                            --if (player:getAccountType() >= ACCOUNT_TYPE_GOD) then
                                --player:setStorageValue(KILLSSTORAGE_BASE + taskId, tasks[taskId].killsRequired)
                                --return true
                            --end

                            killer:setStorageValue(KILLSSTORAGE_BASE + taskId, killAmount + 1)
                            if killer:getStorageValue(KILLSSTORAGE_BASE + taskId, killAmount) == tasks[taskId].killsRequired then -- if player has completed kill amount
                            killer:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Task: You have culled the required amount of" .. targetName .. ". Report back to Grizzly Adams.")
                            elseif killer:getStorageValue(KILLSSTORAGE_BASE + taskId, killAmount) < tasks[taskId].killsRequired then -- is on quest, killed a creature, report how many
                            killer:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, string.format("Task: You have slain [%i/%i] %ss.", killer:getStorageValue(KILLSSTORAGE_BASE + taskId, killAmount), tasks[taskId].killsRequired, target:getName()))
                            end
                        end
                    end
                end
            end
        end
    end
end
 
Back
Top