diff --git a/client/lobby/CBonusSelection.cpp b/client/lobby/CBonusSelection.cpp index 1ba12e16d..595cb50b2 100644 --- a/client/lobby/CBonusSelection.cpp +++ b/client/lobby/CBonusSelection.cpp @@ -51,6 +51,7 @@ #include "../../lib/mapObjects/CGHeroInstance.h" + std::shared_ptr CBonusSelection::getCampaign() { return CSH->si->campState; @@ -61,8 +62,7 @@ CBonusSelection::CBonusSelection() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - loadPositionsOfGraphics(); - std::string bgName = campDescriptions[getCampaign()->camp->header.mapVersion].campPrefix + "_BG.BMP"; + std::string bgName = getCampaign()->camp->header.campaignRegions.campPrefix + "_BG.BMP"; setBackground(bgName); panelBackground = std::make_shared("CAMPBRF.BMP", 456, 6); @@ -106,35 +106,9 @@ CBonusSelection::CBonusSelection() for(int g = 0; g < getCampaign()->camp->scenarios.size(); ++g) { if(getCampaign()->camp->conquerable(g)) - regions.push_back(std::make_shared(g, true, true, campDescriptions[getCampaign()->camp->header.mapVersion])); + regions.push_back(std::make_shared(g, true, true, getCampaign()->camp->header.campaignRegions)); else if(getCampaign()->camp->scenarios[g].conquered) //display as striped - regions.push_back(std::make_shared(g, false, false, campDescriptions[getCampaign()->camp->header.mapVersion])); - } -} - -void CBonusSelection::loadPositionsOfGraphics() -{ - const JsonNode config(ResourceID("config/campaign_regions.json")); - - for(const JsonNode & campaign : config["campaign_regions"].Vector()) - { - SCampPositions sc; - - sc.campPrefix = campaign["prefix"].String(); - sc.colorSuffixLength = static_cast(campaign["color_suffix_length"].Float()); - - for(const JsonNode & desc : campaign["desc"].Vector()) - { - SCampPositions::SRegionDesc rd; - - rd.infix = desc["infix"].String(); - rd.xpos = static_cast(desc["x"].Float()); - rd.ypos = static_cast(desc["y"].Float()); - sc.regions.push_back(rd); - } - - campDescriptions.push_back(sc); - + regions.push_back(std::make_shared(g, false, false, getCampaign()->camp->header.campaignRegions)); } } @@ -470,7 +444,7 @@ void CBonusSelection::decreaseDifficulty() CSH->setDifficulty(CSH->si->difficulty - 1); } -CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, const SCampPositions & campDsc) +CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, const CampaignRegions & campDsc) : CIntObject(LCLICK | RCLICK), idOfMapAndRegion(id), accessible(accessible), selectable(selectable) { OBJ_CONSTRUCTION; @@ -480,7 +454,7 @@ CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, cons {"Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi"} }; - const SCampPositions::SRegionDesc & desc = campDsc.regions[idOfMapAndRegion]; + const CampaignRegions::RegionDescription & desc = campDsc.regions[idOfMapAndRegion]; pos.x += desc.xpos; pos.y += desc.ypos; diff --git a/client/lobby/CBonusSelection.h b/client/lobby/CBonusSelection.h index ecf322758..55b77cd9e 100644 --- a/client/lobby/CBonusSelection.h +++ b/client/lobby/CBonusSelection.h @@ -8,6 +8,7 @@ * */ #pragma once +#include "../../lib/mapping/CCampaignHandler.h" #include "../windows/CWindowObject.h" VCMI_LIB_NAMESPACE_BEGIN @@ -32,21 +33,6 @@ public: std::shared_ptr getCampaign(); CBonusSelection(); - struct SCampPositions - { - std::string campPrefix; - int colorSuffixLength; - - struct SRegionDesc - { - std::string infix; - int xpos, ypos; - }; - - std::vector regions; - - }; - class CRegion : public CIntObject { @@ -57,13 +43,12 @@ public: bool accessible; // false if region should be striped bool selectable; // true if region should be selectable public: - CRegion(int id, bool accessible, bool selectable, const SCampPositions & campDsc); + CRegion(int id, bool accessible, bool selectable, const CampaignRegions & campDsc); void updateState(); void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; }; - void loadPositionsOfGraphics(); void createBonusesIcons(); void updateAfterStateChange(); @@ -84,7 +69,6 @@ public: std::shared_ptr mapName; std::shared_ptr labelMapDescription; std::shared_ptr mapDescription; - std::vector campDescriptions; std::vector> regions; std::shared_ptr flagbox; diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index a9ecde719..16ab9a175 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -100,8 +100,8 @@ bool mapSorter::operator()(const std::shared_ptr aaa, const std::share switch(sortBy) { case _numOfMaps: //by number of maps in campaign - return CGI->generaltexth->getCampaignLength(aaa->campaignHeader->mapVersion) < - CGI->generaltexth->getCampaignLength(bbb->campaignHeader->mapVersion); + return aaa->campaignHeader->numberOfScenarios < + bbb->campaignHeader->numberOfScenarios; break; case _name: //by name return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name); @@ -665,7 +665,7 @@ void SelectionTab::ListItem::updateItem(std::shared_ptr info, bool sel iconLossCondition->disable(); labelNumberOfCampaignMaps->enable(); std::ostringstream ostr(std::ostringstream::out); - ostr << CGI->generaltexth->getCampaignLength(info->campaignHeader->mapVersion); + ostr << info->campaignHeader->numberOfScenarios; labelNumberOfCampaignMaps->setText(ostr.str()); labelNumberOfCampaignMaps->setColor(color); } diff --git a/lib/mapping/CCampaignHandler.cpp b/lib/mapping/CCampaignHandler.cpp index 3253a0d00..a9064f285 100644 --- a/lib/mapping/CCampaignHandler.cpp +++ b/lib/mapping/CCampaignHandler.cpp @@ -36,12 +36,53 @@ VCMI_LIB_NAMESPACE_BEGIN +CampaignRegions::RegionDescription CampaignRegions::RegionDescription::fromJson(const JsonNode & node) +{ + CampaignRegions::RegionDescription rd; + rd.infix = node["infix"].String(); + rd.xpos = static_cast(node["x"].Float()); + rd.ypos = static_cast(node["y"].Float()); + return rd; +} + +CampaignRegions CampaignRegions::fromJson(const JsonNode & node) +{ + CampaignRegions cr; + cr.campPrefix = node["prefix"].String(); + cr.colorSuffixLength = static_cast(node["color_suffix_length"].Float()); + + for(const JsonNode & desc : node["desc"].Vector()) + cr.regions.push_back(CampaignRegions::RegionDescription::fromJson(desc)); + + return cr; +} + +CampaignRegions CampaignRegions::getLegacy(int campId) +{ + static std::vector campDescriptions; + if(campDescriptions.empty()) //read once + { + const JsonNode config(ResourceID("config/campaign_regions.json")); + for(const JsonNode & campaign : config["campaign_regions"].Vector()) + campDescriptions.push_back(CampaignRegions::fromJson(campaign)); + } + + return campDescriptions.at(campId); +} + + bool CScenarioTravel::STravelBonus::isBonusForHero() const { return type == SPELL || type == MONSTER || type == ARTIFACT || type == SPELL_SCROLL || type == PRIMARY_SKILL || type == SECONDARY_SKILL; } +void CCampaignHeader::loadLegacyData(ui8 campId) +{ + campaignRegions = CampaignRegions::getLegacy(campId); + numberOfScenarios = VLC->generaltexth->getCampaignLength(campId); +} + CCampaignHeader CCampaignHandler::getHeader( const std::string & name) { ResourceID resourceID(name, EResType::CAMPAIGN); @@ -86,10 +127,10 @@ std::unique_ptr CCampaignHandler::getCampaign( const std::string & na CBinaryReader reader(&stream); ret->header = readHeaderFromMemory(reader, resourceID.getName(), modName, encoding); - int howManyScenarios = static_cast(VLC->generaltexth->getCampaignLength(ret->header.mapVersion)); - for(int g=0; gheader.numberOfScenarios; + for(int g = 0; g < howManyScenarios; ++g) { - CCampaignScenario sc = readScenarioFromMemory(reader, resourceID.getName(), modName, encoding, ret->header.version, ret->header.mapVersion); + CCampaignScenario sc = readScenarioFromMemory(reader, ret->header); ret->scenarios.push_back(sc); } @@ -126,7 +167,7 @@ std::unique_ptr CCampaignHandler::getCampaign( const std::string & na for(auto & scenario : jsonCampaign["scenarios"].Vector()) { - CCampaignScenario sc = readScenarioFromJson(scenario, resourceID.getName(), modName, encoding, ret->header.version, ret->header.mapVersion); + CCampaignScenario sc = readScenarioFromJson(scenario); if(sc.isNotVoid()) { CMapService mapService; @@ -180,7 +221,7 @@ std::string CCampaignHandler::readLocalizedString(CBinaryReader & reader, std::s return VLC->generaltexth->translate(stringID.get()); } -CCampaignHeader CCampaignHandler::readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding ) +CCampaignHeader CCampaignHandler::readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding) { CCampaignHeader ret; @@ -192,7 +233,8 @@ CCampaignHeader CCampaignHandler::readHeaderFromJson(JsonNode & reader, std::str } ret.version = CampaignVersion::VCMI; - ret.mapVersion = reader["campaignId"].Integer(); + ret.campaignRegions = CampaignRegions::fromJson(reader["regions"]); + ret.numberOfScenarios = reader["scenarios"].Vector().size(); ret.name = reader["name"].String(); ret.description = reader["description"].String(); ret.difficultyChoosenByPlayer = reader["allowDifficultySelection"].Bool(); @@ -204,7 +246,7 @@ CCampaignHeader CCampaignHandler::readHeaderFromJson(JsonNode & reader, std::str return ret; } -CCampaignScenario CCampaignHandler::readScenarioFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding, int version, int mapVersion) +CCampaignScenario CCampaignHandler::readScenarioFromJson(JsonNode & reader) { auto prologEpilogReader = [](JsonNode & identifier) -> CCampaignScenario::SScenarioPrologEpilog { @@ -231,12 +273,12 @@ CCampaignScenario CCampaignHandler::readScenarioFromJson(JsonNode & reader, std: ret.prolog = prologEpilogReader(reader["prolog"]); ret.epilog = prologEpilogReader(reader["epilog"]); - ret.travelOptions = readScenarioTravelFromJson(reader, version); + ret.travelOptions = readScenarioTravelFromJson(reader); return ret; } -CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader, int version ) +CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader) { CScenarioTravel ret; @@ -442,7 +484,8 @@ CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader, CCampaignHeader ret; ret.version = reader.readUInt32(); - ret.mapVersion = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19] + ui8 campId = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19] + ret.loadLegacyData(campId); ret.name = readLocalizedString(reader, filename, modName, encoding, "name"); ret.description = readLocalizedString(reader, filename, modName, encoding, "description"); if (ret.version > CampaignVersion::RoE) @@ -457,7 +500,7 @@ CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader, return ret; } -CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, int version, int mapVersion ) +CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CCampaignHeader & header) { auto prologEpilogReader = [&](const std::string & identifier) -> CCampaignScenario::SScenarioPrologEpilog { @@ -467,7 +510,7 @@ CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & read { ret.prologVideo = reader.readUInt8(); ret.prologMusic = reader.readUInt8(); - ret.prologText = readLocalizedString(reader, filename, modName, encoding, identifier); + ret.prologText = readLocalizedString(reader, header.filename, header.modName, header.encoding, identifier); } return ret; }; @@ -476,7 +519,7 @@ CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & read ret.conquered = false; ret.mapName = reader.readBaseString(); ret.packedMapSize = reader.readUInt32(); - if(mapVersion == 18)//unholy alliance + if(header.numberOfScenarios > 0) //unholy alliance { ret.loadPreconditionRegions(reader.readUInt16()); } @@ -486,11 +529,11 @@ CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & read } ret.regionColor = reader.readUInt8(); ret.difficulty = reader.readUInt8(); - ret.regionText = readLocalizedString(reader, filename, modName, encoding, ret.mapName + ".region"); + ret.regionText = readLocalizedString(reader, header.filename, header.modName, header.encoding, ret.mapName + ".region"); ret.prolog = prologEpilogReader(ret.mapName + ".prolog"); ret.epilog = prologEpilogReader(ret.mapName + ".epilog"); - ret.travelOptions = readScenarioTravelFromMemory(reader, version); + ret.travelOptions = readScenarioTravelFromMemory(reader, header.version); return ret; } diff --git a/lib/mapping/CCampaignHandler.h b/lib/mapping/CCampaignHandler.h index 2afeacdc4..6d093122a 100644 --- a/lib/mapping/CCampaignHandler.h +++ b/lib/mapping/CCampaignHandler.h @@ -37,11 +37,45 @@ namespace CampaignVersion const int VCMI_MAX = 1; } +struct DLL_LINKAGE CampaignRegions +{ + std::string campPrefix; + int colorSuffixLength; + + struct DLL_LINKAGE RegionDescription + { + std::string infix; + int xpos, ypos; + + template void serialize(Handler &h, const int formatVersion) + { + h & infix; + h & xpos; + h & ypos; + } + + static CampaignRegions::RegionDescription fromJson(const JsonNode & node); + }; + + std::vector regions; + + template void serialize(Handler &h, const int formatVersion) + { + h & campPrefix; + h & colorSuffixLength; + h & regions; + } + + static CampaignRegions fromJson(const JsonNode & node); + static CampaignRegions getLegacy(int campId); +}; + class DLL_LINKAGE CCampaignHeader { public: si32 version = 0; //4 - RoE, 5 - AB, 6 - SoD and WoG - ui8 mapVersion = 0; //CampText.txt's format + CampaignRegions campaignRegions; + int numberOfScenarios = 0; std::string name, description; ui8 difficultyChoosenByPlayer = 0; ui8 music = 0; //CmpMusic.txt, start from 0, field is unused in vcmi @@ -50,15 +84,28 @@ public: std::string filename; std::string modName; std::string encoding; + + void loadLegacyData(ui8 campId); template void serialize(Handler &h, const int formatVersion) { h & version; - h & mapVersion; + if(!h.saving && formatVersion < 821) + { + ui8 campId = 0; //legacy field + h & campId; + loadLegacyData(campId); + } + else + { + h & campaignRegions; + h & numberOfScenarios; + } h & name; h & description; h & difficultyChoosenByPlayer; - h & music; + if(formatVersion < 821) + h & music; //deprecated h & filename; h & modName; h & encoding; @@ -223,18 +270,16 @@ public: class DLL_LINKAGE CCampaignHandler { - std::vector scenariosCountPerCampaign; - static std::string readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier); //parsers for VCMI campaigns (*.vcmp) static CCampaignHeader readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding); - static CCampaignScenario readScenarioFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding, int version, int mapVersion ); - static CScenarioTravel readScenarioTravelFromJson(JsonNode & reader, int version); + static CCampaignScenario readScenarioFromJson(JsonNode & reader); + static CScenarioTravel readScenarioTravelFromJson(JsonNode & reader); //parsers for original H3C campaigns static CCampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding); - static CCampaignScenario readScenarioFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, int version, int mapVersion ); + static CCampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CCampaignHeader & header); static CScenarioTravel readScenarioTravelFromMemory(CBinaryReader & reader, int version); /// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m) /// headerOnly - only header will be decompressed, returned vector wont have any maps