|
@@ -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()
|
|
@@ -485,6 +541,16 @@ void CGameStateCampaign::initHeroes()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ auto campaignState = gameState->scenarioOps->campState;
|
|
|
+ auto * yog = gameState->getUsedHero(HeroTypeID::SOLMYR);
|
|
|
+ if (yog && boost::starts_with(campaignState->getFilename(), "DATA/YOG") && campaignState->currentScenario()->getNum() == 2)
|
|
|
+ {
|
|
|
+ assert(yog->isCampaignYog());
|
|
|
+ gameState->giveHeroArtifact(yog, ArtifactID::ANGELIC_ALLIANCE);
|
|
|
+ }
|
|
|
+
|
|
|
+ transferMissingArtifacts(campaignState->scenario(*campaignState->currentScenario()).travelOptions);
|
|
|
}
|
|
|
|
|
|
void CGameStateCampaign::initStartingResources()
|
|
@@ -598,7 +664,7 @@ bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-std::unique_ptr<CMap> CGameStateCampaign::getCurrentMap() const
|
|
|
+std::unique_ptr<CMap> CGameStateCampaign::getCurrentMap()
|
|
|
{
|
|
|
return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE, gameState->callback);
|
|
|
}
|