2023-06-23 18:33:47 +02:00
/*
* CGameStateCampaign . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
# include "CGameStateCampaign.h"
# include "CGameState.h"
# include "QuestInfo.h"
2023-06-25 21:28:24 +02:00
# include "../campaign/CampaignState.h"
2023-06-23 18:33:47 +02:00
# include "../mapping/CMapEditManager.h"
# include "../mapObjects/CGHeroInstance.h"
# include "../registerTypes/RegisterTypes.h"
# include "../mapObjectConstructors/AObjectTypeHandler.h"
# include "../mapObjectConstructors/CObjectClassesHandler.h"
# include "../StartInfo.h"
# include "../CBuildingHandler.h"
# include "../CHeroHandler.h"
# include "../mapping/CMap.h"
# include "../ArtifactUtils.h"
# include "../CPlayerState.h"
# include "../serializer/CMemorySerializer.h"
VCMI_LIB_NAMESPACE_BEGIN
CampaignHeroReplacement : : CampaignHeroReplacement ( CGHeroInstance * hero , const ObjectInstanceID & heroPlaceholderId ) :
hero ( hero ) ,
heroPlaceholderId ( heroPlaceholderId )
{
}
CGameStateCampaign : : CGameStateCampaign ( CGameState * owner ) :
gameState ( owner )
{
assert ( gameState - > scenarioOps - > mode = = StartInfo : : CAMPAIGN ) ;
assert ( gameState - > scenarioOps - > campState ! = nullptr ) ;
}
2023-06-26 00:07:55 +02:00
std : : optional < CampaignBonus > CGameStateCampaign : : currentBonus ( ) const
{
auto campaignState = gameState - > scenarioOps - > campState ;
return campaignState - > getBonus ( * campaignState - > currentScenario ( ) ) ;
}
2023-06-26 15:25:34 +02:00
std : : optional < CampaignScenarioID > CGameStateCampaign : : getHeroesSourceScenario ( ) const
2023-06-23 18:33:47 +02:00
{
auto campaignState = gameState - > scenarioOps - > campState ;
2023-06-26 00:07:55 +02:00
auto bonus = currentBonus ( ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
if ( bonus & & bonus - > type = = CampaignBonusType : : HEROES_FROM_PREVIOUS_SCENARIO )
return static_cast < CampaignScenarioID > ( bonus - > info2 ) ; ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
return campaignState - > lastScenario ( ) ;
2023-06-23 18:33:47 +02:00
}
2023-06-25 20:16:03 +02:00
void CGameStateCampaign : : trimCrossoverHeroesParameters ( std : : vector < CampaignHeroReplacement > & campaignHeroReplacements , const CampaignTravel & travelOptions )
2023-06-23 18:33:47 +02:00
{
// 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 )
{
cgh - > initExp ( gameState - > getRandomGenerator ( ) ) ;
}
}
if ( ! travelOptions . whatHeroKeeps . primarySkills )
{
//trimming prim skills
for ( CGHeroInstance * cgh : crossoverHeroes )
{
2023-10-05 15:13:52 +02:00
for ( auto g = PrimarySkill : : BEGIN ; g < PrimarySkill : : END ; + + g )
2023-06-23 18:33:47 +02:00
{
auto sel = Selector : : type ( ) ( BonusType : : PRIMARY_SKILL )
2023-10-21 13:50:42 +02:00
. And ( Selector : : subtype ( ) ( BonusSubtypeID ( g ) ) )
2023-06-23 18:33:47 +02:00
. And ( Selector : : sourceType ( ) ( BonusSource : : HERO_BASE_SKILL ) ) ;
2023-11-02 16:56:02 +02:00
cgh - > getBonusLocalFirst ( sel ) - > val = cgh - > type - > heroClass - > primarySkillInitial [ g . getNum ( ) ] ;
2023-06-23 18:33:47 +02:00
}
}
}
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 )
{
2023-10-05 15:13:52 +02:00
const auto & checkAndRemoveArtifact = [ & ] ( const ArtifactPosition & artifactPosition )
2023-06-23 18:33:47 +02:00
{
2023-06-26 15:25:34 +02:00
if ( artifactPosition = = ArtifactPosition : : SPELLBOOK )
2023-07-31 21:06:54 +02:00
return ; // do not handle spellbook this way
2023-06-23 18:33:47 +02:00
const ArtSlotInfo * info = hero - > getSlot ( artifactPosition ) ;
if ( ! info )
2023-07-31 21:06:54 +02:00
return ;
2023-06-23 18:33:47 +02:00
// TODO: why would there be nullptr artifacts?
const CArtifactInstance * art = info - > artifact ;
if ( ! art )
2023-07-31 21:06:54 +02:00
return ;
2023-06-23 18:33:47 +02:00
bool takeable = travelOptions . artifactsKeptByHero . count ( art - > artType - > getId ( ) ) ;
2023-10-14 21:00:39 +02:00
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 ) ;
2023-07-31 21:06:54 +02:00
} ;
// process on copy - removal of artifact will invalidate container
auto artifactsWorn = hero - > artifactsWorn ;
2023-10-05 15:13:52 +02:00
for ( const auto & art : artifactsWorn )
2023-07-31 21:06:54 +02:00
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 - - )
2023-08-19 23:22:31 +02:00
checkAndRemoveArtifact ( ArtifactPosition : : BACKPACK_START + slotNumber ) ;
2023-06-23 18:33:47 +02:00
}
}
//trimming creatures
for ( CGHeroInstance * cgh : crossoverHeroes )
{
auto shouldSlotBeErased = [ & ] ( const std : : pair < SlotID , CStackInstance * > & j ) - > bool
{
2023-08-19 19:48:28 +02:00
CreatureID crid = j . second - > getCreatureID ( ) ;
2023-06-23 18:33:47 +02:00
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 CGameStateCampaign : : placeCampaignHeroes ( )
{
2023-06-23 20:35:14 +02:00
// place bonus hero
2023-06-26 00:07:55 +02:00
auto campaignState = gameState - > scenarioOps - > campState ;
auto campaignBonus = campaignState - > getBonus ( * campaignState - > currentScenario ( ) ) ;
2023-06-25 20:16:03 +02:00
bool campaignGiveHero = campaignBonus & & campaignBonus - > type = = CampaignBonusType : : HERO ;
2023-06-23 18:33:47 +02:00
2023-06-23 20:35:14 +02:00
if ( campaignGiveHero )
{
auto playerColor = PlayerColor ( campaignBonus - > info1 ) ;
auto it = gameState - > scenarioOps - > playerInfos . find ( playerColor ) ;
if ( it ! = gameState - > scenarioOps - > playerInfos . end ( ) )
2023-06-23 18:33:47 +02:00
{
2023-06-23 20:35:14 +02:00
auto heroTypeId = campaignBonus - > info2 ;
if ( heroTypeId = = 0xffff ) // random bonus hero
2023-06-23 18:33:47 +02:00
{
2023-06-23 20:35:14 +02:00
heroTypeId = gameState - > pickUnusedHeroTypeRandomly ( playerColor ) ;
2023-06-23 18:33:47 +02:00
}
2023-06-23 20:35:14 +02:00
gameState - > placeStartingHero ( playerColor , HeroTypeID ( heroTypeId ) , gameState - > map - > players [ playerColor . getNum ( ) ] . posOfMainTown ) ;
2023-06-23 18:33:47 +02:00
}
2023-06-23 20:35:14 +02:00
}
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
logGlobal - > debug ( " \t Generate list of hero placeholders " ) ;
auto campaignHeroReplacements = generateCampaignHeroesToReplace ( ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
logGlobal - > debug ( " \t Prepare crossover heroes " ) ;
trimCrossoverHeroesParameters ( campaignHeroReplacements , 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
// with the same hero type id
std : : vector < CGHeroInstance * > removedHeroes ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
std : : set < HeroTypeID > heroesToRemove = campaignState - > getReservedHeroes ( ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
for ( auto & campaignHeroReplacement : campaignHeroReplacements )
2023-10-24 16:11:25 +02:00
heroesToRemove . insert ( campaignHeroReplacement . hero - > getHeroType ( ) ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
for ( auto & heroID : heroesToRemove )
{
auto * hero = gameState - > getUsedHero ( heroID ) ;
if ( hero )
2023-06-23 20:35:14 +02:00
{
2023-06-26 15:25:34 +02:00
removedHeroes . push_back ( hero ) ;
gameState - > map - > heroesOnMap - = hero ;
gameState - > map - > objects [ hero - > id . getNum ( ) ] = nullptr ;
gameState - > map - > removeBlockVisTiles ( hero , true ) ;
2023-06-23 20:35:14 +02:00
}
2023-06-26 15:25:34 +02:00
}
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
logGlobal - > debug ( " \t Replace placeholders with heroes " ) ;
replaceHeroesPlaceholders ( campaignHeroReplacements ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
// now add removed heroes again with unused type ID
for ( auto * hero : removedHeroes )
{
2023-09-28 18:43:04 +02:00
HeroTypeID heroTypeId ;
2023-06-26 15:25:34 +02:00
if ( hero - > ID = = Obj : : HERO )
2023-06-23 20:35:14 +02:00
{
2023-06-26 15:25:34 +02:00
heroTypeId = gameState - > pickUnusedHeroTypeRandomly ( hero - > tempOwner ) ;
}
else if ( hero - > ID = = Obj : : PRISON )
{
auto unusedHeroTypeIds = gameState - > getUnusedAllowedHeroes ( ) ;
if ( ! unusedHeroTypeIds . empty ( ) )
2023-06-23 20:35:14 +02:00
{
2023-09-28 18:43:04 +02:00
heroTypeId = ( * RandomGeneratorUtil : : nextItem ( unusedHeroTypeIds , gameState - > getRandomGenerator ( ) ) ) ;
2023-06-23 18:33:47 +02:00
}
2023-06-23 20:35:14 +02:00
else
{
2023-06-26 15:25:34 +02:00
logGlobal - > error ( " No free hero type ID found to replace prison. " ) ;
assert ( 0 ) ;
2023-06-23 20:35:14 +02:00
}
2023-06-23 18:33:47 +02:00
}
2023-06-26 15:25:34 +02:00
else
{
assert ( 0 ) ; // should not happen
}
2023-10-28 11:27:10 +02:00
hero - > setHeroType ( heroTypeId ) ;
2023-06-26 15:25:34 +02:00
gameState - > map - > getEditManager ( ) - > insertObject ( hero ) ;
2023-06-23 18:33:47 +02:00
}
}
void CGameStateCampaign : : giveCampaignBonusToHero ( CGHeroInstance * hero )
{
2023-06-26 00:07:55 +02:00
auto curBonus = currentBonus ( ) ;
2023-06-23 18:33:47 +02:00
if ( ! curBonus )
return ;
2023-06-23 20:35:14 +02:00
assert ( curBonus - > isBonusForHero ( ) ) ;
//apply bonus
switch ( curBonus - > type )
2023-06-23 18:33:47 +02:00
{
2023-06-25 20:16:03 +02:00
case CampaignBonusType : : SPELL :
2023-06-23 20:35:14 +02:00
{
2023-06-23 18:33:47 +02:00
hero - > addSpellToSpellbook ( SpellID ( curBonus - > info2 ) ) ;
break ;
2023-06-23 20:35:14 +02:00
}
2023-06-25 20:16:03 +02:00
case CampaignBonusType : : MONSTER :
2023-06-23 20:35:14 +02:00
{
for ( int i = 0 ; i < GameConstants : : ARMY_SIZE ; i + + )
2023-06-23 18:33:47 +02:00
{
2023-06-23 20:35:14 +02:00
if ( hero - > slotEmpty ( SlotID ( i ) ) )
2023-06-23 18:33:47 +02:00
{
2023-06-23 20:35:14 +02:00
hero - > addToSlot ( SlotID ( i ) , CreatureID ( curBonus - > info2 ) , curBonus - > info3 ) ;
break ;
2023-06-23 18:33:47 +02:00
}
}
break ;
2023-06-23 20:35:14 +02:00
}
2023-06-25 20:16:03 +02:00
case CampaignBonusType : : ARTIFACT :
2023-06-23 20:35:14 +02:00
{
2023-06-23 18:33:47 +02:00
if ( ! gameState - > giveHeroArtifact ( hero , static_cast < ArtifactID > ( curBonus - > info2 ) ) )
logGlobal - > error ( " Cannot give starting artifact - no free slots! " ) ;
break ;
2023-06-23 20:35:14 +02:00
}
2023-06-25 20:16:03 +02:00
case CampaignBonusType : : SPELL_SCROLL :
2023-06-23 20:35:14 +02:00
{
CArtifactInstance * scroll = ArtifactUtils : : createScroll ( SpellID ( curBonus - > info2 ) ) ;
const auto slot = ArtifactUtils : : getArtAnyPosition ( hero , scroll - > getTypeId ( ) ) ;
if ( ArtifactUtils : : isSlotEquipment ( slot ) | | ArtifactUtils : : isSlotBackpack ( slot ) )
2023-10-14 21:00:39 +02:00
scroll - > putAt ( * hero , slot ) ;
2023-06-23 20:35:14 +02:00
else
logGlobal - > error ( " Cannot give starting scroll - no free slots! " ) ;
2023-06-23 18:33:47 +02:00
break ;
2023-06-23 20:35:14 +02:00
}
2023-06-25 20:16:03 +02:00
case CampaignBonusType : : PRIMARY_SKILL :
2023-06-23 20:35:14 +02:00
{
const ui8 * ptr = reinterpret_cast < const ui8 * > ( & curBonus - > info2 ) ;
2023-10-05 15:13:52 +02:00
for ( auto g = PrimarySkill : : BEGIN ; g < PrimarySkill : : END ; + + g )
2023-06-23 18:33:47 +02:00
{
2023-10-05 15:13:52 +02:00
int val = ptr [ g . getNum ( ) ] ;
2023-06-23 20:35:14 +02:00
if ( val = = 0 )
continue ;
2023-10-05 15:13:52 +02:00
2023-10-10 17:05:18 +02:00
auto currentScenario = * gameState - > scenarioOps - > campState - > currentScenario ( ) ;
2023-10-21 13:50:42 +02:00
auto bb = std : : make_shared < Bonus > ( BonusDuration : : PERMANENT , BonusType : : PRIMARY_SKILL , BonusSource : : CAMPAIGN_BONUS , val , BonusSourceID ( currentScenario ) , BonusSubtypeID ( g ) ) ;
2023-06-23 20:35:14 +02:00
hero - > addNewBonus ( bb ) ;
2023-06-23 18:33:47 +02:00
}
break ;
2023-06-23 20:35:14 +02:00
}
2023-06-25 20:16:03 +02:00
case CampaignBonusType : : SECONDARY_SKILL :
2023-06-23 20:35:14 +02:00
{
2023-06-23 18:33:47 +02:00
hero - > setSecSkillLevel ( SecondarySkill ( curBonus - > info2 ) , curBonus - > info3 , true ) ;
break ;
}
}
}
void CGameStateCampaign : : replaceHeroesPlaceholders ( const std : : vector < CampaignHeroReplacement > & campaignHeroReplacements )
{
for ( const auto & campaignHeroReplacement : campaignHeroReplacements )
{
auto * heroPlaceholder = dynamic_cast < CGHeroPlaceholder * > ( gameState - > getObjInstance ( campaignHeroReplacement . heroPlaceholderId ) ) ;
CGHeroInstance * heroToPlace = campaignHeroReplacement . hero ;
heroToPlace - > id = campaignHeroReplacement . heroPlaceholderId ;
2023-10-24 23:58:26 +02:00
if ( heroPlaceholder - > tempOwner . isValidPlayer ( ) )
heroToPlace - > tempOwner = heroPlaceholder - > tempOwner ;
2023-06-23 18:33:47 +02:00
heroToPlace - > pos = heroPlaceholder - > pos ;
2023-10-24 16:11:25 +02:00
heroToPlace - > type = VLC - > heroh - > objects [ heroToPlace - > getHeroType ( ) . getNum ( ) ] ;
2023-06-26 15:25:34 +02:00
heroToPlace - > appearance = VLC - > objtypeh - > getHandlerFor ( Obj : : HERO , heroToPlace - > type - > heroClass - > getIndex ( ) ) - > getTemplates ( ) . front ( ) ;
2023-06-23 18:33:47 +02:00
gameState - > map - > removeBlockVisTiles ( heroPlaceholder , true ) ;
gameState - > map - > objects [ heroPlaceholder - > id . getNum ( ) ] = nullptr ;
gameState - > map - > instanceNames . erase ( heroPlaceholder - > instanceName ) ;
gameState - > map - > heroesOnMap . emplace_back ( heroToPlace ) ;
gameState - > map - > objects [ heroToPlace - > id . getNum ( ) ] = heroToPlace ;
gameState - > map - > addBlockVisTiles ( heroToPlace ) ;
gameState - > map - > instanceNames [ heroToPlace - > instanceName ] = heroToPlace ;
delete heroPlaceholder ;
}
}
2023-06-26 15:25:34 +02:00
std : : vector < CampaignHeroReplacement > CGameStateCampaign : : generateCampaignHeroesToReplace ( )
2023-06-23 18:33:47 +02:00
{
2023-06-26 15:25:34 +02:00
auto campaignState = gameState - > scenarioOps - > campState ;
2023-06-23 18:33:47 +02:00
std : : vector < CampaignHeroReplacement > campaignHeroReplacements ;
2023-06-26 15:25:34 +02:00
std : : vector < CGHeroPlaceholder * > placeholdersByPower ;
std : : vector < CGHeroPlaceholder * > placeholdersByType ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
// find all placeholders on map
2023-06-23 18:33:47 +02:00
for ( auto obj : gameState - > map - > objects )
{
2023-06-26 15:25:34 +02:00
if ( ! obj )
continue ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
if ( obj - > ID ! = Obj : : HERO_PLACEHOLDER )
continue ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
auto * heroPlaceholder = dynamic_cast < CGHeroPlaceholder * > ( obj . get ( ) ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 15:25:34 +02:00
// only 1 field must be set
2023-11-02 16:56:02 +02:00
assert ( heroPlaceholder - > powerRank . has_value ( ) ! = heroPlaceholder - > heroType . has_value ( ) ) ;
2023-06-26 15:25:34 +02:00
if ( heroPlaceholder - > powerRank )
placeholdersByPower . push_back ( heroPlaceholder ) ;
if ( heroPlaceholder - > heroType )
placeholdersByType . push_back ( heroPlaceholder ) ;
}
//selecting heroes by type
2023-10-05 15:13:52 +02:00
for ( const auto * placeholder : placeholdersByType )
2023-06-23 18:33:47 +02:00
{
2023-10-05 15:13:52 +02:00
const auto & node = campaignState - > getHeroByType ( * placeholder - > heroType ) ;
2023-06-26 15:25:34 +02:00
if ( node . isNull ( ) )
2023-06-23 18:33:47 +02:00
{
2023-06-26 15:25:34 +02:00
logGlobal - > info ( " Hero crossover: Unable to replace placeholder for %d (%s)! " , placeholder - > heroType - > getNum ( ) , VLC - > heroTypes ( ) - > getById ( * placeholder - > heroType ) - > getNameTranslated ( ) ) ;
continue ;
2023-06-23 18:33:47 +02:00
}
2023-06-26 15:25:34 +02:00
CGHeroInstance * hero = CampaignState : : crossoverDeserialize ( node , gameState - > map ) ;
2023-10-24 16:11:25 +02:00
logGlobal - > info ( " Hero crossover: Loading placeholder for %d (%s) " , hero - > getHeroType ( ) , hero - > getNameTranslated ( ) ) ;
2023-06-26 15:25:34 +02:00
campaignHeroReplacements . emplace_back ( hero , placeholder - > id ) ;
2023-06-23 18:33:47 +02:00
}
2023-06-26 15:25:34 +02:00
auto lastScenario = getHeroesSourceScenario ( ) ;
if ( ! placeholdersByPower . empty ( ) & & lastScenario )
2023-06-23 18:33:47 +02:00
{
2023-06-26 15:25:34 +02:00
// sort hero placeholders descending power
boost : : range : : sort ( placeholdersByPower , [ ] ( const CGHeroPlaceholder * a , const CGHeroPlaceholder * b )
{
return * a - > powerRank > * b - > powerRank ;
} ) ;
2023-10-05 15:13:52 +02:00
const auto & nodeList = campaignState - > getHeroesByPower ( lastScenario . value ( ) ) ;
2023-06-26 15:25:34 +02:00
auto nodeListIter = nodeList . begin ( ) ;
2023-10-05 15:13:52 +02:00
for ( const auto * placeholder : placeholdersByPower )
2023-06-23 18:33:47 +02:00
{
2023-06-26 15:25:34 +02:00
if ( nodeListIter = = nodeList . end ( ) )
break ;
CGHeroInstance * hero = CampaignState : : crossoverDeserialize ( * nodeListIter , gameState - > map ) ;
nodeListIter + + ;
2023-10-24 16:11:25 +02:00
logGlobal - > info ( " Hero crossover: Loading placeholder as %d (%s) " , hero - > getHeroType ( ) , hero - > getNameTranslated ( ) ) ;
2023-06-26 15:25:34 +02:00
campaignHeroReplacements . emplace_back ( hero , placeholder - > id ) ;
2023-06-23 18:33:47 +02:00
}
}
return campaignHeroReplacements ;
}
void CGameStateCampaign : : initHeroes ( )
{
2023-06-26 00:07:55 +02:00
auto chosenBonus = currentBonus ( ) ;
2023-06-23 18:33:47 +02:00
if ( chosenBonus & & chosenBonus - > isBonusForHero ( ) & & chosenBonus - > info1 ! = 0xFFFE ) //exclude generated heroes
{
//find human player
PlayerColor humanPlayer = PlayerColor : : NEUTRAL ;
for ( auto & elem : gameState - > players )
{
if ( elem . second . human )
{
humanPlayer = elem . first ;
break ;
}
}
assert ( humanPlayer ! = PlayerColor : : NEUTRAL ) ;
std : : vector < ConstTransitivePtr < CGHeroInstance > > & heroes = gameState - > 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 )
{
2023-10-24 16:11:25 +02:00
if ( heroe - > getHeroType ( ) . getNum ( ) = = chosenBonus - > info1 )
2023-06-23 18:33:47 +02:00
{
giveCampaignBonusToHero ( heroe ) ;
break ;
}
}
}
}
}
void CGameStateCampaign : : initStartingResources ( )
{
auto getHumanPlayerInfo = [ & ] ( ) - > std : : vector < const PlayerSettings * >
{
std : : vector < const PlayerSettings * > ret ;
for ( const auto & playerInfo : gameState - > scenarioOps - > playerInfos )
{
if ( playerInfo . second . isControlledByHuman ( ) )
ret . push_back ( & playerInfo . second ) ;
}
return ret ;
} ;
2023-06-26 00:07:55 +02:00
auto chosenBonus = currentBonus ( ) ;
2023-06-25 20:16:03 +02:00
if ( chosenBonus & & chosenBonus - > type = = CampaignBonusType : : RESOURCE )
2023-06-23 18:33:47 +02:00
{
std : : vector < const PlayerSettings * > people = getHumanPlayerInfo ( ) ; //players we will give resource bonus
for ( const PlayerSettings * ps : people )
{
2023-11-02 16:56:02 +02:00
std : : vector < GameResID > res ; //resources we will give
2023-06-23 18:33:47 +02:00
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 )
{
gameState - > players [ ps - > color ] . resources [ re ] + = chosenBonus - > info2 ;
}
}
}
}
void CGameStateCampaign : : initTowns ( )
{
2023-06-26 00:07:55 +02:00
auto chosenBonus = currentBonus ( ) ;
2023-06-23 18:33:47 +02:00
2023-06-26 16:15:47 +02:00
if ( ! chosenBonus )
return ;
if ( chosenBonus - > type ! = CampaignBonusType : : BUILDING )
return ;
for ( int g = 0 ; g < gameState - > map - > towns . size ( ) ; + + g )
2023-06-23 18:33:47 +02:00
{
2023-06-26 16:15:47 +02:00
CGTownInstance * town = gameState - > map - > towns [ g ] ;
PlayerState * owner = gameState - > getPlayerState ( town - > getOwner ( ) ) ;
if ( ! owner )
continue ;
PlayerInfo & pi = gameState - > map - > players [ owner - > color . getNum ( ) ] ;
if ( ! owner - > human )
continue ;
if ( town - > pos ! = pi . posOfMainTown )
continue ;
BuildingID newBuilding ;
if ( gameState - > scenarioOps - > campState - > formatVCMI ( ) )
newBuilding = BuildingID ( chosenBonus - > info1 ) ;
else
2023-10-24 16:11:25 +02:00
newBuilding = CBuildingHandler : : campToERMU ( chosenBonus - > info1 , town - > getFaction ( ) , town - > builtBuildings ) ;
2023-06-26 16:15:47 +02:00
// Build granted building & all prerequisites - e.g. Mages Guild Lvl 3 should also give Mages Guild Lvl 1 & 2
while ( true )
2023-06-23 18:33:47 +02:00
{
2023-06-26 16:15:47 +02:00
if ( newBuilding = = BuildingID : : NONE )
break ;
2023-06-23 18:33:47 +02:00
2023-06-26 16:15:47 +02:00
if ( town - > builtBuildings . count ( newBuilding ) ! = 0 )
break ;
2023-06-23 18:33:47 +02:00
2023-06-26 16:15:47 +02:00
town - > builtBuildings . insert ( newBuilding ) ;
auto building = town - > town - > buildings . at ( newBuilding ) ;
newBuilding = building - > upgrade ;
2023-06-23 18:33:47 +02:00
}
2023-06-26 16:15:47 +02:00
break ;
2023-06-23 18:33:47 +02:00
}
}
bool CGameStateCampaign : : playerHasStartingHero ( PlayerColor playerColor ) const
{
2023-06-26 00:07:55 +02:00
auto campaignBonus = currentBonus ( ) ;
2023-06-23 18:33:47 +02:00
if ( ! campaignBonus )
return false ;
2023-06-25 20:16:03 +02:00
if ( campaignBonus - > type = = CampaignBonusType : : HERO & & playerColor = = PlayerColor ( campaignBonus - > info1 ) )
2023-06-23 18:33:47 +02:00
return true ;
return false ;
}
2023-06-25 20:16:03 +02:00
std : : unique_ptr < CMap > CGameStateCampaign : : getCurrentMap ( ) const
2023-06-23 18:33:47 +02:00
{
2023-06-25 20:16:03 +02:00
return gameState - > scenarioOps - > campState - > getMap ( CampaignScenarioID : : NONE ) ;
2023-06-23 18:33:47 +02:00
}
VCMI_LIB_NAMESPACE_END