• 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.4.2] Boss Reward Bag - onDeath (with DamageMap)

Unknown Soldier

Mapping a map
Joined
Oct 30, 2010
Messages
297
Solutions
11
Reaction score
670
Hello everyone,

I want to share a script that gives reward bag to all the players participating in fight against boss. The reward will be sent automatically as soon as boss dies, to players' inventory, or when no cap or space, will be sent to players' mailbox.

It features:
  • any player who has dealt damage to the boss (DamageMap) and was within a certain distance of the fallen boss at the time of death (local allowedDistance) will receive a reward
  • if players' inventory is full, or player has no capacity, the reward will be sent to depot mailbox
  • a reward can be obtained once for a certain time (delayTimer), killing the boss before that time passes will result in no loot for the impatient player
rewardBag.gif

rewardBag3.gif
*On gif above allowedDistance = 3
Installation:
  • register the event in given monster file
    XML:
        <script>
                <event name="OrshabaalDeath"/>
        </script>
  • set boss corpse to 0, otherwise you will get additional loot message
    XML:
    <look type="201" corpse="0" />
  • put the script anywhere in data/scripts folder
Lua:
local creatureName = "orshabaal"
local timeCounterStorage = 66666
local delayTimer = 30 --in seconds
local bagId = 1993
local mailboxPos = Position(1993, 1328, 7) --mailbox at this coordinates is required to send the reward, when no space in inventory or no cap
local allowedDistance = 20 --max distance between boss and player, above this value no reward will be given to the remote player
local loot = {
    {item = 2160, count = 5, chance = 100000}, --crystal coin
    {item = 2494, count = 1, chance = 50000}, --demon armor
    {item = 18408, count = 1, chance = 15000}, --prismatic ring
    {item = 18407, charges = 750, count = 1, chance = 15000} --prismatic necklace
}

local creatureevent = CreatureEvent("OrshabaalDeath")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
    if getCreatureName(creature):lower() == creatureName then
        local creaturePos = getCreaturePosition(creature)
        for i, damage in pairs(creature:getDamageMap()) do
            local p = Player(i)
            local ppos = p:getPosition()
            if p then
                if ppos:getDistance(creaturePos) <= allowedDistance then
                    sendReward(p)
                else
                    p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
                end
            end
        end
    end
end

creatureevent:register()

function sendReward(player)
    local currentTime, currentStorage = os.time(), player:getStorageValue(timeCounterStorage)
    if currentStorage > currentTime then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can obtain the reward again in " .. os.date("!%H hours %M minutes and %S seconds", currentStorage - currentTime) .. ".")
        return false
    end

    local message = "You found reward bag containing: "
    local bag = Game.createItem(bagId, 1)
    for i = 1, #loot do
        local rand = math.random(100000)
        if rand <= loot[i].chance then
            local randomcount = math.random(loot[i].count)
            if loot[i].count >= 2 and not loot[i].charges then
                item = Game.createItem(loot[i].item, randomcount)
                bag:addItemEx(item)
            elseif loot[i].charges then
                item = Game.createItem(loot[i].item, loot[i].charges)
                bag:addItemEx(item)
            else
                item = Game.createItem(loot[i].item, loot[i].count)
                bag:addItemEx(item)
            end

            if message ~= "You found reward bag containing: " then
                message = message .. ", "
            end

                message = message .. randomcount .. " " .. ItemType(loot[i].item):getName()
        end
    end

    if message == "You found reward bag containing: " then
        message = message .. "nothing"
    end

    if player:addItemEx(bag) ~= RETURNVALUE_NOERROR then
        local mailboxCheck = player:getInbox()
        if not mailboxCheck then
            print("Mailbox not found!")
            return false
        else
            --print("Mailbox is fine.")
        end
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        message = message .. ".\n\nIt was sent to you mailbox due to lack of space/capacity"
        local parcel = Game.createItem(ITEM_PARCEL, 1)
        local label = Game.createItem(2599, 1)
        local mailbox = Tile(mailboxPos)
        doSetItemText(label.uid, player:getName())
        parcel:addItemEx(bag)
        parcel:addItemEx(label)
        mailbox:addItemEx(parcel)
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a parcel with reward to your mailbox!")
    end
    currentTime = currentTime + delayTimer
    player:setStorageValue(timeCounterStorage, currentTime)
    player:sendTextMessage(MESSAGE_LOOT, message .. ".")
    return true
end
 
Last edited:
I tested it and almost everything is working, but when it sends it to the depot, it literally leaves it on top of the depot, not inside it.
 
You should use the getInbox() method instead
Lua:
local inbox = player:getInbox()
inbox:addItemEx(bag)
 
I tested it and almost everything is working, but when it sends it to the depot, it literally leaves it on top of the depot, not inside it.
Strange...

Well, try placing the parcel directly into the players' inbox, like @Fjorda said, or just make sure, that the mailboxPos has the exact coordinates of mailbox on the map (Item id 2593 for example). For me, adding the bag directly info the mailbox didn't work, and I wish it would, because now player has to open the parcel first, that looks quite poor.

I've made some changes to the script, few small bug fixes.
 
Last edited:
I'm using tfs 1.5 and in the mailbox.cpp function it needs the name and city name, would it be possible to make this modification?

Could you also add the function to choose to go directly to the dp or bag?
 
I'm using tfs 1.5 and in the mailbox.cpp function it needs the name and city name, would it be possible to make this modification?

Could you also add the function to choose to go directly to the dp or bag?
Try changing:

Lua:
doSetItemText(label.uid, player:getName())

To

Lua:
doSetItemText(label.uid, player:getName() .. "\n" .. player:getTown())

Or
Lua:
doSetItemText(label.uid, player:getName() .. "\nThais")

And no, I won't do version without a parcel as it cannot be done in lua in TFS 1.4.2, as far as I know.
 
Try changing:

Lua:
doSetItemText(label.uid, player:getName())

To

Lua:
doSetItemText(label.uid, player:getName() .. "\n" .. player:getTown())

Or
Lua:
doSetItemText(label.uid, player:getName() .. "\nThais")

And no, I won't do version without a parcel as it cannot be done in lua in TFS 1.4.2, as far as I know.

I think I expressed myself wrong, it's with the parcel itself, but putting the option so that when you kill you go straight to the depot, or go to the bag, understand?
 
I think I expressed myself wrong, it's with the parcel itself, but putting the option so that when you kill you go straight to the depot, or go to the bag, understand?
Well, okay, then maybe

Lua:
local depot = player:getDepotChest(depot id)
depot:addItemEx(bag)

Cannot test it now though, seems too simple
 
Where do I exchange it for this?
Change whole code from line:
Lua:
if player:addItemEx(bag) ~= RETURNVALUE_NOERROR then
till the end of the script, or just paste whole script:

Lua:
local creatureName = "orshabaal"
local timeCounterStorage = 66666
local delayTimer = 30 --in seconds
local bagId = 1993
local mailboxPos = Position(1993, 1328, 7) --mailbox at this coordinates is required to send the reward, when no space in inventory or no cap
local allowedDistance = 20 --max distance between boss and player, above this value no reward will be given to the remote player
local loot = {
    {item = 2160, count = 5, chance = 100000}, --crystal coin
    {item = 2494, count = 1, chance = 50000}, --demon armor
    {item = 18408, count = 1, chance = 15000}, --prismatic ring
    {item = 18407, charges = 750, count = 1, chance = 15000} --prismatic necklace
}

local creatureevent = CreatureEvent("OrshabaalDeath")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
    if getCreatureName(creature):lower() == creatureName then
        local creaturePos = getCreaturePosition(creature)
        for i, damage in pairs(creature:getDamageMap()) do
            local p = Player(i)
            local ppos = p:getPosition()
            if p then
                if ppos:getDistance(creaturePos) <= allowedDistance then
                    sendReward(p)
                else
                    p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
                end
            end
        end
    end
end

creatureevent:register()

function sendReward(player)
    local currentTime, currentStorage = os.time(), player:getStorageValue(timeCounterStorage)
    if currentStorage > currentTime then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can obtain the reward again in " .. os.date("!%H hours %M minutes and %S seconds", currentStorage - currentTime) .. ".")
        return false
    end

    local message = "You found reward bag containing: "
    local bag = Game.createItem(bagId, 1)
    for i = 1, #loot do
        local rand = math.random(100000)
        if rand <= loot[i].chance then
            local randomcount = math.random(loot[i].count)
            if loot[i].count >= 2 and not loot[i].charges then
                item = Game.createItem(loot[i].item, randomcount)
                bag:addItemEx(item)
            elseif loot[i].charges then
                item = Game.createItem(loot[i].item, loot[i].charges)
                bag:addItemEx(item)
            else
                item = Game.createItem(loot[i].item, loot[i].count)
                bag:addItemEx(item)
            end

            if message ~= "You found reward bag containing: " then
                message = message .. ", "
            end

                message = message .. randomcount .. " " .. ItemType(loot[i].item):getName()
        end
    end

    if message == "You found reward bag containing: " then
        message = message .. "nothing"
    end

    if player:addItemEx(bag) ~= RETURNVALUE_NOERROR then
        local depot = player:getDepotChest(1) --pick your depot ID
        if not depot then
            print("Depot with such ID not found!")
            return false
        else
            --print("Depot is fine.")
        end
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        message = message .. ".\n\nIt was sent to your depot due to lack of space/capacity"
        depot:addItemEx(bag)
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a bag with reward to your depot chest!")
    end
    currentTime = currentTime + delayTimer
    player:setStorageValue(timeCounterStorage, currentTime)
    player:sendTextMessage(MESSAGE_LOOT, message .. ".")
    return true
end
 
Thank you, it worked perfectly. Could you make one last change please?I would like the script to configure the depot id to send the bag, as I want to use the system to send it to depots in different cities
 
Thank you, it worked perfectly. Could you make one last change please?I would like the script to configure the depot id to send the bag, as I want to use the system to send it to depots in different cities
The only solution i can think of for now is creating a talkaction, so the player can decide which town should be the town where the rewards will be sent.

Lua:
rewardTownStorage = 66667

function onSay(player, words, param)
    if param == "" then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Usage: /setrewardtown <town>")
        return false
    end
   
    local townId = getTownIdByName(param)
    if townId == nil then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Unknown town.")
        return false
    end
   
    player:setStorageValue(rewardTownStorage, townId)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Reward town set to: " .. param)
    print("Reward town ID set to: " .. player:getStorageValue(rewardTownStorage))
    return false
end

function getTownIdByName(townName)
    local towns = {
        ["thais"] = 1,
        ["carlin"] = 2,
        -- Add more towns as needed
    }
    townName = townName:lower()
    local townId = towns[townName]
    -- print("Town ID for " .. townName .. " is: " .. tostring(townId))
    return townId
end

function getPlayerRewardTown(player)
    return player:getStorageValue(rewardTownStorage)
end

XML:
 <talkaction words="/setrewardtown" separator=" " script="rewardtownid.lua" />

and modify the main script, change:
Lua:
local depot = player:getDepotChest(1) --pick your depot ID
to
Lua:
local depot = player:getDepotChest(player:getStorageValue(rewardTownStorage)) --pick your depot ID

And note, that by default players will not have that storage value, it will be nil, or -1, so in case they never used the talkaction, and decided to kill a boss, and if they have no cap/space, they will not get the reward at all. So you want to adress that, and set the default value, for example to 1.

data/creaturescripts/scripts/login.lua
inside the function add:
Lua:
local getRewardTownStorage = player:getStorageValue(rewardTownStorage)
if getRewardTownStorage == nil or getRewardTownStorage == -1 then
    player:setStorageValue(rewardTownStorage, 1)
end

Should work fine

@edit

I have a question to some experienced people.

Will the adding of these lines:
Lua:
        if label then
            label:remove()
        end
right after the parcel is sent and delivered:
Lua:
mailbox:addItemEx(parcel)
hurt in any way? I mean... I don't want to try to access the item that doesn't exist anymore and thus cause a crash or some other bad things. The label does not disappear after sending the parcel obviously, but I wonder if it is still within the >safe< reach of that script and if I should even think about touching it?
 
Last edited:
I understand, in this case the other way, sending the parcel via mailbox, would be a more viable option, as there I can enter the name of the city where it will be sent. Could you send it directly to depot without going to bag in mailbox mode?

Thanks for the answers!
 
Last edited:
I understand, in this case the other way, sending the parcel via mailbox, would be a more viable option, as there I can enter the name of the city where it will be sent. Could you send it directly to depot without going to bag in mailbox mode?

Thanks for the answers!
Wait. I don't get it. You wanted to have the possibility to choose the town, where the reward will go, so I made the talkaction that will allow it. How else you wanted to achieve it? Do you understand the script at all?

If in game you type /setrewardtown thais your rewards will be sent to Thais depot, if you type /setrewardtown carlin then they will go into Carlin's depot. If you forget about this talkaction, and never use /setrewardtown then your rewards will be sent to Thais depot by default (or other city you specify, you just have to change numbers - depot ids). Besides, you should also change the cities and their depot chest IDs, depending on what you have on your server:
Lua:
--they are only examples
    local towns = {
        ["thais"] = 1,
        ["carlin"] = 2,
        -- Add more towns as needed
    }

If you want any other midifications, basically you have everything in scripts above, which is:
  • sending items to depot,
  • sending items to mailbox,
  • you can specify if you want the items to be sent in parcel or in bag,
  • I have even posted the option to remove label after the parcel is sent.
Just modify it, everything happens after the line if player:addItemEx(bag) ~= RETURNVALUE_NOERROR then.
 
I'm using this version


Lua:
local creatureName = "orshabaal"
local timeCounterStorage = 66666
local delayTimer = 30 --in seconds
local bagId = 1993
local mailboxPos = Position(956, 1213, 7) --mailbox at this coordinates is required to send the reward, when no space in inventory or no cap
local allowedDistance = 20 --max distance between boss and player, above this value no reward will be given to the remote player
local loot = {
    {item = 2160, count = 5, chance = 100000}, --crystal coin
    {item = 2494, count = 1, chance = 50000}, --demon armor
    {item = 18408, count = 1, chance = 15000}, --prismatic ring
    {item = 18407, charges = 750, count = 1, chance = 15000} --prismatic necklace
}

local creatureevent = CreatureEvent("OrshabaalDeath")

function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
    if getCreatureName(creature):lower() == creatureName then
        local creaturePos = getCreaturePosition(creature)
        for i, damage in pairs(creature:getDamageMap()) do
            local p = Player(i)
            local ppos = p:getPosition()
            if p then
                if ppos:getDistance(creaturePos) <= allowedDistance then
                    sendReward(p)
                else
                    p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
                end
            end
        end
    end
end

creatureevent:register()

function sendReward(player)
    local currentTime, currentStorage = os.time(), player:getStorageValue(timeCounterStorage)
    if currentStorage > currentTime then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can obtain the reward again in " .. os.date("!%H hours %M minutes and %S seconds", currentStorage - currentTime) .. ".")
        return false
    end

    local message = "You found reward bag containing: "
    local bag = Game.createItem(bagId, 1)
    for i = 1, #loot do
        local rand = math.random(100000)
        if rand <= loot[i].chance then
            local randomcount = math.random(loot[i].count)
            if loot[i].count >= 2 and not loot[i].charges then
                item = Game.createItem(loot[i].item, randomcount)
                bag:addItemEx(item)
            elseif loot[i].charges then
                item = Game.createItem(loot[i].item, charges)
                bag:addItemEx(item)
            else
                item = Game.createItem(loot[i].item, loot[i].count)
                bag:addItemEx(item)
            end

            if message ~= "You found reward bag containing: " then
                message = message .. ", "
            end

                message = message .. randomcount .. " " .. ItemType(loot[i].item):getName()
        end
    end

    if message == "You found reward bag containing: " then
        message = message .. "nothing"
    end

    if RETURNVALUE_NOERROR then
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        message = message .. ".\n\nIt was sent to you mailbox due to lack of space/capacity"
        local parcel = Game.createItem(ITEM_PARCEL, 1)
        local label = Game.createItem(2599, 1)
        local mailbox = Tile(mailboxPos)

        if not mailbox then
            print("Mailbox not found!")
            return
        end
        doSetItemText(label.uid, player:getName() .. "\nAkravi")
        parcel:addItemEx(bag)
        parcel:addItemEx(label)
        mailbox:addItemEx(parcel)
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a parcel with reward to your mailbox!")
    else
        currentTime = currentTime + delayTimer
        player:setStorageValue(timeCounterStorage, currentTime)
    end
    player:sendTextMessage(MESSAGE_LOOT, message .. ".")
    return true
end


This error is showing me when I kill more than 2 people on the boss


Code:
Lua Script Error: [Scripts Interface]
C:\Users\gabri\Desktop\baiakthunder-master\data\scripts\rewardbagdp.lua:callback
...Desktop\baiakthunder-master\data\scripts\rewardbagdp.lua:21: attempt to index local 'p' (a nil value)
stack traceback:
        [C]: in function '__index'
        ...Desktop\baiakthunder-master\data\scripts\rewardbagdp.lua:21: in function <...Desktop\baiakthunder-master\data\scripts\rewardbagdp.lua:16>
 
change:
Lua:
local ppos = p:getPosition()
if p then
    if ppos:getDistance(creaturePos) <= allowedDistance then
        sendReward(p)
    else
        p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
    end
end
to:
Lua:
if p then
    local ppos = p:getPosition()
    if ppos:getDistance(creaturePos) <= allowedDistance then
        sendReward(p)
    else
        p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
    end
end
 
for some reason, if there are two players killing the boss, 1 is only getting the other's reward, in this case the two prizes are going to just 1 person
change:
Lua:
local ppos = p:getPosition()
if p then
    if ppos:getDistance(creaturePos) <= allowedDistance then
        sendReward(p)
    else
        p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
    end
end
to:
Lua:
if p then
    local ppos = p:getPosition()
    if ppos:getDistance(creaturePos) <= allowedDistance then
        sendReward(p)
    else
        p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
    end
end
 
Maybe try something like this:

Lua:
function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, unjustified, mostDamageUnjustified)
    if getCreatureName(creature):lower() == creatureName then
        local creaturePos = getCreaturePosition(creature)
        local processedPlayers = {}
        for i, damage in pairs(creature:getDamageMap()) do
            local p = Player(i)
            if p and not processedPlayers[p] then
                local ppos = p:getPosition()
                if ppos:getDistance(creaturePos) <= allowedDistance then
                    sendReward(p)
                else
                    p:sendTextMessage(MESSAGE_EVENT_ADVANCE,"The monster you fought has fallen, but you are too far away to claim your prize.")
                end
                processedPlayers[p] = true
            end
        end
    end
end
 
Back
Top