From 8f492f7e067b1295cfa6cadc0ec928f08ab86496 Mon Sep 17 00:00:00 2001
From: Kamil <sn4ake@hotmail.com>
Date: Mon, 9 Jul 2012 08:59:16 +0200
Subject: [PATCH 1/3] Fixed bug with adding Vip from console or map, who is
 already on viplist.

---
 init.lua                                 |   4 +-
 modules/game/player.lua                  |   7 +
 modules/game_console/console.lua         |   4 +-
 modules/game_interface/gameinterface.lua |   4 +-
 src/framework/core/binarytree.cpp        |  15 +-
 src/framework/pch.h                      |   4 +
 src/otclient/CMakeLists.txt              |   4 +
 src/otclient/core/declarations.h         |   6 +
 src/otclient/core/item.cpp               | 129 ++++++------
 src/otclient/core/item.h                 |  13 +-
 src/otclient/core/map.cpp                | 245 +++++++++++------------
 src/otclient/core/map.h                  |   9 +-
 src/otclient/core/thingtypemanager.cpp   |   5 +-
 src/otclient/core/tile.h                 |   2 +-
 14 files changed, 226 insertions(+), 225 deletions(-)

diff --git a/init.lua b/init.lua
index 0bc3469e..deddc3b5 100644
--- a/init.lua
+++ b/init.lua
@@ -45,7 +45,7 @@ if g_resources.fileExists("/otclientrc.lua") then
   dofile("/otclientrc.lua")
 end
 
---g_things.loadOtb("/items.otb")
---g_map.loadOtbm("/forgotten.otbm")
+g_things.loadOtb("/items.otb")
+g_map.loadOtbm("/forgotten.otbm")
 
 
diff --git a/modules/game/player.lua b/modules/game/player.lua
index 715729a5..8043d336 100644
--- a/modules/game/player.lua
+++ b/modules/game/player.lua
@@ -31,3 +31,10 @@ function Player:isPartySharedExperienceActive()
           shield == ShieldBlueNoSharedExpBlink or
           shield == ShieldBlueNoSharedExp)
 end
+
+function Player:hasVip(creatureName)
+  for id, vip in pairs(g_game.getVips()) do
+    if (vip[1] == creatureName) then return true end
+  end
+  return false
+end
diff --git a/modules/game_console/console.lua b/modules/game_console/console.lua
index f2cb0785..f4acfb75 100644
--- a/modules/game_console/console.lua
+++ b/modules/game_console/console.lua
@@ -403,7 +403,9 @@ function Console.popupMenu(mousePos, mouseButton, creatureName, text)
     if creatureName then
       if creatureName ~= g_game.getCharacterName() then
         menu:addOption(tr('Message to ' .. creatureName), function () g_game.openPrivateChannel(creatureName) end)
-        menu:addOption(tr('Add to VIP list'), function () g_game.addVip(creatureName) end) --TODO not show if creature already in vip
+        if (not Player:hasVip(creatureName)) then
+          menu:addOption(tr('Add to VIP list'), function () g_game.addVip(creatureName) end)
+        end
         -- TODO ignore creatureName
         menu:addSeparator()
       end
diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua
index 13a1c402..92f2e28d 100644
--- a/modules/game_interface/gameinterface.lua
+++ b/modules/game_interface/gameinterface.lua
@@ -263,7 +263,9 @@ function GameInterface.createThingMenu(menuPosition, lookThing, useThing, creatu
             menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end)
             menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end) -- [TODO] must be removed after message's popup labels been implemented
           end
-          menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end)
+          if (not Player:hasVip(creatureName)) then
+            menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end)
+          end
 
           local localPlayerShield = localPlayer:asCreature():getShield()
           local creatureShield = creatureThing:getShield()
diff --git a/src/framework/core/binarytree.cpp b/src/framework/core/binarytree.cpp
index 3ebfa452..7a4ff7b5 100644
--- a/src/framework/core/binarytree.cpp
+++ b/src/framework/core/binarytree.cpp
@@ -60,7 +60,7 @@ void BinaryTree::seek(uint pos)
 
 uint8 BinaryTree::getU8()
 {
-    if(m_pos+1 > m_buffer.size())
+    if (m_pos+1 > m_buffer.size())
         stdext::throw_exception("BinaryTree: getU8 failed");
     uint8 v = m_buffer[m_pos];
     m_pos += 1;
@@ -69,7 +69,7 @@ uint8 BinaryTree::getU8()
 
 uint16 BinaryTree::getU16()
 {
-    if(m_pos+2 > m_buffer.size())
+    if (m_pos+2 > m_buffer.size())
         stdext::throw_exception("BinaryTree: getU16 failed");
     uint16 v = stdext::readLE16(&m_buffer[m_pos]);
     m_pos += 2;
@@ -78,7 +78,7 @@ uint16 BinaryTree::getU16()
 
 uint32 BinaryTree::getU32()
 {
-    if(m_pos+4 > m_buffer.size())
+    if (m_pos+4 > m_buffer.size())
         stdext::throw_exception("BinaryTree: getU32 failed");
     uint32 v = stdext::readLE32(&m_buffer[m_pos]);
     m_pos += 4;
@@ -87,8 +87,8 @@ uint32 BinaryTree::getU32()
 
 uint64 BinaryTree::getU64()
 {
-    if(m_pos+8 > m_buffer.size())
-        stdext::throw_exception("BinaryTree: getu64 failed");
+    if (m_pos+8 > m_buffer.size())
+        stdext::throw_exception("BinaryTree: getU64 failed");
     uint64 v = stdext::readLE64(&m_buffer[m_pos]);
     m_pos += 8;
     return v;
@@ -98,7 +98,10 @@ std::string BinaryTree::getString()
 {
     uint16 len = getU16();
     if (len == 0 || len > 8192)
-        stdext::throw_exception("failed to get string from binary tree - invalid size read.");
+        stdext::throw_exception("BinaryTree: getString failed: invalid or too large string length");
+
+    if (m_pos+len > m_buffer.size())
+        stdext::throw_exception("BinaryTree: getString failed: string length exceeded buffer size.");
 
     std::string ret((char *)&m_buffer[m_pos], len);
     m_pos += len;
diff --git a/src/framework/pch.h b/src/framework/pch.h
index e167b528..a27c02ee 100644
--- a/src/framework/pch.h
+++ b/src/framework/pch.h
@@ -63,4 +63,8 @@
 // boost utilities
 #include <boost/algorithm/string.hpp>
 
+// tiny XML
+#define TIXML_USE_STL
+#include <framework/thirdparty/tinyxml.h>
+
 #endif
diff --git a/src/otclient/CMakeLists.txt b/src/otclient/CMakeLists.txt
index 693778bf..dde44a03 100644
--- a/src/otclient/CMakeLists.txt
+++ b/src/otclient/CMakeLists.txt
@@ -74,6 +74,10 @@ SET(otclient_SOURCES ${otclient_SOURCES}
     ${CMAKE_CURRENT_LIST_DIR}/core/thingtypeotb.h
     ${CMAKE_CURRENT_LIST_DIR}/core/tile.cpp
     ${CMAKE_CURRENT_LIST_DIR}/core/tile.h
+    ${CMAKE_CURRENT_LIST_DIR}/core/houses.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/core/houses.h
+    ${CMAKE_CURRENT_LIST_DIR}/core/towns.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/core/towns.h
 
     # lua
     ${CMAKE_CURRENT_LIST_DIR}/luascript/luavaluecasts.cpp
diff --git a/src/otclient/core/declarations.h b/src/otclient/core/declarations.h
index 78dd4ccb..a03083de 100644
--- a/src/otclient/core/declarations.h
+++ b/src/otclient/core/declarations.h
@@ -43,6 +43,8 @@ class AnimatedText;
 class StaticText;
 class ThingTypeDat;
 class ThingTypeOtb;
+class House;
+class Town;
 
 typedef std::shared_ptr<MapView> MapViewPtr;
 typedef std::shared_ptr<Tile> TilePtr;
@@ -60,9 +62,13 @@ typedef std::shared_ptr<AnimatedText> AnimatedTextPtr;
 typedef std::shared_ptr<StaticText> StaticTextPtr;
 typedef std::shared_ptr<ThingTypeDat> ThingTypeDatPtr;
 typedef std::shared_ptr<ThingTypeOtb> ThingTypeOtbPtr;
+typedef std::shared_ptr<House> HousePtr;
+typedef std::shared_ptr<Town> TownPtr;
 
 typedef std::vector<ThingPtr> ThingList;
 typedef std::vector<ThingTypeDatPtr> ThingTypeDatList;
 typedef std::vector<ThingTypeOtbPtr> ThingTypeOtbList;
+typedef std::vector<HousePtr> HouseList;
+typedef std::vector<TownPtr> TownList;
 
 #endif
diff --git a/src/otclient/core/item.cpp b/src/otclient/core/item.cpp
index df5799fb..c9fbc41f 100644
--- a/src/otclient/core/item.cpp
+++ b/src/otclient/core/item.cpp
@@ -45,14 +45,14 @@ Item::Item() :
 
 ItemPtr Item::create(int id)
 {
-    ItemPtr item = ItemPtr(new Item);
+    ItemPtr item(new Item);
     item->setId(id);
     return item;
 }
 
 ItemPtr Item::createFromOtb(int id)
 {
-    ItemPtr item = ItemPtr(new Item);
+    ItemPtr item(new Item);
     item->setOtbId(id);
     return item;
 }
@@ -204,70 +204,71 @@ bool Item::isValid()
     return g_things.isValidDatId(m_id, DatItemCategory);
 }
 
-bool Item::unserializeAttr(const BinaryTreePtr &fin)
+void Item::unserializeItem(const BinaryTreePtr &in)
 {
-    uint8 attrType;
-    while ((attrType = fin->getU8()) != 0)
-        readAttr((AttrTypes_t)attrType, fin);
+    // Yet another TODO.
+    while (in->canRead()) {
+        uint8 attrType = in->getU8();
+        if (attrType == 0)
+            break;
 
-    return true;
-}
-
-void Item::readAttr(AttrTypes_t attrType, const BinaryTreePtr &fin)
-{
-    switch (attrType) {
-    case ATTR_COUNT:
-        setSubType(fin->getU8());
-        break;
-    case ATTR_ACTION_ID:
-        setActionId(fin->getU16());
-        break;
-    case ATTR_UNIQUE_ID:
-        setUniqueId(fin->getU16());
-        break;
-    case ATTR_NAME:
-        setName(fin->getString());
-        break;
-    case ATTR_ARTICLE:
-        fin->getString();
-    case ATTR_ATTACK: // \/ not needed.
-    case ATTR_EXTRAATTACK:
-    case ATTR_DEFENSE:
-    case ATTR_EXTRADEFENSE:
-    case ATTR_ARMOR:
-    case ATTR_ATTACKSPEED:
-    case ATTR_HPPITCHANCE:
-    case ATTR_DURATION:
-        fin->getU32();
-        break;
-    case ATTR_SCRIPTPROTECTED:
-    case ATTR_DUALWIELD:
-    case ATTR_DECAYING_STATE:
-    case ATTR_HPPOUSEDOORID:
-        fin->getU8();
-        break;
-    case ATTR_TEXT:
-        setText(fin->getString());
-        break;
-    case ATTR_WRITTENDATE:
-        fin->getU32();
-        break;
-    case ATTR_WRITTENBY:
-        fin->getString();
-        break;
-    case ATTR_DESC:
-        setDescription(fin->getString());
-        break;
-    case ATTR_RUNE_CHARGES:
-        fin->getU8();
-        break;
-    case ATTR_TELE_DEST: // Teleport should read that.
-    case ATTR_SLEEPERGUID: // Bed should read that.
-    case ATTR_SLEEPSTART:
-    case ATTR_CONTAINER_ITEMS:
-    case ATTR_ATTRIBUTE_MAP:
-    default:
-        break;
+        switch ((AttrTypes_t)attrType) {
+            case ATTR_COUNT:
+                setSubType(in->getU8());
+                break;
+            case ATTR_CHARGES:
+                setSubType(in->getU16());
+                break;
+            case ATTR_ACTION_ID:
+                setActionId(in->getU16());
+                break;
+            case ATTR_UNIQUE_ID:
+                setUniqueId(in->getU16());
+                break;
+            case ATTR_NAME:
+                setName(in->getString());
+                break;
+            case ATTR_ARTICLE: // ?
+            case ATTR_WRITTENBY:
+            case ATTR_DESC:
+                in->getString();
+                break;
+            case ATTR_ATTACK:
+            case ATTR_EXTRAATTACK:
+            case ATTR_DEFENSE:
+            case ATTR_EXTRADEFENSE:
+            case ATTR_ARMOR:
+            case ATTR_ATTACKSPEED:
+            case ATTR_HITCHANCE:
+            case ATTR_DURATION:
+            case ATTR_WRITTENDATE:
+            case ATTR_SLEEPERGUID:
+            case ATTR_SLEEPSTART:
+            case ATTR_CONTAINER_ITEMS:
+            case ATTR_ATTRIBUTE_MAP:
+                in->skip(4);
+                break;
+            case ATTR_SCRIPTPROTECTED:
+            case ATTR_DUALWIELD:
+            case ATTR_DECAYING_STATE:
+            case ATTR_HOUSEDOORID:
+            case ATTR_RUNE_CHARGES:
+                in->skip(1);
+                break;
+            case ATTR_TEXT:
+                setText(in->getString());
+                break;
+            case ATTR_DEPOT_ID:
+                in->skip(2); // trolol
+                break;
+            case ATTR_TELE_DEST:
+            {
+                Position pos(in->getU16(), in->getU16(), in->getU8());
+                break;
+            }
+            default:
+                stdext::throw_exception(stdext::format("invalid item attribute %d", (int)attrType));
+        }
     }
 }
 
diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h
index a66997dd..6263f1db 100644
--- a/src/otclient/core/item.h
+++ b/src/otclient/core/item.h
@@ -29,8 +29,6 @@
 
 enum AttrTypes_t {
     ATTR_END = 0,
-    //ATTR_DESCRIPTION = 1,
-    //ATTR_EXT_FILE = 2,
     ATTR_TILE_FLAGS = 3,
     ATTR_ACTION_ID = 4,
     ATTR_UNIQUE_ID = 5,
@@ -39,10 +37,8 @@ enum AttrTypes_t {
     ATTR_TELE_DEST = 8,
     ATTR_ITEM = 9,
     ATTR_DEPOT_ID = 10,
-    //ATTR_EXT_SPAWN_FILE = 11,
     ATTR_RUNE_CHARGES = 12,
-    //ATTR_EXT_HPPOUSE_FILE = 13,
-    ATTR_HPPOUSEDOORID = 14,
+    ATTR_HOUSEDOORID = 14,
     ATTR_COUNT = 15,
     ATTR_DURATION = 16,
     ATTR_DECAYING_STATE = 17,
@@ -60,7 +56,7 @@ enum AttrTypes_t {
     ATTR_EXTRADEFENSE = 36,
     ATTR_ARMOR = 37,
     ATTR_ATTACKSPEED = 38,
-    ATTR_HPPITCHANCE = 39,
+    ATTR_HITCHANCE = 39,
     ATTR_SHOOTRANGE = 40,
     ATTR_ARTICLE = 41,
     ATTR_SCRIPTPROTECTED = 42,
@@ -100,10 +96,7 @@ public:
     ItemPtr asItem() { return std::static_pointer_cast<Item>(shared_from_this()); }
     bool isItem() { return true; }
 
-    // TODO: These should be abstract and declared in i.e containers, doors, etc.
-    bool unserializeAttr(const BinaryTreePtr &fin);
-    bool unserializeItemNode(const BinaryTreePtr &fin) { return unserializeAttr(fin); }
-    void readAttr(AttrTypes_t attrType, const BinaryTreePtr &fin);
+    void unserializeItem(const BinaryTreePtr &in);
     bool isMoveable();
 
 private:
diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp
index bcdb37ab..20c5b96f 100644
--- a/src/otclient/core/map.cpp
+++ b/src/otclient/core/map.cpp
@@ -27,12 +27,14 @@
 #include "item.h"
 #include "missile.h"
 #include "statictext.h"
+#include "houses.h"
+#include "towns.h"
 
 #include <framework/core/eventdispatcher.h>
 #include "mapview.h"
 #include <framework/core/resourcemanager.h>
 #include <framework/core/filestream.h>
-#include <framework/core/binarytree.h>
+#include <framework/core/binarytree.h>	
 
 Map g_map;
 
@@ -59,67 +61,61 @@ void Map::notificateTileUpdateToMapViews(const Position& pos)
         mapView->onTileUpdate(pos);
 }
 
-bool Map::loadOtbm(const std::string& fileName)
+void Map::loadOtbm(const std::string& fileName, bool /*display*/)
 {
     FileStreamPtr fin = g_resources.openFile(fileName);
-    if (!fin) {
-        g_logger.error(stdext::format("Unable to load map '%s'", fileName));
-        return false;
-    }
+    if (!fin)
+        stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName));
 
-    if (!g_things.isOtbLoaded()/* || !g_things.isXmlLoaded()*/) {
-        g_logger.error("OTB and XML are not loaded yet to load a map.");
-        return false;
-    }
+    fin->cache();
+    if (!g_things.isOtbLoaded())
+        stdext::throw_exception("OTB isn't loaded yet to load a map.");
+
+    if (fin->getU32())
+        stdext::throw_exception("Unknown file version detected");
 
-    assert(fin->getU32() == 0);
     BinaryTreePtr root = fin->getBinaryTree();
-    root->skip(1); // Not sure, what exactly that is... perhaps node type?
+    if (root->getU8() != 0)
+        stdext::throw_exception("could not read root property!");
+
     uint32 headerVersion = root->getU32();
-    dump << headerVersion;
-    if (!headerVersion || headerVersion > 3) {
-        g_logger.error("Unknown OTBM version detected.");
-        return false;
-    }
+    if (!headerVersion || headerVersion > 3)
+        stdext::throw_exception(stdext::format("Unknown OTBM version detected: %u.", headerVersion));
+
+    uint16 w = root->getU16(), h = root->getU16();
+    dump << "Map size: " << w << "x" << h;
 
-    root->skip(1); // ??
     uint32 headerMajorItems = root->getU8();
-    dump << headerMajorItems;
-    dump << "major: " << g_things.getOtbMajorVersion();
-    if (headerMajorItems < 3) {
-        g_logger.error("This map needs to be upgraded.");
-        return false;
-    }
+    if (headerMajorItems < 3)
+        stdext::throw_exception(stdext::format("This map needs to be upgraded. read %d what it's supposed to be: %u",
+                                               headerMajorItems, g_things.getOtbMajorVersion()));
 
-    if (headerMajorItems > g_things.getOtbMajorVersion()) {
-        g_logger.error("This map was saved with different OTB version.");
-        return false;
-    }
+    if (headerMajorItems > g_things.getOtbMajorVersion())
+        stdext::throw_exception(stdext::format("This map was saved with different OTB version. read %d what it's supposed to be: %d",
+                                               headerMajorItems, g_things.getOtbMajorVersion()));
 
-    root->skip(1); /// ??
-    uint32 headerMinorItems =  root->getU8();
-    dump << headerMinorItems;
-    dump << "minor: " << g_things.getOtbMinorVersion();
+    root->skip(3);
+    uint32 headerMinorItems =  root->getU32();
     if (headerMinorItems > g_things.getOtbMinorVersion())
-        g_logger.warning("This map needs an updated OTB.");
+        g_logger.warning(stdext::format("This map needs an updated OTB. read %d what it's supposed to be: %d",
+                                        headerMinorItems, g_things.getOtbMinorVersion()));
 
     BinaryTreePtr node = root->getChildren()[0];
-    if (node->getU8() != OTBM_MAP_DATA) {
-        g_logger.error("Could not read data node.");
-        return false;
-    }
+    if (node->getU8() != OTBM_MAP_DATA)
+        stdext::throw_exception("Could not read root data node");
 
-    std::string tmp;
-    uint8 attribute;
+    Boolean<true> first;
     while (node->canRead()) {
-        attribute = node->getU8();
-        tmp = node->getString();
+        uint8 attribute = node->getU8();
+        std::string tmp = node->getString();
         switch (attribute) {
         case OTBM_ATTR_DESCRIPTION:
-            if (!m_description.empty())
-                m_description += "\n" + tmp;
-            else
+            if (first) {
+                first = false;
                 m_description = tmp;
+            } else {
+                m_description += "\n" + tmp;
+            }
             break;
         case OTBM_ATTR_SPAWN_FILE:
             m_spawnFile = fileName.substr(0, fileName.rfind('/') + 1) + tmp;
@@ -128,151 +124,130 @@ bool Map::loadOtbm(const std::string& fileName)
             m_houseFile = fileName.substr(0, fileName.rfind('/') + 1) + tmp;
             break;
         default:
-            g_logger.error(stdext::format("Invalid attribute '%c'", attribute));
-            break;
+            stdext::throw_exception(stdext::format("Invalid attribute '%c'", attribute));
         }
     }
 
-    dump << m_description;
-    dump << m_houseFile;
-    dump << m_spawnFile;
-
     for (const BinaryTreePtr &nodeMapData : node->getChildren()) {
         uint8 mapDataType = nodeMapData->getU8();
-        dump << "mapData: " << (int)mapDataType;
         if (mapDataType == OTBM_TILE_AREA) {
-            dump << "Tile: reading pos...";
             uint16 baseX = nodeMapData->getU16(), baseY = nodeMapData->getU16();
             uint8 pz = nodeMapData->getU8();
 
             for (const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) {
                 uint8 type = nodeTile->getU8();
                 if (type == OTBM_TILE || type == OTBM_HOUSETILE) {
-                    TilePtr tile = 0;
-                    ItemPtr ground = 0;
+                    TilePtr tile = nullptr;
+                    ItemPtr ground = nullptr;
+                    HousePtr house = nullptr;
                     uint32 flags = 0;
 
-                    uint16 px = baseX + nodeTile->getU16(), py = baseY + nodeTile->getU16();
+                    uint16 px = baseX + nodeTile->getU8(), py = baseY + nodeTile->getU8();
                     Position pos(px, py, pz);
 
-                    // TODO: Houses.
                     if (type ==  OTBM_HOUSETILE) {
                         uint32 hId = nodeTile->getU32();
-
                         tile = createTile(pos);
-                        // TODO: add it to house.
+                        if (!(house = g_houses.getHouse(hId)))
+                            house = HousePtr(new House(hId));
+                        house->setTile(tile);
                     }
 
-                    uint8 tileAttr;
                     while (nodeTile->canRead()) {
-                        tileAttr = nodeTile->getU8();
+                        uint8 tileAttr = nodeTile->getU8();
                         switch (tileAttr) {
-                        case OTBM_ATTR_TILE_FLAGS: {
-                            uint32 _flags = nodeTile->getU32();
+                            case OTBM_ATTR_TILE_FLAGS: {
+                                uint32 _flags = nodeTile->getU32();
+                                if ((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
+                                    flags |= TILESTATE_PROTECTIONZONE;
+                                else if ((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
+                                    flags |= TILESTATE_OPTIONALZONE;
+                                else if ((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE)
+                                    flags |= TILESTATE_HARDCOREZONE;
 
-                            if ((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
-                                flags |= TILESTATE_PROTECTIONZONE;
-                            else if ((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
-                                flags |= TILESTATE_OPTIONALZONE;
-                            else if ((_flags & TILESTATE_HPPARDCOREZONE) == TILESTATE_HPPARDCOREZONE)
-                                flags |= TILESTATE_HPPARDCOREZONE;
+                                if ((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
+                                    flags |= TILESTATE_NOLOGOUT;
 
-                            if ((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
-                                flags |= TILESTATE_NOLOGOUT;
+                                if ((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
+                                    flags |= TILESTATE_REFRESH;
 
-                            if ((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
-                                flags |= TILESTATE_REFRESH;
-                            break;
-                        } case OTBM_ATTR_ITEM: {
-                            ItemPtr item = Item::createFromOtb(nodeTile->getU16());
-                            if (!item) {
-                                g_logger.error(stdext::format("failed to create new item at tile pos %d, %d, %d", px, py, pz));
-                                return false;
+                                break;
                             }
-
-                            if (tile) {
-                                tile->addThing(item);
-                            } else if (item->isGround()) {
-                                ground = item;
-                            } else {
-                                tile = createTile(pos);
-                                tile->addThing(ground);
-                                tile->addThing(item);
+                            case OTBM_ATTR_ITEM: {
+                                ItemPtr item = Item::createFromOtb(nodeTile->getU16());
+                                if (tile)
+                                    tile->addThing(item);
+                                else if (item->isGround())
+                                    ground = item;
+                                else {
+                                    tile = createTile(pos, ground);
+                                    tile->addThing(item);
+                                }
+                                break;
                             }
-                        } default: {
-                            g_logger.error(stdext::format("invalid tile attribute at pos %d, %d, %d", px, py, pz));
-                            return false;
-                        }
+                            default:
+                                stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %d, %d, %d",
+                                                                       (int)tileAttr, px, py, pz));
                         }
                     }
 
                     for (const BinaryTreePtr &nodeItem : nodeTile->getChildren()) {
                         if (nodeItem->getU8() == OTBM_ITEM) {
                             ItemPtr item = Item::createFromOtb(nodeItem->getU16());
-                            if (!item) {
-                                g_logger.error(stdext::format("failed to create new item at pos %d, %d, %d", px, py, pz));
-                                return false;
-                            }
-
-                            if (item->unserializeItemNode(nodeItem)) {
-                                if (/* house  && */item->isMoveable()) {
-                                    g_logger.warning(stdext::format("Moveable item found in house: %d at pos %d %d %d", item->getId(),
-                                                                    px, py, pz));
-                                    item = nullptr;
-                                } else if (tile) {
-                                    tile->addThing(item);
-                                } else if (item->isGround()) {
-                                    ground = item;
-                                } else {
-                                    tile = createTile(pos);
-                                    tile->addThing(ground);
-                                    tile->addThing(item);
-                                }
+                            item->unserializeItem(nodeItem);
+                            if (house  && item->isMoveable()) {
+                                g_logger.warning(stdext::format("Moveable item found in house: %d at pos %d %d %d", item->getId(),
+                                                                px, py, pz));
+                                item = nullptr;
+                            } else if (tile) {
+                                tile->addThing(item);
+                            } else if (item->isGround()) {
+                                ground = item;
                             } else {
-                                g_logger.error(stdext::format("failed to unserialize item with %d at pos %d %d %d", item->getId(),
-                                                              px, py, pz));
-                                return false;
+                                tile = createTile(pos, ground);
+                                tile->addThing(item);
                             }
-                        } else {
-                            g_logger.error(stdext::format("Unknown item node type %d", type));
-                            return false;
-                        }
+                        } else
+                            stdext::throw_exception("Unknown item node");
                     }
 
-                    if (!tile) {
-                        tile = createTile(pos);
-                        tile->addThing(ground);
-                    }
+                    if (!tile)
+                        tile = createTile(pos, ground);
 
                     tile->setFlags((tileflags_t)flags);
-                } else {
-                    g_logger.error(stdext::format("Unknown tile node type %d", type));
-                    return false;
-                }
+                } else
+                    stdext::throw_exception(stdext::format("Unknown tile node type %d", type));
             }
         } else if (mapDataType == OTBM_TOWNS) {
+            TownPtr town = nullptr;
             for (const BinaryTreePtr &nodeTown : nodeMapData->getChildren()) {
                 if (nodeTown->getU8() == OTBM_TOWN) {
                     uint32 townId = nodeTown->getU32();
                     std::string townName = nodeTown->getString();
-
                     Position townCoords(nodeTown->getU16(), nodeTown->getU16(), nodeTown->getU8());
+                    if (!(town = g_towns.getTown(townId)))
+                        town = TownPtr(new Town(townId, townName, townCoords));
                 } else
-                    return false;
+                    stdext::throw_exception("invalid town node");
             }
         } else if (mapDataType == OTBM_WAYPOINTS && headerVersion > 1) {
             for (const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) {
                 if (nodeWaypoint->getU8() == OTBM_WAYPOINT) {
                     std::string name = nodeWaypoint->getString();
                     Position waypointPos(nodeWaypoint->getU16(), nodeWaypoint->getU16(), nodeWaypoint->getU8());
-                }
+                    if (waypointPos.isValid() && !name.empty())
+                        m_waypoints.insert(std::make_pair(waypointPos, name));
+
+                } else
+                    stdext::throw_exception("invalid waypoint node");
             }
         } else
-            return false;
+            stdext::throw_exception("Unknown map data node");
     }
 
-    // TODO: Load house & spawns.
-    return true;
+    g_logger.debug("OTBM read successfully.");
+    fin->close();
+    /// TODO read XML Stuff (houses & spawns).
 }
 
 bool Map::loadOtcm(const std::string& fileName)
@@ -300,7 +275,7 @@ bool Map::loadOtcm(const std::string& fileName)
                 fin->getString(); // world name
 
                 if(datSignature != g_things.getDatSignature())
-                    g_logger.warning("otcm map loaded is was created with a different dat signature");
+                    g_logger.warning("otcm map loaded was created with a different dat signature");
 
                 break;
             }
@@ -417,6 +392,7 @@ void Map::clean()
 {
     cleanDynamicThings();
     m_tiles.clear();
+    m_waypoints.clear();
 }
 
 void Map::cleanDynamicThings()
@@ -538,9 +514,12 @@ bool Map::removeThingByPos(const Position& pos, int stackPos)
     return false;
 }
 
-TilePtr Map::createTile(const Position& pos)
+TilePtr Map::createTile(const Position& pos, const ItemPtr &g)
 {
     TilePtr tile = TilePtr(new Tile(pos));
+    if (g)
+        tile->addThing(g);
+
     m_tiles[pos] = tile;
     return tile;
 }
diff --git a/src/otclient/core/map.h b/src/otclient/core/map.h
index 029d6fe6..4b79ff91 100644
--- a/src/otclient/core/map.h
+++ b/src/otclient/core/map.h
@@ -42,7 +42,7 @@ enum OTBM_AttrTypes_t
     OTBM_ATTR_SPAWN_FILE = 11,
     OTBM_ATTR_RUNE_CHARGES = 12,
     OTBM_ATTR_HOUSE_FILE = 13,
-    OTBM_ATTR_HPPOUSEDOORID = 14,
+    OTBM_ATTR_HOUSEDOORID = 14,
     OTBM_ATTR_COUNT = 15,
     OTBM_ATTR_DURATION = 16,
     OTBM_ATTR_DECAYING_STATE = 17,
@@ -77,7 +77,7 @@ enum OTBM_NodeTypes_t
 
 enum {
     OTCM_SIGNATURE = 0x4D43544F,
-    OTCM_VERSION = 1,
+    OTCM_VERSION = 1
 };
 
 //@bindsingleton g_map
@@ -93,7 +93,7 @@ public:
     bool loadOtcm(const std::string& fileName);
     void saveOtcm(const std::string& fileName);
 
-    bool loadOtbm(const std::string& fileName);
+    void loadOtbm(const std::string& fileName, bool display = false/* temporary*/);
     //void saveOtbm(const std::string& fileName);
 
     void clean();
@@ -106,7 +106,7 @@ public:
     bool removeThingByPos(const Position& pos, int stackPos);
 
     // tile related
-    TilePtr createTile(const Position& pos);
+    TilePtr createTile(const Position& pos, const ItemPtr &g = nullptr);
     const TilePtr& getTile(const Position& pos);
     TilePtr getOrCreateTile(const Position& pos);
     void cleanTile(const Position& pos);
@@ -146,6 +146,7 @@ private:
     std::vector<AnimatedTextPtr> m_animatedTexts;
     std::vector<StaticTextPtr> m_staticTexts;
     std::vector<MapViewPtr> m_mapViews;
+    std::unordered_map<Position, std::string, PositionHasher> m_waypoints;
 
     Light m_light;
     Position m_centralPosition;
diff --git a/src/otclient/core/thingtypemanager.cpp b/src/otclient/core/thingtypemanager.cpp
index 1ea9ad8f..6782076f 100644
--- a/src/otclient/core/thingtypemanager.cpp
+++ b/src/otclient/core/thingtypemanager.cpp
@@ -30,9 +30,6 @@
 #include <framework/core/filestream.h>
 #include <framework/core/binarytree.h>
 
-#define TIXML_USE_STL // use STL strings instead.
-#include <framework/thirdparty/tinyxml.h>
-
 ThingTypeManager g_things;
 
 void ThingTypeManager::init()
@@ -112,9 +109,11 @@ bool ThingTypeManager::loadOtb(const std::string& file)
         root->getU32(); // build number
         root->skip(128); // description
 
+        m_otbTypes.resize(root->getChildren().size(), m_nullOtbType);
         for(const BinaryTreePtr& node : root->getChildren()) {
             ThingTypeOtbPtr otbType(new ThingTypeOtb);
             otbType->unserialize(node);
+            addOtbType(otbType);
         }
 
         m_otbLoaded = true;
diff --git a/src/otclient/core/tile.h b/src/otclient/core/tile.h
index 586502d2..013f7e9e 100644
--- a/src/otclient/core/tile.h
+++ b/src/otclient/core/tile.h
@@ -33,7 +33,7 @@ enum tileflags_t
     TILESTATE_TRASHED = 1 << 1,
     TILESTATE_OPTIONALZONE = 1 << 2,
     TILESTATE_NOLOGOUT = 1 << 3,
-    TILESTATE_HPPARDCOREZONE = 1 << 4,
+    TILESTATE_HARDCOREZONE = 1 << 4,
     TILESTATE_REFRESH = 1 << 5,
 
     // internal usage

From 992cbf9d1d3a1021883e3a35153a7cb7f1ceea24 Mon Sep 17 00:00:00 2001
From: niczkx <fallen@.(none)>
Date: Sat, 14 Jul 2012 16:20:38 -0700
Subject: [PATCH 2/3] mapeditor changes

---
 TODO                                          |   2 +-
 init.lua                                      |  12 +-
 src/framework/application.h                   |   1 +
 src/framework/const.h                         |  16 +-
 src/framework/core/filestream.h               |   2 +
 src/framework/luafunctions.cpp                |   1 +
 src/framework/pch.h                           |   2 +-
 src/otclient/core/item.cpp                    |  44 ++-
 src/otclient/core/item.h                      |  13 +-
 src/otclient/core/map.cpp                     | 347 ++++++++++++------
 src/otclient/core/map.h                       |  37 +-
 src/otclient/core/thingtypemanager.cpp        | 178 +++------
 src/otclient/core/thingtypemanager.h          |   4 +-
 src/otclient/core/thingtypeotb.cpp            |  11 +
 src/otclient/core/thingtypeotb.h              |  13 +-
 src/otclient/core/tile.h                      |   1 +
 src/otclient/luafunctions.cpp                 |  37 ++
 .../generate_bitmap_font.py                   |   0
 .../generate_lua_bindings.lua                 |   0
 tools/pkgs/makeotc                            |   0
 20 files changed, 461 insertions(+), 260 deletions(-)
 mode change 100755 => 100644 tools/gimp-bitmap-generator/generate_bitmap_font.py
 mode change 100755 => 100644 tools/lua-binding-generator/generate_lua_bindings.lua
 mode change 100755 => 100644 tools/pkgs/makeotc

diff --git a/TODO b/TODO
index 0a208716..1537544c 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
 == MODULES
 game_shaders (with shader manager)
-game_map (with save/load/options)
+game_map (with save/load/option)
 game_minimap (with all tibia functionality)
 
 == NOTABLE FEATURES
diff --git a/init.lua b/init.lua
index deddc3b5..3a3982dc 100644
--- a/init.lua
+++ b/init.lua
@@ -5,7 +5,7 @@
 g_logger.setLogFile(g_resources.getWorkDir() .. g_app.getCompactName() .. ".log")
 
 -- print first terminal message
-g_logger.info(g_app.getName() .. ' ' .. g_app.getVersion() .. ' rev ' .. g_app.getBuildRevision() .. ' (' .. g_app.getBuildCommit() .. ') built on ' .. g_app.getBuildDate())
+g_logger.info(g_app.getName() .. ' ' .. g_app.getVersion() .. ' rev ' .. g_app.getBuildRevision() .. ' (' .. g_app.getBuildCommit() .. ') built on ' .. g_app.getBuildDate() .. ' for arch ' .. g_app.getBuildArch())
 
 --add base folder to search path
 g_resources.addToSearchPath(g_resources.getWorkDir())
@@ -27,15 +27,15 @@ g_configs.load("/config.otml")
 g_modules.discoverModules()
 
 -- core modules 0-99
-g_modules.autoLoadModules(99);
+g_modules.autoLoadModules(99)
 g_modules.ensureModuleLoaded("corelib")
 
 -- client modules 100-499
-g_modules.autoLoadModules(499);
+g_modules.autoLoadModules(499)
 g_modules.ensureModuleLoaded("client")
 
 -- game modules 500-999
-g_modules.autoLoadModules(999);
+g_modules.autoLoadModules(999)
 g_modules.ensureModuleLoaded("game")
 
 -- addons 1000-9999
@@ -45,7 +45,3 @@ if g_resources.fileExists("/otclientrc.lua") then
   dofile("/otclientrc.lua")
 end
 
-g_things.loadOtb("/items.otb")
-g_map.loadOtbm("/forgotten.otbm")
-
-
diff --git a/src/framework/application.h b/src/framework/application.h
index d74909c0..910189a2 100644
--- a/src/framework/application.h
+++ b/src/framework/application.h
@@ -71,6 +71,7 @@ public:
     std::string getBuildRevision() { return BUILD_REVISION; }
     std::string getBuildCommit() { return BUILD_COMMIT; }
     std::string getBuildType() { return BUILD_TYPE; }
+    std::string getBuildArch() { return BUILD_ARCH; }
     std::string getStartupOptions() { return m_startupOptions; }
 
 protected:
diff --git a/src/framework/const.h b/src/framework/const.h
index cb7f2d2d..f103b0cc 100644
--- a/src/framework/const.h
+++ b/src/framework/const.h
@@ -29,8 +29,8 @@
 #define BUILD_COMPILER "gcc " __VERSION__
 #define BUILD_DATE __DATE__
 
-    #ifndef BUILD_COMMIT
-#define BUILD_COMMIT "custom"
+#ifndef BUILD_COMMIT
+#define BUILD_COMMIT "devel"
 #endif
 
 #ifndef BUILD_REVISION
@@ -41,6 +41,16 @@
 #define BUILD_TYPE "unknown"
 #endif
 
+#ifndef BUILD_ARCH
+#if defined(__amd64) || defined(_M_X64)
+#define BUILD_ARCH "x64"
+#elif defined(__i386) || defined(_M_IX86) || defined(_X86_)
+#define BUILD_ARCH "X86"
+#else
+#define BUILD_ARCH "unknown"
+#endif
+#endif
+
 namespace Fw
 {
     constexpr float pi = 3.14159265;
@@ -207,7 +217,7 @@ namespace Fw
         AnchorLeft,
         AnchorRight,
         AnchorVerticalCenter,
-        AnchorHorizontalCenter,
+        AnchorHorizontalCenter
     };
 
     enum FocusReason {
diff --git a/src/framework/core/filestream.h b/src/framework/core/filestream.h
index b55426f8..44801c2d 100644
--- a/src/framework/core/filestream.h
+++ b/src/framework/core/filestream.h
@@ -59,6 +59,8 @@ public:
     void addU32(uint32 v);
     void addU64(uint64 v);
     void addString(const std::string& v);
+    void startNode(uint8 nodeType) { addU8(0xFE); addU8(nodeType); }
+    void endNode() { addU8(0xFF); }
 
     FileStreamPtr asFileStream() { return std::static_pointer_cast<FileStream>(shared_from_this()); }
 
diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp
index 8890a539..34a7e001 100644
--- a/src/framework/luafunctions.cpp
+++ b/src/framework/luafunctions.cpp
@@ -501,6 +501,7 @@ void Application::registerLuaFunctions()
     g_lua.bindSingletonFunction("g_app", "getBuildRevision", &Application::getBuildRevision, &g_app);
     g_lua.bindSingletonFunction("g_app", "getBuildCommit", &Application::getBuildCommit, &g_app);
     g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, &g_app);
+    g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, &g_app);
     g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, &g_app);
 
     // ConfigManager
diff --git a/src/framework/pch.h b/src/framework/pch.h
index a27c02ee..222768c4 100644
--- a/src/framework/pch.h
+++ b/src/framework/pch.h
@@ -64,7 +64,7 @@
 #include <boost/algorithm/string.hpp>
 
 // tiny XML
-#define TIXML_USE_STL
+#define TIXML_USE_STL // use std::string's instead
 #include <framework/thirdparty/tinyxml.h>
 
 #endif
diff --git a/src/otclient/core/item.cpp b/src/otclient/core/item.cpp
index c9fbc41f..f32b6749 100644
--- a/src/otclient/core/item.cpp
+++ b/src/otclient/core/item.cpp
@@ -26,6 +26,9 @@
 #include "thing.h"
 #include "tile.h"
 #include "shadermanager.h"
+#include "container.h"
+#include "map.h"
+#include "houses.h"
 
 #include <framework/core/clock.h>
 #include <framework/core/eventdispatcher.h>
@@ -206,12 +209,12 @@ bool Item::isValid()
 
 void Item::unserializeItem(const BinaryTreePtr &in)
 {
-    // Yet another TODO.
     while (in->canRead()) {
         uint8 attrType = in->getU8();
         if (attrType == 0)
             break;
 
+        // fugly switch yes?
         switch ((AttrTypes_t)attrType) {
             case ATTR_COUNT:
                 setSubType(in->getU8());
@@ -228,9 +231,31 @@ void Item::unserializeItem(const BinaryTreePtr &in)
             case ATTR_NAME:
                 setName(in->getString());
                 break;
-            case ATTR_ARTICLE: // ?
-            case ATTR_WRITTENBY:
+            case ATTR_TEXT:
+                setText(in->getString());
+                break;
             case ATTR_DESC:
+                m_description = in->getString();
+                break;
+            case ATTR_CONTAINER_ITEMS:
+                m_isContainer = true;
+                in->skip(4);
+                break;
+            case ATTR_HOUSEDOORID:
+                m_isDoor = true;
+                m_doorId = in->getU8();
+                break;
+            case ATTR_DEPOT_ID:
+                m_depotId = in->getU16();
+                break;
+            case ATTR_TELE_DEST: {
+                m_teleportDestination.x = in->getU16();
+                m_teleportDestination.y = in->getU16();
+                m_teleportDestination.z = in->getU8();
+                break;
+            }
+            case ATTR_ARTICLE:
+            case ATTR_WRITTENBY:
                 in->getString();
                 break;
             case ATTR_ATTACK:
@@ -244,28 +269,15 @@ void Item::unserializeItem(const BinaryTreePtr &in)
             case ATTR_WRITTENDATE:
             case ATTR_SLEEPERGUID:
             case ATTR_SLEEPSTART:
-            case ATTR_CONTAINER_ITEMS:
             case ATTR_ATTRIBUTE_MAP:
                 in->skip(4);
                 break;
             case ATTR_SCRIPTPROTECTED:
             case ATTR_DUALWIELD:
             case ATTR_DECAYING_STATE:
-            case ATTR_HOUSEDOORID:
             case ATTR_RUNE_CHARGES:
                 in->skip(1);
                 break;
-            case ATTR_TEXT:
-                setText(in->getString());
-                break;
-            case ATTR_DEPOT_ID:
-                in->skip(2); // trolol
-                break;
-            case ATTR_TELE_DEST:
-            {
-                Position pos(in->getU16(), in->getU16(), in->getU8());
-                break;
-            }
             default:
                 stdext::throw_exception(stdext::format("invalid item attribute %d", (int)attrType));
         }
diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h
index 6263f1db..0f22bd82 100644
--- a/src/otclient/core/item.h
+++ b/src/otclient/core/item.h
@@ -27,7 +27,8 @@
 #include "thing.h"
 #include "thingtypeotb.h"
 
-enum AttrTypes_t {
+enum AttrTypes_t
+{
     ATTR_END = 0,
     ATTR_TILE_FLAGS = 3,
     ATTR_ACTION_ID = 4,
@@ -82,6 +83,7 @@ public:
     void setSubType(int subType) { m_countOrSubType = subType; }
     void setActionId(int actionId) { m_actionId = actionId; }
     void setUniqueId(int uniqueId) { m_uniqueId = uniqueId; }
+    void setDoorId(int doorId) { m_doorId = doorId; }
     void setName(const std::string &name) { m_name = name; }
     void setText(const std::string &text) { m_text = text; }
     void setDescription(const std::string &description) { m_description = description; }
@@ -91,6 +93,7 @@ public:
     int getCount() { return m_countOrSubType; }
     uint32 getId() { return m_id; }
     std::string getName() { return m_name; }
+    uint8 getDoorId() { return m_doorId; }
     bool isValid();
 
     ItemPtr asItem() { return std::static_pointer_cast<Item>(shared_from_this()); }
@@ -98,14 +101,22 @@ public:
 
     void unserializeItem(const BinaryTreePtr &in);
     bool isMoveable();
+    bool isContainer() { return m_isContainer; }
+    bool isDoor() { return m_isDoor; }
 
 private:
     uint16 m_id;
     uint8 m_countOrSubType;
     uint32 m_actionId, m_uniqueId;
+    uint16 m_depotId;
+    uint8 m_doorId;
+    Boolean<false> m_isContainer;
+    Boolean<false> m_isDoor;
+
     std::string m_name, m_text, m_description;
     PainterShaderProgramPtr m_shaderProgram;
     ThingTypeOtbPtr m_otbType;
+    Position m_teleportDestination;
 };
 
 #endif
diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp
index 20c5b96f..501d3d55 100644
--- a/src/otclient/core/map.cpp
+++ b/src/otclient/core/map.cpp
@@ -27,14 +27,13 @@
 #include "item.h"
 #include "missile.h"
 #include "statictext.h"
-#include "houses.h"
-#include "towns.h"
 
 #include <framework/core/eventdispatcher.h>
 #include "mapview.h"
 #include <framework/core/resourcemanager.h>
 #include <framework/core/filestream.h>
 #include <framework/core/binarytree.h>	
+#include <framework/application.h>
 
 Map g_map;
 
@@ -61,7 +60,7 @@ void Map::notificateTileUpdateToMapViews(const Position& pos)
         mapView->onTileUpdate(pos);
 }
 
-void Map::loadOtbm(const std::string& fileName, bool /*display*/)
+void Map::loadOtbm(const std::string& fileName)
 {
     FileStreamPtr fin = g_resources.openFile(fileName);
     if (!fin)
@@ -75,47 +74,45 @@ void Map::loadOtbm(const std::string& fileName, bool /*display*/)
         stdext::throw_exception("Unknown file version detected");
 
     BinaryTreePtr root = fin->getBinaryTree();
-    if (root->getU8() != 0)
+    if (root->getU8())
         stdext::throw_exception("could not read root property!");
 
     uint32 headerVersion = root->getU32();
     if (!headerVersion || headerVersion > 3)
         stdext::throw_exception(stdext::format("Unknown OTBM version detected: %u.", headerVersion));
 
-    uint16 w = root->getU16(), h = root->getU16();
-    dump << "Map size: " << w << "x" << h;
+    m_width = root->getU16();
+    m_height = root->getU16();
+    dump << "Map size: " << m_width << "x" << m_height;
 
     uint32 headerMajorItems = root->getU8();
-    if (headerMajorItems < 3)
+    if (headerMajorItems < 3) {
         stdext::throw_exception(stdext::format("This map needs to be upgraded. read %d what it's supposed to be: %u",
                                                headerMajorItems, g_things.getOtbMajorVersion()));
+    }
 
-    if (headerMajorItems > g_things.getOtbMajorVersion())
+    if (headerMajorItems > g_things.getOtbMajorVersion()) {
         stdext::throw_exception(stdext::format("This map was saved with different OTB version. read %d what it's supposed to be: %d",
                                                headerMajorItems, g_things.getOtbMajorVersion()));
+    }
 
     root->skip(3);
     uint32 headerMinorItems =  root->getU32();
-    if (headerMinorItems > g_things.getOtbMinorVersion())
+    if (headerMinorItems > g_things.getOtbMinorVersion()) {
         g_logger.warning(stdext::format("This map needs an updated OTB. read %d what it's supposed to be: %d",
                                         headerMinorItems, g_things.getOtbMinorVersion()));
+    }
 
     BinaryTreePtr node = root->getChildren()[0];
     if (node->getU8() != OTBM_MAP_DATA)
         stdext::throw_exception("Could not read root data node");
 
-    Boolean<true> first;
     while (node->canRead()) {
         uint8 attribute = node->getU8();
         std::string tmp = node->getString();
         switch (attribute) {
         case OTBM_ATTR_DESCRIPTION:
-            if (first) {
-                first = false;
-                m_description = tmp;
-            } else {
-                m_description += "\n" + tmp;
-            }
+            m_description += tmp + "\n";
             break;
         case OTBM_ATTR_SPAWN_FILE:
             m_spawnFile = fileName.substr(0, fileName.rfind('/') + 1) + tmp;
@@ -136,110 +133,131 @@ void Map::loadOtbm(const std::string& fileName, bool /*display*/)
 
             for (const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) {
                 uint8 type = nodeTile->getU8();
-                if (type == OTBM_TILE || type == OTBM_HOUSETILE) {
-                    TilePtr tile = nullptr;
-                    ItemPtr ground = nullptr;
-                    HousePtr house = nullptr;
-                    uint32 flags = 0;
+                if (type != OTBM_TILE && type != OTBM_HOUSETILE)
+                    stdext::throw_exception(stdext::format("invalid node tile type %d", (int)type));
 
-                    uint16 px = baseX + nodeTile->getU8(), py = baseY + nodeTile->getU8();
-                    Position pos(px, py, pz);
+                TilePtr tile = nullptr;
+                ItemPtr ground = nullptr;
+                HousePtr house = nullptr;
+                uint32 flags = TILESTATE_NONE;
 
-                    if (type ==  OTBM_HOUSETILE) {
-                        uint32 hId = nodeTile->getU32();
-                        tile = createTile(pos);
-                        if (!(house = g_houses.getHouse(hId)))
-                            house = HousePtr(new House(hId));
-                        house->setTile(tile);
+                uint16 px = baseX + nodeTile->getU8(), py = baseY + nodeTile->getU8();
+                Position pos(px, py, pz);
+
+                if (type ==  OTBM_HOUSETILE) {
+                    uint32 hId = nodeTile->getU32();
+                    tile = createTile(pos);
+                    if (!(house = m_houses.getHouse(hId))) {
+                        house = HousePtr(new House(hId));
+                        m_houses.addHouse(house);
                     }
+                    house->setTile(tile);
+                }
 
-                    while (nodeTile->canRead()) {
-                        uint8 tileAttr = nodeTile->getU8();
-                        switch (tileAttr) {
-                            case OTBM_ATTR_TILE_FLAGS: {
-                                uint32 _flags = nodeTile->getU32();
-                                if ((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
-                                    flags |= TILESTATE_PROTECTIONZONE;
-                                else if ((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
-                                    flags |= TILESTATE_OPTIONALZONE;
-                                else if ((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE)
-                                    flags |= TILESTATE_HARDCOREZONE;
+                while (nodeTile->canRead()) {
+                    uint8 tileAttr = nodeTile->getU8();
+                    switch (tileAttr) {
+                        case OTBM_ATTR_TILE_FLAGS: {
+                            uint32 _flags = nodeTile->getU32();
+                            if ((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
+                                flags |= TILESTATE_PROTECTIONZONE;
+                            else if ((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
+                                flags |= TILESTATE_OPTIONALZONE;
+                            else if ((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE)
+                                flags |= TILESTATE_HARDCOREZONE;
 
-                                if ((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
-                                    flags |= TILESTATE_NOLOGOUT;
+                            if ((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
+                                flags |= TILESTATE_NOLOGOUT;
 
-                                if ((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
-                                    flags |= TILESTATE_REFRESH;
+                            if ((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
+                                flags |= TILESTATE_REFRESH;
 
-                                break;
-                            }
-                            case OTBM_ATTR_ITEM: {
-                                ItemPtr item = Item::createFromOtb(nodeTile->getU16());
-                                if (tile)
-                                    tile->addThing(item);
-                                else if (item->isGround())
-                                    ground = item;
-                                else {
-                                    tile = createTile(pos, ground);
-                                    tile->addThing(item);
-                                }
-                                break;
-                            }
-                            default:
-                                stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %d, %d, %d",
-                                                                       (int)tileAttr, px, py, pz));
+                            break;
                         }
-                    }
-
-                    for (const BinaryTreePtr &nodeItem : nodeTile->getChildren()) {
-                        if (nodeItem->getU8() == OTBM_ITEM) {
-                            ItemPtr item = Item::createFromOtb(nodeItem->getU16());
-                            item->unserializeItem(nodeItem);
-                            if (house  && item->isMoveable()) {
-                                g_logger.warning(stdext::format("Moveable item found in house: %d at pos %d %d %d", item->getId(),
-                                                                px, py, pz));
-                                item = nullptr;
-                            } else if (tile) {
-                                tile->addThing(item);
-                            } else if (item->isGround()) {
+                        case OTBM_ATTR_ITEM: {
+                            ItemPtr item = Item::createFromOtb(nodeTile->getU16());
+                            if (tile)
+                                addThing(item, pos, 255);
+                            else if (item->isGround())
                                 ground = item;
-                            } else {
-                                tile = createTile(pos, ground);
-                                tile->addThing(item);
-                            }
-                        } else
-                            stdext::throw_exception("Unknown item node");
+                            else
+                                tile = createTileEx(pos, ground, item);
+                            break;
+                        }
+                        default:
+                            stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %d, %d, %d",
+                                                                   (int)tileAttr, px, py, pz));
+                    }
+                }
+
+                for (const BinaryTreePtr &nodeItem : nodeTile->getChildren()) {
+                    if (nodeItem->getU8() != OTBM_ITEM)
+                        stdext::throw_exception("invalid item node");
+
+                    ItemPtr item = Item::createFromOtb(nodeItem->getU16());
+                    item->unserializeItem(nodeItem);
+                    if (item->isContainer()) {
+                        // This is a temporary way for reading container items.
+                        MapContainerPtr mapContainer(new MapContainer);
+                        for (const BinaryTreePtr &insideItem : nodeItem->getChildren()) {
+                            if (insideItem->getU8() != OTBM_ITEM)
+                                stdext::throw_exception("invalid container item node");
+
+                            ItemPtr newItem = Item::createFromOtb(insideItem->getU16());
+                            newItem->unserializeItem(insideItem);
+                            mapContainer->add(newItem);
+                        }
+                        m_containers.push_back(mapContainer);
                     }
 
-                    if (!tile)
-                        tile = createTile(pos, ground);
+                    if (house) {
+                        if (item->isMoveable()) {
+                            g_logger.warning(stdext::format("Movable item found in house: %d at pos %d %d %d - escaping...", item->getId(),
+                                                            px, py, pz));
+                            item = nullptr;
+                        } else if (item->isDoor())
+                            house->addDoor(item->getDoorId(), pos);
+                    } else if (tile)
+                        addThing(item, pos, 255);
+                    else if (item->isGround())
+                        ground = item;
+                    else
+                        tile = createTileEx(pos, ground, item);
+                }
 
-                    tile->setFlags((tileflags_t)flags);
-                } else
-                    stdext::throw_exception(stdext::format("Unknown tile node type %d", type));
+                if (!tile)
+                    tile = createTileEx(pos, ground);
+
+                tile->setFlags((tileflags_t)flags);
             }
         } else if (mapDataType == OTBM_TOWNS) {
             TownPtr town = nullptr;
             for (const BinaryTreePtr &nodeTown : nodeMapData->getChildren()) {
-                if (nodeTown->getU8() == OTBM_TOWN) {
-                    uint32 townId = nodeTown->getU32();
-                    std::string townName = nodeTown->getString();
-                    Position townCoords(nodeTown->getU16(), nodeTown->getU16(), nodeTown->getU8());
-                    if (!(town = g_towns.getTown(townId)))
-                        town = TownPtr(new Town(townId, townName, townCoords));
-                } else
-                    stdext::throw_exception("invalid town node");
+                if (nodeTown->getU8() != OTBM_TOWN)
+                    stdext::throw_exception("invalid town node.");
+
+                uint32 townId = nodeTown->getU32();
+                std::string townName = nodeTown->getString();
+                Position townCoords(nodeTown->getU16(), nodeTown->getU16(), nodeTown->getU8());
+                if (!(town = m_towns.getTown(townId))) {
+                    town = TownPtr(new Town(townId, townName, townCoords));
+                    m_towns.addTown(town);
+                } else {
+                    // override data
+                    town->setName(townName);
+                    town->setPos(townCoords);
+                    town->setId(townId);
+                }
             }
         } else if (mapDataType == OTBM_WAYPOINTS && headerVersion > 1) {
             for (const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) {
-                if (nodeWaypoint->getU8() == OTBM_WAYPOINT) {
-                    std::string name = nodeWaypoint->getString();
-                    Position waypointPos(nodeWaypoint->getU16(), nodeWaypoint->getU16(), nodeWaypoint->getU8());
-                    if (waypointPos.isValid() && !name.empty())
-                        m_waypoints.insert(std::make_pair(waypointPos, name));
+                if (nodeWaypoint->getU8() != OTBM_WAYPOINT)
+                    stdext::throw_exception("invalid waypoint node.");
 
-                } else
-                    stdext::throw_exception("invalid waypoint node");
+                std::string name = nodeWaypoint->getString();
+                Position waypointPos(nodeWaypoint->getU16(), nodeWaypoint->getU16(), nodeWaypoint->getU8());
+                if (waypointPos.isValid() && !name.empty() && m_waypoints.find(waypointPos) == m_waypoints.end())
+                    m_waypoints.insert(std::make_pair(waypointPos, name));
             }
         } else
             stdext::throw_exception("Unknown map data node");
@@ -250,6 +268,115 @@ void Map::loadOtbm(const std::string& fileName, bool /*display*/)
     /// TODO read XML Stuff (houses & spawns).
 }
 
+void Map::saveOtbm(const std::string &fileName)
+{
+    // TODO: Continue sleepy work.
+#if 0
+    /// TODO: Use binary trees for this
+    FileStreamPtr fin = g_resources.openFile(fileName);
+    if(!fin)
+        stdext::throw_exception(stdext::format("failed to open file '%s'", fileName));
+
+    std::string dir;
+    if (fileName.find_last_of('/') <= 0)
+        dir = g_resources.getWorkDir();
+    else
+        dir = fileName.substr(0, fileName.find_last_of('/'));
+
+    if (m_houseFile.empty())
+        m_houseFile = "houses.xml";
+    if (m_spawnFile.empty())
+        m_spawnFile = "spawns.xml";
+
+#if 0
+    if (!m_houses->save(dir + "/" + m_houseFile))
+        ;
+    if (!m_spawns->save(dir + "/" + m_spawnFile))
+        ;
+#endif
+
+    uint32 ver;
+    if (g_things.getOtbMajorVersion() < 2)
+        ver =0;
+    else if (g_things.getOtbMajorVersion() < 10)
+        ver = 1;
+    else
+        ver = 2;
+
+    fin->addU32(0x00); // file version
+    {
+        fin->startNode(0x00); //  root
+        fin->addU32(ver);
+        fin->addU16(m_width); // some random width.
+        fin->addU16(m_height); // some random height.
+
+        fin->addU32(g_things.getOtbMajorVersion());
+        fin->addU32(g_things.getOtbMinorVersion());
+
+        fin->startNode(OTBM_MAP_DATA); // map data node
+        {
+            // own description.
+            fin->addU8(OTBM_ATTR_DESCRIPTION);
+            fin->addString(m_description);
+
+            // special one
+            fin->addU8(OTBM_ATTR_DESCRIPTION);
+            fin->addString(stdext::format("Saved with %s v%d", g_app.getName(), stdext::unsafe_cast<int>(g_app.getVersion())));
+
+            // spawn file.
+            fin->addU8(OTBM_ATTR_SPAWN_FILE);
+            fin->addString(m_spawnFile);
+
+            // house file.
+            if (ver > 1) {
+                fin->addU8(OTBM_ATTR_HOUSE_FILE);
+                fin->addString(m_houseFile);
+            }
+
+            Position pos(-1, -1, -1);
+            Boolean<true> first;
+            for (auto& pair : m_tiles) {
+                TilePtr tile = pair.second;
+                if(!tile || tile->isEmpty())
+                    continue;
+
+                Position tilePos = pair.first;
+                if (tilePos.x < pos.x || tilePos.x >= pos.x + 256 ||
+                    tilePos.y < pos.y || tilePos.y >= pos.y + 256 ||
+                    tilePos.z != pos.z) {
+                    if (!first)
+                        fin->endNode();
+
+                    pos.x = tilePos.x & 0xFF00;
+                    pos.y = tilePos.y & 0xFF00;
+                    pos.z = tilePos.z;
+                    fin->addU16(pos.x);
+                    fin->addU16(pos.y);
+                    fin->addU8(pos.z);
+                }
+
+                // TODO: hOUSES.
+                fin->startNode(OTBM_TILE);
+                fin->addU8(tilePos.x);
+                fin->addU8(tilePos.y);
+
+#if 0
+                // TODO: hOUSES again.
+                if (is house tile)
+                    add u32 house id...;
+#endif
+                if (tile->flags()) {
+                    fin->addU8(OTBM_ATTR_TILE_FLAGS);
+                    fin->addU32(tile->flags());
+                }
+
+
+            }
+        }
+    }
+#endif
+}
+
 bool Map::loadOtcm(const std::string& fileName)
 {
     try {
@@ -393,6 +520,10 @@ void Map::clean()
     cleanDynamicThings();
     m_tiles.clear();
     m_waypoints.clear();
+
+    // This is a fix to a segfault on exit.
+    m_towns.clear();
+    m_houses.clear();
 }
 
 void Map::cleanDynamicThings()
@@ -514,12 +645,20 @@ bool Map::removeThingByPos(const Position& pos, int stackPos)
     return false;
 }
 
-TilePtr Map::createTile(const Position& pos, const ItemPtr &g)
+template <typename... Items>
+TilePtr Map::createTileEx(const Position& pos, const Items&... items)
+{
+    TilePtr tile = getOrCreateTile(pos);
+    auto vec = {items...};
+    for (auto it : vec)
+        addThing(it, pos, 255);
+
+    return tile;
+}
+
+TilePtr Map::createTile(const Position& pos)
 {
     TilePtr tile = TilePtr(new Tile(pos));
-    if (g)
-        tile->addThing(g);
-
     m_tiles[pos] = tile;
     return tile;
 }
diff --git a/src/otclient/core/map.h b/src/otclient/core/map.h
index 4b79ff91..cf2a7f89 100644
--- a/src/otclient/core/map.h
+++ b/src/otclient/core/map.h
@@ -24,6 +24,8 @@
 #define MAP_H
 
 #include "creature.h"
+#include "houses.h"
+#include "towns.h"
 #include "animatedtext.h"
 #include <framework/core/clock.h>
 
@@ -80,6 +82,23 @@ enum {
     OTCM_VERSION = 1
 };
 
+/// Temporary way for reading container items
+struct MapContainer {
+private:
+    std::vector<ItemPtr> m_items;
+
+public:
+    void add(const ItemPtr& item) { m_items.push_back(item); }
+    ItemPtr operator[](uint idx) { return getItem(idx); }
+    ItemPtr getItem(int index) {
+        if (index < 0 || index > (int)m_items.size())
+            return nullptr;
+
+        return m_items[index];
+    }
+};
+typedef std::shared_ptr<MapContainer> MapContainerPtr;
+
 //@bindsingleton g_map
 class Map
 {
@@ -93,8 +112,8 @@ public:
     bool loadOtcm(const std::string& fileName);
     void saveOtcm(const std::string& fileName);
 
-    void loadOtbm(const std::string& fileName, bool display = false/* temporary*/);
-    //void saveOtbm(const std::string& fileName);
+    void loadOtbm(const std::string& fileName);
+    void saveOtbm(const std::string& fileName);
 
     void clean();
     void cleanDynamicThings();
@@ -106,7 +125,9 @@ public:
     bool removeThingByPos(const Position& pos, int stackPos);
 
     // tile related
-    TilePtr createTile(const Position& pos, const ItemPtr &g = nullptr);
+    template <typename... Items>
+    TilePtr createTileEx(const Position& pos, const Items&... items);
+    TilePtr createTile(const Position& pos);
     const TilePtr& getTile(const Position& pos);
     TilePtr getOrCreateTile(const Position& pos);
     void cleanTile(const Position& pos);
@@ -120,6 +141,10 @@ public:
     std::vector<CreaturePtr> getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange);
     std::vector<CreaturePtr> getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange);
 
+    // town/house related
+    TownPtr getTown(uint32 tid) { return m_towns.getTown(tid); }
+    HousePtr getHouse(uint32 hid) { return m_houses.getHouse(hid); }
+
     void setLight(const Light& light) { m_light = light; }
     void setCentralPosition(const Position& centralPosition);
 
@@ -147,11 +172,17 @@ private:
     std::vector<StaticTextPtr> m_staticTexts;
     std::vector<MapViewPtr> m_mapViews;
     std::unordered_map<Position, std::string, PositionHasher> m_waypoints;
+    std::vector<MapContainerPtr> m_containers;
 
     Light m_light;
     Position m_centralPosition;
 
     std::string m_description, m_spawnFile, m_houseFile;
+
+    Houses m_houses;
+    Towns m_towns;
+
+    uint16 m_width, m_height;
 };
 
 extern Map g_map;
diff --git a/src/otclient/core/thingtypemanager.cpp b/src/otclient/core/thingtypemanager.cpp
index 6782076f..5ebffdba 100644
--- a/src/otclient/core/thingtypemanager.cpp
+++ b/src/otclient/core/thingtypemanager.cpp
@@ -87,151 +87,89 @@ bool ThingTypeManager::loadDat(const std::string& file)
     }
 }
 
-bool ThingTypeManager::loadOtb(const std::string& file)
+void ThingTypeManager::loadOtb(const std::string& file)
 {
-    try {
-        FileStreamPtr fin = g_resources.openFile(file);
+    FileStreamPtr fin = g_resources.openFile(file);
 
-        uint signature = fin->getU32();
-        if(signature != 0)
-            stdext::throw_exception("invalid otb file");
+    uint signature = fin->getU32();
+    if(signature != 0)
+        stdext::throw_exception("invalid otb file");
 
-        BinaryTreePtr root = fin->getBinaryTree();
+    BinaryTreePtr root = fin->getBinaryTree();
 
-        signature = root->getU32();
-        if(signature != 0)
-            stdext::throw_exception("invalid otb file");
+    signature = root->getU32();
+    if(signature != 0)
+        stdext::throw_exception("invalid otb file");
 
-        root->getU32(); // flags
+    root->getU32(); // flags
 
-        m_otbMajorVersion = root->getU32();
-        m_otbMinorVersion = root->getU32();
-        root->getU32(); // build number
-        root->skip(128); // description
+    m_otbMajorVersion = root->getU32();
+    m_otbMinorVersion = root->getU32();
+    root->getU32(); // build number
+    root->skip(128); // description
 
-        m_otbTypes.resize(root->getChildren().size(), m_nullOtbType);
-        for(const BinaryTreePtr& node : root->getChildren()) {
-            ThingTypeOtbPtr otbType(new ThingTypeOtb);
-            otbType->unserialize(node);
-            addOtbType(otbType);
-        }
-
-        m_otbLoaded = true;
-        return true;
-    } catch(stdext::exception& e) {
-        g_logger.error(stdext::format("failed to load otb '%s': %s", file, e.what()));
-        return false;
+    m_otbTypes.resize(root->getChildren().size(), m_nullOtbType);
+    for(const BinaryTreePtr& node : root->getChildren()) {
+        ThingTypeOtbPtr otbType(new ThingTypeOtb);
+        otbType->unserialize(node);
+        addOtbType(otbType);
     }
+
+    m_otbLoaded = true;
 }
 
-bool ThingTypeManager::loadXml(const std::string& file)
+void ThingTypeManager::loadXml(const std::string& file)
 {
-    /*
-    try {
-        TiXmlDocument doc(file.c_str());
-        if (!doc.LoadFile()) {
-            g_logger.error(stdext::format("failed to load xml '%s'", file));
-            return false;
-        }
+    TiXmlDocument doc(file.c_str());
+    if (!doc.LoadFile())
+        stdext::throw_exception(stdext::format("failed to load xml '%s'", file));
 
-        TiXmlElement* root = doc.FirstChildElement();
-        if (!root) {
-            g_logger.error("invalid xml root");
-            return false;
-        }
+    TiXmlElement* root = doc.FirstChildElement();
+    if (!root || root->ValueTStr() != "items")
+        stdext::throw_exception("invalid root tag name");
 
-        if (root->ValueTStr() != "items") {
-            g_logger.error("invalid xml tag name, should be 'items'");
-            return false;
-        }
+    ThingTypeOtbPtr otbType = nullptr;
+    for (TiXmlElement *element = root->FirstChildElement(); element; element = element->NextSiblingElement()) {
+        if (element->ValueTStr() != "item")
+            continue;
 
-        for (TiXmlElement *element = root->FirstChildElement(); element; element = element->NextSiblingElement()) {
-            if (element->ValueTStr() != "item")
-                continue;
+        std::string name = element->Attribute("id");
+        if (name.empty())
+            continue;
 
-            std::string name = element->Attribute("id");
-            if (name.empty())
-                continue;
+        uint16 id = stdext::unsafe_cast<uint16>(element->Attribute("id"));
+        if (!(otbType = getOtbType(id))) {
+            // try reading fromId toId
+            uint16 from = stdext::unsafe_cast<uint16>(element->Attribute("fromId"));
+            uint16 to = stdext::unsafe_cast<uint16>(element->Attribute("toid"));
 
-            uint16 id = stdext::unsafe_cast<uint16>(element->Attribute("id"));
-            uint16 idEx = 0;
-            if (!id) {
-                bool found = false;
-                // fallback into reading fromid and toid
-                uint16 fromid = stdext::unsafe_cast<uint16>(element->Attribute("fromid"));
-                uint16 toid = stdext::unsafe_cast<uint16>(element->Attribute("toid"));
-                ThingTypeOtbPtr iType;
-                for (int __id = fromid; __id < toid; ++__id) {
-                    if (!(iType = getType(__id)))
-                        continue;
-
-                    iType->name = name;
-                    idEx = iType->id == fromid ? fromid : toid;
-                    found = true;
-                }
-
-                if (!found)
+            for (uint16 __id = from; __id < to; ++__id) {
+                if (!(otbType = getOtbType(__id)))
                     continue;
+
+                otbType->setHasRange();
+                otbType->setFromServerId(from);
+                otbType->setToServerId(to);
+                break;
             }
 
-            ThingTypeOtbPtr otbType = getType(id);
+            // perform last check
             if (!otbType) {
-                otbType = ThingTypeOtbPtr(new ItemData);
-                otbType->id = idEx ? idEx : id;
-                otbType->name = name;
-                addType(otbType->id, otbType);
-            }
-
-            otbType->name = name;
-
-            for (TiXmlElement *attr = element->FirstChildElement(); attr; attr = attr->NextSiblingElement()) {
-                if (attr->ValueTStr() != "attribute")
-                    continue;
-
-                std::string key = attr->Attribute("key");
-                std::string value = attr->Attribute("value");
-                if (key == "type") {
-                    if (value == "magicfield")
-                        otbType->category = IsMagicField;
-                    else if (value == "key")
-                        otbType->category = IsKey;
-                    else if (value == "depot")
-                        otbType->isDepot = true;
-                    else if (value == "teleport")
-                        otbType->category = IsTeleport;
-                    else if (value == "bed")
-                        otbType->isBed = true;
-                    else if (value == "door")
-                        otbType->category = IsDoor;
-                } else if (key == "name") {
-                    otbType->name = value;
-                } else if (key == "description") {
-                    otbType->description = value;
-                } else if (key == "weight") {
-                    otbType->weight = stdext::unsafe_cast<double>(stdext::unsafe_cast<double>(value) / 100.f);
-                } else if (key == "containerSize") {
-                    int containerSize = stdext::unsafe_cast<int>(value);
-                    if (containerSize)
-                        otbType->containerSize = containerSize;
-                    otbType->category = IsContainer;
-                } else if (key == "writeable") {
-                    if (!value.empty())
-                        otbType->category = IsWritable;
-                } else if (key == "maxTextLen") {
-                    otbType->maxTextLength = stdext::unsafe_cast<int>(value);
-                } else if (key == "charges") {
-                    otbType->charges = stdext::unsafe_cast<int>(value);
-                }
+                stdext::throw_exception(stdext::format("failed to find item with server id %d - tried reading fromid to id",
+                                                       id));
             }
         }
 
-        doc.Clear();
-    } catch(stdext::exception& e) {
+        for (TiXmlElement *attr = element->FirstChildElement(); attr; attr = attr->NextSiblingElement()) {
+            if (attr->ValueTStr() != "attribute")
+                continue;
 
+            otbType->unserializeXML(attr);
+        }
     }
-    return true;
-    */
-    return false;
+
+    doc.Clear();
+    m_xmlLoaded = true;
 }
 
 void ThingTypeManager::addOtbType(const ThingTypeOtbPtr& otbType)
diff --git a/src/otclient/core/thingtypemanager.h b/src/otclient/core/thingtypemanager.h
index 811cdd63..904ba12a 100644
--- a/src/otclient/core/thingtypemanager.h
+++ b/src/otclient/core/thingtypemanager.h
@@ -36,8 +36,8 @@ public:
     void terminate();
 
     bool loadDat(const std::string& file);
-    bool loadOtb(const std::string& file);
-    bool loadXml(const std::string& file);
+    void loadOtb(const std::string& file);
+    void loadXml(const std::string& file);
 
     void addOtbType(const ThingTypeOtbPtr& otbType);
     const ThingTypeOtbPtr& findOtbForClientId(uint16 id);
diff --git a/src/otclient/core/thingtypeotb.cpp b/src/otclient/core/thingtypeotb.cpp
index 4dbbaf2a..9d5f59a5 100644
--- a/src/otclient/core/thingtypeotb.cpp
+++ b/src/otclient/core/thingtypeotb.cpp
@@ -62,3 +62,14 @@ void ThingTypeOtb::unserialize(const BinaryTreePtr& node)
         }
     }
 }
+
+void ThingTypeOtb::unserializeXML(const TiXmlElement* elem)
+{
+    std::string key   = elem->Attribute("key");
+    std::string value = elem->Attribute("value");
+
+    if (key == "name")
+        setName(value);
+    else if (key == "description")
+        setDesc(value);
+}
diff --git a/src/otclient/core/thingtypeotb.h b/src/otclient/core/thingtypeotb.h
index f99360bf..dda35414 100644
--- a/src/otclient/core/thingtypeotb.h
+++ b/src/otclient/core/thingtypeotb.h
@@ -85,19 +85,30 @@ public:
     ThingTypeOtb();
 
     void unserialize(const BinaryTreePtr& node);
-    void unserializeXML();
+    void unserializeXML(const TiXmlElement* elem);
 
     uint16 getServerId() { return m_serverId; }
     uint16 getClientId() { return m_clientId; }
     OtbCategory getCategory() { return m_category; }
 
     bool isNull() { return m_null; }
+    bool hasRange() { return m_hasRange; }
+
+    void setHasRange() { m_hasRange = true; }
+    void setFromServerId(uint16 from) { m_fromId = from; }
+    void setToServerId(uint16 to)   { m_toId = to; }
+    void setName(const std::string& name) { m_name = name; }
+    void setDesc(const std::string& desc) { m_desc = desc; }
 
 private:
     uint16 m_serverId;
     uint16 m_clientId;
+    uint16 m_fromId, m_toId;
+
+    std::string m_name, m_desc;
     OtbCategory m_category;
     Boolean<true> m_null;
+    Boolean<false> m_hasRange;
 };
 
 #endif
diff --git a/src/otclient/core/tile.h b/src/otclient/core/tile.h
index 013f7e9e..e6a6844b 100644
--- a/src/otclient/core/tile.h
+++ b/src/otclient/core/tile.h
@@ -100,6 +100,7 @@ public:
 
     TilePtr asTile() { return std::static_pointer_cast<Tile>(shared_from_this()); }
     void setFlags(tileflags_t flags) { m_flags |= (uint32)flags; }
+    uint32 flags() { return m_flags; }
 
 private:
     void update();
diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp
index 4387fd62..4b612786 100644
--- a/src/otclient/luafunctions.cpp
+++ b/src/otclient/luafunctions.cpp
@@ -38,6 +38,8 @@
 #include <otclient/core/thingtypemanager.h>
 #include <otclient/core/spritemanager.h>
 #include <otclient/core/shadermanager.h>
+#include <otclient/core/houses.h>
+#include <otclient/core/towns.h>
 #include <otclient/net/protocolgame.h>
 #include <otclient/ui/uiitem.h>
 #include <otclient/ui/uicreature.h>
@@ -53,6 +55,19 @@ void OTClient::registerLuaFunctions()
     g_lua.bindSingletonFunction("g_things", "loadXml", &ThingTypeManager::loadXml, &g_things);
     g_lua.bindSingletonFunction("g_things", "getDatSignature", &ThingTypeManager::getDatSignature, &g_things);
 
+#if 0
+    g_lua.registerSingletonClass("g_houses");
+    g_lua.bindSingletonFunction("g_houses", "load",        &Houses::load,        &g_houses);
+    g_lua.bindSingletonFunction("g_houses", "getHouse",    &Houses::getHouse,    &g_houses);
+    g_lua.bindSingletonFunction("g_houses", "addHouse",    &Houses::addHouse,    &g_houses);
+    g_lua.bindSingletonFunction("g_houses", "removeHouse", &Houses::removeHouse, &g_houses);
+    
+    g_lua.registerSingletonClass("g_towns");
+    g_lua.bindSingletonFunction("g_towns", "getTown",      &Towns::getTown,      &g_towns);
+    g_lua.bindSingletonFunction("g_towns", "addTown",      &Towns::addTown,      &g_towns);
+    g_lua.bindSingletonFunction("g_towns", "removeTown",   &Towns::removeTown,   &g_towns);
+#endif
+
     g_lua.registerSingletonClass("g_sprites");
     g_lua.bindSingletonFunction("g_sprites", "loadSpr", &SpriteManager::loadSpr, &g_sprites);
     g_lua.bindSingletonFunction("g_sprites", "unload", &SpriteManager::unload, &g_sprites);
@@ -81,6 +96,8 @@ void OTClient::registerLuaFunctions()
     //g_lua.bindSingletonFunction("g_map", "saveOtbm", &Map::saveOtbm, &g_map);
     g_lua.bindSingletonFunction("g_map", "loadOtcm", &Map::loadOtcm, &g_map);
     g_lua.bindSingletonFunction("g_map", "saveOtcm", &Map::saveOtcm, &g_map);
+    g_lua.bindSingletonFunction("g_map", "getTown", &Map::getTown, &g_map);
+    g_lua.bindSingletonFunction("g_map", "getHouse", &Map::getHouse, &g_map);
 
     g_lua.registerSingletonClass("g_game");
     g_lua.bindSingletonFunction("g_game", "loginWorld", &Game::loginWorld, &g_game);
@@ -255,6 +272,26 @@ void OTClient::registerLuaFunctions()
     g_lua.bindClassMemberFunction<Thing>("isFullGround", &Thing::isFullGround);
     g_lua.bindClassMemberFunction<Thing>("getParentContainer", &Thing::getParentContainer);
 
+    g_lua.registerClass<House>();
+    g_lua.bindClassStaticFunction<House>("create", []{ return HousePtr(new House); });
+    g_lua.bindClassMemberFunction<House>("setId", &House::setId);
+    g_lua.bindClassMemberFunction<House>("setName", &House::setName);
+    g_lua.bindClassMemberFunction<House>("addDoor", &House::addDoor);
+    g_lua.bindClassMemberFunction<House>("addDoorPos", &House::addDoor); // alternative method
+    g_lua.bindClassMemberFunction<House>("setTile", &House::setTile);
+    g_lua.bindClassMemberFunction<House>("addTile", &House::addDoor);    // alternative method
+
+    g_lua.registerClass<Town>();
+    g_lua.bindClassStaticFunction<Town>("create", []{ return TownPtr(new Town); });
+    g_lua.bindClassMemberFunction<Town>("setId", &Town::setId);
+    g_lua.bindClassMemberFunction<Town>("setName", &Town::setName);
+    g_lua.bindClassMemberFunction<Town>("setPos", &Town::setPos);
+    g_lua.bindClassMemberFunction<Town>("setTemplePos", &Town::setPos); // alternative method
+    g_lua.bindClassMemberFunction<Town>("getId", &Town::getId);
+    g_lua.bindClassMemberFunction<Town>("getName", &Town::getName);
+    g_lua.bindClassMemberFunction<Town>("getPos", &Town::getPos);
+    g_lua.bindClassMemberFunction<Town>("getTemplePos", &Town::getPos); // alternative method
+
     g_lua.registerClass<Creature, Thing>();
     g_lua.bindClassStaticFunction<Creature>("create", []{ return CreaturePtr(new Creature); });
     g_lua.bindClassMemberFunction<Creature>("getId", &Creature::getId);
diff --git a/tools/gimp-bitmap-generator/generate_bitmap_font.py b/tools/gimp-bitmap-generator/generate_bitmap_font.py
old mode 100755
new mode 100644
diff --git a/tools/lua-binding-generator/generate_lua_bindings.lua b/tools/lua-binding-generator/generate_lua_bindings.lua
old mode 100755
new mode 100644
diff --git a/tools/pkgs/makeotc b/tools/pkgs/makeotc
old mode 100755
new mode 100644

From 5e0f18ab5bcdaa75c131044ccfa64048dabd20fc Mon Sep 17 00:00:00 2001
From: niczkx <fallen@.(none)>
Date: Sat, 14 Jul 2012 16:34:24 -0700
Subject: [PATCH 3/3] missing files

---
 src/otclient/houses.cpp | 124 ++++++++++++++++++++++++++++++++++++++++
 src/otclient/houses.h   |  80 ++++++++++++++++++++++++++
 src/otclient/towns.cpp  |  60 +++++++++++++++++++
 src/otclient/towns.h    |  69 ++++++++++++++++++++++
 4 files changed, 333 insertions(+)
 create mode 100644 src/otclient/houses.cpp
 create mode 100644 src/otclient/houses.h
 create mode 100644 src/otclient/towns.cpp
 create mode 100644 src/otclient/towns.h

diff --git a/src/otclient/houses.cpp b/src/otclient/houses.cpp
new file mode 100644
index 00000000..ea0af0c9
--- /dev/null
+++ b/src/otclient/houses.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "map.h"
+
+Houses g_houses;
+
+House::House(uint32 hId, const std::string &name, const Position &pos)
+    : m_id(hId), m_name(name)
+{
+    if (pos.isValid())
+        m_doors.insert(std::make_pair(0, pos)); // first door
+}
+
+void House::addDoor(uint16 doorId, const Position& pos)
+{
+    if (m_doors.find(doorId) == m_doors.end())
+        m_doors.insert(std::make_pair(doorId, pos));
+}
+
+void House::setTile(const TilePtr& tile)
+{
+    tile->setFlags(TILESTATE_HOUSE);
+    if (std::find(m_tiles.begin(), m_tiles.end(), tile) == m_tiles.end())
+        m_tiles.push_back(tile);
+}
+
+#define fugly_get(attrib, type) stdext::unsafe_cast<type>(elem->Attribute((attrib)))
+
+void House::load(const TiXmlElement *elem)
+{
+    std::string name = elem->Attribute("name");
+    if (name.empty())
+        name = stdext::format("UnNamed house #%u", getId());
+
+    uint32 rent = fugly_get("rent", uint32);
+    m_rent = rent;
+
+    uint32 townId = fugly_get("townid", uint32);
+    if (!g_map.getTown(townId))
+        stdext::throw_exception(stdext::format("invalid town id for house %d", townId));
+
+    uint32 size = fugly_get("size", uint32);
+    if (size == 0)
+        size = 1;
+
+    m_size = size;
+    m_isGuildHall = fugly_get("guildhall", bool);
+    addDoor(0, Position(fugly_get("entryx", uint16), fugly_get("entryy", uint16),
+                        fugly_get("entryz", uint8)));
+}
+
+void Houses::addHouse(const HousePtr& house)
+{
+    if (findHouse(house->getId()) == m_houses.end())
+        m_houses.push_back(house);
+}
+
+void Houses::removeHouse(uint32 houseId)
+{
+    auto it = findHouse(houseId);
+    if (it != m_houses.end())
+        m_houses.erase(it);
+}
+
+HousePtr Houses::getHouse(uint32 houseId)
+{
+    auto it = findHouse(houseId);
+    if (it != m_houses.end())
+        return *it;
+
+    return nullptr;
+}
+
+void Houses::load(const std::string& fileName)
+{
+    TiXmlDocument doc(fileName.c_str());
+    if (!doc.LoadFile())
+        stdext::throw_exception(stdext::format("failed to load '%s' (House XML)", fileName));
+
+    TiXmlElement *root = doc.FirstChildElement();
+    if (!root || root->ValueTStr() != "houses")
+        stdext::throw_exception("invalid root tag name");
+
+    for (TiXmlElement *elem = root->FirstChildElement(); elem; elem = elem->NextSiblingElement()) {
+        if (elem->ValueTStr() != "house")
+            stdext::throw_exception("invalid house tag.");
+
+        uint32 houseId = fugly_get("houseid", uint32);
+        HousePtr house = getHouse(houseId);
+        if (!house)
+            house = HousePtr(new House(houseId)), addHouse(house);
+
+        house->load(elem);
+    }
+
+    stdext::throw_exception("This has not been fully implemented yet.");
+}
+
+HouseList::iterator Houses::findHouse(uint32 houseId)
+{
+    return std::find_if(m_houses.begin(), m_houses.end(),
+                           [=] (const HousePtr& house) -> bool { return house->getId() == houseId; });
+}
+
diff --git a/src/otclient/houses.h b/src/otclient/houses.h
new file mode 100644
index 00000000..0bff0c71
--- /dev/null
+++ b/src/otclient/houses.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HOUSES_H
+#define HOUSES_H
+
+#include "declarations.h"
+#include "tile.h"
+
+#include <framework/luascript/luaobject.h>
+
+class House : public LuaObject
+{
+public:
+    House() { }
+    House(uint32 hId, const std::string& name = "", const Position& pos=Position());
+    ~House() { m_tiles.clear(); m_doors.clear(); }
+
+    void setId(uint32 hId) { m_id = hId; }
+    void setName(const std::string& name) { m_name = name; }
+    void addDoor(uint16 doorId, const Position& pos);
+    void setTile(const TilePtr& tile);
+
+    uint32 getId() const { return m_id; }
+    std::string getName() const { return m_name; }
+
+protected:
+    void load(const TiXmlElement* elem);
+    void save(TiXmlElement &elem) { } // TODO
+
+private:
+    uint32 m_id, m_size, m_rent;
+    std::string m_name;
+
+    std::map<uint16, Position> m_doors;
+    std::vector<TilePtr> m_tiles;
+    Boolean<false> m_isGuildHall;
+
+    friend class Houses;
+};
+
+class Houses {
+public:
+    void addHouse(const HousePtr& house);
+    void removeHouse(uint32 houseId);
+    void load(const std::string& fileName);
+
+    HouseList houseList() const { return m_houses; }
+    HousePtr getHouse(uint32 houseId);
+
+    // Fix to segfault on exit.
+    void clear() { m_houses.clear(); }
+
+private:
+    HouseList m_houses;
+
+protected:
+    HouseList::iterator findHouse(uint32 houseId);
+};
+
+#endif
diff --git a/src/otclient/towns.cpp b/src/otclient/towns.cpp
new file mode 100644
index 00000000..426b4ec8
--- /dev/null
+++ b/src/otclient/towns.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "towns.h"
+
+Towns g_towns;
+
+Town::Town(uint32 tid, const std::string& name, const Position& pos)
+    : m_id(tid), m_name(name)
+{
+    if (pos.isValid())
+        m_pos = pos;
+}
+
+
+void Towns::addTown(const TownPtr &town)
+{
+    if (findTown(town->getId()) == m_towns.end())
+        m_towns.push_back(town);
+}
+
+void Towns::removeTown(uint32 townId)
+{
+    auto it = findTown(townId);
+    if (it != m_towns.end())
+        m_towns.erase(it);
+}
+
+TownPtr Towns::getTown(uint32 townId)
+{
+    auto it = findTown(townId);
+    if (it != m_towns.end())
+        return *it;
+    return nullptr;
+}
+
+TownList::iterator Towns::findTown(uint32 townId)
+{
+    return std::find_if(m_towns.begin(), m_towns.end(),
+                        [=] (const TownPtr& town) -> bool { return town->getId() == townId; });
+}
diff --git a/src/otclient/towns.h b/src/otclient/towns.h
new file mode 100644
index 00000000..979ad1e7
--- /dev/null
+++ b/src/otclient/towns.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef TOWNS_H
+#define TOWNS_H
+
+#include "declarations.h"
+#include <framework/luascript/luaobject.h>
+
+
+class Town : public LuaObject
+{
+public:
+    Town() { }
+    Town(uint32 tid, const std::string& name, const Position& pos=Position());
+
+    void setId(uint32 tid) { m_id = tid; }
+    void setName(const std::string& name) { m_name = name; }
+    void setPos(const Position& pos) { m_pos = pos; }
+
+    uint32 getId() { return m_id; }
+    std::string getName() { return m_name; }
+    Position getPos() { return m_pos; }
+
+private:
+    uint32 m_id;
+    std::string m_name;
+    Position m_pos; // temple pos
+};
+
+class Towns
+{
+public:
+    void addTown(const TownPtr &town);
+    void removeTown(uint32 townId);
+
+    TownPtr getTown(uint32 townId);
+    TownList getTowns() { return m_towns; }
+
+    // Fix to segfault on exit
+    void clear() { m_towns.clear(); }
+
+private:
+    TownList m_towns;
+
+protected:
+    TownList::iterator findTown(uint32 townId);
+};
+
+#endif