mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Added encapsulation for CampaignState class
This commit is contained in:
		| @@ -210,8 +210,8 @@ void ClientCommandManager::handleConvertTextCommand() | ||||
| 	for (auto const & campaignName : campaignList) | ||||
| 	{ | ||||
| 		auto state = CampaignHandler::getCampaign(campaignName.getName()); | ||||
| 		for (auto const & part : state->mapPieces) | ||||
| 			state->getMap(part.first); | ||||
| 		for (auto const & part : state->allScenarios()) | ||||
| 			state->getMap(part); | ||||
| 	} | ||||
|  | ||||
| 	VLC->generaltexth->dumpAllTexts(); | ||||
|   | ||||
| @@ -51,10 +51,10 @@ void UserEventHandler::handleUserEvent(const SDL_UserEvent & user) | ||||
| 			CSH->campaignServerRestartLock.set(true); | ||||
| 			CSH->endGameplay(); | ||||
| 			auto ourCampaign = std::shared_ptr<CampaignState>(reinterpret_cast<CampaignState *>(user.data1)); | ||||
| 			auto & epilogue = ourCampaign->scenarios[ourCampaign->mapsConquered.back()].epilog; | ||||
| 			auto & epilogue = ourCampaign->scenario(*ourCampaign->lastScenario()).epilog; | ||||
| 			auto finisher = [=]() | ||||
| 			{ | ||||
| 				if(!ourCampaign->mapsRemaining.empty()) | ||||
| 				if(!ourCampaign->isCampaignFinished()) | ||||
| 				{ | ||||
| 					GH.windows().pushWindow(CMM); | ||||
| 					GH.windows().pushWindow(CMM->menu); | ||||
|   | ||||
| @@ -64,7 +64,7 @@ CBonusSelection::CBonusSelection() | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; | ||||
|  | ||||
| 	std::string bgName = getCampaign()->header.campaignRegions.campPrefix + "_BG.BMP"; | ||||
| 	std::string bgName = getCampaign()->getHeader().campaignRegions.campPrefix + "_BG.BMP"; | ||||
| 	setBackground(bgName); | ||||
|  | ||||
| 	panelBackground = std::make_shared<CPicture>("CAMPBRF.BMP", 456, 6); | ||||
| @@ -78,7 +78,7 @@ CBonusSelection::CBonusSelection() | ||||
| 	iconsMapSizes = std::make_shared<CAnimImage>("SCNRMPSZ", 4, 0, 735, 26); | ||||
|  | ||||
| 	labelCampaignDescription = std::make_shared<CLabel>(481, 63, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]); | ||||
| 	campaignDescription = std::make_shared<CTextBox>(getCampaign()->header.description, Rect(480, 86, 286, 117), 1); | ||||
| 	campaignDescription = std::make_shared<CTextBox>(getCampaign()->getHeader().description, Rect(480, 86, 286, 117), 1); | ||||
|  | ||||
| 	mapName = std::make_shared<CLabel>(481, 219, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->mi->getName()); | ||||
| 	labelMapDescription = std::make_shared<CLabel>(481, 253, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[496]); | ||||
| @@ -99,27 +99,25 @@ CBonusSelection::CBonusSelection() | ||||
| 		difficultyIcons[b] = std::make_shared<CAnimImage>("GSPBUT" + std::to_string(b + 3) + ".DEF", 0, 0, 709, 455); | ||||
| 	} | ||||
|  | ||||
| 	if(getCampaign()->header.difficultyChoosenByPlayer) | ||||
| 	if(getCampaign()->getHeader().difficultyChoosenByPlayer) | ||||
| 	{ | ||||
| 		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)); | ||||
| 	} | ||||
|  | ||||
| 	for(int g = 0; g < getCampaign()->scenarios.size(); ++g) | ||||
| 	for(auto scenarioID : getCampaign()->allScenarios()) | ||||
| 	{ | ||||
| 		auto scenarioID = static_cast<CampaignScenarioID>(g); | ||||
|  | ||||
| 		if(getCampaign()->conquerable(scenarioID)) | ||||
| 			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)); | ||||
| 		if(getCampaign()->isAvailable(scenarioID)) | ||||
| 			regions.push_back(std::make_shared<CRegion>(scenarioID, true, true, getCampaign()->getHeader().campaignRegions)); | ||||
| 		else if(getCampaign()->isConquered(scenarioID)) //display as striped | ||||
| 			regions.push_back(std::make_shared<CRegion>(scenarioID, false, false, getCampaign()->getHeader().campaignRegions)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CBonusSelection::createBonusesIcons() | ||||
| { | ||||
| 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; | ||||
| 	const CampaignScenario & scenario = getCampaign()->scenarios[CSH->campaignMap]; | ||||
| 	const CampaignScenario & scenario = getCampaign()->scenario(CSH->campaignMap); | ||||
| 	const std::vector<CampaignBonus> & bonDescs = scenario.travelOptions.bonusesToChoose; | ||||
| 	groupBonuses = std::make_shared<CToggleGroup>(std::bind(&IServerAPI::setCampaignBonus, CSH, _1)); | ||||
|  | ||||
| @@ -172,7 +170,7 @@ void CBonusSelection::createBonusesIcons() | ||||
| 			assert(faction != -1); | ||||
|  | ||||
| 			BuildingID buildID; | ||||
| 			if(getCampaign()->header.version == CampaignVersion::VCMI) | ||||
| 			if(getCampaign()->getHeader().version == CampaignVersion::VCMI) | ||||
| 				buildID = BuildingID(bonDescs[i].info1); | ||||
| 			else | ||||
| 				buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>()); | ||||
| @@ -272,13 +270,13 @@ void CBonusSelection::createBonusesIcons() | ||||
| 		} | ||||
| 		case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO: | ||||
| 		{ | ||||
| 			auto superhero = getCampaign()->scenarios[static_cast<CampaignScenarioID>(bonDescs[i].info2)].strongestHero(PlayerColor(bonDescs[i].info1)); | ||||
| 			auto superhero = getCampaign()->strongestHero(static_cast<CampaignScenarioID>(bonDescs[i].info2), PlayerColor(bonDescs[i].info1)); | ||||
| 			if(!superhero) | ||||
| 				logGlobal->warn("No superhero! How could it be transferred?"); | ||||
| 			picNumber = superhero ? superhero->portrait : 0; | ||||
| 			desc = CGI->generaltexth->allTexts[719]; | ||||
|  | ||||
| 			boost::algorithm::replace_first(desc, "%s", getCampaign()->scenarios[static_cast<CampaignScenarioID>(bonDescs[i].info2)].scenarioName); | ||||
| 			boost::algorithm::replace_first(desc, "%s", getCampaign()->scenario(static_cast<CampaignScenarioID>(bonDescs[i].info2)).scenarioName); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| @@ -313,10 +311,8 @@ void CBonusSelection::createBonusesIcons() | ||||
| 		groupBonuses->addToggle(i, bonusButton); | ||||
| 	} | ||||
|  | ||||
| 	if(vstd::contains(getCampaign()->chosenCampaignBonuses, CSH->campaignMap)) | ||||
| 	{ | ||||
| 		groupBonuses->setSelected(getCampaign()->chosenCampaignBonuses[CSH->campaignMap]); | ||||
| 	} | ||||
| 	if(getCampaign()->getBonusID(CSH->campaignMap)) | ||||
| 		groupBonuses->setSelected(*getCampaign()->getBonusID(CSH->campaignMap)); | ||||
| } | ||||
|  | ||||
| void CBonusSelection::updateAfterStateChange() | ||||
| @@ -325,7 +321,7 @@ void CBonusSelection::updateAfterStateChange() | ||||
| 	{ | ||||
| 		buttonRestart->disable(); | ||||
| 		buttonStart->enable(); | ||||
| 		if(!getCampaign()->mapsConquered.empty()) | ||||
| 		if(!getCampaign()->conqueredScenarios().empty()) | ||||
| 			buttonBack->block(true); | ||||
| 		else | ||||
| 			buttonBack->block(false); | ||||
| @@ -342,7 +338,7 @@ void CBonusSelection::updateAfterStateChange() | ||||
| 	} | ||||
| 	if(CSH->campaignBonus == -1) | ||||
| 	{ | ||||
| 		buttonStart->block(getCampaign()->scenarios[CSH->campaignMap].travelOptions.bonusesToChoose.size()); | ||||
| 		buttonStart->block(getCampaign()->scenario(CSH->campaignMap).travelOptions.bonusesToChoose.size()); | ||||
| 	} | ||||
| 	else if(buttonStart->isBlocked()) | ||||
| 	{ | ||||
| @@ -398,7 +394,7 @@ void CBonusSelection::startMap() | ||||
| 			CSH->sendStartGame(); | ||||
| 		}; | ||||
|  | ||||
| 		const CampaignScenario & scenario = getCampaign()->scenarios[CSH->campaignMap]; | ||||
| 		const CampaignScenario & scenario = getCampaign()->scenario(CSH->campaignMap); | ||||
| 		if(scenario.prolog.hasPrologEpilog) | ||||
| 		{ | ||||
| 			GH.windows().createAndPushWindow<CPrologEpilogVideo>(scenario.prolog, exitCb); | ||||
| @@ -464,7 +460,7 @@ CBonusSelection::CRegion::CRegion(CampaignScenarioID id, bool accessible, bool s | ||||
| 	pos.y += desc.ypos; | ||||
|  | ||||
| 	std::string prefix = campDsc.campPrefix + desc.infix + "_"; | ||||
| 	std::string suffix = colors[campDsc.colorSuffixLength - 1][CSH->si->campState->scenarios[idOfMapAndRegion].regionColor]; | ||||
| 	std::string suffix = colors[campDsc.colorSuffixLength - 1][CSH->si->campState->scenario(idOfMapAndRegion).regionColor]; | ||||
| 	graphicsNotSelected = std::make_shared<CPicture>(prefix + "En" + suffix + ".BMP"); | ||||
| 	graphicsNotSelected->disable(); | ||||
| 	graphicsSelected = std::make_shared<CPicture>(prefix + "Se" + suffix + ".BMP"); | ||||
| @@ -513,7 +509,7 @@ void CBonusSelection::CRegion::clickLeft(tribool down, bool previousState) | ||||
| void CBonusSelection::CRegion::showPopupWindow() | ||||
| { | ||||
| 	// FIXME: For some reason "down" is only ever contain indeterminate_value | ||||
| 	auto text = CSH->si->campState->scenarios[idOfMapAndRegion].regionText; | ||||
| 	auto text = CSH->si->campState->scenario(idOfMapAndRegion).regionText; | ||||
| 	if(!graphicsNotSelected->getSurface()->isTransparent(GH.getCursorPosition() - pos.topLeft()) && text.size()) | ||||
| 	{ | ||||
| 		CRClickPopup::createAndPush(text); | ||||
|   | ||||
| @@ -923,7 +923,7 @@ void CCastleBuildings::enterMagesGuild() | ||||
| 		const StartInfo *si = LOCPLINT->cb->getStartInfo(); | ||||
| 		// it would be nice to find a way to move this hack to config/mapOverrides.json | ||||
| 		if(si && si->campState && si->campState &&                // We're in campaign, | ||||
| 			(si->campState->header.filename == "DATA/YOG.H3C") && // which is "Birth of a Barbarian", | ||||
| 			(si->campState->getHeader().filename == "DATA/YOG.H3C") && // which is "Birth of a Barbarian", | ||||
| 			(hero->subID == 45))                                        // and the hero is Yog (based on Solmyr) | ||||
| 		{ | ||||
| 			// "Yog has given up magic in all its forms..." | ||||
|   | ||||
| @@ -1076,21 +1076,7 @@ void PlayerEndsGame::applyGs(CGameState * gs) const | ||||
| 					// keep all heroes from the winning player | ||||
| 					crossoverHeroes.push_back(hero); | ||||
| 				} | ||||
| 				else if (vstd::contains(gs->scenarioOps->campState->getCurrentScenario().keepHeroes, HeroTypeID(hero->subID))) | ||||
| 				{ | ||||
| 					// keep hero whether lost or won (like Xeron in AB campaign) | ||||
| 					crossoverHeroes.push_back(hero); | ||||
| 			} | ||||
| 			} | ||||
| 			// keep lost heroes which are in heroes pool | ||||
| 			for (auto & heroPair : gs->hpool.heroesPool) | ||||
| 			{ | ||||
| 				if (vstd::contains(gs->scenarioOps->campState->getCurrentScenario().keepHeroes, HeroTypeID(heroPair.first))) | ||||
| 				{ | ||||
| 					crossoverHeroes.push_back(heroPair.second.get()); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			gs->scenarioOps->campState->setCurrentMapAsConquered(crossoverHeroes); | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -62,8 +62,8 @@ PlayerSettings * StartInfo::getPlayersSettings(const ui8 connectedPlayerId) | ||||
|  | ||||
| std::string StartInfo::getCampaignName() const | ||||
| { | ||||
| 	if(campState->header.name.length()) | ||||
| 		return campState->header.name; | ||||
| 	if(campState->getHeader().name.empty()) | ||||
| 		return campState->getHeader().name; | ||||
| 	else | ||||
| 		return VLC->generaltexth->allTexts[508]; | ||||
| } | ||||
|   | ||||
| @@ -111,14 +111,6 @@ std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string & | ||||
| 		scenarioID++; | ||||
| 	} | ||||
|  | ||||
| 	for(int i = 0; i < ret->scenarios.size(); i++) | ||||
| 	{ | ||||
| 		auto scenarioID = static_cast<CampaignScenarioID>(i); | ||||
|  | ||||
| 		if(vstd::contains(ret->mapPieces, scenarioID)) //not all maps must be present in a campaign | ||||
| 			ret->mapsRemaining.push_back(scenarioID); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @@ -188,7 +180,6 @@ CampaignScenario CampaignHandler::readScenarioFromJson(JsonNode & reader) | ||||
| 	}; | ||||
|  | ||||
| 	CampaignScenario ret; | ||||
| 	ret.conquered = false; | ||||
| 	ret.mapName = reader["map"].String(); | ||||
| 	for(auto & g : reader["preconditions"].Vector()) | ||||
| 		ret.preconditionRegions.insert(static_cast<CampaignScenarioID>(g.Integer())); | ||||
| @@ -428,7 +419,6 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader | ||||
| 	}; | ||||
|  | ||||
| 	CampaignScenario ret; | ||||
| 	ret.conquered = false; | ||||
| 	ret.mapName = reader.readBaseString(); | ||||
| 	reader.readUInt32(); //packedMapSize - not used | ||||
| 	if(header.numberOfScenarios > 8) //unholy alliance | ||||
|   | ||||
| @@ -83,7 +83,12 @@ void CampaignHeader::loadLegacyData(ui8 campId) | ||||
| 	numberOfScenarios = VLC->generaltexth->getCampaignLength(campId); | ||||
| } | ||||
|  | ||||
| bool CampaignState::conquerable(CampaignScenarioID whichScenario) const | ||||
| bool CampaignState::isConquered(CampaignScenarioID whichScenario) const | ||||
| { | ||||
| 	return vstd::contains(mapsConquered, whichScenario); | ||||
| } | ||||
|  | ||||
| bool CampaignState::isAvailable(CampaignScenarioID whichScenario) const | ||||
| { | ||||
| 	//check for void scenraio | ||||
| 	if (!scenarios.at(whichScenario).isNotVoid()) | ||||
| @@ -91,14 +96,14 @@ bool CampaignState::conquerable(CampaignScenarioID whichScenario) const | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (scenarios.at(whichScenario).conquered) | ||||
| 	if (vstd::contains(mapsConquered, whichScenario)) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 	//check preconditioned regions | ||||
| 	for (auto const & it : scenarios.at(whichScenario).preconditionRegions) | ||||
| 	{ | ||||
| 		if (!scenarios.at(it).conquered) | ||||
| 		if (!vstd::contains(mapsConquered, it)) | ||||
| 			return false; | ||||
| 	} | ||||
| 	return true; | ||||
| @@ -109,18 +114,18 @@ bool CampaignScenario::isNotVoid() const | ||||
| 	return !mapName.empty(); | ||||
| } | ||||
|  | ||||
| const CGHeroInstance * CampaignScenario::strongestHero(const PlayerColor & owner) | ||||
| const CGHeroInstance * CampaignState::strongestHero(CampaignScenarioID scenarioId, const PlayerColor & owner) const | ||||
| { | ||||
| 	std::function<bool(JsonNode & node)> isOwned = [owner](JsonNode & node) | ||||
| 	std::function<bool(const JsonNode & node)> isOwned = [owner](const JsonNode & node) | ||||
| 	{ | ||||
| 		auto * h = CampaignState::crossoverDeserialize(node); | ||||
| 		bool result = h->tempOwner == owner; | ||||
| 		vstd::clear_pointer(h); | ||||
| 		return result; | ||||
| 	}; | ||||
| 	auto ownedHeroes = crossoverHeroes | boost::adaptors::filtered(isOwned); | ||||
| 	auto ownedHeroes = crossover.placedHeroes.at(scenarioId) | boost::adaptors::filtered(isOwned); | ||||
|  | ||||
| 	auto i = vstd::maxElementByFun(ownedHeroes, [](JsonNode & node) | ||||
| 	auto i = vstd::maxElementByFun(ownedHeroes, [](const JsonNode & node) | ||||
| 	{ | ||||
| 		auto * h = CampaignState::crossoverDeserialize(node); | ||||
| 		double result = h->getHeroStrength(); | ||||
| @@ -130,41 +135,43 @@ const CGHeroInstance * CampaignScenario::strongestHero(const PlayerColor & owner | ||||
| 	return i == ownedHeroes.end() ? nullptr : CampaignState::crossoverDeserialize(*i); | ||||
| } | ||||
|  | ||||
| std::vector<CGHeroInstance *> CampaignScenario::getLostCrossoverHeroes() | ||||
| std::vector<CGHeroInstance *> CampaignState::getLostCrossoverHeroes(CampaignScenarioID scenarioId) const | ||||
| { | ||||
| 	std::vector<CGHeroInstance *> lostCrossoverHeroes; | ||||
| 	if(conquered) | ||||
| 	{ | ||||
| 		for(auto node2 : placedCrossoverHeroes) | ||||
|  | ||||
| 	for(auto node2 :  crossover.placedHeroes.at(scenarioId)) | ||||
| 	{ | ||||
| 		auto * hero = CampaignState::crossoverDeserialize(node2); | ||||
| 			auto it = range::find_if(crossoverHeroes, [hero](JsonNode node) | ||||
| 		auto it = range::find_if(crossover.crossoverHeroes.at(scenarioId), [hero](JsonNode node) | ||||
| 		{ | ||||
| 				  auto * h = CampaignState::crossoverDeserialize(node); | ||||
| 				  bool result = hero->subID == h->subID; | ||||
| 				  vstd::clear_pointer(h); | ||||
| 				  return result; | ||||
| 	}); | ||||
| 			if(it == crossoverHeroes.end()) | ||||
| 		if(it == crossover.crossoverHeroes.at(scenarioId).end()) | ||||
| 		{ | ||||
| 			lostCrossoverHeroes.push_back(hero); | ||||
| 		} | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	return lostCrossoverHeroes; | ||||
| } | ||||
|  | ||||
| std::vector<JsonNode> CampaignState::getCrossoverHeroes(CampaignScenarioID scenarioId) const | ||||
| { | ||||
| 	return crossover.crossoverHeroes.at(scenarioId); | ||||
| } | ||||
|  | ||||
| void CampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes) | ||||
| { | ||||
| 	scenarios.at(*currentMap).crossoverHeroes.clear(); | ||||
| 	crossover.crossoverHeroes[*currentMap].clear(); | ||||
| 	for(CGHeroInstance * hero : heroes) | ||||
| 	{ | ||||
| 		scenarios.at(*currentMap).crossoverHeroes.push_back(crossoverSerialize(hero)); | ||||
| 		crossover.crossoverHeroes[*currentMap].push_back(crossoverSerialize(hero)); | ||||
| 	} | ||||
|  | ||||
| 	mapsConquered.push_back(*currentMap); | ||||
| 	mapsRemaining -= *currentMap; | ||||
| 	scenarios.at(*currentMap).conquered = true; | ||||
| } | ||||
|  | ||||
| std::optional<CampaignBonus> CampaignState::getBonusForCurrentMap() const | ||||
| @@ -183,9 +190,12 @@ const CampaignScenario & CampaignState::getCurrentScenario() const | ||||
| 	return scenarios.at(*currentMap); | ||||
| } | ||||
|  | ||||
| CampaignScenario & CampaignState::getCurrentScenario() | ||||
| std::optional<ui8> CampaignState::getBonusID(CampaignScenarioID & which) const | ||||
| { | ||||
| 	return scenarios.at(*currentMap); | ||||
| 	if (!chosenCampaignBonuses.count(which)) | ||||
| 		return std::nullopt; | ||||
|  | ||||
| 	return chosenCampaignBonuses.at(which); | ||||
| } | ||||
|  | ||||
| ui8 CampaignState::currentBonusID() const | ||||
| @@ -242,11 +252,74 @@ JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero) | ||||
| 	return node; | ||||
| } | ||||
|  | ||||
| CGHeroInstance * CampaignState::crossoverDeserialize(JsonNode & node) | ||||
| CGHeroInstance * CampaignState::crossoverDeserialize(const JsonNode & node) | ||||
| { | ||||
| 	JsonDeserializer handler(nullptr, node); | ||||
| 	JsonDeserializer handler(nullptr, const_cast<JsonNode&>(node)); | ||||
| 	auto * hero = new CGHeroInstance(); | ||||
| 	hero->ID = Obj::HERO; | ||||
| 	hero->serializeJsonOptions(handler); | ||||
| 	return hero; | ||||
| } | ||||
|  | ||||
| void CampaignState::setCurrentMap(CampaignScenarioID which) | ||||
| { | ||||
| 	assert(scenarios.count(which)); | ||||
| 	assert(scenarios.at(which).isNotVoid()); | ||||
|  | ||||
| 	currentMap = which; | ||||
| } | ||||
|  | ||||
| void CampaignState::setCurrentMapBonus(ui8 which) | ||||
| { | ||||
| 	chosenCampaignBonuses[*currentMap] = which; | ||||
| } | ||||
|  | ||||
| std::optional<CampaignScenarioID> CampaignState::currentScenario() const | ||||
| { | ||||
| 	return currentMap; | ||||
| } | ||||
|  | ||||
| std::optional<CampaignScenarioID> CampaignState::lastScenario() const | ||||
| { | ||||
| 	if (mapsConquered.empty()) | ||||
| 		return std::nullopt; | ||||
| 	return mapsConquered.back(); | ||||
| } | ||||
|  | ||||
| std::set<CampaignScenarioID> CampaignState::conqueredScenarios() const | ||||
| { | ||||
| 	std::set<CampaignScenarioID> result; | ||||
| 	result.insert(mapsConquered.begin(), mapsConquered.end()); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| std::set<CampaignScenarioID> CampaignState::allScenarios() const | ||||
| { | ||||
| 	std::set<CampaignScenarioID> result; | ||||
|  | ||||
| 	for (auto const & entry : scenarios) | ||||
| 	{ | ||||
| 		if (entry.second.isNotVoid()) | ||||
| 			result.insert(entry.first); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| const CampaignScenario & CampaignState::scenario(CampaignScenarioID which) const | ||||
| { | ||||
| 	assert(scenarios.count(which)); | ||||
| 	assert(scenarios.at(which).isNotVoid()); | ||||
|  | ||||
| 	return scenarios.at(which); | ||||
| } | ||||
|  | ||||
| bool CampaignState::isCampaignFinished() const | ||||
| { | ||||
| 	return conqueredScenarios() == allScenarios(); | ||||
| } | ||||
|  | ||||
| const CampaignHeader & CampaignState::getHeader() const | ||||
| { | ||||
| 	return header; | ||||
| } | ||||
|   | ||||
| @@ -156,22 +156,15 @@ public: | ||||
| 	std::set<CampaignScenarioID> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c) | ||||
| 	ui8 regionColor = 0; | ||||
| 	ui8 difficulty = 0; | ||||
| 	bool conquered = false; | ||||
|  | ||||
| 	std::string regionText; | ||||
| 	CampaignScenarioPrologEpilog prolog; | ||||
| 	CampaignScenarioPrologEpilog epilog; | ||||
|  | ||||
| 	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<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 | ||||
|  | ||||
| 	void loadPreconditionRegions(ui32 regions); | ||||
| 	bool isNotVoid() const; | ||||
| 	// FIXME: due to usage of JsonNode I can't make these methods const | ||||
| 	const CGHeroInstance * strongestHero(const PlayerColor & owner); | ||||
| 	std::vector<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int formatVersion) | ||||
| 	{ | ||||
| @@ -180,44 +173,85 @@ public: | ||||
| 		h & preconditionRegions; | ||||
| 		h & regionColor; | ||||
| 		h & difficulty; | ||||
| 		h & conquered; | ||||
| 		h & regionText; | ||||
| 		h & prolog; | ||||
| 		h & epilog; | ||||
| 		h & travelOptions; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CampaignHeroes | ||||
| { | ||||
| 	using ScenarioHeroesList = std::vector<JsonNode>; | ||||
| 	using CampaignHeroesList = std::map<CampaignScenarioID, ScenarioHeroesList>; | ||||
|  | ||||
| 	CampaignHeroesList crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished | ||||
| 	CampaignHeroesList placedHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int formatVersion) | ||||
| 	{ | ||||
| 		h & crossoverHeroes; | ||||
| 		h & placedCrossoverHeroes; | ||||
| 		h & keepHeroes; | ||||
| 		h & placedHeroes; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CampaignState | ||||
| { | ||||
| public: | ||||
| 	CampaignHeader header; | ||||
| 	friend class CampaignHandler; | ||||
|  | ||||
| 	/// List of all maps completed by player, in order of their completion | ||||
| 	std::vector<CampaignScenarioID> mapsConquered; | ||||
|  | ||||
| 	std::map<CampaignScenarioID, CampaignScenario> scenarios; | ||||
| 	std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data | ||||
|  | ||||
| 	std::vector<CampaignScenarioID> mapsConquered; | ||||
| 	std::vector<CampaignScenarioID> mapsRemaining; | ||||
| 	std::map<CampaignScenarioID, ui8> chosenCampaignBonuses; | ||||
| 	std::optional<CampaignScenarioID> currentMap; | ||||
|  | ||||
| 	std::map<CampaignScenarioID, ui8> chosenCampaignBonuses; | ||||
| 	CampaignHeader header; | ||||
| 	CampaignHeroes crossover; | ||||
|  | ||||
| public: | ||||
| 	std::optional<CampaignScenarioID> lastScenario() const; | ||||
| 	std::optional<CampaignScenarioID> currentScenario() const; | ||||
| 	std::set<CampaignScenarioID> allScenarios() const; | ||||
| 	std::set<CampaignScenarioID> conqueredScenarios() const; | ||||
|  | ||||
| 	const CampaignScenario & scenario(CampaignScenarioID which) const; | ||||
|  | ||||
| 	std::optional<CampaignBonus> getBonusForCurrentMap() const; | ||||
| 	const CampaignScenario & getCurrentScenario() const; | ||||
|  | ||||
| 	std::optional<ui8> getBonusID(CampaignScenarioID & which) const; | ||||
| 	ui8 currentBonusID() const; | ||||
| 	bool conquerable(CampaignScenarioID whichScenario) const; | ||||
|  | ||||
| 	/// Returns true if selected scenario can be selected and started by player | ||||
| 	bool isAvailable(CampaignScenarioID whichScenario) const; | ||||
|  | ||||
| 	/// Returns true if selected scenario has been already completed by player | ||||
| 	bool isConquered(CampaignScenarioID whichScenario) const; | ||||
|  | ||||
| 	/// Returns true if all available scenarios have been completed and campaign is finished | ||||
| 	bool isCampaignFinished() const; | ||||
|  | ||||
| 	const CampaignHeader & getHeader() const; | ||||
|  | ||||
| 	std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const; | ||||
| 	std::unique_ptr<CMapHeader> getMapHeader(CampaignScenarioID scenarioId) const; | ||||
| 	std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const; | ||||
|  | ||||
| 	CampaignScenario & getCurrentScenario(); | ||||
| 	void setCurrentMap(CampaignScenarioID which); | ||||
| 	void setCurrentMapBonus(ui8 which); | ||||
| 	void setCurrentMapAsConquered(const std::vector<CGHeroInstance*> & heroes); | ||||
|  | ||||
| 	const CGHeroInstance * strongestHero(CampaignScenarioID scenarioId, const PlayerColor & owner) const; | ||||
|  | ||||
| 	/// returns a list of crossover heroes which started the scenario, but didn't complete it | ||||
| 	std::vector<CGHeroInstance *> getLostCrossoverHeroes(CampaignScenarioID scenarioId) const; | ||||
|  | ||||
| 	std::vector<JsonNode> getCrossoverHeroes(CampaignScenarioID scenarioId) const; | ||||
|  | ||||
| 	static JsonNode crossoverSerialize(CGHeroInstance * hero); | ||||
| 	static CGHeroInstance * crossoverDeserialize(JsonNode & node); | ||||
| 	static CGHeroInstance * crossoverDeserialize(const JsonNode & node); | ||||
|  | ||||
| 	CampaignState() = default; | ||||
|  | ||||
| @@ -225,8 +259,8 @@ public: | ||||
| 	{ | ||||
| 		h & header; | ||||
| 		h & scenarios; | ||||
| 		h & crossover; | ||||
| 		h & mapPieces; | ||||
| 		h & mapsRemaining; | ||||
| 		h & mapsConquered; | ||||
| 		h & currentMap; | ||||
| 		h & chosenCampaignBonuses; | ||||
|   | ||||
| @@ -65,7 +65,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios( | ||||
| 		auto scenarioID = static_cast<CampaignScenarioID>(bonus->info2); | ||||
|  | ||||
| 		std::vector<CGHeroInstance *> heroes; | ||||
| 		for(auto & node : campaignState->scenarios.at(scenarioID).crossoverHeroes) | ||||
| 		for(auto & node : campaignState->getCrossoverHeroes(scenarioID)) | ||||
| 		{ | ||||
| 			auto * h = CampaignState::crossoverDeserialize(node); | ||||
| 			heroes.push_back(h); | ||||
| @@ -76,14 +76,13 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios( | ||||
| 		return crossoverHeroes; | ||||
| 	} | ||||
|  | ||||
| 	if(campaignState->mapsConquered.empty()) | ||||
| 	if(!campaignState->lastScenario()) | ||||
| 		return crossoverHeroes; | ||||
|  | ||||
| 	for(auto mapNr : campaignState->mapsConquered) | ||||
| 	for(auto mapNr : campaignState->conqueredScenarios()) | ||||
| 	{ | ||||
| 		// create a list of deleted heroes | ||||
| 		auto & scenario = campaignState->scenarios[mapNr]; | ||||
| 		auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes(); | ||||
| 		auto lostCrossoverHeroes = campaignState->getLostCrossoverHeroes(mapNr); | ||||
|  | ||||
| 		// remove heroes which didn't reached the end of the scenario, but were available at the start | ||||
| 		for(auto * hero : lostCrossoverHeroes) | ||||
| @@ -96,7 +95,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios( | ||||
| 		} | ||||
|  | ||||
| 		// now add heroes which completed the scenario | ||||
| 		for(auto node : scenario.crossoverHeroes) | ||||
| 		for(auto node : campaignState->getCrossoverHeroes(mapNr)) | ||||
| 		{ | ||||
| 			auto * hero = CampaignState::crossoverDeserialize(node); | ||||
| 			// add new heroes and replace old heroes with newer ones | ||||
| @@ -116,7 +115,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios( | ||||
| 				crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero); | ||||
| 			} | ||||
|  | ||||
| 			if(mapNr == campaignState->mapsConquered.back()) | ||||
| 			if(mapNr == campaignState->lastScenario()) | ||||
| 			{ | ||||
| 				crossoverHeroes.heroesFromPreviousScenario.push_back(hero); | ||||
| 			} | ||||
| @@ -392,7 +391,7 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero) | ||||
| 					continue; | ||||
| 				} | ||||
| 				auto bb = std::make_shared<Bonus>( | ||||
| 					BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, static_cast<int>(*gameState->scenarioOps->campState->currentMap), g | ||||
| 					BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, static_cast<int>(*gameState->scenarioOps->campState->currentScenario()), g | ||||
| 				); | ||||
| 				hero->addNewBonus(bb); | ||||
| 			} | ||||
| @@ -445,8 +444,6 @@ void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHer | ||||
| 		gameState->map->instanceNames[heroToPlace->instanceName] = heroToPlace; | ||||
|  | ||||
| 		delete heroPlaceholder; | ||||
|  | ||||
| 		gameState->scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CampaignState::crossoverSerialize(heroToPlace)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -628,7 +625,7 @@ void CGameStateCampaign::initTowns() | ||||
| 					gameState->map->towns[g]->pos == pi.posOfMainTown) | ||||
| 				{ | ||||
| 					BuildingID buildingId; | ||||
| 					if(gameState->scenarioOps->campState->header.version == CampaignVersion::VCMI) | ||||
| 					if(gameState->scenarioOps->campState->getHeader().version == CampaignVersion::VCMI) | ||||
| 						buildingId = BuildingID(chosenBonus->info1); | ||||
| 					else | ||||
| 						buildingId = CBuildingHandler::campToERMU(chosenBonus->info1, gameState->map->towns[g]->subID, gameState->map->towns[g]->builtBuildings); | ||||
|   | ||||
| @@ -296,8 +296,8 @@ bool CVCMIServer::prepareToStartGame() | ||||
| 	{ | ||||
| 	case StartInfo::CAMPAIGN: | ||||
| 		logNetwork->info("Preparing to start new campaign"); | ||||
| 		si->campState->currentMap = std::make_optional(campaignMap); | ||||
| 		si->campState->chosenCampaignBonuses[campaignMap] = campaignBonus; | ||||
| 		si->campState->setCurrentMap(campaignMap); | ||||
| 		si->campState->setCurrentMapBonus(campaignBonus); | ||||
| 		gh->init(si.get()); | ||||
| 		break; | ||||
|  | ||||
| @@ -668,7 +668,7 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo, | ||||
| 		si = CMemorySerializer::deepCopy(*mi->scenarioOptionsOfSave); | ||||
| 		si->mode = StartInfo::LOAD_GAME; | ||||
| 		if(si->campState) | ||||
| 			campaignMap = si->campState->currentMap.value(); | ||||
| 			campaignMap = si->campState->currentScenario().value(); | ||||
|  | ||||
| 		for(auto & ps : si->playerInfos) | ||||
| 		{ | ||||
| @@ -873,7 +873,7 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir) | ||||
| void CVCMIServer::setCampaignMap(CampaignScenarioID mapId) | ||||
| { | ||||
| 	campaignMap = mapId; | ||||
| 	si->difficulty = si->campState->scenarios[mapId].difficulty; | ||||
| 	si->difficulty = si->campState->scenario(mapId).difficulty; | ||||
| 	campaignBonus = -1; | ||||
| 	updateStartInfoOnMapChange(si->campState->getMapInfo(mapId)); | ||||
| } | ||||
| @@ -882,7 +882,7 @@ void CVCMIServer::setCampaignBonus(int bonusId) | ||||
| { | ||||
| 	campaignBonus = bonusId; | ||||
|  | ||||
| 	const CampaignScenario & scenario = si->campState->scenarios[campaignMap]; | ||||
| 	const CampaignScenario & scenario = si->campState->scenario(campaignMap); | ||||
| 	const std::vector<CampaignBonus> & bonDescs = scenario.travelOptions.bonusesToChoose; | ||||
| 	if(bonDescs[bonusId].type == CampaignBonusType::HERO) | ||||
| 	{ | ||||
|   | ||||
| @@ -211,18 +211,18 @@ void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack) | ||||
|  | ||||
| void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack) | ||||
| { | ||||
| 	srv.si->mapname = pack.ourCampaign->header.filename; | ||||
| 	srv.si->mapname = pack.ourCampaign->getHeader().filename; | ||||
| 	srv.si->mode = StartInfo::CAMPAIGN; | ||||
| 	srv.si->campState = pack.ourCampaign; | ||||
| 	srv.si->turnTime = 0; | ||||
| 	bool isCurrentMapConquerable = pack.ourCampaign->currentMap && pack.ourCampaign->conquerable(*pack.ourCampaign->currentMap); | ||||
| 	for(int i = 0; i < pack.ourCampaign->scenarios.size(); i++) | ||||
| 	{ | ||||
| 		auto scenarioID = static_cast<CampaignScenarioID>(i); | ||||
|  | ||||
| 		if(pack.ourCampaign->conquerable(scenarioID)) | ||||
| 	bool isCurrentMapConquerable = pack.ourCampaign->currentScenario() && pack.ourCampaign->isAvailable(*pack.ourCampaign->currentScenario()); | ||||
|  | ||||
| 	for(auto scenarioID : pack.ourCampaign->allScenarios()) | ||||
| 	{ | ||||
| 			if(!isCurrentMapConquerable || (isCurrentMapConquerable && scenarioID == *pack.ourCampaign->currentMap)) | ||||
| 		if(pack.ourCampaign->isAvailable(scenarioID)) | ||||
| 		{ | ||||
| 			if(!isCurrentMapConquerable || (isCurrentMapConquerable && scenarioID == *pack.ourCampaign->currentScenario())) | ||||
| 			{ | ||||
| 				srv.setCampaignMap(scenarioID); | ||||
| 			} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user