1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

Refactoring of campaign handler: rename types and use strong typing

This commit is contained in:
Ivan Savenko
2023-06-25 21:16:03 +03:00
parent 453d441562
commit d1e5a347ff
29 changed files with 419 additions and 397 deletions

View File

@@ -442,7 +442,7 @@ void CServerHandler::sendClientDisconnecting()
sendLobbyPack(lcd); sendLobbyPack(lcd);
} }
void CServerHandler::setCampaignState(std::shared_ptr<CCampaignState> newCampaign) void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
{ {
state = EClientState::LOBBY_CAMPAIGN; state = EClientState::LOBBY_CAMPAIGN;
LobbySetCampaign lsc; LobbySetCampaign lsc;
@@ -450,7 +450,7 @@ void CServerHandler::setCampaignState(std::shared_ptr<CCampaignState> newCampaig
sendLobbyPack(lsc); sendLobbyPack(lsc);
} }
void CServerHandler::setCampaignMap(int mapId) const void CServerHandler::setCampaignMap(CampaignScenarioID mapId) const
{ {
if(state == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place if(state == EClientState::GAMEPLAY) // FIXME: UI shouldn't sent commands in first place
return; return;
@@ -660,7 +660,7 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
saveSession->Bool() = false; saveSession->Bool() = false;
} }
void CServerHandler::startCampaignScenario(std::shared_ptr<CCampaignState> cs) void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
{ {
if(cs) if(cs)
GH.pushUserEvent(EUserEvent::CAMPAIGN_START_SCENARIO, CMemorySerializer::deepCopy(*cs.get()).release()); GH.pushUserEvent(EUserEvent::CAMPAIGN_START_SCENARIO, CMemorySerializer::deepCopy(*cs.get()).release());

View File

@@ -57,8 +57,8 @@ public:
virtual void sendClientConnecting() const = 0; virtual void sendClientConnecting() const = 0;
virtual void sendClientDisconnecting() = 0; virtual void sendClientDisconnecting() = 0;
virtual void setCampaignState(std::shared_ptr<CCampaignState> newCampaign) = 0; virtual void setCampaignState(std::shared_ptr<CampaignState> newCampaign) = 0;
virtual void setCampaignMap(int mapId) const = 0; virtual void setCampaignMap(CampaignScenarioID mapId) const = 0;
virtual void setCampaignBonus(int bonusId) const = 0; virtual void setCampaignBonus(int bonusId) const = 0;
virtual void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const = 0; virtual void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const = 0;
virtual void setPlayer(PlayerColor color) const = 0; virtual void setPlayer(PlayerColor color) const = 0;
@@ -92,7 +92,7 @@ public:
// FIXME: Bunch of crutches to glue it all together // FIXME: Bunch of crutches to glue it all together
// For starting non-custom campaign and continue to next mission // For starting non-custom campaign and continue to next mission
std::shared_ptr<CCampaignState> campaignStateToSend; std::shared_ptr<CampaignState> campaignStateToSend;
ui8 screenType; // To create lobby UI only after server is setup ui8 screenType; // To create lobby UI only after server is setup
ui8 loadMode; // For saves filtering in SelectionTab ui8 loadMode; // For saves filtering in SelectionTab
@@ -135,8 +135,8 @@ public:
// Lobby server API for UI // Lobby server API for UI
void sendClientConnecting() const override; void sendClientConnecting() const override;
void sendClientDisconnecting() override; void sendClientDisconnecting() override;
void setCampaignState(std::shared_ptr<CCampaignState> newCampaign) override; void setCampaignState(std::shared_ptr<CampaignState> newCampaign) override;
void setCampaignMap(int mapId) const override; void setCampaignMap(CampaignScenarioID mapId) const override;
void setCampaignBonus(int bonusId) const override; void setCampaignBonus(int bonusId) const override;
void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const override; void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const override;
void setPlayer(PlayerColor color) const override; void setPlayer(PlayerColor color) const override;
@@ -150,7 +150,7 @@ public:
void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr); void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
void endGameplay(bool closeConnection = true, bool restart = false); void endGameplay(bool closeConnection = true, bool restart = false);
void startCampaignScenario(std::shared_ptr<CCampaignState> cs = {}); void startCampaignScenario(std::shared_ptr<CampaignState> cs = {});
void showServerError(std::string txt); void showServerError(std::string txt);
// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle // TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle

View File

@@ -23,7 +23,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct CPack; struct CPack;
struct CPackForServer; struct CPackForServer;
class CCampaignState; class CampaignState;
class IGameEventsReceiver; class IGameEventsReceiver;
class IBattleEventsReceiver; class IBattleEventsReceiver;
class CBattleGameInterface; class CBattleGameInterface;

View File

@@ -209,9 +209,9 @@ void ClientCommandManager::handleConvertTextCommand()
logGlobal->info("Loading campaigns for export"); logGlobal->info("Loading campaigns for export");
for (auto const & campaignName : campaignList) for (auto const & campaignName : campaignList)
{ {
CCampaignState state(CCampaignHandler::getCampaign(campaignName.getName())); auto state = CampaignHandler::getCampaign(campaignName.getName());
for (auto const & part : state.camp->mapPieces) for (auto const & part : state->mapPieces)
delete state.getMap(part.first); state->getMap(part.first);
} }
VLC->generaltexth->dumpAllTexts(); VLC->generaltexth->dumpAllTexts();

View File

@@ -48,8 +48,8 @@ void UserEventHandler::handleUserEvent(const SDL_UserEvent & user)
{ {
CSH->campaignServerRestartLock.set(true); CSH->campaignServerRestartLock.set(true);
CSH->endGameplay(); CSH->endGameplay();
auto ourCampaign = std::shared_ptr<CCampaignState>(reinterpret_cast<CCampaignState *>(user.data1)); auto ourCampaign = std::shared_ptr<CampaignState>(reinterpret_cast<CampaignState *>(user.data1));
auto & epilogue = ourCampaign->camp->scenarios[ourCampaign->mapsConquered.back()].epilog; auto & epilogue = ourCampaign->scenarios[ourCampaign->mapsConquered.back()].epilog;
auto finisher = [=]() auto finisher = [=]()
{ {
if(!ourCampaign->mapsRemaining.empty()) if(!ourCampaign->mapsRemaining.empty())

View File

@@ -54,7 +54,7 @@
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
std::shared_ptr<CCampaignState> CBonusSelection::getCampaign() std::shared_ptr<CampaignState> CBonusSelection::getCampaign()
{ {
return CSH->si->campState; return CSH->si->campState;
} }
@@ -64,7 +64,7 @@ CBonusSelection::CBonusSelection()
{ {
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
std::string bgName = getCampaign()->camp->header.campaignRegions.campPrefix + "_BG.BMP"; std::string bgName = getCampaign()->header.campaignRegions.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()->camp->header.description, Rect(480, 86, 286, 117), 1); campaignDescription = std::make_shared<CTextBox>(getCampaign()->header.description, 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,26 +99,28 @@ 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()->camp->header.difficultyChoosenByPlayer) if(getCampaign()->header.difficultyChoosenByPlayer)
{ {
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));
} }
for(int g = 0; g < getCampaign()->camp->scenarios.size(); ++g) for(int g = 0; g < getCampaign()->scenarios.size(); ++g)
{ {
if(getCampaign()->camp->conquerable(g)) auto scenarioID = static_cast<CampaignScenarioID>(g);
regions.push_back(std::make_shared<CRegion>(g, true, true, getCampaign()->camp->header.campaignRegions));
else if(getCampaign()->camp->scenarios[g].conquered) //display as striped if(getCampaign()->conquerable(scenarioID))
regions.push_back(std::make_shared<CRegion>(g, false, false, getCampaign()->camp->header.campaignRegions)); regions.push_back(std::make_shared<CRegion>(scenarioID, true, true, getCampaign()->header.campaignRegions));
else if(getCampaign()->scenarios[scenarioID].conquered) //display as striped
regions.push_back(std::make_shared<CRegion>(scenarioID, false, false, getCampaign()->header.campaignRegions));
} }
} }
void CBonusSelection::createBonusesIcons() void CBonusSelection::createBonusesIcons()
{ {
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
const CCampaignScenario & scenario = getCampaign()->camp->scenarios[CSH->campaignMap]; const CampaignScenario & scenario = getCampaign()->scenarios[CSH->campaignMap];
const std::vector<CScenarioTravel::STravelBonus> & bonDescs = scenario.travelOptions.bonusesToChoose; const std::vector<CampaignBonus> & bonDescs = scenario.travelOptions.bonusesToChoose;
groupBonuses = std::make_shared<CToggleGroup>(std::bind(&IServerAPI::setCampaignBonus, CSH, _1)); groupBonuses = std::make_shared<CToggleGroup>(std::bind(&IServerAPI::setCampaignBonus, CSH, _1));
static const char * bonusPics[] = static const char * bonusPics[] =
@@ -138,23 +140,24 @@ void CBonusSelection::createBonusesIcons()
for(int i = 0; i < bonDescs.size(); i++) for(int i = 0; i < bonDescs.size(); i++)
{ {
std::string picName = bonusPics[bonDescs[i].type]; int bonusType = static_cast<size_t>(bonDescs[i].type);
std::string picName = bonusPics[bonusType];
size_t picNumber = bonDescs[i].info2; size_t picNumber = bonDescs[i].info2;
std::string desc; std::string desc;
switch(bonDescs[i].type) switch(bonDescs[i].type)
{ {
case CScenarioTravel::STravelBonus::SPELL: case CampaignBonusType::SPELL:
desc = CGI->generaltexth->allTexts[715]; desc = CGI->generaltexth->allTexts[715];
boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getNameTranslated()); boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getNameTranslated());
break; break;
case CScenarioTravel::STravelBonus::MONSTER: case CampaignBonusType::MONSTER:
picNumber = bonDescs[i].info2 + 2; picNumber = bonDescs[i].info2 + 2;
desc = CGI->generaltexth->allTexts[717]; desc = CGI->generaltexth->allTexts[717];
boost::algorithm::replace_first(desc, "%d", std::to_string(bonDescs[i].info3)); boost::algorithm::replace_first(desc, "%d", std::to_string(bonDescs[i].info3));
boost::algorithm::replace_first(desc, "%s", CGI->creatures()->getByIndex(bonDescs[i].info2)->getNamePluralTranslated()); boost::algorithm::replace_first(desc, "%s", CGI->creatures()->getByIndex(bonDescs[i].info2)->getNamePluralTranslated());
break; break;
case CScenarioTravel::STravelBonus::BUILDING: case CampaignBonusType::BUILDING:
{ {
int faction = -1; int faction = -1;
for(auto & elem : CSH->si->playerInfos) for(auto & elem : CSH->si->playerInfos)
@@ -169,7 +172,7 @@ void CBonusSelection::createBonusesIcons()
assert(faction != -1); assert(faction != -1);
BuildingID buildID; BuildingID buildID;
if(getCampaign()->camp->header.version == CampaignVersion::VCMI) if(getCampaign()->header.version == CampaignVersion::VCMI)
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>());
@@ -181,15 +184,15 @@ void CBonusSelection::createBonusesIcons()
break; break;
} }
case CScenarioTravel::STravelBonus::ARTIFACT: case CampaignBonusType::ARTIFACT:
desc = CGI->generaltexth->allTexts[715]; desc = CGI->generaltexth->allTexts[715];
boost::algorithm::replace_first(desc, "%s", CGI->artifacts()->getByIndex(bonDescs[i].info2)->getNameTranslated()); boost::algorithm::replace_first(desc, "%s", CGI->artifacts()->getByIndex(bonDescs[i].info2)->getNameTranslated());
break; break;
case CScenarioTravel::STravelBonus::SPELL_SCROLL: case CampaignBonusType::SPELL_SCROLL:
desc = CGI->generaltexth->allTexts[716]; desc = CGI->generaltexth->allTexts[716];
boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getNameTranslated()); boost::algorithm::replace_first(desc, "%s", CGI->spells()->getByIndex(bonDescs[i].info2)->getNameTranslated());
break; break;
case CScenarioTravel::STravelBonus::PRIMARY_SKILL: case CampaignBonusType::PRIMARY_SKILL:
{ {
int leadingSkill = -1; int leadingSkill = -1;
std::vector<std::pair<int, int>> toPrint; //primary skills to be listed <num, val> std::vector<std::pair<int, int>> toPrint; //primary skills to be listed <num, val>
@@ -222,7 +225,7 @@ void CBonusSelection::createBonusesIcons()
boost::algorithm::replace_first(desc, "%s", substitute); boost::algorithm::replace_first(desc, "%s", substitute);
break; break;
} }
case CScenarioTravel::STravelBonus::SECONDARY_SKILL: case CampaignBonusType::SECONDARY_SKILL:
desc = CGI->generaltexth->allTexts[718]; desc = CGI->generaltexth->allTexts[718];
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->levels[bonDescs[i].info3 - 1]); //skill level boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->levels[bonDescs[i].info3 - 1]); //skill level
@@ -230,7 +233,7 @@ void CBonusSelection::createBonusesIcons()
picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1; picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
break; break;
case CScenarioTravel::STravelBonus::RESOURCE: case CampaignBonusType::RESOURCE:
{ {
int serialResID = 0; int serialResID = 0;
switch(bonDescs[i].info1) switch(bonDescs[i].info1)
@@ -267,19 +270,19 @@ void CBonusSelection::createBonusesIcons()
boost::algorithm::replace_first(desc, "%s", replacement); boost::algorithm::replace_first(desc, "%s", replacement);
break; break;
} }
case CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO: case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:
{ {
auto superhero = getCampaign()->camp->scenarios[bonDescs[i].info2].strongestHero(PlayerColor(bonDescs[i].info1)); auto superhero = getCampaign()->scenarios[static_cast<CampaignScenarioID>(bonDescs[i].info2)].strongestHero(PlayerColor(bonDescs[i].info1));
if(!superhero) if(!superhero)
logGlobal->warn("No superhero! How could it be transferred?"); logGlobal->warn("No superhero! How could it be transferred?");
picNumber = superhero ? superhero->portrait : 0; picNumber = superhero ? superhero->portrait : 0;
desc = CGI->generaltexth->allTexts[719]; desc = CGI->generaltexth->allTexts[719];
boost::algorithm::replace_first(desc, "%s", getCampaign()->camp->scenarios[bonDescs[i].info2].scenarioName); boost::algorithm::replace_first(desc, "%s", getCampaign()->scenarios[static_cast<CampaignScenarioID>(bonDescs[i].info2)].scenarioName);
break; break;
} }
case CScenarioTravel::STravelBonus::HERO: case CampaignBonusType::HERO:
desc = CGI->generaltexth->allTexts[718]; desc = CGI->generaltexth->allTexts[718];
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->capColors[bonDescs[i].info1]); //hero's color boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->capColors[bonDescs[i].info1]); //hero's color
@@ -339,7 +342,7 @@ void CBonusSelection::updateAfterStateChange()
} }
if(CSH->campaignBonus == -1) if(CSH->campaignBonus == -1)
{ {
buttonStart->block(getCampaign()->camp->scenarios[CSH->campaignMap].travelOptions.bonusesToChoose.size()); buttonStart->block(getCampaign()->scenarios[CSH->campaignMap].travelOptions.bonusesToChoose.size());
} }
else if(buttonStart->isBlocked()) else if(buttonStart->isBlocked())
{ {
@@ -391,11 +394,11 @@ void CBonusSelection::startMap()
{ {
auto exitCb = [=]() auto exitCb = [=]()
{ {
logGlobal->info("Starting scenario %d", CSH->campaignMap); logGlobal->info("Starting scenario %d", static_cast<int>(CSH->campaignMap));
CSH->sendStartGame(); CSH->sendStartGame();
}; };
const CCampaignScenario & scenario = getCampaign()->camp->scenarios[CSH->campaignMap]; const CampaignScenario & scenario = getCampaign()->scenarios[CSH->campaignMap];
if(scenario.prolog.hasPrologEpilog) if(scenario.prolog.hasPrologEpilog)
{ {
GH.windows().createAndPushWindow<CPrologEpilogVideo>(scenario.prolog, exitCb); GH.windows().createAndPushWindow<CPrologEpilogVideo>(scenario.prolog, exitCb);
@@ -446,7 +449,7 @@ void CBonusSelection::decreaseDifficulty()
CSH->setDifficulty(CSH->si->difficulty - 1); CSH->setDifficulty(CSH->si->difficulty - 1);
} }
CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, const CampaignRegions & campDsc) CBonusSelection::CRegion::CRegion(CampaignScenarioID id, bool accessible, bool selectable, const CampaignRegions & campDsc)
: CIntObject(LCLICK | SHOW_POPUP), idOfMapAndRegion(id), accessible(accessible), selectable(selectable) : CIntObject(LCLICK | SHOW_POPUP), idOfMapAndRegion(id), accessible(accessible), selectable(selectable)
{ {
OBJ_CONSTRUCTION; OBJ_CONSTRUCTION;
@@ -456,12 +459,12 @@ CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, cons
{"Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi"} {"Re", "Bl", "Br", "Gr", "Or", "Vi", "Te", "Pi"}
}; };
const CampaignRegions::RegionDescription & desc = campDsc.regions[idOfMapAndRegion]; const CampaignRegions::RegionDescription & desc = campDsc.regions[static_cast<int>(idOfMapAndRegion)];
pos.x += desc.xpos; pos.x += desc.xpos;
pos.y += desc.ypos; pos.y += desc.ypos;
std::string prefix = campDsc.campPrefix + desc.infix + "_"; std::string prefix = campDsc.campPrefix + desc.infix + "_";
std::string suffix = colors[campDsc.colorSuffixLength - 1][CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionColor]; std::string suffix = colors[campDsc.colorSuffixLength - 1][CSH->si->campState->scenarios[idOfMapAndRegion].regionColor];
graphicsNotSelected = std::make_shared<CPicture>(prefix + "En" + suffix + ".BMP"); graphicsNotSelected = std::make_shared<CPicture>(prefix + "En" + suffix + ".BMP");
graphicsNotSelected->disable(); graphicsNotSelected->disable();
graphicsSelected = std::make_shared<CPicture>(prefix + "Se" + suffix + ".BMP"); graphicsSelected = std::make_shared<CPicture>(prefix + "Se" + suffix + ".BMP");
@@ -510,7 +513,7 @@ void CBonusSelection::CRegion::clickLeft(tribool down, bool previousState)
void CBonusSelection::CRegion::showPopupWindow() void CBonusSelection::CRegion::showPopupWindow()
{ {
// FIXME: For some reason "down" is only ever contain indeterminate_value // FIXME: For some reason "down" is only ever contain indeterminate_value
auto text = CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionText; auto text = CSH->si->campState->scenarios[idOfMapAndRegion].regionText;
if(!graphicsNotSelected->getSurface()->isTransparent(GH.getCursorPosition() - pos.topLeft()) && text.size()) if(!graphicsNotSelected->getSurface()->isTransparent(GH.getCursorPosition() - pos.topLeft()) && text.size())
{ {
CRClickPopup::createAndPush(text); CRClickPopup::createAndPush(text);

View File

@@ -10,10 +10,11 @@
#pragma once #pragma once
#include "../windows/CWindowObject.h" #include "../windows/CWindowObject.h"
#include "../lib/mapping/CCampaignHandler.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CCampaignState; class CampaignState;
struct CampaignRegions; struct CampaignRegions;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
@@ -30,7 +31,7 @@ class ISelectionScreenInfo;
class CBonusSelection : public CWindowObject class CBonusSelection : public CWindowObject
{ {
public: public:
std::shared_ptr<CCampaignState> getCampaign(); std::shared_ptr<CampaignState> getCampaign();
CBonusSelection(); CBonusSelection();
class CRegion class CRegion
@@ -39,11 +40,11 @@ public:
std::shared_ptr<CPicture> graphicsNotSelected; std::shared_ptr<CPicture> graphicsNotSelected;
std::shared_ptr<CPicture> graphicsSelected; std::shared_ptr<CPicture> graphicsSelected;
std::shared_ptr<CPicture> graphicsStriped; std::shared_ptr<CPicture> graphicsStriped;
int idOfMapAndRegion; CampaignScenarioID idOfMapAndRegion;
bool accessible; // false if region should be striped bool accessible; // false if region should be striped
bool selectable; // true if region should be selectable bool selectable; // true if region should be selectable
public: public:
CRegion(int id, bool accessible, bool selectable, const CampaignRegions & campDsc); CRegion(CampaignScenarioID id, bool accessible, bool selectable, const CampaignRegions & campDsc);
void updateState(); void updateState();
void clickLeft(tribool down, bool previousState) override; void clickLeft(tribool down, bool previousState) override;
void showPopupWindow() override; void showPopupWindow() override;

View File

@@ -119,7 +119,7 @@ void CLobbyScreen::startCampaign()
{ {
if(CSH->mi) if(CSH->mi)
{ {
auto ourCampaign = std::make_shared<CCampaignState>(CCampaignHandler::getCampaign(CSH->mi->fileURI)); auto ourCampaign = CampaignHandler::getCampaign(CSH->mi->fileURI);
CSH->setCampaignState(ourCampaign); CSH->setCampaignState(ourCampaign);
} }
} }

View File

@@ -96,7 +96,7 @@ CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config)
status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED; status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;
CCampaignHeader header = CCampaignHandler::getHeader(campFile); CampaignHeader header = CampaignHandler::getHeader(campFile);
hoverText = header.name; hoverText = header.name;
if(status != CCampaignScreen::DISABLED) if(status != CCampaignScreen::DISABLED)

View File

@@ -354,11 +354,11 @@ void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vec
void CMainMenu::openCampaignLobby(const std::string & campaignFileName) void CMainMenu::openCampaignLobby(const std::string & campaignFileName)
{ {
auto ourCampaign = std::make_shared<CCampaignState>(CCampaignHandler::getCampaign(campaignFileName)); auto ourCampaign = CampaignHandler::getCampaign(campaignFileName);
openCampaignLobby(ourCampaign); openCampaignLobby(ourCampaign);
} }
void CMainMenu::openCampaignLobby(std::shared_ptr<CCampaignState> campaign) void CMainMenu::openCampaignLobby(std::shared_ptr<CampaignState> campaign)
{ {
CSH->resetStateForLobby(StartInfo::CAMPAIGN); CSH->resetStateForLobby(StartInfo::CAMPAIGN);
CSH->screenType = ESelectionScreen::campaignList; CSH->screenType = ESelectionScreen::campaignList;

View File

@@ -14,7 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CCampaignState; class CampaignState;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
@@ -147,7 +147,7 @@ public:
void update() override; void update() override;
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode); static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode);
static void openCampaignLobby(const std::string & campaignFileName); static void openCampaignLobby(const std::string & campaignFileName);
static void openCampaignLobby(std::shared_ptr<CCampaignState> campaign); static void openCampaignLobby(std::shared_ptr<CampaignState> campaign);
void openCampaignScreen(std::string name); void openCampaignScreen(std::string name);
static std::shared_ptr<CMainMenu> create(); static std::shared_ptr<CMainMenu> create();

View File

@@ -21,7 +21,7 @@
#include "../../lib/mapping/CCampaignHandler.h" #include "../../lib/mapping/CCampaignHandler.h"
CPrologEpilogVideo::CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback) CPrologEpilogVideo::CPrologEpilogVideo(CampaignScenarioPrologEpilog _spe, std::function<void()> callback)
: CWindowObject(BORDERED), spe(_spe), positionCounter(0), voiceSoundHandle(-1), exitCb(callback) : CWindowObject(BORDERED), spe(_spe), positionCounter(0), voiceSoundHandle(-1), exitCb(callback)
{ {
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;

View File

@@ -15,7 +15,7 @@ class CMultiLineLabel;
class CPrologEpilogVideo : public CWindowObject class CPrologEpilogVideo : public CWindowObject
{ {
CCampaignScenario::SScenarioPrologEpilog spe; CampaignScenarioPrologEpilog spe;
int positionCounter; int positionCounter;
int voiceSoundHandle; int voiceSoundHandle;
std::function<void()> exitCb; std::function<void()> exitCb;
@@ -23,7 +23,7 @@ class CPrologEpilogVideo : public CWindowObject
std::shared_ptr<CMultiLineLabel> text; std::shared_ptr<CMultiLineLabel> text;
public: public:
CPrologEpilogVideo(CCampaignScenario::SScenarioPrologEpilog _spe, std::function<void()> callback); CPrologEpilogVideo(CampaignScenarioPrologEpilog _spe, std::function<void()> callback);
void clickLeft(tribool down, bool previousState) override; void clickLeft(tribool down, bool previousState) override;
void show(Canvas & to) override; void show(Canvas & to) override;

View File

@@ -922,8 +922,8 @@ 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->camp && // We're in campaign, if(si && si->campState && si->campState && // We're in campaign,
(si->campState->camp->header.filename == "DATA/YOG.H3C") && // which is "Birth of a Barbarian", (si->campState->header.filename == "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

@@ -18,7 +18,7 @@ class CVCMIServer;
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CCampaignState; class CampaignState;
class CMapInfo; class CMapInfo;
struct StartInfo; struct StartInfo;
class CMapGenOptions; class CMapGenOptions;
@@ -175,7 +175,7 @@ struct DLL_LINKAGE LobbySetMap : public CLobbyPackToServer
struct DLL_LINKAGE LobbySetCampaign : public CLobbyPackToServer struct DLL_LINKAGE LobbySetCampaign : public CLobbyPackToServer
{ {
std::shared_ptr<CCampaignState> ourCampaign; std::shared_ptr<CampaignState> ourCampaign;
virtual void visitTyped(ICPackVisitor & visitor) override; virtual void visitTyped(ICPackVisitor & visitor) override;
@@ -187,7 +187,7 @@ struct DLL_LINKAGE LobbySetCampaign : public CLobbyPackToServer
struct DLL_LINKAGE LobbySetCampaignMap : public CLobbyPackToServer struct DLL_LINKAGE LobbySetCampaignMap : public CLobbyPackToServer
{ {
int mapId = -1; CampaignScenarioID mapId = CampaignScenarioID::NONE;
virtual void visitTyped(ICPackVisitor & visitor) override; virtual void visitTyped(ICPackVisitor & visitor) override;

View File

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

View File

@@ -10,11 +10,12 @@
#pragma once #pragma once
#include "GameConstants.h" #include "GameConstants.h"
#include "../lib/mapping/CCampaignHandler.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CMapGenOptions; class CMapGenOptions;
class CCampaignState; class CampaignState;
class CMapInfo; class CMapInfo;
struct PlayerInfo; struct PlayerInfo;
class PlayerColor; class PlayerColor;
@@ -87,7 +88,7 @@ struct DLL_LINKAGE StartInfo
bool createRandomMap() const { return mapGenOptions != nullptr; } bool createRandomMap() const { return mapGenOptions != nullptr; }
std::shared_ptr<CMapGenOptions> mapGenOptions; std::shared_ptr<CMapGenOptions> mapGenOptions;
std::shared_ptr<CCampaignState> campState; std::shared_ptr<CampaignState> campState;
PlayerSettings & getIthPlayersSettings(const PlayerColor & no); PlayerSettings & getIthPlayersSettings(const PlayerColor & no);
const PlayerSettings & getIthPlayersSettings(const PlayerColor & no) const; const PlayerSettings & getIthPlayersSettings(const PlayerColor & no) const;
@@ -138,10 +139,10 @@ struct DLL_LINKAGE LobbyState
int hostClientId; int hostClientId;
// TODO: Campaign-only and we don't really need either of them. // TODO: Campaign-only and we don't really need either of them.
// Before start both go into CCampaignState that is part of StartInfo // Before start both go into CCampaignState that is part of StartInfo
int campaignMap; CampaignScenarioID campaignMap;
int campaignBonus; int campaignBonus;
LobbyState() : si(new StartInfo()), hostClientId(-1), campaignMap(-1), campaignBonus(-1) {} LobbyState() : si(new StartInfo()), hostClientId(-1), campaignMap(CampaignScenarioID::NONE), campaignBonus(-1) {}
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@@ -684,7 +684,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
void CGameState::initCampaign() void CGameState::initCampaign()
{ {
campaign = std::make_unique<CGameStateCampaign>(this); campaign = std::make_unique<CGameStateCampaign>(this);
map = campaign->getCurrentMap(); map = campaign->getCurrentMap().release();
} }
void CGameState::checkMapChecksum() void CGameState::checkMapChecksum()

View File

@@ -26,7 +26,7 @@ class CMap;
struct CPack; struct CPack;
class CHeroClass; class CHeroClass;
struct EventCondition; struct EventCondition;
class CScenarioTravel; class CampaignTravel;
class CStackInstance; class CStackInstance;
class CGameStateCampaign; class CGameStateCampaign;

View File

@@ -60,12 +60,14 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios(
auto campaignState = gameState->scenarioOps->campState; auto campaignState = gameState->scenarioOps->campState;
auto bonus = campaignState->getBonusForCurrentMap(); auto bonus = campaignState->getBonusForCurrentMap();
if(bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO) if(bonus && bonus->type == CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO)
{ {
auto scenarioID = static_cast<CampaignScenarioID>(bonus->info2);
std::vector<CGHeroInstance *> heroes; std::vector<CGHeroInstance *> heroes;
for(auto & node : campaignState->camp->scenarios[bonus->info2].crossoverHeroes) for(auto & node : campaignState->scenarios.at(scenarioID).crossoverHeroes)
{ {
auto * h = CCampaignState::crossoverDeserialize(node); auto * h = CampaignState::crossoverDeserialize(node);
heroes.push_back(h); heroes.push_back(h);
} }
crossoverHeroes.heroesFromAnyPreviousScenarios = heroes; crossoverHeroes.heroesFromAnyPreviousScenarios = heroes;
@@ -80,7 +82,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios(
for(auto mapNr : campaignState->mapsConquered) for(auto mapNr : campaignState->mapsConquered)
{ {
// create a list of deleted heroes // create a list of deleted heroes
auto & scenario = campaignState->camp->scenarios[mapNr]; auto & scenario = campaignState->scenarios[mapNr];
auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes(); auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes();
// remove heroes which didn't reached the end of the scenario, but were available at the start // remove heroes which didn't reached the end of the scenario, but were available at the start
@@ -96,7 +98,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios(
// now add heroes which completed the scenario // now add heroes which completed the scenario
for(auto node : scenario.crossoverHeroes) for(auto node : scenario.crossoverHeroes)
{ {
auto * hero = CCampaignState::crossoverDeserialize(node); auto * hero = CampaignState::crossoverDeserialize(node);
// add new heroes and replace old heroes with newer ones // add new heroes and replace old heroes with newer ones
auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h) auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
{ {
@@ -123,7 +125,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios(
return crossoverHeroes; return crossoverHeroes;
} }
void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions) void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions)
{ {
// create heroes list for convenience iterating // create heroes list for convenience iterating
std::vector<CGHeroInstance *> crossoverHeroes; std::vector<CGHeroInstance *> crossoverHeroes;
@@ -253,7 +255,7 @@ void CGameStateCampaign::placeCampaignHeroes()
// place bonus hero // place bonus hero
auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
bool campaignGiveHero = campaignBonus && campaignBonus->type == CScenarioTravel::STravelBonus::HERO; bool campaignGiveHero = campaignBonus && campaignBonus->type == CampaignBonusType::HERO;
if(campaignGiveHero) if(campaignGiveHero)
{ {
@@ -337,7 +339,7 @@ void CGameStateCampaign::placeCampaignHeroes()
void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero) void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
{ {
const std::optional<CScenarioTravel::STravelBonus> & curBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); const std::optional<CampaignBonus> & curBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
if(!curBonus) if(!curBonus)
return; return;
@@ -346,12 +348,12 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
//apply bonus //apply bonus
switch(curBonus->type) switch(curBonus->type)
{ {
case CScenarioTravel::STravelBonus::SPELL: case CampaignBonusType::SPELL:
{ {
hero->addSpellToSpellbook(SpellID(curBonus->info2)); hero->addSpellToSpellbook(SpellID(curBonus->info2));
break; break;
} }
case CScenarioTravel::STravelBonus::MONSTER: case CampaignBonusType::MONSTER:
{ {
for(int i = 0; i < GameConstants::ARMY_SIZE; i++) for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
{ {
@@ -363,13 +365,13 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
} }
break; break;
} }
case CScenarioTravel::STravelBonus::ARTIFACT: case CampaignBonusType::ARTIFACT:
{ {
if(!gameState->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2))) if(!gameState->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2)))
logGlobal->error("Cannot give starting artifact - no free slots!"); logGlobal->error("Cannot give starting artifact - no free slots!");
break; break;
} }
case CScenarioTravel::STravelBonus::SPELL_SCROLL: case CampaignBonusType::SPELL_SCROLL:
{ {
CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2)); CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2));
const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId()); const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId());
@@ -379,7 +381,7 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
logGlobal->error("Cannot give starting scroll - no free slots!"); logGlobal->error("Cannot give starting scroll - no free slots!");
break; break;
} }
case CScenarioTravel::STravelBonus::PRIMARY_SKILL: case CampaignBonusType::PRIMARY_SKILL:
{ {
const ui8 * ptr = reinterpret_cast<const ui8 *>(&curBonus->info2); const ui8 * ptr = reinterpret_cast<const ui8 *>(&curBonus->info2);
for(int g = 0; g < GameConstants::PRIMARY_SKILLS; ++g) for(int g = 0; g < GameConstants::PRIMARY_SKILLS; ++g)
@@ -390,13 +392,13 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
continue; continue;
} }
auto bb = std::make_shared<Bonus>( auto bb = std::make_shared<Bonus>(
BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, *gameState->scenarioOps->campState->currentMap, g BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, static_cast<int>(*gameState->scenarioOps->campState->currentMap), g
); );
hero->addNewBonus(bb); hero->addNewBonus(bb);
} }
break; break;
} }
case CScenarioTravel::STravelBonus::SECONDARY_SKILL: case CampaignBonusType::SECONDARY_SKILL:
{ {
hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true); hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
break; break;
@@ -444,7 +446,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHer
delete heroPlaceholder; delete heroPlaceholder;
gameState->scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CCampaignState::crossoverSerialize(heroToPlace)); gameState->scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CampaignState::crossoverSerialize(heroToPlace));
} }
} }
@@ -575,7 +577,7 @@ void CGameStateCampaign::initStartingResources()
}; };
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
if(chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::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
for(const PlayerSettings *ps : people) for(const PlayerSettings *ps : people)
@@ -613,7 +615,7 @@ void CGameStateCampaign::initTowns()
{ {
auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap(); auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
if (chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::BUILDING) if (chosenBonus && chosenBonus->type == CampaignBonusType::BUILDING)
{ {
for (int g=0; g<gameState->map->towns.size(); ++g) for (int g=0; g<gameState->map->towns.size(); ++g)
{ {
@@ -626,7 +628,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->camp->header.version == CampaignVersion::VCMI) if(gameState->scenarioOps->campState->header.version == CampaignVersion::VCMI)
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);
@@ -646,14 +648,14 @@ bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const
if (!campaignBonus) if (!campaignBonus)
return false; return false;
if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1)) if(campaignBonus->type == CampaignBonusType::HERO && playerColor == PlayerColor(campaignBonus->info1))
return true; return true;
return false; return false;
} }
CMap * CGameStateCampaign::getCurrentMap() const std::unique_ptr<CMap> CGameStateCampaign::getCurrentMap() const
{ {
return gameState->scenarioOps->campState->getMap(); return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE);
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@@ -13,7 +13,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CScenarioTravel; class CampaignTravel;
class CGHeroInstance; class CGHeroInstance;
class CGameState; class CGameState;
class CMap; class CMap;
@@ -43,7 +43,7 @@ class CGameStateCampaign
std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes); std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes);
/// 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 CScenarioTravel & travelOptions); void trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions);
void replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements); void replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements);
@@ -59,7 +59,7 @@ public:
void initTowns(); void initTowns();
bool playerHasStartingHero(PlayerColor player) const; bool playerHasStartingHero(PlayerColor player) const;
CMap * getCurrentMap() const; std::unique_ptr<CMap> getCurrentMap() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@@ -42,7 +42,7 @@ public:
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember, public ICreatureUpgrader class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember, public ICreatureUpgrader
{ {
// We serialize heroes into JSON for crossover // We serialize heroes into JSON for crossover
friend class CCampaignState; friend class CampaignState;
friend class CMapLoaderH3M; friend class CMapLoaderH3M;
friend class CMapFormatJson; friend class CMapFormatJson;

View File

@@ -71,19 +71,23 @@ CampaignRegions CampaignRegions::getLegacy(int campId)
} }
bool CScenarioTravel::STravelBonus::isBonusForHero() const bool CampaignBonus::isBonusForHero() const
{ {
return type == SPELL || type == MONSTER || type == ARTIFACT || type == SPELL_SCROLL || type == PRIMARY_SKILL return type == CampaignBonusType::SPELL ||
|| type == SECONDARY_SKILL; type == CampaignBonusType::MONSTER ||
type == CampaignBonusType::ARTIFACT ||
type == CampaignBonusType::SPELL_SCROLL ||
type == CampaignBonusType::PRIMARY_SKILL ||
type == CampaignBonusType::SECONDARY_SKILL;
} }
void CCampaignHeader::loadLegacyData(ui8 campId) void CampaignHeader::loadLegacyData(ui8 campId)
{ {
campaignRegions = CampaignRegions::getLegacy(campId); campaignRegions = CampaignRegions::getLegacy(campId);
numberOfScenarios = VLC->generaltexth->getCampaignLength(campId); numberOfScenarios = VLC->generaltexth->getCampaignLength(campId);
} }
CCampaignHeader CCampaignHandler::getHeader( const std::string & name) CampaignHeader 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);
@@ -105,14 +109,14 @@ CCampaignHeader CCampaignHandler::getHeader( const std::string & name)
return readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding); return readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding);
} }
std::unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & name ) std::shared_ptr<CampaignState> CampaignHandler::getCampaign( 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<CCampaign>(); auto ret = std::make_unique<CampaignState>();
auto fileStream = CResourceHandler::get(modName)->load(resourceID); auto fileStream = CResourceHandler::get(modName)->load(resourceID);
@@ -125,20 +129,30 @@ std::unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & na
ret->header = readHeaderFromMemory(reader, resourceID.getName(), modName, encoding); ret->header = readHeaderFromMemory(reader, resourceID.getName(), modName, encoding);
for(int g = 0; g < ret->header.numberOfScenarios; ++g) for(int g = 0; g < ret->header.numberOfScenarios; ++g)
ret->scenarios.emplace_back(readScenarioFromMemory(reader, ret->header)); {
auto scenarioID = static_cast<CampaignScenarioID>(ret->scenarios.size());
ret->scenarios[scenarioID] = readScenarioFromMemory(reader, ret->header);
}
} }
else // text format (json) else // text format (json)
{ {
JsonNode jsonCampaign((const char*)files[0].data(), files[0].size()); JsonNode jsonCampaign((const char*)files[0].data(), files[0].size());
ret->header = readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding); ret->header = readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding);
for(auto & scenario : jsonCampaign["scenarios"].Vector()) for(auto & scenario : jsonCampaign["scenarios"].Vector())
ret->scenarios.emplace_back(readScenarioFromJson(scenario)); {
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->header.numberOfScenarios; ++g)
{ {
while(!ret->scenarios[scenarioID].isNotVoid()) //skip void scenarios auto id = static_cast<CampaignScenarioID>(scenarioID);
while(!ret->scenarios[id].isNotVoid()) //skip void scenarios
scenarioID++; scenarioID++;
std::string scenarioName = resourceID.getName(); std::string scenarioName = resourceID.getName();
@@ -146,30 +160,24 @@ std::unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & na
scenarioName += ':' + std::to_string(g - 1); scenarioName += ':' + std::to_string(g - 1);
//set map piece appropriately, convert vector to string //set map piece appropriately, convert vector to string
ret->mapPieces[scenarioID].assign(reinterpret_cast<const char*>(files[g].data()), files[g].size()); ret->mapPieces[id].assign(reinterpret_cast<const char*>(files[g].data()), files[g].size());
CMapService mapService; CMapService mapService;
auto hdr = mapService.loadMapHeader( auto hdr = mapService.loadMapHeader(
reinterpret_cast<const ui8 *>(ret->mapPieces[scenarioID].c_str()), reinterpret_cast<const ui8 *>(ret->mapPieces[id].c_str()),
static_cast<int>(ret->mapPieces[scenarioID].size()), static_cast<int>(ret->mapPieces[id].size()),
scenarioName, scenarioName,
modName, modName,
encoding); encoding);
ret->scenarios[scenarioID].scenarioName = hdr->name; ret->scenarios[id].scenarioName = hdr->name;
scenarioID++; scenarioID++;
} }
// handle campaign specific discrepancies for(int i = 0; i < ret->scenarios.size(); i++)
if(resourceID.getName() == "DATA/AB")
{ {
ret->scenarios[6].keepHeroes.emplace_back(155); // keep hero Xeron for map 7 To Kill A Hero auto scenarioID = static_cast<CampaignScenarioID>(i);
}
else if(resourceID.getName() == "DATA/FINAL") if(vstd::contains(ret->mapPieces, scenarioID)) //not all maps must be present in a campaign
{ ret->mapsRemaining.push_back(scenarioID);
// keep following heroes for map 8 Final H
ret->scenarios[7].keepHeroes.emplace_back(148); // Gelu
ret->scenarios[7].keepHeroes.emplace_back(27); // Gem
ret->scenarios[7].keepHeroes.emplace_back(102); // Crag Hack
ret->scenarios[7].keepHeroes.emplace_back(96); // Yog
} }
return ret; return ret;
@@ -188,7 +196,7 @@ static std::string convertMapName(std::string input)
return input; return input;
} }
std::string CCampaignHandler::readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier) std::string CampaignHandler::readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier)
{ {
TextIdentifier stringID( "campaign", convertMapName(filename), identifier); TextIdentifier stringID( "campaign", convertMapName(filename), identifier);
@@ -201,14 +209,14 @@ std::string CCampaignHandler::readLocalizedString(CBinaryReader & reader, std::s
return VLC->generaltexth->translate(stringID.get()); return VLC->generaltexth->translate(stringID.get());
} }
CCampaignHeader CCampaignHandler::readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding) CampaignHeader CampaignHandler::readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding)
{ {
CCampaignHeader ret; CampaignHeader ret;
ret.version = 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, ret.version); logGlobal->info("VCMP Loading: Unsupported campaign %s version %d", filename, static_cast<int>(ret.version));
return ret; return ret;
} }
@@ -226,11 +234,11 @@ CCampaignHeader CCampaignHandler::readHeaderFromJson(JsonNode & reader, std::str
return ret; return ret;
} }
CCampaignScenario CCampaignHandler::readScenarioFromJson(JsonNode & reader) CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader)
{ {
auto prologEpilogReader = [](JsonNode & identifier) -> CCampaignScenario::SScenarioPrologEpilog auto prologEpilogReader = [](JsonNode & identifier) -> CampaignScenarioPrologEpilog
{ {
CCampaignScenario::SScenarioPrologEpilog ret; CampaignScenarioPrologEpilog ret;
ret.hasPrologEpilog = !identifier.isNull(); ret.hasPrologEpilog = !identifier.isNull();
if(ret.hasPrologEpilog) if(ret.hasPrologEpilog)
{ {
@@ -241,11 +249,11 @@ CCampaignScenario CCampaignHandler::readScenarioFromJson(JsonNode & reader)
return ret; return ret;
}; };
CCampaignScenario ret; CampaignScenario ret;
ret.conquered = false; ret.conquered = false;
ret.mapName = reader["map"].String(); ret.mapName = reader["map"].String();
for(auto & g : reader["preconditions"].Vector()) for(auto & g : reader["preconditions"].Vector())
ret.preconditionRegions.insert(g.Integer()); ret.preconditionRegions.insert(static_cast<CampaignScenarioID>(g.Integer()));
ret.regionColor = reader["color"].Integer(); ret.regionColor = reader["color"].Integer();
ret.difficulty = reader["difficulty"].Integer(); ret.difficulty = reader["difficulty"].Integer();
@@ -258,26 +266,26 @@ CCampaignScenario CCampaignHandler::readScenarioFromJson(JsonNode & reader)
return ret; return ret;
} }
CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader) CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
{ {
CScenarioTravel ret; CampaignTravel ret;
std::map<std::string, ui8> startOptionsMap = { std::map<std::string, CampaignStartOptions> startOptionsMap = {
{"none", 0}, {"none", CampaignStartOptions::NONE},
{"bonus", 1}, {"bonus", CampaignStartOptions::START_BONUS},
{"crossover", 2}, {"crossover", CampaignStartOptions::HERO_CROSSOVER},
{"hero", 3} {"hero", CampaignStartOptions::HERO_OPTIONS}
}; };
std::map<std::string, CScenarioTravel::STravelBonus::EBonusType> bonusTypeMap = { std::map<std::string, CampaignBonusType> bonusTypeMap = {
{"spell", CScenarioTravel::STravelBonus::EBonusType::SPELL}, {"spell", CampaignBonusType::SPELL},
{"creature", CScenarioTravel::STravelBonus::EBonusType::MONSTER}, {"creature", CampaignBonusType::MONSTER},
{"building", CScenarioTravel::STravelBonus::EBonusType::BUILDING}, {"building", CampaignBonusType::BUILDING},
{"artifact", CScenarioTravel::STravelBonus::EBonusType::ARTIFACT}, {"artifact", CampaignBonusType::ARTIFACT},
{"scroll", CScenarioTravel::STravelBonus::EBonusType::SPELL_SCROLL}, {"scroll", CampaignBonusType::SPELL_SCROLL},
{"primarySkill", CScenarioTravel::STravelBonus::EBonusType::PRIMARY_SKILL}, {"primarySkill", CampaignBonusType::PRIMARY_SKILL},
{"secondarySkill", CScenarioTravel::STravelBonus::EBonusType::SECONDARY_SKILL}, {"secondarySkill", CampaignBonusType::SECONDARY_SKILL},
{"resource", CScenarioTravel::STravelBonus::EBonusType::RESOURCE}, {"resource", CampaignBonusType::RESOURCE},
//{"prevHero", CScenarioTravel::STravelBonus::EBonusType::HEROES_FROM_PREVIOUS_SCENARIO}, //{"prevHero", CScenarioTravel::STravelBonus::EBonusType::HEROES_FROM_PREVIOUS_SCENARIO},
//{"hero", CScenarioTravel::STravelBonus::EBonusType::HERO}, //{"hero", CScenarioTravel::STravelBonus::EBonusType::HERO},
}; };
@@ -336,24 +344,24 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
ret.startOptions = startOptionsMap[reader["startOptions"].String()]; ret.startOptions = startOptionsMap[reader["startOptions"].String()];
switch(ret.startOptions) switch(ret.startOptions)
{ {
case 0: case CampaignStartOptions::NONE:
//no bonuses. Seems to be OK //no bonuses. Seems to be OK
break; break;
case 1: //reading of bonuses player can choose case CampaignStartOptions::START_BONUS: //reading of bonuses player can choose
{ {
ret.playerColor = reader["playerColor"].Integer(); ret.playerColor = reader["playerColor"].Integer();
for(auto & bjson : reader["bonuses"].Vector()) for(auto & bjson : reader["bonuses"].Vector())
{ {
CScenarioTravel::STravelBonus bonus; CampaignBonus bonus;
bonus.type = bonusTypeMap[bjson["what"].String()]; bonus.type = bonusTypeMap[bjson["what"].String()];
switch (bonus.type) switch (bonus.type)
{ {
case CScenarioTravel::STravelBonus::EBonusType::RESOURCE: case CampaignBonusType::RESOURCE:
bonus.info1 = resourceTypeMap[bjson["type"].String()]; bonus.info1 = resourceTypeMap[bjson["type"].String()];
bonus.info2 = bjson["amount"].Integer(); bonus.info2 = bjson["amount"].Integer();
break; break;
case CScenarioTravel::STravelBonus::EBonusType::BUILDING: case CampaignBonusType::BUILDING:
bonus.info1 = vstd::find_pos(EBuildingType::names, bjson["type"].String()); bonus.info1 = vstd::find_pos(EBuildingType::names, bjson["type"].String());
if(bonus.info1 == -1) if(bonus.info1 == -1)
logGlobal->warn("VCMP Loading: unresolved building identifier %s", bjson["type"].String()); logGlobal->warn("VCMP Loading: unresolved building identifier %s", bjson["type"].String());
@@ -372,24 +380,24 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
switch(bonus.type) switch(bonus.type)
{ {
case CScenarioTravel::STravelBonus::EBonusType::SPELL: case CampaignBonusType::SPELL:
case CScenarioTravel::STravelBonus::EBonusType::MONSTER: case CampaignBonusType::MONSTER:
case CScenarioTravel::STravelBonus::EBonusType::SECONDARY_SKILL: case CampaignBonusType::SECONDARY_SKILL:
case CScenarioTravel::STravelBonus::EBonusType::ARTIFACT: case CampaignBonusType::ARTIFACT:
if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), bjson["what"].String(), bjson["type"].String())) if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), bjson["what"].String(), bjson["type"].String()))
bonus.info2 = identifier.value(); bonus.info2 = identifier.value();
else else
logGlobal->warn("VCMP Loading: unresolved %s identifier %s", bjson["what"].String(), bjson["type"].String()); logGlobal->warn("VCMP Loading: unresolved %s identifier %s", bjson["what"].String(), bjson["type"].String());
break; break;
case CScenarioTravel::STravelBonus::EBonusType::SPELL_SCROLL: case CampaignBonusType::SPELL_SCROLL:
if(auto Identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "spell", bjson["type"].String())) if(auto Identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "spell", bjson["type"].String()))
bonus.info2 = Identifier.value(); bonus.info2 = Identifier.value();
else else
logGlobal->warn("VCMP Loading: unresolved spell scroll identifier %s", bjson["type"].String()); logGlobal->warn("VCMP Loading: unresolved spell scroll identifier %s", bjson["type"].String());
break; break;
case CScenarioTravel::STravelBonus::EBonusType::PRIMARY_SKILL: case CampaignBonusType::PRIMARY_SKILL:
for(auto & ps : primarySkillsMap) for(auto & ps : primarySkillsMap)
bonus.info2 |= bjson[ps.first].Integer() << ps.second; bonus.info2 |= bjson[ps.first].Integer() << ps.second;
break; break;
@@ -403,24 +411,24 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
} }
break; break;
} }
case 2: //reading of players (colors / scenarios ?) player can choose case CampaignStartOptions::HERO_CROSSOVER: //reading of players (colors / scenarios ?) player can choose
{ {
for(auto & bjson : reader["bonuses"].Vector()) for(auto & bjson : reader["bonuses"].Vector())
{ {
CScenarioTravel::STravelBonus bonus; CampaignBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO; bonus.type = CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO;
bonus.info1 = bjson["playerColor"].Integer(); //player color bonus.info1 = bjson["playerColor"].Integer(); //player color
bonus.info2 = bjson["scenario"].Integer(); //from what scenario bonus.info2 = bjson["scenario"].Integer(); //from what scenario
ret.bonusesToChoose.push_back(bonus); ret.bonusesToChoose.push_back(bonus);
} }
break; break;
} }
case 3: //heroes player can choose between case CampaignStartOptions::HERO_OPTIONS: //heroes player can choose between
{ {
for(auto & bjson : reader["bonuses"].Vector()) for(auto & bjson : reader["bonuses"].Vector())
{ {
CScenarioTravel::STravelBonus bonus; CampaignBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HERO; bonus.type = CampaignBonusType::HERO;
bonus.info1 = bjson["playerColor"].Integer(); //player color bonus.info1 = bjson["playerColor"].Integer(); //player color
if(int heroId = heroSpecialMap[bjson["hero"].String()]) if(int heroId = heroSpecialMap[bjson["hero"].String()])
@@ -446,11 +454,11 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
} }
CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader, std::string filename, std::string modName, std::string encoding ) CampaignHeader CampaignHandler::readHeaderFromMemory( CBinaryReader & reader, std::string filename, std::string modName, std::string encoding )
{ {
CCampaignHeader ret; CampaignHeader ret;
ret.version = 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);
ret.name = readLocalizedString(reader, filename, modName, encoding, "name"); ret.name = readLocalizedString(reader, filename, modName, encoding, "name");
@@ -467,22 +475,22 @@ CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader,
return ret; return ret;
} }
CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CCampaignHeader & header) CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader, const CampaignHeader & header)
{ {
auto prologEpilogReader = [&](const std::string & identifier) -> CCampaignScenario::SScenarioPrologEpilog auto prologEpilogReader = [&](const std::string & identifier) -> CampaignScenarioPrologEpilog
{ {
CCampaignScenario::SScenarioPrologEpilog ret; CampaignScenarioPrologEpilog ret;
ret.hasPrologEpilog = reader.readUInt8(); ret.hasPrologEpilog = reader.readUInt8();
if(ret.hasPrologEpilog) if(ret.hasPrologEpilog)
{ {
ret.prologVideo = CCampaignHandler::prologVideoName(reader.readUInt8()); ret.prologVideo = CampaignHandler::prologVideoName(reader.readUInt8());
ret.prologMusic = CCampaignHandler::prologMusicName(reader.readUInt8()); ret.prologMusic = CampaignHandler::prologMusicName(reader.readUInt8());
ret.prologText = readLocalizedString(reader, header.filename, header.modName, header.encoding, identifier); ret.prologText = readLocalizedString(reader, header.filename, header.modName, header.encoding, identifier);
} }
return ret; return ret;
}; };
CCampaignScenario ret; CampaignScenario ret;
ret.conquered = false; ret.conquered = false;
ret.mapName = reader.readBaseString(); ret.mapName = reader.readBaseString();
reader.readUInt32(); //packedMapSize - not used reader.readUInt32(); //packedMapSize - not used
@@ -505,18 +513,30 @@ CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & read
return ret; return ret;
} }
void CCampaignScenario::loadPreconditionRegions(ui32 regions) void CampaignScenario::loadPreconditionRegions(ui32 regions)
{ {
for (int i=0; i<32; i++) //for each bit in region. h3c however can only hold up to 16 for (int i=0; i<32; i++) //for each bit in region. h3c however can only hold up to 16
{ {
if ( (1 << i) & regions) if ( (1 << i) & regions)
preconditionRegions.insert(i); preconditionRegions.insert(static_cast<CampaignScenarioID>(i));
} }
} }
CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & reader, int version ) template<typename Identifier>
static void readContainer(std::set<Identifier> container, CBinaryReader & reader, int sizeBytes)
{ {
CScenarioTravel ret; for(int iId = 0, byte = 0; iId < sizeBytes * 8; ++iId)
{
if(iId % 8 == 0)
byte = reader.readUInt8();
if(byte & (1 << iId % 8))
container.insert(Identifier(iId));
}
}
CampaignTravel CampaignHandler::readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version )
{
CampaignTravel ret;
ui8 whatHeroKeeps = reader.readUInt8(); ui8 whatHeroKeeps = reader.readUInt8();
ret.whatHeroKeeps.experience = whatHeroKeeps & 1; ret.whatHeroKeeps.experience = whatHeroKeeps & 1;
@@ -525,83 +545,71 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r
ret.whatHeroKeeps.spells = whatHeroKeeps & 8; ret.whatHeroKeeps.spells = whatHeroKeeps & 8;
ret.whatHeroKeeps.artifacts = whatHeroKeeps & 16; ret.whatHeroKeeps.artifacts = whatHeroKeeps & 16;
//TODO: replace with template lambda form C++20 and make typed containers readContainer(ret.monstersKeptByHero, reader, 19);
auto bitMaskToId = [&reader](std::set<int> & container, int size) readContainer(ret.artifactsKeptByHero, reader, version < CampaignVersion::SoD ? 17 : 18);
{
for(int iId = 0, byte = 0; iId < size * 8; ++iId)
{
if(iId % 8 == 0)
byte = reader.readUInt8();
if(byte & (1 << iId % 8))
container.insert(iId);
}
};
bitMaskToId(ret.monstersKeptByHero, 19); ret.startOptions = static_cast<CampaignStartOptions>(reader.readUInt8());
bitMaskToId(ret.artifactsKeptByHero, version < CampaignVersion::SoD ? 17 : 18);
ret.startOptions = reader.readUInt8();
switch(ret.startOptions) switch(ret.startOptions)
{ {
case 0: case CampaignStartOptions::NONE:
//no bonuses. Seems to be OK //no bonuses. Seems to be OK
break; break;
case 1: //reading of bonuses player can choose case CampaignStartOptions::START_BONUS: //reading of bonuses player can choose
{ {
ret.playerColor = reader.readUInt8(); ret.playerColor = reader.readUInt8();
ui8 numOfBonuses = reader.readUInt8(); ui8 numOfBonuses = reader.readUInt8();
for (int g=0; g<numOfBonuses; ++g) for (int g=0; g<numOfBonuses; ++g)
{ {
CScenarioTravel::STravelBonus bonus; CampaignBonus bonus;
bonus.type = static_cast<CScenarioTravel::STravelBonus::EBonusType>(reader.readUInt8()); bonus.type = static_cast<CampaignBonusType>(reader.readUInt8());
//hero: FFFD means 'most powerful' and FFFE means 'generated' //hero: FFFD means 'most powerful' and FFFE means 'generated'
switch(bonus.type) switch(bonus.type)
{ {
case CScenarioTravel::STravelBonus::SPELL: case CampaignBonusType::SPELL:
{ {
bonus.info1 = reader.readUInt16(); //hero bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt8(); //spell ID bonus.info2 = reader.readUInt8(); //spell ID
break; break;
} }
case CScenarioTravel::STravelBonus::MONSTER: case CampaignBonusType::MONSTER:
{ {
bonus.info1 = reader.readUInt16(); //hero bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt16(); //monster type bonus.info2 = reader.readUInt16(); //monster type
bonus.info3 = reader.readUInt16(); //monster count bonus.info3 = reader.readUInt16(); //monster count
break; break;
} }
case CScenarioTravel::STravelBonus::BUILDING: case CampaignBonusType::BUILDING:
{ {
bonus.info1 = reader.readUInt8(); //building ID (0 - town hall, 1 - city hall, 2 - capitol, etc) bonus.info1 = reader.readUInt8(); //building ID (0 - town hall, 1 - city hall, 2 - capitol, etc)
break; break;
} }
case CScenarioTravel::STravelBonus::ARTIFACT: case CampaignBonusType::ARTIFACT:
{ {
bonus.info1 = reader.readUInt16(); //hero bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt16(); //artifact ID bonus.info2 = reader.readUInt16(); //artifact ID
break; break;
} }
case CScenarioTravel::STravelBonus::SPELL_SCROLL: case CampaignBonusType::SPELL_SCROLL:
{ {
bonus.info1 = reader.readUInt16(); //hero bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt8(); //spell ID bonus.info2 = reader.readUInt8(); //spell ID
break; break;
} }
case CScenarioTravel::STravelBonus::PRIMARY_SKILL: case CampaignBonusType::PRIMARY_SKILL:
{ {
bonus.info1 = reader.readUInt16(); //hero bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt32(); //bonuses (4 bytes for 4 skills) bonus.info2 = reader.readUInt32(); //bonuses (4 bytes for 4 skills)
break; break;
} }
case CScenarioTravel::STravelBonus::SECONDARY_SKILL: case CampaignBonusType::SECONDARY_SKILL:
{ {
bonus.info1 = reader.readUInt16(); //hero bonus.info1 = reader.readUInt16(); //hero
bonus.info2 = reader.readUInt8(); //skill ID bonus.info2 = reader.readUInt8(); //skill ID
bonus.info3 = reader.readUInt8(); //skill level bonus.info3 = reader.readUInt8(); //skill level
break; break;
} }
case CScenarioTravel::STravelBonus::RESOURCE: case CampaignBonusType::RESOURCE:
{ {
bonus.info1 = reader.readUInt8(); //type bonus.info1 = reader.readUInt8(); //type
//FD - wood+ore //FD - wood+ore
@@ -617,13 +625,13 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r
} }
break; break;
} }
case 2: //reading of players (colors / scenarios ?) player can choose case CampaignStartOptions::HERO_CROSSOVER: //reading of players (colors / scenarios ?) player can choose
{ {
ui8 numOfBonuses = reader.readUInt8(); ui8 numOfBonuses = reader.readUInt8();
for (int g=0; g<numOfBonuses; ++g) for (int g=0; g<numOfBonuses; ++g)
{ {
CScenarioTravel::STravelBonus bonus; CampaignBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO; bonus.type = CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO;
bonus.info1 = reader.readUInt8(); //player color bonus.info1 = reader.readUInt8(); //player color
bonus.info2 = reader.readUInt8(); //from what scenario bonus.info2 = reader.readUInt8(); //from what scenario
@@ -631,13 +639,13 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r
} }
break; break;
} }
case 3: //heroes player can choose between case CampaignStartOptions::HERO_OPTIONS: //heroes player can choose between
{ {
ui8 numOfBonuses = reader.readUInt8(); ui8 numOfBonuses = reader.readUInt8();
for (int g=0; g<numOfBonuses; ++g) for (int g=0; g<numOfBonuses; ++g)
{ {
CScenarioTravel::STravelBonus bonus; CampaignBonus bonus;
bonus.type = CScenarioTravel::STravelBonus::HERO; bonus.type = CampaignBonusType::HERO;
bonus.info1 = reader.readUInt8(); //player color bonus.info1 = reader.readUInt8(); //player color
bonus.info2 = reader.readUInt16(); //hero, FF FF - random bonus.info2 = reader.readUInt16(); //hero, FF FF - random
@@ -655,7 +663,7 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r
return ret; return ret;
} }
std::vector< std::vector<ui8> > CCampaignHandler::getFile(std::unique_ptr<CInputStream> file, bool headerOnly) std::vector< std::vector<ui8> > CampaignHandler::getFile(std::unique_ptr<CInputStream> file, bool headerOnly)
{ {
CCompressedStream stream(std::move(file), true); CCompressedStream stream(std::move(file), true);
@@ -671,38 +679,37 @@ std::vector< std::vector<ui8> > CCampaignHandler::getFile(std::unique_ptr<CInput
return ret; return ret;
} }
bool CCampaign::conquerable( int whichScenario ) const bool CampaignState::conquerable(CampaignScenarioID whichScenario) const
{ {
//check for void scenraio //check for void scenraio
if (!scenarios[whichScenario].isNotVoid()) if (!scenarios.at(whichScenario).isNotVoid())
{ {
return false; return false;
} }
if (scenarios[whichScenario].conquered) if (scenarios.at(whichScenario).conquered)
{ {
return false; return false;
} }
//check preconditioned regions //check preconditioned regions
for (int g=0; g<scenarios.size(); ++g) for (auto const & it : scenarios.at(whichScenario).preconditionRegions)
{ {
if( vstd::contains(scenarios[whichScenario].preconditionRegions, g) && !scenarios[g].conquered) if (!scenarios.at(it).conquered)
return false; //prerequisite does not met return false;
} }
return true; return true;
} }
bool CCampaignScenario::isNotVoid() const bool CampaignScenario::isNotVoid() const
{ {
return !mapName.empty(); return !mapName.empty();
} }
const CGHeroInstance * CCampaignScenario::strongestHero(const PlayerColor & owner) const CGHeroInstance * CampaignScenario::strongestHero(const PlayerColor & owner)
{ {
std::function<bool(JsonNode & node)> isOwned = [owner](JsonNode & node) std::function<bool(JsonNode & node)> isOwned = [owner](JsonNode & node)
{ {
auto * h = CCampaignState::crossoverDeserialize(node); auto * h = CampaignState::crossoverDeserialize(node);
bool result = h->tempOwner == owner; bool result = h->tempOwner == owner;
vstd::clear_pointer(h); vstd::clear_pointer(h);
return result; return result;
@@ -711,25 +718,25 @@ const CGHeroInstance * CCampaignScenario::strongestHero(const PlayerColor & owne
auto i = vstd::maxElementByFun(ownedHeroes, [](JsonNode & node) auto i = vstd::maxElementByFun(ownedHeroes, [](JsonNode & node)
{ {
auto * h = CCampaignState::crossoverDeserialize(node); auto * h = CampaignState::crossoverDeserialize(node);
double result = h->getHeroStrength(); double result = h->getHeroStrength();
vstd::clear_pointer(h); vstd::clear_pointer(h);
return result; return result;
}); });
return i == ownedHeroes.end() ? nullptr : CCampaignState::crossoverDeserialize(*i); return i == ownedHeroes.end() ? nullptr : CampaignState::crossoverDeserialize(*i);
} }
std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes() std::vector<CGHeroInstance *> CampaignScenario::getLostCrossoverHeroes()
{ {
std::vector<CGHeroInstance *> lostCrossoverHeroes; std::vector<CGHeroInstance *> lostCrossoverHeroes;
if(conquered) if(conquered)
{ {
for(auto node2 : placedCrossoverHeroes) for(auto node2 : placedCrossoverHeroes)
{ {
auto * hero = CCampaignState::crossoverDeserialize(node2); auto * hero = CampaignState::crossoverDeserialize(node2);
auto it = range::find_if(crossoverHeroes, [hero](JsonNode node) auto it = range::find_if(crossoverHeroes, [hero](JsonNode node)
{ {
auto * h = CCampaignState::crossoverDeserialize(node); auto * h = CampaignState::crossoverDeserialize(node);
bool result = hero->subID == h->subID; bool result = hero->subID == h->subID;
vstd::clear_pointer(h); vstd::clear_pointer(h);
return result; return result;
@@ -743,96 +750,87 @@ std::vector<CGHeroInstance *> CCampaignScenario::getLostCrossoverHeroes()
return lostCrossoverHeroes; return lostCrossoverHeroes;
} }
void CCampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes) void CampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes)
{ {
camp->scenarios[*currentMap].crossoverHeroes.clear(); scenarios.at(*currentMap).crossoverHeroes.clear();
for(CGHeroInstance * hero : heroes) for(CGHeroInstance * hero : heroes)
{ {
camp->scenarios[*currentMap].crossoverHeroes.push_back(crossoverSerialize(hero)); scenarios.at(*currentMap).crossoverHeroes.push_back(crossoverSerialize(hero));
} }
mapsConquered.push_back(*currentMap); mapsConquered.push_back(*currentMap);
mapsRemaining -= *currentMap; mapsRemaining -= *currentMap;
camp->scenarios[*currentMap].conquered = true; scenarios.at(*currentMap).conquered = true;
} }
std::optional<CScenarioTravel::STravelBonus> CCampaignState::getBonusForCurrentMap() const std::optional<CampaignBonus> CampaignState::getBonusForCurrentMap() const
{ {
auto bonuses = getCurrentScenario().travelOptions.bonusesToChoose; auto bonuses = getCurrentScenario().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<CScenarioTravel::STravelBonus>(); return std::optional<CampaignBonus>();
else else
return bonuses[currentBonusID()]; return bonuses[currentBonusID()];
} }
const CCampaignScenario & CCampaignState::getCurrentScenario() const const CampaignScenario & CampaignState::getCurrentScenario() const
{ {
return camp->scenarios[*currentMap]; return scenarios.at(*currentMap);
} }
CCampaignScenario & CCampaignState::getCurrentScenario() CampaignScenario & CampaignState::getCurrentScenario()
{ {
return camp->scenarios[*currentMap]; return scenarios.at(*currentMap);
} }
ui8 CCampaignState::currentBonusID() const ui8 CampaignState::currentBonusID() const
{ {
return chosenCampaignBonuses.at(*currentMap); return chosenCampaignBonuses.at(*currentMap);
} }
CCampaignState::CCampaignState( std::unique_ptr<CCampaign> _camp ) : camp(std::move(_camp)) std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
{
for(int i = 0; i < camp->scenarios.size(); i++)
{
if(vstd::contains(camp->mapPieces, i)) //not all maps must be present in a campaign
mapsRemaining.push_back(i);
}
}
CMap * CCampaignState::getMap(int scenarioId) const
{ {
// FIXME: there is certainly better way to handle maps inside campaigns // FIXME: there is certainly better way to handle maps inside campaigns
if(scenarioId == -1) if(scenarioId == CampaignScenarioID::NONE)
scenarioId = currentMap.value(); scenarioId = currentMap.value();
CMapService mapService; CMapService mapService;
std::string scenarioName = camp->header.filename.substr(0, camp->header.filename.find('.')); std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
boost::to_lower(scenarioName); boost::to_lower(scenarioName);
scenarioName += ':' + std::to_string(scenarioId); scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
std::string & mapContent = camp->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, camp->header.modName, camp->header.encoding).release(); return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding);
} }
std::unique_ptr<CMapHeader> CCampaignState::getHeader(int scenarioId) const std::unique_ptr<CMapHeader> CampaignState::getHeader(CampaignScenarioID scenarioId) const
{ {
if(scenarioId == -1) if(scenarioId == CampaignScenarioID::NONE)
scenarioId = currentMap.value(); scenarioId = currentMap.value();
CMapService mapService; CMapService mapService;
std::string scenarioName = camp->header.filename.substr(0, camp->header.filename.find('.')); std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
boost::to_lower(scenarioName); boost::to_lower(scenarioName);
scenarioName += ':' + std::to_string(scenarioId); scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
std::string & mapContent = camp->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, camp->header.modName, camp->header.encoding); return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding);
} }
std::shared_ptr<CMapInfo> CCampaignState::getMapInfo(int scenarioId) const std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioId) const
{ {
if(scenarioId == -1) if(scenarioId == CampaignScenarioID::NONE)
scenarioId = currentMap.value(); scenarioId = currentMap.value();
auto mapInfo = std::make_shared<CMapInfo>(); auto mapInfo = std::make_shared<CMapInfo>();
mapInfo->fileURI = camp->header.filename; mapInfo->fileURI = header.filename;
mapInfo->mapHeader = getHeader(scenarioId); mapInfo->mapHeader = getHeader(scenarioId);
mapInfo->countPlayers(); mapInfo->countPlayers();
return mapInfo; return mapInfo;
} }
JsonNode CCampaignState::crossoverSerialize(CGHeroInstance * hero) JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero)
{ {
JsonNode node; JsonNode node;
JsonSerializer handler(nullptr, node); JsonSerializer handler(nullptr, node);
@@ -840,7 +838,7 @@ JsonNode CCampaignState::crossoverSerialize(CGHeroInstance * hero)
return node; return node;
} }
CGHeroInstance * CCampaignState::crossoverDeserialize(JsonNode & node) CGHeroInstance * CampaignState::crossoverDeserialize(JsonNode & node)
{ {
JsonDeserializer handler(nullptr, node); JsonDeserializer handler(nullptr, node);
auto * hero = new CGHeroInstance(); auto * hero = new CGHeroInstance();
@@ -849,7 +847,7 @@ CGHeroInstance * CCampaignState::crossoverDeserialize(JsonNode & node)
return hero; return hero;
} }
std::string CCampaignHandler::prologVideoName(ui8 index) std::string CampaignHandler::prologVideoName(ui8 index)
{ {
JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT)); JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT));
auto vids = config["videos"].Vector(); auto vids = config["videos"].Vector();
@@ -858,13 +856,13 @@ std::string CCampaignHandler::prologVideoName(ui8 index)
return ""; return "";
} }
std::string CCampaignHandler::prologMusicName(ui8 index) std::string CampaignHandler::prologMusicName(ui8 index)
{ {
std::vector<std::string> music; std::vector<std::string> music;
return VLC->generaltexth->translate("core.cmpmusic." + std::to_string(static_cast<int>(index))); return VLC->generaltexth->translate("core.cmpmusic." + std::to_string(static_cast<int>(index)));
} }
std::string CCampaignHandler::prologVoiceName(ui8 index) std::string CampaignHandler::prologVoiceName(ui8 index)
{ {
JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT)); JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT));
auto audio = config["voice"].Vector(); auto audio = config["voice"].Vector();

View File

@@ -22,21 +22,19 @@ class CMapHeader;
class CMapInfo; class CMapInfo;
class JsonNode; class JsonNode;
namespace CampaignVersion enum class CampaignVersion : uint8_t
{
enum Version
{ {
NONE = 0,
RoE = 4, RoE = 4,
AB = 5, AB = 5,
SoD = 6, SoD = 6,
WoG = 6, WoG = 6,
// Chr = 7, // Heroes Chronicles, likely identical to SoD, untested // Chr = 7, // Heroes Chronicles, likely identical to SoD, untested
VCMI = 1
};
const int VCMI_MIN = 1; VCMI = 1,
const int VCMI_MAX = 1; VCMI_MIN = 1,
} VCMI_MAX = 1,
};
struct DLL_LINKAGE CampaignRegions struct DLL_LINKAGE CampaignRegions
{ {
@@ -71,10 +69,10 @@ struct DLL_LINKAGE CampaignRegions
static CampaignRegions getLegacy(int campId); static CampaignRegions getLegacy(int campId);
}; };
class DLL_LINKAGE CCampaignHeader class DLL_LINKAGE CampaignHeader
{ {
public: public:
si32 version = 0; //4 - RoE, 5 - AB, 6 - SoD, WoG and HotA CampaignVersion version = CampaignVersion::NONE;
CampaignRegions campaignRegions; CampaignRegions campaignRegions;
int numberOfScenarios = 0; int numberOfScenarios = 0;
std::string name, description; std::string name, description;
@@ -102,7 +100,56 @@ public:
} }
}; };
class DLL_LINKAGE CScenarioTravel enum class CampaignStartOptions: int8_t
{
NONE = 0,
START_BONUS,
HERO_CROSSOVER,
HERO_OPTIONS
};
enum class CampaignBonusType : int8_t
{
NONE = -1,
SPELL,
MONSTER,
BUILDING,
ARTIFACT,
SPELL_SCROLL,
PRIMARY_SKILL,
SECONDARY_SKILL,
RESOURCE,
HEROES_FROM_PREVIOUS_SCENARIO,
HERO
};
enum class CampaignScenarioID : int8_t
{
NONE = -1,
// no members - fake enum to create integer type that is not implicitly convertible to int
};
struct DLL_LINKAGE CampaignBonus
{
CampaignBonusType type = CampaignBonusType::NONE; //uses EBonusType
//purpose depends on type
int32_t info1 = 0;
int32_t info2 = 0;
int32_t info3 = 0;
bool isBonusForHero() const;
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & type;
h & info1;
h & info2;
h & info3;
}
};
class DLL_LINKAGE CampaignTravel
{ {
public: public:
@@ -124,35 +171,13 @@ public:
} }
}; };
std::set<CreatureID> monstersKeptByHero;
std::set<ArtifactID> artifactsKeptByHero;
std::vector<CampaignBonus> bonusesToChoose;
WhatHeroKeeps whatHeroKeeps; WhatHeroKeeps whatHeroKeeps;
CampaignStartOptions startOptions = CampaignStartOptions::NONE; //1 - start bonus, 2 - traveling hero, 3 - hero options
//TODO: use typed containers PlayerColor playerColor = PlayerColor::NEUTRAL; //only for startOptions == 1
std::set<int> monstersKeptByHero;
std::set<int> artifactsKeptByHero;
ui8 startOptions = 0; //1 - start bonus, 2 - traveling hero, 3 - hero options
ui8 playerColor = 0; //only for startOptions == 1
struct DLL_LINKAGE STravelBonus
{
enum EBonusType {SPELL, MONSTER, BUILDING, ARTIFACT, SPELL_SCROLL, PRIMARY_SKILL, SECONDARY_SKILL, RESOURCE,
HEROES_FROM_PREVIOUS_SCENARIO, HERO};
EBonusType type = EBonusType::SPELL; //uses EBonusType
si32 info1 = 0, info2 = 0, info3 = 0; //purpose depends on type
bool isBonusForHero() const;
template <typename Handler> void serialize(Handler &h, const int formatVersion)
{
h & type;
h & info1;
h & info2;
h & info3;
}
};
std::vector<STravelBonus> bonusesToChoose;
template <typename Handler> void serialize(Handler &h, const int formatVersion) template <typename Handler> void serialize(Handler &h, const int formatVersion)
{ {
@@ -163,13 +188,9 @@ public:
h & playerColor; h & playerColor;
h & bonusesToChoose; h & bonusesToChoose;
} }
}; };
class DLL_LINKAGE CCampaignScenario struct DLL_LINKAGE CampaignScenarioPrologEpilog
{
public:
struct DLL_LINKAGE SScenarioPrologEpilog
{ {
bool hasPrologEpilog = false; bool hasPrologEpilog = false;
std::string prologVideo; // from CmpMovie.txt std::string prologVideo; // from CmpMovie.txt
@@ -185,17 +206,21 @@ public:
} }
}; };
class DLL_LINKAGE CampaignScenario
{
public:
std::string mapName; //*.h3m std::string mapName; //*.h3m
std::string scenarioName; //from header. human-readble std::string scenarioName; //from header. human-readble
std::set<ui8> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c) std::set<CampaignScenarioID> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
ui8 regionColor = 0; ui8 regionColor = 0;
ui8 difficulty = 0; ui8 difficulty = 0;
bool conquered = false; bool conquered = false;
std::string regionText; std::string regionText;
SScenarioPrologEpilog prolog, epilog; CampaignScenarioPrologEpilog prolog;
CampaignScenarioPrologEpilog epilog;
CScenarioTravel travelOptions; CampaignTravel travelOptions;
std::vector<HeroTypeID> keepHeroes; // contains list of heroes which should be kept for next scenario (doesn't matter if they lost) std::vector<HeroTypeID> keepHeroes; // contains list of heroes which should be kept for next scenario (doesn't matter if they lost)
std::vector<JsonNode> crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished std::vector<JsonNode> crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished
std::vector<JsonNode> placedCrossoverHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started std::vector<JsonNode> placedCrossoverHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started
@@ -224,52 +249,42 @@ public:
} }
}; };
class DLL_LINKAGE CCampaign class DLL_LINKAGE CampaignState
{ {
public: public:
CCampaignHeader header; CampaignHeader header;
std::vector<CCampaignScenario> scenarios; std::map<CampaignScenarioID, CampaignScenario> scenarios;
std::map<int, std::string > mapPieces; //binary h3ms, scenario number -> map data std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data
template <typename Handler> void serialize(Handler &h, const int formatVersion) std::string fileEncoding;
std::vector<CampaignScenarioID> mapsConquered;
std::vector<CampaignScenarioID> mapsRemaining;
std::optional<CampaignScenarioID> currentMap;
std::map<CampaignScenarioID, ui8> chosenCampaignBonuses;
public:
std::optional<CampaignBonus> getBonusForCurrentMap() const;
const CampaignScenario & getCurrentScenario() const;
ui8 currentBonusID() const;
bool conquerable(CampaignScenarioID whichScenario) const;
std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const;
std::unique_ptr<CMapHeader> getHeader(CampaignScenarioID scenarioId) const;
std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const;
CampaignScenario & getCurrentScenario();
void setCurrentMapAsConquered(const std::vector<CGHeroInstance*> & heroes);
static JsonNode crossoverSerialize(CGHeroInstance * hero);
static CGHeroInstance * crossoverDeserialize(JsonNode & node);
CampaignState() = default;
template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & header; h & header;
h & scenarios; h & scenarios;
h & mapPieces; h & mapPieces;
}
bool conquerable(int whichScenario) const;
};
class DLL_LINKAGE CCampaignState
{
public:
std::unique_ptr<CCampaign> camp;
std::string fileEncoding;
std::vector<ui8> mapsConquered, mapsRemaining;
std::optional<si32> currentMap;
std::map<ui8, ui8> chosenCampaignBonuses;
void setCurrentMapAsConquered(const std::vector<CGHeroInstance*> & heroes);
std::optional<CScenarioTravel::STravelBonus> getBonusForCurrentMap() const;
const CCampaignScenario & getCurrentScenario() const;
CCampaignScenario & getCurrentScenario();
ui8 currentBonusID() const;
CMap * getMap(int scenarioId = -1) const;
std::unique_ptr<CMapHeader> getHeader(int scenarioId = -1) const;
std::shared_ptr<CMapInfo> getMapInfo(int scenarioId = -1) const;
static JsonNode crossoverSerialize(CGHeroInstance * hero);
static CGHeroInstance * crossoverDeserialize(JsonNode & node);
CCampaignState() = default;
CCampaignState(std::unique_ptr<CCampaign> _camp);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & camp;
h & mapsRemaining; h & mapsRemaining;
h & mapsConquered; h & mapsConquered;
h & currentMap; h & currentMap;
@@ -277,19 +292,19 @@ public:
} }
}; };
class DLL_LINKAGE CCampaignHandler 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);
//parsers for VCMI campaigns (*.vcmp) //parsers for VCMI campaigns (*.vcmp)
static CCampaignHeader readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding); static CampaignHeader readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding);
static CCampaignScenario readScenarioFromJson(JsonNode & reader); static CampaignScenario readScenarioFromJson(JsonNode & reader);
static CScenarioTravel readScenarioTravelFromJson(JsonNode & reader); static CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
//parsers for original H3C campaigns //parsers for original H3C campaigns
static CCampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding); static CampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
static CCampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CCampaignHeader & header); static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CampaignHeader & header);
static CScenarioTravel readScenarioTravelFromMemory(CBinaryReader & reader, int 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)
/// headerOnly - only header will be decompressed, returned vector wont have any maps /// headerOnly - only header will be decompressed, returned vector wont have any maps
static std::vector<std::vector<ui8>> getFile(std::unique_ptr<CInputStream> file, bool headerOnly); static std::vector<std::vector<ui8>> getFile(std::unique_ptr<CInputStream> file, bool headerOnly);
@@ -299,9 +314,9 @@ class DLL_LINKAGE CCampaignHandler
static std::string prologVoiceName(ui8 index); static std::string prologVoiceName(ui8 index);
public: public:
static CCampaignHeader getHeader( const std::string & name); //name - name of appropriate file static CampaignHeader getHeader( const std::string & name); //name - name of appropriate file
static std::unique_ptr<CCampaign> 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
}; };
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@@ -66,7 +66,7 @@ void CMapInfo::saveInit(const ResourceID & file)
void CMapInfo::campaignInit() void CMapInfo::campaignInit()
{ {
campaignHeader = std::make_unique<CCampaignHeader>(CCampaignHandler::getHeader(fileURI)); campaignHeader = std::make_unique<CampaignHeader>(CampaignHandler::getHeader(fileURI));
if(!campaignHeader->valid) if(!campaignHeader->valid)
campaignHeader.reset(); campaignHeader.reset();
} }

View File

@@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct StartInfo; struct StartInfo;
class CMapHeader; class CMapHeader;
class CCampaignHeader; class CampaignHeader;
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<CCampaignHeader> campaignHeader; //may be nullptr if scenario std::unique_ptr<CampaignHeader> campaignHeader; //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;

View File

@@ -870,10 +870,10 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
s.bonus = PlayerSettings::RANDOM; s.bonus = PlayerSettings::RANDOM;
} }
void CVCMIServer::setCampaignMap(int mapId) void CVCMIServer::setCampaignMap(CampaignScenarioID mapId)
{ {
campaignMap = mapId; campaignMap = mapId;
si->difficulty = si->campState->camp->scenarios[mapId].difficulty; si->difficulty = si->campState->scenarios[mapId].difficulty;
campaignBonus = -1; campaignBonus = -1;
updateStartInfoOnMapChange(si->campState->getMapInfo(mapId)); updateStartInfoOnMapChange(si->campState->getMapInfo(mapId));
} }
@@ -882,9 +882,9 @@ void CVCMIServer::setCampaignBonus(int bonusId)
{ {
campaignBonus = bonusId; campaignBonus = bonusId;
const CCampaignScenario & scenario = si->campState->camp->scenarios[campaignMap]; const CampaignScenario & scenario = si->campState->scenarios[campaignMap];
const std::vector<CScenarioTravel::STravelBonus> & bonDescs = scenario.travelOptions.bonusesToChoose; const std::vector<CampaignBonus> & bonDescs = scenario.travelOptions.bonusesToChoose;
if(bonDescs[bonusId].type == CScenarioTravel::STravelBonus::HERO) if(bonDescs[bonusId].type == CampaignBonusType::HERO)
{ {
for(auto & elem : si->playerInfos) for(auto & elem : si->playerInfos)
{ {

View File

@@ -112,7 +112,7 @@ public:
void optionNextCastle(PlayerColor player, int dir); //dir == -1 or + void optionNextCastle(PlayerColor player, int dir); //dir == -1 or +
// Campaigns // Campaigns
void setCampaignMap(int mapId); void setCampaignMap(CampaignScenarioID mapId);
void setCampaignBonus(int bonusId); void setCampaignBonus(int bonusId);
ui8 getIdOfFirstUnallocatedPlayer() const; ui8 getIdOfFirstUnallocatedPlayer() const;

View File

@@ -213,18 +213,20 @@ void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack)
void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack) void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack)
{ {
srv.si->mapname = pack.ourCampaign->camp->header.filename; srv.si->mapname = pack.ourCampaign->header.filename;
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;
bool isCurrentMapConquerable = pack.ourCampaign->currentMap && pack.ourCampaign->camp->conquerable(*pack.ourCampaign->currentMap); bool isCurrentMapConquerable = pack.ourCampaign->currentMap && pack.ourCampaign->conquerable(*pack.ourCampaign->currentMap);
for(int i = 0; i < pack.ourCampaign->camp->scenarios.size(); i++) for(int i = 0; i < pack.ourCampaign->scenarios.size(); i++)
{ {
if(pack.ourCampaign->camp->conquerable(i)) auto scenarioID = static_cast<CampaignScenarioID>(i);
if(pack.ourCampaign->conquerable(scenarioID))
{ {
if(!isCurrentMapConquerable || (isCurrentMapConquerable && i == *pack.ourCampaign->currentMap)) if(!isCurrentMapConquerable || (isCurrentMapConquerable && scenarioID == *pack.ourCampaign->currentMap))
{ {
srv.setCampaignMap(i); srv.setCampaignMap(scenarioID);
} }
} }
} }