From a878f5f79af62967522e9848ea3b2f2f910e2a52 Mon Sep 17 00:00:00 2001 From: beegee1 Date: Sat, 3 Nov 2012 13:30:47 +0000 Subject: [PATCH] * Separated map loading from the map object * Moved map classes to lib/Map * Renamed map.h/cpp to CMap.h/cpp * Profiling of map loading is now optional * Updated CMemoryStream --- AI/VCAI/VCAI.h | 2 +- CCallback.cpp | 2 +- client/AdventureMapClasses.cpp | 2 +- client/BattleInterface/CBattleInterface.cpp | 2 +- client/CAdvmapInterface.cpp | 2 +- client/CPlayerInterface.cpp | 2 +- client/CPreGame.cpp | 61 +- client/CPreGame.h | 4 +- client/Client.cpp | 4 +- client/GUIClasses.cpp | 2 +- client/NetPacksClient.cpp | 4 +- client/mapHandler.cpp | 2 +- lib/CGameState.cpp | 17 +- lib/CGameState.h | 2 +- lib/CMakeLists.txt | 7 +- lib/CMapInfo.cpp | 71 - lib/CMapInfo.h | 35 - lib/CObjectHandler.cpp | 2 +- lib/Connection.cpp | 6 +- lib/Connection.h | 2 +- lib/Filesystem/CFileInputStream.cpp | 6 +- lib/Filesystem/CMemoryStream.cpp | 32 +- lib/Filesystem/CMemoryStream.h | 19 +- lib/Filesystem/CResourceLoader.h | 3 +- lib/IGameCallback.cpp | 2 +- lib/{ => Map}/CCampaignHandler.cpp | 18 +- lib/{ => Map}/CCampaignHandler.h | 0 lib/Map/CMap.cpp | 241 ++ lib/{map.h => Map/CMap.h} | 576 ++--- lib/Map/CMapInfo.cpp | 49 + lib/Map/CMapInfo.h | 42 + lib/Map/CMapService.cpp | 2543 +++++++++++++++++++ lib/Map/CMapService.h | 361 +++ lib/NetPacks.h | 2 +- lib/NetPacksLib.cpp | 2 +- lib/VCMI_lib.vcxproj | 16 +- lib/map.cpp | 2281 ----------------- lib/vcmi_endian.h | 16 +- server/CGameHandler.cpp | 4 +- server/CVCMIServer.cpp | 6 +- server/NetPacksServer.cpp | 2 +- 41 files changed, 3548 insertions(+), 2904 deletions(-) delete mode 100644 lib/CMapInfo.cpp delete mode 100644 lib/CMapInfo.h rename lib/{ => Map}/CCampaignHandler.cpp (93%) rename lib/{ => Map}/CCampaignHandler.h (100%) create mode 100644 lib/Map/CMap.cpp rename lib/{map.h => Map/CMap.h} (62%) create mode 100644 lib/Map/CMapInfo.cpp create mode 100644 lib/Map/CMapInfo.h create mode 100644 lib/Map/CMapService.cpp create mode 100644 lib/Map/CMapService.h delete mode 100644 lib/map.cpp diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index fbaf9bcfd..a0f47f004 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -14,7 +14,7 @@ #include "../../lib/CObjectHandler.h" #include "../../lib/Connection.h" #include "../../lib/CGameState.h" -#include "../../lib/map.h" +#include "../../lib/Map/CMap.h" #include "../../lib/NetPacks.h" #include "../../lib/CondSh.h" #include "../../lib/CStopWatch.h" diff --git a/CCallback.cpp b/CCallback.cpp index 8b3bfd77a..069658851 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -7,7 +7,7 @@ #include "lib/BattleState.h" #include "client/CPlayerInterface.h" #include "client/Client.h" -#include "lib/map.h" +#include "lib/Map/CMap.h" #include "lib/CBuildingHandler.h" #include "lib/CDefObjInfoHandler.h" #include "lib/CGeneralTextHandler.h" diff --git a/client/AdventureMapClasses.cpp b/client/AdventureMapClasses.cpp index 03721d936..5d782ba81 100644 --- a/client/AdventureMapClasses.cpp +++ b/client/AdventureMapClasses.cpp @@ -4,7 +4,7 @@ #include "../CCallback.h" #include "../lib/JsonNode.h" #include "../lib/Filesystem/CResourceLoader.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "../lib/CModHandler.h" #include "../lib/CObjectHandler.h" #include "../lib/CGameState.h" diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index 3e936bd03..956d5d042 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/client/BattleInterface/CBattleInterface.cpp @@ -24,7 +24,7 @@ #include "../CCreatureWindow.h" #include "../CVideoHandler.h" #include "../../lib/CTownHandler.h" -#include "../../lib/map.h" +#include "../../lib/Map/CMap.h" #include "CBattleAnimations.h" #include "CBattleInterfaceClasses.h" diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index f3da8fdbf..cf4021a77 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -19,7 +19,7 @@ #include "../lib/CHeroHandler.h" #include "../lib/CObjectHandler.h" #include "../lib/CTownHandler.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "../lib/JsonNode.h" #include "mapHandler.h" #include "CPreGame.h" diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 03dffbdb5..c07425160 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -30,7 +30,7 @@ #include "CMusicHandler.h" #include "../lib/CondSh.h" #include "../lib/NetPacks.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "../lib/VCMIDirs.h" #include "mapHandler.h" #include "../lib/CStopWatch.h" diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index ca1f0f320..9670821e6 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -16,7 +16,7 @@ #include "../lib/CTownHandler.h" #include "../lib/CHeroHandler.h" #include "../lib/CObjectHandler.h" -#include "../lib/CCampaignHandler.h" +#include "../lib/Map/CCampaignHandler.h" #include "../lib/CCreatureHandler.h" #include "../lib/JsonNode.h" #include "CMusicHandler.h" @@ -24,7 +24,7 @@ #include "Graphics.h" #include "../lib/Connection.h" #include "../lib/VCMIDirs.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "GUIClasses.h" #include "CPlayerInterface.h" #include "../CCallback.h" @@ -41,6 +41,7 @@ #include "../lib/GameConstants.h" #include "UIFramework/CGuiHandler.h" #include "UIFramework/CIntObjectClasses.h" +#include "../lib/Map/CMapService.h" /* * CPreGame.cpp, part of VCMI engine @@ -100,9 +101,8 @@ static void do_quit() static CMapInfo *mapInfoFromGame() { - CMapInfo *ret = new CMapInfo(); - CMapHeader *headerCopy = new CMapHeader(*LOCPLINT->cb->getMapHeader()); //will be deleted by CMapInfo d-tor - ret->setHeader(headerCopy); + CMapInfo * ret = new CMapInfo(); + ret->mapHeader = std::unique_ptr(new CMapHeader(*LOCPLINT->cb->getMapHeader())); return ret; } @@ -771,7 +771,7 @@ void CSelectionScreen::changeSelection( const CMapInfo *to ) SEL->sInfo.difficulty = to->scenarioOpts->difficulty; if(screenType != CMenuScreen::campaignList) { - updateStartInfo(to ? to->fileURI : "", sInfo, to ? to->mapHeader : NULL); + updateStartInfo(to ? to->fileURI : "", sInfo, to ? to->mapHeader.get() : NULL); } card->changeSelection(to); if(screenType != CMenuScreen::campaignList) @@ -1036,31 +1036,24 @@ std::vector SelectionTab::getFiles(std::string dirURI, int resType) ++iterator; } - allItems.resize(ret.size()); return ret; } -void SelectionTab::parseMaps(const std::vector &files, int start, int threads) +void SelectionTab::parseMaps(const std::vector & files) { - ui8 mapBuffer[1500]; - - while(start < allItems.size()) + allItems.clear(); + for(int i = 0; i < files.size(); ++i) { - try - { - TInputStreamPtr stream(CMap::getMapStream(files[start].getName())); - int read = stream->read(mapBuffer, 1500); - - if(read < 50 || !mapBuffer[4]) - throw std::runtime_error("corrupted map file"); - - allItems[start].mapInit(files[start].getName(), mapBuffer); - } - catch(std::exception &e) - { - tlog3 << "\t\tWarning: failed to load map " << files[start].getName() << ": " << e.what() << std::endl; - } - start += threads; + try + { + CMapInfo mapInfo; + mapInfo.mapInit(files[i].getName()); + allItems.push_back(std::move(mapInfo)); + } + catch(std::exception & e) + { + tlog2 << "Map " << files[i].getName() << " is invalid. Message: " << e.what() << std::endl; + } } } @@ -1077,7 +1070,7 @@ void SelectionTab::parseGames(const std::vector &files, bool multi) if(std::memcmp(sign,"VCMISVG",7)) throw std::runtime_error("not a correct savefile!"); - allItems[i].mapHeader = new CMapHeader(); + allItems[i].mapHeader = std::unique_ptr(new CMapHeader); lf >> *(allItems[i].mapHeader) >> allItems[i].scenarioOpts; allItems[i].fileURI = files[i].getName(); allItems[i].countPlayers(); @@ -1086,12 +1079,12 @@ void SelectionTab::parseGames(const std::vector &files, bool multi) if((allItems[i].actualHumanPlayers > 1) != multi) //if multi mode then only multi games, otherwise single { - vstd::clear_pointer(allItems[i].mapHeader); + allItems[i].mapHeader.reset(nullptr); } } catch(std::exception &e) { - vstd::clear_pointer(allItems[i].mapHeader); + allItems[i].mapHeader.reset(nullptr); tlog3 << "Failed to process " << files[i].getName() <<": " << e.what() << std::endl; } } @@ -2612,8 +2605,8 @@ CScenarioInfo::~CScenarioInfo() bool mapSorter::operator()(const CMapInfo *aaa, const CMapInfo *bbb) { - const CMapHeader * a = aaa->mapHeader, - * b = bbb->mapHeader; + const CMapHeader * a = aaa->mapHeader.get(), + * b = bbb->mapHeader.get(); if(a && b) //if we are sorting scenarios { switch (sortBy) @@ -2949,10 +2942,10 @@ void CBonusSelection::selectMap( int whichOne ) ourCampaign->currentMap = whichOne; //get header - int i = 0; delete ourHeader; - ourHeader = new CMapHeader(); - ourHeader->initFromMemory((const unsigned char*)ourCampaign->camp->mapPieces.find(whichOne)->second.data(), i); + std::string & headerStr = ourCampaign->camp->mapPieces.find(whichOne)->second; + auto buffer = reinterpret_cast(headerStr.data()); + ourHeader = CMapService::loadMapHeader(buffer, headerStr.size()).release(); std::map names; names[1] = settings["general"]["playerName"].String(); diff --git a/client/CPreGame.h b/client/CPreGame.h index d051162e8..fd6783f0f 100644 --- a/client/CPreGame.h +++ b/client/CPreGame.h @@ -5,7 +5,7 @@ #include "../lib/StartInfo.h" #include "GUIClasses.h" #include "FunctionList.h" -#include "../lib/CMapInfo.h" +#include "../lib/Map/CMapInfo.h" /* * CPreGame.h, part of VCMI engine @@ -142,7 +142,7 @@ class SelectionTab : public CIntObject private: CDefHandler *format; //map size - void parseMaps(const std::vector &files, int start = 0, int threads = 1); + void parseMaps(const std::vector &files); void parseGames(const std::vector &files, bool multi); void parseCampaigns(const std::vector & files ); std::vector getFiles(std::string dirURI, int resType); diff --git a/client/Client.cpp b/client/Client.cpp index e6c68330b..951044ea7 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -1,7 +1,7 @@ #include "StdInc.h" #include "CMusicHandler.h" -#include "../lib/CCampaignHandler.h" +#include "../lib/Map/CCampaignHandler.h" #include "../CCallback.h" #include "../lib/CConsoleHandler.h" #include "CGameInfo.h" @@ -23,7 +23,7 @@ #include "../lib/NetPacks.h" #include "../lib/VCMI_Lib.h" #include "../lib/VCMIDirs.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "../lib/JsonNode.h" #include "mapHandler.h" #include "../lib/CConfigHandler.h" diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index bf5a83464..1625f479b 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -26,7 +26,7 @@ #include "../lib/CSpellHandler.h" #include "../lib/CTownHandler.h" #include "../lib/CondSh.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "mapHandler.h" #include "../lib/CStopWatch.h" #include "../lib/NetPacks.h" diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 237ec94e8..dd3f86620 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -13,7 +13,7 @@ #include "../lib/CHeroHandler.h" #include "../lib/CObjectHandler.h" #include "../lib/VCMI_Lib.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "../lib/VCMIDirs.h" #include "../lib/CSpellHandler.h" #include "CSoundBase.h" @@ -22,7 +22,7 @@ #include "../lib/CConfigHandler.h" #include "UIFramework/SDL_Extensions.h" #include "BattleInterface/CBattleInterface.h" -#include "../lib/CCampaignHandler.h" +#include "../lib/Map/CCampaignHandler.h" #include "../lib/CGameState.h" #include "../lib/BattleState.h" #include "../lib/GameConstants.h" diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index 35d6a8552..d6fef8a4c 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -9,7 +9,7 @@ #include "../lib/CTownHandler.h" #include "Graphics.h" #include "../lib/CObjectHandler.h" -#include "../lib/map.h" +#include "../lib/Map/CMap.h" #include "CDefHandler.h" #include "../lib/CConfigHandler.h" #include "../lib/CGeneralTextHandler.h" diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 6f0411103..f4819359b 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2,7 +2,7 @@ #include "CGameState.h" #include -#include "CCampaignHandler.h" +#include "Map/CCampaignHandler.h" #include "CDefObjInfoHandler.h" #include "CArtHandler.h" #include "CBuildingHandler.h" @@ -15,11 +15,12 @@ #include "CModHandler.h" #include "VCMI_Lib.h" #include "Connection.h" -#include "map.h" +#include "Map/CMap.h" +#include "Map/CMapService.h" #include "StartInfo.h" #include "NetPacks.h" #include "RegisterTypes.h" -#include "CMapInfo.h" +#include "Map/CMapInfo.h" #include "BattleState.h" #include "JsonNode.h" #include "Filesystem/CResourceLoader.h" @@ -865,16 +866,18 @@ void CGameState::init(StartInfo * si) switch(scenarioOps->mode) { case StartInfo::NEW_GAME: - map = new CMap(scenarioOps->mapname); + tlog0 << "Open map file: " << scenarioOps->mapname << std::endl; + map = CMapService::loadMap(scenarioOps->mapname).release(); break; case StartInfo::CAMPAIGN: { + tlog0 << "Open campaign map file: " << scenarioOps->campState->currentMap << std::endl; auto campaign = scenarioOps->campState; assert(vstd::contains(campaign->camp->mapPieces, scenarioOps->campState->currentMap)); - std::string &mapContent = campaign->camp->mapPieces[scenarioOps->campState->currentMap]; - map = new CMap(); - map->initFromBytes((const ui8*)mapContent.c_str(), mapContent.size()); + std::string & mapContent = campaign->camp->mapPieces[scenarioOps->campState->currentMap]; + auto buffer = reinterpret_cast(mapContent.data()); + map = CMapService::loadMap(buffer, mapContent.size()).release(); } break; case StartInfo::DUEL: diff --git a/lib/CGameState.h b/lib/CGameState.h index d35061c38..f622051dd 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -5,7 +5,7 @@ #ifndef _MSC_VER #include "CCreatureHandler.h" #include "VCMI_Lib.h" -#include "map.h" +#include "Map/CMap.h" #endif #include "HeroBonus.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3c621f7fd..1b2583fc2 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -13,13 +13,16 @@ set(lib_SRCS Filesystem/CResourceLoader.cpp Filesystem/CFileInputStream.cpp Filesystem/CCompressedStream.cpp + Map/CCampaignHandler.cpp + Map/CMap.cpp + Map/CMapInfo.cpp + Map/CMapService.cpp BattleAction.cpp BattleHex.cpp BattleState.cpp CArtHandler.cpp CBattleCallback.cpp CBuildingHandler.cpp - CCampaignHandler.cpp CConfigHandler.cpp CConsoleHandler.cpp CCreatureHandler.cpp @@ -30,7 +33,6 @@ set(lib_SRCS CGeneralTextHandler.cpp CHeroHandler.cpp CLogger.cpp - CMapInfo.cpp CModHandler.cpp CObjectHandler.cpp CObstacleInstance.cpp @@ -41,7 +43,6 @@ set(lib_SRCS HeroBonus.cpp IGameCallback.cpp JsonNode.cpp - map.cpp NetPacksLib.cpp ResourceSet.cpp VCMI_Lib.cpp diff --git a/lib/CMapInfo.cpp b/lib/CMapInfo.cpp deleted file mode 100644 index fc11df470..000000000 --- a/lib/CMapInfo.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "StdInc.h" -#include "CMapInfo.h" - -#include "StartInfo.h" -#include "map.h" -#include "CCampaignHandler.h" -#include "GameConstants.h" - -void CMapInfo::countPlayers() -{ - actualHumanPlayers = playerAmnt = humanPlayers = 0; - for(int i=0;iplayers[i].canHumanPlay) - { - playerAmnt++; - humanPlayers++; - } - else if(mapHeader->players[i].canComputerPlay) - { - playerAmnt++; - } - } - - if(scenarioOpts) - for (auto i = scenarioOpts->playerInfos.cbegin(); i != scenarioOpts->playerInfos.cend(); i++) - if(i->second.human) - actualHumanPlayers++; -} - -CMapInfo::CMapInfo(bool map) - : mapHeader(NULL), campaignHeader(NULL), scenarioOpts(NULL) -{ -} - -void CMapInfo::mapInit(const std::string &fname, const ui8 *map ) -{ - fileURI = fname; - int i = 0; - mapHeader = new CMapHeader(); - mapHeader->version = EMapFormat::INVALID; - - try - { - mapHeader->initFromMemory(map, i); - countPlayers(); - } - catch (const std::exception &e) - { - tlog1 << "\t\tWarning: evil map file: " << fname << ": " << e.what() << std::endl; - delete mapHeader; - mapHeader = NULL; - } -} - -CMapInfo::~CMapInfo() -{ - delete mapHeader; - delete campaignHeader; -} - -void CMapInfo::campaignInit() -{ - campaignHeader = new CCampaignHeader( CCampaignHandler::getHeader(fileURI) ); -} - -void CMapInfo::setHeader(CMapHeader *header) -{ - mapHeader = header; -} - diff --git a/lib/CMapInfo.h b/lib/CMapInfo.h deleted file mode 100644 index 582f8d4da..000000000 --- a/lib/CMapInfo.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - - - -class CMapHeader; -class CCampaignHeader; -struct StartInfo; - -/// A class which stores the count of human players and all players, the filename, -/// scenario options, the map header information,... -class DLL_LINKAGE CMapInfo -{ -public: - CMapHeader * mapHeader; //may be NULL if campaign - CCampaignHeader * campaignHeader; //may be NULL if scenario - StartInfo *scenarioOpts; //options with which scenario has been started (used only with saved games) - std::string fileURI; - std::string date; - int playerAmnt, //players in map - humanPlayers; //players ALLOWED to be controlled by human - int actualHumanPlayers; // >1 if multiplayer game - CMapInfo(bool map = true); - ~CMapInfo(); - //CMapInfo(const std::string &fname, const ui8 *map); - void setHeader(CMapHeader *header); - void mapInit(const std::string &fname, const ui8 *map); - void campaignInit(); - void countPlayers(); - - template void serialize(Handler &h, const int Version) - { - h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers; - h & actualHumanPlayers; - } -}; diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 9420e696a..5cc5a74f2 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -16,7 +16,7 @@ #include "CGameState.h" #include "NetPacks.h" #include "StartInfo.h" -#include "map.h" +#include "Map/CMap.h" #include #include "CBuildingHandler.h" #include "JsonNode.h" diff --git a/lib/Connection.cpp b/lib/Connection.cpp index f943535de..d9260e676 100644 --- a/lib/Connection.cpp +++ b/lib/Connection.cpp @@ -6,11 +6,11 @@ #endif //for smart objs serialization over net -#include "../lib/CMapInfo.h" +#include "../lib/Map/CMapInfo.h" #include "StartInfo.h" #include "BattleState.h" #include "CGameState.h" -#include "map.h" +#include "Map/CMap.h" #include "CModHandler.h" #include "CObjectHandler.h" #include "CCreatureHandler.h" @@ -19,7 +19,7 @@ #include "CHeroHandler.h" #include "CSpellHandler.h" #include "CTownHandler.h" -#include "CCampaignHandler.h" +#include "Map/CCampaignHandler.h" #include "NetPacks.h" #include diff --git a/lib/Connection.h b/lib/Connection.h index 4ca62f4b0..f4d6c9598 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -23,7 +23,7 @@ #include "ConstTransitivePtr.h" #include "CCreatureSet.h" //for CStackInstance #include "CObjectHandler.h" //for CArmedInstance -#include "CCampaignHandler.h" //for CCampaignState +#include "Map/CCampaignHandler.h" //for CCampaignState const ui32 version = 733; const TSlot COMMANDER_SLOT_PLACEHOLDER = -2; diff --git a/lib/Filesystem/CFileInputStream.cpp b/lib/Filesystem/CFileInputStream.cpp index 76ad5bb95..414c72d94 100644 --- a/lib/Filesystem/CFileInputStream.cpp +++ b/lib/Filesystem/CFileInputStream.cpp @@ -49,9 +49,9 @@ si64 CFileInputStream::read(ui8 * data, si64 size) si64 CFileInputStream::seek(si64 position) { - fileStream.seekg(dataStart + std::min(position, dataSize)); - - return tell(); + si64 origin = tell(); + fileStream.seekg(dataStart + std::min(position, dataSize)); + return tell() - origin; } si64 CFileInputStream::tell() diff --git a/lib/Filesystem/CMemoryStream.cpp b/lib/Filesystem/CMemoryStream.cpp index a8f389dd3..4e8bf2b1c 100644 --- a/lib/Filesystem/CMemoryStream.cpp +++ b/lib/Filesystem/CMemoryStream.cpp @@ -1,45 +1,37 @@ #include "StdInc.h" #include "CMemoryStream.h" -CMemoryStream::CMemoryStream(const ui8 * data, si64 size, bool freeData /*= false*/): - data(data), - size(size), - position(0), - freeData(freeData) +CMemoryStream::CMemoryStream(const ui8 * data, si64 size) : + data(data), size(size), position(0) { -} -CMemoryStream::~CMemoryStream() -{ - if(freeData) - { - delete[] data; - } } si64 CMemoryStream::read(ui8 * data, si64 size) { - std::copy(this->data + position, this->data + position + size, data); + si64 toRead = std::min(this->size - tell(), size); + std::copy(this->data + position, this->data + position + toRead, data); position += size; - return size; + return toRead; } si64 CMemoryStream::seek(si64 position) { - si64 diff = this->position; - this->position = position; - return position - diff; + si64 origin = tell(); + this->position = std::min(position, size); + return tell() - origin; } si64 CMemoryStream::tell() { - return this->position; + return this->position; } si64 CMemoryStream::skip(si64 delta) { - this->position += delta; - return delta; + si64 origin = tell(); + this->position += std::min(size - origin, delta); + return tell() - origin; } si64 CMemoryStream::getSize() diff --git a/lib/Filesystem/CMemoryStream.h b/lib/Filesystem/CMemoryStream.h index 9a0ed2bc7..29fe76f74 100644 --- a/lib/Filesystem/CMemoryStream.h +++ b/lib/Filesystem/CMemoryStream.h @@ -20,18 +20,12 @@ class DLL_LINKAGE CMemoryStream : public CInputStream { public: /** - * C-tor. The data buffer will be freed by the stream's destructor. + * C-tor. The data buffer won't be free'd. (no ownership) * - * @param data A pointer to the data array. + * @param data a pointer to the data array. * @param size The size in bytes of the array. - * @param freeData Flag which specifies if the data array should be freed in the memory stream's destructor. */ - CMemoryStream(const ui8 * data, si64 size, bool freeData); - - /** - * D-tor. Frees the data array if the freeData flag was set to true. - */ - ~CMemoryStream(); + CMemoryStream(const ui8 * data, si64 size); /** * Reads n bytes from the stream into the data buffer. @@ -73,15 +67,12 @@ public: si64 getSize(); private: - /** A pointer to the data array. */ - const ui8 * data; + /** A pointer to the data array. */ + const ui8 * data; /** The size in bytes of the array. */ si64 size; /** Current reading position of the stream. */ si64 position; - - /** Flag which specifies if the data array should be freed in the memory stream's destructor. */ - bool freeData; }; diff --git a/lib/Filesystem/CResourceLoader.h b/lib/Filesystem/CResourceLoader.h index b0dd77eb0..cd430c606 100644 --- a/lib/Filesystem/CResourceLoader.h +++ b/lib/Filesystem/CResourceLoader.h @@ -330,8 +330,7 @@ public: */ void addLoader(std::string mountPoint, shared_ptr loader, bool writeable); - public: - +private: /** * Contains lists of same resources which can be accessed uniquely by an * resource identifier. diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 1d96deb94..02acbaeac 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -4,7 +4,7 @@ #include #include "CGameState.h" -#include "map.h" +#include "Map/CMap.h" #include "CObjectHandler.h" #include "CHeroHandler.h" #include "StartInfo.h" diff --git a/lib/CCampaignHandler.cpp b/lib/Map/CCampaignHandler.cpp similarity index 93% rename from lib/CCampaignHandler.cpp rename to lib/Map/CCampaignHandler.cpp index 02123f102..727774e81 100644 --- a/lib/CCampaignHandler.cpp +++ b/lib/Map/CCampaignHandler.cpp @@ -1,15 +1,15 @@ #include "StdInc.h" #include "CCampaignHandler.h" -#include "Filesystem/CResourceLoader.h" -#include "Filesystem/CCompressedStream.h" -#include "../lib/VCMI_Lib.h" -#include "../lib/vcmi_endian.h" -#include "CGeneralTextHandler.h" -#include "StartInfo.h" -#include "CArtHandler.h" //for hero crossover -#include "CObjectHandler.h" //for hero crossover -#include "CHeroHandler.h" +#include "../Filesystem/CResourceLoader.h" +#include "../Filesystem/CCompressedStream.h" +#include "../VCMI_Lib.h" +#include "../vcmi_endian.h" +#include "../CGeneralTextHandler.h" +#include "../StartInfo.h" +#include "../CArtHandler.h" //for hero crossover +#include "../CObjectHandler.h" //for hero crossover +#include "../CHeroHandler.h" namespace fs = boost::filesystem; diff --git a/lib/CCampaignHandler.h b/lib/Map/CCampaignHandler.h similarity index 100% rename from lib/CCampaignHandler.h rename to lib/Map/CCampaignHandler.h diff --git a/lib/Map/CMap.cpp b/lib/Map/CMap.cpp new file mode 100644 index 000000000..20bf68c28 --- /dev/null +++ b/lib/Map/CMap.cpp @@ -0,0 +1,241 @@ +#include "StdInc.h" +#include "CMap.h" + +#include "../CObjectHandler.h" +#include "../CArtHandler.h" + +PlayerInfo::PlayerInfo(): p7(0), p8(0), p9(0), canHumanPlay(0), canComputerPlay(0), + AITactic(0), isFactionRandom(0), + mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0), + team(255), generateHero(0) +{ + +} + +si8 PlayerInfo::defaultCastle() const +{ + assert(!allowedFactions.empty()); // impossible? + + if(allowedFactions.size() == 1) + { + // only one faction is available - pick it + return *allowedFactions.begin(); + } + + // set to random + return -1; +} + +si8 PlayerInfo::defaultHero() const +{ + // we will generate hero in front of main town + if((generateHeroAtMainTown && hasMainTown) || p8) + { + //random hero + return -1; + } + + return -2; +} + +CMapHeader::CMapHeader() : version(EMapFormat::INVALID) +{ + areAnyPLayers = difficulty = levelLimit = howManyTeams = 0; + height = width = twoLevel = -1; +} + +CMapHeader::~CMapHeader() +{ + +} + +void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total) +{ + for(int fx=0; fx<8; ++fx) + { + for(int fy=0; fy<6; ++fy) + { + int xVal = obj->pos.x + fx - 7; + int yVal = obj->pos.y + fy - 5; + int zVal = obj->pos.z; + if(xVal>=0 && xVal=0 && yValdefInfo->visitMap[fy] >> (7 - fx)) & 1)) + { + curt.visitableObjects -= obj; + curt.visitable = curt.visitableObjects.size(); + } + if(total || !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) + { + curt.blockingObjects -= obj; + curt.blocked = curt.blockingObjects.size(); + } + } + } + } +} +void CMap::addBlockVisTiles(CGObjectInstance * obj) +{ + for(int fx=0; fx<8; ++fx) + { + for(int fy=0; fy<6; ++fy) + { + int xVal = obj->pos.x + fx - 7; + int yVal = obj->pos.y + fy - 5; + int zVal = obj->pos.z; + if(xVal>=0 && xVal=0 && yValdefInfo->visitMap[fy] >> (7 - fx)) & 1)) + { + curt.visitableObjects.push_back(obj); + curt.visitable = true; + } + if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) + { + curt.blockingObjects.push_back(obj); + curt.blocked = true; + } + } + } + } +} + +CMap::CMap() : terrain(nullptr) +{ + +} + +CMap::~CMap() +{ + if(terrain) + { + for(int ii=0;ii >::iterator i = events.begin(); i != events.end(); i++) + { + i->dellNull(); + } +} + +CGHeroInstance * CMap::getHero(int heroID) +{ + for(ui32 i=0; isubID == heroID) + return heroes[i]; + return nullptr; +} + +bool CMap::isInTheMap(const int3 &pos) const +{ + if(pos.x<0 || pos.y<0 || pos.z<0 || pos.x >= width || pos.y >= height || pos.z > twoLevel) + return false; + else return true; +} + +TerrainTile & CMap::getTile( const int3 & tile ) +{ + return terrain[tile.x][tile.y][tile.z]; +} + +const TerrainTile & CMap::getTile( const int3 & tile ) const +{ + return terrain[tile.x][tile.y][tile.z]; +} + +bool CMap::isWaterTile(const int3 &pos) const +{ + return isInTheMap(pos) && getTile(pos).tertype == ETerrainType::WATER; +} + +const CGObjectInstance *CMap::getObjectiveObjectFrom(int3 pos, bool lookForHero) +{ + const std::vector & objs = getTile(pos).visitableObjects; + assert(objs.size()); + if(objs.size() > 1 && lookForHero && objs.front()->ID != Obj::HERO) + { + assert(objs.back()->ID == Obj::HERO); + return objs.back(); + } + else + return objs.front(); +} + +void CMap::checkForObjectives() +{ + if(isInTheMap(victoryCondition.pos)) + victoryCondition.obj = getObjectiveObjectFrom(victoryCondition.pos, victoryCondition.condition == EVictoryConditionType::BEATHERO); + + if(isInTheMap(lossCondition.pos)) + lossCondition.obj = getObjectiveObjectFrom(lossCondition.pos, lossCondition.typeOfLossCon == ELossConditionType::LOSSHERO); +} + +void CMap::addNewArtifactInstance( CArtifactInstance *art ) +{ + art->id = artInstances.size(); + artInstances.push_back(art); +} + +void CMap::eraseArtifactInstance(CArtifactInstance *art) +{ + assert(artInstances[art->id] == art); + artInstances[art->id].dellNull(); +} + +LossCondition::LossCondition() +{ + obj = NULL; + timeLimit = -1; + pos = int3(-1,-1,-1); +} + +VictoryCondition::VictoryCondition() +{ + pos = int3(-1,-1,-1); + obj = NULL; + ID = allowNormalVictory = appliesToAI = count = 0; +} + +bool TerrainTile::entrableTerrain(const TerrainTile * from /*= NULL*/) const +{ + return entrableTerrain(from ? from->tertype != ETerrainType::WATER : true, from ? from->tertype == ETerrainType::WATER : true); +} + +bool TerrainTile::entrableTerrain(bool allowLand, bool allowSea) const +{ + return tertype != ETerrainType::ROCK + && ((allowSea && tertype == ETerrainType::WATER) || (allowLand && tertype != ETerrainType::WATER)); +} + +bool TerrainTile::isClear(const TerrainTile *from /*= NULL*/) const +{ + return entrableTerrain(from) && !blocked; +} + +int TerrainTile::topVisitableID() const +{ + return visitableObjects.size() ? visitableObjects.back()->ID : -1; +} + +bool TerrainTile::isCoastal() const +{ + return extTileFlags & 64; +} + +bool TerrainTile::hasFavourableWinds() const +{ + return extTileFlags & 128; +} + +bool TerrainTile::isWater() const +{ + return tertype == ETerrainType::WATER; +} diff --git a/lib/map.h b/lib/Map/CMap.h similarity index 62% rename from lib/map.h rename to lib/Map/CMap.h index af7c381db..42c159bf5 100644 --- a/lib/map.h +++ b/lib/Map/CMap.h @@ -1,18 +1,6 @@ -#pragma once - - -#ifndef _MSC_VER -#include "CObjectHandler.h" -#include "CDefObjInfoHandler.h" -#endif - -#include "ConstTransitivePtr.h" -#include "ResourceSet.h" -#include "int3.h" -#include "GameConstants.h" /* - * map.h, part of VCMI engine + * CMap.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -21,6 +9,18 @@ * */ +#pragma once + +#ifndef _MSC_VER +#include "../CObjectHandler.h" +#include "../CDefObjInfoHandler.h" +#endif + +#include "../ConstTransitivePtr.h" +#include "../ResourceSet.h" +#include "../int3.h" +#include "../GameConstants.h" + class CArtifactInstance; class CGDefInfo; class CGObjectInstance; @@ -31,9 +31,7 @@ class CQuest; class CGTownInstance; class IModableArt; class IQuestObject; - class CInputStream; -typedef std::unique_ptr TInputStreamPtr; namespace ETerrainType { @@ -139,21 +137,21 @@ struct DLL_LINKAGE TerrainTile * * @return true if the terrain type is water */ - bool isWater() const; + bool isWater() const; /** * Gets true if the terrain tile is coastal. * * @return true if the terrain tile is coastal */ - bool isCoastal() const; + bool isCoastal() const; /** * Gets true if the terrain tile has favourable winds. * * @return true if the terrain tile has favourable winds */ - bool hasFavourableWinds() const; + bool hasFavourableWinds() const; /** * Serialize method. @@ -174,22 +172,22 @@ struct DLL_LINKAGE TerrainTile /** * The hero name struct consists of the hero id and name. */ -struct DLL_LINKAGE SheroName +struct DLL_LINKAGE SheroName { /** the id of the hero */ - int heroID; + int heroID; /** the name of the hero */ - std::string heroName; + std::string heroName; /** * Serialize method. */ template void serialize(Handler & h, const int version) - { - h & heroID & heroName; - } + { + h & heroID & heroName; + } }; /** @@ -211,10 +209,10 @@ struct DLL_LINKAGE PlayerInfo ui8 powerPlacehodlers; /** player can be played by a human */ - ui8 canHumanPlay; + ui8 canHumanPlay; /** player can be played by the computer */ - ui8 canComputerPlay; + ui8 canComputerPlay; /** defines the tactical setting of the AI: 0 - random, 1 - warrior, 2 - builder, 3 - explorer */ ui32 AITactic; @@ -229,25 +227,25 @@ struct DLL_LINKAGE PlayerInfo ui32 mainHeroPortrait; /** the name of the main hero */ - std::string mainHeroName; + std::string mainHeroName; /** list of available heroes */ - std::vector heroesNames; + std::vector heroesNames; /** has the player a main town */ - ui8 hasMainTown; + ui8 hasMainTown; /** generates the hero at the main town */ - ui8 generateHeroAtMainTown; + ui8 generateHeroAtMainTown; /** the position of the main town */ - int3 posOfMainTown; + int3 posOfMainTown; /** the team id to which the player belongs to */ - ui8 team; + ui8 team; /** unused. generates a hero */ - ui8 generateHero; + ui8 generateHero; /** * Default constructor. @@ -273,11 +271,11 @@ struct DLL_LINKAGE PlayerInfo */ template void serialize(Handler & h, const int version) - { - h & p7 & p8 & p9 & canHumanPlay & canComputerPlay & AITactic & allowedFactions & isFactionRandom & - mainHeroPortrait & mainHeroName & heroesNames & hasMainTown & generateHeroAtMainTown & - posOfMainTown & team & generateHero; - } + { + h & p7 & p8 & p9 & canHumanPlay & canComputerPlay & AITactic & allowedFactions & isFactionRandom & + mainHeroPortrait & mainHeroName & heroesNames & hasMainTown & generateHeroAtMainTown & + posOfMainTown & team & generateHero; + } }; /** @@ -286,10 +284,10 @@ struct DLL_LINKAGE PlayerInfo struct DLL_LINKAGE LossCondition { /** specifies the condition type */ - ELossConditionType::ELossConditionType typeOfLossCon; + ELossConditionType::ELossConditionType typeOfLossCon; /** the position of an object which mustn't be lost */ - int3 pos; + int3 pos; /** time limit in days, -1 if not used */ si32 timeLimit; @@ -307,9 +305,9 @@ struct DLL_LINKAGE LossCondition */ template void serialize(Handler & h, const int version) - { - h & typeOfLossCon & pos & timeLimit & obj; - } + { + h & typeOfLossCon & pos & timeLimit & obj; + } }; /** @@ -349,9 +347,9 @@ struct DLL_LINKAGE VictoryCondition */ template void serialize(Handler & h, const int version) - { - h & condition & allowNormalVictory & appliesToAI & pos & ID & count & obj; - } + { + h & condition & allowNormalVictory & appliesToAI & pos & ID & count & obj; + } }; /** @@ -370,9 +368,9 @@ struct DLL_LINKAGE Rumor */ template void serialize(Handler & h, const int version) - { - h & name & text; - } + { + h & name & text; + } }; /** @@ -381,13 +379,13 @@ struct DLL_LINKAGE Rumor struct DLL_LINKAGE DisposedHero { /** the id of the hero */ - ui32 ID; + ui32 ID; /** the portrait id of the hero, 0xFF is default */ ui16 portrait; /** the name of the hero */ - std::string name; + std::string name; /** who can hire this hero (bitfield) */ ui8 players; @@ -397,9 +395,9 @@ struct DLL_LINKAGE DisposedHero */ template void serialize(Handler & h, const int version) - { - h & ID & portrait & name & players; - } + { + h & ID & portrait & name & players; + } }; /// Class which manages map events. @@ -424,13 +422,13 @@ public: ui8 players; /** affected humans */ - ui8 humanAffected; + ui8 humanAffected; /** affacted computer players */ - ui8 computerAffected; + ui8 computerAffected; /** the day counted continously where the event happens */ - ui32 firstOccurence; + ui32 firstOccurence; /** specifies after how many days the event will occur the next time; 0 if event occurs only one time */ ui32 nextOccurence; @@ -449,10 +447,10 @@ public: */ template void serialize(Handler & h, const int version) - { - h & name & message & resources - & players & humanAffected & computerAffected & firstOccurence & nextOccurence; - } + { + h & name & message & resources + & players & humanAffected & computerAffected & firstOccurence & nextOccurence; + } }; /** @@ -475,10 +473,10 @@ public: */ template void serialize(Handler & h, const int version) - { + { h & static_cast(*this); - h & buildings & creatures; - } + h & buildings & creatures; + } }; namespace EMapFormat @@ -502,42 +500,10 @@ public: CMapHeader(); /** - * Constructor. - * - * @param map a pointer to an buffer which contains bytes describing a map header - */ - CMapHeader(const ui8 * map); - - /** - * Destructor. + * D-tor. */ virtual ~CMapHeader(); - /** - * Initializes the map header from memory. - * - * @param buffer a pointer to an buffer which contains bytes describing a map header - * @param i the index where to start reading from - */ - void initFromMemory(const ui8 * buffer, int & i); - - /** - * Loads victory/loss conditions. - * - * @param buffer a pointer to an buffer which contains bytes describing a map header - * @param i the index where to start reading from - */ - void loadViCLossConditions(const ui8 * buffer, int & i); - - /** - * Loads information about players. - * - * @param pom - * @param buffer a pointer to an buffer which contains bytes describing a map header - * @param i the index where to start reading from - */ - void loadPlayerInfo(int & pom, const ui8 * buffer, int & i); - /** the version of the map */ EMapFormat::EMapFormat version; @@ -566,7 +532,7 @@ public: ui8 levelLimit; /** the loss condition */ - LossCondition lossCondition; + LossCondition lossCondition; /** the victory condition */ VictoryCondition victoryCondition; @@ -575,7 +541,7 @@ public: std::vector players; /** number of teams */ - ui8 howManyTeams; + ui8 howManyTeams; /** list of allowed heroes, index is hero id */ std::vector allowedHeroes; @@ -588,10 +554,10 @@ public: */ template void serialize(Handler & h, const int Version) - { - h & version & name & description & width & height & twoLevel & difficulty & levelLimit & areAnyPLayers; + { + h & version & name & description & width & height & twoLevel & difficulty & levelLimit & areAnyPLayers; h & players & lossCondition & victoryCondition & howManyTeams & allowedHeroes; - } + } }; /** @@ -601,205 +567,15 @@ public: class DLL_LINKAGE CMap : public CMapHeader { public: - /** the checksum of the map */ - ui32 checksum; - - /** a 3-dimensional array of terrain tiles, access is as follows: x, y, level */ - TerrainTile*** terrain; - - /** list of rumors */ - std::vector rumors; - - /** list of disposed heroes */ - std::vector disposedHeroes; - - /** list of predefined heroes */ - std::vector > predefinedHeroes; - - /** list of .def files with definitions from .h3m (may be custom) */ - std::vector > customDefs; - - /** list of allowed spells, index is the spell id */ - std::vector allowedSpell; - - /** list of allowed artifacts, index is the artifact id */ - std::vector allowedArtifact; - - /** list of allowed abilities, index is the ability id */ - std::vector allowedAbilities; - - /** list of map events */ - std::list > events; - - /** specifies the position of the grail */ - int3 grailPos; - - /** specifies the radius of the grail */ - int grailRadious; - - /** list of objects */ - std::vector< ConstTransitivePtr > objects; - - /** list of heroes */ - std::vector< ConstTransitivePtr > heroes; - - /** list of towns */ - std::vector< ConstTransitivePtr > towns; - - /** list of artifacts */ - std::vector< ConstTransitivePtr > artInstances; - - /** list of quests */ - std::vector< ConstTransitivePtr > quests; - - /** associative list to identify which hero/creature id belongs to which object id(index for objects) */ - bmap questIdentifierToId; + /** + * Default constructor. + */ + CMap(); /** - * Creates map from decompressed .h3m data. - * - * @param buffer a pointer to an buffer which contains bytes describing a map - * @param size the length of the buffer + * Destructor. */ - void initFromBytes(const ui8 * buffer, size_t size); - - /** - * Reads events from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - */ - void readEvents(const ui8 * buffer, int & i); - - /** - * Reads objects from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing objects - * @param i the index where to start reading from - */ - void readObjects(const ui8 * buffer, int & i); - - /** - * Loads quest information and stores that to a quest guard object. - * - * @param guard the quest guard object where the quest info should be applied to - * @param buffer a pointer to an buffer which contains bytes describing objects - * @param i the index where to start reading from - */ - void loadQuest(IQuestObject * guard, const ui8 * buffer, int & i); - - /** - * Reads def information from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing def information - * @param i the index where to start reading from - */ - void readDefInfo(const ui8 * buffer, int & i); - - /** - * Reads terrain data from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing terrain data - * @param i the index where to start reading from - */ - void readTerrain(const ui8 * buffer, int & i); - - /** - * Reads predefined heroes from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - */ - void readPredefinedHeroes(const ui8 * buffer, int & i); - - /** - * Reads events from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - */ - void readHeader(const ui8 * buffer, int & i); - - /** - * Reads events from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - */ - void readRumors(const ui8 * buffer, int & i); - - /** - * Loads a hero from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - * @param idToBeGiven the object id which should be set for the hero - * @return a object instance - */ - CGObjectInstance * loadHero(const ui8 * buffer, int & i, int idToBeGiven); - - /** - * Loads artifacts of a hero from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - * @param hero the hero which should hold those artifacts - */ - void loadArtifactsOfHero(const ui8 * buffer, int & i, CGHeroInstance * hero); - - /** - * Loads an artifact from a buffer to the given slot of the specified hero. - * - * @param hero the hero which should hold that artifact - * @param slot the artifact slot where to place that artifact - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - * @return true if it loaded an artifact - */ - bool loadArtifactToSlot(CGHeroInstance * hero, int slot, const ui8 * buffer, int & i); - - /** - * Loads a town from a buffer. - * - * @param town a pointer to a town object which gets filled by data - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - * @param castleID the id of the castle type - */ - void loadTown(CGObjectInstance * & town, const ui8 * buffer, int & i, int castleID); - - /** - * Loads a seer hut from a buffer. - * - * @param buffer a pointer to an buffer which contains bytes describing events - * @param i the index where to start reading from - * @param seerHut the seer hut object which gets filled by data - * @return index of the reading position of the buffer - */ - int loadSeerHut(const ui8 * buffer, int i, CGObjectInstance * & seerHut); - - /** - * Creates an artifact instance. - * - * @param aid the id of the artifact - * @param spellID optional. the id of a spell if a spell scroll object should be created - * @return the created artifact instance - */ - CArtifactInstance * createArt(int aid, int spellID = -1); - - /** - * Adds the specified artifact instance to the list of artifacts of this map. - * - * @param art the artifact which should be added to the list of artifacts - */ - void addNewArtifactInstance(CArtifactInstance * art); - - /** - * Adds a quest to the list of quests of this map. - * - * @param quest the quest object which should be added to the list of quests - */ - void addQuest(CGObjectInstance * quest); + ~CMap(); /** * Erases an artifact instance. @@ -822,14 +598,14 @@ public: /** * Sets the victory/loss condition objectives. */ - void checkForObjectives(); + void checkForObjectives(); /** * Adds an visitable/blocking object to a terrain tile. * * @param obj the visitable/blocking object to add to a tile */ - void addBlockVisTiles(CGObjectInstance * obj); + void addBlockVisTiles(CGObjectInstance * obj); /** * Removes an visitable/blocking object from a terrain tile. @@ -839,23 +615,6 @@ public: */ void removeBlockVisTiles(CGObjectInstance * obj, bool total = false); - /** - * Constructor. Creates a map from file. - * - * @param filename the name of the h3m map file - */ - CMap(std::string filename); - - /** - * Default constructor. - */ - CMap(); - - /** - * Destructor. - */ - ~CMap(); - /** * Gets the terrain tile of the specified position. * @@ -896,27 +655,80 @@ public: bool isWaterTile(const int3 & pos) const; /** - * Gets a input stream from the given map name. + * Adds the specified artifact instance to the list of artifacts of this map. * - * @param name the name of the map - * @return a unique ptr to the input stream + * @param art the artifact which should be added to the list of artifacts */ - static TInputStreamPtr getMapStream(std::string name); + void addNewArtifactInstance(CArtifactInstance * art); + + /** the checksum of the map */ + ui32 checksum; + + /** a 3-dimensional array of terrain tiles, access is as follows: x, y, level */ + TerrainTile*** terrain; + + /** list of rumors */ + std::vector rumors; + + /** list of disposed heroes */ + std::vector disposedHeroes; + + /** list of predefined heroes */ + std::vector > predefinedHeroes; + + /** list of .def files with definitions from .h3m (may be custom) */ + std::vector > customDefs; + + /** list of allowed spells, index is the spell id */ + std::vector allowedSpell; + + /** list of allowed artifacts, index is the artifact id */ + std::vector allowedArtifact; + + /** list of allowed abilities, index is the ability id */ + std::vector allowedAbilities; + + /** list of map events */ + std::list > events; + + /** specifies the position of the grail */ + int3 grailPos; + + /** specifies the radius of the grail */ + int grailRadious; + + /** list of objects */ + std::vector< ConstTransitivePtr > objects; + + /** list of heroes */ + std::vector< ConstTransitivePtr > heroes; + + /** list of towns */ + std::vector< ConstTransitivePtr > towns; + + /** list of artifacts */ + std::vector< ConstTransitivePtr > artInstances; + + /** list of quests */ + std::vector< ConstTransitivePtr > quests; + + /** associative list to identify which hero/creature id belongs to which object id(index for objects) */ + bmap questIdentifierToId; /** * Serialize method. */ template void serialize(Handler &h, const int formatVersion) - { - h & static_cast(*this); + { + h & static_cast(*this); h & rumors & allowedSpell & allowedAbilities & allowedArtifact & events & grailPos; h & artInstances & quests; h & questIdentifierToId; - //TODO: viccondetails - if(h.saving) - { + //TODO: viccondetails + if(h.saving) + { // Save terrain for(int i = 0; i < width ; ++i) { @@ -924,60 +736,60 @@ public: { for(int k = 0; k <= twoLevel; ++k) { - h & terrain[i][j][k]; + h & terrain[i][j][k]; } } } - } - else - { + } + else + { // Load terrain terrain = new TerrainTile**[width]; for(int ii = 0; ii < width; ++ii) - { + { terrain[ii] = new TerrainTile*[height]; for(int jj = 0; jj < height; ++jj) { terrain[ii][jj] = new TerrainTile[twoLevel + 1]; } - } + } for(int i = 0; i < width ; ++i) { for(int j = 0; j < height ; ++j) { for(int k = 0; k <= twoLevel ; ++k) { - h & terrain[i][j][k]; + h & terrain[i][j][k]; } } } - } - + } + h & customDefs & objects; // static members - h & CGTeleport::objs; - h & CGTeleport::gates; - h & CGKeys::playerKeyMap; - h & CGMagi::eyelist; - h & CGObelisk::obeliskCount & CGObelisk::visited; - h & CGTownInstance::merchantArtifacts; + h & CGTeleport::objs; + h & CGTeleport::gates; + h & CGKeys::playerKeyMap; + h & CGMagi::eyelist; + h & CGObelisk::obeliskCount & CGObelisk::visited; + h & CGTownInstance::merchantArtifacts; - if(!h.saving) - { + if(!h.saving) + { for(ui32 i = 0; i < objects.size(); ++i) - { - if(!objects[i]) continue; + { + if(!objects[i]) continue; - switch (objects[i]->ID) - { - case Obj::HERO: - heroes.push_back (static_cast(+objects[i])); - break; - case Obj::TOWN: - towns.push_back (static_cast(+objects[i])); - break; - } + switch (objects[i]->ID) + { + case Obj::HERO: + heroes.push_back (static_cast(+objects[i])); + break; + case Obj::TOWN: + towns.push_back (static_cast(+objects[i])); + break; + } // recreate blockvis map addBlockVisTiles(objects[i]); @@ -985,46 +797,46 @@ public: // if hero is visiting/garrisoned in town set appropriate pointers for(ui32 i = 0; i < heroes.size(); ++i) - { + { int3 vistile = heroes[i]->pos; vistile.x++; for(ui32 j = 0; j < towns.size(); ++j) - { + { // hero stands on the town entrance if(vistile == towns[j]->pos) - { - if(heroes[i]->inTownGarrison) - { - towns[j]->garrisonHero = heroes[i]; - removeBlockVisTiles(heroes[i]); - } - else - { - towns[j]->visitingHero = heroes[i]; - } + { + if(heroes[i]->inTownGarrison) + { + towns[j]->garrisonHero = heroes[i]; + removeBlockVisTiles(heroes[i]); + } + else + { + towns[j]->visitingHero = heroes[i]; + } - heroes[i]->visitedTown = towns[j]; - break; - } - } + heroes[i]->visitedTown = towns[j]; + break; + } + } - vistile.x -= 2; //manifest pos + vistile.x -= 2; //manifest pos const TerrainTile & t = getTile(vistile); if(t.tertype != ETerrainType::WATER) continue; - //hero stands on the water - he must be in the boat + //hero stands on the water - he must be in the boat for(ui32 j = 0; j < t.visitableObjects.size(); ++j) - { - if(t.visitableObjects[j]->ID == Obj::BOAT) - { + { + if(t.visitableObjects[j]->ID == Obj::BOAT) + { CGBoat * b = static_cast(t.visitableObjects[j]); - heroes[i]->boat = b; - b->hero = heroes[i]; - removeBlockVisTiles(b); - break; - } - } + heroes[i]->boat = b; + b->hero = heroes[i]; + removeBlockVisTiles(b); + break; + } + } } } - } + } }; diff --git a/lib/Map/CMapInfo.cpp b/lib/Map/CMapInfo.cpp new file mode 100644 index 000000000..0beb83edc --- /dev/null +++ b/lib/Map/CMapInfo.cpp @@ -0,0 +1,49 @@ +#include "StdInc.h" +#include "CMapInfo.h" + +#include "../StartInfo.h" +#include "CMap.h" +#include "CCampaignHandler.h" +#include "../GameConstants.h" +#include "CMapService.h" + +void CMapInfo::countPlayers() +{ + actualHumanPlayers = playerAmnt = humanPlayers = 0; + for(int i=0;iplayers[i].canHumanPlay) + { + playerAmnt++; + humanPlayers++; + } + else if(mapHeader->players[i].canComputerPlay) + { + playerAmnt++; + } + } + + if(scenarioOpts) + for (auto i = scenarioOpts->playerInfos.cbegin(); i != scenarioOpts->playerInfos.cend(); i++) + if(i->second.human) + actualHumanPlayers++; +} + +CMapInfo::CMapInfo() : mapHeader(nullptr), campaignHeader(nullptr), + scenarioOpts(nullptr) +{ + +} + +void CMapInfo::mapInit(const std::string & fname) +{ + fileURI = fname; + mapHeader = CMapService::loadMapHeader(fname); + countPlayers(); +} + +void CMapInfo::campaignInit() +{ + campaignHeader = std::unique_ptr(new CCampaignHeader(CCampaignHandler::getHeader(fileURI))); +} + diff --git a/lib/Map/CMapInfo.h b/lib/Map/CMapInfo.h new file mode 100644 index 000000000..9cd9fea62 --- /dev/null +++ b/lib/Map/CMapInfo.h @@ -0,0 +1,42 @@ +#pragma once + +// Forward class declarations aren't enough here. The compiler +// generated CMapInfo d-tor, generates the unique_ptr d-tor as well here +// as a inline method. The unique_ptr d-tor requires a complete type. Defining +// the CMapInfo d-tor to let the compiler add the d-tor stuff in the .cpp file +// would work with one exception. It prevents the generation of the move +// constructor which is needed. (Writing such a c-tor is nasty.) With the +// new c++11 keyword "default" for constructors this problem could be solved. But it isn't +// available for Visual Studio for now. (Empty d-tor in .cpp would be required anyway) +#include "CMap.h" +#include "CCampaignHandler.h" + +struct StartInfo; + +/** + * A class which stores the count of human players and all players, the filename, + * scenario options, the map header information,... + */ +class DLL_LINKAGE CMapInfo +{ +public: + std::unique_ptr mapHeader; //may be nullptr if campaign + std::unique_ptr campaignHeader; //may be nullptr if scenario + StartInfo * scenarioOpts; //options with which scenario has been started (used only with saved games) + std::string fileURI; + std::string date; + int playerAmnt; //players in map + int humanPlayers; //players ALLOWED to be controlled by human + int actualHumanPlayers; // >1 if multiplayer game + + CMapInfo(); + void mapInit(const std::string & fname); + void campaignInit(); + void countPlayers(); + + template void serialize(Handler &h, const int Version) + { + h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers; + h & actualHumanPlayers; + } +}; diff --git a/lib/Map/CMapService.cpp b/lib/Map/CMapService.cpp new file mode 100644 index 000000000..5b540d40f --- /dev/null +++ b/lib/Map/CMapService.cpp @@ -0,0 +1,2543 @@ +#include "StdInc.h" +#include "CMapService.h" +#include "../Filesystem/CResourceLoader.h" +#include "../Filesystem/CBinaryReader.h" +#include "../Filesystem/CCompressedStream.h" +#include "../Filesystem/CMemoryStream.h" +#include "CMap.h" +#include +#include "../vcmi_endian.h" +#include "../CStopWatch.h" +#include "../VCMI_Lib.h" +#include "../CSpellHandler.h" +#include "../CCreatureHandler.h" + +std::unique_ptr CMapService::loadMap(const std::string & name) +{ + auto stream = getStreamFromFS(name); + return getMapLoader(stream)->loadMap(); +} + +std::unique_ptr CMapService::loadMapHeader(const std::string & name) +{ + auto stream = getStreamFromFS(name); + return getMapLoader(stream)->loadMapHeader(); +} + +std::unique_ptr CMapService::loadMap(const ui8 * buffer, int size) +{ + auto stream = getStreamFromMem(buffer, size); + return getMapLoader(stream)->loadMap(); +} + +std::unique_ptr CMapService::loadMapHeader(const ui8 * buffer, int size) +{ + auto stream = getStreamFromMem(buffer, size); + return getMapLoader(stream)->loadMapHeader(); +} + +std::unique_ptr CMapService::getStreamFromFS(const std::string & name) +{ + return CResourceHandler::get()->load(ResourceID(name, EResType::MAP)); +} + +std::unique_ptr CMapService::getStreamFromMem(const ui8 * buffer, int size) +{ + return std::unique_ptr(new CMemoryStream(buffer, size)); +} + +std::unique_ptr CMapService::getMapLoader(std::unique_ptr & stream) +{ + // Read map header + CBinaryReader reader(*stream.get()); + ui32 header = reader.readUInt32(); + reader.getStream()->seek(0); + + // Check which map format is used + // gzip header is 3 bytes only in size + switch(header & 0xffffff) + { + // gzip header magic number, reversed for LE + case 0x00088B1F: + stream = std::unique_ptr(new CCompressedStream(std::move(stream), true)); + return std::unique_ptr(new CMapLoaderH3M(stream.get())); + case EMapFormat::WOG : + case EMapFormat::AB : + case EMapFormat::ROE : + case EMapFormat::SOD : + return std::unique_ptr(new CMapLoaderH3M(stream.get())); + default : + throw std::runtime_error("Unknown map format"); + } +} + +const bool CMapLoaderH3M::IS_PROFILING_ENABLED = false; + +CMapLoaderH3M::CMapLoaderH3M(CInputStream * stream) : map(nullptr), mapHeader(nullptr), buffer(nullptr), pos(-1), size(-1) +{ + initBuffer(stream); +} + +CMapLoaderH3M::~CMapLoaderH3M() +{ + delete buffer; +} + +void CMapLoaderH3M::initBuffer(CInputStream * stream) +{ + // Read map data into memory completely + // TODO Replace with CBinaryReader later... (no need for reading whole + // file in memory at once, storing pos & size separately...) + stream->seek(0); + size = stream->getSize(); + buffer = new ui8[size]; + stream->read(buffer, size); +} + +std::unique_ptr CMapLoaderH3M::loadMap() +{ + // Init map object by parsing the input buffer + map = new CMap; + mapHeader = std::unique_ptr(dynamic_cast(map)); + init(); + + return std::unique_ptr(dynamic_cast(mapHeader.release()));; +} + +std::unique_ptr CMapLoaderH3M::loadMapHeader() +{ + // Read header + mapHeader = std::unique_ptr(new CMapHeader); + readHeader(); + + return std::move(mapHeader); +} + +void CMapLoaderH3M::init() +{ + // Compute checksum + boost::crc_32_type result; + result.process_bytes(buffer, size); + map->checksum = result.checksum(); + + CStopWatch sw; + + struct MapLoadingTime + { + std::string name; + si64 time; + + MapLoadingTime(std::string name, si64 time) : name(name), + time(time) + { + + } + }; + std::vector times; + + readHeader(); + times.push_back(MapLoadingTime("header", sw.getDiff())); + + readDisposedHeroes(); + times.push_back(MapLoadingTime("disposed heroes", sw.getDiff())); + + readAllowedArtifacts(); + times.push_back(MapLoadingTime("allowed artifacts", sw.getDiff())); + + readAllowedSpellsAbilities(); + times.push_back(MapLoadingTime("allowed spells and abilities", sw.getDiff())); + + readRumors(); + times.push_back(MapLoadingTime("rumors", sw.getDiff())); + + readPredefinedHeroes(); + times.push_back(MapLoadingTime("predefined heroes", sw.getDiff())); + + readTerrain(); + times.push_back(MapLoadingTime("terrain", sw.getDiff())); + + readDefInfo(); + times.push_back(MapLoadingTime("def info", sw.getDiff())); + + readObjects(); + times.push_back(MapLoadingTime("objects", sw.getDiff())); + + readEvents(); + times.push_back(MapLoadingTime("events", sw.getDiff())); + + // Calculate blocked / visitable positions + for(int f = 0; f < map->objects.size(); ++f) + { + if(!map->objects[f]->defInfo) continue; + addBlockVisibleTiles(map->objects[f]); + } + times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff())); + + // Print profiling times + if(IS_PROFILING_ENABLED) + { + BOOST_FOREACH(MapLoadingTime & mlt, times) + { + tlog0 << "\tReading " << mlt.name << " took " << mlt.time << " ms." << std::endl; + } + } +} + +void CMapLoaderH3M::readHeader() +{ + pos = 0; + + // Check map for validity + if(size < 50 || !buffer[4]) + { + throw std::runtime_error("Corrupted map file."); + } + + // Map version + mapHeader->version = (EMapFormat::EMapFormat)(read_le_u32(buffer + pos)); + pos += 4; + if(mapHeader->version != EMapFormat::ROE && mapHeader->version != EMapFormat::AB && mapHeader->version != EMapFormat::SOD + && mapHeader->version != EMapFormat::WOG) + { + throw std::runtime_error("Invalid map format!"); + } + + // Read map name, description, dimensions,... + mapHeader->areAnyPLayers = readChar(buffer, pos); // Invalid on some maps + mapHeader->height = mapHeader->width = (read_le_u32(buffer + pos)); + pos += 4; + mapHeader->twoLevel = readChar(buffer, pos); + mapHeader->name = readString(buffer, pos); + mapHeader->description = readString(buffer, pos); + mapHeader->difficulty = readChar(buffer, pos); + if(mapHeader->version != EMapFormat::ROE) + { + mapHeader->levelLimit = readChar(buffer, pos); + } + else + { + mapHeader->levelLimit = 0; + } + + readPlayerInfo(); + readVictoryLossConditions(); + readTeamInfo(); + readAllowedHeroes(); +} + +void CMapLoaderH3M::readPlayerInfo() +{ + mapHeader->players.resize(8); + for(int i = 0; i < 8; ++i) + { + mapHeader->players[i].canHumanPlay = buffer[pos++]; + mapHeader->players[i].canComputerPlay = buffer[pos++]; + + // If nobody can play with this player + if((!(mapHeader->players[i].canHumanPlay || mapHeader->players[i].canComputerPlay))) + { + switch(mapHeader->version) + { + case EMapFormat::SOD: + case EMapFormat::WOG: + pos += 13; + break; + case EMapFormat::AB: + pos += 12; + break; + case EMapFormat::ROE: + pos += 6; + break; + } + continue; + } + + mapHeader->players[i].AITactic = buffer[pos++]; + + if(mapHeader->version == EMapFormat::SOD || mapHeader->version == EMapFormat::WOG) + { + mapHeader->players[i].p7 = buffer[pos++]; + } + else + { + mapHeader->players[i].p7 = -1; + } + + // Factions this player can choose + ui16 allowedFactions = buffer[pos++]; + if(mapHeader->version != EMapFormat::ROE) + { + allowedFactions += (buffer[pos++]) * 256; + } + + for(int fact = 0; fact < 16; ++fact) + { + if(allowedFactions & (1 << fact)) + { + mapHeader->players[i].allowedFactions.insert(fact); + } + } + + mapHeader->players[i].isFactionRandom = buffer[pos++]; + mapHeader->players[i].hasMainTown = buffer[pos++]; + if(mapHeader->players[i].hasMainTown) + { + if(mapHeader->version != EMapFormat::ROE) + { + mapHeader->players[i].generateHeroAtMainTown = buffer[pos++]; + mapHeader->players[i].generateHero = buffer[pos++]; + } + else + { + mapHeader->players[i].generateHeroAtMainTown = true; + mapHeader->players[i].generateHero = false; + } + + mapHeader->players[i].posOfMainTown.x = buffer[pos++]; + mapHeader->players[i].posOfMainTown.y = buffer[pos++]; + mapHeader->players[i].posOfMainTown.z = buffer[pos++]; + } + + mapHeader->players[i].p8 = buffer[pos++]; + mapHeader->players[i].p9 = buffer[pos++]; + if(mapHeader->players[i].p9 != 0xff) + { + mapHeader->players[i].mainHeroPortrait = buffer[pos++]; + mapHeader->players[i].mainHeroName = readString(buffer, pos); + } + + if(mapHeader->version != EMapFormat::ROE) + { + mapHeader->players[i].powerPlacehodlers = buffer[pos++]; //unknown byte + int heroCount = buffer[pos++]; + pos += 3; + for(int pp = 0; pp < heroCount; ++pp) + { + SheroName vv; + vv.heroID = buffer[pos++]; + int hnl = buffer[pos++]; + pos += 3; + for(int zz = 0; zz < hnl; ++zz) + { + vv.heroName += buffer[pos++]; + } + mapHeader->players[i].heroesNames.push_back(vv); + } + } + } +} + +void CMapLoaderH3M::readVictoryLossConditions() +{ + mapHeader->victoryCondition.obj = nullptr; + mapHeader->victoryCondition.condition = (EVictoryConditionType::EVictoryConditionType)buffer[pos++]; + + // Specific victory conditions + if(mapHeader->victoryCondition.condition != EVictoryConditionType::WINSTANDARD) + { + // Read victory conditions + int nr = 0; + switch(mapHeader->victoryCondition.condition) + { + case EVictoryConditionType::ARTIFACT: + { + mapHeader->victoryCondition.ID = buffer[pos + 2]; + nr = (mapHeader->version == EMapFormat::ROE ? 1 : 2); + break; + } + case EVictoryConditionType::GATHERTROOP: + { + mapHeader->victoryCondition.ID = buffer[pos + 2]; + mapHeader->victoryCondition.count = read_le_u32(buffer + pos + (mapHeader->version == EMapFormat::ROE ? 3 : 4)); + nr = (mapHeader->version == EMapFormat::ROE ? 5 : 6); + break; + } + case EVictoryConditionType::GATHERRESOURCE: + { + mapHeader->victoryCondition.ID = buffer[pos + 2]; + mapHeader->victoryCondition.count = read_le_u32(buffer + pos + 3); + nr = 5; + break; + } + case EVictoryConditionType::BUILDCITY: + { + mapHeader->victoryCondition.pos.x = buffer[pos + 2]; + mapHeader->victoryCondition.pos.y = buffer[pos + 3]; + mapHeader->victoryCondition.pos.z = buffer[pos + 4]; + mapHeader->victoryCondition.count = buffer[pos + 5]; + mapHeader->victoryCondition.ID = buffer[pos + 6]; + nr = 5; + break; + } + case EVictoryConditionType::BUILDGRAIL: + { + if(buffer[pos + 4] > 2) + { + mapHeader->victoryCondition.pos = int3(-1,-1,-1); + } + else + { + mapHeader->victoryCondition.pos.x = buffer[pos + 2]; + mapHeader->victoryCondition.pos.y = buffer[pos + 3]; + mapHeader->victoryCondition.pos.z = buffer[pos + 4]; + } + nr = 3; + break; + } + case EVictoryConditionType::BEATHERO: + case EVictoryConditionType::CAPTURECITY: + case EVictoryConditionType::BEATMONSTER: + { + mapHeader->victoryCondition.pos.x = buffer[pos + 2]; + mapHeader->victoryCondition.pos.y = buffer[pos + 3]; + mapHeader->victoryCondition.pos.z = buffer[pos + 4]; + nr = 3; + break; + } + case EVictoryConditionType::TAKEDWELLINGS: + case EVictoryConditionType::TAKEMINES: + { + nr = 0; + break; + } + case EVictoryConditionType::TRANSPORTITEM: + { + mapHeader->victoryCondition.ID = buffer[pos + 2]; + mapHeader->victoryCondition.pos.x = buffer[pos + 3]; + mapHeader->victoryCondition.pos.y = buffer[pos + 4]; + mapHeader->victoryCondition.pos.z = buffer[pos + 5]; + nr = 4; + break; + } + default: + assert(0); + } + mapHeader->victoryCondition.allowNormalVictory = buffer[pos++]; + mapHeader->victoryCondition.appliesToAI = buffer[pos++]; + pos += nr; + } + + // Read loss conditions + mapHeader->lossCondition.typeOfLossCon = (ELossConditionType::ELossConditionType)buffer[pos++]; + switch(mapHeader->lossCondition.typeOfLossCon) + { + case ELossConditionType::LOSSCASTLE: + case ELossConditionType::LOSSHERO: + { + mapHeader->lossCondition.pos.x = buffer[pos++]; + mapHeader->lossCondition.pos.y = buffer[pos++]; + mapHeader->lossCondition.pos.z = buffer[pos++]; + break; + } + case ELossConditionType::TIMEEXPIRES: + { + mapHeader->lossCondition.timeLimit = read_le_u16(buffer + pos); + pos += 2; + break; + } + } +} + +void CMapLoaderH3M::readTeamInfo() +{ + mapHeader->howManyTeams = buffer[pos++]; + if(mapHeader->howManyTeams > 0) + { + // Teams + for(int i = 0; i < 8; ++i) + { + mapHeader->players[i].team = buffer[pos++]; + } + } + else + { + // No alliances + for(int i = 0; i < GameConstants::PLAYER_LIMIT; i++) + { + if(mapHeader->players[i].canComputerPlay || mapHeader->players[i].canHumanPlay) + { + mapHeader->players[i].team = mapHeader->howManyTeams++; + } + } + } +} + +void CMapLoaderH3M::readAllowedHeroes() +{ + int pom = pos; + mapHeader->allowedHeroes.resize(GameConstants::HEROES_QUANTITY, false); + for(; pos < pom + (mapHeader->version == EMapFormat::ROE ? 16 : 20) ; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - pom) * 8 + yy < GameConstants::HEROES_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + mapHeader->allowedHeroes[(pos - pom) * 8 + yy] = true; + } + } + } + } + + // Probably reserved for further heroes + if(mapHeader->version > EMapFormat::ROE) + { + int placeholdersQty = read_le_u32(buffer + pos); + pos += 4; + for(int p = 0; p < placeholdersQty; ++p) + { + mapHeader->placeholdedHeroes.push_back(buffer[pos++]); + } + } +} + +void CMapLoaderH3M::readDisposedHeroes() +{ + // Reading disposed heroes (20 bytes) + ui8 disp = 0; + if(map->version >= EMapFormat::SOD) + { + disp = buffer[pos++]; + map->disposedHeroes.resize(disp); + for(int g = 0; g < disp; ++g) + { + map->disposedHeroes[g].ID = buffer[pos++]; + map->disposedHeroes[g].portrait = buffer[pos++]; + int lenbuf = read_le_u32(buffer + pos); + pos += 4; + for(int zz = 0; zz < lenbuf; zz++) + { + map->disposedHeroes[g].name += buffer[pos++]; + } + map->disposedHeroes[g].players = buffer[pos++]; + } + } + + //omitting NULLS + pos += 31; +} + +void CMapLoaderH3M::readAllowedArtifacts() +{ + map->allowedArtifact.resize(GameConstants::ARTIFACTS_QUANTITY); + for(ui32 x = 0; x < map->allowedArtifact.size(); x++) + { + map->allowedArtifact[x] = true; + } + + // Reading allowed artifacts: 17 or 18 bytes + if(map->version != EMapFormat::ROE) + { + // Starting i for loop + int ist = pos; + for(; pos < ist + (map->version == EMapFormat::AB ? 17 : 18); ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::ARTIFACTS_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + map->allowedArtifact[(pos - ist) * 8 + yy] = false; + } + } + } + } + } + + // ban combo artifacts + if (map->version == EMapFormat::ROE || map->version == EMapFormat::AB) + { + BOOST_FOREACH(CArtifact * artifact, VLC->arth->artifacts) + { + // combo + if (artifact->constituents) + { + map->allowedArtifact[artifact->id] = false; + } + } + if (map->version == EMapFormat::ROE) + { + // Armageddon's Blade + map->allowedArtifact[128] = false; + } + } + + // Messy, but needed + if(map->victoryCondition.condition == EVictoryConditionType::ARTIFACT + || map->victoryCondition.condition == EVictoryConditionType::TRANSPORTITEM) + { + map->allowedArtifact[map->victoryCondition.ID] = false; + } +} + +void CMapLoaderH3M::readAllowedSpellsAbilities() +{ + // Read allowed spells + map->allowedSpell.resize(GameConstants::SPELLS_QUANTITY); + for(ui32 x = 0; x < map->allowedSpell.size(); x++) + { + map->allowedSpell[x] = true; + } + + // Read allowed abilities + map->allowedAbilities.resize(GameConstants::SKILL_QUANTITY); + for(ui32 x = 0; x < map->allowedAbilities.size(); x++) + { + map->allowedAbilities[x] = true; + } + + if(map->version >= EMapFormat::SOD) + { + // Reading allowed spells (9 bytes) + int ist = pos; + for(; pos < ist + 9; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + map->allowedSpell[(pos - ist) * 8 + yy] = false; + } + } + } + } + + + // Allowed hero's abilities (4 bytes) + ist = pos; + for(; pos < ist + 4; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SKILL_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + map->allowedAbilities[(pos - ist) * 8 + yy] = false; + } + } + } + } + } +} + +void CMapLoaderH3M::readRumors() +{ + int rumNr = read_le_u32(buffer + pos); + pos += 4; + + for(int it = 0; it < rumNr; it++) + { + Rumor ourRumor; + + // Read rumor name and text + int nameL = read_le_u32(buffer + pos); + pos += 4; + for(int zz = 0; zz < nameL; zz++) + { + ourRumor.name += buffer[pos++]; + } + + nameL = read_le_u32(buffer + pos); + pos += 4; + for(int zz = 0; zz < nameL; zz++) + { + ourRumor.text += buffer[pos++]; + } + + map->rumors.push_back(ourRumor); + } +} + +void CMapLoaderH3M::readPredefinedHeroes() +{ + switch(map->version) + { + case EMapFormat::WOG: + case EMapFormat::SOD: + { + // Disposed heroes + for(int z = 0; z < GameConstants::HEROES_QUANTITY; z++) + { + int custom = buffer[pos++]; + if(!custom) continue; + + CGHeroInstance * hero = new CGHeroInstance; + hero->ID = Obj::HERO; + hero->subID = z; + + // True if hore's experience is greater than 0 + if(readChar(buffer, pos)) + { + hero->exp = read_le_u32(buffer + pos); + pos += 4; + } + else + { + hero->exp = 0; + } + + // True if hero has specified abilities + if(readChar(buffer, pos)) + { + int howMany = read_le_u32(buffer + pos); + pos += 4; + hero->secSkills.resize(howMany); + for(int yy = 0; yy < howMany; ++yy) + { + hero->secSkills[yy].first = buffer[pos]; + ++pos; + hero->secSkills[yy].second = buffer[pos]; + ++pos; + } + } + + loadArtifactsOfHero(hero); + + // custom bio + if(readChar(buffer, pos)) + { + hero->biography = readString(buffer, pos); + } + + // 0xFF is default, 00 male, 01 female + hero->sex = buffer[pos++]; + + // are spells + if(readChar(buffer, pos)) + { + int ist = pos; + for(; pos < ist + 9; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + hero->spells.insert((pos - ist) * 8 + yy); + } + } + } + } + } + + // customPrimSkills + if(readChar(buffer, pos)) + { + for(int xx = 0; xx < GameConstants::PRIMARY_SKILLS; xx++) + { + hero->pushPrimSkill(xx, buffer[pos++]); + } + } + map->predefinedHeroes.push_back(hero); + } + break; + } + case EMapFormat::ROE: + pos+=0; + break; + } +} + +void CMapLoaderH3M::loadArtifactsOfHero(CGHeroInstance * hero) +{ + bool artSet = buffer[pos]; + ++pos; + + // True if artifact set is not default (hero has some artifacts) + if(artSet) + { + for(int pom = 0; pom < 16; pom++) + { + loadArtifactToSlot(hero, pom); + } + + // misc5 art //17 + if(map->version >= EMapFormat::SOD) + { + if(!loadArtifactToSlot(hero, ArtifactPosition::MACH4)) + { + // catapult by default + hero->putArtifact(ArtifactPosition::MACH4, createArtifact(GameConstants::ID_CATAPULT)); + } + } + + loadArtifactToSlot(hero, ArtifactPosition::SPELLBOOK); + + // 19 //???what is that? gap in file or what? - it's probably fifth slot.. + if(map->version > EMapFormat::ROE) + { + loadArtifactToSlot(hero, ArtifactPosition::MISC5); + } + else + { + ++pos; + } + + // bag artifacts //20 + // number of artifacts in hero's bag + int amount = read_le_u16(buffer + pos); + pos += 2; + for(int ss = 0; ss < amount; ++ss) + { + loadArtifactToSlot(hero, GameConstants::BACKPACK_START + hero->artifactsInBackpack.size()); + } + } +} + +bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot) +{ + const int artmask = map->version == EMapFormat::ROE ? 0xff : 0xffff; + int aid; + + if(map->version == EMapFormat::ROE) + { + aid = buffer[pos]; + pos++; + } + else + { + aid = read_le_u16(buffer + pos); + pos += 2; + } + + bool isArt = aid != artmask; + if(isArt) + { + if(vstd::contains(VLC->arth->bigArtifacts, aid) && slot >= GameConstants::BACKPACK_START) + { + tlog3 << "Warning: A big artifact (war machine) in hero's backpack, ignoring..." << std::endl; + return false; + } + if(aid == 0 && slot == ArtifactPosition::MISC5) + { + //TODO: check how H3 handles it -> art 0 in slot 18 in AB map + tlog3 << "Spellbook to MISC5 slot? Putting it spellbook place. AB format peculiarity ? (format " + << static_cast(map->version) << ")" << std::endl; + slot = ArtifactPosition::SPELLBOOK; + } + + hero->putArtifact(slot, createArtifact(aid)); + } + + return isArt; +} + +CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/) +{ + CArtifactInstance * a = nullptr; + if(aid >= 0) + { + if(spellID < 0) + { + a = CArtifactInstance::createNewArtifactInstance(aid); + } + else + { + a = CArtifactInstance::createScroll(VLC->spellh->spells[spellID]); + } + } + else + { + a = new CArtifactInstance; + } + + addNewArtifactInstance(a); + + //TODO make it nicer + if(a->artType && a->artType->constituents) + { + CCombinedArtifactInstance * comb = dynamic_cast(a); + BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo & ci, comb->constituentsInfo) + { + addNewArtifactInstance(ci.art); + } + } + + return a; +} + +void CMapLoaderH3M::addNewArtifactInstance(CArtifactInstance * art) +{ + art->id = map->artInstances.size(); + map->artInstances.push_back(art); +} + +void CMapLoaderH3M::readTerrain() +{ + // Allocate memory for terrain data + map->terrain = new TerrainTile**[map->width]; + for(int ii = 0; ii < map->width; ii++) + { + map->terrain[ii] = new TerrainTile*[map->height]; + for(int jj = 0; jj < map->height; jj++) + { + map->terrain[ii][jj] = new TerrainTile[map->twoLevel + 1]; + } + } + + // Read terrain + for(int a = 0; a < 2; ++a) + { + if(a == 1 && !map->twoLevel) + { + break; + } + + for(int c = 0; c < map->width; c++) + { + for(int z = 0; z < map->height; z++) + { + map->terrain[z][c][a].tertype = static_cast(buffer[pos++]); + map->terrain[z][c][a].terview = buffer[pos++]; + map->terrain[z][c][a].riverType = static_cast(buffer[pos++]); + map->terrain[z][c][a].riverDir = buffer[pos++]; + map->terrain[z][c][a].roadType = static_cast(buffer[pos++]); + map->terrain[z][c][a].roadDir = buffer[pos++]; + map->terrain[z][c][a].extTileFlags = buffer[pos++]; + map->terrain[z][c][a].blocked = (map->terrain[z][c][a].tertype == ETerrainType::ROCK ? 1 : 0); //underground tiles are always blocked + map->terrain[z][c][a].visitable = 0; + } + } + } +} + +void CMapLoaderH3M::readDefInfo() +{ + int defAmount = read_le_u32(buffer + pos); + pos += 4; + map->customDefs.reserve(defAmount + 8); + + // Read custom defs + for(int idd = 0; idd < defAmount; ++idd) + { + CGDefInfo * defInfo = new CGDefInfo; + + // Read name + int nameLength = read_le_u32(buffer + pos); + pos += 4; + defInfo->name.reserve(nameLength); + for(int cd = 0; cd < nameLength; ++cd) + { + defInfo->name += buffer[pos++]; + } + std::transform(defInfo->name.begin(),defInfo->name.end(),defInfo->name.begin(),(int(*)(int))toupper); + + ui8 bytes[12]; + for(int v = 0; v < 12; ++v) + { + bytes[v] = buffer[pos++]; + } + + defInfo->terrainAllowed = read_le_u16(buffer + pos); + pos += 2; + defInfo->terrainMenu = read_le_u16(buffer + pos); + pos += 2; + defInfo->id = read_le_u32(buffer + pos); + pos += 4; + defInfo->subid = read_le_u32(buffer + pos); + pos += 4; + defInfo->type = buffer[pos++]; + defInfo->printPriority = buffer[pos++]; + for(int zi = 0; zi < 6; ++zi) + { + defInfo->blockMap[zi] = reverse(bytes[zi]); + } + for(int zi = 0; zi < 6; ++zi) + { + defInfo->visitMap[zi] = reverse(bytes[6 + zi]); + } + pos += 16; + if(defInfo->id != Obj::HERO && defInfo->id != 70) + { + CGDefInfo * h = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid]; + if(!h) + { + //remove fake entry + VLC->dobjinfo->gobjs[defInfo->id].erase(defInfo->subid); + if(VLC->dobjinfo->gobjs[defInfo->id].size()) + { + VLC->dobjinfo->gobjs.erase(defInfo->id); + } + tlog2 << "\t\tWarning: no defobjinfo entry for object ID=" + << defInfo->id << " subID=" << defInfo->subid << std::endl; + } + else + { + defInfo->visitDir = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid]->visitDir; + } + } + else + { + defInfo->visitDir = 0xff; + } + + if(defInfo->id == Obj::EVENT) + { + std::memset(defInfo->blockMap, 255, 6); + } + + //calculating coverageMap + defInfo->fetchInfoFromMSK(); + + map->customDefs.push_back(defInfo); + } + + //add holes - they always can appear + for(int i = 0; i < 8 ; ++i) + { + map->customDefs.push_back(VLC->dobjinfo->gobjs[124][i]); + } +} + +void CMapLoaderH3M::readObjects() +{ + int howManyObjs = read_le_u32(buffer + pos); + pos += 4; + + for(int ww = 0; ww < howManyObjs; ++ww) + { + CGObjectInstance * nobj = 0; + + int3 objPos; + objPos.x = buffer[pos++]; + objPos.y = buffer[pos++]; + objPos.z = buffer[pos++]; + + int defnum = read_le_u32(buffer + pos); + pos += 4; + int idToBeGiven = map->objects.size(); + + CGDefInfo * defInfo = map->customDefs.at(defnum); + pos += 5; + + switch(defInfo->id) + { + case Obj::EVENT: + { + CGEvent * evnt = new CGEvent(); + nobj = evnt; + + bool guardMess = buffer[pos]; + ++pos; + if(guardMess) + { + int messLong = read_le_u32(buffer + pos); + pos += 4; + if(messLong > 0) + { + for(int yy = 0; yy < messLong; ++yy) + { + evnt->message += buffer[pos + yy]; + } + pos += messLong; + } + if(buffer[pos++]) + { + readCreatureSet(evnt, 7, map->version > EMapFormat::ROE); + } + pos += 4; + } + evnt->gainedExp = read_le_u32(buffer + pos); + pos += 4; + evnt->manaDiff = read_le_u32(buffer + pos); + pos += 4; + evnt->moraleDiff = static_cast(buffer[pos]); + ++pos; + evnt->luckDiff = static_cast(buffer[pos]); + ++pos; + + evnt->resources.resize(GameConstants::RESOURCE_QUANTITY); + for(int x = 0; x < 7; ++x) + { + evnt->resources[x] = read_le_u32(buffer + pos); + pos += 4; + } + + evnt->primskills.resize(GameConstants::PRIMARY_SKILLS); + for(int x = 0; x < 4; ++x) + { + evnt->primskills[x] = buffer[pos]; + ++pos; + } + + int gabn; // Number of gained abilities + gabn = buffer[pos]; + ++pos; + for(int oo = 0; oo < gabn; ++oo) + { + evnt->abilities.push_back(buffer[pos]); + ++pos; + evnt->abilityLevels.push_back(buffer[pos]); + ++pos; + } + + int gart = buffer[pos]; // Number of gained artifacts + ++pos; + for(int oo = 0; oo < gart; ++oo) + { + if(map->version == EMapFormat::ROE) + { + evnt->artifacts.push_back(buffer[pos]); + ++pos; + } + else + { + evnt->artifacts.push_back(read_le_u16(buffer + pos)); + pos += 2; + } + } + + int gspel = buffer[pos]; // Number of gained spells + ++pos; + for(int oo = 0; oo < gspel; ++oo) + { + evnt->spells.push_back(buffer[pos]); + ++pos; + } + + int gcre = buffer[pos]; //number of gained creatures + ++pos; + readCreatureSet(&evnt->creatures, gcre, map->version > EMapFormat::ROE); + + pos += 8; + evnt->availableFor = buffer[pos]; + ++pos; + evnt->computerActivate = buffer[pos]; + ++pos; + evnt->removeAfterVisit = buffer[pos]; + ++pos; + evnt->humanActivate = true; + + pos += 4; + break; + } + case 34: case 70: case 62: //34 - hero; 70 - random hero; 62 - prison + { + nobj = readHero(idToBeGiven); + break; + } + case 4: //Arena + case 51: //Mercenary Camp + case 23: //Marletto Tower + case 61: // Star Axis + case 32: // Garden of Revelation + case 100: //Learning Stone + case 102: //Tree of Knowledge + case 41: //Library of Enlightenment + case 47: //School of Magic + case 107: //School of War + { + nobj = new CGVisitableOPH; + break; + } + case 55: //mystical garden + case 112://windmill + case 109://water wheel + { + nobj = new CGVisitableOPW; + break; + } + case 43: //teleport + case 44: //teleport + case 45: //teleport + case 103://subterranean gate + case 111://Whirlpool + { + nobj = new CGTeleport; + break; + } + case 12: //campfire + case 29: //Flotsam + case 82: //Sea Chest + case 86: //Shipwreck Survivor + case 101://treasure chest + { + nobj = new CGPickable; + break; + } + case 54: //Monster + case 71: case 72: case 73: case 74: case 75: // Random Monster 1 - 4 + case 162: case 163: case 164: // Random Monster 5 - 7 + { + CGCreature * cre = new CGCreature; + nobj = cre; + + if(map->version > EMapFormat::ROE) + { + cre->identifier = read_le_u32(buffer + pos); + pos += 4; + map->questIdentifierToId[cre->identifier] = idToBeGiven; + } + + CStackInstance * hlp = new CStackInstance; + hlp->count = read_le_u16(buffer + pos); + pos += 2; + + //type will be set during initialization + cre->putStack(0, hlp); + + cre->character = buffer[pos]; + ++pos; + bool isMesTre = buffer[pos]; //true if there is message or treasury + ++pos; + if(isMesTre) + { + cre->message = readString(buffer, pos); + cre->resources.resize(GameConstants::RESOURCE_QUANTITY); + for(int j = 0; j < 7; ++j) + { + cre->resources[j] = read_le_u32(buffer + pos); + pos += 4; + } + + int artID; + if (map->version == EMapFormat::ROE) + { + artID = buffer[pos]; + ++pos; + } + else + { + artID = read_le_u16(buffer + pos); + pos += 2; + } + + if(map->version == EMapFormat::ROE) + { + if(artID != 0xff) + { + cre->gainedArtifact = artID; + } + else + { + cre->gainedArtifact = -1; + } + } + else + { + if(artID != 0xffff) + { + cre->gainedArtifact = artID; + } + else + { + cre->gainedArtifact = -1; + } + } + } + cre->neverFlees = buffer[pos]; + ++pos; + cre->notGrowingTeam = buffer[pos]; + ++pos; + pos += 2; + break; + } + case 59: case 91: //ocean bottle and sign + { + CGSignBottle * sb = new CGSignBottle; + nobj = sb; + sb->message = readString(buffer, pos); + pos += 4; + break; + } + case 83: //seer's hut + { + nobj = readSeerHut(); + addQuest(nobj); + break; + } + case 113: //witch hut + { + CGWitchHut * wh = new CGWitchHut; + nobj = wh; + + // in reo we cannot specify it - all are allowed (I hope) + if(map->version > EMapFormat::ROE) + { + int ist=pos; + for(; pos < ist + 4; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SKILL_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + wh->allowedAbilities.push_back((pos - ist) * 8 + yy); + } + } + } + } + } + else + { + // RoE map + for(int gg = 0; gg < GameConstants::SKILL_QUANTITY; ++gg) + { + wh->allowedAbilities.push_back(gg); + } + } + break; + } + case 81: //scholar + { + CGScholar * sch = new CGScholar; + nobj = sch; + sch->bonusType = buffer[pos++]; + sch->bonusID = buffer[pos++]; + pos += 6; + break; + } + case 33: case 219: //garrison + { + CGGarrison * gar = new CGGarrison; + nobj = gar; + nobj->setOwner(buffer[pos++]); + pos += 3; + readCreatureSet(gar, 7, map->version > EMapFormat::ROE); + if(map->version > EMapFormat::ROE) + { + gar->removableUnits = buffer[pos]; + ++pos; + } + else + { + gar->removableUnits = true; + } + pos += 8; + break; + } + case 5: //artifact + case 65: case 66: case 67: case 68: case 69: //random artifact + case 93: //spell scroll + { + int artID = -1; + int spellID = -1; + CGArtifact * art = new CGArtifact; + nobj = art; + + bool areSettings = buffer[pos++]; + if(areSettings) + { + art->message = readString(buffer, pos); + bool areGuards = buffer[pos++]; + if(areGuards) + { + readCreatureSet(art, 7, map->version > EMapFormat::ROE); + } + pos += 4; + } + + if(defInfo->id == 93) + { + spellID = read_le_u32(buffer + pos); + pos += 4; + artID = 1; + } + else if(defInfo->id == 5) + { + //specific artifact + artID = defInfo->subid; + } + + art->storedArtifact = createArtifact(artID, spellID); + break; + } + case 76: case 79: //random resource; resource + { + CGResource * res = new CGResource; + nobj = res; + + bool isMessGuard = buffer[pos]; + ++pos; + if(isMessGuard) + { + res->message = readString(buffer, pos); + if(buffer[pos++]) + { + readCreatureSet(res, 7, map->version > EMapFormat::ROE); + } + pos += 4; + } + res->amount = read_le_u32(buffer + pos); + pos += 4; + if(defInfo->subid == 6) + { + // Gold is multiplied by 100. + res->amount *= 100; + } + pos += 4; + + break; + } + case 77: case 98: //random town; town + { + nobj = readTown(defInfo->subid); + break; + } + case 53: + case 220://mine (?) + { + nobj = new CGMine; + nobj->setOwner(buffer[pos++]); + pos += 3; + break; + } + case 17: case 18: case 19: case 20: //dwellings + { + nobj = new CGDwelling; + nobj->setOwner(buffer[pos++]); + pos += 3; + break; + } + case 78: //Refugee Camp + case 106: //War Machine Factory + { + nobj = new CGDwelling; + break; + } + case 88: case 89: case 90: //spell shrine + { + CGShrine * shr = new CGShrine; + nobj = shr; + shr->spell = buffer[pos]; + pos += 4; + break; + } + case 6: //pandora's box + { + CGPandoraBox * box = new CGPandoraBox; + nobj = box; + bool messg = buffer[pos]; + ++pos; + if(messg) + { + box->message = readString(buffer, pos); + if(buffer[pos++]) + { + readCreatureSet(box, 7, map->version > EMapFormat::ROE); + } + pos += 4; + } + + box->gainedExp = read_le_u32(buffer + pos); + pos += 4; + box->manaDiff = read_le_u32(buffer + pos); + pos += 4; + box->moraleDiff = static_cast(buffer[pos]); + ++pos; + box->luckDiff = static_cast(buffer[pos]); + ++pos; + + box->resources.resize(GameConstants::RESOURCE_QUANTITY); + for(int x = 0; x < 7; ++x) + { + box->resources[x] = read_le_u32(buffer + pos); + pos += 4; + } + + box->primskills.resize(GameConstants::PRIMARY_SKILLS); + for(int x = 0; x < 4; ++x) + { + box->primskills[x] = buffer[pos]; + ++pos; + } + + int gabn; //number of gained abilities + gabn = buffer[pos]; + ++pos; + for(int oo = 0; oo < gabn; ++oo) + { + box->abilities.push_back(buffer[pos]); + ++pos; + box->abilityLevels.push_back(buffer[pos]); + ++pos; + } + int gart = buffer[pos]; //number of gained artifacts + ++pos; + for(int oo = 0; oo < gart; ++oo) + { + if(map->version > EMapFormat::ROE) + { + box->artifacts.push_back(read_le_u16(buffer + pos)); + pos += 2; + } + else + { + box->artifacts.push_back(buffer[pos]); + ++pos; + } + } + int gspel = buffer[pos]; //number of gained spells + ++pos; + for(int oo = 0; oo < gspel; ++oo) + { + box->spells.push_back(buffer[pos]); + ++pos; + } + int gcre = buffer[pos]; //number of gained creatures + ++pos; + readCreatureSet(&box->creatures, gcre, map->version > EMapFormat::ROE); + pos += 8; + break; + } + case 36: //grail + { + map->grailPos = objPos; + map->grailRadious = read_le_u32(buffer + pos); + pos += 4; + continue; + } + //dwellings + case 216: //same as castle + level range + case 217: //same as castle + case 218: //level range + { + nobj = new CGDwelling; + CSpecObjInfo * spec = nullptr; + switch(defInfo->id) + { + break; case 216: spec = new CCreGenLeveledCastleInfo; + break; case 217: spec = new CCreGenAsCastleInfo; + break; case 218: spec = new CCreGenLeveledInfo; + } + + spec->player = read_le_u32(buffer + pos); + pos += 4; + + //216 and 217 + if (auto castleSpec = dynamic_cast(spec)) + { + castleSpec->identifier = read_le_u32(buffer + pos); + pos += 4; + if(!castleSpec->identifier) + { + castleSpec->asCastle = false; + castleSpec->castles[0] = buffer[pos]; + ++pos; + castleSpec->castles[1] = buffer[pos]; + ++pos; + } + else + { + castleSpec->asCastle = true; + } + } + + //216 and 218 + if (auto lvlSpec = dynamic_cast(spec)) + { + lvlSpec->minLevel = std::max(buffer[pos], ui8(1)); + ++pos; + lvlSpec->maxLevel = std::min(buffer[pos], ui8(7)); + ++pos; + } + nobj->setOwner(spec->player); + static_cast(nobj)->info = spec; + break; + } + case 215: + { + CGQuestGuard * guard = new CGQuestGuard; + addQuest(guard); + readQuest(guard); + nobj = guard; + break; + } + case 28: //faerie ring + case 14: //Swan pond + case 38: //idol of fortune + case 30: //Fountain of Fortune + case 64: //Rally Flag + case 56: //oasis + case 96: //temple + case 110://Watering Hole + case 31: //Fountain of Youth + case 11: //Buoy + case 52: //Mermaid + case 94: //Stables + { + nobj = new CGBonusingObject; + break; + } + case 49: //Magic Well + { + nobj = new CGMagicWell; + break; + } + case 15: //Cover of darkness + case 58: //Redwood Observatory + case 60: //Pillar of Fire + { + nobj = new CGObservatory; + break; + } + case 22: //Corpse + case 39: //Lean To + case 105://Wagon + case 108://Warrior's Tomb + { + nobj = new CGOnceVisitable; + break; + } + case 8: //Boat + { + nobj = new CGBoat; + break; + } + case 92: //Sirens + { + nobj = new CGSirens; + break; + } + case 87: //Shipyard + { + nobj = new CGShipyard; + nobj->setOwner(read_le_u32(buffer + pos)); + pos += 4; + break; + } + case 214: //hero placeholder + { + CGHeroPlaceholder * hp = new CGHeroPlaceholder; + nobj = hp; + + int a = buffer[pos++]; //unknown byte, seems to be always 0 (if not - scream!) + tlog2 << "Unhandled Hero Placeholder detected: " << a << std::endl; + + int htid = buffer[pos++]; //hero type id + nobj->subID = htid; + + if(htid == 0xff) + { + hp->power = buffer[pos++]; + } + else + { + hp->power = 0; + } + + break; + } + case 10: //Keymaster + { + nobj = new CGKeymasterTent; + break; + } + case 9: //Border Guard + { + nobj = new CGBorderGuard; + addQuest(nobj); + break; + } + case 212: //Border Gate + { + nobj = new CGBorderGate; + addQuest (nobj); + break; + } + case 27: case 37: //Eye and Hut of Magi + { + nobj = new CGMagi; + break; + } + case 16: case 24: case 25: case 84: case 85: //treasure bank + { + nobj = new CBank; + break; + } + case 63: //Pyramid + { + nobj = new CGPyramid; + break; + } + case 13: //Cartographer + { + nobj = new CCartographer; + break; + } + case 48: //Magic Spring + { + nobj = new CGMagicSpring; + break; + } + case 97: //den of thieves + { + nobj = new CGDenOfthieves; + break; + } + case 57: //Obelisk + { + nobj = new CGObelisk; + break; + } + case 42: //Lighthouse + { + nobj = new CGLighthouse; + nobj->tempOwner = read_le_u32(buffer + pos); + pos += 4; + break; + } + case 2: //Altar of Sacrifice + case 99: //Trading Post + case 213: //Freelancer's Guild + case 221: //Trading Post (snow) + { + nobj = new CGMarket; + break; + } + case 104: //University + { + nobj = new CGUniversity; + break; + } + case 7: //Black Market + { + nobj = new CGBlackMarket; + break; + } + default: //any other object + { + nobj = new CGObjectInstance; + break; + } + } + + nobj->pos = objPos; + nobj->ID = defInfo->id; + nobj->id = idToBeGiven; + if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON) + { + nobj->subID = defInfo->subid; + } + nobj->defInfo = defInfo; + assert(idToBeGiven == map->objects.size()); + map->objects.push_back(nobj); + if(nobj->ID == Obj::TOWN) + { + map->towns.push_back(static_cast(nobj)); + } + if(nobj->ID == Obj::HERO) + { + map->heroes.push_back(static_cast(nobj)); + } + } + + std::sort(map->heroes.begin(), map->heroes.end(), [](const ConstTransitivePtr & a, const ConstTransitivePtr & b) + { + return a->subID < b->subID; + }); +} + +void CMapLoaderH3M::readCreatureSet(CCreatureSet * out, int number, bool version) +{ + const int bytesPerCre = version ? 4 : 3; + const int maxID = version ? 0xffff : 0xff; + + for(int ir = 0; ir < number; ++ir) + { + int creID; + int count; + + if(version) + { + creID = read_le_u16(buffer + pos + ir * bytesPerCre); + count = read_le_u16(buffer + pos + ir * bytesPerCre + 2); + } + else + { + creID = buffer[pos + ir * bytesPerCre]; + count = read_le_u16(buffer + pos + ir * bytesPerCre + 1); + } + + // Empty slot + if(creID == maxID) continue; + + CStackInstance * hlp = new CStackInstance; + hlp->count = count; + + if(creID > maxID - 0xf) + { + //this will happen when random object has random army + creID = maxID + 1 - creID + VLC->creh->creatures.size(); + hlp->idRand = creID; + } + else + { + hlp->setType(creID); + } + + out->putStack(ir, hlp); + } + + pos += number * bytesPerCre; + out->validTypes(true); +} + +CGObjectInstance * CMapLoaderH3M::readHero(int idToBeGiven) +{ + CGHeroInstance * nhi = new CGHeroInstance; + + int identifier = 0; + if(map->version > EMapFormat::ROE) + { + identifier = read_le_u32(buffer + pos); + pos += 4; + map->questIdentifierToId[identifier] = idToBeGiven; + } + + ui8 owner = buffer[pos++]; + nhi->subID = buffer[pos++]; + + for(int j = 0; j < map->predefinedHeroes.size(); ++j) + { + if(map->predefinedHeroes[j]->subID == nhi->subID) + { + tlog0 << "Hero " << nhi->subID << " will be taken from the predefined heroes list." << std::endl; + delete nhi; + nhi = map->predefinedHeroes[j]; + break; + } + } + nhi->setOwner(owner); + + nhi->portrait = nhi->subID; + + for(int j = 0; j < map->disposedHeroes.size(); ++j) + { + if(map->disposedHeroes[j].ID == nhi->subID) + { + nhi->name = map->disposedHeroes[j].name; + nhi->portrait = map->disposedHeroes[j].portrait; + break; + } + } + + // True if hero has nonstandard name + if(readChar(buffer, pos)) + { + nhi->name = readString(buffer, pos); + } + if(map->version > EMapFormat::AB) + { + // True if hero's experience is greater than 0 + if(readChar(buffer, pos)) + { + nhi->exp = read_le_u32(buffer + pos); + pos += 4; + } + else + { + nhi->exp = 0xffffffff; + } + } + else + { + nhi->exp = read_le_u32(buffer + pos); + pos += 4; + + //0 means "not set" in <=AB maps + if(!nhi->exp) + { + nhi->exp = 0xffffffff; + } + } + + bool portrait = buffer[pos]; + ++pos; + if(portrait) + { + nhi->portrait = buffer[pos++]; + } + + // True if hero has specified abilities + if(readChar(buffer, pos)) + { + int howMany = read_le_u32(buffer + pos); + pos += 4; + nhi->secSkills.resize(howMany); + for(int yy = 0; yy < howMany; ++yy) + { + nhi->secSkills[yy].first = buffer[pos++]; + nhi->secSkills[yy].second = buffer[pos++]; + } + } + + // True if hero has nonstandard garrison + if(readChar(buffer, pos)) + { + readCreatureSet(nhi, 7, map->version > EMapFormat::ROE); + } + + nhi->formation = buffer[pos]; + ++pos; + loadArtifactsOfHero(nhi); + nhi->patrol.patrolRadious = buffer[pos]; + ++pos; + if(nhi->patrol.patrolRadious == 0xff) + { + nhi->patrol.patrolling = false; + } + else + { + nhi->patrol.patrolling = true; + } + + if(map->version > EMapFormat::ROE) + { + // True if hero has nonstandard (mapmaker defined) biography + if(readChar(buffer, pos)) + { + nhi->biography = readString(buffer, pos); + } + nhi->sex = buffer[pos]; + ++pos; + + // Remove trash + if (nhi->sex != 0xFF) + { + nhi->sex &= 1; + } + } + else + { + nhi->sex = 0xFF; + } + + // Spells + if(map->version > EMapFormat::AB) + { + bool areSpells = buffer[pos]; + ++pos; + + if(areSpells) + { + nhi->spells.insert(0xffffffff); //placeholder "preset spells" + int ist = pos; + for(; pos < ist + 9; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + nhi->spells.insert((pos - ist) * 8 + yy); + } + } + } + } + } + } + else if(map->version == EMapFormat::AB) + { + //we can read one spell + ui8 buff = buffer[pos]; + ++pos; + if(buff != 254) + { + nhi->spells.insert(0xffffffff); //placeholder "preset spells" + if(buff < 254) //255 means no spells + { + nhi->spells.insert(buff); + } + } + } + + if(map->version > EMapFormat::AB) + { + //customPrimSkills + if(readChar(buffer, pos)) + { + for(int xx = 0; xx < GameConstants::PRIMARY_SKILLS; ++xx) + { + nhi->pushPrimSkill(xx, buffer[pos++]); + } + } + } + pos += 16; + + return nhi; +} + +CGSeerHut * CMapLoaderH3M::readSeerHut() +{ + CGSeerHut * hut = new CGSeerHut; + + if(map->version > EMapFormat::ROE) + { + readQuest(hut); + } + else + { + //RoE + int artID = buffer[pos]; + ++pos; + if (artID != 255) + { + //not none quest + hut->quest->m5arts.push_back (artID); + hut->quest->missionType = CQuest::MISSION_ART; + } + else + { + hut->quest->missionType = CQuest::MISSION_NONE; + } + hut->quest->lastDay = -1; //no timeout + hut->quest->isCustomFirst = hut->quest->isCustomNext = hut->quest->isCustomComplete = false; + } + + if (hut->quest->missionType) + { + ui8 rewardType = buffer[pos]; + ++pos; + hut->rewardType = rewardType; + + switch(rewardType) + { + case 1: + { + hut->rVal = read_le_u32(buffer + pos); + pos += 4; + break; + } + case 2: + { + hut->rVal = read_le_u32(buffer + pos); + pos += 4; + break; + } + case 3: + { + hut->rVal = buffer[pos]; + ++pos; + break; + } + case 4: + { + hut->rVal = buffer[pos]; + ++pos; + break; + } + case 5: + { + hut->rID = buffer[pos]; + ++pos; + // Only the first 3 bytes are used. Skip the 4th. + hut->rVal = read_le_u32(buffer + pos) & 0x00ffffff; + pos += 4; + break; + } + case 6: + { + hut->rID = buffer[pos]; + ++pos; + hut->rVal = buffer[pos]; + ++pos; + break; + } + case 7: + { + hut->rID = buffer[pos]; + ++pos; + hut->rVal = buffer[pos]; + ++pos; + break; + } + case 8: + { + if (map->version == EMapFormat::ROE) + { + hut->rID = buffer[pos]; + ++pos; + } + else + { + hut->rID = read_le_u16(buffer + pos); + pos += 2; + } + break; + } + case 9: + { + hut->rID = buffer[pos]; + ++pos; + break; + } + case 10: + { + if(map->version > EMapFormat::ROE) + { + hut->rID = read_le_u16(buffer + pos); + pos += 2; + hut->rVal = read_le_u16(buffer + pos); + pos += 2; + } + else + { + hut->rID = buffer[pos]; + ++pos; + hut->rVal = read_le_u16(buffer + pos); + pos += 2; + } + break; + } + } + pos += 2; + } + else + { + // missionType==255 + pos += 3; + } + + return hut; +} + +void CMapLoaderH3M::readQuest(IQuestObject * guard) +{ + guard->quest->missionType = buffer[pos]; + ++pos; + + switch(guard->quest->missionType) + { + case 0: + return; + case 2: + { + guard->quest->m2stats.resize(4); + for(int x = 0; x < 4; ++x) + { + guard->quest->m2stats[x] = buffer[pos++]; + } + } + break; + case 1: + case 3: + case 4: + { + guard->quest->m13489val = read_le_u32(buffer + pos); + pos += 4; + break; + } + case 5: + { + int artNumber = buffer[pos]; + ++pos; + for(int yy = 0; yy < artNumber; ++yy) + { + int artid = read_le_u16(buffer + pos); + pos += 2; + guard->quest->m5arts.push_back(artid); + map->allowedArtifact[artid] = false; //these are unavailable for random generation + } + break; + } + case 6: + { + int typeNumber = buffer[pos]; + ++pos; + guard->quest->m6creatures.resize(typeNumber); + for(int hh = 0; hh < typeNumber; ++hh) + { + guard->quest->m6creatures[hh].type = VLC->creh->creatures[read_le_u16(buffer + pos)]; + pos += 2; + guard->quest->m6creatures[hh].count = read_le_u16(buffer + pos); + pos += 2; + } + break; + } + case 7: + { + guard->quest->m7resources.resize(7); + for(int x = 0; x < 7; ++x) + { + guard->quest->m7resources[x] = read_le_u32(buffer + pos); + pos += 4; + } + break; + } + case 8: + case 9: + { + guard->quest->m13489val = buffer[pos]; + ++pos; + break; + } + } + + int limit = read_le_u32(buffer + pos); + pos += 4; + if(limit == (static_cast(0xffffffff))) + { + guard->quest->lastDay = -1; + } + else + { + guard->quest->lastDay = limit; + } + guard->quest->firstVisitText = readString(buffer, pos); + guard->quest->nextVisitText = readString(buffer, pos); + guard->quest->completedText = readString(buffer, pos); + guard->quest->isCustomFirst = guard->quest->firstVisitText.size() > 0; + guard->quest->isCustomNext = guard->quest->nextVisitText.size() > 0; + guard->quest->isCustomComplete = guard->quest->completedText.size() > 0; +} + +void CMapLoaderH3M::addQuest(CGObjectInstance * quest) +{ + auto q = dynamic_cast(quest); + q->quest->qid = map->quests.size(); + map->quests.push_back(q->quest); +} + +CGTownInstance * CMapLoaderH3M::readTown(int castleID) +{ + CGTownInstance * nt = new CGTownInstance; + nt->identifier = 0; + if(map->version > EMapFormat::ROE) + { + nt->identifier = read_le_u32(buffer + pos); + pos += 4; + } + nt->tempOwner = buffer[pos]; + ++pos; + if(readChar(buffer, pos)) + { + // Has name + nt->name = readString(buffer, pos); + } + + // True if garrison isn't empty + if(readChar(buffer, pos)) + { + readCreatureSet(nt, 7, map->version > EMapFormat::ROE); + } + nt->formation = buffer[pos]; + ++pos; + + // Custom buildings info + if(readChar(buffer, pos)) + { + // Built buildings + for(int byte = 0; byte < 6; ++byte) + { + for(int bit = 0; bit < 8; ++bit) + { + if(buffer[pos] & (1 << bit)) + { + nt->builtBuildings.insert(byte * 8 + bit); + } + } + ++pos; + } + + // Forbidden buildings + for(int byte = 6; byte < 12; ++byte) + { + for(int bit = 0; bit < 8; ++bit) + { + if(buffer[pos] & (1 << bit)) + { + nt->forbiddenBuildings.insert((byte - 6) * 8 + bit); + } + } + ++pos; + } + nt->builtBuildings = convertBuildings(nt->builtBuildings, castleID); + nt->forbiddenBuildings = convertBuildings(nt->forbiddenBuildings, castleID); + } + // Standard buildings + else + { + if(readChar(buffer, pos)) + { + // Has fort + nt->builtBuildings.insert(EBuilding::FORT); + } + + //means that set of standard building should be included + nt->builtBuildings.insert(-50); + } + + int ist = pos; + if(map->version > EMapFormat::ROE) + { + for(; pos < ist + 9; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + nt->obligatorySpells.push_back((pos - ist) * 8 + yy); + } + } + } + } + } + + ist = pos; + for(; pos < ist + 9; ++pos) + { + ui8 c = buffer[pos]; + for(int yy = 0; yy < 8; ++yy) + { + if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY) + { + if(c == (c | static_cast(std::pow(2, yy)))) + { + nt->possibleSpells.push_back((pos - ist) * 8 + yy); + } + } + } + } + + // Read castle events + int numberOfEvent = read_le_u32(buffer + pos); + pos += 4; + + for(int gh = 0; gh < numberOfEvent; ++gh) + { + CCastleEvent * nce = new CCastleEvent; + nce->town = nt; + nce->name = readString(buffer, pos); + nce->message = readString(buffer, pos); + for(int x = 0; x < 7; ++x) + { + nce->resources[x] = read_le_u32(buffer + pos); + pos += 4; + } + + nce->players = buffer[pos]; + ++pos; + if(map->version > EMapFormat::AB) + { + nce->humanAffected = buffer[pos]; + ++pos; + } + else + { + nce->humanAffected = true; + } + + nce->computerAffected = buffer[pos]; + ++pos; + nce->firstOccurence = read_le_u16(buffer + pos); + pos += 2; + nce->nextOccurence = buffer[pos]; + ++pos; + + pos += 17; + + // New buildings + for(int byte = 0; byte < 6; ++byte) + { + for(int bit = 0; bit < 8; ++bit) + { + if(buffer[pos] & (1 << bit)) + { + nce->buildings.insert(byte * 8 + bit); + } + } + ++pos; + } + nce->buildings = convertBuildings(nce->buildings, castleID, false); + + nce->creatures.resize(7); + for(int vv = 0; vv < 7; ++vv) + { + nce->creatures[vv] = read_le_u16(buffer + pos); + pos += 2; + } + pos += 4; + nt->events.push_back(nce); + } + + if(map->version > EMapFormat::AB) + { + nt->alignment = buffer[pos]; + ++pos; + } + else + { + nt->alignment = 0xff; + } + pos += 3; + + nt->builded = 0; + nt->destroyed = 0; + nt->garrisonHero = nullptr; + + return nt; +} + +std::set CMapLoaderH3M::convertBuildings(const std::set h3m, int castleID, bool addAuxiliary /*= true*/) +{ + std::map mapa; + std::set ret; + + // Note: this file is parsed many times. + const JsonNode config(ResourceID("config/buildings5.json")); + + BOOST_FOREACH(const JsonNode & entry, config["table"].Vector()) + { + int town = entry["town"].Float(); + + if (town == castleID || town == -1) + { + mapa[entry["h3"].Float()] = entry["vcmi"].Float(); + } + } + + for(auto i = h3m.begin(); i != h3m.end(); ++i) + { + if(mapa[*i] >= 0) + { + ret.insert(mapa[*i]); + } + // horde buildings + else if(mapa[*i] >= (-GameConstants::CREATURES_PER_TOWN)) + { + int level = (mapa[*i]); + + //(-30)..(-36) - horde buildings (for game loading only), don't see other way to handle hordes in random towns + ret.insert(level - 30); + } + else + { + tlog3 << "Conversion warning: unknown building " << *i << " in castle " + << castleID << std::endl; + } + } + + if(addAuxiliary) + { + //village hall is always present + ret.insert(EBuilding::VILLAGE_HALL); + } + + if(ret.find(EBuilding::CITY_HALL) != ret.end()) + { + ret.insert(EBuilding::EXTRA_CITY_HALL); + } + if(ret.find(EBuilding::TOWN_HALL) != ret.end()) + { + ret.insert(EBuilding::EXTRA_TOWN_HALL); + } + if(ret.find(EBuilding::CAPITOL) != ret.end()) + { + ret.insert(EBuilding::EXTRA_CAPITOL); + } + + return ret; +} + +void CMapLoaderH3M::readEvents() +{ + int numberOfEvents = read_le_u32(buffer + pos); + pos += 4; + for(int yyoo = 0; yyoo < numberOfEvents; ++yyoo) + { + CMapEvent * ne = new CMapEvent; + ne->name = std::string(); + ne->message = std::string(); + int nameLen = read_le_u32(buffer + pos); + pos += 4; + for(int qq = 0; qq < nameLen; ++qq) + { + ne->name += buffer[pos]; + ++pos; + } + int messLen = read_le_u32(buffer + pos); + pos += 4; + for(int qq = 0; qq < messLen; ++qq) + { + ne->message +=buffer[pos]; + ++pos; + } + for(int k = 0; k < 7; ++k) + { + ne->resources[k] = read_le_u32(buffer + pos); + pos += 4; + } + ne->players = buffer[pos]; + ++pos; + if(map->version > EMapFormat::AB) + { + ne->humanAffected = buffer[pos]; + ++pos; + } + else + { + ne->humanAffected = true; + } + ne->computerAffected = buffer[pos]; + ++pos; + ne->firstOccurence = read_le_u16(buffer + pos); + pos += 2; + ne->nextOccurence = buffer[pos]; + ++pos; + + char unknown[17]; + memcpy(unknown, buffer + pos, 17); + pos += 17; + + map->events.push_back(ne); + } +} + +void CMapLoaderH3M::addBlockVisibleTiles(CGObjectInstance * obj) +{ + for(int fx = 0; fx < 8; ++fx) + { + for(int fy = 0; fy < 6; ++fy) + { + int xVal = obj->pos.x + fx - 7; + int yVal = obj->pos.y + fy - 5; + int zVal = obj->pos.z; + if(xVal >= 0 && xVal < map->width && yVal >= 0 && yVal < map->height) + { + TerrainTile & curt = map->terrain[xVal][yVal][zVal]; + if(((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1)) + { + curt.visitableObjects.push_back(obj); + curt.visitable = true; + } + if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) + { + curt.blockingObjects.push_back(obj); + curt.blocked = true; + } + } + } + } +} + +ui8 CMapLoaderH3M::reverse(ui8 arg) +{ + ui8 ret = 0; + for(int i = 0; i < 8; ++i) + { + if((arg & (1 << i)) >> i) + { + ret |= (128 >> i); + } + } + return ret; +} diff --git a/lib/Map/CMapService.h b/lib/Map/CMapService.h new file mode 100644 index 000000000..ae6a10dbb --- /dev/null +++ b/lib/Map/CMapService.h @@ -0,0 +1,361 @@ + +/* + * CMapService.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +class CMap; +class CMapHeader; +class CInputStream; +class CGHeroInstance; +class CArtifactInstance; +class CGObjectInstance; +class CGSeerHut; +class IQuestObject; +class CGTownInstance; +class CCreatureSet; +class CInputStream; +class IMapLoader; + +/** + * The map service provides loading of VCMI/H3 map files. It can + * be extended to save maps later as well. + */ +class DLL_LINKAGE CMapService +{ +public: + /** + * Loads the VCMI/H3 map file specified by the name. + * + * @param name the name of the map + * @return a unique ptr to the loaded map class + */ + static std::unique_ptr loadMap(const std::string & name); + + /** + * Loads the VCMI/H3 map header specified by the name. + * + * @param name the name of the map + * @return a unique ptr to the loaded map header class + */ + static std::unique_ptr loadMapHeader(const std::string & name); + + /** + * Loads the VCMI/H3 map file from a buffer. This method is temporarily + * in use to ease the transition to use the new map service. + * + * TODO Replace method params with a CampaignMapInfo struct which contains + * a campaign loading object + name of map. + * + * @param buffer a pointer to a buffer containing the map data + * @param size the size of the buffer + * @return a unique ptr to the loaded map class + */ + static std::unique_ptr loadMap(const ui8 * buffer, int size); + + /** + * Loads the VCMI/H3 map header from a buffer. This method is temporarily + * in use to ease the transition to use the new map service. + * + * TODO Replace method params with a CampaignMapInfo struct which contains + * a campaign loading object + name of map. + * + * @param buffer a pointer to a buffer containing the map header data + * @param size the size of the buffer + * @return a unique ptr to the loaded map class + */ + static std::unique_ptr loadMapHeader(const ui8 * buffer, int size); + +private: + /** + * Gets a map input stream object specified by a map name. + * + * @param name the name of the map + * @return a unique ptr to the input stream class + */ + static std::unique_ptr getStreamFromFS(const std::string & name); + + /** + * Gets a map input stream from a buffer. + * + * @param buffer a pointer to a buffer containing the map data + * @param size the size of the buffer + * @return a unique ptr to the input stream class + */ + static std::unique_ptr getStreamFromMem(const ui8 * buffer, int size); + + /** + * Gets a map loader from the given stream. It performs checks to test + * in which map format the map is. + * + * @param stream the input map stream + * @return the constructed map loader + */ + static std::unique_ptr getMapLoader(std::unique_ptr & stream); +}; + +/** + * Interface for loading a map. + */ +class DLL_LINKAGE IMapLoader +{ +public: + /** + * Loads the VCMI/H3 map file. + * + * @return a unique ptr of the loaded map class + */ + virtual std::unique_ptr loadMap() = 0; + + /** + * Loads the VCMI/H3 map header. + * + * @return a unique ptr of the loaded map header class + */ + virtual std::unique_ptr loadMapHeader() = 0; +}; + +class DLL_LINKAGE CMapLoaderH3M : public IMapLoader +{ +public: + /** + * Default constructor. + * + * @param stream a stream containing the map data + */ + CMapLoaderH3M(CInputStream * stream); + + /** + * Destructor. + */ + ~CMapLoaderH3M(); + + /** + * Loads the VCMI/H3 map file. + * + * @return a unique ptr of the loaded map class + */ + std::unique_ptr loadMap(); + + /** + * Loads the VCMI/H3 map header. + * + * @return a unique ptr of the loaded map header class + */ + std::unique_ptr loadMapHeader(); + + /** true if you want to enable the map loader profiler to see how long a specific part took; default=false */ + static const bool IS_PROFILING_ENABLED; + +private: + /** + * Initializes the map object from parsing the input buffer. + */ + void init(); + + /** + * Reads the map header. + */ + void readHeader(); + + /** + * Reads player information. + */ + void readPlayerInfo(); + + /** + * Reads victory/loss conditions. + */ + void readVictoryLossConditions(); + + /** + * Reads team information. + */ + void readTeamInfo(); + + /** + * Reads the list of allowed heroes. + */ + void readAllowedHeroes(); + + /** + * Reads the list of disposed heroes. + */ + void readDisposedHeroes(); + + /** + * Reads the list of allowed artifacts. + */ + void readAllowedArtifacts(); + + /** + * Reads the list of allowed spells and abilities. + */ + void readAllowedSpellsAbilities(); + + /** + * Loads artifacts of a hero. + * + * @param hero the hero which should hold those artifacts + */ + void loadArtifactsOfHero(CGHeroInstance * hero); + + /** + * Loads an artifact to the given slot of the specified hero. + * + * @param hero the hero which should hold that artifact + * @param slot the artifact slot where to place that artifact + * @return true if it loaded an artifact + */ + bool loadArtifactToSlot(CGHeroInstance * hero, int slot); + + /** + * Creates an artifact instance. + * + * @param aid the id of the artifact + * @param spellID optional. the id of a spell if a spell scroll object should be created + * @return the created artifact instance + */ + CArtifactInstance * createArtifact(int aid, int spellID = -1); + + /** + * Adds the specified artifact instance to the list of artifacts of this map. + * + * @param art the artifact which should be added to the list of artifacts + */ + void addNewArtifactInstance(CArtifactInstance * art); + + /** + * Read rumors. + */ + void readRumors(); + + /** + * Reads predefined heroes. + */ + void readPredefinedHeroes(); + + /** + * Reads terrain data. + */ + void readTerrain(); + + /** + * Reads custom(map) def information. + */ + void readDefInfo(); + + /** + * Reads objects(towns, mines,...). + */ + void readObjects(); + + /** + * Reads a creature set. + * + * @param out the loaded creature set + * @param number the count of creatures to read + * @param version true for > ROE maps + */ + void readCreatureSet(CCreatureSet * out, int number, bool version); + + /** + * Reads a hero. + * + * @param idToBeGiven the object id which should be set for the hero + * @return a object instance + */ + CGObjectInstance * readHero(int idToBeGiven); + + /** + * Reads a seer hut. + * + * @return the initialized seer hut object + */ + CGSeerHut * readSeerHut(); + + /** + * Reads a quest for the given quest guard. + * + * @param guard the quest guard where that quest should be applied to + */ + void readQuest(IQuestObject * guard); + + /** + * Adds the specified quest instance to the list of quests. + * + * @param quest the quest object which should be added to the list of quests + */ + void addQuest(CGObjectInstance * quest); + + /** + * Reads a town. + * + * @param castleID the id of the castle type + * @return the loaded town object + */ + CGTownInstance * readTown(int castleID); + + /** + * Converts buildings to the specified castle id. + * + * @param h3m the ids of the buildings + * @param castleID the castle id + * @param addAuxiliary true if the village hall should be added + * @return the converted buildings + */ + std::set convertBuildings(const std::set h3m, int castleID, bool addAuxiliary = true); + + /** + * Reads events. + */ + void readEvents(); + + /** + * Adds object instance to block visitable tiles. + * + * @param obj the object to add + */ + void addBlockVisibleTiles(CGObjectInstance * obj); + + /** + * Reverses the input argument. + * + * @param arg the input argument + * @return the reversed 8-bit integer + */ + ui8 reverse(ui8 arg); + + /** + * Init buffer / size. + * + * @param stream the stream which serves as the data input + */ + void initBuffer(CInputStream * stream); + + /** ptr to the map object which gets filled by data from the buffer */ + CMap * map; + + /** + * ptr to the map header object which gets filled by data from the buffer. + * (when loading a map then the mapHeader ptr points to the same object) + */ + std::unique_ptr mapHeader; + + /** pointer to the array containing the map data; + * TODO replace with CBinaryReader later (this makes pos & size redundant) */ + ui8 * buffer; + + /** current buffer reading position */ + int pos; + + /** size of the map in bytes */ + int size; +}; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 179af578f..d8ad83351 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -5,7 +5,7 @@ #include "BattleAction.h" #include "HeroBonus.h" #include "CCreatureSet.h" -#include "CMapInfo.h" +#include "Map/CMapInfo.h" #include "StartInfo.h" #include "ConstTransitivePtr.h" #include "int3.h" diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 167e750bd..1b7e7f433 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -8,7 +8,7 @@ #include "CObjectHandler.h" #include "CModHandler.h" #include "VCMI_Lib.h" -#include "map.h" +#include "Map/CMap.h" #include "CSpellHandler.h" #include "CCreatureHandler.h" #include "CGameState.h" diff --git a/lib/VCMI_lib.vcxproj b/lib/VCMI_lib.vcxproj index 6d0a6a74b..7427fccb2 100644 --- a/lib/VCMI_lib.vcxproj +++ b/lib/VCMI_lib.vcxproj @@ -225,7 +225,6 @@ - @@ -236,7 +235,6 @@ - @@ -252,11 +250,14 @@ + + + + - @@ -278,7 +279,6 @@ - @@ -289,7 +289,6 @@ - @@ -311,6 +310,10 @@ + + + + @@ -319,7 +322,6 @@ - @@ -332,4 +334,4 @@ - \ No newline at end of file + diff --git a/lib/map.cpp b/lib/map.cpp deleted file mode 100644 index 77cf1677d..000000000 --- a/lib/map.cpp +++ /dev/null @@ -1,2281 +0,0 @@ -#include "StdInc.h" -#include "map.h" - -#include "Filesystem/CBinaryReader.h" -#include "Filesystem/CResourceLoader.h" -#include "Filesystem/CCompressedStream.h" -#include "CObjectHandler.h" -#include "CDefObjInfoHandler.h" -#include "VCMI_Lib.h" -#include -#include "CArtHandler.h" -#include "CCreatureHandler.h" -#include "CSpellHandler.h" -#include "../lib/JsonNode.h" -#include "vcmi_endian.h" -#include "GameConstants.h" -#include "CStopWatch.h" - -/* - * map.cpp, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -static std::set convertBuildings(const std::set h3m, int castleID, bool addAuxiliary = true) -{ - std::map mapa; - std::set ret; - - // Note: this file is parsed many times. - const JsonNode config(ResourceID("config/buildings5.json")); - - BOOST_FOREACH(const JsonNode &entry, config["table"].Vector()) - { - int town = entry["town"].Float(); - - if ( town == castleID || town == -1 ) - mapa[entry["h3"].Float()] = entry["vcmi"].Float(); - } - - for(std::set::const_iterator i=h3m.begin();i!=h3m.end();i++) - { - if(mapa[*i]>=0) - ret.insert(mapa[*i]); - else if(mapa[*i] >= (-GameConstants::CREATURES_PER_TOWN)) // horde buildings - { - int level = (mapa[*i]); - ret.insert(level-30);//(-30)..(-36) - horde buildings (for game loading only), don't see other way to handle hordes in random towns - } - else - { - tlog3<<"Conversion warning: unknown building "<<*i<<" in castle "<>i) - { - ret |= (128>>i); - } - } - return ret; -} - -void readCreatureSet(CCreatureSet *out, const ui8 * buffer, int &i, int number, bool version) //version==true for >RoE maps -{ - const int bytesPerCre = version ? 4 : 3, - maxID = version ? 0xffff : 0xff; - - for(int ir=0;ir < number; ir++) - { - int creID; - int count; - - if (version) - { - creID = read_le_u16(buffer + i+ir*bytesPerCre); - count = read_le_u16(buffer + i+ir*bytesPerCre + 2); - } - else - { - creID = buffer[i+ir*bytesPerCre]; - count = read_le_u16(buffer + i+ir*bytesPerCre + 1); - } - - if(creID == maxID) //empty slot - continue; - - CStackInstance *hlp = new CStackInstance(); - hlp->count = count; - - if(creID > maxID - 0xf) - { - creID = maxID + 1 - creID + VLC->creh->creatures.size();//this will happen when random object has random army - hlp->idRand = creID; - } - else - hlp->setType(creID); - - out->putStack(ir, hlp); - } - i+=number*bytesPerCre; - - out->validTypes(true); -} - -PlayerInfo::PlayerInfo(): p7(0), p8(0), p9(0), canHumanPlay(0), canComputerPlay(0), - AITactic(0), isFactionRandom(0), - mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0), - team(255), generateHero(0) -{ - -} - -si8 PlayerInfo::defaultCastle() const -{ - assert(!allowedFactions.empty()); // impossible? - - if(allowedFactions.size() == 1) - { - // only one faction is available - pick it - return *allowedFactions.begin(); - } - - // set to random - return -1; -} - -si8 PlayerInfo::defaultHero() const -{ - // we will generate hero in front of main town - if((generateHeroAtMainTown && hasMainTown) || p8) - { - //random hero - return -1; - } - - return -2; -} - -CMapHeader::CMapHeader(const ui8 *map) -{ - int i=0; - initFromMemory(map,i); -} - -CMapHeader::CMapHeader() -{ - areAnyPLayers = difficulty = levelLimit = howManyTeams = 0; - height = width = twoLevel = -1; -} - -void CMapHeader::initFromMemory( const ui8 *buffer, int &i ) -{ - version = (EMapFormat::EMapFormat)(read_le_u32(buffer + i)); i+=4; //map version - if(version != EMapFormat::ROE && version != EMapFormat::AB && version != EMapFormat::SOD - && version != EMapFormat::WOG) - { - throw std::runtime_error("Invalid map format!"); - } - areAnyPLayers = readChar(buffer,i); //invalid on some maps - height = width = (read_le_u32(buffer + i)); i+=4; // dimensions of map - twoLevel = readChar(buffer,i); //if there is underground - int pom; - name = readString(buffer,i); - description= readString(buffer,i); - difficulty = readChar(buffer,i); // reading map difficulty - if(version != EMapFormat::ROE) - levelLimit = readChar(buffer,i); // hero level limit - else - levelLimit = 0; - loadPlayerInfo(pom, buffer, i); - loadViCLossConditions(buffer, i); - - howManyTeams=buffer[i++]; //read number of teams - if(howManyTeams>0) //read team numbers - { - for(int rr=0; rr<8; ++rr) - { - players[rr].team = buffer[i++]; - } - } - else//no alliances - { - for(int i=0;i EMapFormat::ROE) //probably reserved for further heroes - { - int placeholdersQty = read_le_u32(buffer + i); i+=4; - for(int p = 0; p < placeholdersQty; p++) - placeholdedHeroes.push_back(buffer[i++]); - } -} -void CMapHeader::loadPlayerInfo( int &pom, const ui8 * buffer, int &i ) -{ - players.resize(8); - for (pom=0;pom<8;pom++) - { - players[pom].canHumanPlay = buffer[i++]; - players[pom].canComputerPlay = buffer[i++]; - if ((!(players[pom].canHumanPlay || players[pom].canComputerPlay))) //if nobody can play with this player - { - switch(version) - { - case EMapFormat::SOD: - case EMapFormat::WOG: - i+=13; - break; - case EMapFormat::AB: - i+=12; - break; - case EMapFormat::ROE: - i+=6; - break; - } - continue; - } - - players[pom].AITactic = buffer[i++]; - - if(version == EMapFormat::SOD || version == EMapFormat::WOG) - players[pom].p7= buffer[i++]; - else - players[pom].p7= -1; - - //factions this player can choose - ui16 allowedFactions = buffer[i++]; - if(version != EMapFormat::ROE) - allowedFactions += (buffer[i++])*256; - - for (size_t fact=0; fact<16; fact++) - if (allowedFactions & (1 << fact)) - players[pom].allowedFactions.insert(fact); - - players[pom].isFactionRandom = buffer[i++]; - players[pom].hasMainTown = buffer[i++]; - if (players[pom].hasMainTown) - { - if(version != EMapFormat::ROE) - { - players[pom].generateHeroAtMainTown = buffer[i++]; - players[pom].generateHero = buffer[i++]; - } - else - { - players[pom].generateHeroAtMainTown = true; - players[pom].generateHero = false; - } - - players[pom].posOfMainTown.x = buffer[i++]; - players[pom].posOfMainTown.y = buffer[i++]; - players[pom].posOfMainTown.z = buffer[i++]; - - - } - players[pom].p8= buffer[i++]; - players[pom].p9= buffer[i++]; - if(players[pom].p9!=0xff) - { - players[pom].mainHeroPortrait = buffer[i++]; - players[pom].mainHeroName = readString(buffer,i); - } - - if(version != EMapFormat::ROE) - { - players[pom].powerPlacehodlers = buffer[i++];//unknown byte - int heroCount = buffer[i++]; - i+=3; - for (int pp=0;pp2) - victoryCondition.pos = int3(-1,-1,-1); - else - { - victoryCondition.pos.x = buffer[i+2]; - victoryCondition.pos.y = buffer[i+3]; - victoryCondition.pos.z = buffer[i+4]; - } - nr = 3; - break; - } - case EVictoryConditionType::BEATHERO: - case EVictoryConditionType::CAPTURECITY: - case EVictoryConditionType::BEATMONSTER: - { - victoryCondition.pos.x = buffer[i+2]; - victoryCondition.pos.y = buffer[i+3]; - victoryCondition.pos.z = buffer[i+4]; - nr = 3; - break; - } - case EVictoryConditionType::TAKEDWELLINGS: - case EVictoryConditionType::TAKEMINES: - { - nr = 0; - break; - } - case EVictoryConditionType::TRANSPORTITEM: - { - victoryCondition.ID = buffer[i+2]; - victoryCondition.pos.x = buffer[i+3]; - victoryCondition.pos.y = buffer[i+4]; - victoryCondition.pos.z = buffer[i+5]; - nr = 4; - break; - } - default: - assert(0); - } - victoryCondition.allowNormalVictory = buffer[i++]; - victoryCondition.appliesToAI = buffer[i++]; - i+=nr; - } - lossCondition.typeOfLossCon = (ELossConditionType::ELossConditionType)buffer[i++]; - switch (lossCondition.typeOfLossCon) //read loss conditions - { - case ELossConditionType::LOSSCASTLE: - case ELossConditionType::LOSSHERO: - { - lossCondition.pos.x=buffer[i++]; - lossCondition.pos.y=buffer[i++]; - lossCondition.pos.z=buffer[i++]; - break; - } - case ELossConditionType::TIMEEXPIRES: - { - lossCondition.timeLimit = read_le_u16(buffer + i); - i+=2; - break; - } - } -} - -CMapHeader::~CMapHeader() -{ - -} - -void CMap::initFromBytes(const ui8 * buffer, size_t size) -{ - // Compute checksum - boost::crc_32_type result; - result.process_bytes(buffer, size); - checksum = result.checksum(); - tlog0 << "\tOur map checksum: "<defInfo) - continue; - addBlockVisTiles(objects[f]); - } - tlog0<<"\tCalculating blocked/visitable tiles: "<pos.x + fx - 7; - int yVal = obj->pos.y + fy - 5; - int zVal = obj->pos.z; - if(xVal>=0 && xVal=0 && yValdefInfo->visitMap[fy] >> (7 - fx)) & 1)) - { - curt.visitableObjects -= obj; - curt.visitable = curt.visitableObjects.size(); - } - if(total || !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) - { - curt.blockingObjects -= obj; - curt.blocked = curt.blockingObjects.size(); - } - } - } - } -} -void CMap::addBlockVisTiles(CGObjectInstance * obj) -{ - for(int fx=0; fx<8; ++fx) - { - for(int fy=0; fy<6; ++fy) - { - int xVal = obj->pos.x + fx - 7; - int yVal = obj->pos.y + fy - 5; - int zVal = obj->pos.z; - if(xVal>=0 && xVal=0 && yValdefInfo->visitMap[fy] >> (7 - fx)) & 1)) - { - curt.visitableObjects.push_back(obj); - curt.visitable = true; - } - if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)) - { - curt.blockingObjects.push_back(obj); - curt.blocked = true; - } - } - } - } -} - -TInputStreamPtr CMap::getMapStream(std::string name) -{ - TInputStreamPtr file = CResourceHandler::get()->load(ResourceID(name, EResType::MAP)); - - CBinaryReader reader(*file.get()); - - ui32 header = reader.readUInt32(); - file->seek(0); //reset file - - switch (header & 0xffffff) // gzip header is 3 bytes only in size - { - case 0x00088B1F: // gzip header magic number, reversed for LE - return TInputStreamPtr(new CCompressedStream(std::move(file), true)); - case EMapFormat::WOG : - case EMapFormat::AB : - case EMapFormat::ROE : - case EMapFormat::SOD : - return file; - default : - tlog0 << "Error: Failed to open map " << name << ": unknown format\n"; - return TInputStreamPtr(); - } -} - -CMap::CMap(std::string filename) - : grailPos(-1, -1, -1), grailRadious(0) -{ - tlog0<<"Opening map file: "<getSize(); - std::unique_ptr data(new ui8 [mapSize] ); - file->read(data.get(), mapSize); - - tlog0<<"done."<serial < 0) //def not present in the main vector in defobjinfo - // delete customDefs[i]; - - if(terrain) - { - for(int ii=0;ii >::iterator i = events.begin(); i != events.end(); i++) - { - i->dellNull(); - } -} - -CGHeroInstance * CMap::getHero(int heroID) -{ - for(ui32 i=0; isubID == heroID) - return heroes[i]; - return nullptr; -} - -int CMap::loadSeerHut(const ui8 * buffer, int i, CGObjectInstance * & seerHut) -{ - CGSeerHut *hut = new CGSeerHut(); - seerHut = hut; - - if(version > EMapFormat::ROE) - { - loadQuest(hut,buffer,i); - } - else //RoE - { - int artID = buffer[i]; ++i; - if (artID != 255) //not none quest - { - hut->quest->m5arts.push_back (artID); - hut->quest->missionType = CQuest::MISSION_ART; - } - else - { - hut->quest->missionType = CQuest::MISSION_NONE; //no mission - } - hut->quest->lastDay = -1; //no timeout - hut->quest->isCustomFirst = hut->quest->isCustomNext = hut->quest->isCustomComplete = false; - } - - if (hut->quest->missionType) - { - ui8 rewardType = buffer[i]; ++i; - hut->rewardType = rewardType; - - switch(rewardType) - { - case 1: - { - hut->rVal = read_le_u32(buffer + i); i+=4; - break; - } - case 2: - { - hut->rVal = read_le_u32(buffer + i); i+=4; - break; - } - case 3: - { - hut->rVal = buffer[i]; ++i; - break; - } - case 4: - { - hut->rVal = buffer[i]; ++i; - break; - } - case 5: - { - hut->rID = buffer[i]; ++i; - /* Only the first 3 bytes are used. Skip the 4th. */ - hut->rVal = read_le_u32(buffer + i) & 0x00ffffff; - i+=4; - break; - } - case 6: - { - hut->rID = buffer[i]; ++i; - hut->rVal = buffer[i]; ++i; - break; - } - case 7: - { - hut->rID = buffer[i]; ++i; - hut->rVal = buffer[i]; ++i; - break; - } - case 8: - { - if (version == EMapFormat::ROE) - { - hut->rID = buffer[i]; i++; - } - else - { - hut->rID = read_le_u16(buffer + i); i+=2; - } - break; - } - case 9: - { - hut->rID = buffer[i]; ++i; - break; - } - case 10: - { - if(version > EMapFormat::ROE) - { - hut->rID = read_le_u16(buffer + i); i+=2; - hut->rVal = read_le_u16(buffer + i); i+=2; - } - else - { - hut->rID = buffer[i]; ++i; - hut->rVal = read_le_u16(buffer + i); i+=2; - } - break; - } - }// end of internal switch - i+=2; - } - else //missionType==255 - { - i+=3; - } - return i; -} - -void CMap::loadTown(CGObjectInstance * & town, const ui8 * buffer, int &i, int castleID) -{ - CGTownInstance * nt = new CGTownInstance(); - town = nt; - nt->identifier = 0; - if(version > EMapFormat::ROE) - { - nt->identifier = read_le_u32(buffer + i); i+=4; - } - nt->tempOwner = buffer[i]; ++i; - if(readChar(buffer,i)) //has name - nt->name = readString(buffer,i); - if(readChar(buffer,i))//true if garrison isn't empty - readCreatureSet(nt, buffer, i, 7, version > EMapFormat::ROE); - nt->formation = buffer[i]; ++i; - if(readChar(buffer,i)) //custom buildings info - { - //built buildings - for(int byte=0;byte<6;byte++) - { - for(int bit=0;bit<8;bit++) - if(buffer[i] & (1<builtBuildings.insert(byte*8+bit); - i++; - } - //forbidden buildings - for(int byte=6;byte<12;byte++) - { - for(int bit=0;bit<8;bit++) - if(buffer[i] & (1<forbiddenBuildings.insert((byte-6)*8+bit); - i++; - } - nt->builtBuildings = convertBuildings(nt->builtBuildings,castleID); - nt->forbiddenBuildings = convertBuildings(nt->forbiddenBuildings,castleID); - } - else //standard buildings - { - if(readChar(buffer,i)) //has fort - nt->builtBuildings.insert(EBuilding::FORT); - nt->builtBuildings.insert(-50); //means that set of standard building should be included - } - - int ist = i; - if(version > EMapFormat::ROE) - { - for(; iobligatorySpells.push_back((i-ist)*8+yy); - } - } - } - } - - ist = i; - for(; ipossibleSpells.push_back((i-ist)*8+yy); - } - } - } - - /////// reading castle events ////////////////////////////////// - - int numberOfEvent = read_le_u32(buffer + i); i+=4; - - for(int gh = 0; ghtown = nt; - nce->name = readString(buffer,i); - nce->message = readString(buffer,i); - for(int x=0; x < 7; x++) - { - nce->resources[x] = read_le_u32(buffer + i); - i+=4; - } - - nce->players = buffer[i]; ++i; - if(version > EMapFormat::AB) - { - nce->humanAffected = buffer[i]; ++i; - } - else - nce->humanAffected = true; - - nce->computerAffected = buffer[i]; ++i; - nce->firstOccurence = read_le_u16(buffer + i); i+=2; - nce->nextOccurence = buffer[i]; ++i; - - i+=17; - - //new buildings - for(int byte=0;byte<6;byte++) - { - for(int bit=0;bit<8;bit++) - if(buffer[i] & (1<buildings.insert(byte*8+bit); - i++; - } - nce->buildings = convertBuildings(nce->buildings,castleID, false); - - nce->creatures.resize(7); - for(int vv=0; vv<7; ++vv) - { - nce->creatures[vv] = read_le_u16(buffer + i);i+=2; - } - i+=4; - nt->events.push_back(nce); - }//castle events have been read - - if(version > EMapFormat::AB) - { - nt->alignment = buffer[i]; ++i; - } - else - nt->alignment = 0xff; - i+=3; - - nt->builded = 0; - nt->destroyed = 0; - nt->garrisonHero = NULL; -} - -CGObjectInstance * CMap::loadHero(const ui8 * buffer, int &i, int idToBeGiven) -{ - CGHeroInstance * nhi = new CGHeroInstance(); - - int identifier = 0; - if(version > EMapFormat::ROE) - { - identifier = read_le_u32(buffer + i); i+=4; - questIdentifierToId[identifier] = idToBeGiven; - } - - ui8 owner = buffer[i++]; - nhi->subID = buffer[i++]; - - for(ui32 j=0; jsubID == nhi->subID) - { - tlog0 << "Hero " << nhi->subID << " will be taken from the predefined heroes list.\n"; - delete nhi; - nhi = predefinedHeroes[j]; - break; - } - } - nhi->setOwner(owner); - - nhi->portrait = nhi->subID; - - for(ui32 j=0; jsubID) - { - nhi->name = disposedHeroes[j].name; - nhi->portrait = disposedHeroes[j].portrait; - break; - } - } - if(readChar(buffer,i))//true if hero has nonstandard name - nhi->name = readString(buffer,i); - if(version > EMapFormat::AB) - { - if(readChar(buffer,i))//true if hero's experience is greater than 0 - { nhi->exp = read_le_u32(buffer + i); i+=4; } - else - nhi->exp = 0xffffffff; - } - else - { - nhi->exp = read_le_u32(buffer + i); i+=4; - if(!nhi->exp) //0 means "not set" in <=AB maps - nhi->exp = 0xffffffff; - } - - bool portrait=buffer[i]; ++i; - if (portrait) - nhi->portrait = buffer[i++]; - if(readChar(buffer,i))//true if hero has specified abilities - { - int howMany = read_le_u32(buffer + i); i+=4; - nhi->secSkills.resize(howMany); - for(int yy=0; yysecSkills[yy].first = buffer[i++]; - nhi->secSkills[yy].second = buffer[i++]; - } - } - if(readChar(buffer,i))//true if hero has nonstandard garrison - readCreatureSet(nhi, buffer, i, 7, version > EMapFormat::ROE); - - nhi->formation =buffer[i]; ++i; //formation - loadArtifactsOfHero(buffer, i, nhi); - nhi->patrol.patrolRadious = buffer[i]; ++i; - if(nhi->patrol.patrolRadious == 0xff) - nhi->patrol.patrolling = false; - else - nhi->patrol.patrolling = true; - - if(version > EMapFormat::ROE) - { - if(readChar(buffer,i))//true if hero has nonstandard (mapmaker defined) biography - nhi->biography = readString(buffer,i); - nhi->sex = buffer[i]; ++i; - - if (nhi->sex != 0xFF)//remove trash - nhi->sex &=1; - } - else - nhi->sex = 0xFF; - //spells - if(version > EMapFormat::AB) - { - bool areSpells = buffer[i]; ++i; - - if(areSpells) //TODO: sprawdzi //seems to be ok - tow - { - nhi->spells.insert(0xffffffff); //placeholder "preset spells" - int ist = i; - for(; ispells.insert((i-ist)*8+yy); - } - } - } - } - } - else if(version == EMapFormat::AB) //we can read one spell - { - ui8 buff = buffer[i]; ++i; - if(buff != 254) - { - nhi->spells.insert(0xffffffff); //placeholder "preset spells" - if(buff < 254) //255 means no spells - nhi->spells.insert(buff); - } - } - //spells loaded - if(version > EMapFormat::AB) - { - if(readChar(buffer,i))//customPrimSkills - { - for(int xx=0; xxpushPrimSkill(xx, buffer[i++]); - } - } - i+=16; - - return nhi; -} - -void CMap::readRumors( const ui8 * buffer, int &i) -{ - int rumNr = read_le_u32(buffer + i);i+=4; - for (int it=0;it= EMapFormat::SOD) - { - disp = buffer[i++]; - disposedHeroes.resize(disp); - for(int g=0; garth->artifacts) - { - if (artifact->constituents) //combo - { - allowedArtifact[artifact->id] = false; - } - } - if (version == EMapFormat::ROE) - allowedArtifact[128] = false; //Armageddon's Blade - } - - allowedSpell.resize(GameConstants::SPELLS_QUANTITY); - for(ui32 x=0;x= EMapFormat::SOD) - { - //reading allowed spells (9 bytes) - ist=i; //starting i for loop - for(; iID = Obj::HERO; - cgh->subID = z; - if(readChar(buffer,i))//true if hore's experience is greater than 0 - { cgh->exp = read_le_u32(buffer + i); i+=4; } - else - cgh->exp = 0; - if(readChar(buffer,i))//true if hero has specified abilities - { - int howMany = read_le_u32(buffer + i); i+=4; - cgh->secSkills.resize(howMany); - for(int yy=0; yysecSkills[yy].first = buffer[i]; ++i; - cgh->secSkills[yy].second = buffer[i]; ++i; - } - } - - loadArtifactsOfHero(buffer, i, cgh); - - if(readChar(buffer,i))//customBio - cgh->biography = readString(buffer,i); - cgh->sex = buffer[i++]; // 0xFF is default, 00 male, 01 female - if(readChar(buffer,i))//are spells - { - int ist = i; - for(; ispells.insert((i-ist)*8+yy); - } - } - } - } - if(readChar(buffer,i))//customPrimSkills - { - for(int xx=0; xxpushPrimSkill(xx, buffer[i++]); - } - predefinedHeroes.push_back(cgh); - } - break; - } - case EMapFormat::ROE: - i+=0; - break; - } -} - -void CMap::readTerrain( const ui8 * buffer, int &i) -{ - terrain = new TerrainTile**[width]; // allocate memory - for (int ii=0;ii(buffer[i++]); - terrain[z][c][0].terview = buffer[i++]; - terrain[z][c][0].riverType = static_cast(buffer[i++]); - terrain[z][c][0].riverDir = buffer[i++]; - terrain[z][c][0].roadType = static_cast(buffer[i++]); - terrain[z][c][0].roadDir = buffer[i++]; - terrain[z][c][0].extTileFlags = buffer[i++]; - terrain[z][c][0].blocked = (terrain[z][c][0].tertype == ETerrainType::ROCK ? 1 : 0); //underground tiles are always blocked - terrain[z][c][0].visitable = 0; - } - } - if (twoLevel) // read underground terrain - { - for (int c=0; c(buffer[i++]); - terrain[z][c][1].terview = buffer[i++]; - terrain[z][c][1].riverType = static_cast(buffer[i++]); - terrain[z][c][1].riverDir = buffer[i++]; - terrain[z][c][1].roadType = static_cast(buffer[i++]); - terrain[z][c][1].roadDir = buffer[i++]; - terrain[z][c][1].extTileFlags = buffer[i++]; - terrain[z][c][1].blocked = (terrain[z][c][1].tertype == ETerrainType::ROCK ? 1 : 0); //underground tiles are always blocked - terrain[z][c][1].visitable = 0; - } - } - } -} - -void CMap::readDefInfo( const ui8 * buffer, int &i) -{ - int defAmount = read_le_u32(buffer + i); i+=4; - customDefs.reserve(defAmount+8); - for (int idd = 0 ; iddname.reserve(nameLength); - for (int cd=0;cdname += buffer[i++]; - } - std::transform(vinya->name.begin(),vinya->name.end(),vinya->name.begin(),(int(*)(int))toupper); - - - ui8 bytes[12]; - for (int v=0; v<12; v++) // read info - { - bytes[v] = buffer[i++]; - } - vinya->terrainAllowed = read_le_u16(buffer + i);i+=2; - vinya->terrainMenu = read_le_u16(buffer + i);i+=2; - vinya->id = read_le_u32(buffer + i);i+=4; - vinya->subid = read_le_u32(buffer + i);i+=4; - vinya->type = buffer[i++]; - vinya->printPriority = buffer[i++]; - for (int zi=0; zi<6; zi++) - { - vinya->blockMap[zi] = reverse(bytes[zi]); - } - for (int zi=0; zi<6; zi++) - { - vinya->visitMap[zi] = reverse(bytes[6+zi]); - } - i+=16; - if(vinya->id!=Obj::HERO && vinya->id!=70) - { - CGDefInfo *h = VLC->dobjinfo->gobjs[vinya->id][vinya->subid]; - if(!h) - { - //remove fake entry - VLC->dobjinfo->gobjs[vinya->id].erase(vinya->subid); - if(VLC->dobjinfo->gobjs[vinya->id].size()) - VLC->dobjinfo->gobjs.erase(vinya->id); - tlog2<<"\t\tWarning: no defobjinfo entry for object ID="<id<<" subID=" << vinya->subid<visitDir = VLC->dobjinfo->gobjs[vinya->id][vinya->subid]->visitDir; - } - } - else - { - vinya->visitDir = 0xff; - } - - if(vinya->id == Obj::EVENT) - std::memset(vinya->blockMap,255,6); - - //calculating coverageMap - vinya->fetchInfoFromMSK(); - - customDefs.push_back(vinya); // add this def to the vector - } - - //add holes - they always can appear - for (int i = 0; i < 8 ; i++) - { - customDefs.push_back(VLC->dobjinfo->gobjs[124][i]); - } -} - -class _HERO_SORTER -{ -public: - bool operator()(const ConstTransitivePtr & a, const ConstTransitivePtr & b) - { - return a->subID < b->subID; - } -}; - -void CMap::readObjects(const ui8 * buffer, int &i) -{ - int howManyObjs = read_le_u32(buffer + i); i+=4; - for(int ww=0; wwid) - { - case Obj::EVENT: //for event objects - { - CGEvent *evnt = new CGEvent(); - nobj = evnt; - - bool guardMess = buffer[i]; ++i; - if(guardMess) - { - int messLong = read_le_u32(buffer + i); i+=4; - if(messLong>0) - { - for(int yy=0; yymessage +=buffer[i+yy]; - } - i+=messLong; - } - if(buffer[i++]) - { - readCreatureSet(evnt, buffer, i, 7, version > EMapFormat::ROE); - } - i+=4; - } - evnt->gainedExp = read_le_u32(buffer + i); i+=4; - evnt->manaDiff = read_le_u32(buffer + i); i+=4; - evnt->moraleDiff = (char)buffer[i]; ++i; - evnt->luckDiff = (char)buffer[i]; ++i; - - evnt->resources.resize(GameConstants::RESOURCE_QUANTITY); - for(int x=0; x<7; x++) - { - evnt->resources[x] = read_le_u32(buffer + i); - i+=4; - } - - evnt->primskills.resize(GameConstants::PRIMARY_SKILLS); - for(int x=0; x<4; x++) - { - evnt->primskills[x] = buffer[i]; - i++; - } - - int gabn; //number of gained abilities - gabn = buffer[i]; ++i; - for(int oo = 0; ooabilities.push_back(buffer[i]); ++i; - evnt->abilityLevels.push_back(buffer[i]); ++i; - } - - int gart = buffer[i]; ++i; //number of gained artifacts - for(int oo = 0; ooartifacts.push_back(buffer[i]); i++; - } - else - { - evnt->artifacts.push_back(read_le_u16(buffer + i)); i+=2; - } - } - - int gspel = buffer[i]; ++i; //number of gained spells - for(int oo = 0; oospells.push_back(buffer[i]); ++i; - } - - int gcre = buffer[i]; ++i; //number of gained creatures - readCreatureSet(&evnt->creatures, buffer,i,gcre,(version > EMapFormat::ROE)); - - i+=8; - evnt->availableFor = buffer[i]; ++i; - evnt->computerActivate = buffer[i]; ++i; - evnt->removeAfterVisit = buffer[i]; ++i; - evnt->humanActivate = true; - - i+=4; - break; - } - case 34: case 70: case 62: //34 - hero; 70 - random hero; 62 - prison - { - nobj = loadHero(buffer, i, idToBeGiven); - break; - } - case 4: //Arena - case 51: //Mercenary Camp - case 23: //Marletto Tower - case 61: // Star Axis - case 32: // Garden of Revelation - case 100: //Learning Stone - case 102: //Tree of Knowledge - case 41: //Library of Enlightenment - case 47: //School of Magic - case 107: //School of War - { - nobj = new CGVisitableOPH(); - break; - } - case 55: //mystical garden - case 112://windmill - case 109://water wheel - { - nobj = new CGVisitableOPW(); - break; - } - case 43: //teleport - case 44: //teleport - case 45: //teleport - case 103://subterranean gate - case 111://Whirlpool - { - nobj = new CGTeleport(); - break; - } - case 12: //campfire - case 29: //Flotsam - case 82: //Sea Chest - case 86: //Shipwreck Survivor - case 101://treasure chest - { - nobj = new CGPickable(); - break; - } - case 54: //Monster - case 71: case 72: case 73: case 74: case 75: // Random Monster 1 - 4 - case 162: case 163: case 164: // Random Monster 5 - 7 - { - CGCreature *cre = new CGCreature(); - nobj = cre; - - if(version > EMapFormat::ROE) - { - cre->identifier = read_le_u32(buffer + i); i+=4; - questIdentifierToId[cre->identifier] = idToBeGiven; - //monsters[cre->identifier] = cre; - } - - CStackInstance *hlp = new CStackInstance(); - hlp->count = read_le_u16(buffer + i); i+=2; - //type will be set during initialization - cre->putStack(0, hlp); - - cre->character = buffer[i]; ++i; - bool isMesTre = buffer[i]; ++i; //true if there is message or treasury - if(isMesTre) - { - cre->message = readString(buffer,i); - cre->resources.resize(GameConstants::RESOURCE_QUANTITY); - for(int j=0; j<7; j++) - { - cre->resources[j] = read_le_u32(buffer + i); i+=4; - } - - int artID; - if (version == EMapFormat::ROE) - { - artID = buffer[i]; i++; - } - else - { - artID = read_le_u16(buffer + i); i+=2; - } - - if(version == EMapFormat::ROE) - { - if(artID!=0xff) - cre->gainedArtifact = artID; - else - cre->gainedArtifact = -1; - } - else - { - if(artID!=0xffff) - cre->gainedArtifact = artID; - else - cre->gainedArtifact = -1; - } - } - cre->neverFlees = buffer[i]; ++i; - cre->notGrowingTeam = buffer[i]; ++i; - i+=2;; - break; - } - case 59: case 91: //ocean bottle and sign - { - CGSignBottle *sb = new CGSignBottle(); - nobj = sb; - sb->message = readString(buffer,i); - i+=4; - break; - } - case 83: //seer's hut - { - i = loadSeerHut(buffer, i, nobj); - addQuest (nobj); - break; - } - case 113: //witch hut - { - CGWitchHut *wh = new CGWitchHut(); - nobj = wh; - if(version > EMapFormat::ROE) //in reo we cannot specify it - all are allowed (I hope) - { - int ist=i; //starting i for loop - for(; iallowedAbilities.push_back((i-ist)*8+yy); - } - } - } - } - else //(RoE map) - { - for(int gg=0; ggallowedAbilities.push_back(gg); - } - } - break; - } - case 81: //scholar - { - CGScholar *sch = new CGScholar(); - nobj = sch; - sch->bonusType = buffer[i++]; - sch->bonusID = buffer[i++]; - i+=6; - break; - } - case 33: case 219: //garrison - { - CGGarrison *gar = new CGGarrison(); - nobj = gar; - nobj->setOwner(buffer[i++]); - i+=3; - readCreatureSet(gar, buffer, i, 7, version > EMapFormat::ROE); - if(version > EMapFormat::ROE) - { - gar->removableUnits = buffer[i]; ++i; - } - else - gar->removableUnits = true; - i+=8; - break; - } - case 5: //artifact - case 65: case 66: case 67: case 68: case 69: //random artifact - case 93: //spell scroll - { - int artID = -1; - int spellID = -1; - CGArtifact *art = new CGArtifact(); - nobj = art; - - bool areSettings = buffer[i++]; - if(areSettings) - { - art->message = readString(buffer,i); - bool areGuards = buffer[i++]; - if(areGuards) - { - readCreatureSet(art, buffer, i, 7, version > EMapFormat::ROE); - } - i+=4; - } - - if(defInfo->id==93) - { - spellID = read_le_u32(buffer + i); i+=4; - artID = 1; - } - else if(defInfo->id == 5) //specific artifact - { - artID = defInfo->subid; - } - - art->storedArtifact = createArt(artID, spellID); - break; - } - case 76: case 79: //random resource; resource - { - CGResource *res = new CGResource(); - nobj = res; - - bool isMessGuard = buffer[i]; ++i; - if(isMessGuard) - { - res->message = readString(buffer,i); - if(buffer[i++]) - { - readCreatureSet(res, buffer, i, 7, version > EMapFormat::ROE); - } - i+=4; - } - res->amount = read_le_u32(buffer + i); i+=4; - if (defInfo->subid == 6) // Gold is multiplied by 100. - res->amount *= 100; - i+=4; - - break; - } - case 77: case 98: //random town; town - { - loadTown(nobj, buffer, i, defInfo->subid); - break; - } - case 53: - case 220://mine (?) - { - nobj = new CGMine(); - nobj->setOwner(buffer[i++]); - i+=3; - break; - } - case 17: case 18: case 19: case 20: //dwellings - { - nobj = new CGDwelling(); - nobj->setOwner(buffer[i++]); - i+=3; - break; - } - case 78: //Refugee Camp - case 106: //War Machine Factory - { - nobj = new CGDwelling(); - break; - } - case 88: case 89: case 90: //spell shrine - { - CGShrine * shr = new CGShrine(); - nobj = shr; - shr->spell = buffer[i]; i+=4; - break; - } - case 6: //pandora's box - { - CGPandoraBox *box = new CGPandoraBox(); - nobj = box; - bool messg = buffer[i]; ++i; - if(messg) - { - box->message = readString(buffer,i); - if(buffer[i++]) - { - readCreatureSet(box, buffer, i, 7, version > EMapFormat::ROE); - } - i+=4; - } - - box->gainedExp = read_le_u32(buffer + i); i+=4; - box->manaDiff = read_le_u32(buffer + i); i+=4; - box->moraleDiff = (si8)buffer[i]; ++i; - box->luckDiff = (si8)buffer[i]; ++i; - - box->resources.resize(GameConstants::RESOURCE_QUANTITY); - for(int x=0; x<7; x++) - { - box->resources[x] = read_le_u32(buffer + i); - i+=4; - } - - box->primskills.resize(GameConstants::PRIMARY_SKILLS); - for(int x=0; x<4; x++) - { - box->primskills[x] = buffer[i]; - i++; - } - - int gabn; //number of gained abilities - gabn = buffer[i]; ++i; - for(int oo = 0; ooabilities.push_back(buffer[i]); ++i; - box->abilityLevels.push_back(buffer[i]); ++i; - } - int gart = buffer[i]; ++i; //number of gained artifacts - for(int oo = 0; oo EMapFormat::ROE) - { - box->artifacts.push_back(read_le_u16(buffer + i)); i+=2; - } - else - { - box->artifacts.push_back(buffer[i]); i+=1; - } - } - int gspel = buffer[i]; ++i; //number of gained spells - for(int oo = 0; oospells.push_back(buffer[i]); ++i; - } - int gcre = buffer[i]; ++i; //number of gained creatures - readCreatureSet(&box->creatures, buffer,i,gcre,(version > EMapFormat::ROE)); - i+=8; - break; - } - case 36: //grail - { - grailPos = pos; - grailRadious = read_le_u32(buffer + i); i+=4; - continue; - } - //dwellings - case 216: //same as castle + level range - case 217: //same as castle - case 218: //level range - { - nobj = new CGDwelling(); - CSpecObjInfo * spec = nullptr; - switch(defInfo->id) - { - break; case 216: spec = new CCreGenLeveledCastleInfo; - break; case 217: spec = new CCreGenAsCastleInfo; - break; case 218: spec = new CCreGenLeveledInfo; - } - - spec->player = read_le_u32(buffer + i); i+=4; - //216 and 217 - if (auto castleSpec = dynamic_cast(spec)) - { - castleSpec->identifier = read_le_u32(buffer + i); i+=4; - if(!castleSpec->identifier) - { - castleSpec->asCastle = false; - castleSpec->castles[0] = buffer[i]; ++i; - castleSpec->castles[1] = buffer[i]; ++i; - } - else - { - castleSpec->asCastle = true; - } - } - - //216 and 218 - if (auto lvlSpec = dynamic_cast(spec)) - { - lvlSpec->minLevel = std::max(buffer[i], ui8(1)); ++i; - lvlSpec->maxLevel = std::min(buffer[i], ui8(7)); ++i; - } - nobj->setOwner(spec->player); - static_cast(nobj)->info = spec; - break; - } - case 215: - { - CGQuestGuard *guard = new CGQuestGuard(); - addQuest (guard); - loadQuest(guard, buffer, i); - nobj = guard; - break; - } - case 28: //faerie ring - case 14: //Swan pond - case 38: //idol of fortune - case 30: //Fountain of Fortune - case 64: //Rally Flag - case 56: //oasis - case 96: //temple - case 110://Watering Hole - case 31: //Fountain of Youth - case 11: //Buoy - case 52: //Mermaid - case 94: //Stables - { - nobj = new CGBonusingObject(); - break; - } - case 49: //Magic Well - { - nobj = new CGMagicWell(); - break; - } - case 15: //Cover of darkness - case 58: //Redwood Observatory - case 60: //Pillar of Fire - { - nobj = new CGObservatory(); - break; - } - case 22: //Corpse - case 39: //Lean To - case 105://Wagon - case 108://Warrior's Tomb - { - nobj = new CGOnceVisitable(); - break; - } - case 8: //Boat - { - nobj = new CGBoat(); - break; - } - case 92: //Sirens - { - nobj = new CGSirens(); - break; - } - case 87: //Shipyard - { - nobj = new CGShipyard(); - nobj->setOwner(read_le_u32(buffer + i)); i+=4; - break; - } - case 214: //hero placeholder - { - CGHeroPlaceholder *hp = new CGHeroPlaceholder();; - nobj = hp; - - int a = buffer[i++]; //unknown byte, seems to be always 0 (if not - scream!) - tlog2 << "Unhandled Hero Placeholder detected: "<subID = htid; - - if(htid == 0xff) - hp->power = buffer[i++]; - else - hp->power = 0; - - break; - } - case 10: //Keymaster - { - nobj = new CGKeymasterTent(); - break; - } - case 9: //Border Guard - { - nobj = new CGBorderGuard(); - addQuest (nobj); - break; - } - case 212: //Border Gate - { - nobj = new CGBorderGate(); - addQuest (nobj); - break; - } - case 27: case 37: //Eye and Hut of Magi - { - nobj = new CGMagi(); - break; - } - case 16: case 24: case 25: case 84: case 85: //treasure bank - { - nobj = new CBank(); - break; - } - case 63: //Pyramid - { - nobj = new CGPyramid(); - break; - } - case 13: //Cartographer - { - nobj = new CCartographer(); - break; - } - case 48: //Magic Spring - { - nobj = new CGMagicSpring(); - break; - } - case 97: //den of thieves - { - nobj = new CGDenOfthieves(); - break; - } - case 57: //Obelisk - { - nobj = new CGObelisk(); - break; - } - case 42: //Lighthouse - { - nobj = new CGLighthouse(); - nobj->tempOwner = read_le_u32(buffer + i); i+=4; - break; - } - case 2: //Altar of Sacrifice - case 99: //Trading Post - case 213: //Freelancer's Guild - case 221: //Trading Post (snow) - { - nobj = new CGMarket(); - break; - } - case 104: //University - { - nobj = new CGUniversity(); - break; - } - case 7: //Black Market - { - nobj = new CGBlackMarket(); - break; - } - - default: //any other object - { - nobj = new CGObjectInstance(); - break; - } - - } //end of main switch - - nobj->pos = pos; - nobj->ID = defInfo->id; - nobj->id = idToBeGiven; - if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON) - nobj->subID = defInfo->subid; - nobj->defInfo = defInfo; - assert(idToBeGiven == objects.size()); - objects.push_back(nobj); - if(nobj->ID==Obj::TOWN) - towns.push_back(static_cast(nobj)); - if(nobj->ID==Obj::HERO) - heroes.push_back(static_cast(nobj)); - } - - std::sort(heroes.begin(), heroes.end(), _HERO_SORTER()); -} - -void CMap::readEvents( const ui8 * buffer, int &i ) -{ - int numberOfEvents = read_le_u32(buffer + i); i+=4; - for(int yyoo=0; yyooname = std::string(); - ne->message = std::string(); - int nameLen = read_le_u32(buffer + i); i+=4; - for(int qq=0; qqname += buffer[i]; ++i; - } - int messLen = read_le_u32(buffer + i); i+=4; - for(int qq=0; qqmessage +=buffer[i]; ++i; - } - for(int k=0; k < 7; k++) - { - ne->resources[k] = read_le_u32(buffer + i); i+=4; - } - ne->players = buffer[i]; ++i; - if(version > EMapFormat::AB) - { - ne->humanAffected = buffer[i]; ++i; - } - else - ne->humanAffected = true; - ne->computerAffected = buffer[i]; ++i; - ne->firstOccurence = read_le_u16(buffer + i); i+=2; - ne->nextOccurence = buffer[i]; ++i; - - char unknown[17]; - memcpy(unknown, buffer+i, 17); - i+=17; - - events.push_back(ne); - } -} - -bool CMap::isInTheMap(const int3 &pos) const -{ - if(pos.x<0 || pos.y<0 || pos.z<0 || pos.x >= width || pos.y >= height || pos.z > twoLevel) - return false; - else return true; -} - -void CMap::loadQuest(IQuestObject * guard, const ui8 * buffer, int & i) -{ - guard->quest->missionType = buffer[i]; ++i; - //int len1, len2, len3; - switch(guard->quest->missionType) - { - case 0: - return; - case 2: - { - guard->quest->m2stats.resize(4); - for(int x=0; x<4; x++) - { - guard->quest->m2stats[x] = buffer[i++]; - } - } - break; - case 1: - case 3: - case 4: - { - guard->quest->m13489val = read_le_u32(buffer + i); i+=4; - break; - } - case 5: - { - int artNumber = buffer[i]; ++i; - for(int yy=0; yyquest->m5arts.push_back(artid); - allowedArtifact[artid] = false; //these are unavailable for random generation - } - break; - } - case 6: - { - int typeNumber = buffer[i]; ++i; - guard->quest->m6creatures.resize(typeNumber); - for(int hh=0; hhquest->m6creatures[hh].type = VLC->creh->creatures[read_le_u16(buffer + i)]; i+=2; - guard->quest->m6creatures[hh].count = read_le_u16(buffer + i); i+=2; - } - break; - } - case 7: - { - guard->quest->m7resources.resize(7); - for(int x=0; x<7; x++) - { - guard->quest->m7resources[x] = read_le_u32(buffer + i); - i+=4; - } - break; - } - case 8: - case 9: - { - guard->quest->m13489val = buffer[i]; ++i; - break; - } - } - - - int limit = read_le_u32(buffer + i); i+=4; - if(limit == ((int)0xffffffff)) - { - guard->quest->lastDay = -1; - } - else - { - guard->quest->lastDay = limit; - } - guard->quest->firstVisitText = readString(buffer,i); - guard->quest->nextVisitText = readString(buffer,i); - guard->quest->completedText = readString(buffer,i); - guard->quest->isCustomFirst = guard->quest->firstVisitText.size() > 0; - guard->quest->isCustomNext = guard->quest->nextVisitText.size() > 0; - guard->quest->isCustomComplete = guard->quest->completedText.size() > 0; -} - -TerrainTile & CMap::getTile( const int3 & tile ) -{ - return terrain[tile.x][tile.y][tile.z]; -} - -const TerrainTile & CMap::getTile( const int3 & tile ) const -{ - return terrain[tile.x][tile.y][tile.z]; -} - -bool CMap::isWaterTile(const int3 &pos) const -{ - return isInTheMap(pos) && getTile(pos).tertype == ETerrainType::WATER; -} - -const CGObjectInstance *CMap::getObjectiveObjectFrom(int3 pos, bool lookForHero) -{ - const std::vector & objs = getTile(pos).visitableObjects; - assert(objs.size()); - if(objs.size() > 1 && lookForHero && objs.front()->ID != Obj::HERO) - { - assert(objs.back()->ID == Obj::HERO); - return objs.back(); - } - else - return objs.front(); -} - -void CMap::checkForObjectives() -{ - if(isInTheMap(victoryCondition.pos)) - victoryCondition.obj = getObjectiveObjectFrom(victoryCondition.pos, victoryCondition.condition == EVictoryConditionType::BEATHERO); - - if(isInTheMap(lossCondition.pos)) - lossCondition.obj = getObjectiveObjectFrom(lossCondition.pos, lossCondition.typeOfLossCon == ELossConditionType::LOSSHERO); -} - -void CMap::addNewArtifactInstance( CArtifactInstance *art ) -{ - art->id = artInstances.size(); - artInstances.push_back(art); -} - -void CMap::addQuest (CGObjectInstance * quest) -{ - auto q = dynamic_cast(quest); - q->quest->qid = quests.size(); - quests.push_back (q->quest); -} - -bool CMap::loadArtifactToSlot(CGHeroInstance * hero, int slot, const ui8 * buffer, int & i) -{ - const int artmask = version == EMapFormat::ROE ? 0xff : 0xffff; - int aid; - - if (version == EMapFormat::ROE) - { - aid = buffer[i]; i++; - } - else - { - aid = read_le_u16(buffer + i); i+=2; - } - - bool isArt = aid != artmask; - if(isArt) - { - if(vstd::contains(VLC->arth->bigArtifacts, aid) && slot >= GameConstants::BACKPACK_START) - { - tlog3 << "Warning: A big artifact (war machine) in hero's backpack, ignoring...\n"; - return false; - } - if(aid == 0 && slot == ArtifactPosition::MISC5) - { - //TODO: check how H3 handles it -> art 0 in slot 18 in AB map - tlog3 << "Spellbook to MISC5 slot? Putting it spellbook place. AB format peculiarity ? (format " << (int)version << ")\n"; - slot = ArtifactPosition::SPELLBOOK; - } - - hero->putArtifact(slot, createArt(aid)); - } - return isArt; -} - -void CMap::loadArtifactsOfHero(const ui8 * buffer, int & i, CGHeroInstance * hero) -{ - bool artSet = buffer[i]; ++i; //true if artifact set is not default (hero has some artifacts) - if(artSet) - { - for(int pom=0;pom<16;pom++) - loadArtifactToSlot(hero, pom, buffer, i); - - //misc5 art //17 - if(version >= EMapFormat::SOD) - { - if(!loadArtifactToSlot(hero, ArtifactPosition::MACH4, buffer, i)) - hero->putArtifact(ArtifactPosition::MACH4, createArt(GameConstants::ID_CATAPULT)); //catapult by default - } - - loadArtifactToSlot(hero, ArtifactPosition::SPELLBOOK, buffer, i); - - //19 //???what is that? gap in file or what? - it's probably fifth slot.. - if(version > EMapFormat::ROE) - loadArtifactToSlot(hero, ArtifactPosition::MISC5, buffer, i); - else - i+=1; - - //bag artifacts //20 - int amount = read_le_u16(buffer + i); i+=2; //number of artifacts in hero's bag - for(int ss = 0; ss < amount; ++ss) - loadArtifactToSlot(hero, GameConstants::BACKPACK_START + hero->artifactsInBackpack.size(), buffer, i); - } //artifacts -} - -CArtifactInstance * CMap::createArt(int aid, int spellID /*= -1*/) -{ - CArtifactInstance *a = NULL; - if(aid >= 0) - { - if(spellID < 0) - a = CArtifactInstance::createNewArtifactInstance(aid); - else - a = CArtifactInstance::createScroll(VLC->spellh->spells[spellID]); - } - else - a = new CArtifactInstance(); - - addNewArtifactInstance(a); - if(a->artType && a->artType->constituents) //TODO make it nicer - { - CCombinedArtifactInstance *comb = dynamic_cast(a); - BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo &ci, comb->constituentsInfo) - { - addNewArtifactInstance(ci.art); - } - } - return a; -} - -void CMap::eraseArtifactInstance(CArtifactInstance *art) -{ - assert(artInstances[art->id] == art); - artInstances[art->id].dellNull(); -} - -LossCondition::LossCondition() -{ - obj = NULL; - timeLimit = -1; - pos = int3(-1,-1,-1); -} - -VictoryCondition::VictoryCondition() -{ - pos = int3(-1,-1,-1); - obj = NULL; - ID = allowNormalVictory = appliesToAI = count = 0; -} - -bool TerrainTile::entrableTerrain(const TerrainTile * from /*= NULL*/) const -{ - return entrableTerrain(from ? from->tertype != ETerrainType::WATER : true, from ? from->tertype == ETerrainType::WATER : true); -} - -bool TerrainTile::entrableTerrain(bool allowLand, bool allowSea) const -{ - return tertype != ETerrainType::ROCK - && ((allowSea && tertype == ETerrainType::WATER) || (allowLand && tertype != ETerrainType::WATER)); -} - -bool TerrainTile::isClear(const TerrainTile *from /*= NULL*/) const -{ - return entrableTerrain(from) && !blocked; -} - -int TerrainTile::topVisitableID() const -{ - return visitableObjects.size() ? visitableObjects.back()->ID : -1; -} - -bool TerrainTile::isCoastal() const -{ - return extTileFlags & 64; -} - -bool TerrainTile::hasFavourableWinds() const -{ - return extTileFlags & 128; -} - -bool TerrainTile::isWater() const -{ - return tertype == ETerrainType::WATER; -} diff --git a/lib/vcmi_endian.h b/lib/vcmi_endian.h index e83225a0c..2bb78024e 100644 --- a/lib/vcmi_endian.h +++ b/lib/vcmi_endian.h @@ -44,19 +44,21 @@ static inline ui32 read_unaligned_u32(const void *p) #define read_le_u32(p) (SDL_SwapLE32(* reinterpret_cast(p))) #endif -static inline char readChar(const ui8 * bufor, int &i) +static inline char readChar(const ui8 * buffer, int & i) { - return bufor[i++]; + return buffer[i++]; } -static inline std::string readString(const ui8 * bufor, int &i) +static inline std::string readString(const ui8 * buffer, int & i) { - int len = read_le_u32(bufor + i); i+=4; + int len = read_le_u32(buffer + i); + i += 4; assert(len >= 0 && len <= 500000); //not too long - std::string ret; ret.reserve(len); - for(int gg=0; gg