Signup Now
Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 23
  1. #11
    Moderator Raphael's Avatar
    Join Date
    Dec 2013
    Location
    raphseller.com
    Posts
    2,441
    Reputation
    309
    Rep Power
    28
    @Jesseh, sorry for the long delay to reply, life has been rough lately. Yes, this does (partially) solve the problem of not knowing if it's looted or not but still means I have to redo a lot of the work the bot already does. Seeing as time has been short and I hate reinventing the wheel, I'll not be implementing this for now. @mistgun might wanna tackle it though, not sure.

  2. #12
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    Thanks @Raphael, no problem.
    @mistgun: I saw a HUD from you, with fire wall timers. I don't know how to implement it, but I think it has similar things, like the time counter. I thought of the timer way, reseting the tile after some time, but also thought on using the ID of the ground, for example, if the monster is recently dead it got an ID, after some time the corpse decay and the ID changes, resetting the tile, but this option would have the problem, at first, of other monsters dieing on the same SQM.

    The bot should sinchronize the recent deadcreature with it's coordinates, set a timer there, to keep it colored, and reset after the timer ends.
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  3. #13
    Banned
    Join Date
    Mar 2016
    Posts
    422
    Reputation
    54
    Rep Power
    0
    Is it really that hard to remember what monsters you looted? :P

  4. #14
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    When you Hunt luring like 15+ monsters at a time and many of them die at the same time, and you keep walking luring more, it becomes harder to track which SQMs got a good loot, so you must walk near all dead monsters and wait a bit until bot identifies which one is the right one.
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  5. #15
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    Bump. Any ideas?
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  6. #16
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    20
    Quote Originally Posted by Jesseh View Post
    Bump. Any ideas?
    This seemed like an interesting problem, one which cannot be solved on Xeno where I normally hang out. Does Windbot's API reveal which corpse that is bound to which loot message?

  7. #17
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    Quote Originally Posted by shAdOwArt View Post
    This seemed like an interesting problem, one which cannot be solved on Xeno where I normally hang out. Does Windbot's API reveal which corpse that is bound to which loot message?
    I don't know exactly how, or if it's possible. There is a function with "foreach deadcreature" that can store the coordinates of that dead body. Something must be done in order to concatenate that SQM to a recent loot msg, one that shows a valuable body. I don't know if that binding exists or if we need to try to figure it via a sloppy workaround, like one time based.
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  8. #18
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    20
    Quote Originally Posted by Jesseh View Post
    I don't know exactly how, or if it's possible. There is a function with "foreach deadcreature" that can store the coordinates of that dead body. Something must be done in order to concatenate that SQM to a recent loot msg, one that shows a valuable body. I don't know if that binding exists or if we need to try to figure it via a sloppy workaround, like one time based.
    So I'm pretty sure this isn't possible now. There are two problems that prevent you from tying loot messages to dead creatures;
    • foreach deadcreature does not iterate through the corpses in the same order as the message iterator iterates through the loot messages.
    • foreach deadcreature is not really tied to creatures dying at all, but rather it looks for corpses on the floor. Whenever a creature dies offscreen you're in trouble.

    I have some ideas that may be able to work around the latter restriction but the former prevents the script from working under all circumstances where it's actually useful.

    Anyway, here's what I ended up with before I quit:

    init start
    local DELAY = 1000
    setfontstyle('Tahoma', 7, 75, 0xFFFFFF, 1, 0x000000)

    -- Assumes no overlap between the keysets
    local function mergeTables(a, b)
    local a = a or {}
    local b = b or {}

    local res = {}
    for key, val in pairs(a) do
    res[key] = val
    end
    for key, val in pairs(b) do
    res[key] = val
    end
    return res
    end

    -- Consume current dead creatures
    foreach deadcreature c do end

    local unlootedCorpses = {}

    local lastCorpseIndex = 1
    local function checkCorpses()
    foreach deadcreature c do
    local corpseData = {x = c.posx, y = c.posy, z = c.posz}
    unlootedCorpses[lastCorpseIndex] = mergeTables(unlootedCorpses[lastCorpseIndex], corpseData)
    lastCorpseIndex = lastCorpseIndex + 1
    end
    end

    -- Lifted from Rydan
    local pluralExceptions = {
    {"knives", "knife"},
    {"pieces of *", "piece of "},
    {"*pieces of", "piece of "},
    {"bunches of *", "bunch of "},
    {"haunches of *", "haunch of "},
    {"flasks of *", "flask of "},
    {"veins of *", "vein of "},
    {"bowls of *", "bowl of "},
    {"sandwiches", "sandwich"}
    }

    -- Lifted from Rydan
    local pluralEndings = {
    {"che", "ch"},
    {"she", "sh"},
    {"ie", "y"},
    {"ve", "fe"},
    {"oe", "o"},
    {"ze", "z"}
    }

    -- Lifted from Rydan
    function string:explode(div)
    if (div == '') then
    return false
    end

    local pos, arr = 0, {}
    for st, sp in function() return string.find(self, div, pos, true) end do
    table.insert(arr, string.sub(self, pos, st - 1))
    pos = sp + 1
    end
    table.insert(arr, string.sub(self, pos))
    for i = 1, #arr do
    arr[i] = arr[i]:trim()
    end
    return arr
    end

    -- Mostly lifed from Rydan
    local function getLootAsTable(msg)
    local tbl = {}

    local parts = msg:explode(", ")
    for i = 1, #parts do
    local name = parts[i]


    local count = tonumber(name:match("^(%d+)"))
    if count then
    name = name:gsub("^%d+ ", "")
    for j = 1, #pluralExceptions do
    local pluralException = pluralExceptions[j]
    if name:find(pluralException[1]) then
    name = name:gsub(pluralException[1], pluralException[2])
    break
    end
    end

    if name:find("s$") then
    name = name:sub(1, -2)
    for j = 1, #pluralEndings do
    local pluralEnding = pluralEndings[j]
    if name:find(pluralEnding[1].."$") then
    name = name:gsub(pluralEnding[1].."$", pluralEnding[2])
    break
    end
    end
    end
    end

    count = count or 1
    name = name:gsub("^a ", "")
    name = name:gsub("^an ", "")
    table.insert(tbl, {id = itemid(name), count = count})
    end
    return tbl
    end

    -- Consume all previous messages
    local lastInfoMsgId = 1
    foreach newmessage m do
    if m.type == MSG_INFO then
    lastInfoMsgId = lastInfoMsgId + 1
    end
    end

    local lastLootMsgIndex = 1
    local function checkLootMsgs()
    local id = 1
    foreach newmessage m do
    if m.type == MSG_INFO then
    -- Ignore old messages
    if id >= lastInfoMsgId then
    local monster, loot = m.content:match('Loot of a?n? (.+): (.+)')
    -- Ignore non loot messages
    if monster then
    print(loot)
    local loot = loot:lower()
    local lootData = {loot = {}}
    if loot ~= 'nothing' then
    -- Figure out if the loot message contains an interesting item
    local containsInteresting = false
    foreach lootingitem item do
    if loot:find(item.name) then
    containsInteresting = true
    break
    end
    end

    -- Only add loot data for messages containing relevant loot
    if containsInteresting then
    lootData.msg = loot
    lootData.loot = getLootAsTable(loot)
    end
    end
    unlootedCorpses[lastLootMsgIndex] = mergeTables(unlootedCorpses[lastLootMsgIndex], lootData)
    lastLootMsgIndex = lastLootMsgIndex + 1
    end
    end
    id = id + 1
    end
    end
    lastInfoMsgId = id
    end

    -- Liften from the waypoint hud
    local function gettilepos(x, y, z)
    local tile = getobjectarea(x, y, z)

    if tile == nil then
    local xDiff, yDiff = x - $posx, y - $posy
    if math.abs($posx - x) <= 7 then
    tile = getobjectarea(x, $posy, $posz)
    xDiff = 0
    elseif math.abs($posy - y) <= 5 then
    tile = getobjectarea($posx, y, $posz)
    yDiff = 0
    else
    tile = getobjectarea($posx, $posy, $posz)
    end

    -- Some strange stuff happens when you go from 0 to -1, so I'm
    -- adding this as a precaution.
    if tile ~= nil then
    local width, height = $worldwin.width, $worldwin.height

    tile.left = tile.left + (width * xDiff)
    tile.right = tile.right + (width * xDiff)
    tile.centerx = tile.centerx + (width * xDiff)
    tile.top = tile.top + (height * yDiff)
    tile.bottom = tile.bottom + (height * yDiff)
    tile.centery = tile.centery + (height * yDiff)
    end
    end

    return tile
    end

    useworldhud()
    local function drawCorpses()
    local function isOnScreen(corpse)
    return corpse.z == $posz and math.abs(corpse.y - $posy) <= 5 and math.abs(corpse.x - $posx) <= 7
    end

    for _, corpse in pairs(unlootedCorpses) do
    if corpse.msg and isOnScreen(corpse) then
    local tile = gettilepos(corpse.x, corpse.y, corpse.z)
    drawtext(corpse.msg, tile.left, tile.top)
    end
    end
    end

    local function updateLooted()
    local function isCorpse(cont)
    local corpseWords = {"the", "demonic", "dead", "slain", "dissolved", "remains", "elemental", "split"}

    local name = cont.name:lower()
    for _, word in ipairs(corpseWords) do
    if name:find(word) then
    return true
    end
    end
    return false
    end

    local function contentsMatch(cont, corpse)
    if not corpse.loot or cont.itemcount ~= #corpse.loot then return false end

    for i = 1, cont.itemcount do
    local contItem = cont.item[i]
    local corpseItem = corpse.loot[i]
    if contItem.id ~= corpseItem.id or contItem.count ~= corpseItem.count then return false end
    end
    return true
    end

    local function isAdjecent(corpse)
    return corpse.z == $posz and math.abs(corpse.y - $posy) <= 1 and math.abs(corpse.x - $posx) <= 1
    end

    -- foreach container
    for i = 0, 15 do
    local cont = getcontainer(i)
    if cont.isopen and isCorpse(cont) then
    for index, corpse in pairs(unlootedCorpses) do
    -- Remove corpses whose loot messages exactly matches some open corpse
    if isAdjecent(corpse) and contentsMatch(cont, corpse) then
    unlootedCorpses[index] = nil
    break
    end
    end
    end
    end
    end

    local lastc, lastm = 1, 1
    local function debugPrint()
    if lastCorpseIndex > lastc or lastLootMsgIndex > lastm then
    print(string.format("CorpseIndex: %d, MsgIndex: %d", lastCorpseIndex, lastLootMsgIndex))
    lastc = lastCorpseIndex
    lastm = lastLootMsgIndex
    end
    end
    init end

    auto(DELAY)
    checkCorpses()
    checkLootMsgs()
    drawCorpses()
    updateLooted()
    debugPrint()


    Note that checking whether a corpse has been looted or not can be done with good accuracy by compating the contents of open corpses the contents of adjecent corpses, if you manage to correctly tie loot messages to corpses. Tying loot messages to corpses really is the crux of it all, it's the same problem I ran into when I tried to write a very similar script in Xeno a long time ago.
    Last edited by shAdOwArt; 08-31-2016 at 07:26 AM.

  9. #19
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    Great effort @shAdOwArt! But as you said, it may not work correctly and we may need to wait for an update, if this feature catch developers' eyes.

    By now, what I do is,I set looting range to like 15 SQM or lower, set to loot 101+ gp value bodies, and after killing monsters manually, I start cavebot with no waypoints, so it starts running and collecting useful loot, but sometimes I get into trouble with the bot running into many monsters and not stopping even if cavebot is already set to off.
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  10. #20
    Moderator Raphael's Avatar
    Join Date
    Dec 2013
    Location
    raphseller.com
    Posts
    2,441
    Reputation
    309
    Rep Power
    28
    Quote Originally Posted by shAdOwArt View Post
    So I'm pretty sure this isn't possible now. There are two problems that prevent you from tying loot messages to dead creatures;
    • foreach deadcreature does not iterate through the corpses in the same order as the message iterator iterates through the loot messages.
    • foreach deadcreature is not really tied to creatures dying at all, but rather it looks for corpses on the floor. Whenever a creature dies offscreen you're in trouble.

    I have some ideas that may be able to work around the latter restriction but the former prevents the script from working under all circumstances where it's actually useful.

    Anyway, here's what I ended up with before I quit:
    ...


    Note that checking whether a corpse has been looted or not can be done with good accuracy by compating the contents of open corpses the contents of adjecent corpses, if you manage to correctly tie loot messages to corpses. Tying loot messages to corpses really is the crux of it all, it's the same problem I ran into when I tried to write a very similar script in Xeno a long time ago.
    Number 1 really isn't that much of a problem, all you gotta do is keep a "cache" of the currently dead monsters and when you read a "Loot of..." message, check which new corpse was added. That's the one that just died. I'd argue you don't even need foreach deadcreature, you should just loop over all alive creatures (foreach creature) and if one disappears within a certain timespan of a "Loot of..." message, then that's the creature. Well, both are the same approach on a different direction.

    Number 2 doesn't really have any real solution, there's not much that can be done, unfortunately.

    About your code, a few possible improvements:

    • WindBot has string.explode and table.marge (equivalent of your mergeTable) baked in one of its standard libs (Mine, actually)
    • WindBot has a few useful regexes like REGEX_LOOT baked in one of its standard libs (Mine, actually)


    Other than that, pretty good stuff. The part about checking for looted bodies was really well thought...

 

 

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •