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

TalkAction [TFS 1.2+] Minimap generator (map scanner)

Gesior.pl

Mega Noob&LOL 2012
Senator
Premium User
Joined
Sep 18, 2007
Messages
2,275
Solutions
36
Reaction score
1,630
Location
PLand
I've needed Minimap for Tibia client, so I wrote script that teleports player to all possible positions in given area.

It's also good to scan map for possible client debugs. It detects only basic 'wrong items', but I found one debug on my map.

It scans 30 positions per second. You can edit addEventDelay and teleportsPerEvent, but too low interval or too many teleports per event may debug client.
I've tested 30 positions per second and it did not debug client.

On 173MB RL map there around 1130k possible positions.
One Tibia client can scan up to 100k per hour, but you can run few clients at once, each one on other floor. If you do so, remember to reduce maxEventExecutionTime to some low value (100/500) or client will get message about disconnection from server (server will freez for too long).

data/talkactions/talkactions.xml
XML:
    <talkaction words="/minimap" separator=" " script="minimap_scan.lua" />

data/talkactions/scripts/minimap_scan.lua
Lua:
local distanceBetweenPositionsX = 8
local distanceBetweenPositionsY = 8
local addEventDelay = 100
local teleportsPerEvent = 3
local maxEventExecutionTime = 1000

local function teleportToClosestPosition(player, x, y, z)
   -- direct to position
   local tile = Tile(x, y, z)

   if not tile or not tile:getGround() or tile:hasFlag(TILESTATE_TELEPORT) or not player:teleportTo(tile:getPosition()) then
      for distance = 1, 3 do
         -- try to find some close tile
         for changeX = -distance, distance, distance do
            for changeY = -distance, distance, distance do
               tile = Tile(x + changeX, y + changeY, z)
               if tile and tile:getGround() and not tile:hasFlag(TILESTATE_TELEPORT) and player:teleportTo(tile:getPosition()) then
                  return true
               end
            end
         end
      end

      return false
   end

   return true
end

local function sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
   local progress = math.floor(((y - minY + (((x - minX) / (maxX - minX)) * distanceBetweenPositionsY)) / (maxY - minY)) * 100)
   if progress ~= lastProgress then
      player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan progress: ~' .. progress .. '%')
   end

   return progress
end

local function minimapScan(cid, minX, maxX, minY, maxY, x, y, z, lastProgress)
   local player = Player(cid)

   if not player then
      --print('Minimap scan stopped - player logged out', cid, minX, maxX, minY, maxY, x, y, z)
      return
   end

   local scanStartTime = os.mtime()
   local teleportsDone = 0
   while true do
      if scanStartTime + maxEventExecutionTime < os.mtime() then
         lastProgress = sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
         addEvent(minimapScan, addEventDelay, cid, minX, maxX, minY, maxY, x, y, z, lastProgress)
         break
      end

      x = x + distanceBetweenPositionsX
      if x > maxX then
         x = minX
         y = y + distanceBetweenPositionsY
         if y > maxY then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan finished: ' .. os.time())
            --print('Minimap scan complete', player:getName(), minX, maxX, minY, maxY, x, y, z)
            break
         end
      end

      if teleportToClosestPosition(player, x, y, z) then
         teleportsDone = teleportsDone + 1
         lastProgress = sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)

         --print('Minimap scan teleport', player:getName(), minX, maxX, minY, maxY, x, y, z, progress, teleportsDone)
         if teleportsDone == teleportsPerEvent then
            addEvent(minimapScan, addEventDelay, cid, minX, maxX, minY, maxY, x, y, z, progress)
            break
         end
      end
   end
end

local function minimapStart(player, minX, maxX, minY, maxY, x, y, z)
   player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan started: ' .. os.time())
   --print('Minimap scan start', player:getName(), minX, maxX, minY, maxY, x, y, z)
   minimapScan(player:getId(), minX, maxX, minY, maxY, minX - 5, minY, z)
end

function onSay(player, words, param)
   if player:getGroup():getId() <= 4 then
      player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Only GOD can scan map. Too low Player group.')
      return false
   end

   if player:getAccountType() < ACCOUNT_TYPE_GAMEMASTER then
      player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Only GOD can scan map.Too low Account type.')
      return false
   end

   local positions = param:split(',')
   if #positions ~= 5 then
      player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Command requires 5 parameters: /minimap minX, maxX, minY, maxY, z')
      return false
   end

   for key, position in pairs(positions) do
      local value = tonumber(position)

      if not value then
         player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Invalid parameter ' .. key .. ': ' .. position)
         return false
      end

      positions[key] = value
   end

   minimapStart(player, positions[1], positions[2], positions[3], positions[4], positions[1] - distanceBetweenPositionsX, positions[3], positions[5])
   return false
end

Example use:
/minimap minX, maxX, minY, maxY, z
/minimap 0, 40000, 0, 40000, 7
 
Last edited:

Ochman

Advanced OT User
Joined
Feb 27, 2016
Messages
337
Solutions
5
Reaction score
237
One Tibia client can scan up to 100k per hour, but you can run few clients at once, each one on other floor.
But it won't save minimap if you use multiple clients at once, right? Or will it?
 
OP
Gesior.pl

Gesior.pl

Mega Noob&LOL 2012
Senator
Premium User
Joined
Sep 18, 2007
Messages
2,275
Solutions
36
Reaction score
1,630
Location
PLand
But it won't save minimap if you use multiple clients at once, right? Or will it?
Up to 10.x client it created files with names like:
13213107.map
Name format is:
xxx - 132 * 256 (all tiles with X between '132 * 256' and '132 * 256 + 255' are stored in this file)
yyy - 131 * 256
zz - 7

Client modifies these files in real time, as player sees new tiles in client. It does not modify files of other 'floors'. I ran 2 clients in same time with scans of floor 5 and 6. It created files:
15:51 13112405.map
15:52 13112406.map
15:51 13112505.map
15:52 13112605.map
15:50 13212205.map
15:51 13212206.map

I also ran scanner with parameters:
local distanceBetweenPositionsX = 8
local distanceBetweenPositionsY = 6
and:
local distanceBetweenPositionsX = 12
local distanceBetweenPositionsY = 8
First config worked for 59 minutes to scan 'floor 7' (ground). Second worked for only 23 minutes. Second scanner scanned 99.5% of map.
If you don't have some '3 tile width tunnels with no border around' (NULL tiles around that tunnel), it will scan every possible place on map and take much less time.
 

Ochman

Advanced OT User
Joined
Feb 27, 2016
Messages
337
Solutions
5
Reaction score
237
I see. I still live in the word of client <10.0 that's why I am asking.
Good job, like, really.
 
OP
Gesior.pl

Gesior.pl

Mega Noob&LOL 2012
Senator
Premium User
Joined
Sep 18, 2007
Messages
2,275
Solutions
36
Reaction score
1,630
Location
PLand
I see. I still live in the word of client <10.0 that's why I am asking.
Good job, like, really.
7.6, 8.0, 8.6, 9.6, 10.99 - all old clients use that format of map files, so you can even run 16 clients to scan all floors at once.

Updated first post. Just calculated that the best parameters are 8 and 8. It will scan every possible tile on map (closest position algorithm looks 3 tiles around).

EDIT:
I've scanned 16 levels of 173 MB RL map in 33 minutes using 12 MCs. Of course most of time took scan of level 7 (ground).
 
Last edited:

diegozenho

New Member
Joined
Feb 26, 2011
Messages
27
Reaction score
2
Would you have as converter to 0.4? or would it take up a lot of your time? Thank you, success!
 

Vinicius Chaves

New Member
Joined
Oct 30, 2018
Messages
23
Solutions
2
Reaction score
4
omg does it still works? i am trying for otbr but does not work :( ,

Lua:
local minimap = TalkAction("/minimap")

local distanceBetweenPositionsX = 8
local distanceBetweenPositionsY = 8
local addEventDelay = 100
local teleportsPerEvent = 3
local maxEventExecutionTime = 1000

local function teleportToClosestPosition(player, x, y, z)
   -- direct to position
   local tile = Tile(x, y, z)

   if not tile or not tile:getGround() or tile:hasFlag(TILESTATE_TELEPORT) or not player:teleportTo(tile:getPosition()) then
      for distance = 1, 3 do
         -- try to find some close tile
         for changeX = -distance, distance, distance do
            for changeY = -distance, distance, distance do
               tile = Tile(x + changeX, y + changeY, z)
               if tile and tile:getGround() and not tile:hasFlag(TILESTATE_TELEPORT) and player:teleportTo(tile:getPosition()) then
                  return true
               end
            end
         end
      end

      return false
   end

   return true
end

local function sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
   local progress = math.floor(((y - minY + (((x - minX) / (maxX - minX)) * distanceBetweenPositionsY)) / (maxY - minY)) * 100)
   if progress ~= lastProgress then
      player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan progress: ~' .. progress .. '%')
   end

   return progress
end

local function minimapScan(cid, minX, maxX, minY, maxY, x, y, z, lastProgress)
   local player = Player(cid)

   if not player then
      --print('Minimap scan stopped - player logged out', cid, minX, maxX, minY, maxY, x, y, z)
      return
   end

   local scanStartTime = os.mtime()
   local teleportsDone = 0
   while true do
      if scanStartTime + maxEventExecutionTime < os.mtime() then
         lastProgress = sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
         addEvent(minimapScan, addEventDelay, cid, minX, maxX, minY, maxY, x, y, z, lastProgress)
         break
      end

      x = x + distanceBetweenPositionsX
      if x > maxX then
         x = minX
         y = y + distanceBetweenPositionsY
         if y > maxY then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan finished: ' .. os.time())
            --print('Minimap scan complete', player:getName(), minX, maxX, minY, maxY, x, y, z)
            break
         end
      end

      if teleportToClosestPosition(player, x, y, z) then
         teleportsDone = teleportsDone + 1
         lastProgress = sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)

         --print('Minimap scan teleport', player:getName(), minX, maxX, minY, maxY, x, y, z, progress, teleportsDone)
         if teleportsDone == teleportsPerEvent then
            addEvent(minimapScan, addEventDelay, cid, minX, maxX, minY, maxY, x, y, z, progress)
            break
         end
      end
   end
end

minimap:separator(" ")
minimap:register()
 
Top