1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +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;
std::string bgName = getCampaign()->getHeader().campaignRegions.campPrefix + "_BG.BMP";
std::string bgName = getCampaign()->getRegions().campPrefix + "_BG.BMP";
setBackground(bgName);
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);
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());
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);
}
if(getCampaign()->getHeader().difficultyChoosenByPlayer)
if(getCampaign()->playerSelectedDifficulty())
{
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));
@ -108,9 +108,9 @@ CBonusSelection::CBonusSelection()
for(auto scenarioID : getCampaign()->allScenarios())
{
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
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);
BuildingID buildID;
if(getCampaign()->getHeader().version == CampaignVersion::VCMI)
if(getCampaign()->formatVCMI())
buildID = BuildingID(bonDescs[i].info1);
else
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)
{
case _numOfMaps: //by number of maps in campaign
return aaa->campaignHeader->numberOfScenarios <
bbb->campaignHeader->numberOfScenarios;
break;
return aaa->campaign->scenariosCount() < bbb->campaign->scenariosCount();
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:
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));
info->fileURI = file.getName();
info->campaignInit();
if(info->campaignHeader)
if(info->campaign)
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;
if(info->campaignHeader)
if(info->campaign)
{
labelAmountOfPlayers->disable();
labelMapSizeLetter->disable();
@ -686,7 +684,7 @@ void SelectionTab::ListItem::updateItem(std::shared_ptr<CMapInfo> info, bool sel
iconLossCondition->disable();
labelNumberOfCampaignMaps->enable();
std::ostringstream ostr(std::ostringstream::out);
ostr << info->campaignHeader->numberOfScenarios;
ostr << info->campaign->scenariosCount();
labelNumberOfCampaignMaps->setText(ostr.str());
labelNumberOfCampaignMaps->setColor(color);
}

View File

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

View File

@ -923,7 +923,7 @@ void CCastleBuildings::enterMagesGuild()
const StartInfo *si = LOCPLINT->cb->getStartInfo();
// 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,
(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)
{
// "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
{
if(campState->getHeader().name.empty())
return campState->getHeader().name;
if(campState->getName().empty())
return campState->getName();
else
return VLC->generaltexth->allTexts[508];
}

View File

@ -27,26 +27,49 @@
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);
std::string modName = VLC->modh->findResourceOrigin(resourceID);
std::string language = VLC->modh->getModLanguage(modName);
std::string encoding = Languages::getLanguageOptions(language).encoding;
auto ret = std::make_unique<Campaign>();
auto fileStream = CResourceHandler::get(modName)->load(resourceID);
std::vector<ui8> cmpgn = getFile(std::move(fileStream), true)[0];
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)
return std::make_unique<CampaignHeader>(readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding));
readCampaign(ret.get(), cmpgn, resourceID.getName(), modName, encoding);
return ret;
}
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);
if (files[0].front() < uint8_t(' ')) // binary format
{
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);
}
}
readCampaign(ret.get(), files[0], resourceID.getName(), modName, encoding);
//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);
@ -140,15 +141,13 @@ std::string CampaignHandler::readLocalizedString(CBinaryReader & reader, std::st
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());
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));
return ret;
return;
}
ret.version = CampaignVersion::VCMI;
@ -161,7 +160,6 @@ CampaignHeader CampaignHandler::readHeaderFromJson(JsonNode & reader, std::strin
ret.filename = filename;
ret.modName = modName;
ret.encoding = encoding;
return ret;
}
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());
ui8 campId = reader.readUInt8() - 1;//change range of it from [1, 20] to [0, 19]
ret.loadLegacyData(campId);
@ -400,7 +396,6 @@ CampaignHeader CampaignHandler::readHeaderFromMemory( CBinaryReader & reader, st
ret.filename = filename;
ret.modName = modName;
ret.encoding = encoding;
return ret;
}
CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CampaignHeader & header)

View File

@ -16,14 +16,16 @@ VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE CampaignHandler
{
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)
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 CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
//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 CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version);
/// 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);
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
};

View File

@ -83,6 +83,36 @@ void CampaignHeader::loadLegacyData(ui8 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
{
return vstd::contains(mapsConquered, whichScenario);
@ -91,7 +121,7 @@ bool CampaignState::isConquered(CampaignScenarioID whichScenario) const
bool CampaignState::isAvailable(CampaignScenarioID whichScenario) const
{
//check for void scenraio
if (!scenarios.at(whichScenario).isNotVoid())
if (!scenario(whichScenario).isNotVoid())
{
return false;
}
@ -101,7 +131,7 @@ bool CampaignState::isAvailable(CampaignScenarioID whichScenario) const
return false;
}
//check preconditioned regions
for (auto const & it : scenarios.at(whichScenario).preconditionRegions)
for (auto const & it : scenario(whichScenario).preconditionRegions)
{
if (!vstd::contains(mapsConquered, it))
return false;
@ -174,23 +204,21 @@ void CampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *>
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);
if(bonuses.empty())
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
{
return scenarios.at(*currentMap);
}
std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID & which) const
std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID which) const
{
if (!chosenCampaignBonuses.count(which))
return std::nullopt;
@ -198,11 +226,6 @@ std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID & which) const
return chosenCampaignBonuses.at(which);
}
ui8 CampaignState::currentBonusID() const
{
return chosenCampaignBonuses.at(*currentMap);
}
std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
{
// 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();
CMapService mapService;
std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
std::string scenarioName = filename.substr(0, filename.find('.'));
boost::to_lower(scenarioName);
scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
const std::string & mapContent = mapPieces.find(scenarioId)->second;
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
@ -224,12 +247,12 @@ std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scena
scenarioId = currentMap.value();
CMapService mapService;
std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
std::string scenarioName = filename.substr(0, filename.find('.'));
boost::to_lower(scenarioName);
scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
const std::string & mapContent = mapPieces.find(scenarioId)->second;
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
@ -238,7 +261,7 @@ std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioI
scenarioId = currentMap.value();
auto mapInfo = std::make_shared<CMapInfo>();
mapInfo->fileURI = header.filename;
mapInfo->fileURI = filename;
mapInfo->mapHeader = getMapHeader(scenarioId);
mapInfo->countPlayers();
return mapInfo;
@ -263,8 +286,7 @@ CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node)
void CampaignState::setCurrentMap(CampaignScenarioID which)
{
assert(scenarios.count(which));
assert(scenarios.at(which).isNotVoid());
assert(scenario(which).isNotVoid());
currentMap = which;
}
@ -293,7 +315,7 @@ std::set<CampaignScenarioID> CampaignState::conqueredScenarios() const
return result;
}
std::set<CampaignScenarioID> CampaignState::allScenarios() const
std::set<CampaignScenarioID> Campaign::allScenarios() const
{
std::set<CampaignScenarioID> result;
@ -306,7 +328,12 @@ std::set<CampaignScenarioID> CampaignState::allScenarios() const
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.at(which).isNotVoid());
@ -318,8 +345,3 @@ bool CampaignState::isCampaignFinished() const
{
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);
};
class DLL_LINKAGE CampaignHeader
class DLL_LINKAGE CampaignHeader : public boost::noncopyable
{
public:
friend class CampaignHandler;
int numberOfScenarios = 0;
CampaignVersion version = CampaignVersion::NONE;
CampaignRegions campaignRegions;
@ -69,10 +70,21 @@ public:
void loadLegacyData(ui8 campId);
protected:
std::string filename;
std::string modName;
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)
{
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;
/// List of all maps completed by player, in order of their completion
std::vector<CampaignScenarioID> mapsConquered;
std::map<CampaignScenarioID, CampaignScenario> scenarios;
std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data
std::map<CampaignScenarioID, ui8> chosenCampaignBonuses;
std::optional<CampaignScenarioID> currentMap;
CampaignHeader header;
CampaignHeroes crossover;
public:
std::optional<CampaignScenarioID> lastScenario() const;
std::optional<CampaignScenarioID> currentScenario() const;
std::set<CampaignScenarioID> allScenarios() const;
std::set<CampaignScenarioID> conqueredScenarios() const;
const CampaignScenario & scenario(CampaignScenarioID which) const;
std::optional<CampaignBonus> getBonus(CampaignScenarioID which) const;
std::optional<CampaignBonus> getBonusForCurrentMap() const;
const CampaignScenario & getCurrentScenario() const;
std::optional<ui8> getBonusID(CampaignScenarioID & which) const;
ui8 currentBonusID() const;
std::optional<ui8> getBonusID(CampaignScenarioID which) const;
/// Returns true if selected scenario can be selected and started by player
bool isAvailable(CampaignScenarioID whichScenario) const;
@ -233,8 +259,6 @@ public:
/// Returns true if all available scenarios have been completed and campaign is finished
bool isCampaignFinished() const;
const CampaignHeader & getHeader() const;
std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const;
std::unique_ptr<CMapHeader> getMapHeader(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)
{
h & header;
h & scenarios;
h & static_cast<Campaign&>(*this);
h & crossover;
h & mapPieces;
h & mapsConquered;

View File

@ -54,12 +54,19 @@ CGameStateCampaign::CGameStateCampaign(CGameState * owner):
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 crossoverHeroes;
auto campaignState = gameState->scenarioOps->campState;
auto bonus = campaignState->getBonusForCurrentMap();
auto bonus = currentBonus();
if(bonus && bonus->type == CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO)
{
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
// 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;
if(campaignGiveHero)
@ -281,7 +289,7 @@ void CGameStateCampaign::placeCampaignHeroes()
auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
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
// 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)
{
const std::optional<CampaignBonus> & curBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
auto curBonus = currentBonus();
if(!curBonus)
return;
@ -513,7 +521,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
void CGameStateCampaign::initHeroes()
{
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
auto chosenBonus = currentBonus();
if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes
{
//find human player
@ -573,7 +581,7 @@ void CGameStateCampaign::initStartingResources()
return ret;
};
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
auto chosenBonus = currentBonus();
if(chosenBonus && chosenBonus->type == CampaignBonusType::RESOURCE)
{
std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
@ -610,7 +618,7 @@ void CGameStateCampaign::initStartingResources()
void CGameStateCampaign::initTowns()
{
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
auto chosenBonus = currentBonus();
if (chosenBonus && chosenBonus->type == CampaignBonusType::BUILDING)
{
@ -625,7 +633,7 @@ void CGameStateCampaign::initTowns()
gameState->map->towns[g]->pos == pi.posOfMainTown)
{
BuildingID buildingId;
if(gameState->scenarioOps->campState->getHeader().version == CampaignVersion::VCMI)
if(gameState->scenarioOps->campState->formatVCMI())
buildingId = BuildingID(chosenBonus->info1);
else
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
{
auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
auto campaignBonus = currentBonus();
if (!campaignBonus)
return false;

View File

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

View File

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

View File

@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct StartInfo;
class CMapHeader;
class CampaignHeader;
class Campaign;
class ResourceID;
/**
@ -25,7 +25,7 @@ class DLL_LINKAGE CMapInfo
{
public:
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)
std::string fileURI;
std::string date;
@ -58,7 +58,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int Version)
{
h & mapHeader;
h & campaignHeader;
h & campaign;
h & scenarioOptionsOfSave;
h & fileURI;
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)
{
if(mi->campaignHeader)
if(mi->campaign)
return;
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)
{
srv.si->mapname = pack.ourCampaign->getHeader().filename;
srv.si->mapname = pack.ourCampaign->getFilename();
srv.si->mode = StartInfo::CAMPAIGN;
srv.si->campState = pack.ourCampaign;
srv.si->turnTime = 0;