浏览代码

Implemented transfer of artifacts held by non-transferred heroes

Ivan Savenko 1 年之前
父节点
当前提交
5c5fb523a4
共有 2 个文件被更改,包括 107 次插入43 次删除
  1. 98 40
      lib/gameState/CGameStateCampaign.cpp
  2. 9 3
      lib/gameState/CGameStateCampaign.h

+ 98 - 40
lib/gameState/CGameStateCampaign.cpp

@@ -60,30 +60,22 @@ std::optional<CampaignScenarioID> CGameStateCampaign::getHeroesSourceScenario()
 	return campaignState->lastScenario();
 }
 
-void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions)
+void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & travelOptions)
 {
-	// create heroes list for convenience iterating
-	std::vector<CGHeroInstance *> crossoverHeroes;
-	crossoverHeroes.reserve(campaignHeroReplacements.size());
-	for(auto & campaignHeroReplacement : campaignHeroReplacements)
-	{
-		crossoverHeroes.push_back(campaignHeroReplacement.hero);
-	}
-
 	// TODO this logic (what should be kept) should be part of CScenarioTravel and be exposed via some clean set of methods
 	if(!travelOptions.whatHeroKeeps.experience)
 	{
 		//trimming experience
-		for(CGHeroInstance * cgh : crossoverHeroes)
+		for(auto & hero : campaignHeroReplacements)
 		{
-			cgh->initExp(gameState->getRandomGenerator());
+			hero.hero->initExp(gameState->getRandomGenerator());
 		}
 	}
 
 	if(!travelOptions.whatHeroKeeps.primarySkills)
 	{
 		//trimming prim skills
-		for(CGHeroInstance * cgh : crossoverHeroes)
+		for(auto & hero : campaignHeroReplacements)
 		{
 			for(auto g = PrimarySkill::BEGIN; g < PrimarySkill::END; ++g)
 			{
@@ -91,7 +83,7 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
 					.And(Selector::subtype()(BonusSubtypeID(g)))
 					.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
 
-				cgh->getLocalBonus(sel)->val = cgh->type->heroClass->primarySkillInitial[g.getNum()];
+				hero.hero->getLocalBonus(sel)->val = hero.hero->type->heroClass->primarySkillInitial[g.getNum()];
 			}
 		}
 	}
@@ -99,32 +91,32 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
 	if(!travelOptions.whatHeroKeeps.secondarySkills)
 	{
 		//trimming sec skills
-		for(CGHeroInstance * cgh : crossoverHeroes)
+		for(auto & hero : campaignHeroReplacements)
 		{
-			cgh->secSkills = cgh->type->secSkillsInit;
-			cgh->recreateSecondarySkillsBonuses();
+			hero.hero->secSkills = hero.hero->type->secSkillsInit;
+			hero.hero->recreateSecondarySkillsBonuses();
 		}
 	}
 
 	if(!travelOptions.whatHeroKeeps.spells)
 	{
-		for(CGHeroInstance * cgh : crossoverHeroes)
+		for(auto & hero : campaignHeroReplacements)
 		{
-			cgh->removeSpellbook();
+			hero.hero->removeSpellbook();
 		}
 	}
 
 	if(!travelOptions.whatHeroKeeps.artifacts)
 	{
 		//trimming artifacts
-		for(CGHeroInstance * hero : crossoverHeroes)
+		for(auto & hero : campaignHeroReplacements)
 		{
 			const auto & checkAndRemoveArtifact = [&](const ArtifactPosition & artifactPosition)
 			{
 				if(artifactPosition == ArtifactPosition::SPELLBOOK)
 					return; // do not handle spellbook this way
 
-				const ArtSlotInfo *info = hero->getSlot(artifactPosition);
+				const ArtSlotInfo *info = hero.hero->getSlot(artifactPosition);
 				if(!info)
 					return;
 
@@ -135,24 +127,27 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
 
 				bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
 
-				ArtifactLocation al(hero->id, artifactPosition);
-				if(!takeable && !hero->getSlot(al.slot)->locked)  //don't try removing locked artifacts -> it crashes #1719
-					hero->getArt(al.slot)->removeFrom(*hero, al.slot);
+				if (takeable)
+					hero.transferrableArtifacts.push_back(artifactPosition);
+
+				ArtifactLocation al(hero.hero->id, artifactPosition);
+				if(!takeable && !hero.hero->getSlot(al.slot)->locked)  //don't try removing locked artifacts -> it crashes #1719
+					hero.hero->getArt(al.slot)->removeFrom(*hero.hero, al.slot);
 			};
 
 			// process on copy - removal of artifact will invalidate container
-			auto artifactsWorn = hero->artifactsWorn;
+			auto artifactsWorn = hero.hero->artifactsWorn;
 			for(const auto & art : artifactsWorn)
 				checkAndRemoveArtifact(art.first);
 
 			// process in reverse - removal of artifact will shift all artifacts after this one
-			for(int slotNumber = hero->artifactsInBackpack.size() - 1; slotNumber >= 0; slotNumber--)
+			for(int slotNumber = hero.hero->artifactsInBackpack.size() - 1; slotNumber >= 0; slotNumber--)
 				checkAndRemoveArtifact(ArtifactPosition::BACKPACK_START + slotNumber);
 		}
 	}
 
 	//trimming creatures
-	for(CGHeroInstance * cgh : crossoverHeroes)
+	for(auto & hero : campaignHeroReplacements)
 	{
 		auto shouldSlotBeErased = [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
 		{
@@ -160,16 +155,16 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroR
 			return !travelOptions.monstersKeptByHero.count(crid);
 		};
 
-		auto stacksCopy = cgh->stacks; //copy of the map, so we can iterate iover it and remove stacks
+		auto stacksCopy = hero.hero->stacks; //copy of the map, so we can iterate iover it and remove stacks
 		for(auto &slotPair : stacksCopy)
 			if(shouldSlotBeErased(slotPair))
-				cgh->eraseStack(slotPair.first);
+				hero.hero->eraseStack(slotPair.first);
 	}
 
 	// Removing short-term bonuses
-	for(CGHeroInstance * cgh : crossoverHeroes)
+	for(auto & hero : campaignHeroReplacements)
 	{
-		cgh->removeBonusesRecursive(CSelector(Bonus::OneDay)
+		hero.hero->removeBonusesRecursive(CSelector(Bonus::OneDay)
 			.Or(CSelector(Bonus::OneWeek))
 			.Or(CSelector(Bonus::NTurns))
 			.Or(CSelector(Bonus::NDays))
@@ -201,10 +196,10 @@ void CGameStateCampaign::placeCampaignHeroes()
 	}
 
 	logGlobal->debug("\tGenerate list of hero placeholders");
-	auto campaignHeroReplacements = generateCampaignHeroesToReplace();
+	generateCampaignHeroesToReplace();
 
 	logGlobal->debug("\tPrepare crossover heroes");
-	trimCrossoverHeroesParameters(campaignHeroReplacements, campaignState->scenario(*campaignState->currentScenario()).travelOptions);
+	trimCrossoverHeroesParameters(campaignState->scenario(*campaignState->currentScenario()).travelOptions);
 
 	// remove same heroes on the map which will be added through crossover heroes
 	// INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes
@@ -221,8 +216,9 @@ void CGameStateCampaign::placeCampaignHeroes()
 			heroesToRemove.insert(heroID);
 	}
 
-	for(auto & campaignHeroReplacement : campaignHeroReplacements)
-		heroesToRemove.insert(campaignHeroReplacement.hero->getHeroType());
+	for(auto & replacement : campaignHeroReplacements)
+		if (replacement.heroPlaceholderId.hasValue())
+			heroesToRemove.insert(replacement.hero->getHeroType());
 
 	for(auto & heroID : heroesToRemove)
 	{
@@ -237,7 +233,7 @@ void CGameStateCampaign::placeCampaignHeroes()
 	}
 
 	logGlobal->debug("\tReplace placeholders with heroes");
-	replaceHeroesPlaceholders(campaignHeroReplacements);
+	replaceHeroesPlaceholders();
 
 	// now add removed heroes again with unused type ID
 	for(auto * hero : removedHeroes)
@@ -337,10 +333,13 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
 	}
 }
 
-void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements)
+void CGameStateCampaign::replaceHeroesPlaceholders()
 {
 	for(const auto & campaignHeroReplacement : campaignHeroReplacements)
 	{
+		if (!campaignHeroReplacement.heroPlaceholderId.hasValue())
+			continue;
+
 		auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(gameState->getObjInstance(campaignHeroReplacement.heroPlaceholderId));
 
 		CGHeroInstance *heroToPlace = campaignHeroReplacement.hero;
@@ -364,14 +363,65 @@ void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHer
 	}
 }
 
-std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesToReplace()
+void CGameStateCampaign::transferMissingArtifacts(const CampaignTravel & travelOptions)
+{
+	CGHeroInstance * receiver = nullptr;
+
+	for(auto obj : gameState->map->objects)
+	{
+		if (!obj)
+			continue;
+
+		if (obj->ID != Obj::HERO)
+			continue;
+
+		auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
+
+		if (gameState->getPlayerState(hero->getOwner())->isHuman())
+		{
+			receiver = hero;
+			break;
+		}
+	}
+	assert(receiver);
+
+	for(const auto & campaignHeroReplacement : campaignHeroReplacements)
+	{
+		if (campaignHeroReplacement.heroPlaceholderId.hasValue())
+			continue;
+
+		auto * donorHero = campaignHeroReplacement.hero;
+
+		for (auto const & artLocation : campaignHeroReplacement.transferrableArtifacts)
+		{
+			auto * artifact = donorHero->getArt(artLocation);
+			artifact->removeFrom(*donorHero, artLocation);
+
+			if (receiver)
+			{
+				const auto slot = ArtifactUtils::getArtAnyPosition(receiver, artifact->getTypeId());
+				if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
+					artifact->putAt(*receiver, slot);
+				else
+					logGlobal->error("Cannot transfer artifact - no free slots!");
+			}
+			else
+				logGlobal->error("Cannot transfer artifact - no receiver hero!");
+		}
+
+		delete donorHero;
+	}
+}
+
+void CGameStateCampaign::generateCampaignHeroesToReplace()
 {
 	auto campaignState = gameState->scenarioOps->campState;
 
-	std::vector<CampaignHeroReplacement> campaignHeroReplacements;
 	std::vector<CGHeroPlaceholder *> placeholdersByPower;
 	std::vector<CGHeroPlaceholder *> placeholdersByType;
 
+	campaignHeroReplacements.clear();
+
 	// find all placeholders on map
 	for(auto obj : gameState->map->objects)
 	{
@@ -412,7 +462,7 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
 
 	auto lastScenario = getHeroesSourceScenario();
 
-	if (!placeholdersByPower.empty() && lastScenario)
+	if (lastScenario)
 	{
 		// sort hero placeholders descending power
 		boost::range::sort(placeholdersByPower, [](const CGHeroPlaceholder * a, const CGHeroPlaceholder * b)
@@ -435,8 +485,14 @@ std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesT
 
 			campaignHeroReplacements.emplace_back(hero, placeholder->id);
 		}
+
+		// Add remaining heroes without placeholders - to transfer their artifacts to placed heroes
+		for (;nodeListIter != nodeList.end(); ++nodeListIter)
+		{
+			CGHeroInstance * hero = campaignState->crossoverDeserialize(*nodeListIter, gameState->map);
+			campaignHeroReplacements.emplace_back(hero, ObjectInstanceID::NONE);
+		}
 	}
-	return campaignHeroReplacements;
 }
 
 void CGameStateCampaign::initHeroes()
@@ -493,6 +549,8 @@ void CGameStateCampaign::initHeroes()
 		assert(yog->isCampaignYog());
 		gameState->giveHeroArtifact(yog, ArtifactID::ANGELIC_ALLIANCE);
 	}
+
+	transferMissingArtifacts(campaignState->scenario(*campaignState->currentScenario()).travelOptions);
 }
 
 void CGameStateCampaign::initStartingResources()

+ 9 - 3
lib/gameState/CGameStateCampaign.h

@@ -25,24 +25,30 @@ struct CampaignHeroReplacement
 	CampaignHeroReplacement(CGHeroInstance * hero, const ObjectInstanceID & heroPlaceholderId);
 	CGHeroInstance * hero;
 	ObjectInstanceID heroPlaceholderId;
+	std::vector<ArtifactPosition> transferrableArtifacts;
 };
 
 class CGameStateCampaign
 {
 	CGameState * gameState;
 
+	/// Contains list of heroes that may be available in this scenario
+	/// temporary helper for game initialization, not serialized
+	std::vector<CampaignHeroReplacement> campaignHeroReplacements;
+
 	/// Returns ID of scenario from which hero placeholders should be selected
 	std::optional<CampaignScenarioID> getHeroesSourceScenario() const;
 
 	/// returns heroes and placeholders in where heroes will be put
-	std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace();
+	void generateCampaignHeroesToReplace();
 
 	std::optional<CampaignBonus> currentBonus() const;
 
 	/// Trims hero parameters that should not transfer between scenarios according to travelOptions flags
-	void trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions);
+	void trimCrossoverHeroesParameters(const CampaignTravel & travelOptions);
 
-	void replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements);
+	void replaceHeroesPlaceholders();
+	void transferMissingArtifacts(const CampaignTravel & travelOptions);
 
 	void giveCampaignBonusToHero(CGHeroInstance * hero);