00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <cstring>
00023
00024 #include "game-server/mapreader.hpp"
00025
00026 #include "game-server/map.hpp"
00027 #include "game-server/mapcomposite.hpp"
00028 #include "game-server/mapmanager.hpp"
00029 #include "game-server/monstermanager.hpp"
00030 #include "game-server/resourcemanager.hpp"
00031 #include "game-server/spawnarea.hpp"
00032 #include "game-server/trigger.hpp"
00033 #include "scripting/script.hpp"
00034 #include "utils/base64.h"
00035 #include "utils/logger.h"
00036 #include "utils/trim.hpp"
00037 #include "utils/xml.hpp"
00038 #include "utils/zlib.hpp"
00039 #include "utils/string.hpp"
00040
00041 static std::vector< int > tilesetFirstGids;
00042
00043 void MapReader::readMap(const std::string &filename, MapComposite *composite)
00044 {
00045 int fileSize;
00046 char *buffer = ResourceManager::loadFile(filename, fileSize);
00047
00048 if (buffer == NULL)
00049 {
00050 LOG_ERROR("Error: Map file not found (" << filename.c_str() << ")");
00051 return;
00052 }
00053
00054 xmlDocPtr doc = NULL;
00055
00056 int l = filename.length();
00057 if (l > 3 && filename.substr(l - 3) == ".gz")
00058 {
00059
00060 char *inflated;
00061 unsigned inflatedSize = 0;
00062 bool ret = inflateMemory(buffer, fileSize, inflated, inflatedSize);
00063 free(buffer);
00064 buffer = ret ? inflated : NULL;
00065 fileSize = inflatedSize;
00066 }
00067
00068 if (buffer)
00069 {
00070
00071 doc = xmlParseMemory(buffer, fileSize);
00072 free(buffer);
00073 }
00074
00075 if (!doc)
00076 {
00077 LOG_ERROR("Error while parsing map file '" << filename << "'!");
00078 return;
00079 }
00080
00081 Map *map = NULL;
00082 xmlNodePtr node = xmlDocGetRootElement(doc);
00083
00084 std::vector<Thing *> things;
00085
00086
00087 if (node && xmlStrEqual(node->name, BAD_CAST "map"))
00088 {
00089 map = MapReader::readMap(node, filename, composite, things);
00090 }
00091 else
00092 {
00093 LOG_ERROR("Error: Not a map file (" << filename << ")!");
00094 }
00095
00096 xmlFreeDoc(doc);
00097
00098 if (map != NULL)
00099 {
00100 composite->setMap(map);
00101
00102 for (std::vector< Thing * >::const_iterator i = things.begin(),
00103 i_end = things.end(); i != i_end; ++i)
00104 {
00105 composite->insert(*i);
00106 }
00107
00108 if (Script *s = composite->getScript())
00109 {
00110 s->setMap(composite);
00111 s->prepare("initialize");
00112 s->execute();
00113 }
00114 }
00115 }
00116
00117 Map* MapReader::readMap(xmlNodePtr node, const std::string &path,
00118 MapComposite *composite, std::vector<Thing *> &things)
00119 {
00120
00121 std::string pathDir = path.substr(0, path.rfind("/") + 1);
00122 int w = XML::getProperty(node, "width", 0);
00123 int h = XML::getProperty(node, "height", 0);
00124
00125 int tilew = XML::getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH);
00126 int tileh = XML::getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT);
00127 int layerNr = 0;
00128 Map* map = new Map(w, h, tilew, tileh);
00129
00130 for (node = node->xmlChildrenNode; node != NULL; node = node->next)
00131 {
00132 if (xmlStrEqual(node->name, BAD_CAST "tileset"))
00133 {
00134 if (xmlHasProp(node, BAD_CAST "source"))
00135 {
00136 LOG_WARN("Warning: External tilesets not supported yet.");
00137 }
00138 else
00139 {
00140 ::tilesetFirstGids.push_back(XML::getProperty(node, "firstgid", 0));
00141 }
00142 }
00143 else if (xmlStrEqual(node->name, BAD_CAST "properties"))
00144 {
00145 for_each_xml_child_node(propNode, node)
00146 {
00147 if (xmlStrEqual(propNode->name, BAD_CAST "property"))
00148 {
00149 std::string key = XML::getProperty(propNode, "name", "");
00150 std::string val = XML::getProperty(propNode, "value", "");
00151 LOG_DEBUG(" "<<key<<": "<<val);
00152 map->setProperty(key, val);
00153 }
00154 }
00155 }
00156 else if (xmlStrEqual(node->name, BAD_CAST "layer"))
00157 {
00158
00159 if (layerNr++ == 3)
00160 {
00161 readLayer(node, map);
00162 }
00163 }
00164 else if (xmlStrEqual(node->name, BAD_CAST "objectgroup"))
00165 {
00166
00167 for_each_xml_child_node(objectNode, node)
00168 {
00169 if (!xmlStrEqual(objectNode->name, BAD_CAST "object"))
00170 {
00171 continue;
00172 }
00173
00174 std::string objName = XML::getProperty(objectNode, "name", "");
00175 std::string objType = XML::getProperty(objectNode, "type", "");
00176 objType = utils::toupper(objType);
00177 int objX = XML::getProperty(objectNode, "x", 0);
00178 int objY = XML::getProperty(objectNode, "y", 0);
00179 int objW = XML::getProperty(objectNode, "width", 0);
00180 int objH = XML::getProperty(objectNode, "height", 0);
00181 Rectangle rect = { objX, objY, objW, objH };
00182
00183
00184 if (objType == "WARP")
00185 {
00186 std::string destMapName = std::string();
00187 int destX = -1;
00188 int destY = -1;
00189
00190 for_each_xml_child_node(propertiesNode, objectNode)
00191 {
00192 if (!xmlStrEqual(propertiesNode->name, BAD_CAST "properties"))
00193 {
00194 continue;
00195 }
00196
00197 for_each_xml_child_node(propertyNode, propertiesNode)
00198 {
00199 if (xmlStrEqual(propertyNode->name, BAD_CAST "property"))
00200 {
00201 std::string value = XML::getProperty(propertyNode, "name", std::string());
00202 value = utils::toupper(value);
00203 if (value == "DEST_MAP")
00204 {
00205 destMapName = getObjectProperty(propertyNode, std::string());
00206 }
00207 else if (value == "DEST_X")
00208 {
00209 destX = getObjectProperty(propertyNode, -1);
00210 }
00211 else if (value == "DEST_Y")
00212 {
00213 destY = getObjectProperty(propertyNode, -1);
00214 }
00215 }
00216 }
00217 }
00218
00219 if (destMapName != "" && destX != -1 && destY != -1)
00220 {
00221 MapComposite *destMap = MapManager::getMap(destMapName);
00222 if (destMap)
00223 {
00224 things.push_back(new TriggerArea(
00225 composite, rect,
00226 new WarpAction(destMap, destX, destY),
00227 false));
00228 }
00229 }
00230 else
00231 {
00232 LOG_WARN("Unrecognized warp format");
00233 }
00234 }
00235 else if (objType == "SPAWN")
00236 {
00237 int monsterId = -1;
00238 int maxBeings = 10;
00239 int spawnRate = 10;
00240
00241 for_each_xml_child_node(propertiesNode, objectNode)
00242 {
00243 if (!xmlStrEqual(propertiesNode->name, BAD_CAST "properties"))
00244 {
00245 continue;
00246 }
00247
00248 for_each_xml_child_node(propertyNode, propertiesNode)
00249 {
00250 if (xmlStrEqual(propertyNode->name, BAD_CAST "property"))
00251 {
00252 std::string value = XML::getProperty(propertyNode, "name", std::string());
00253 value = utils::toupper(value);
00254 if (value == "MONSTER_ID")
00255 {
00256 monsterId = getObjectProperty(propertyNode, monsterId);
00257 }
00258 else if (value == "MAX_BEINGS")
00259 {
00260 maxBeings = getObjectProperty(propertyNode, maxBeings);
00261 }
00262 else if (value == "SPAWN_RATE")
00263 {
00264 spawnRate = getObjectProperty(propertyNode, spawnRate);
00265 }
00266 }
00267 }
00268 }
00269
00270 MonsterClass *monster = MonsterManager::getMonster(monsterId);
00271 if (monster != NULL)
00272 {
00273 things.push_back(new SpawnArea(composite, monster, rect, maxBeings, spawnRate));
00274 }
00275 else
00276 {
00277 LOG_WARN("Couldn't find monster ID " << monsterId <<
00278 " for spawn area");
00279 }
00280 }
00281 else if (objType == "NPC")
00282 {
00283 Script *s = composite->getScript();
00284 if (!s)
00285 {
00286
00287 s = Script::create("lua");
00288 composite->setScript(s);
00289 }
00290
00291 int npcId = -1;
00292 std::string scriptText;
00293
00294 for_each_xml_child_node(propertiesNode, objectNode)
00295 {
00296 if (!xmlStrEqual(propertiesNode->name, BAD_CAST "properties"))
00297 {
00298 continue;
00299 }
00300
00301 for_each_xml_child_node(propertyNode, propertiesNode)
00302 {
00303 if (xmlStrEqual(propertyNode->name, BAD_CAST "property"))
00304 {
00305 std::string value = XML::getProperty(propertyNode, "name", std::string());
00306 value = utils::toupper(value);
00307 if (value == "NPC_ID")
00308 {
00309 npcId = getObjectProperty(propertyNode, npcId);
00310 }
00311 else if (value == "SCRIPT")
00312 {
00313 scriptText = getObjectProperty(propertyNode, "");
00314 }
00315 }
00316 }
00317 }
00318
00319 if (npcId != -1 && !scriptText.empty())
00320 {
00321 s->loadNPC(objName, npcId, objX, objY, scriptText.c_str());
00322 }
00323 else
00324 {
00325 LOG_WARN("Unrecognized format for npc");
00326 }
00327 }
00328 else if (objType == "SCRIPT")
00329 {
00330 Script *s = composite->getScript();
00331 if (!s)
00332 {
00333
00334 s = Script::create("lua");
00335 composite->setScript(s);
00336 }
00337
00338 std::string scriptFilename;
00339 std::string scriptText;
00340
00341 for_each_xml_child_node(propertiesNode, objectNode)
00342 {
00343 if (!xmlStrEqual(propertiesNode->name, BAD_CAST "properties"))
00344 {
00345 continue;
00346 }
00347
00348 for_each_xml_child_node(propertyNode, propertiesNode)
00349 {
00350 if (xmlStrEqual(propertyNode->name, BAD_CAST "property"))
00351 {
00352 std::string value = XML::getProperty(propertyNode, "name", std::string());
00353 value = utils::toupper(value);
00354 if (value == "FILENAME")
00355 {
00356 scriptFilename = getObjectProperty(propertyNode, "");
00357 trim(scriptFilename);
00358 }
00359 else if (value == "TEXT")
00360 {
00361 scriptText = getObjectProperty(propertyNode, "");
00362 }
00363 }
00364 }
00365 }
00366
00367 if (!scriptFilename.empty())
00368 {
00369 s->loadFile(scriptFilename);
00370 }
00371 else if (!scriptText.empty())
00372 {
00373 s->load(scriptText.c_str());
00374 }
00375 else
00376 {
00377 LOG_WARN("Unrecognized format for script");
00378 }
00379 }
00380 }
00381 }
00382 }
00383
00384
00385 ::tilesetFirstGids.clear();
00386
00387 return map;
00388 }
00389
00390 void MapReader::readLayer(xmlNodePtr node, Map *map)
00391 {
00392 node = node->xmlChildrenNode;
00393 int h = map->getHeight();
00394 int w = map->getWidth();
00395 int x = 0;
00396 int y = 0;
00397
00398
00399
00400 while (node)
00401 {
00402 if (xmlStrEqual(node->name, BAD_CAST "data")) break;
00403 node = node->next;
00404 }
00405
00406 if (!node)
00407 {
00408 LOG_WARN("Layer without any 'data' element.");
00409 return;
00410 }
00411
00412 if (XML::getProperty(node, "encoding", std::string()) == "base64")
00413 {
00414
00415 xmlNodePtr dataChild = node->xmlChildrenNode;
00416 if (!dataChild)
00417 {
00418 LOG_WARN("Corrupted layer.");
00419 return;
00420 }
00421
00422 int len = strlen((const char *) dataChild->content) + 1;
00423 char *charData = new char[len + 1];
00424 const char *charStart = (const char *) dataChild->content;
00425 char *charIndex = charData;
00426
00427 while (*charStart)
00428 {
00429 if (*charStart != ' ' && *charStart != '\t' && *charStart != '\n')
00430 {
00431 *charIndex = *charStart;
00432 ++charIndex;
00433 }
00434 ++charStart;
00435 }
00436 *charIndex = '\0';
00437
00438 int binLen;
00439 unsigned char *binData =
00440 php_base64_decode((unsigned char *)charData, strlen(charData), &binLen);
00441
00442 delete[] charData;
00443
00444 if (!binData)
00445 {
00446 LOG_WARN("Failed to decode base64-encoded layer.");
00447 return;
00448 }
00449
00450 if (XML::getProperty(node, "compression", std::string()) == "gzip")
00451 {
00452
00453 char *inflated;
00454 unsigned inflatedSize;
00455 bool res = inflateMemory((char *)binData, binLen, inflated, inflatedSize);
00456 free(binData);
00457
00458 if (!res)
00459 {
00460 LOG_WARN("Failed to decompress gzipped layer");
00461 return;
00462 }
00463
00464 binData = (unsigned char *)inflated;
00465 binLen = inflatedSize;
00466 }
00467
00468 for (int i = 0; i < binLen - 3; i += 4)
00469 {
00470 int gid = binData[i] |
00471 (binData[i + 1] << 8) |
00472 (binData[i + 2] << 16) |
00473 (binData[i + 3] << 24);
00474
00475 setTileWithGid(map, x, y, gid);
00476
00477 if (++x == w)
00478 {
00479 x = 0;
00480 ++y;
00481 }
00482 }
00483 free(binData);
00484 return;
00485 }
00486
00487
00488 node = node->xmlChildrenNode;
00489
00490 while (node)
00491 {
00492 if (xmlStrEqual(node->name, BAD_CAST "tile") && y < h)
00493 {
00494 int gid = XML::getProperty(node, "gid", -1);
00495 setTileWithGid(map, x, y, gid);
00496
00497 if (++x == w)
00498 {
00499 x = 0;
00500 ++y;
00501 }
00502 }
00503
00504 node = node->next;
00505 }
00506 }
00507
00508 std::string MapReader::getObjectProperty(xmlNodePtr node,
00509 const std::string &def)
00510 {
00511 if (xmlHasProp(node, BAD_CAST "value"))
00512 {
00513 return XML::getProperty(node, "value", def);
00514 }
00515 else if (const char *prop = (const char *)node->xmlChildrenNode->content)
00516 {
00517 return std::string(prop);
00518 }
00519 return std::string();
00520 }
00521
00522 int MapReader::getObjectProperty(xmlNodePtr node, int def)
00523 {
00524 int val = def;
00525 if (xmlHasProp(node, BAD_CAST "value"))
00526 {
00527 val = XML::getProperty(node, "value", def);
00528 }
00529 else if (const char *prop = (const char *)node->xmlChildrenNode->content)
00530 {
00531 val = atoi(prop);
00532 }
00533 return val;
00534 }
00535
00536 void MapReader::setTileWithGid(Map *map, int x, int y, int gid)
00537 {
00538
00539 int set = gid;
00540 for (std::vector< int >::const_iterator i = ::tilesetFirstGids.begin(),
00541 i_end = ::tilesetFirstGids.end(); i != i_end; ++i)
00542 {
00543 if (gid < *i)
00544 {
00545 break;
00546 }
00547 set = *i;
00548 }
00549
00550 if (gid!=set)
00551 {
00552 map->blockTile(x, y, Map::BLOCKTYPE_WALL);
00553 }
00554
00555 }