Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/src/client/map.h +307-0 View file
@@ -0,0 +1,307 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef MAP_H
24+#define MAP_H
25+
26+#include "creature.h"
27+#include "houses.h"
28+#include "towns.h"
29+#include "creatures.h"
30+#include "animatedtext.h"
31+#include "statictext.h"
32+#include "tile.h"
33+
34+#include <framework/core/clock.h>
35+
36+enum OTBM_ItemAttr
37+{
38+ OTBM_ATTR_DESCRIPTION = 1,
39+ OTBM_ATTR_EXT_FILE = 2,
40+ OTBM_ATTR_TILE_FLAGS = 3,
41+ OTBM_ATTR_ACTION_ID = 4,
42+ OTBM_ATTR_UNIQUE_ID = 5,
43+ OTBM_ATTR_TEXT = 6,
44+ OTBM_ATTR_DESC = 7,
45+ OTBM_ATTR_TELE_DEST = 8,
46+ OTBM_ATTR_ITEM = 9,
47+ OTBM_ATTR_DEPOT_ID = 10,
48+ OTBM_ATTR_SPAWN_FILE = 11,
49+ OTBM_ATTR_RUNE_CHARGES = 12,
50+ OTBM_ATTR_HOUSE_FILE = 13,
51+ OTBM_ATTR_HOUSEDOORID = 14,
52+ OTBM_ATTR_COUNT = 15,
53+ OTBM_ATTR_DURATION = 16,
54+ OTBM_ATTR_DECAYING_STATE = 17,
55+ OTBM_ATTR_WRITTENDATE = 18,
56+ OTBM_ATTR_WRITTENBY = 19,
57+ OTBM_ATTR_SLEEPERGUID = 20,
58+ OTBM_ATTR_SLEEPSTART = 21,
59+ OTBM_ATTR_CHARGES = 22,
60+ OTBM_ATTR_CONTAINER_ITEMS = 23,
61+ OTBM_ATTR_ATTRIBUTE_MAP = 128,
62+ /// just random numbers, they're not actually used by the binary reader...
63+ OTBM_ATTR_WIDTH = 129,
64+ OTBM_ATTR_HEIGHT = 130
65+};
66+
67+enum OTBM_NodeTypes_t
68+{
69+ OTBM_ROOTV2 = 1,
70+ OTBM_MAP_DATA = 2,
71+ OTBM_ITEM_DEF = 3,
72+ OTBM_TILE_AREA = 4,
73+ OTBM_TILE = 5,
74+ OTBM_ITEM = 6,
75+ OTBM_TILE_SQUARE = 7,
76+ OTBM_TILE_REF = 8,
77+ OTBM_SPAWNS = 9,
78+ OTBM_SPAWN_AREA = 10,
79+ OTBM_MONSTER = 11,
80+ OTBM_TOWNS = 12,
81+ OTBM_TOWN = 13,
82+ OTBM_HOUSETILE = 14,
83+ OTBM_WAYPOINTS = 15,
84+ OTBM_WAYPOINT = 16
85+};
86+
87+enum {
88+ OTCM_SIGNATURE = 0x4D43544F,
89+ OTCM_VERSION = 1
90+};
91+
92+enum {
93+ BLOCK_SIZE = 32
94+};
95+
96+enum : uint8 {
97+ Animation_Force,
98+ Animation_Show
99+};
100+
101+class TileBlock {
102+public:
103+ TileBlock() { m_tiles.fill(nullptr); }
104+
105+ const TilePtr& create(const Position& pos) {
106+ TilePtr& tile = m_tiles[getTileIndex(pos)];
107+ tile = TilePtr(new Tile(pos));
108+ return tile;
109+ }
110+ const TilePtr& getOrCreate(const Position& pos) {
111+ TilePtr& tile = m_tiles[getTileIndex(pos)];
112+ if(!tile)
113+ tile = TilePtr(new Tile(pos));
114+ return tile;
115+ }
116+ const TilePtr& get(const Position& pos) { return m_tiles[getTileIndex(pos)]; }
117+ void remove(const Position& pos) { m_tiles[getTileIndex(pos)] = nullptr; }
118+
119+ uint getTileIndex(const Position& pos) { return ((pos.y % BLOCK_SIZE) * BLOCK_SIZE) + (pos.x % BLOCK_SIZE); }
120+
121+ const std::array<TilePtr, BLOCK_SIZE*BLOCK_SIZE>& getTiles() const { return m_tiles; }
122+
123+private:
124+ std::array<TilePtr, BLOCK_SIZE*BLOCK_SIZE> m_tiles;
125+};
126+
127+struct AwareRange
128+{
129+ int top;
130+ int right;
131+ int bottom;
132+ int left;
133+
134+ int horizontal() { return left + right + 1; }
135+ int vertical() { return top + bottom + 1; }
136+};
137+
138+struct PathFindResult
139+{
140+ Otc::PathFindResult status = Otc::PathFindResultNoWay;
141+ std::vector<Otc::Direction> path;
142+ int complexity = 0;
143+ Position start;
144+ Position destination;
145+};
146+using PathFindResult_ptr = std::shared_ptr<PathFindResult>;
147+
148+struct Node {
149+ float cost;
150+ float totalCost;
151+ Position pos;
152+ Node *prev;
153+ int distance;
154+ int unseen;
155+};
156+
157+//@bindsingleton g_map
158+class Map
159+{
160+public:
161+ void init();
162+ void terminate();
163+
164+ void addMapView(const MapViewPtr& mapView);
165+ void removeMapView(const MapViewPtr& mapView);
166+ void notificateTileUpdate(const Position& pos, bool updateMinimap = false);
167+
168+ void requestVisibleTilesCacheUpdate();
169+
170+ bool loadOtcm(const std::string& fileName);
171+ void saveOtcm(const std::string& fileName);
172+
173+ void loadOtbm(const std::string& fileName);
174+ void saveOtbm(const std::string& fileName);
175+
176+ // otbm attributes (description, size, etc.)
177+ void setHouseFile(const std::string& file) { m_attribs.set(OTBM_ATTR_HOUSE_FILE, file); }
178+ void setSpawnFile(const std::string& file) { m_attribs.set(OTBM_ATTR_SPAWN_FILE, file); }
179+ void setDescription(const std::string& desc) { m_attribs.set(OTBM_ATTR_DESCRIPTION, desc); }
180+
181+ void clearDescriptions() { m_attribs.remove(OTBM_ATTR_DESCRIPTION); }
182+ void setWidth(uint16 w) { m_attribs.set(OTBM_ATTR_WIDTH, w); }
183+ void setHeight(uint16 h) { m_attribs.set(OTBM_ATTR_HEIGHT, h); }
184+
185+ std::string getHouseFile() { return m_attribs.get<std::string>(OTBM_ATTR_HOUSE_FILE); }
186+ std::string getSpawnFile() { return m_attribs.get<std::string>(OTBM_ATTR_SPAWN_FILE); }
187+ Size getSize() { return Size(m_attribs.get<uint16>(OTBM_ATTR_WIDTH), m_attribs.get<uint16>(OTBM_ATTR_HEIGHT)); }
188+ std::vector<std::string> getDescriptions() { return stdext::split(m_attribs.get<std::string>(OTBM_ATTR_DESCRIPTION), "\n"); }
189+
190+ void clean();
191+ void cleanDynamicThings();
192+ void cleanTexts();
193+
194+ // thing related
195+ void addThing(const ThingPtr& thing, const Position& pos, int stackPos = -1);
196+ void setTileSpeed(const Position & pos, uint16_t speed, uint8_t blocking);
197+ ThingPtr getThing(const Position& pos, int stackPos);
198+ bool removeThing(const ThingPtr& thing);
199+ bool removeThingByPos(const Position& pos, int stackPos);
200+ void colorizeThing(const ThingPtr& thing, const Color& color);
201+ void removeThingColor(const ThingPtr& thing);
202+
203+ StaticTextPtr getStaticText(const Position& pos);
204+
205+ // tile related
206+ const TilePtr& createTile(const Position& pos);
207+ template <typename... Items>
208+ const TilePtr& createTileEx(const Position& pos, const Items&... items);
209+ const TilePtr& getOrCreateTile(const Position& pos);
210+ const TilePtr& getTile(const Position& pos);
211+ const TileList getTiles(int floor = -1);
212+ void cleanTile(const Position& pos);
213+
214+ // tile zone related
215+ void setShowZone(tileflags_t zone, bool show);
216+ void setShowZones(bool show);
217+ void setZoneColor(tileflags_t flag, const Color& color);
218+ void setZoneOpacity(float opacity) { m_zoneOpacity = opacity; }
219+
220+ float getZoneOpacity() { return m_zoneOpacity; }
221+ Color getZoneColor(tileflags_t flag);
222+ tileflags_t getZoneFlags() { return (tileflags_t)m_zoneFlags; }
223+ bool showZones() { return m_zoneFlags != 0; }
224+ bool showZone(tileflags_t zone) { return (m_zoneFlags & zone) == zone; }
225+
226+ void setForceShowAnimations(bool force);
227+ bool isForcingAnimations();
228+ bool isShowingAnimations();
229+ void setShowAnimations(bool show);
230+
231+ std::map<Position, ItemPtr> findItemsById(uint16 clientId, uint32 max);
232+
233+ // known creature related
234+ void addCreature(const CreaturePtr& creature);
235+ CreaturePtr getCreatureById(uint32 id);
236+ void removeCreatureById(uint32 id);
237+ std::vector<CreaturePtr> getSightSpectators(const Position& centerPos, bool multiFloor);
238+ std::vector<CreaturePtr> getSpectators(const Position& centerPos, bool multiFloor);
239+ std::vector<CreaturePtr> getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange);
240+ std::vector<CreaturePtr> getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange);
241+ std::vector<CreaturePtr> getSpectatorsByPattern(const Position& centerPos, const std::string& pattern, Otc::Direction direction);
242+
243+ void setLight(const Light& light) { m_light = light; }
244+ void setCentralPosition(const Position& centralPosition);
245+
246+ bool isLookPossible(const Position& pos);
247+ bool isCovered(const Position& pos, int firstFloor = 0);
248+ bool isCompletelyCovered(const Position& pos, int firstFloor = 0);
249+ bool isAwareOfPosition(const Position& pos, bool extended = false);
250+ bool isAwareOfPositionForClean(const Position& pos, bool extended = false);
251+
252+ void setAwareRange(const AwareRange& range);
253+ void resetAwareRange();
254+ AwareRange getAwareRange() { return m_awareRange; }
255+ Size getAwareRangeAsSize() { return Size(m_awareRange.horizontal(), m_awareRange.vertical()); }
256+
257+ Light getLight() { return m_light; }
258+ Position getCentralPosition() { return m_centralPosition; }
259+ int getFirstAwareFloor();
260+ int getLastAwareFloor();
261+ const std::vector<MissilePtr>& getFloorMissiles(int z) { return m_floorMissiles[z]; }
262+
263+ std::vector<AnimatedTextPtr> getAnimatedTexts() { return m_animatedTexts; }
264+ std::vector<StaticTextPtr> getStaticTexts() { return m_staticTexts; }
265+
266+ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> findPath(const Position& start, const Position& goal, int maxComplexity, int flags = 0);
267+ PathFindResult_ptr newFindPath(const Position& start, const Position& goal, std::shared_ptr<std::list<Node*>> visibleNodes);
268+ void findPathAsync(const Position & start, const Position & goal, std::function<void(PathFindResult_ptr)> callback);
269+
270+ // tuple = <cost, distance, prevPos>
271+ std::map<std::string, std::tuple<int, int, int, std::string>> findEveryPath(const Position& start, int maxDistance, const std::map<std::string, std::string>& params);
272+
273+ int getMinimapColor(const Position& pos);
274+ bool isPatchable(const Position& pos);
275+ bool isWalkable(const Position& pos, bool ignoreCreatures);
276+ bool isSightClear(const Position& fromPos, const Position& toPos);
277+ bool checkSightLine(const Position& fromPos, const Position& toPos);
278+
279+private:
280+ void removeUnawareThings();
281+ uint getBlockIndex(const Position& pos) { return ((pos.y / BLOCK_SIZE) * (65536 / BLOCK_SIZE)) + (pos.x / BLOCK_SIZE); }
282+
283+ std::map<uint, TileBlock> m_tileBlocks[Otc::MAX_Z+1];
284+ std::map<uint32, CreaturePtr> m_knownCreatures;
285+ std::array<std::vector<MissilePtr>, Otc::MAX_Z+1> m_floorMissiles;
286+ std::vector<AnimatedTextPtr> m_animatedTexts;
287+ std::vector<StaticTextPtr> m_staticTexts;
288+ std::vector<MapViewPtr> m_mapViews;
289+ std::unordered_map<Position, std::string, PositionHasher> m_waypoints;
290+
291+ uint8 m_animationFlags;
292+ uint32 m_zoneFlags;
293+ std::map<uint32, Color> m_zoneColors;
294+ float m_zoneOpacity;
295+
296+ Light m_light;
297+ Position m_centralPosition;
298+ Rect m_tilesRect;
299+
300+ stdext::packed_storage<uint8> m_attribs;
301+ AwareRange m_awareRange;
302+ static TilePtr m_nulltile;
303+};
304+
305+extern Map g_map;
306+
307+#endif
A RD_Client/src/client/mapio.cpp +538-0 View file
@@ -0,0 +1,538 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "map.h"
24+#include "tile.h"
25+#include "game.h"
26+
27+#include <framework/core/application.h>
28+#include <framework/core/eventdispatcher.h>
29+#include <framework/core/resourcemanager.h>
30+#include <framework/core/filestream.h>
31+#include <framework/core/binarytree.h>
32+#include <framework/xml/tinyxml.h>
33+#include <framework/ui/uiwidget.h>
34+
35+void Map::loadOtbm(const std::string& fileName)
36+{
37+ try {
38+ if(!g_things.isOtbLoaded())
39+ stdext::throw_exception("OTB isn't loaded yet to load a map.");
40+
41+ FileStreamPtr fin = g_resources.openFile(fileName, g_game.getFeature(Otc::GameDontCacheFiles));
42+ if(!fin)
43+ stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName));
44+
45+ char identifier[4];
46+ if(fin->read(identifier, 1, 4) < 4)
47+ stdext::throw_exception("Could not read file identifier");
48+
49+ if(memcmp(identifier, "OTBM", 4) != 0 && memcmp(identifier, "\0\0\0\0", 4) != 0)
50+ stdext::throw_exception(stdext::format("Invalid file identifier detected: %s", identifier));
51+
52+ BinaryTreePtr root = fin->getBinaryTree();
53+ if(root->getU8())
54+ stdext::throw_exception("could not read root property!");
55+
56+ uint32 headerVersion = root->getU32();
57+ if(headerVersion > 3)
58+ stdext::throw_exception(stdext::format("Unknown OTBM version detected: %u.", headerVersion));
59+
60+ setWidth(root->getU16());
61+ setHeight(root->getU16());
62+
63+ uint32 headerMajorItems = root->getU8();
64+ if(headerMajorItems > g_things.getOtbMajorVersion()) {
65+ stdext::throw_exception(stdext::format("This map was saved with different OTB version. read %d what it's supposed to be: %d",
66+ headerMajorItems, g_things.getOtbMajorVersion()));
67+ }
68+
69+ root->skip(3);
70+ uint32 headerMinorItems = root->getU32();
71+ if(headerMinorItems > g_things.getOtbMinorVersion()) {
72+ g_logger.warning(stdext::format("This map needs an updated OTB. read %d what it's supposed to be: %d or less",
73+ headerMinorItems, g_things.getOtbMinorVersion()));
74+ }
75+
76+ BinaryTreePtr node = root->getChildren()[0];
77+ if(node->getU8() != OTBM_MAP_DATA)
78+ stdext::throw_exception("Could not read root data node");
79+
80+ while(node->canRead()) {
81+ uint8 attribute = node->getU8();
82+ std::string tmp = node->getString();
83+ switch (attribute) {
84+ case OTBM_ATTR_DESCRIPTION:
85+ setDescription(tmp);
86+ break;
87+ case OTBM_ATTR_SPAWN_FILE:
88+ setSpawnFile(fileName.substr(0, fileName.rfind('/') + 1) + tmp);
89+ break;
90+ case OTBM_ATTR_HOUSE_FILE:
91+ setHouseFile(fileName.substr(0, fileName.rfind('/') + 1) + tmp);
92+ break;
93+ default:
94+ stdext::throw_exception(stdext::format("Invalid attribute '%d'", (int)attribute));
95+ }
96+ }
97+
98+ for(const BinaryTreePtr& nodeMapData : node->getChildren()) {
99+ uint8 mapDataType = nodeMapData->getU8();
100+ if(mapDataType == OTBM_TILE_AREA) {
101+ Position basePos;
102+ basePos.x = nodeMapData->getU16();
103+ basePos.y = nodeMapData->getU16();
104+ basePos.z = nodeMapData->getU8();
105+
106+ for(const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) {
107+ uint8 type = nodeTile->getU8();
108+ if(unlikely(type != OTBM_TILE && type != OTBM_HOUSETILE))
109+ stdext::throw_exception(stdext::format("invalid node tile type %d", (int)type));
110+
111+ HousePtr house = nullptr;
112+ uint32 flags = TILESTATE_NONE;
113+ Position pos = basePos + nodeTile->getPoint();
114+
115+ if(type == OTBM_HOUSETILE) {
116+ uint32 hId = nodeTile->getU32();
117+ TilePtr tile = getOrCreateTile(pos);
118+ if(!(house = g_houses.getHouse(hId))) {
119+ house = HousePtr(new House(hId));
120+ g_houses.addHouse(house);
121+ }
122+ house->setTile(tile);
123+ }
124+
125+ while(nodeTile->canRead()) {
126+ uint8 tileAttr = nodeTile->getU8();
127+ switch(tileAttr) {
128+ case OTBM_ATTR_TILE_FLAGS: {
129+ uint32 _flags = nodeTile->getU32();
130+ if((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE)
131+ flags |= TILESTATE_PROTECTIONZONE;
132+ else if((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE)
133+ flags |= TILESTATE_OPTIONALZONE;
134+ else if((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE)
135+ flags |= TILESTATE_HARDCOREZONE;
136+
137+ if((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT)
138+ flags |= TILESTATE_NOLOGOUT;
139+
140+ if((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
141+ flags |= TILESTATE_REFRESH;
142+ break;
143+ }
144+ case OTBM_ATTR_ITEM: {
145+ addThing(Item::createFromOtb(nodeTile->getU16()), pos);
146+ break;
147+ }
148+ default: {
149+ stdext::throw_exception(stdext::format("invalid tile attribute %d at pos %s",
150+ (int)tileAttr, stdext::to_string(pos)));
151+ }
152+ }
153+ }
154+
155+ for(const BinaryTreePtr& nodeItem : nodeTile->getChildren()) {
156+ if(unlikely(nodeItem->getU8() != OTBM_ITEM))
157+ stdext::throw_exception("invalid item node");
158+
159+ ItemPtr item = Item::createFromOtb(nodeItem->getU16());
160+ item->unserializeItem(nodeItem);
161+
162+ if(item->isContainer()) {
163+ for(const BinaryTreePtr& containerItem : nodeItem->getChildren()) {
164+ if(containerItem->getU8() != OTBM_ITEM)
165+ stdext::throw_exception("invalid container item node");
166+
167+ ItemPtr cItem = Item::createFromOtb(containerItem->getU16());
168+ cItem->unserializeItem(containerItem);
169+ item->addContainerItem(cItem);
170+ }
171+ }
172+
173+ if(house && item->isMoveable()) {
174+ g_logger.warning(stdext::format("Moveable item found in house: %d at pos %s - escaping...", item->getId(), stdext::to_string(pos)));
175+ item.reset();
176+ }
177+
178+ addThing(item, pos);
179+ }
180+
181+ if(const TilePtr& tile = getTile(pos)) {
182+ if(house)
183+ tile->setFlag(TILESTATE_HOUSE);
184+ tile->setFlag(flags);
185+ }
186+ }
187+ } else if(mapDataType == OTBM_TOWNS) {
188+ TownPtr town = nullptr;
189+ for(const BinaryTreePtr &nodeTown : nodeMapData->getChildren()) {
190+ if(nodeTown->getU8() != OTBM_TOWN)
191+ stdext::throw_exception("invalid town node.");
192+
193+ uint32 townId = nodeTown->getU32();
194+ std::string townName = nodeTown->getString();
195+
196+ Position townCoords;
197+ townCoords.x = nodeTown->getU16();
198+ townCoords.y = nodeTown->getU16();
199+ townCoords.z = nodeTown->getU8();
200+
201+ if(!(town = g_towns.getTown(townId)))
202+ g_towns.addTown(TownPtr(new Town(townId, townName, townCoords)));
203+ }
204+ g_towns.sort();
205+ } else if(mapDataType == OTBM_WAYPOINTS && headerVersion > 1) {
206+ for(const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) {
207+ if(nodeWaypoint->getU8() != OTBM_WAYPOINT)
208+ stdext::throw_exception("invalid waypoint node.");
209+
210+ std::string name = nodeWaypoint->getString();
211+
212+ Position waypointPos;
213+ waypointPos.x = nodeWaypoint->getU16();
214+ waypointPos.y = nodeWaypoint->getU16();
215+ waypointPos.z = nodeWaypoint->getU8();
216+
217+ if(waypointPos.isValid() && !name.empty() && m_waypoints.find(waypointPos) == m_waypoints.end())
218+ m_waypoints.insert(std::make_pair(waypointPos, name));
219+ }
220+ } else
221+ stdext::throw_exception(stdext::format("Unknown map data node %d", (int)mapDataType));
222+ }
223+
224+ fin->close();
225+ } catch(std::exception& e) {
226+ g_logger.error(stdext::format("Failed to load '%s': %s", fileName, e.what()));
227+ }
228+}
229+
230+void Map::saveOtbm(const std::string& fileName)
231+{
232+ try {
233+ FileStreamPtr fin = g_resources.createFile(fileName);
234+ if(!fin)
235+ stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName));
236+
237+ std::string dir;
238+ if(fileName.find_last_of('/') == std::string::npos)
239+ dir = g_resources.getWorkDir();
240+ else
241+ dir = fileName.substr(0, fileName.find_last_of('/'));
242+
243+ uint32 version = 0;
244+ if(g_things.getOtbMajorVersion() < ClientVersion820)
245+ version = 1;
246+ else
247+ version = 2;
248+
249+ /// Usually when a map has empty house/spawn file it means the map is new.
250+ /// TODO: Ask the user for a map name instead of those ugly uses of substr
251+ std::string::size_type sep_pos;
252+ std::string houseFile = getHouseFile();
253+ std::string spawnFile = getSpawnFile();
254+ std::string cpyf;
255+
256+ if((sep_pos = fileName.rfind('.')) != std::string::npos && stdext::ends_with(fileName, ".otbm"))
257+ cpyf = fileName.substr(0, sep_pos);
258+
259+ if(houseFile.empty())
260+ houseFile = cpyf + "-houses.xml";
261+
262+ if(spawnFile.empty())
263+ spawnFile = cpyf + "-spawns.xml";
264+
265+ /// we only need the filename to save to, the directory should be resolved by the OTBM loader not here
266+ if((sep_pos = spawnFile.rfind('/')) != std::string::npos)
267+ spawnFile = spawnFile.substr(sep_pos + 1);
268+
269+ if((sep_pos = houseFile.rfind('/')) != std::string::npos)
270+ houseFile = houseFile.substr(sep_pos + 1);
271+
272+ fin->addU32(0); // file version
273+ OutputBinaryTreePtr root(new OutputBinaryTree(fin));
274+ {
275+ root->addU32(version);
276+
277+ Size mapSize = getSize();
278+ root->addU16(mapSize.width());
279+ root->addU16(mapSize.height());
280+
281+ root->addU32(g_things.getOtbMajorVersion());
282+ root->addU32(g_things.getOtbMinorVersion());
283+
284+ root->startNode(OTBM_MAP_DATA);
285+ {
286+ root->addU8(OTBM_ATTR_DESCRIPTION);
287+ root->addString(m_attribs.get<std::string>(OTBM_ATTR_DESCRIPTION));
288+
289+ root->addU8(OTBM_ATTR_SPAWN_FILE);
290+ root->addString(spawnFile);
291+
292+ root->addU8(OTBM_ATTR_HOUSE_FILE);
293+ root->addString(houseFile);
294+
295+ int px = -1, py = -1, pz =-1;
296+ bool firstNode = true;
297+
298+ for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
299+ for(const auto& it : m_tileBlocks[z]) {
300+ const TileBlock& block = it.second;
301+ for(const TilePtr& tile : block.getTiles()) {
302+ if(unlikely(!tile || tile->isEmpty()))
303+ continue;
304+
305+ const Position& pos = tile->getPosition();
306+ if(unlikely(!pos.isValid()))
307+ continue;
308+
309+ if(pos.x < px || pos.x >= px + 256
310+ || pos.y < py || pos.y >= py + 256
311+ || pos.z != pz) {
312+ if(!firstNode)
313+ root->endNode(); /// OTBM_TILE_AREA
314+
315+ firstNode = false;
316+ root->startNode(OTBM_TILE_AREA);
317+
318+ px = pos.x & 0xFF00;
319+ py = pos.y & 0xFF00;
320+ pz = pos.z;
321+ root->addPos(px, py, pz);
322+ }
323+
324+ root->startNode(tile->isHouseTile() ? OTBM_HOUSETILE : OTBM_TILE);
325+ root->addPoint(Point(pos.x, pos.y) & 0xFF);
326+ if(tile->isHouseTile())
327+ root->addU32(tile->getHouseId());
328+
329+ if(tile->getFlags()) {
330+ root->addU8(OTBM_ATTR_TILE_FLAGS);
331+ root->addU32(tile->getFlags());
332+ }
333+
334+ const auto& itemList = tile->getItems();
335+ const ItemPtr& ground = tile->getGround();
336+ if(ground) {
337+ // Those types are called "complex" needs other stuff to be written.
338+ // For containers, there is container items, for depot, depot it and so on.
339+ if(!ground->isContainer() && !ground->isDepot()
340+ && !ground->isDoor() && !ground->isTeleport()) {
341+ root->addU8(OTBM_ATTR_ITEM);
342+ root->addU16(ground->getServerId());
343+ } else
344+ ground->serializeItem(root);
345+ }
346+ for(const ItemPtr& item : itemList)
347+ if(!item->isGround())
348+ item->serializeItem(root);
349+
350+ root->endNode(); // OTBM_TILE
351+ }
352+ }
353+ }
354+
355+ if(!firstNode)
356+ root->endNode(); // OTBM_TILE_AREA
357+
358+ root->startNode(OTBM_TOWNS);
359+ for(const TownPtr& town : g_towns.getTowns()) {
360+ root->startNode(OTBM_TOWN);
361+
362+ root->addU32(town->getId());
363+ root->addString(town->getName());
364+
365+ Position townPos = town->getPos();
366+ root->addPos(townPos.x, townPos.y, townPos.z);
367+ root->endNode();
368+ }
369+ root->endNode();
370+
371+ if(version > 1) {
372+ root->startNode(OTBM_WAYPOINTS);
373+ for(const auto& it : m_waypoints) {
374+ root->startNode(OTBM_WAYPOINT);
375+ root->addString(it.second);
376+
377+ Position pos = it.first;
378+ root->addPos(pos.x, pos.y, pos.z);
379+ root->endNode();
380+ }
381+ root->endNode();
382+ }
383+ }
384+ root->endNode(); // OTBM_MAP_DATA
385+ }
386+ root->endNode();
387+
388+ fin->flush();
389+ fin->close();
390+ } catch(std::exception& e) {
391+ g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
392+ }
393+}
394+
395+bool Map::loadOtcm(const std::string& fileName)
396+{
397+ try {
398+ FileStreamPtr fin = g_resources.openFile(fileName, g_game.getFeature(Otc::GameDontCacheFiles));
399+ if(!fin)
400+ stdext::throw_exception("unable to open file");
401+
402+ uint32 signature = fin->getU32();
403+ if(signature != OTCM_SIGNATURE)
404+ stdext::throw_exception("invalid otcm file");
405+
406+ uint16 start = fin->getU16();
407+ uint16 version = fin->getU16();
408+ fin->getU32(); // flags
409+
410+ switch(version) {
411+ case 1: {
412+ fin->getString(); // description
413+ uint32 datSignature = fin->getU32();
414+ fin->getU16(); // protocol version
415+ fin->getString(); // world name
416+
417+ if(datSignature != g_things.getDatSignature())
418+ g_logger.warning("otcm map loaded was created with a different dat signature");
419+
420+ break;
421+ }
422+ default:
423+ stdext::throw_exception("otcm version not supported");
424+ }
425+
426+ fin->seek(start);
427+
428+ while(true) {
429+ Position pos;
430+
431+ pos.x = fin->getU16();
432+ pos.y = fin->getU16();
433+ pos.z = fin->getU8();
434+
435+ // end of file
436+ if(!pos.isValid())
437+ break;
438+
439+ const TilePtr& tile = g_map.createTile(pos);
440+
441+ int stackPos = 0;
442+ while(true) {
443+ int id = fin->getU16();
444+
445+ // end of tile
446+ if(id == 0xFFFF)
447+ break;
448+
449+ int countOrSubType = fin->getU8();
450+
451+ ItemPtr item = Item::create(id);
452+ item->setCountOrSubType(countOrSubType);
453+
454+ if(item->isValid())
455+ tile->addThing(item, stackPos++);
456+ }
457+
458+ g_map.notificateTileUpdate(pos);
459+ }
460+
461+ fin->close();
462+
463+ return true;
464+ } catch(stdext::exception& e) {
465+ g_logger.error(stdext::format("failed to load OTCM map: %s", e.what()));
466+ return false;
467+ }
468+}
469+
470+void Map::saveOtcm(const std::string& fileName)
471+{
472+ try {
473+ stdext::timer saveTimer;
474+
475+ FileStreamPtr fin = g_resources.createFile(fileName);
476+
477+ //TODO: compression flag with zlib
478+ uint32 flags = 0;
479+
480+ // header
481+ fin->addU32(OTCM_SIGNATURE);
482+ fin->addU16(0); // data start, will be overwritten later
483+ fin->addU16(OTCM_VERSION);
484+ fin->addU32(flags);
485+
486+ // version 1 header
487+ fin->addString("OTCM 1.0"); // map description
488+ fin->addU32(g_things.getDatSignature());
489+ fin->addU16(g_game.getClientVersion());
490+ fin->addString(g_game.getWorldName());
491+
492+ // go back and rewrite where the map data starts
493+ uint32 start = fin->tell();
494+ fin->seek(4);
495+ fin->addU16(start);
496+ fin->seek(start);
497+
498+ for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
499+ for(const auto& it : m_tileBlocks[z]) {
500+ const TileBlock& block = it.second;
501+ for(const TilePtr& tile : block.getTiles()) {
502+ if(!tile || tile->isEmpty())
503+ continue;
504+
505+ Position pos = tile->getPosition();
506+ fin->addU16(pos.x);
507+ fin->addU16(pos.y);
508+ fin->addU8(pos.z);
509+
510+ for(const ThingPtr& thing : tile->getThings()) {
511+ if(thing->isItem()) {
512+ ItemPtr item = thing->static_self_cast<Item>();
513+ fin->addU16(item->getId());
514+ fin->addU8(item->getCountOrSubType());
515+ }
516+ }
517+
518+ // end of tile
519+ fin->addU16(0xFFFF);
520+ }
521+ }
522+ }
523+
524+ // end of file
525+ Position invalidPos;
526+ fin->addU16(invalidPos.x);
527+ fin->addU16(invalidPos.y);
528+ fin->addU8(invalidPos.z);
529+
530+ fin->flush();
531+
532+ fin->close();
533+ } catch(stdext::exception& e) {
534+ g_logger.error(stdext::format("failed to save OTCM map: %s", e.what()));
535+ }
536+}
537+
538+/* vim: set ts=4 sw=4 et: */
Top