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(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 // tiny XML -#define TIXML_USE_STL +#define TIXML_USE_STL // use std::string's instead #include #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 #include @@ -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(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 m_isContainer; + Boolean 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 #include "mapview.h" #include #include #include +#include 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 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; - - 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 = g_houses.getHouse(hId))) - house = HousePtr(new House(hId)); - house->setTile(tile); + if (type != OTBM_TILE && type != OTBM_HOUSETILE) + stdext::throw_exception(stdext::format("invalid node tile type %d", (int)type)); + + TilePtr tile = nullptr; + ItemPtr ground = nullptr; + HousePtr house = nullptr; + uint32 flags = TILESTATE_NONE; + + 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; - - if ((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT) - flags |= TILESTATE_NOLOGOUT; - - 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)); + 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_REFRESH) == TILESTATE_REFRESH) + flags |= TILESTATE_REFRESH; + + break; } + case OTBM_ATTR_ITEM: { + ItemPtr item = Item::createFromOtb(nodeTile->getU16()); + if (tile) + addThing(item, pos, 255); + else if (item->isGround()) + ground = item; + 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) { - 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()) { - ground = item; - } else { - tile = createTile(pos, ground); - tile->addThing(item); - } - } else - stdext::throw_exception("Unknown item node"); + 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)); - - } else - stdext::throw_exception("invalid waypoint node"); + if (nodeWaypoint->getU8() != OTBM_WAYPOINT) + 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(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 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 +TilePtr Map::createTileEx(const Position& pos, const Items&... items) { - TilePtr tile = TilePtr(new Tile(pos)); - if (g) - tile->addThing(g); + 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)); 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 @@ -80,6 +82,23 @@ enum { OTCM_VERSION = 1 }; +/// Temporary way for reading container items +struct MapContainer { +private: + std::vector 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 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 + 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 getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange); std::vector 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 m_staticTexts; std::vector m_mapViews; std::unordered_map m_waypoints; + std::vector 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_otbTypes.resize(root->getChildren().size(), m_nullOtbType); - for(const BinaryTreePtr& node : root->getChildren()) { - ThingTypeOtbPtr otbType(new ThingTypeOtb); - otbType->unserialize(node); - addOtbType(otbType); - } + m_otbMajorVersion = root->getU32(); + m_otbMinorVersion = root->getU32(); + root->getU32(); // build number + root->skip(128); // description - 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; - } - - TiXmlElement* root = doc.FirstChildElement(); - if (!root) { - g_logger.error("invalid xml root"); - return false; - } - - if (root->ValueTStr() != "items") { - g_logger.error("invalid xml tag name, should be 'items'"); - return false; - } - - for (TiXmlElement *element = root->FirstChildElement(); element; element = element->NextSiblingElement()) { - if (element->ValueTStr() != "item") - continue; - - std::string name = element->Attribute("id"); - if (name.empty()) - continue; - - uint16 id = stdext::unsafe_cast(element->Attribute("id")); - uint16 idEx = 0; - if (!id) { - bool found = false; - // fallback into reading fromid and toid - uint16 fromid = stdext::unsafe_cast(element->Attribute("fromid")); - uint16 toid = stdext::unsafe_cast(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) + 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 || root->ValueTStr() != "items") + stdext::throw_exception("invalid root tag name"); + + ThingTypeOtbPtr otbType = nullptr; + for (TiXmlElement *element = root->FirstChildElement(); element; element = element->NextSiblingElement()) { + if (element->ValueTStr() != "item") + continue; + + std::string name = element->Attribute("id"); + if (name.empty()) + continue; + + uint16 id = stdext::unsafe_cast(element->Attribute("id")); + if (!(otbType = getOtbType(id))) { + // try reading fromId toId + uint16 from = stdext::unsafe_cast(element->Attribute("fromId")); + uint16 to = stdext::unsafe_cast(element->Attribute("toid")); + + for (uint16 __id = from; __id < to; ++__id) { + if (!(otbType = getOtbType(__id))) continue; - } - ThingTypeOtbPtr otbType = getType(id); - if (!otbType) { - otbType = ThingTypeOtbPtr(new ItemData); - otbType->id = idEx ? idEx : id; - otbType->name = name; - addType(otbType->id, otbType); + otbType->setHasRange(); + otbType->setFromServerId(from); + otbType->setToServerId(to); + break; } - 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(stdext::unsafe_cast(value) / 100.f); - } else if (key == "containerSize") { - int containerSize = stdext::unsafe_cast(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(value); - } else if (key == "charges") { - otbType->charges = stdext::unsafe_cast(value); - } + // perform last check + if (!otbType) { + 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 m_null; + Boolean 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(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 #include #include +#include +#include #include #include #include @@ -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("isFullGround", &Thing::isFullGround); g_lua.bindClassMemberFunction("getParentContainer", &Thing::getParentContainer); + g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", []{ return HousePtr(new House); }); + g_lua.bindClassMemberFunction("setId", &House::setId); + g_lua.bindClassMemberFunction("setName", &House::setName); + g_lua.bindClassMemberFunction("addDoor", &House::addDoor); + g_lua.bindClassMemberFunction("addDoorPos", &House::addDoor); // alternative method + g_lua.bindClassMemberFunction("setTile", &House::setTile); + g_lua.bindClassMemberFunction("addTile", &House::addDoor); // alternative method + + g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", []{ return TownPtr(new Town); }); + g_lua.bindClassMemberFunction("setId", &Town::setId); + g_lua.bindClassMemberFunction("setName", &Town::setName); + g_lua.bindClassMemberFunction("setPos", &Town::setPos); + g_lua.bindClassMemberFunction("setTemplePos", &Town::setPos); // alternative method + g_lua.bindClassMemberFunction("getId", &Town::getId); + g_lua.bindClassMemberFunction("getName", &Town::getName); + g_lua.bindClassMemberFunction("getPos", &Town::getPos); + g_lua.bindClassMemberFunction("getTemplePos", &Town::getPos); // alternative method + g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return CreaturePtr(new Creature); }); g_lua.bindClassMemberFunction("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