Signup Now
Results 1 to 5 of 5
  1. #1
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    20

    Dynamic Special Areas

    Description
    This is a mini-library for dynamically adding and removing special areas around your character. I do not provide an actual persistent, but in practice you would likely call the adding function when you either stop luring, or acquire a target and then you would call the removing function when you either restart luring or kill your target or hasn't had a target for X milliseconds or something like that. The fact that there are many different ways to do it, and they are all suitable under different circumstances, is why I left out the persistent part and only provide the init block.

    API
    • addDynamicAreas(int range, int minNeighbours, boolean removeNarrowPassages)
      Adds special areas in a rectangle at range distance away from your character. Also adds areas in the four corners of said rectangle. It also adds special areas on all squares inside said rectangle that has less than minNeighbours walkable neighbours. Squares with special areas are not considerable walkable, but squares with creatures or moveable furniture is considered walkable. If removeNarrowPassages is truthy then squares whose both horizontal or vertical neighbours are unwalkable will also be removed.
    • removeDynamicAreas()
      Removes all special areas that have been added since the first call to addDynamicAreas after the last call to removeSpecialAreas (if any). If you didn't understand that, then just make sure that you don't add any new areas while there are dynamic areas active, or those will later be removed when removeDynamicAreas is called. If you call addDynamicAreas multiple times then all those areas will be removed when you call removeDynamicAreas.

    Examples
    This is the basic shape of the dynamic special areas. In this picture the range parameter is set to 5. In practice you might want it to be higher than that, but I wanted everything to fit on the screen for the sake of the screenshot.

    The script also removes squares with too few walkable neighbours. Here I've set the minNeighbours parameter to 3. Note that unwalkable squares don't necessarily get special areas on them (since those areas does nthing) which explains the shape of the two lefternmost pictures.

    The script is also able to take previously existing special areas into account when it considers which squares that should be removed. Here you can also see what the removeNarrowPassages parameter does. See that square in the bottom right quadrant in the left screenshot that's pinned between a long horizontal area to the left, and a single square area to the right? It has 6 walkable neighbours so it should not have been removed based on the minNeighbours parameter (which was still set to 3). However, that square had an unwalkable square both to the right of it, and to the left so the script considered it a "narrow passage" and removed it. If removeNarrowPassages had not been truthy it would not have been removed.

    Let me end with a zoomed out picture with range set to 9, minNeighbours to 4 and removeNarrowPassages set to true, which are settings that I would use in practice in open areas:


    init start
    local safelist

    function addDynamicAreas(range, minNeighbours, removeNarrowPassages)
    local range = range or 5
    local minNeighbours = minNeighbours or 3

    if not safelist then
    safelist = {}
    foreach settingsentry e "Cavebot/SpecialAreas" do
    safelist[get(e, "Name")] = true
    end
    end

    -- Add a basic rectangle. We're using 1 square areas
    -- since the library api doesn't support a width or height parameter.
    for x = $posx - range, $posx + range do
    addspecialarea("Targeting", x, $posy - range, $posz)
    addspecialarea("Targeting", x, $posy + range, $posz)
    end
    for y = $posy - range, $posy + range do
    addspecialarea("Targeting", $posx - range, y, $posz)
    addspecialarea("Targeting", $posx + range, y, $posz)
    end


    -- First we need some helpers to keep track of which squares that have
    -- been removed with areas.
    local function posToNum(x, y)
    return 100000*x+y
    end

    local walkables = {}
    local candidates = {}
    for x = $posx - range + 1, $posx + range - 1 do
    for y = $posy - range + 1, $posy + range - 1 do
    candidates[posToNum(x, y)] = {x = x, y = y}
    local top = iteminfo(topitem(x, y, $posz).id)
    if tilewalkable(x, y, $posz) and not (top.isunpass and top.isunmove) then
    walkables[posToNum(x, y)] = true
    end
    end
    end

    -- Add creature squares to the walkable list. Even if we cant walk on them they might move so we should
    -- not purge squares based on their current position
    foreach creature c 's' do
    walkables[posToNum(c.posx, c.posy)] = true
    end

    -- Remove all squares already inside a special area from the walkable set
    foreach settingsentry e "Cavebot/SpecialAreas" do
    if get(e, "Policy"):find("Targeting") then
    local x, y, z = get(e, "Coordinates"):match(REGEX_COORDS)
    local w, h = get(e, "Size"):match(REGEX_RANGE)
    for ax = x, x + w - 1 do
    for ay = y, y + h - 1 do
    walkables[posToNum(ax, ay)] = nil
    end
    end
    end
    end


    local function addArea(x, y)
    addspecialarea("Targeting", x, y, $posz)
    walkables[posToNum(x, y)] = nil
    end
    -- Remove the corners because we know that those are bad!
    addArea($posx - range + 1, $posy - range + 1)
    addArea($posx - range + 1, $posy + range - 1)
    addArea($posx + range - 1, $posy - range + 1)
    addArea($posx + range - 1, $posy + range - 1)

    function wneighbours(x, y)
    local t = {}
    local n = 0
    for nx = x - 1, x + 1 do
    for ny = y - 1, y + 1 do
    if walkables[posToNum(nx, ny)] and (nx ~= x or ny ~= y) then
    t[posToNum(nx, ny)] = {x = nx, y = ny}
    n = n + 1
    end
    end
    end
    return t, n
    end

    -- Now we want to iteratively add areas on all squares with less than minNeighbours
    -- walkable neighbours.
    while candidates do
    local newCandidates
    for key, pos in pairs(candidates) do
    local neighbours, nneighbours = wneighbours(pos.x, pos.y)
    local narrowCond = removeNarrowPassages and
    ((not (neighbours[posToNum(pos.x - 1, pos.y)] or neighbours[posToNum(pos.x + 1, pos.y)])) or
    (not (neighbours[posToNum(pos.x, pos.y - 1)] or neighbours[posToNum(pos.x, pos.y + 1)])))
    -- Purge squares with less than minNeighbours walkable neighbours, and possibly also narrow passages!
    if nneighbours < minNeighbours or narrowCond then
    addArea(pos.x, pos.y)
    newCandidates = newCandidates or {}

    for key, npos in pairs(neighbours) do
    newCandidates[key] = npos
    end
    end
    end
    candidates = newCandidates
    end
    end

    function removeDynamicAreas()
    if not safelist then return end

    -- removespecialarea is bugged and does not always succeed so we
    -- loop until everything is gone.
    local changed = true
    while changed do
    changed = false
    foreach settingsentry e "Cavebot/SpecialAreas" do
    local name = get(e, "Name")
    if not safelist[name] then
    removespecialarea(name)
    changed = true
    end
    end
    end
    safelist = nil
    end
    init end
    Last edited by shAdOwArt; 10-25-2016 at 11:31 AM.

  2. #2
    Free User Akiller's Avatar
    Join Date
    Aug 2015
    Posts
    240
    Reputation
    33
    Rep Power
    18
    its not so awful as it seems

  3. #3
    Free User Stusse's Avatar
    Join Date
    Dec 2013
    Posts
    143
    Reputation
    43
    Rep Power
    21
    This is nice. However in a lot of spawn this will not really be enough as you need to specialize the special areas, but for big areas definately good!

    Good work!

    /Stusse

  4. #4
    Moderator Raphael's Avatar
    Join Date
    Dec 2013
    Location
    raphseller.com
    Posts
    2,441
    Reputation
    309
    Rep Power
    28
    I liked the idea, though it didn't work really well with a range 9 when I tried myself. Probably because WindBot doesn't have information 9 sqms away (or this is a bug on Tibia 11, this occurred to me while writing the thread so I'll have to double check).

    A few points about the code:

    • I'd rather have a blacklist than a safelist. You can use the coordinates and size for this. This is because if any special area is added between the calls to addDynamicAreas() and removeDynamicAreas() they would also be deleted.
    • Any specific reason for the conversion to/from coordinates/single number instead of just using a 2D matrix? Something like candidates[x][y].


    And a few points about other stuff:

    • I'm working with @Lucas Terra to get a proper version of addspecialarea() with more parameters.
    • Could you elaborate on and report the removespecialarea() bug?

  5. #5
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    20
    Quote Originally Posted by Raphael View Post
    I liked the idea, though it didn't work really well with a range 9 when I tried myself. Probably because WindBot doesn't have information 9 sqms away (or this is a bug on Tibia 11, this occurred to me while writing the thread so I'll have to double check).

    A few points about the code:

    • I'd rather have a blacklist than a safelist. You can use the coordinates and size for this. This is because if any special area is added between the calls to addDynamicAreas() and removeDynamicAreas() they would also be deleted.
    • Any specific reason for the conversion to/from coordinates/single number instead of just using a 2D matrix? Something like candidates[x][y].


    And a few points about other stuff:

    • I'm working with @Lucas Terra to get a proper version of addspecialarea() with more parameters.
    • Could you elaborate on and report the removespecialarea() bug?
    A blacklist might work, but it would be tricky since I don't get the name of my new area when I create it so I first have to get a safelist, then after adding my areas I have to iterate all areas again to get the set difference. However, addspecialarea is asynchrounous so I have to keep iterating until discover the right number, but this would get me stuck in an infinite loop if something removes an area while Im adding areas. I could get around that most of the time, but then we're back where we started.

    Converting coordinates to numbers is just my preferred way of storing them in tables. This is since if you put them in a matrix your script will crash if you try to do something with table[x][y] when x is not in table which leads to clunky code. In this particular script I have good bounds on which coordinates I'm checking but I'm staying consistent across different scripts.

    The removespecialarea bug is that 2-3 special areas always would remain whenever I just looped through the areas once and removed everything not on the safelist. I'm guessing that it's something related to removing many areas at once.

    When I tried with range 9 in Tibia 10 it seemed like WB had information about those far away squares. I guess it could be related to me having walked past there before in the same session though and wb relying on the old information still being accurate.
    Last edited by shAdOwArt; 10-27-2016 at 07:02 AM.

 

 

Posting Permissions

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