• 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.2 Kill monster, remove stone

henkas

Well-Known Member
Joined
Jul 8, 2015
Messages
1,051
Solutions
5
Reaction score
62
Hello,
so i found only one thread about this question and there is no answer for it. So basically when you kill monster it removes stone which should be based on stone actionid or uniqueid, because if you want to remove same id stones with order. So for example this is how i imagine local
LUA:
local config = {
    {monsters = {"Rat Boss"}, stoneRestIn = 2(hours), StoneUniqueID = 1111},
    {monsters = {"Rat Boss Second"}, stoneRestIn = 2(hours), StoneUniqueID = 1112},
}
But i dont know if its even possible to use actionid uniqueid outside "action", basically i have no idea :D
 
Solution
Hello,
so i found only one thread about this question and there is no answer for it. So basically when you kill monster it removes stone which should be based on stone actionid or uniqueid, because if you want to remove same id stones with order. So for example this is how i imagine local
LUA:
local config = {
    {monsters = {"Rat Boss"}, stoneRestIn = 2(hours), StoneUniqueID = 1111},
    {monsters = {"Rat Boss Second"}, stoneRestIn = 2(hours), StoneUniqueID = 1112},
}
But i dont know if its even possible to use actionid uniqueid outside "action", basically i have no idea :D
You can try this, hope i understand what you are requesting. Untested so let me know.
LUA:
local config = { -- reset is per hour
    {name = {"Rat Boss"}...
Look up, remove stone after killing monster
Like i said there is just one thread and its not based on multiple stones like on my sample.
 
Hello,
so i found only one thread about this question and there is no answer for it. So basically when you kill monster it removes stone which should be based on stone actionid or uniqueid, because if you want to remove same id stones with order. So for example this is how i imagine local
LUA:
local config = {
    {monsters = {"Rat Boss"}, stoneRestIn = 2(hours), StoneUniqueID = 1111},
    {monsters = {"Rat Boss Second"}, stoneRestIn = 2(hours), StoneUniqueID = 1112},
}
But i dont know if its even possible to use actionid uniqueid outside "action", basically i have no idea :D
You can try this, hope i understand what you are requesting. Untested so let me know.
LUA:
local config = { -- reset is per hour
    {name = {"Rat Boss"}, reset = 2, pos = Position(1000, 1000, 7), itemid = 1304},
    {name = {"Rat Boss Second", "Rat Boss Third"}, reset = 2, pos = Position(1000, 1000, 7), itemid = 1304},
}

function onKill(creature, target)
    local player = creature:getPlayer()
    if not player then return true end

    local target_name = target:getName()
    for _, boss_array in pairs(config) do
        if isInArray(boss_array.name, target_name) then
            local count = 0
            for _, boss_name in pairs(boss_array.name) do
                if boss_name == target_name or not Creature(boss_name) then
                    count = count + 1
                end
            end
            if count == #boss_array.name then
                local tile = Tile(boss_array.pos)
                if tile then
                    local stone = tile:getItemById(boss_array.itemid)
                    if stone then
                        stone:remove()
                        addEvent(function(pos, itemid)
                            Game.createItem(itemid, 1, pos)
                        end, boss_array.reset * 60 * 60 * 1000, boss_array.pos, boss_array.itemid)
                    end
                end
            end
            break
        end
    end
    return true
end
 
Solution
If you wanted to remove stones by unique or action id you would have to have a table of all the stones for it to check. There is no way to just scan the entire map for items with the unique or actionid. If there was it would probably consume a bunch of memory and cause lag. Here is a code that removes multiple stones rather than just one. You MUST put the position and itemId of the stones in though there is no other way.

Code:
local bosses = { --Reset time in hours
    ["Boss1"] = {
        stonePositions = {
            [1] = {x = 1000 , y = 1000, z = 7},
            [2] = {x = 1000 , y = 1000, z = 7},
            [3] = {x = 1000 , y = 1000, z = 7}
        },
        stoneId = 1111
    }
}

local resetTime = 2 --hours

local function removeStone(pos, itemid)
    local STONE = Tile(pos):getItemByItemId(itemid)
    
    if not STONE then
        print("Error finding stone.")
    return true
    end
    
    STONE:remove()
    addEvent(resetStone, resetTime * 60 * 60 * 1000, pos, itemid)
end

local function resetStone(pos, itemid)
    Game.createItem(itemid, 1, pos)
end

function onKill(creature, target)
    if not creature:isPlayer() or not target:isMonster() then
        return true
    end
    
    local BOSS = bosses[target:getName()]
    
    if not BOSS then return true end
    
    for i = 1, #BOSS.stonePositions do
        removeStone(BOSS.stonePositions[i], BOSS.stoneId)
    end
return true
end
 
You can try this, hope i understand what you are requesting. Untested so let me know.
LUA:
local config = { -- reset is per hour
    {name = {"Rat Boss"}, reset = 2, pos = Position(1000, 1000, 7), itemid = 1304},
    {name = {"Rat Boss Second", "Rat Boss Third"}, reset = 2, pos = Position(1000, 1000, 7), itemid = 1304},
}

function onKill(creature, target)
    local player = creature:getPlayer()
    if not player then return true end

    local target_name = target:getName()
    for _, boss_array in pairs(config) do
        if isInArray(boss_array.name, target_name) then
            local count = 0
            for _, boss_name in pairs(boss_array.name) do
                if boss_name == target_name or not Creature(boss_name) then
                    count = count + 1
                end
            end
            if count == #boss_array.name then
                local tile = Tile(boss_array.pos)
                if tile then
                    local stone = tile:getItemById(boss_array.itemid)
                    if stone then
                        stone:remove()
                        addEvent(function(pos, itemid)
                            Game.createItem(itemid, 1, pos)
                        end, boss_array.reset * 60 * 60 * 1000, boss_array.pos, boss_array.itemid)
                    end
                end
            end
            break
        end
    end
    return true
end
Doesnt work
If you wanted to remove stones by unique or action id you would have to have a table of all the stones for it to check. There is no way to just scan the entire map for items with the unique or actionid. If there was it would probably consume a bunch of memory and cause lag. Here is a code that removes multiple stones rather than just one. You MUST put the position and itemId of the stones in though there is no other way.

Code:
local bosses = { --Reset time in hours
    ["Boss1"] = {
        stonePositions = {
            [1] = {x = 1000 , y = 1000, z = 7},
            [2] = {x = 1000 , y = 1000, z = 7},
            [3] = {x = 1000 , y = 1000, z = 7}
        },
        stoneId = 1111
    }
}

local resetTime = 2 --hours

local function removeStone(pos, itemid)
    local STONE = Tile(pos):getItemByItemId(itemid)
 
    if not STONE then
        print("Error finding stone.")
    return true
    end
 
    STONE:remove()
    addEvent(resetStone, resetTime * 60 * 60 * 1000, pos, itemid)
end

local function resetStone(pos, itemid)
    Game.createItem(itemid, 1, pos)
end

function onKill(creature, target)
    if not creature:isPlayer() or not target:isMonster() then
        return true
    end
 
    local BOSS = bosses[target:getName()]
 
    if not BOSS then return true end
 
    for i = 1, #BOSS.stonePositions do
        removeStone(BOSS.stonePositions[i], BOSS.stoneId)
    end
return true
end
Doesnt work either.

13:47 You see an item of type 16817.
Item ID: 16817, Action ID: 1902, Unique ID: 1902
Position: 68, 134, 7
XML:
    <event type="kill" name="RemoveStoneAni" script="RemoveStone.lua" />
Itutorial config version that used
LUA:
local bosses = { --Reset time in hours
    ["Dog"] = {
        stonePositions = {
            [1] = {x = 68 , y = 134, z = 7},
        },
        stoneId = 16817
    }
}
Apollos config version that used.
LUA:
{name = {"Dog"}, reset = 2, pos = Position(68, 134, 7), itemid = 16817},

XML:
    <script>
        <event name="RemoveStoneAni" />
    </script>

It looks like declared it correctly but didnt worked.
 
Doesnt work

Doesnt work either.

13:47 You see an item of type 16817.
Item ID: 16817, Action ID: 1902, Unique ID: 1902
Position: 68, 134, 7
XML:
    <event type="kill" name="RemoveStoneAni" script="RemoveStone.lua" />
Itutorial config version that used
LUA:
local bosses = { --Reset time in hours
    ["Dog"] = {
        stonePositions = {
            [1] = {x = 68 , y = 134, z = 7},
        },
        stoneId = 16817
    }
}
Apollos config version that used.
LUA:
{name = {"Dog"}, reset = 2, pos = Position(68, 134, 7), itemid = 16817},

XML:
    <script>
        <event name="RemoveStoneAni" />
    </script>

It looks like declared it correctly but didnt worked.
Mines an onKill event that needs to be registered to player at login
 
Mine is onKill as-well. I would suggest using mine only because its better to index the table by the boss name rather than using a loop to check the names to the monster. Its just a little more efficient.
 
Mine is onKill as-well. I would suggest using mine only because its better to index the table by the boss name rather than using a loop to check the names to the monster. Its just a little more efficient.
I don't think it's a question of efficiency since our scripts have a different feature. Mine includes requirement to kill more than one creature to remove stone.
LUA:
{"Rat Boss Second", "Rat Boss Third"}

Although yours includes the option to remove more than one stone, so it's a matter of what system he prefers to use. But yes, i suggest OP tests out your system as well.
 
@Apollos I'm glad you're learning by helping the community and I congratulate you for that.
however your script has some useless things, apparently you are a bit confused, making a totally unnecessary count.
Code:
local count = 0
            for _, boss_name in pairs(boss_array.name) do
                if boss_name == target_name or not Creature(boss_name) then
                    count = count + 1
                end
            end
            if count == #boss_array.name then
for that there is a function:
Code:
isInArray
in case of not existing, only use a boolean type value.
per example:
Code:
local isok = false
for _, boss_name in pairs(boss_array.name) do
    if boss_name == target_name then
        isok = true
        break
    end
end

if isok then
    -- ....

I'm not the best scripter, but a little lesson will help you think in a much better way.
regarding efficiency it should not be a problem for such a small code ;)

I think that this is enough:
Code:
local config = {
    { monsters = { "Rat Boss" }, stoneId = 16817, stonePos = { Position(100, 100, 7) }, stoneRestIn = 2 },
    { monsters = { "Rat Boss Second" }, stoneId = 16817, stonePos = { Position(100, 100, 7) }, stoneRestIn = 2 }
}

function onKill(creature, target)
    for i, mt in pairs(config) do
        if isInArray(mt.monsters, target:getName()) then
            for _, stpos in pairs(mt.stonePos) do
                local stone = stpos:getTile():getItemById(mt.stoneId) -- or Tile(stpos):getItemById(mt.stoneId)
                if stone then
                    stone:remove()
                    stpos:sendMagicEffect(CONST_ME_POFF)
                    addEvent(function(d, t)
                        Game.createItem(t.stoneId, 1, t.stonePos[d])
                        t.stonePos[d]:sendMagicEffect(CONST_ME_SMALLPLANTS)
                    end, (1000 * 60 * 60 * t.stoneRestIn), _, mt)
                end
            end
        end
    end
    return true
end

* This code will only be executed by players, so it is not necessary to verify if it is a player.
// If someone knows how to do it better, I would like to see it!

Excuse me for getting into the subject, but I saw them fighting ;)
 
Last edited:
@Apollos I'm glad you're learning by helping the community and I congratulate you for that.
however your script has some useless things, apparently you are a bit confused, making a totally unnecessary count.
Code:
local count = 0
            for _, boss_name in pairs(boss_array.name) do
                if boss_name == target_name or not Creature(boss_name) then
                    count = count + 1
                end
            end
            if count == #boss_array.name then
for that there is a function:
Code:
isInArray
in case of not existing, only use a boolean type value.
per example:
Code:
local isok = false
for _, boss_name in pairs(boss_array.name) do
    if boss_name == target_name then
        isok = true
        break
    end
end

if isok then
    -- ....

I'm not the best scripter, but a little lesson will help you think in a much better way.
regarding efficiency it should not be a problem for such a small code ;)

I think that this is enough:
Code:
local config = {
    { monsters = { "Rat Boss" }, stoneId = 16817, stonePos = { Position(100, 100, 7) }, stoneRestIn = 2 },
    { monsters = { "Rat Boss Second" }, stoneId = 16817, stonePos = { Position(100, 100, 7) }, stoneRestIn = 2 }
}

function onKill(creature, target)
    for i, mt in pairs(config) do
        local tn = target:getName()
        if isInArray(mt.monsters, tn) then
            for _, stpos in pairs(mt.stonePos) do
                local stone = stpos:getTile():getItemById(mt.stoneId) -- or Tile(stpos):getItemById(mt.stoneId)
                if stone then
                    stone:remove()
                    stpos:sendMagicEffect(CONST_ME_POFF)
                    addEvent(function(d, t)
                        Game.createItem(t.stoneId, 1, t.stonePos[d])
                        t.stonePos[d]:sendMagicEffect(CONST_ME_SMALLPLANTS)
                    end, (1000 * 60 * 60 * t.stoneRestIn), _, mt)
                end
            end
        end
    end
    return true
end

* This code will only be executed by players, so it is not necessary to verify if it is a player.
// If someone knows how to do it better, I would like to see it!

Excuse me for getting into the subject, but I saw them fighting ;)
Neither of you are understanding that config.name is an array for a reason and this script is checking all monster names in that array and checking if they are either the target or they are already dead. If all the boss monster are dead then it is allowed to remove the stone. (Example: You need to kill both Boss First and Boss Second in order for the stone to be removed). If it were for only one monster required to remove the stone, like how both of you have written it, only then would what you are saying be correct.

Clearly, I know how isInArray works it's right there in the script. The problem here is that you are not thoroughly reading my script nor understanding what's the purpose of these so called useless things.

Edit: if you want to get picky regarding your script you are getting target name multiple times in the for loop, it should be declared before the loop.
 
Last edited:
Its code works for bosses, the title does not specify that it will only be for that reason, that is why your script does not generalize creature by creature.
if there are 1000 rats, it would be necessary to kill them all for this to be fulfilled.
 
Last edited:
Its code works for bosses, the title does not specify that it will only be for that reason, that is why your script does not generalize creature by creature.
if there are 1000 rats, it would be necessary to kill them all for this to be fulfilled.
Well I wrote it with multiple uses and it worked for OP. You can grasp at straws all you want, the fact is you're wrong on many accounts in your reply and your code included more bad practices than mine.

I don't know what the purpose of your condisendence and attempts to prove me wrong is. I'm more than willing to listen to someone who is correct in what they saying but you are not.

If you want to continue this argument you can pm me, this thread is solved and support is not a board for debate.
 
I only mean that you responded to your taste, not what he really asked in the thread.

by the way I would like to know what are the things I did wrong, so I published the code, to know if someone more clever than me, manages to correct me and i wants you to do it. please!

Edit: get the name several times is not something that is wrong, it is an internal variable of the object, which consumes nothing! but I will make you case you are the expert. Good Night Thanks!
 
Last edited:
I only mean that you responded to your taste, not what he really asked in the thread.

by the way I would like to know what are the things I did wrong, so I published the code, to know if someone more clever than me, manages to correct me and i wants you to do it. please!

Edit: get the name several times is not something that is wrong, it is an internal variable of the object, which consumes nothing! but I will make you case you are the expert.
Sarah you clearly have an issue with me for some unseen reason. I did my best to make what OP needed and it worked.

As regards to you script as I said before you included a name declaration inside a loop(which you attempted to edit but still did it in a way that it is unnecessarily called multiple times). In my opinion your variable names are sloppy and confusing, and this is a board to teach new programmers so verifying player and tile I believe is something that should be done. You didn't break your loop after target name was discovered and you passed an index as an addEvent function parameter instead of just passing the itemid and position which makes way more sense.

Now again if you want to argue more pm me I'm not going to keep blowing up this thread.
 
Let's clear this conversation a bit up...

1) If OP wants to have remove stone by each boss kill then what @Itutorial made is the best option.
2) If OP wants to have remove stone by killing 'one' of the bosses (doesn't matter which one) then what @Itutorial made is still the best option.
3) If OP wants to have remove stone by killing several bosses then neither of any will work.

This one can be used by any of the cases however for 1) it's still better to use the one from @Itutorial
LUA:
local config = {
  [{"rat", "rat2"}] = {id = 1, reset = 2*60*60, pos = {Position(1,2,3), Position(1,2,3)}},
  [{"rat3", "rat4"}] = {id = 1, reset = 2*60*60, pos = {Position(2,3,4)}}
}

local bossKills = {}

function onKill(creature, target)
    local player = creature:getPlayer()
    if not player then return true end

    local name = target:getName()
    for namelist, values in pairs(config) do
        if isInArray(namelist, name) then
            if bossKills[namelist] then 
                bossKills[namelist][name] = true 
            else
                bossKills[namelist] = {[name] = true}
            end

            if #bossKills[namelist] == #namelist then
                for _, stonePos in ipairs(values.pos) do
                    local tile = Tile(stonePos)
                    if tile then
                        local stone = tile:getItemById(stones)
                        if stone then
                            stone:remove()
                            addEvent(function(pos, itemid)
                                Game.createItem(itemid, 1, pos)
                            end, values.reset * 1000, stonePos, values.id)
                        end
                       end
                   end
                   bossKills[namelist] = nil
               end
               break
           end
      end
    return true
end
 
Last edited:
Mine is onKill as-well. I would suggest using mine only because its better to index the table by the boss name rather than using a loop to check the names to the monster. Its just a little more efficient.
Well your code gives "attempt to call method 'getItemByItemId' <a nil value> in function 'removeStone'
 

Similar threads

Back
Top