|
@@ -10,36 +10,40 @@
|
|
|
#include "StdInc.h"
|
|
|
#include "CGameState.h"
|
|
|
|
|
|
-#include "mapping/CCampaignHandler.h"
|
|
|
-#include "ArtifactUtils.h"
|
|
|
-#include "CArtHandler.h"
|
|
|
-#include "CBuildingHandler.h"
|
|
|
-#include "CGeneralTextHandler.h"
|
|
|
-#include "CTownHandler.h"
|
|
|
-#include "spells/CSpellHandler.h"
|
|
|
-#include "CHeroHandler.h"
|
|
|
-#include "CModHandler.h"
|
|
|
-#include "GameSettings.h"
|
|
|
-#include "TerrainHandler.h"
|
|
|
-#include "CSkillHandler.h"
|
|
|
-#include "mapping/CMap.h"
|
|
|
-#include "mapping/CMapService.h"
|
|
|
-#include "mapObjectConstructors/CObjectClassesHandler.h"
|
|
|
-#include "StartInfo.h"
|
|
|
-#include "NetPacks.h"
|
|
|
-#include "pathfinder/CPathfinder.h"
|
|
|
-#include "pathfinder/PathfinderOptions.h"
|
|
|
-#include "registerTypes/RegisterTypes.h"
|
|
|
-#include "battle/BattleInfo.h"
|
|
|
-#include "JsonNode.h"
|
|
|
-#include "filesystem/Filesystem.h"
|
|
|
-#include "GameConstants.h"
|
|
|
-#include "rmg/CMapGenerator.h"
|
|
|
-#include "CStopWatch.h"
|
|
|
-#include "mapping/CMapEditManager.h"
|
|
|
-#include "serializer/CTypeList.h"
|
|
|
-#include "serializer/CMemorySerializer.h"
|
|
|
-#include "VCMIDirs.h"
|
|
|
+#include "EVictoryLossCheckResult.h"
|
|
|
+#include "InfoAboutArmy.h"
|
|
|
+#include "CGameStateCampaign.h"
|
|
|
+#include "SThievesGuildInfo.h"
|
|
|
+
|
|
|
+#include "../ArtifactUtils.h"
|
|
|
+#include "../CBuildingHandler.h"
|
|
|
+#include "../CGeneralTextHandler.h"
|
|
|
+#include "../CHeroHandler.h"
|
|
|
+#include "../CPlayerState.h"
|
|
|
+#include "../CStopWatch.h"
|
|
|
+#include "../GameSettings.h"
|
|
|
+#include "../StartInfo.h"
|
|
|
+#include "../TerrainHandler.h"
|
|
|
+#include "../VCMIDirs.h"
|
|
|
+#include "../VCMI_Lib.h"
|
|
|
+#include "../battle/BattleInfo.h"
|
|
|
+#include "../campaign/CampaignState.h"
|
|
|
+#include "../filesystem/ResourceID.h"
|
|
|
+#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
|
|
+#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
|
|
+#include "../mapObjectConstructors/DwellingInstanceConstructor.h"
|
|
|
+#include "../mapObjects/CGHeroInstance.h"
|
|
|
+#include "../mapObjects/CGTownInstance.h"
|
|
|
+#include "../mapping/CMap.h"
|
|
|
+#include "../mapping/CMapEditManager.h"
|
|
|
+#include "../mapping/CMapService.h"
|
|
|
+#include "../pathfinder/CPathfinder.h"
|
|
|
+#include "../pathfinder/PathfinderOptions.h"
|
|
|
+#include "../registerTypes/RegisterTypes.h"
|
|
|
+#include "../rmg/CMapGenerator.h"
|
|
|
+#include "../serializer/CMemorySerializer.h"
|
|
|
+#include "../serializer/CTypeList.h"
|
|
|
+#include "../spells/CSpellHandler.h"
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
@@ -173,37 +177,18 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-void CGameState::CrossoverHeroesList::addHeroToBothLists(CGHeroInstance * hero)
|
|
|
-{
|
|
|
- heroesFromPreviousScenario.push_back(hero);
|
|
|
- heroesFromAnyPreviousScenarios.push_back(hero);
|
|
|
-}
|
|
|
-
|
|
|
-void CGameState::CrossoverHeroesList::removeHeroFromBothLists(CGHeroInstance * hero)
|
|
|
-{
|
|
|
- heroesFromPreviousScenario -= hero;
|
|
|
- heroesFromAnyPreviousScenarios -= hero;
|
|
|
-}
|
|
|
-
|
|
|
-CGameState::CampaignHeroReplacement::CampaignHeroReplacement(CGHeroInstance * hero, const ObjectInstanceID & heroPlaceholderId):
|
|
|
- hero(hero),
|
|
|
- heroPlaceholderId(heroPlaceholderId)
|
|
|
-{
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-int CGameState::pickNextHeroType(const PlayerColor & owner)
|
|
|
+HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner)
|
|
|
{
|
|
|
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
|
|
|
if(ps.hero >= 0 && !isUsedHero(HeroTypeID(ps.hero))) //we haven't used selected hero
|
|
|
{
|
|
|
- return ps.hero;
|
|
|
+ return HeroTypeID(ps.hero);
|
|
|
}
|
|
|
|
|
|
return pickUnusedHeroTypeRandomly(owner);
|
|
|
}
|
|
|
|
|
|
-int CGameState::pickUnusedHeroTypeRandomly(const PlayerColor & owner)
|
|
|
+HeroTypeID CGameState::pickUnusedHeroTypeRandomly(const PlayerColor & owner)
|
|
|
{
|
|
|
//list of available heroes for this faction and others
|
|
|
std::vector<HeroTypeID> factionHeroes;
|
|
@@ -221,23 +206,23 @@ int CGameState::pickUnusedHeroTypeRandomly(const PlayerColor & owner)
|
|
|
// select random hero native to "our" faction
|
|
|
if(!factionHeroes.empty())
|
|
|
{
|
|
|
- return RandomGeneratorUtil::nextItem(factionHeroes, getRandomGenerator())->getNum();
|
|
|
+ return *RandomGeneratorUtil::nextItem(factionHeroes, getRandomGenerator());
|
|
|
}
|
|
|
|
|
|
logGlobal->warn("Cannot find free hero of appropriate faction for player %s - trying to get first available...", owner.getStr());
|
|
|
if(!otherHeroes.empty())
|
|
|
{
|
|
|
- return RandomGeneratorUtil::nextItem(otherHeroes, getRandomGenerator())->getNum();
|
|
|
+ return *RandomGeneratorUtil::nextItem(otherHeroes, getRandomGenerator());
|
|
|
}
|
|
|
|
|
|
logGlobal->error("No free allowed heroes!");
|
|
|
auto notAllowedHeroesButStillBetterThanCrash = getUnusedAllowedHeroes(true);
|
|
|
if(!notAllowedHeroesButStillBetterThanCrash.empty())
|
|
|
- return notAllowedHeroesButStillBetterThanCrash.begin()->getNum();
|
|
|
+ return *notAllowedHeroesButStillBetterThanCrash.begin();
|
|
|
|
|
|
logGlobal->error("No free heroes at all!");
|
|
|
assert(0); //current code can't handle this situation
|
|
|
- return -1; // no available heroes at all
|
|
|
+ return HeroTypeID::NONE; // no available heroes at all
|
|
|
}
|
|
|
|
|
|
std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
|
@@ -526,7 +511,9 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
|
|
|
|
|
|
initGlobalBonuses();
|
|
|
initPlayerStates();
|
|
|
- placeCampaignHeroes();
|
|
|
+ if (campaign)
|
|
|
+ campaign->placeCampaignHeroes();
|
|
|
+ removeHeroPlaceholders();
|
|
|
initGrailPosition();
|
|
|
initRandomFactionsForPlayers();
|
|
|
randomizeMapObjects();
|
|
@@ -697,8 +684,8 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
|
|
|
|
|
|
void CGameState::initCampaign()
|
|
|
{
|
|
|
- logGlobal->info("Open campaign map file: %d", scenarioOps->campState->currentMap.value());
|
|
|
- map = scenarioOps->campState->getMap();
|
|
|
+ campaign = std::make_unique<CGameStateCampaign>(this);
|
|
|
+ map = campaign->getCurrentMap().release();
|
|
|
}
|
|
|
|
|
|
void CGameState::checkMapChecksum()
|
|
@@ -834,108 +821,6 @@ void CGameState::initPlayerStates()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CGameState::placeCampaignHeroes()
|
|
|
-{
|
|
|
- if (scenarioOps->campState)
|
|
|
- {
|
|
|
- // place bonus hero
|
|
|
- auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
- bool campaignGiveHero = campaignBonus && campaignBonus->type == CScenarioTravel::STravelBonus::HERO;
|
|
|
-
|
|
|
- if(campaignGiveHero)
|
|
|
- {
|
|
|
- auto playerColor = PlayerColor(campaignBonus->info1);
|
|
|
- auto it = scenarioOps->playerInfos.find(playerColor);
|
|
|
- if(it != scenarioOps->playerInfos.end())
|
|
|
- {
|
|
|
- auto heroTypeId = campaignBonus->info2;
|
|
|
- if(heroTypeId == 0xffff) // random bonus hero
|
|
|
- {
|
|
|
- heroTypeId = pickUnusedHeroTypeRandomly(playerColor);
|
|
|
- }
|
|
|
-
|
|
|
- placeStartingHero(playerColor, HeroTypeID(heroTypeId), map->players[playerColor.getNum()].posOfMainTown);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // replace heroes placeholders
|
|
|
- auto crossoverHeroes = getCrossoverHeroesFromPreviousScenarios();
|
|
|
-
|
|
|
- if(!crossoverHeroes.heroesFromAnyPreviousScenarios.empty())
|
|
|
- {
|
|
|
- logGlobal->debug("\tGenerate list of hero placeholders");
|
|
|
- auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
|
|
|
-
|
|
|
- logGlobal->debug("\tPrepare crossover heroes");
|
|
|
- prepareCrossoverHeroes(campaignHeroReplacements, scenarioOps->campState->getCurrentScenario().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
|
|
|
- // with the same hero type id
|
|
|
- std::vector<CGHeroInstance *> removedHeroes;
|
|
|
-
|
|
|
- for(auto & campaignHeroReplacement : campaignHeroReplacements)
|
|
|
- {
|
|
|
- auto * hero = getUsedHero(HeroTypeID(campaignHeroReplacement.hero->subID));
|
|
|
- if(hero)
|
|
|
- {
|
|
|
- removedHeroes.push_back(hero);
|
|
|
- map->heroesOnMap -= hero;
|
|
|
- map->objects[hero->id.getNum()] = nullptr;
|
|
|
- map->removeBlockVisTiles(hero, true);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- logGlobal->debug("\tReplace placeholders with heroes");
|
|
|
- replaceHeroesPlaceholders(campaignHeroReplacements);
|
|
|
-
|
|
|
- // now add removed heroes again with unused type ID
|
|
|
- for(auto * hero : removedHeroes)
|
|
|
- {
|
|
|
- si32 heroTypeId = 0;
|
|
|
- if(hero->ID == Obj::HERO)
|
|
|
- {
|
|
|
- heroTypeId = pickUnusedHeroTypeRandomly(hero->tempOwner);
|
|
|
- }
|
|
|
- else if(hero->ID == Obj::PRISON)
|
|
|
- {
|
|
|
- auto unusedHeroTypeIds = getUnusedAllowedHeroes();
|
|
|
- if(!unusedHeroTypeIds.empty())
|
|
|
- {
|
|
|
- heroTypeId = (*RandomGeneratorUtil::nextItem(unusedHeroTypeIds, getRandomGenerator())).getNum();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- logGlobal->error("No free hero type ID found to replace prison.");
|
|
|
- assert(0);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- assert(0); // should not happen
|
|
|
- }
|
|
|
-
|
|
|
- hero->subID = heroTypeId;
|
|
|
- hero->portrait = hero->subID;
|
|
|
- map->getEditManager()->insertObject(hero);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // remove hero placeholders on map
|
|
|
- for(auto obj : map->objects)
|
|
|
- {
|
|
|
- if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
|
|
|
- {
|
|
|
- auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
|
|
|
- map->removeBlockVisTiles(heroPlaceholder, true);
|
|
|
- map->instanceNames.erase(obj->instanceName);
|
|
|
- map->objects[heroPlaceholder->id.getNum()] = nullptr;
|
|
|
- delete heroPlaceholder;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos)
|
|
|
{
|
|
|
for(auto town : map->towns)
|
|
@@ -952,191 +837,6 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy
|
|
|
map->getEditManager()->insertObject(hero);
|
|
|
}
|
|
|
|
|
|
-CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenarios() const
|
|
|
-{
|
|
|
- CrossoverHeroesList crossoverHeroes;
|
|
|
-
|
|
|
- auto campaignState = scenarioOps->campState;
|
|
|
- auto bonus = campaignState->getBonusForCurrentMap();
|
|
|
- if(bonus && bonus->type == CScenarioTravel::STravelBonus::HEROES_FROM_PREVIOUS_SCENARIO)
|
|
|
- {
|
|
|
- std::vector<CGHeroInstance *> heroes;
|
|
|
- for(auto & node : campaignState->camp->scenarios[bonus->info2].crossoverHeroes)
|
|
|
- {
|
|
|
- auto * h = CCampaignState::crossoverDeserialize(node);
|
|
|
- heroes.push_back(h);
|
|
|
- }
|
|
|
- crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if(!campaignState->mapsConquered.empty())
|
|
|
- {
|
|
|
- std::vector<CGHeroInstance *> heroes = {};
|
|
|
-
|
|
|
- crossoverHeroes.heroesFromAnyPreviousScenarios = crossoverHeroes.heroesFromPreviousScenario = heroes;
|
|
|
- crossoverHeroes.heroesFromPreviousScenario = heroes;
|
|
|
-
|
|
|
- for(auto mapNr : campaignState->mapsConquered)
|
|
|
- {
|
|
|
- // create a list of deleted heroes
|
|
|
- auto & scenario = campaignState->camp->scenarios[mapNr];
|
|
|
- auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes();
|
|
|
-
|
|
|
- // remove heroes which didn't reached the end of the scenario, but were available at the start
|
|
|
- for(auto * hero : lostCrossoverHeroes)
|
|
|
- {
|
|
|
- // auto hero = CCampaignState::crossoverDeserialize(node);
|
|
|
- vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
|
|
|
- {
|
|
|
- return hero->subID == h->subID;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // now add heroes which completed the scenario
|
|
|
- for(auto node : scenario.crossoverHeroes)
|
|
|
- {
|
|
|
- auto * hero = CCampaignState::crossoverDeserialize(node);
|
|
|
- // add new heroes and replace old heroes with newer ones
|
|
|
- auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
|
|
|
- {
|
|
|
- return hero->subID == h->subID;
|
|
|
- });
|
|
|
-
|
|
|
- if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
|
|
|
- {
|
|
|
- // replace old hero with newer one
|
|
|
- crossoverHeroes.heroesFromAnyPreviousScenarios[it - crossoverHeroes.heroesFromAnyPreviousScenarios.begin()] = hero;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // add new hero
|
|
|
- crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero);
|
|
|
- }
|
|
|
-
|
|
|
- if(mapNr == campaignState->mapsConquered.back())
|
|
|
- {
|
|
|
- crossoverHeroes.heroesFromPreviousScenario.push_back(hero);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return crossoverHeroes;
|
|
|
-}
|
|
|
-
|
|
|
-void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & 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 replace magic numbers with named constants
|
|
|
- // 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)
|
|
|
- {
|
|
|
- cgh->initExp(getRandomGenerator());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(!travelOptions.whatHeroKeeps.primarySkills)
|
|
|
- {
|
|
|
- //trimming prim skills
|
|
|
- for(CGHeroInstance * cgh : crossoverHeroes)
|
|
|
- {
|
|
|
- for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
|
|
|
- {
|
|
|
- auto sel = Selector::type()(BonusType::PRIMARY_SKILL)
|
|
|
- .And(Selector::subtype()(g))
|
|
|
- .And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
|
|
|
-
|
|
|
- cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g];
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(!travelOptions.whatHeroKeeps.secondarySkills)
|
|
|
- {
|
|
|
- //trimming sec skills
|
|
|
- for(CGHeroInstance * cgh : crossoverHeroes)
|
|
|
- {
|
|
|
- cgh->secSkills = cgh->type->secSkillsInit;
|
|
|
- cgh->recreateSecondarySkillsBonuses();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(!travelOptions.whatHeroKeeps.spells)
|
|
|
- {
|
|
|
- for(CGHeroInstance * cgh : crossoverHeroes)
|
|
|
- {
|
|
|
- cgh->removeSpellbook();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if(!travelOptions.whatHeroKeeps.artifacts)
|
|
|
- {
|
|
|
- //trimming artifacts
|
|
|
- for(CGHeroInstance * hero : crossoverHeroes)
|
|
|
- {
|
|
|
- size_t totalArts = GameConstants::BACKPACK_START + hero->artifactsInBackpack.size();
|
|
|
- for (size_t i = 0; i < totalArts; i++ )
|
|
|
- {
|
|
|
- auto artifactPosition = ArtifactPosition((si32)i);
|
|
|
- if(artifactPosition == ArtifactPosition::SPELLBOOK) continue; // do not handle spellbook this way
|
|
|
-
|
|
|
- const ArtSlotInfo *info = hero->getSlot(artifactPosition);
|
|
|
- if(!info)
|
|
|
- continue;
|
|
|
-
|
|
|
- // TODO: why would there be nullptr artifacts?
|
|
|
- const CArtifactInstance *art = info->artifact;
|
|
|
- if(!art)
|
|
|
- continue;
|
|
|
-
|
|
|
- bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
|
|
|
-
|
|
|
- ArtifactLocation al(hero, artifactPosition);
|
|
|
- if(!takeable && !al.getSlot()->locked) //don't try removing locked artifacts -> it crashes #1719
|
|
|
- al.removeArtifact();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //trimming creatures
|
|
|
- for(CGHeroInstance * cgh : crossoverHeroes)
|
|
|
- {
|
|
|
- auto shouldSlotBeErased = [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
|
|
|
- {
|
|
|
- CreatureID::ECreatureID crid = j.second->getCreatureID().toEnum();
|
|
|
- return !travelOptions.monstersKeptByHero.count(crid);
|
|
|
- };
|
|
|
-
|
|
|
- auto stacksCopy = cgh->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);
|
|
|
- }
|
|
|
-
|
|
|
- // Removing short-term bonuses
|
|
|
- for(CGHeroInstance * cgh : crossoverHeroes)
|
|
|
- {
|
|
|
- cgh->removeBonusesRecursive(CSelector(Bonus::OneDay)
|
|
|
- .Or(CSelector(Bonus::OneWeek))
|
|
|
- .Or(CSelector(Bonus::NTurns))
|
|
|
- .Or(CSelector(Bonus::NDays))
|
|
|
- .Or(CSelector(Bonus::OneBattle)));
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
void CGameState::placeStartingHeroes()
|
|
|
{
|
|
|
logGlobal->debug("\tGiving starting hero");
|
|
@@ -1148,14 +848,8 @@ void CGameState::placeStartingHeroes()
|
|
|
if(playerInfo.generateHeroAtMainTown && playerInfo.hasMainTown)
|
|
|
{
|
|
|
// Do not place a starting hero if the hero was already placed due to a campaign bonus
|
|
|
- if(scenarioOps->campState)
|
|
|
- {
|
|
|
- if(auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap())
|
|
|
- {
|
|
|
- if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1))
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (campaign && campaign->playerHasStartingHero(playerColor))
|
|
|
+ continue;
|
|
|
|
|
|
int heroTypeId = pickNextHeroType(playerColor);
|
|
|
if(playerSettingPair.second.hero == -1)
|
|
@@ -1166,6 +860,22 @@ void CGameState::placeStartingHeroes()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void CGameState::removeHeroPlaceholders()
|
|
|
+{
|
|
|
+ // remove any hero placeholders that remain on map after (potential) campaign heroes placement
|
|
|
+ for(auto obj : map->objects)
|
|
|
+ {
|
|
|
+ if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
|
|
|
+ {
|
|
|
+ auto heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
|
|
|
+ map->removeBlockVisTiles(heroPlaceholder, true);
|
|
|
+ map->instanceNames.erase(obj->instanceName);
|
|
|
+ map->objects[heroPlaceholder->id.getNum()] = nullptr;
|
|
|
+ delete heroPlaceholder;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void CGameState::initStartingResources()
|
|
|
{
|
|
|
logGlobal->debug("\tSetting up resources");
|
|
@@ -1186,55 +896,8 @@ void CGameState::initStartingResources()
|
|
|
p.resources = startresAI;
|
|
|
}
|
|
|
|
|
|
- auto getHumanPlayerInfo = [&]() -> std::vector<const PlayerSettings *>
|
|
|
- {
|
|
|
- std::vector<const PlayerSettings *> ret;
|
|
|
- for(const auto & playerInfo : scenarioOps->playerInfos)
|
|
|
- {
|
|
|
- if(playerInfo.second.isControlledByHuman())
|
|
|
- ret.push_back(&playerInfo.second);
|
|
|
- }
|
|
|
-
|
|
|
- return ret;
|
|
|
- };
|
|
|
-
|
|
|
- //give start resource bonus in case of campaign
|
|
|
- if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
|
|
- {
|
|
|
- auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
- if(chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::RESOURCE)
|
|
|
- {
|
|
|
- std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
|
|
|
- for(const PlayerSettings *ps : people)
|
|
|
- {
|
|
|
- std::vector<int> res; //resources we will give
|
|
|
- switch (chosenBonus->info1)
|
|
|
- {
|
|
|
- case 0: case 1: case 2: case 3: case 4: case 5: case 6:
|
|
|
- res.push_back(chosenBonus->info1);
|
|
|
- break;
|
|
|
- case 0xFD: //wood+ore
|
|
|
- res.push_back(GameResID(EGameResID::WOOD));
|
|
|
- res.push_back(GameResID(EGameResID::ORE));
|
|
|
- break;
|
|
|
- case 0xFE: //rare
|
|
|
- res.push_back(GameResID(EGameResID::MERCURY));
|
|
|
- res.push_back(GameResID(EGameResID::SULFUR));
|
|
|
- res.push_back(GameResID(EGameResID::CRYSTAL));
|
|
|
- res.push_back(GameResID(EGameResID::GEMS));
|
|
|
- break;
|
|
|
- default:
|
|
|
- assert(0);
|
|
|
- break;
|
|
|
- }
|
|
|
- //increasing resource quantity
|
|
|
- for (auto & re : res)
|
|
|
- {
|
|
|
- players[ps->color].resources[re] += chosenBonus->info2;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (campaign)
|
|
|
+ campaign->initStartingResources();
|
|
|
}
|
|
|
|
|
|
void CGameState::initHeroes()
|
|
@@ -1312,115 +975,8 @@ void CGameState::initHeroes()
|
|
|
hpool.pavailable[elem.heroId] = elem.players;
|
|
|
}
|
|
|
|
|
|
- if (scenarioOps->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
|
|
|
- {
|
|
|
- auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
- if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes
|
|
|
- {
|
|
|
- //find human player
|
|
|
- PlayerColor humanPlayer=PlayerColor::NEUTRAL;
|
|
|
- for (auto & elem : players)
|
|
|
- {
|
|
|
- if(elem.second.human)
|
|
|
- {
|
|
|
- humanPlayer = elem.first;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- assert(humanPlayer != PlayerColor::NEUTRAL);
|
|
|
-
|
|
|
- std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = players[humanPlayer].heroes;
|
|
|
-
|
|
|
- if (chosenBonus->info1 == 0xFFFD) //most powerful
|
|
|
- {
|
|
|
- int maxB = -1;
|
|
|
- for (int b=0; b<heroes.size(); ++b)
|
|
|
- {
|
|
|
- if (maxB == -1 || heroes[b]->getTotalStrength() > heroes[maxB]->getTotalStrength())
|
|
|
- {
|
|
|
- maxB = b;
|
|
|
- }
|
|
|
- }
|
|
|
- if(maxB < 0)
|
|
|
- logGlobal->warn("Cannot give bonus to hero cause there are no heroes!");
|
|
|
- else
|
|
|
- giveCampaignBonusToHero(heroes[maxB]);
|
|
|
- }
|
|
|
- else //specific hero
|
|
|
- {
|
|
|
- for (auto & heroe : heroes)
|
|
|
- {
|
|
|
- if (heroe->subID == chosenBonus->info1)
|
|
|
- {
|
|
|
- giveCampaignBonusToHero(heroe);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
|
|
|
-{
|
|
|
- const std::optional<CScenarioTravel::STravelBonus> & curBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
- if(!curBonus)
|
|
|
- return;
|
|
|
-
|
|
|
- if(curBonus->isBonusForHero())
|
|
|
- {
|
|
|
- //apply bonus
|
|
|
- switch (curBonus->type)
|
|
|
- {
|
|
|
- case CScenarioTravel::STravelBonus::SPELL:
|
|
|
- hero->addSpellToSpellbook(SpellID(curBonus->info2));
|
|
|
- break;
|
|
|
- case CScenarioTravel::STravelBonus::MONSTER:
|
|
|
- {
|
|
|
- for(int i=0; i<GameConstants::ARMY_SIZE; i++)
|
|
|
- {
|
|
|
- if(hero->slotEmpty(SlotID(i)))
|
|
|
- {
|
|
|
- hero->addToSlot(SlotID(i), CreatureID(curBonus->info2), curBonus->info3);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- case CScenarioTravel::STravelBonus::ARTIFACT:
|
|
|
- if(!gs->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2)))
|
|
|
- logGlobal->error("Cannot give starting artifact - no free slots!");
|
|
|
- break;
|
|
|
- case CScenarioTravel::STravelBonus::SPELL_SCROLL:
|
|
|
- {
|
|
|
- CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2));
|
|
|
- const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId());
|
|
|
- if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
|
|
|
- scroll->putAt(ArtifactLocation(hero, slot));
|
|
|
- else
|
|
|
- logGlobal->error("Cannot give starting scroll - no free slots!");
|
|
|
- }
|
|
|
- break;
|
|
|
- case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
|
|
|
- {
|
|
|
- const ui8* ptr = reinterpret_cast<const ui8*>(&curBonus->info2);
|
|
|
- for (int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
|
|
|
- {
|
|
|
- int val = ptr[g];
|
|
|
- if (val == 0)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- auto bb = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, *scenarioOps->campState->currentMap, g);
|
|
|
- hero->addNewBonus(bb);
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- case CScenarioTravel::STravelBonus::SECONDARY_SKILL:
|
|
|
- hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (campaign)
|
|
|
+ campaign->initHeroes();
|
|
|
}
|
|
|
|
|
|
void CGameState::initFogOfWar()
|
|
@@ -1503,36 +1059,8 @@ void CGameState::initTowns()
|
|
|
{
|
|
|
logGlobal->debug("\tTowns");
|
|
|
|
|
|
- //campaign bonuses for towns
|
|
|
- if (scenarioOps->mode == StartInfo::CAMPAIGN)
|
|
|
- {
|
|
|
- auto chosenBonus = scenarioOps->campState->getBonusForCurrentMap();
|
|
|
-
|
|
|
- if (chosenBonus && chosenBonus->type == CScenarioTravel::STravelBonus::BUILDING)
|
|
|
- {
|
|
|
- for (int g=0; g<map->towns.size(); ++g)
|
|
|
- {
|
|
|
- PlayerState * owner = getPlayerState(map->towns[g]->getOwner());
|
|
|
- if (owner)
|
|
|
- {
|
|
|
- PlayerInfo & pi = map->players[owner->color.getNum()];
|
|
|
-
|
|
|
- if (owner->human && //human-owned
|
|
|
- map->towns[g]->pos == pi.posOfMainTown)
|
|
|
- {
|
|
|
- BuildingID buildingId;
|
|
|
- if(scenarioOps->campState->camp->header.version == CampaignVersion::VCMI)
|
|
|
- buildingId = BuildingID(chosenBonus->info1);
|
|
|
- else
|
|
|
- buildingId = CBuildingHandler::campToERMU(chosenBonus->info1, map->towns[g]->subID, map->towns[g]->builtBuildings);
|
|
|
-
|
|
|
- map->towns[g]->builtBuildings.insert(buildingId);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (campaign)
|
|
|
+ campaign->initTowns();
|
|
|
|
|
|
CGTownInstance::universitySkills.clear();
|
|
|
for ( int i=0; i<4; i++)
|
|
@@ -2646,114 +2174,6 @@ std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-std::vector<CGameState::CampaignHeroReplacement> CGameState::generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes)
|
|
|
-{
|
|
|
- std::vector<CampaignHeroReplacement> campaignHeroReplacements;
|
|
|
-
|
|
|
- //selecting heroes by type
|
|
|
- for(auto obj : map->objects)
|
|
|
- {
|
|
|
- if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
|
|
|
- {
|
|
|
- auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
|
|
|
- if(heroPlaceholder->subID != 0xFF) //select by type
|
|
|
- {
|
|
|
- auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [heroPlaceholder](CGHeroInstance * hero)
|
|
|
- {
|
|
|
- return hero->subID == heroPlaceholder->subID;
|
|
|
- });
|
|
|
-
|
|
|
- if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
|
|
|
- {
|
|
|
- auto * hero = *it;
|
|
|
- crossoverHeroes.removeHeroFromBothLists(hero);
|
|
|
- campaignHeroReplacements.emplace_back(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //selecting heroes by power
|
|
|
- range::sort(crossoverHeroes.heroesFromPreviousScenario, [](const CGHeroInstance * a, const CGHeroInstance * b)
|
|
|
- {
|
|
|
- return a->getHeroStrength() > b->getHeroStrength();
|
|
|
- }); //sort, descending strength
|
|
|
-
|
|
|
- // sort hero placeholders descending power
|
|
|
- std::vector<CGHeroPlaceholder *> heroPlaceholders;
|
|
|
- for(auto obj : map->objects)
|
|
|
- {
|
|
|
- if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
|
|
|
- {
|
|
|
- auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
|
|
|
- if(heroPlaceholder->subID == 0xFF) //select by power
|
|
|
- {
|
|
|
- heroPlaceholders.push_back(heroPlaceholder);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- range::sort(heroPlaceholders, [](const CGHeroPlaceholder * a, const CGHeroPlaceholder * b)
|
|
|
- {
|
|
|
- return a->power > b->power;
|
|
|
- });
|
|
|
-
|
|
|
- for(int i = 0; i < heroPlaceholders.size(); ++i)
|
|
|
- {
|
|
|
- auto * heroPlaceholder = heroPlaceholders[i];
|
|
|
- if(crossoverHeroes.heroesFromPreviousScenario.size() > i)
|
|
|
- {
|
|
|
- auto * hero = crossoverHeroes.heroesFromPreviousScenario[i];
|
|
|
- campaignHeroReplacements.emplace_back(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return campaignHeroReplacements;
|
|
|
-}
|
|
|
-
|
|
|
-void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::CampaignHeroReplacement> & campaignHeroReplacements)
|
|
|
-{
|
|
|
- for(const auto & campaignHeroReplacement : campaignHeroReplacements)
|
|
|
- {
|
|
|
- auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(getObjInstance(campaignHeroReplacement.heroPlaceholderId));
|
|
|
-
|
|
|
- CGHeroInstance *heroToPlace = campaignHeroReplacement.hero;
|
|
|
- heroToPlace->id = campaignHeroReplacement.heroPlaceholderId;
|
|
|
- heroToPlace->tempOwner = heroPlaceholder->tempOwner;
|
|
|
- heroToPlace->pos = heroPlaceholder->pos;
|
|
|
- heroToPlace->type = VLC->heroh->objects[heroToPlace->subID];
|
|
|
- heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO,
|
|
|
- heroToPlace->type->heroClass->getIndex())->getTemplates().front();
|
|
|
-
|
|
|
- for(auto &&i : heroToPlace->stacks)
|
|
|
- i.second->type = VLC->creh->objects[i.second->getCreatureID()];
|
|
|
-
|
|
|
- auto fixArtifact = [&](CArtifactInstance * art)
|
|
|
- {
|
|
|
- art->artType = VLC->arth->objects[art->artType->getId()];
|
|
|
- gs->map->artInstances.emplace_back(art);
|
|
|
- art->id = ArtifactInstanceID((si32)gs->map->artInstances.size() - 1);
|
|
|
- };
|
|
|
-
|
|
|
- for(auto &&i : heroToPlace->artifactsWorn)
|
|
|
- fixArtifact(i.second.artifact);
|
|
|
- for(auto &&i : heroToPlace->artifactsInBackpack)
|
|
|
- fixArtifact(i.artifact);
|
|
|
-
|
|
|
- map->removeBlockVisTiles(heroPlaceholder, true);
|
|
|
- map->objects[heroPlaceholder->id.getNum()] = nullptr;
|
|
|
- map->instanceNames.erase(heroPlaceholder->instanceName);
|
|
|
-
|
|
|
- map->heroesOnMap.emplace_back(heroToPlace);
|
|
|
- map->objects[heroToPlace->id.getNum()] = heroToPlace;
|
|
|
- map->addBlockVisTiles(heroToPlace);
|
|
|
- map->instanceNames[heroToPlace->instanceName] = heroToPlace;
|
|
|
-
|
|
|
- delete heroPlaceholder;
|
|
|
-
|
|
|
- scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CCampaignState::crossoverSerialize(heroToPlace));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
bool CGameState::isUsedHero(const HeroTypeID & hid) const
|
|
|
{
|
|
|
return getUsedHero(hid);
|
|
@@ -2801,170 +2221,6 @@ bool RumorState::update(int id, int extra)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-InfoAboutArmy::InfoAboutArmy():
|
|
|
- owner(PlayerColor::NEUTRAL)
|
|
|
-{}
|
|
|
-
|
|
|
-InfoAboutArmy::InfoAboutArmy(const CArmedInstance *Army, bool detailed)
|
|
|
-{
|
|
|
- initFromArmy(Army, detailed);
|
|
|
-}
|
|
|
-
|
|
|
-void InfoAboutArmy::initFromArmy(const CArmedInstance *Army, bool detailed)
|
|
|
-{
|
|
|
- army = ArmyDescriptor(Army, detailed);
|
|
|
- owner = Army->tempOwner;
|
|
|
- name = Army->getObjectName();
|
|
|
-}
|
|
|
-
|
|
|
-void InfoAboutHero::assign(const InfoAboutHero & iah)
|
|
|
-{
|
|
|
- vstd::clear_pointer(details);
|
|
|
- InfoAboutArmy::operator = (iah);
|
|
|
-
|
|
|
- details = (iah.details ? new Details(*iah.details) : nullptr);
|
|
|
- hclass = iah.hclass;
|
|
|
- portrait = iah.portrait;
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutHero::InfoAboutHero(): portrait(-1) {}
|
|
|
-
|
|
|
-InfoAboutHero::InfoAboutHero(const InfoAboutHero & iah): InfoAboutArmy(iah)
|
|
|
-{
|
|
|
- assign(iah);
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutHero::InfoAboutHero(const CGHeroInstance * h, InfoAboutHero::EInfoLevel infoLevel):
|
|
|
- portrait(-1)
|
|
|
-{
|
|
|
- initFromHero(h, infoLevel);
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutHero::~InfoAboutHero()
|
|
|
-{
|
|
|
- vstd::clear_pointer(details);
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutHero & InfoAboutHero::operator=(const InfoAboutHero & iah)
|
|
|
-{
|
|
|
- assign(iah);
|
|
|
- return *this;
|
|
|
-}
|
|
|
-
|
|
|
-void InfoAboutHero::initFromHero(const CGHeroInstance *h, InfoAboutHero::EInfoLevel infoLevel)
|
|
|
-{
|
|
|
- vstd::clear_pointer(details);
|
|
|
- if(!h)
|
|
|
- return;
|
|
|
-
|
|
|
- bool detailed = ( (infoLevel == EInfoLevel::DETAILED) || (infoLevel == EInfoLevel::INBATTLE) );
|
|
|
-
|
|
|
- initFromArmy(h, detailed);
|
|
|
-
|
|
|
- hclass = h->type->heroClass;
|
|
|
- name = h->getNameTranslated();
|
|
|
- portrait = h->portrait;
|
|
|
-
|
|
|
- if(detailed)
|
|
|
- {
|
|
|
- //include details about hero
|
|
|
- details = new Details();
|
|
|
- details->luck = h->luckVal();
|
|
|
- details->morale = h->moraleVal();
|
|
|
- details->mana = h->mana;
|
|
|
- details->primskills.resize(GameConstants::PRIMARY_SKILLS);
|
|
|
-
|
|
|
- for (int i = 0; i < GameConstants::PRIMARY_SKILLS ; i++)
|
|
|
- {
|
|
|
- details->primskills[i] = h->getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(i));
|
|
|
- }
|
|
|
- if (infoLevel == EInfoLevel::INBATTLE)
|
|
|
- details->manaLimit = h->manaLimit();
|
|
|
- else
|
|
|
- details->manaLimit = -1; //we do not want to leak max mana info outside battle so set to meaningless value
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutTown::InfoAboutTown():
|
|
|
- details(nullptr),
|
|
|
- tType(nullptr),
|
|
|
- built(0),
|
|
|
- fortLevel(0)
|
|
|
-{
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutTown::InfoAboutTown(const CGTownInstance *t, bool detailed):
|
|
|
- details(nullptr),
|
|
|
- tType(nullptr),
|
|
|
- built(0),
|
|
|
- fortLevel(0)
|
|
|
-{
|
|
|
- initFromTown(t, detailed);
|
|
|
-}
|
|
|
-
|
|
|
-InfoAboutTown::~InfoAboutTown()
|
|
|
-{
|
|
|
- vstd::clear_pointer(details);
|
|
|
-}
|
|
|
-
|
|
|
-void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
|
|
|
-{
|
|
|
- initFromArmy(t, detailed);
|
|
|
- army = ArmyDescriptor(t->getUpperArmy(), detailed);
|
|
|
- built = t->builded;
|
|
|
- fortLevel = t->fortLevel();
|
|
|
- name = t->getNameTranslated();
|
|
|
- tType = t->getTown();
|
|
|
-
|
|
|
- vstd::clear_pointer(details);
|
|
|
-
|
|
|
- if(detailed)
|
|
|
- {
|
|
|
- //include details about hero
|
|
|
- details = new Details();
|
|
|
- TResources income = t->dailyIncome();
|
|
|
- details->goldIncome = income[EGameResID::GOLD];
|
|
|
- details->customRes = t->hasBuilt(BuildingID::RESOURCE_SILO);
|
|
|
- details->hallLevel = t->hallLevel();
|
|
|
- details->garrisonedHero = t->garrisonHero;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-ArmyDescriptor::ArmyDescriptor(const CArmedInstance *army, bool detailed)
|
|
|
- : isDetailed(detailed)
|
|
|
-{
|
|
|
- for(const auto & elem : army->Slots())
|
|
|
- {
|
|
|
- if(detailed)
|
|
|
- (*this)[elem.first] = *elem.second;
|
|
|
- else
|
|
|
- (*this)[elem.first] = CStackBasicDescriptor(elem.second->type, (int)elem.second->getQuantityID());
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-ArmyDescriptor::ArmyDescriptor()
|
|
|
- : isDetailed(false)
|
|
|
-{
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-int ArmyDescriptor::getStrength() const
|
|
|
-{
|
|
|
- ui64 ret = 0;
|
|
|
- if(isDetailed)
|
|
|
- {
|
|
|
- for(const auto & elem : *this)
|
|
|
- ret += elem.second.type->getAIValue() * elem.second.count;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- for(const auto & elem : *this)
|
|
|
- ret += elem.second.type->getAIValue() * CCreature::estimateCreatureCount(elem.second.count);
|
|
|
- }
|
|
|
- return static_cast<int>(ret);
|
|
|
-}
|
|
|
-
|
|
|
TeamState::TeamState()
|
|
|
{
|
|
|
setNodeType(TEAM);
|