/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ultima/ultima.h" #include "ultima/ultima8/misc/debugger.h" #include "ultima/ultima8/misc/point3.h" #include "ultima/ultima8/world/map.h" #include "ultima/ultima8/world/item_factory.h" #include "ultima/ultima8/world/container.h" #include "ultima/ultima8/world/coord_utils.h" #include "ultima/ultima8/kernel/object_manager.h" #include "ultima/ultima8/ultima8.h" #include "ultima/ultima8/games/game_data.h" #include "ultima/ultima8/gfx/main_shape_archive.h" namespace Ultima { namespace Ultima8 { //#define DUMP_ITEMS Map::Map(uint32 mapNum) : _mapNum(mapNum) { } Map::~Map() { clear(); } void Map::clear() { for (auto *item : _fixedItems) { delete item; } _fixedItems.clear(); for (auto *item : _dynamicItems) { delete item; } _dynamicItems.clear(); } void Map::loadNonFixed(Common::SeekableReadStream *rs) { loadFixedFormatObjects(_dynamicItems, rs, 0); } // Utility function for fixing up map bugs: shift a coordinate to a // different z without changing its on-screen position. static void shiftCoordsToZ(int32 &x, int32 &y, int32 &z, int32 newz) { int32 zd = newz - z; x += 4 * zd; y += 4 * zd; z = newz; } void Map::addMapFix(uint32 shape, uint32 frame, int32 x, int32 y, int32 z) { Item *item = ItemFactory::createItem(shape, frame, 0, 0, 0, 0, Item::EXT_FIXED, false); item->setLocation(x, y, z); _fixedItems.push_back(item); } void Map::loadFixed(Common::SeekableReadStream *rs) { loadFixedFormatObjects(_fixedItems, rs, Item::EXT_FIXED); // WORKAROUND - add some missing map tiles from game data // U8 hack for missing ground tiles on map 25. See docs/u8bugs.txt if (GAME_IS_U8 && _mapNum == 25) { // TODO } // U8 hack for missing ground tiles on map 7 (north road). if (GAME_IS_U8 && _mapNum == 7) { addMapFix(301, 1, 2815, 25727, 8); addMapFix(301, 1, 9983, 21157, 8); addMapFix(301, 1, 13183, 16511, 8); } // Upper Catacombs after using the skull (ScummVM bug #12097) // Not a perfect fix (still a few pixels off) but reduces chance // of bug happening if (GAME_IS_U8 && _mapNum == 50) { addMapFix(34, 7, 16127, 6143, 0); } // U8 hack for missing ground/wall tiles on map 62. See docs/u8bugs.txt if (GAME_IS_U8 && _mapNum == 62) { addMapFix(301, 1, 16255, 6143, 48); addMapFix(301, 1, 16639, 6143, 48); addMapFix(301, 1, 16511, 6143, 48); addMapFix(301, 1, 15999, 6143, 48); addMapFix(301, 1, 15871, 6143, 48); addMapFix(301, 1, 15743, 6143, 48); addMapFix(301, 1, 15615, 6143, 48); addMapFix(301, 1, 15999, 6015, 48); addMapFix(301, 1, 15871, 6015, 48); addMapFix(301, 1, 15743, 6015, 48); addMapFix(301, 1, 15615, 6015, 48); addMapFix(301, 1, 20095, 6911, 48); addMapFix(301, 1, 20223, 6911, 48); addMapFix(301, 1, 20095, 6783, 48); addMapFix(301, 1, 20223, 6783, 48); addMapFix(301, 1, 19839, 6655, 48); addMapFix(301, 1, 19967, 6655, 48); addMapFix(301, 1, 19839, 6527, 48); addMapFix(301, 1, 19967, 6527, 48); addMapFix(301, 1, 20095, 6527, 48); addMapFix(301, 1, 19967, 6399, 48); addMapFix(301, 1, 19839, 6399, 48); addMapFix(301, 1, 19711, 6399, 48); addMapFix(497, 0, 15487, 6271, 48); addMapFix(497, 0, 15359, 6271, 48); addMapFix(409, 32, 14975, 6399, 0); addMapFix(409, 32, 14975, 6015, 0); addMapFix(409, 32, 15103, 6015, 0); } if (GAME_IS_U8 && _mapNum == 49) { // Map 49 has some water tiles at the wrong z for (auto *item : _fixedItems) { if (item->getShape() == 347 && item->getZ() == 96) { Point3 pt = item->getLocation(); if ((pt.x == 23007 && pt.y == 21343) || (pt.x == 23135 && pt.y == 21471) || (pt.x == 23135 && pt.y == 21343)) { shiftCoordsToZ(pt.x, pt.y, pt.z, 40); item->setLocation(pt); } } } } if (GAME_IS_U8 && _mapNum == 21) { // Map 21 has some ground and wall tiles at the wrong z for (auto *item : _fixedItems) { int32 z = item->getZ(); uint32 sh = item->getShape(); if (z == 8 && (sh == 301 || sh == 31 || sh == 32)) { Point3 pt = item->getLocation(); if ((pt.x == 6783 || pt.x == 6655) && (pt.y == 15743 || pt.y == 15615)) { shiftCoordsToZ(pt.x, pt.y, pt.z, 16); item->setLocation(pt); } } } } if (GAME_IS_U8 && _mapNum == 5) { // Map 5 has some ground tiles at the wrong z for (auto *item : _fixedItems) { if (item->getShape() == 71 && item->getFrame() == 8 && item->getZ() == 0) { Point3 pt = item->getLocation(); if ((pt.x == 9151 && pt.y == 24127) || (pt.x == 9279 && pt.y == 23999) || (pt.x == 9535 && pt.y == 23615) || (pt.x == 9151 && pt.y == 23487) || (pt.x == 10303 && pt.y == 23487) || (pt.x == 9919 && pt.y == 23487) || (pt.x == 10559 && pt.y == 23487)) { shiftCoordsToZ(pt.x, pt.y, pt.z, 48); item->setLocation(pt); } } } } } void Map::unloadFixed() { for (auto *item : _fixedItems) { delete item; } _fixedItems.clear(); } void Map::loadFixedFormatObjects(Std::list &itemlist, Common::SeekableReadStream *rs, uint32 extendedflags) { if (!rs) return; uint32 size = rs->size(); if (size == 0) return; uint32 itemcount = size / 16; Common::Stack cont; int contdepth = 0; for (uint32 i = 0; i < itemcount; ++i) { // These are ALL unsigned on disk int32 x = static_cast(rs->readUint16LE()); int32 y = static_cast(rs->readUint16LE()); int32 z = static_cast(rs->readByte()); World_FromUsecodeXY(x, y); uint32 shape = rs->readUint16LE(); uint32 frame = rs->readByte(); uint16 flags = rs->readUint16LE(); uint16 quality = rs->readUint16LE(); uint16 npcNum = static_cast(rs->readByte()); uint16 mapNum = static_cast(rs->readByte()); uint16 next = rs->readUint16LE(); // do we need next for anything? // find container this item belongs to, if any. // the x coordinate stores the container-depth of this item, // so pop items from the container stack until we reach x, // or, if x is too large, the item is added to the top-level list while (contdepth != x && contdepth > 0) { cont.pop(); contdepth--; #ifdef DUMP_ITEMS debugC(kDebugObject, "---- Ending container ----"); #endif } #ifdef DUMP_ITEMS debugC(kDebugObject, "%u,%u:\t(%d, %d, %d),\t%x, %u, %u, %u, %u", shape , frame, x, y, z, flags, quality, npcNum, mapNum, next); #endif Item *item = ItemFactory::createItem(shape, frame, quality, flags, npcNum, mapNum, extendedflags, false); if (!item) { warning("Couldn't create item: %u,%u:\t(%d, %d, %d),\t%x, %u, %u, %u, %u", shape, frame, x, y, z, flags, quality, npcNum, mapNum, next); continue; } item->setLocation(x, y, z); if (contdepth > 0) { cont.top()->addItem(item); } else { itemlist.push_back(item); } Container *c = dynamic_cast(item); if (c) { // container, so prepare to read contents contdepth++; cont.push(c); #ifdef DUMP_ITEMS debugC(kDebugObject, "---- Starting container ----"); #endif } } } void Map::save(Common::WriteStream *ws) { ws->writeUint32LE(static_cast(_dynamicItems.size())); for (auto *item : _dynamicItems) { ObjectManager::get_instance()->saveObject(ws, item); } } bool Map::load(Common::ReadStream *rs, uint32 version) { uint32 itemcount = rs->readUint32LE(); // Integrity check if (itemcount > 65536) { warning("improbable item count in map data: %d", itemcount); return false; } for (unsigned int i = 0; i < itemcount; ++i) { Object *obj = ObjectManager::get_instance()->loadObject(rs, version); Item *item = dynamic_cast(obj); if (!item) return false; _dynamicItems.push_back(item); } return true; } } // End of namespace Ultima8 } // End of namespace Ultima