1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-17 01:32:21 +02:00

Use inheritance instead of composition for campaign header

This commit is contained in:
Ivan Savenko
2023-06-26 01:07:55 +03:00
parent a08fe09517
commit f6b2f58da9
15 changed files with 183 additions and 132 deletions

View File

@ -64,7 +64,7 @@ CBonusSelection::CBonusSelection()
{ {
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
std::string bgName = getCampaign()->getHeader().campaignRegions.campPrefix + "_BG.BMP"; std::string bgName = getCampaign()->getRegions().campPrefix + "_BG.BMP";
setBackground(bgName); setBackground(bgName);
panelBackground = std::make_shared<CPicture>("CAMPBRF.BMP", 456, 6); panelBackground = std::make_shared<CPicture>("CAMPBRF.BMP", 456, 6);
@ -78,7 +78,7 @@ CBonusSelection::CBonusSelection()
iconsMapSizes = std::make_shared<CAnimImage>("SCNRMPSZ", 4, 0, 735, 26); iconsMapSizes = std::make_shared<CAnimImage>("SCNRMPSZ", 4, 0, 735, 26);
labelCampaignDescription = std::make_shared<CLabel>(481, 63, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]); labelCampaignDescription = std::make_shared<CLabel>(481, 63, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]);
campaignDescription = std::make_shared<CTextBox>(getCampaign()->getHeader().description, Rect(480, 86, 286, 117), 1); campaignDescription = std::make_shared<CTextBox>(getCampaign()->getDescription(), Rect(480, 86, 286, 117), 1);
mapName = std::make_shared<CLabel>(481, 219, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->mi->getName()); mapName = std::make_shared<CLabel>(481, 219, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->mi->getName());
labelMapDescription = std::make_shared<CLabel>(481, 253, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]); labelMapDescription = std::make_shared<CLabel>(481, 253, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]);
@ -99,7 +99,7 @@ CBonusSelection::CBonusSelection()
difficultyIcons[b] = std::make_shared<CAnimImage>("GSPBUT" + std::to_string(b + 3) + ".DEF", 0, 0, 709, 455); difficultyIcons[b] = std::make_shared<CAnimImage>("GSPBUT" + std::to_string(b + 3) + ".DEF", 0, 0, 709, 455);
} }
if(getCampaign()->getHeader().difficultyChoosenByPlayer) if(getCampaign()->playerSelectedDifficulty())
{ {
buttonDifficultyLeft = std::make_shared<CButton>(Point(694, 508), "SCNRBLF.DEF", CButton::tooltip(), std::bind(&CBonusSelection::decreaseDifficulty, this)); buttonDifficultyLeft = std::make_shared<CButton>(Point(694, 508), "SCNRBLF.DEF", CButton::tooltip(), std::bind(&CBonusSelection::decreaseDifficulty, this));
buttonDifficultyRight = std::make_shared<CButton>(Point(738, 508), "SCNRBRT.DEF", CButton::tooltip(), std::bind(&CBonusSelection::increaseDifficulty, this)); buttonDifficultyRight = std::make_shared<CButton>(Point(738, 508), "SCNRBRT.DEF", CButton::tooltip(), std::bind(&CBonusSelection::increaseDifficulty, this));
@ -108,9 +108,9 @@ CBonusSelection::CBonusSelection()
for(auto scenarioID : getCampaign()->allScenarios()) for(auto scenarioID : getCampaign()->allScenarios())
{ {
if(getCampaign()->isAvailable(scenarioID)) if(getCampaign()->isAvailable(scenarioID))
regions.push_back(std::make_shared<CRegion>(scenarioID, true, true, getCampaign()->getHeader().campaignRegions)); regions.push_back(std::make_shared<CRegion>(scenarioID, true, true, getCampaign()->getRegions()));
else if(getCampaign()->isConquered(scenarioID)) //display as striped else if(getCampaign()->isConquered(scenarioID)) //display as striped
regions.push_back(std::make_shared<CRegion>(scenarioID, false, false, getCampaign()->getHeader().campaignRegions)); regions.push_back(std::make_shared<CRegion>(scenarioID, false, false, getCampaign()->getRegions()));
} }
} }
@ -170,7 +170,7 @@ void CBonusSelection::createBonusesIcons()
assert(faction != -1); assert(faction != -1);
BuildingID buildID; BuildingID buildID;
if(getCampaign()->getHeader().version == CampaignVersion::VCMI) if(getCampaign()->formatVCMI())
buildID = BuildingID(bonDescs[i].info1); buildID = BuildingID(bonDescs[i].info1);
else else
buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>()); buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>());

View File

@ -104,13 +104,11 @@ bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::share
switch(sortBy) switch(sortBy)
{ {
case _numOfMaps: //by number of maps in campaign case _numOfMaps: //by number of maps in campaign
return aaa->campaignHeader->numberOfScenarios < return aaa->campaign->scenariosCount() < bbb->campaign->scenariosCount();
bbb->campaignHeader->numberOfScenarios;
break;
case _name: //by name case _name: //by name
return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name); return boost::ilexicographical_compare(aaa->campaign->getName(), bbb->campaign->getName());
default: default:
return boost::ilexicographical_compare(aaa->campaignHeader->name, bbb->campaignHeader->name); return boost::ilexicographical_compare(aaa->campaign->getName(), bbb->campaign->getName());
} }
} }
} }
@ -623,7 +621,7 @@ void SelectionTab::parseCampaigns(const std::unordered_set<ResourceID> & files)
//allItems[i].date = std::asctime(std::localtime(&files[i].date)); //allItems[i].date = std::asctime(std::localtime(&files[i].date));
info->fileURI = file.getName(); info->fileURI = file.getName();
info->campaignInit(); info->campaignInit();
if(info->campaignHeader) if(info->campaign)
allItems.push_back(info); allItems.push_back(info);
} }
} }
@ -677,7 +675,7 @@ void SelectionTab::ListItem::updateItem(std::shared_ptr<CMapInfo> info, bool sel
} }
auto color = selected ? Colors::YELLOW : Colors::WHITE; auto color = selected ? Colors::YELLOW : Colors::WHITE;
if(info->campaignHeader) if(info->campaign)
{ {
labelAmountOfPlayers->disable(); labelAmountOfPlayers->disable();
labelMapSizeLetter->disable(); labelMapSizeLetter->disable();
@ -686,7 +684,7 @@ void SelectionTab::ListItem::updateItem(std::shared_ptr<CMapInfo> info, bool sel
iconLossCondition->disable(); iconLossCondition->disable();
labelNumberOfCampaignMaps->enable(); labelNumberOfCampaignMaps->enable();
std::ostringstream ostr(std::ostringstream::out); std::ostringstream ostr(std::ostringstream::out);
ostr << info->campaignHeader->numberOfScenarios; ostr << info->campaign->scenariosCount();
labelNumberOfCampaignMaps->setText(ostr.str()); labelNumberOfCampaignMaps->setText(ostr.str());
labelNumberOfCampaignMaps->setColor(color); labelNumberOfCampaignMaps->setColor(color);
} }

View File

@ -97,7 +97,7 @@ CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config)
status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED; status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;
auto header = CampaignHandler::getHeader(campFile); auto header = CampaignHandler::getHeader(campFile);
hoverText = header->name; hoverText = header->getName();
if(status != CCampaignScreen::DISABLED) if(status != CCampaignScreen::DISABLED)
{ {

View File

@ -923,7 +923,7 @@ void CCastleBuildings::enterMagesGuild()
const StartInfo *si = LOCPLINT->cb->getStartInfo(); const StartInfo *si = LOCPLINT->cb->getStartInfo();
// it would be nice to find a way to move this hack to config/mapOverrides.json // it would be nice to find a way to move this hack to config/mapOverrides.json
if(si && si->campState && si->campState && // We're in campaign, if(si && si->campState && si->campState && // We're in campaign,
(si->campState->getHeader().filename == "DATA/YOG.H3C") && // which is "Birth of a Barbarian", (si->campState->getFilename() == "DATA/YOG.H3C") && // which is "Birth of a Barbarian",
(hero->subID == 45)) // and the hero is Yog (based on Solmyr) (hero->subID == 45)) // and the hero is Yog (based on Solmyr)
{ {
// "Yog has given up magic in all its forms..." // "Yog has given up magic in all its forms..."

View File

@ -62,8 +62,8 @@ PlayerSettings * StartInfo::getPlayersSettings(const ui8 connectedPlayerId)
std::string StartInfo::getCampaignName() const std::string StartInfo::getCampaignName() const
{ {
if(campState->getHeader().name.empty()) if(campState->getName().empty())
return campState->getHeader().name; return campState->getName();
else else
return VLC->generaltexth->allTexts[508]; return VLC->generaltexth->allTexts[508];
} }

View File

@ -27,26 +27,49 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
std::unique_ptr<CampaignHeader> CampaignHandler::getHeader( const std::string & name) void CampaignHandler::readCampaign(Campaign * ret, const std::vector<ui8> & input, std::string filename, std::string modName, std::string encoding)
{
if (input.front() < uint8_t(' ')) // binary format
{
CMemoryStream stream(input.data(), input.size());
CBinaryReader reader(&stream);
readHeaderFromMemory(*ret, reader, filename, modName, encoding);
for(int g = 0; g < ret->numberOfScenarios; ++g)
{
auto scenarioID = static_cast<CampaignScenarioID>(ret->scenarios.size());
ret->scenarios[scenarioID] = readScenarioFromMemory(reader, *ret);
}
}
else // text format (json)
{
JsonNode jsonCampaign((const char*)input.data(), input.size());
readHeaderFromJson(*ret, jsonCampaign, filename, modName, encoding);
for(auto & scenario : jsonCampaign["scenarios"].Vector())
{
auto scenarioID = static_cast<CampaignScenarioID>(ret->scenarios.size());
ret->scenarios[scenarioID] = readScenarioFromJson(scenario);
}
}
}
std::unique_ptr<Campaign> CampaignHandler::getHeader( const std::string & name)
{ {
ResourceID resourceID(name, EResType::CAMPAIGN); ResourceID resourceID(name, EResType::CAMPAIGN);
std::string modName = VLC->modh->findResourceOrigin(resourceID); std::string modName = VLC->modh->findResourceOrigin(resourceID);
std::string language = VLC->modh->getModLanguage(modName); std::string language = VLC->modh->getModLanguage(modName);
std::string encoding = Languages::getLanguageOptions(language).encoding; std::string encoding = Languages::getLanguageOptions(language).encoding;
auto ret = std::make_unique<Campaign>();
auto fileStream = CResourceHandler::get(modName)->load(resourceID); auto fileStream = CResourceHandler::get(modName)->load(resourceID);
std::vector<ui8> cmpgn = getFile(std::move(fileStream), true)[0]; std::vector<ui8> cmpgn = getFile(std::move(fileStream), true)[0];
JsonNode jsonCampaign((const char*)cmpgn.data(), cmpgn.size()); JsonNode jsonCampaign((const char*)cmpgn.data(), cmpgn.size());
if(jsonCampaign.isNull())
{
//legacy OH3 campaign (*.h3c)
CMemoryStream stream(cmpgn.data(), cmpgn.size());
CBinaryReader reader(&stream);
return std::make_unique<CampaignHeader>(readHeaderFromMemory(reader, resourceID.getName(), modName, encoding));
}
//VCMI (*.vcmp) readCampaign(ret.get(), cmpgn, resourceID.getName(), modName, encoding);
return std::make_unique<CampaignHeader>(readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding));
return ret;
} }
std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string & name ) std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string & name )
@ -62,32 +85,10 @@ std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string &
std::vector<std::vector<ui8>> files = getFile(std::move(fileStream), false); std::vector<std::vector<ui8>> files = getFile(std::move(fileStream), false);
if (files[0].front() < uint8_t(' ')) // binary format readCampaign(ret.get(), files[0], resourceID.getName(), modName, encoding);
{
CMemoryStream stream(files[0].data(), files[0].size());
CBinaryReader reader(&stream);
ret->header = readHeaderFromMemory(reader, resourceID.getName(), modName, encoding);
for(int g = 0; g < ret->header.numberOfScenarios; ++g)
{
auto scenarioID = static_cast<CampaignScenarioID>(ret->scenarios.size());
ret->scenarios[scenarioID] = readScenarioFromMemory(reader, ret->header);
}
}
else // text format (json)
{
JsonNode jsonCampaign((const char*)files[0].data(), files[0].size());
ret->header = readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding);
for(auto & scenario : jsonCampaign["scenarios"].Vector())
{
auto scenarioID = static_cast<CampaignScenarioID>(ret->scenarios.size());
ret->scenarios[scenarioID] = readScenarioFromJson(scenario);
}
}
//first entry is campaign header. start loop from 1 //first entry is campaign header. start loop from 1
for(int scenarioID = 0, g = 1; g < files.size() && scenarioID < ret->header.numberOfScenarios; ++g) for(int scenarioID = 0, g = 1; g < files.size() && scenarioID < ret->numberOfScenarios; ++g)
{ {
auto id = static_cast<CampaignScenarioID>(scenarioID); auto id = static_cast<CampaignScenarioID>(scenarioID);
@ -140,15 +141,13 @@ std::string CampaignHandler::readLocalizedString(CBinaryReader & reader, std::st
return VLC->generaltexth->translate(stringID.get()); return VLC->generaltexth->translate(stringID.get());
} }
CampaignHeader CampaignHandler::readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding) void CampaignHandler::readHeaderFromJson(CampaignHeader & ret, JsonNode & reader, std::string filename, std::string modName, std::string encoding)
{ {
CampaignHeader ret;
ret.version = static_cast<CampaignVersion>(reader["version"].Integer()); ret.version = static_cast<CampaignVersion>(reader["version"].Integer());
if(ret.version < CampaignVersion::VCMI_MIN || ret.version > CampaignVersion::VCMI_MAX) if(ret.version < CampaignVersion::VCMI_MIN || ret.version > CampaignVersion::VCMI_MAX)
{ {
logGlobal->info("VCMP Loading: Unsupported campaign %s version %d", filename, static_cast<int>(ret.version)); logGlobal->info("VCMP Loading: Unsupported campaign %s version %d", filename, static_cast<int>(ret.version));
return ret; return;
} }
ret.version = CampaignVersion::VCMI; ret.version = CampaignVersion::VCMI;
@ -161,7 +160,6 @@ CampaignHeader CampaignHandler::readHeaderFromJson(JsonNode & reader, std::strin
ret.filename = filename; ret.filename = filename;
ret.modName = modName; ret.modName = modName;
ret.encoding = encoding; ret.encoding = encoding;
return ret;
} }
CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader) CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
@ -383,10 +381,8 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
} }
CampaignHeader CampaignHandler::readHeaderFromMemory( CBinaryReader & reader, std::string filename, std::string modName, std::string encoding ) void CampaignHandler::readHeaderFromMemory( CampaignHeader & ret, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding )
{ {
CampaignHeader ret;
ret.version = static_cast<CampaignVersion>(reader.readUInt32()); ret.version = static_cast<CampaignVersion>(reader.readUInt32());
ui8 campId = 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.loadLegacyData(campId);
@ -400,7 +396,6 @@ CampaignHeader CampaignHandler::readHeaderFromMemory( CBinaryReader & reader, st
ret.filename = filename; ret.filename = filename;
ret.modName = modName; ret.modName = modName;
ret.encoding = encoding; ret.encoding = encoding;
return ret;
} }
CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CampaignHeader & header) CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CampaignHeader & header)

View File

@ -17,13 +17,15 @@ class DLL_LINKAGE CampaignHandler
{ {
static std::string readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier); static std::string readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier);
static void readCampaign(Campaign * target, const std::vector<ui8> & stream, std::string filename, std::string modName, std::string encoding);
//parsers for VCMI campaigns (*.vcmp) //parsers for VCMI campaigns (*.vcmp)
static CampaignHeader readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding); static void readHeaderFromJson(CampaignHeader & target, JsonNode & reader, std::string filename, std::string modName, std::string encoding);
static CampaignScenario readScenarioFromJson(JsonNode & reader); static CampaignScenario readScenarioFromJson(JsonNode & reader);
static CampaignTravel readScenarioTravelFromJson(JsonNode & reader); static CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
//parsers for original H3C campaigns //parsers for original H3C campaigns
static CampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding); static void readHeaderFromMemory(CampaignHeader & target, CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CampaignHeader & header); static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CampaignHeader & header);
static CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version); static CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version);
/// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m) /// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m)
@ -35,7 +37,7 @@ class DLL_LINKAGE CampaignHandler
static std::string prologVoiceName(ui8 index); static std::string prologVoiceName(ui8 index);
public: public:
static std::unique_ptr<CampaignHeader> getHeader( const std::string & name); //name - name of appropriate file static std::unique_ptr<Campaign> getHeader( const std::string & name); //name - name of appropriate file
static std::shared_ptr<CampaignState> getCampaign(const std::string & name); //name - name of appropriate file static std::shared_ptr<CampaignState> getCampaign(const std::string & name); //name - name of appropriate file
}; };

View File

@ -83,6 +83,36 @@ void CampaignHeader::loadLegacyData(ui8 campId)
numberOfScenarios = VLC->generaltexth->getCampaignLength(campId); numberOfScenarios = VLC->generaltexth->getCampaignLength(campId);
} }
bool CampaignHeader::playerSelectedDifficulty() const
{
return difficultyChoosenByPlayer;
}
bool CampaignHeader::formatVCMI() const
{
return version == CampaignVersion::VCMI;
}
std::string CampaignHeader::getDescription() const
{
return description;
}
std::string CampaignHeader::getName() const
{
return name;
}
std::string CampaignHeader::getFilename() const
{
return filename;
}
const CampaignRegions & CampaignHeader::getRegions() const
{
return campaignRegions;
}
bool CampaignState::isConquered(CampaignScenarioID whichScenario) const bool CampaignState::isConquered(CampaignScenarioID whichScenario) const
{ {
return vstd::contains(mapsConquered, whichScenario); return vstd::contains(mapsConquered, whichScenario);
@ -91,7 +121,7 @@ bool CampaignState::isConquered(CampaignScenarioID whichScenario) const
bool CampaignState::isAvailable(CampaignScenarioID whichScenario) const bool CampaignState::isAvailable(CampaignScenarioID whichScenario) const
{ {
//check for void scenraio //check for void scenraio
if (!scenarios.at(whichScenario).isNotVoid()) if (!scenario(whichScenario).isNotVoid())
{ {
return false; return false;
} }
@ -101,7 +131,7 @@ bool CampaignState::isAvailable(CampaignScenarioID whichScenario) const
return false; return false;
} }
//check preconditioned regions //check preconditioned regions
for (auto const & it : scenarios.at(whichScenario).preconditionRegions) for (auto const & it : scenario(whichScenario).preconditionRegions)
{ {
if (!vstd::contains(mapsConquered, it)) if (!vstd::contains(mapsConquered, it))
return false; return false;
@ -174,23 +204,21 @@ void CampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *>
mapsConquered.push_back(*currentMap); mapsConquered.push_back(*currentMap);
} }
std::optional<CampaignBonus> CampaignState::getBonusForCurrentMap() const std::optional<CampaignBonus> CampaignState::getBonus(CampaignScenarioID which) const
{ {
auto bonuses = getCurrentScenario().travelOptions.bonusesToChoose; auto bonuses = scenario(which).travelOptions.bonusesToChoose;
assert(chosenCampaignBonuses.count(*currentMap) || bonuses.size() == 0); assert(chosenCampaignBonuses.count(*currentMap) || bonuses.size() == 0);
if(bonuses.empty()) if(bonuses.empty())
return std::optional<CampaignBonus>(); return std::optional<CampaignBonus>();
else
return bonuses[currentBonusID()]; if (!getBonusID(which))
return std::optional<CampaignBonus>();
return bonuses[getBonusID(which).value()];
} }
const CampaignScenario & CampaignState::getCurrentScenario() const std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID which) const
{
return scenarios.at(*currentMap);
}
std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID & which) const
{ {
if (!chosenCampaignBonuses.count(which)) if (!chosenCampaignBonuses.count(which))
return std::nullopt; return std::nullopt;
@ -198,11 +226,6 @@ std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID & which) const
return chosenCampaignBonuses.at(which); return chosenCampaignBonuses.at(which);
} }
ui8 CampaignState::currentBonusID() const
{
return chosenCampaignBonuses.at(*currentMap);
}
std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
{ {
// FIXME: there is certainly better way to handle maps inside campaigns // FIXME: there is certainly better way to handle maps inside campaigns
@ -210,12 +233,12 @@ std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
scenarioId = currentMap.value(); scenarioId = currentMap.value();
CMapService mapService; CMapService mapService;
std::string scenarioName = header.filename.substr(0, header.filename.find('.')); std::string scenarioName = filename.substr(0, filename.find('.'));
boost::to_lower(scenarioName); boost::to_lower(scenarioName);
scenarioName += ':' + std::to_string(static_cast<int>(scenarioId)); scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
const std::string & mapContent = mapPieces.find(scenarioId)->second; const std::string & mapContent = mapPieces.find(scenarioId)->second;
const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data()); const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding); return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, modName, encoding);
} }
std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scenarioId) const std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scenarioId) const
@ -224,12 +247,12 @@ std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scena
scenarioId = currentMap.value(); scenarioId = currentMap.value();
CMapService mapService; CMapService mapService;
std::string scenarioName = header.filename.substr(0, header.filename.find('.')); std::string scenarioName = filename.substr(0, filename.find('.'));
boost::to_lower(scenarioName); boost::to_lower(scenarioName);
scenarioName += ':' + std::to_string(static_cast<int>(scenarioId)); scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
const std::string & mapContent = mapPieces.find(scenarioId)->second; const std::string & mapContent = mapPieces.find(scenarioId)->second;
const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data()); const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding); return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, modName, encoding);
} }
std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioId) const std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioId) const
@ -238,7 +261,7 @@ std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioI
scenarioId = currentMap.value(); scenarioId = currentMap.value();
auto mapInfo = std::make_shared<CMapInfo>(); auto mapInfo = std::make_shared<CMapInfo>();
mapInfo->fileURI = header.filename; mapInfo->fileURI = filename;
mapInfo->mapHeader = getMapHeader(scenarioId); mapInfo->mapHeader = getMapHeader(scenarioId);
mapInfo->countPlayers(); mapInfo->countPlayers();
return mapInfo; return mapInfo;
@ -263,8 +286,7 @@ CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node)
void CampaignState::setCurrentMap(CampaignScenarioID which) void CampaignState::setCurrentMap(CampaignScenarioID which)
{ {
assert(scenarios.count(which)); assert(scenario(which).isNotVoid());
assert(scenarios.at(which).isNotVoid());
currentMap = which; currentMap = which;
} }
@ -293,7 +315,7 @@ std::set<CampaignScenarioID> CampaignState::conqueredScenarios() const
return result; return result;
} }
std::set<CampaignScenarioID> CampaignState::allScenarios() const std::set<CampaignScenarioID> Campaign::allScenarios() const
{ {
std::set<CampaignScenarioID> result; std::set<CampaignScenarioID> result;
@ -306,7 +328,12 @@ std::set<CampaignScenarioID> CampaignState::allScenarios() const
return result; return result;
} }
const CampaignScenario & CampaignState::scenario(CampaignScenarioID which) const int Campaign::scenariosCount() const
{
return allScenarios().size();
}
const CampaignScenario & Campaign::scenario(CampaignScenarioID which) const
{ {
assert(scenarios.count(which)); assert(scenarios.count(which));
assert(scenarios.at(which).isNotVoid()); assert(scenarios.at(which).isNotVoid());
@ -318,8 +345,3 @@ bool CampaignState::isCampaignFinished() const
{ {
return conqueredScenarios() == allScenarios(); return conqueredScenarios() == allScenarios();
} }
const CampaignHeader & CampaignState::getHeader() const
{
return header;
}

View File

@ -57,9 +57,10 @@ struct DLL_LINKAGE CampaignRegions
static CampaignRegions getLegacy(int campId); static CampaignRegions getLegacy(int campId);
}; };
class DLL_LINKAGE CampaignHeader class DLL_LINKAGE CampaignHeader : public boost::noncopyable
{ {
public: friend class CampaignHandler;
int numberOfScenarios = 0; int numberOfScenarios = 0;
CampaignVersion version = CampaignVersion::NONE; CampaignVersion version = CampaignVersion::NONE;
CampaignRegions campaignRegions; CampaignRegions campaignRegions;
@ -69,10 +70,21 @@ public:
void loadLegacyData(ui8 campId); void loadLegacyData(ui8 campId);
protected:
std::string filename; std::string filename;
std::string modName; std::string modName;
std::string encoding; std::string encoding;
public:
bool playerSelectedDifficulty() const;
bool formatVCMI() const;
std::string getDescription() const;
std::string getName() const;
std::string getFilename() const;
const CampaignRegions & getRegions() const;
template <typename Handler> void serialize(Handler &h, const int formatVersion) template <typename Handler> void serialize(Handler &h, const int formatVersion)
{ {
h & version; h & version;
@ -195,34 +207,48 @@ struct DLL_LINKAGE CampaignHeroes
} }
}; };
class DLL_LINKAGE CampaignState /// Class that represents loaded campaign information
class DLL_LINKAGE Campaign : public CampaignHeader
{
friend class CampaignHandler;
std::map<CampaignScenarioID, CampaignScenario> scenarios;
public:
const CampaignScenario & scenario(CampaignScenarioID which) const;
std::set<CampaignScenarioID> allScenarios() const;
int scenariosCount() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CampaignHeader&>(*this);
h & scenarios;
}
};
/// Class that represent campaign that is being played at
/// Contains campaign itself as well as current state of the campaign
class DLL_LINKAGE CampaignState : public Campaign
{ {
friend class CampaignHandler; friend class CampaignHandler;
/// List of all maps completed by player, in order of their completion /// List of all maps completed by player, in order of their completion
std::vector<CampaignScenarioID> mapsConquered; std::vector<CampaignScenarioID> mapsConquered;
std::map<CampaignScenarioID, CampaignScenario> scenarios;
std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data
std::map<CampaignScenarioID, ui8> chosenCampaignBonuses; std::map<CampaignScenarioID, ui8> chosenCampaignBonuses;
std::optional<CampaignScenarioID> currentMap; std::optional<CampaignScenarioID> currentMap;
CampaignHeader header;
CampaignHeroes crossover; CampaignHeroes crossover;
public: public:
std::optional<CampaignScenarioID> lastScenario() const; std::optional<CampaignScenarioID> lastScenario() const;
std::optional<CampaignScenarioID> currentScenario() const; std::optional<CampaignScenarioID> currentScenario() const;
std::set<CampaignScenarioID> allScenarios() const;
std::set<CampaignScenarioID> conqueredScenarios() const; std::set<CampaignScenarioID> conqueredScenarios() const;
const CampaignScenario & scenario(CampaignScenarioID which) const; std::optional<CampaignBonus> getBonus(CampaignScenarioID which) const;
std::optional<CampaignBonus> getBonusForCurrentMap() const; std::optional<ui8> getBonusID(CampaignScenarioID which) const;
const CampaignScenario & getCurrentScenario() const;
std::optional<ui8> getBonusID(CampaignScenarioID & which) const;
ui8 currentBonusID() const;
/// Returns true if selected scenario can be selected and started by player /// Returns true if selected scenario can be selected and started by player
bool isAvailable(CampaignScenarioID whichScenario) const; bool isAvailable(CampaignScenarioID whichScenario) const;
@ -233,8 +259,6 @@ public:
/// Returns true if all available scenarios have been completed and campaign is finished /// Returns true if all available scenarios have been completed and campaign is finished
bool isCampaignFinished() const; bool isCampaignFinished() const;
const CampaignHeader & getHeader() const;
std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const; std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const;
std::unique_ptr<CMapHeader> getMapHeader(CampaignScenarioID scenarioId) const; std::unique_ptr<CMapHeader> getMapHeader(CampaignScenarioID scenarioId) const;
std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const; std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const;
@ -257,8 +281,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & header; h & static_cast<Campaign&>(*this);
h & scenarios;
h & crossover; h & crossover;
h & mapPieces; h & mapPieces;
h & mapsConquered; h & mapsConquered;

View File

@ -54,12 +54,19 @@ CGameStateCampaign::CGameStateCampaign(CGameState * owner):
assert(gameState->scenarioOps->campState != nullptr); assert(gameState->scenarioOps->campState != nullptr);
} }
std::optional<CampaignBonus> CGameStateCampaign::currentBonus() const
{
auto campaignState = gameState->scenarioOps->campState;
return campaignState->getBonus(*campaignState->currentScenario());
}
CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios() const CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios() const
{ {
CrossoverHeroesList crossoverHeroes; CrossoverHeroesList crossoverHeroes;
auto campaignState = gameState->scenarioOps->campState; auto campaignState = gameState->scenarioOps->campState;
auto bonus = campaignState->getBonusForCurrentMap(); auto bonus = currentBonus();
if(bonus && bonus->type == CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO) if(bonus && bonus->type == CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO)
{ {
auto scenarioID = static_cast<CampaignScenarioID>(bonus->info2); auto scenarioID = static_cast<CampaignScenarioID>(bonus->info2);
@ -253,7 +260,8 @@ void CGameStateCampaign::placeCampaignHeroes()
// - exception: if starting bonus is 'select player' then power placeholders are taken from 'scenario pool' of linked map // - exception: if starting bonus is 'select player' then power placeholders are taken from 'scenario pool' of linked map
// place bonus hero // place bonus hero
auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto campaignState = gameState->scenarioOps->campState;
auto campaignBonus = campaignState->getBonus(*campaignState->currentScenario());
bool campaignGiveHero = campaignBonus && campaignBonus->type == CampaignBonusType::HERO; bool campaignGiveHero = campaignBonus && campaignBonus->type == CampaignBonusType::HERO;
if(campaignGiveHero) if(campaignGiveHero)
@ -281,7 +289,7 @@ void CGameStateCampaign::placeCampaignHeroes()
auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes); auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
logGlobal->debug("\tPrepare crossover heroes"); logGlobal->debug("\tPrepare crossover heroes");
trimCrossoverHeroesParameters(campaignHeroReplacements, gameState->scenarioOps->campState->getCurrentScenario().travelOptions); trimCrossoverHeroesParameters(campaignHeroReplacements, campaignState->scenario(*campaignState->currentScenario()).travelOptions);
// remove same heroes on the map which will be added through crossover heroes // remove same heroes on the map which will be added through crossover heroes
// INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes // INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes
@ -338,7 +346,7 @@ void CGameStateCampaign::placeCampaignHeroes()
void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero) void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
{ {
const std::optional<CampaignBonus> & curBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto curBonus = currentBonus();
if(!curBonus) if(!curBonus)
return; return;
@ -513,7 +521,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
void CGameStateCampaign::initHeroes() void CGameStateCampaign::initHeroes()
{ {
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto chosenBonus = currentBonus();
if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes
{ {
//find human player //find human player
@ -573,7 +581,7 @@ void CGameStateCampaign::initStartingResources()
return ret; return ret;
}; };
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto chosenBonus = currentBonus();
if(chosenBonus && chosenBonus->type == CampaignBonusType::RESOURCE) if(chosenBonus && chosenBonus->type == CampaignBonusType::RESOURCE)
{ {
std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
@ -610,7 +618,7 @@ void CGameStateCampaign::initStartingResources()
void CGameStateCampaign::initTowns() void CGameStateCampaign::initTowns()
{ {
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto chosenBonus = currentBonus();
if (chosenBonus && chosenBonus->type == CampaignBonusType::BUILDING) if (chosenBonus && chosenBonus->type == CampaignBonusType::BUILDING)
{ {
@ -625,7 +633,7 @@ void CGameStateCampaign::initTowns()
gameState->map->towns[g]->pos == pi.posOfMainTown) gameState->map->towns[g]->pos == pi.posOfMainTown)
{ {
BuildingID buildingId; BuildingID buildingId;
if(gameState->scenarioOps->campState->getHeader().version == CampaignVersion::VCMI) if(gameState->scenarioOps->campState->formatVCMI())
buildingId = BuildingID(chosenBonus->info1); buildingId = BuildingID(chosenBonus->info1);
else else
buildingId = CBuildingHandler::campToERMU(chosenBonus->info1, gameState->map->towns[g]->subID, gameState->map->towns[g]->builtBuildings); buildingId = CBuildingHandler::campToERMU(chosenBonus->info1, gameState->map->towns[g]->subID, gameState->map->towns[g]->builtBuildings);
@ -640,7 +648,7 @@ void CGameStateCampaign::initTowns()
bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const
{ {
auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto campaignBonus = currentBonus();
if (!campaignBonus) if (!campaignBonus)
return false; return false;

View File

@ -13,6 +13,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct CampaignBonus;
class CampaignTravel; class CampaignTravel;
class CGHeroInstance; class CGHeroInstance;
class CGameState; class CGameState;
@ -42,6 +43,8 @@ class CGameStateCampaign
/// returns heroes and placeholders in where heroes will be put /// returns heroes and placeholders in where heroes will be put
std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes); std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes);
std::optional<CampaignBonus> currentBonus() const;
/// Trims hero parameters that should not transfer between scenarios according to travelOptions flags /// Trims hero parameters that should not transfer between scenarios according to travelOptions flags
void trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions); void trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions);

View File

@ -66,7 +66,7 @@ void CMapInfo::saveInit(const ResourceID & file)
void CMapInfo::campaignInit() void CMapInfo::campaignInit()
{ {
campaignHeader = CampaignHandler::getHeader(fileURI); campaign = CampaignHandler::getHeader(fileURI);
} }
void CMapInfo::countPlayers() void CMapInfo::countPlayers()
@ -92,8 +92,8 @@ void CMapInfo::countPlayers()
std::string CMapInfo::getName() const std::string CMapInfo::getName() const
{ {
if(campaignHeader && !campaignHeader->name.empty()) if(campaign && !campaign->getName().empty())
return campaignHeader->name; return campaign->getName();
else if(mapHeader && mapHeader->name.length()) else if(mapHeader && mapHeader->name.length())
return mapHeader->name; return mapHeader->name;
else else
@ -117,8 +117,8 @@ std::string CMapInfo::getNameForList() const
std::string CMapInfo::getDescription() const std::string CMapInfo::getDescription() const
{ {
if(campaignHeader) if(campaign)
return campaignHeader->description; return campaign->getDescription();
else else
return mapHeader->description; return mapHeader->description;
} }

View File

@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct StartInfo; struct StartInfo;
class CMapHeader; class CMapHeader;
class CampaignHeader; class Campaign;
class ResourceID; class ResourceID;
/** /**
@ -25,7 +25,7 @@ class DLL_LINKAGE CMapInfo
{ {
public: public:
std::unique_ptr<CMapHeader> mapHeader; //may be nullptr if campaign std::unique_ptr<CMapHeader> mapHeader; //may be nullptr if campaign
std::unique_ptr<CampaignHeader> campaignHeader; //may be nullptr if scenario std::unique_ptr<Campaign> campaign; //may be nullptr if scenario
StartInfo * scenarioOptionsOfSave; // Options with which scenario has been started (used only with saved games) StartInfo * scenarioOptionsOfSave; // Options with which scenario has been started (used only with saved games)
std::string fileURI; std::string fileURI;
std::string date; std::string date;
@ -58,7 +58,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int Version) template <typename Handler> void serialize(Handler &h, const int Version)
{ {
h & mapHeader; h & mapHeader;
h & campaignHeader; h & campaign;
h & scenarioOptionsOfSave; h & scenarioOptionsOfSave;
h & fileURI; h & fileURI;
h & date; h & date;

View File

@ -684,7 +684,7 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo,
} }
else if(si->mode == StartInfo::NEW_GAME || si->mode == StartInfo::CAMPAIGN) else if(si->mode == StartInfo::NEW_GAME || si->mode == StartInfo::CAMPAIGN)
{ {
if(mi->campaignHeader) if(mi->campaign)
return; return;
for(int i = 0; i < mi->mapHeader->players.size(); i++) for(int i = 0; i < mi->mapHeader->players.size(); i++)

View File

@ -211,7 +211,7 @@ void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack) void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack)
{ {
srv.si->mapname = pack.ourCampaign->getHeader().filename; srv.si->mapname = pack.ourCampaign->getFilename();
srv.si->mode = StartInfo::CAMPAIGN; srv.si->mode = StartInfo::CAMPAIGN;
srv.si->campState = pack.ourCampaign; srv.si->campState = pack.ourCampaign;
srv.si->turnTime = 0; srv.si->turnTime = 0;