1 /***************************************************************************
2  *   Copyright (C) 2009 by Andrey Afletdinov <fheroes2@gmail.com>          *
3  *                                                                         *
4  *   Part of the Free Heroes2 Engine:                                      *
5  *   http://sourceforge.net/projects/fheroes2                              *
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  *   This program is distributed in the hope that it will be useful,       *
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15  *   GNU General Public License for more details.                          *
16  *                                                                         *
17  *   You should have received a copy of the GNU General Public License     *
18  *   along with this program; if not, write to the                         *
19  *   Free Software Foundation, Inc.,                                       *
20  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
21  ***************************************************************************/
22 
23 #include <algorithm>
24 #include <array>
25 
26 #include "ai.h"
27 #include "battle.h"
28 #include "campaign_data.h"
29 #include "campaign_savedata.h"
30 #include "color.h"
31 #include "difficulty.h"
32 #include "game.h"
33 #include "game_interface.h"
34 #include "game_static.h"
35 #include "kingdom.h"
36 #include "logging.h"
37 #include "players.h"
38 #include "profit.h"
39 #include "race.h"
40 #include "save_format_version.h"
41 #include "serialize.h"
42 #include "settings.h"
43 #include "tools.h"
44 #include "translations.h"
45 #include "visit.h"
46 #include "world.h"
47 
48 #include <cassert>
49 
HeroesStrongestArmy(const Heroes * h1,const Heroes * h2)50 bool HeroesStrongestArmy( const Heroes * h1, const Heroes * h2 )
51 {
52     return h1 && h2 && h2->GetArmy().isStrongerThan( h1->GetArmy() );
53 }
54 
Kingdom()55 Kingdom::Kingdom()
56     : color( Color::NONE )
57     , _lastBattleWinHeroID( 0 )
58     , lost_town_days( 0 )
59     , visited_tents_colors( 0 )
60     , _topItemInKingdomView( -1 )
61 {
62     heroes_cond_loss.reserve( 4 );
63 }
64 
Init(int clr)65 void Kingdom::Init( int clr )
66 {
67     clear();
68     color = clr;
69 
70     if ( Color::ALL & color ) {
71         heroes.reserve( GetMaxHeroes() );
72         castles.reserve( 15 );
73         resource = _getKingdomStartingResources( Game::getDifficulty() );
74     }
75     else {
76         DEBUG_LOG( DBG_GAME, DBG_WARN, "Kingdom: unknown player: " << Color::String( color ) << "(" << static_cast<int>( color ) << ")" );
77     }
78 }
79 
clear(void)80 void Kingdom::clear( void )
81 {
82     modes = 0;
83 
84     color = Color::NONE;
85     visited_tents_colors = 0;
86     lost_town_days = Game::GetLostTownDays() + 1;
87 
88     heroes.clear();
89     castles.clear();
90     visit_object.clear();
91 
92     recruits.Reset();
93 
94     heroes_cond_loss.clear();
95     puzzle_maps.reset();
96 
97     ResetLastLostHero();
98 }
99 
GetControl(void) const100 int Kingdom::GetControl( void ) const
101 {
102     return Players::GetPlayerControl( color );
103 }
104 
GetColor(void) const105 int Kingdom::GetColor( void ) const
106 {
107     return color;
108 }
109 
GetRace(void) const110 int Kingdom::GetRace( void ) const
111 {
112     return Players::GetPlayerRace( GetColor() );
113 }
114 
isLoss(void) const115 bool Kingdom::isLoss( void ) const
116 {
117     return castles.empty() && heroes.empty();
118 }
119 
isPlay(void) const120 bool Kingdom::isPlay( void ) const
121 {
122     return Players::GetPlayerInGame( color );
123 }
124 
LossPostActions(void)125 void Kingdom::LossPostActions( void )
126 {
127     if ( isPlay() ) {
128         Players::SetPlayerInGame( color, false );
129 
130         // Heroes::SetFreeman() calls Kingdom::RemoveHeroes(), which eventually calls heroes.erase()
131         while ( !heroes.empty() ) {
132             Heroes * hero = heroes.back();
133 
134             assert( hero->GetColor() == GetColor() );
135 
136             hero->SetFreeman( static_cast<int>( Battle::RESULT_LOSS ) );
137         }
138 
139         if ( !castles.empty() ) {
140             castles.ChangeColors( GetColor(), Color::NONE );
141             castles.clear();
142         }
143 
144         world.ResetCapturedObjects( GetColor() );
145     }
146 }
147 
ActionBeforeTurn(void)148 void Kingdom::ActionBeforeTurn( void )
149 {
150     // rescan heroes path
151     std::for_each( heroes.begin(), heroes.end(), []( Heroes * hero ) { hero->RescanPath(); } );
152 }
153 
ActionNewDay(void)154 void Kingdom::ActionNewDay( void )
155 {
156     // countdown of days since the loss of the last town, first day isn't counted
157     if ( world.CountDay() > 1 && castles.empty() && lost_town_days > 0 ) {
158         --lost_town_days;
159     }
160 
161     // check the conditions of the loss
162     if ( isLoss() || 0 == lost_town_days ) {
163         LossPostActions();
164         return;
165     }
166 
167     // modes
168     ResetModes( IDENTIFYHERO );
169 
170     // skip the income for the first day
171     if ( world.CountDay() > 1 ) {
172         // income
173         AddFundsResource( GetIncome() );
174 
175         // handle resource bonus campaign awards
176         if ( isControlHuman() && Settings::Get().isCampaignGameType() ) {
177             const std::vector<Campaign::CampaignAwardData> campaignAwards = Campaign::CampaignSaveData::Get().getObtainedCampaignAwards();
178 
179             for ( size_t i = 0; i < campaignAwards.size(); ++i ) {
180                 if ( campaignAwards[i]._type != Campaign::CampaignAwardData::TYPE_RESOURCE_BONUS )
181                     continue;
182 
183                 AddFundsResource( Funds( campaignAwards[i]._subType, campaignAwards[i]._amount ) );
184             }
185         }
186     }
187 
188     // check event day AI
189     EventsDate events = world.GetEventsDate( GetColor() );
190     for ( EventsDate::const_iterator it = events.begin(); it != events.end(); ++it )
191         AddFundsResource( ( *it ).resource );
192 
193     // remove day visit object
194     visit_object.remove_if( Visit::isDayLife );
195 }
196 
ActionNewWeek(void)197 void Kingdom::ActionNewWeek( void )
198 {
199     // skip the first week
200     if ( world.CountWeek() > 1 ) {
201         // debug a gift
202         if ( IS_DEVEL() && isControlHuman() ) {
203             Funds gift( 20, 20, 10, 10, 10, 10, 5000 );
204             DEBUG_LOG( DBG_GAME, DBG_INFO, "debug gift: " << gift.String() );
205             resource += gift;
206         }
207     }
208 
209     // remove week visit object
210     visit_object.remove_if( Visit::isWeekLife );
211 
212     UpdateRecruits();
213 }
214 
ActionNewMonth(void)215 void Kingdom::ActionNewMonth( void )
216 {
217     // remove month visit object
218     visit_object.remove_if( Visit::isMonthLife );
219 }
220 
AddHeroes(Heroes * hero)221 void Kingdom::AddHeroes( Heroes * hero )
222 {
223     if ( hero ) {
224         if ( heroes.end() == std::find( heroes.begin(), heroes.end(), hero ) )
225             heroes.push_back( hero );
226 
227         const Player * player = Settings::Get().GetPlayers().GetCurrent();
228         if ( player && player->isColor( GetColor() ) && player->isControlHuman() )
229             Interface::Basic::Get().GetIconsPanel().ResetIcons( ICON_HEROES );
230 
231         AI::Get().HeroesAdd( *hero );
232     }
233 }
234 
RemoveHeroes(const Heroes * hero)235 void Kingdom::RemoveHeroes( const Heroes * hero )
236 {
237     if ( hero ) {
238         if ( !heroes.empty() ) {
239             auto it = std::find( heroes.begin(), heroes.end(), hero );
240             assert( it != heroes.end() );
241             if ( it != heroes.end() ) {
242                 heroes.erase( it );
243             }
244         }
245 
246         Player * player = Players::Get( GetColor() );
247 
248         if ( player && player->GetFocus().GetHeroes() == hero ) {
249             player->GetFocus().Reset();
250         }
251 
252         assert( hero != nullptr );
253 
254         AI::Get().HeroesRemove( *hero );
255     }
256 
257     if ( isLoss() )
258         LossPostActions();
259 }
260 
AddCastle(const Castle * castle)261 void Kingdom::AddCastle( const Castle * castle )
262 {
263     if ( castle ) {
264         if ( castles.end() == std::find( castles.begin(), castles.end(), castle ) )
265             castles.push_back( const_cast<Castle *>( castle ) );
266 
267         const Player * player = Settings::Get().GetPlayers().GetCurrent();
268         if ( player && player->isColor( GetColor() ) )
269             Interface::Basic::Get().GetIconsPanel().ResetIcons( ICON_CASTLES );
270 
271         AI::Get().CastleAdd( *castle );
272     }
273 
274     lost_town_days = Game::GetLostTownDays() + 1;
275 }
276 
RemoveCastle(const Castle * castle)277 void Kingdom::RemoveCastle( const Castle * castle )
278 {
279     if ( castle ) {
280         if ( !castles.empty() ) {
281             auto it = std::find( castles.begin(), castles.end(), castle );
282             assert( it != castles.end() );
283             if ( it != castles.end() ) {
284                 castles.erase( it );
285             }
286         }
287 
288         Player * player = Players::Get( GetColor() );
289 
290         if ( player && player->GetFocus().GetCastle() == castle ) {
291             player->GetFocus().Reset();
292         }
293 
294         assert( castle != nullptr );
295 
296         AI::Get().CastleRemove( *castle );
297     }
298 
299     if ( isLoss() )
300         LossPostActions();
301 }
302 
GetCountCastle(void) const303 u32 Kingdom::GetCountCastle( void ) const
304 {
305     return static_cast<uint32_t>( std::count_if( castles.begin(), castles.end(), Castle::PredicateIsCastle ) );
306 }
307 
GetCountTown(void) const308 u32 Kingdom::GetCountTown( void ) const
309 {
310     return static_cast<uint32_t>( std::count_if( castles.begin(), castles.end(), Castle::PredicateIsTown ) );
311 }
312 
GetCountMarketplace(void) const313 u32 Kingdom::GetCountMarketplace( void ) const
314 {
315     return static_cast<uint32_t>(
316         std::count_if( castles.begin(), castles.end(), []( const Castle * castle ) { return Castle::PredicateIsBuildBuilding( castle, BUILD_MARKETPLACE ); } ) );
317 }
318 
GetCountNecromancyShrineBuild(void) const319 u32 Kingdom::GetCountNecromancyShrineBuild( void ) const
320 {
321     return static_cast<uint32_t>( std::count_if( castles.begin(), castles.end(), []( const Castle * castle ) { return castle->isNecromancyShrineBuild(); } ) );
322 }
323 
GetCountBuilding(u32 build) const324 u32 Kingdom::GetCountBuilding( u32 build ) const
325 {
326     return static_cast<uint32_t>( std::count_if( castles.begin(), castles.end(), [build]( const Castle * castle ) { return castle->isBuild( build ); } ) );
327 }
328 
GetCountThievesGuild() const329 uint32_t Kingdom::GetCountThievesGuild() const
330 {
331     return static_cast<uint32_t>(
332         std::count_if( castles.begin(), castles.end(), []( const Castle * castle ) { return Castle::PredicateIsBuildBuilding( castle, BUILD_THIEVESGUILD ); } ) );
333 }
334 
GetCountArtifacts() const335 uint32_t Kingdom::GetCountArtifacts() const
336 {
337     uint32_t result = 0;
338     for ( const Heroes * hero : heroes )
339         result += hero->GetCountArtifacts();
340     return result;
341 }
342 
AllowPayment(const Funds & funds) const343 bool Kingdom::AllowPayment( const Funds & funds ) const
344 {
345     return ( resource.wood >= funds.wood || 0 == funds.wood ) && ( resource.mercury >= funds.mercury || 0 == funds.mercury )
346            && ( resource.ore >= funds.ore || 0 == funds.ore ) && ( resource.sulfur >= funds.sulfur || 0 == funds.sulfur )
347            && ( resource.crystal >= funds.crystal || 0 == funds.crystal ) && ( resource.gems >= funds.gems || 0 == funds.gems )
348            && ( resource.gold >= funds.gold || 0 == funds.gold );
349 }
350 
351 /* is visited cell */
isVisited(const Maps::Tiles & tile) const352 bool Kingdom::isVisited( const Maps::Tiles & tile ) const
353 {
354     return isVisited( tile.GetIndex(), tile.GetObject( false ) );
355 }
356 
isVisited(s32 index,const MP2::MapObjectType objectType) const357 bool Kingdom::isVisited( s32 index, const MP2::MapObjectType objectType ) const
358 {
359     std::list<IndexObject>::const_iterator it = std::find_if( visit_object.begin(), visit_object.end(), [index]( const IndexObject & v ) { return v.isIndex( index ); } );
360     return visit_object.end() != it && ( *it ).isObject( objectType );
361 }
362 
363 /* return true if object visited */
isVisited(const MP2::MapObjectType objectType) const364 bool Kingdom::isVisited( const MP2::MapObjectType objectType ) const
365 {
366     return std::any_of( visit_object.begin(), visit_object.end(), [objectType]( const IndexObject & v ) { return v.isObject( objectType ); } );
367 }
368 
CountVisitedObjects(const MP2::MapObjectType objectType) const369 uint32_t Kingdom::CountVisitedObjects( const MP2::MapObjectType objectType ) const
370 {
371     // Safe to downcast as we don't deal with gigantic amount of data.
372     return static_cast<uint32_t>( std::count_if( visit_object.begin(), visit_object.end(), [objectType]( const IndexObject & v ) { return v.isObject( objectType ); } ) );
373 }
374 
375 /* set visited cell */
SetVisited(s32 index,const MP2::MapObjectType objectType=MP2::OBJ_ZERO)376 void Kingdom::SetVisited( s32 index, const MP2::MapObjectType objectType = MP2::OBJ_ZERO )
377 {
378     if ( !isVisited( index, objectType ) && objectType != MP2::OBJ_ZERO )
379         visit_object.push_front( IndexObject( index, objectType ) );
380 }
381 
isValidKingdomObject(const Maps::Tiles & tile,const MP2::MapObjectType objectType) const382 bool Kingdom::isValidKingdomObject( const Maps::Tiles & tile, const MP2::MapObjectType objectType ) const
383 {
384     if ( !MP2::isActionObject( objectType ) && objectType != MP2::OBJ_COAST )
385         return false;
386 
387     if ( isVisited( tile.GetIndex(), objectType ) )
388         return false;
389 
390     // Check castle first to ignore guest hero (tile with both Castle and Hero)
391     if ( tile.GetObject( false ) == MP2::OBJ_CASTLE ) {
392         const int tileColor = tile.QuantityColor();
393         if ( Players::isFriends( color, tileColor ) ) {
394             // false only if alliance castles can't be visited
395             return color == tileColor;
396         }
397         return true;
398     }
399 
400     // Hero object can overlay other objects when standing on top of it: force check with GetObject( true )
401     if ( objectType == MP2::OBJ_HEROES ) {
402         const Heroes * hero = tile.GetHeroes();
403         return hero && ( color == hero->GetColor() || !Players::isFriends( color, hero->GetColor() ) );
404     }
405 
406     if ( MP2::isCaptureObject( objectType ) )
407         return !Players::isFriends( color, tile.QuantityColor() );
408 
409     if ( MP2::isQuantityObject( objectType ) )
410         return tile.QuantityIsValid();
411 
412     return true;
413 }
414 
HeroesMayStillMove(void) const415 bool Kingdom::HeroesMayStillMove( void ) const
416 {
417     return std::any_of( heroes.begin(), heroes.end(), []( const Heroes * hero ) { return hero->MayStillMove( false, false ); } );
418 }
419 
AddFundsResource(const Funds & funds)420 void Kingdom::AddFundsResource( const Funds & funds )
421 {
422     resource = resource + funds;
423     resource.Trim();
424 }
425 
OddFundsResource(const Funds & funds)426 void Kingdom::OddFundsResource( const Funds & funds )
427 {
428     resource = resource - funds;
429     resource.Trim();
430 }
431 
GetLostTownDays(void) const432 u32 Kingdom::GetLostTownDays( void ) const
433 {
434     return lost_town_days;
435 }
436 
GetRecruits(void)437 Recruits & Kingdom::GetRecruits( void )
438 {
439     // update hero1
440     if ( Heroes::UNKNOWN == recruits.GetID1() || ( recruits.GetHero1() && !recruits.GetHero1()->isFreeman() ) ) {
441         const bool preferNative = recruits.GetID1() == Heroes::UNKNOWN && recruits.GetID2() == Heroes::UNKNOWN;
442 
443         recruits.SetHero1( world.GetFreemanHeroes( preferNative ? GetRace() : Race::NONE ) );
444     }
445 
446     // update hero2
447     if ( Heroes::UNKNOWN == recruits.GetID2() || ( recruits.GetHero2() && !recruits.GetHero2()->isFreeman() ) )
448         recruits.SetHero2( world.GetFreemanHeroes() );
449 
450     if ( recruits.GetID1() == recruits.GetID2() )
451         world.UpdateRecruits( recruits );
452 
453     return recruits;
454 }
455 
UpdateRecruits(void)456 void Kingdom::UpdateRecruits( void )
457 {
458     bool hasSpecialHireableHero = false;
459     if ( isControlHuman() && ( Settings::Get().isCampaignGameType() ) && world.CountWeek() < 2 ) {
460         const std::vector<Campaign::CampaignAwardData> obtainedAwards = Campaign::CampaignSaveData::Get().getObtainedCampaignAwards();
461 
462         for ( size_t i = 0; i < obtainedAwards.size(); ++i ) {
463             if ( obtainedAwards[i]._type != Campaign::CampaignAwardData::TYPE_HIREABLE_HERO )
464                 continue;
465 
466             // Use the standard GetHeroes() function instead of GetFreemanHeroesSpecial() and check the hero's freeman status below
467             const Heroes * hero = world.GetHeroes( obtainedAwards[i]._subType );
468 
469             if ( hero && hero->isFreeman() ) {
470                 recruits.SetHero1( hero );
471                 hasSpecialHireableHero = true;
472                 break;
473             }
474         }
475     }
476 
477     if ( !hasSpecialHireableHero ) {
478         const bool preferNative = recruits.GetID1() == Heroes::UNKNOWN && recruits.GetID2() == Heroes::UNKNOWN;
479         recruits.SetHero1( world.GetFreemanHeroes( preferNative ? GetRace() : Race::NONE ) );
480     }
481 
482     recruits.SetHero2( world.GetFreemanHeroes() );
483 
484     if ( recruits.GetID1() == recruits.GetID2() )
485         world.UpdateRecruits( recruits );
486 }
487 
PuzzleMaps(void)488 Puzzle & Kingdom::PuzzleMaps( void )
489 {
490     return puzzle_maps;
491 }
492 
SetVisitTravelersTent(int col)493 void Kingdom::SetVisitTravelersTent( int col )
494 {
495     // visited_tents_color is a bitfield
496     visited_tents_colors |= ( 1 << col );
497 }
498 
IsVisitTravelersTent(int col) const499 bool Kingdom::IsVisitTravelersTent( int col ) const
500 {
501     // visited_tents_color is a bitfield
502     return ( visited_tents_colors & ( 1 << col ) ) != 0;
503 }
504 
AllowRecruitHero(bool check_payment,int level) const505 bool Kingdom::AllowRecruitHero( bool check_payment, int level ) const
506 {
507     return ( heroes.size() < GetMaxHeroes() ) && ( !check_payment || AllowPayment( PaymentConditions::RecruitHero( level ) ) );
508 }
509 
ApplyPlayWithStartingHero(void)510 void Kingdom::ApplyPlayWithStartingHero( void )
511 {
512     if ( !isPlay() || castles.empty() )
513         return;
514 
515     bool foundHeroes = false;
516 
517     for ( KingdomCastles::const_iterator it = castles.begin(); it != castles.end(); ++it ) {
518         const Castle * castle = *it;
519         if ( castle == nullptr )
520             continue;
521 
522         // check manual set hero (castle position + point(0, 1))?
523         const fheroes2::Point & cp = castle->GetCenter();
524         Heroes * hero = world.GetTiles( cp.x, cp.y + 1 ).GetHeroes();
525 
526         // and move manual set hero to castle
527         if ( hero && hero->GetColor() == GetColor() ) {
528             const bool patrol = hero->Modes( Heroes::PATROL );
529             if ( hero->isValid() ) {
530                 hero->Move2Dest( Maps::GetIndexFromAbsPoint( cp ) );
531             }
532             else {
533                 hero->SetFreeman( 0 );
534                 hero->Recruit( *castle );
535             }
536 
537             if ( patrol ) {
538                 hero->SetModes( Heroes::PATROL );
539                 hero->SetCenterPatrol( cp );
540             }
541             foundHeroes = true;
542         }
543     }
544 
545     if ( !foundHeroes && Settings::Get().GameStartWithHeroes() ) {
546         // get first castle
547         const Castle * first = castles.GetFirstCastle();
548         if ( nullptr == first )
549             first = castles.front();
550 
551         Heroes * hero = world.GetFreemanHeroes( first->GetRace() );
552         if ( hero && AllowRecruitHero( false, 0 ) )
553             hero->Recruit( *first );
554     }
555 }
556 
GetMaxHeroes(void)557 u32 Kingdom::GetMaxHeroes( void )
558 {
559     return GameStatic::GetKingdomMaxHeroes();
560 }
561 
GetIncome(int type) const562 Funds Kingdom::GetIncome( int type /* INCOME_ALL */ ) const
563 {
564     Funds totalIncome;
565 
566     if ( INCOME_CAPTURED & type ) {
567         // captured object
568         const int resources[]
569             = {Resource::WOOD, Resource::ORE, Resource::MERCURY, Resource::SULFUR, Resource::CRYSTAL, Resource::GEMS, Resource::GOLD, Resource::UNKNOWN};
570 
571         for ( u32 index = 0; resources[index] != Resource::UNKNOWN; ++index )
572             totalIncome += ProfitConditions::FromMine( resources[index] ) * world.CountCapturedMines( resources[index], GetColor() );
573     }
574 
575     if ( INCOME_CASTLES & type ) {
576         // castles
577         for ( KingdomCastles::const_iterator it = castles.begin(); it != castles.end(); ++it ) {
578             const Castle & castle = **it;
579 
580             // castle or town profit
581             totalIncome += ProfitConditions::FromBuilding( ( castle.isCastle() ? BUILD_CASTLE : BUILD_TENT ), 0 );
582 
583             // statue
584             if ( castle.isBuild( BUILD_STATUE ) )
585                 totalIncome += ProfitConditions::FromBuilding( BUILD_STATUE, 0 );
586 
587             // dungeon for warlock
588             if ( castle.isBuild( BUILD_SPEC ) && Race::WRLK == castle.GetRace() )
589                 totalIncome += ProfitConditions::FromBuilding( BUILD_SPEC, Race::WRLK );
590         }
591     }
592 
593     if ( INCOME_ARTIFACTS & type ) {
594         // find artifacts
595         const std::array<int, 10> artifacts
596             = { Artifact::GOLDEN_GOOSE,         Artifact::ENDLESS_SACK_GOLD,    Artifact::ENDLESS_BAG_GOLD,   Artifact::ENDLESS_PURSE_GOLD,
597                 Artifact::ENDLESS_POUCH_SULFUR, Artifact::ENDLESS_VIAL_MERCURY, Artifact::ENDLESS_POUCH_GEMS, Artifact::ENDLESS_CORD_WOOD,
598                 Artifact::ENDLESS_CART_ORE,     Artifact::ENDLESS_POUCH_CRYSTAL };
599 
600         for ( const Heroes * hero : heroes ) {
601             for ( const int art : artifacts )
602                 totalIncome += ProfitConditions::FromArtifact( art ) * hero->artifactCount( Artifact( art ) );
603             // TAX_LIEN
604             totalIncome -= ProfitConditions::FromArtifact( Artifact::TAX_LIEN ) * hero->artifactCount( Artifact( Artifact::TAX_LIEN ) );
605         }
606     }
607 
608     if ( INCOME_HEROSKILLS & type ) {
609         // estates skill bonus
610         for ( KingdomHeroes::const_iterator ith = heroes.begin(); ith != heroes.end(); ++ith )
611             totalIncome.gold += ( **ith ).GetSecondaryValues( Skill::Secondary::ESTATES );
612     }
613 
614     if ( isControlAI() ) {
615         totalIncome.gold = static_cast<int32_t>( totalIncome.gold * Difficulty::GetGoldIncomeBonus( Game::getDifficulty() ) );
616     }
617 
618     return totalIncome;
619 }
620 
GetBestHero()621 Heroes * Kingdom::GetBestHero()
622 {
623     return !heroes.empty() ? *std::max_element( heroes.begin(), heroes.end(), HeroesStrongestArmy ) : nullptr;
624 }
625 
GetStrongestMonster() const626 Monster Kingdom::GetStrongestMonster() const
627 {
628     Monster monster( Monster::UNKNOWN );
629     for ( const Heroes * hero : heroes ) {
630         const Monster currentMonster = hero->GetArmy().GetStrongestMonster();
631         if ( currentMonster.GetMonsterStrength() > monster.GetMonsterStrength() ) {
632             monster = currentMonster;
633         }
634     }
635     for ( const Castle * castle : castles ) {
636         const Monster currentMonster = castle->GetArmy().GetStrongestMonster();
637         if ( currentMonster.GetMonsterStrength() > monster.GetMonsterStrength() ) {
638             monster = currentMonster;
639         }
640     }
641     return monster;
642 }
643 
GetArmiesStrength(void) const644 double Kingdom::GetArmiesStrength( void ) const
645 {
646     double res = 0;
647 
648     for ( KingdomHeroes::const_iterator ith = heroes.begin(); ith != heroes.end(); ++ith )
649         res += ( **ith ).GetArmy().GetStrength();
650 
651     for ( KingdomCastles::const_iterator itc = castles.begin(); itc != castles.end(); ++itc )
652         res += ( **itc ).GetArmy().GetStrength();
653 
654     return res;
655 }
656 
Init(void)657 void Kingdoms::Init( void )
658 {
659     const Colors colors( Settings::Get().GetPlayers().GetColors() );
660 
661     clear();
662 
663     for ( Colors::const_iterator it = colors.begin(); it != colors.end(); ++it )
664         GetKingdom( *it ).Init( *it );
665 }
666 
clear(void)667 void Kingdoms::clear( void )
668 {
669     for ( Kingdom & kingdom : kingdoms )
670         kingdom.clear();
671 }
672 
ApplyPlayWithStartingHero(void)673 void Kingdoms::ApplyPlayWithStartingHero( void )
674 {
675     for ( Kingdom & kingdom : kingdoms )
676         if ( kingdom.isPlay() )
677             kingdom.ApplyPlayWithStartingHero();
678 }
679 
GetKingdom(int color) const680 const Kingdom & Kingdoms::GetKingdom( int color ) const
681 {
682     switch ( color ) {
683     case Color::BLUE:
684         return kingdoms[0];
685     case Color::GREEN:
686         return kingdoms[1];
687     case Color::RED:
688         return kingdoms[2];
689     case Color::YELLOW:
690         return kingdoms[3];
691     case Color::ORANGE:
692         return kingdoms[4];
693     case Color::PURPLE:
694         return kingdoms[5];
695     default:
696         break;
697     }
698 
699     return kingdoms[6];
700 }
701 
GetKingdom(int color)702 Kingdom & Kingdoms::GetKingdom( int color )
703 {
704     switch ( color ) {
705     case Color::BLUE:
706         return kingdoms[0];
707     case Color::GREEN:
708         return kingdoms[1];
709     case Color::RED:
710         return kingdoms[2];
711     case Color::YELLOW:
712         return kingdoms[3];
713     case Color::ORANGE:
714         return kingdoms[4];
715     case Color::PURPLE:
716         return kingdoms[5];
717     default:
718         break;
719     }
720 
721     return kingdoms[6];
722 }
723 
SetLastLostHero(const Heroes & hero)724 void Kingdom::SetLastLostHero( const Heroes & hero )
725 {
726     lost_hero.id = hero.GetID();
727     lost_hero.date = world.CountDay();
728 }
729 
SetLastBattleWinHero(const Heroes & hero)730 void Kingdom::SetLastBattleWinHero( const Heroes & hero )
731 {
732     _lastBattleWinHeroID = hero.GetID();
733 }
734 
ResetLastLostHero(void)735 void Kingdom::ResetLastLostHero( void )
736 {
737     lost_hero.id = Heroes::UNKNOWN;
738     lost_hero.date = 0;
739 }
740 
GetLastLostHero(void) const741 Heroes * Kingdom::GetLastLostHero( void ) const
742 {
743     return Heroes::UNKNOWN != lost_hero.id && world.CountDay() - lost_hero.date < DAYOFWEEK ? world.GetHeroes( lost_hero.id ) : nullptr;
744 }
745 
GetLastBattleWinHero() const746 Heroes * Kingdom::GetLastBattleWinHero() const
747 {
748     return Heroes::UNKNOWN != _lastBattleWinHeroID ? world.GetHeroes( _lastBattleWinHeroID ) : nullptr;
749 }
750 
NewDay(void)751 void Kingdoms::NewDay( void )
752 {
753     for ( Kingdom & kingdom : kingdoms )
754         if ( kingdom.isPlay() )
755             kingdom.ActionNewDay();
756 }
757 
NewWeek(void)758 void Kingdoms::NewWeek( void )
759 {
760     for ( Kingdom & kingdom : kingdoms )
761         if ( kingdom.isPlay() )
762             kingdom.ActionNewWeek();
763 }
764 
NewMonth(void)765 void Kingdoms::NewMonth( void )
766 {
767     for ( Kingdom & kingdom : kingdoms )
768         if ( kingdom.isPlay() )
769             kingdom.ActionNewMonth();
770 }
771 
GetNotLossColors(void) const772 int Kingdoms::GetNotLossColors( void ) const
773 {
774     int result = 0;
775     for ( const Kingdom & kingdom : kingdoms )
776         if ( kingdom.GetColor() && !kingdom.isLoss() )
777             result |= kingdom.GetColor();
778     return result;
779 }
780 
FindWins(int cond) const781 int Kingdoms::FindWins( int cond ) const
782 {
783     for ( const Kingdom & kingdom : kingdoms )
784         if ( kingdom.GetColor() && world.KingdomIsWins( kingdom, cond ) )
785             return kingdom.GetColor();
786     return 0;
787 }
788 
AddHeroes(const AllHeroes & heroes)789 void Kingdoms::AddHeroes( const AllHeroes & heroes )
790 {
791     for ( AllHeroes::const_iterator it = heroes.begin(); it != heroes.end(); ++it )
792         // skip gray color
793         if ( ( *it )->GetColor() )
794             GetKingdom( ( *it )->GetColor() ).AddHeroes( *it );
795 }
796 
AddCastles(const AllCastles & castles)797 void Kingdoms::AddCastles( const AllCastles & castles )
798 {
799     for ( const auto & castle : castles ) {
800         // skip gray color
801         if ( castle->GetColor() )
802             GetKingdom( castle->GetColor() ).AddCastle( castle );
803     }
804 }
805 
AddTributeEvents(CapturedObjects & captureobj,const uint32_t day,const MP2::MapObjectType objectType)806 void Kingdoms::AddTributeEvents( CapturedObjects & captureobj, const uint32_t day, const MP2::MapObjectType objectType )
807 {
808     for ( Kingdom & kingdom : kingdoms ) {
809         if ( kingdom.isPlay() ) {
810             const int color = kingdom.GetColor();
811             Funds funds;
812             int objectCount = 0;
813 
814             captureobj.tributeCapturedObjects( color, objectType, funds, objectCount );
815             if ( objectCount == 0 ) {
816                 continue;
817             }
818 
819             kingdom.AddFundsResource( funds );
820 
821             // for show dialogs
822             if ( funds.GetValidItemsCount() && kingdom.isControlHuman() ) {
823                 EventDate event;
824 
825                 event.computer = true;
826                 event.first = day;
827                 event.colors = color;
828                 event.resource = funds;
829 
830                 if ( objectCount > 1 ) {
831                     event.title = std::to_string( objectCount );
832                     event.title += ' ';
833                     event.title += MP2::getPluralObjectName( objectType, objectCount );
834                 }
835                 else {
836                     event.title = MP2::StringObject( objectType );
837                 }
838 
839                 world.AddEventDate( event );
840             }
841         }
842     }
843 }
844 
845 // Check if tile is visible from any crystal ball of any hero
IsTileVisibleFromCrystalBall(const int32_t dest) const846 bool Kingdom::IsTileVisibleFromCrystalBall( const int32_t dest ) const
847 {
848     for ( const Heroes * hero : heroes ) {
849         if ( hero->hasArtifact( Artifact::CRYSTAL_BALL ) ) {
850             const uint32_t crystalBallDistance = hero->GetVisionsDistance();
851             if ( Maps::GetApproximateDistance( hero->GetIndex(), dest ) <= crystalBallDistance ) {
852                 return true;
853             }
854         }
855     }
856     return false;
857 }
858 
_getKingdomStartingResources(const int difficulty)859 cost_t Kingdom::_getKingdomStartingResources( const int difficulty )
860 {
861     if ( isControlAI() )
862         return { 10000, 30, 10, 30, 10, 10, 10 };
863 
864     switch ( difficulty ) {
865     case Difficulty::EASY:
866         return { 10000, 30, 10, 30, 10, 10, 10 };
867     case Difficulty::NORMAL:
868         return { 7500, 20, 5, 20, 5, 5, 5 };
869     case Difficulty::HARD:
870         return { 5000, 10, 2, 10, 2, 2, 2 };
871     case Difficulty::EXPERT:
872         return { 2500, 5, 0, 5, 0, 0, 0 };
873     case Difficulty::IMPOSSIBLE:
874         return { 0, 0, 0, 0, 0, 0, 0 };
875     default:
876         // Did you add a new difficulty level?
877         assert( 0 );
878         break;
879     }
880 
881     return { 7500, 20, 5, 20, 5, 5, 5 };
882 }
883 
operator <<(StreamBase & msg,const Kingdom & kingdom)884 StreamBase & operator<<( StreamBase & msg, const Kingdom & kingdom )
885 {
886     return msg << kingdom.modes << kingdom.color << kingdom.resource << kingdom.lost_town_days << kingdom.castles << kingdom.heroes << kingdom.recruits
887                << kingdom.lost_hero << kingdom.visit_object << kingdom.puzzle_maps << kingdom.visited_tents_colors << kingdom.heroes_cond_loss
888                << kingdom._lastBattleWinHeroID << kingdom._topItemInKingdomView;
889 }
890 
operator >>(StreamBase & msg,Kingdom & kingdom)891 StreamBase & operator>>( StreamBase & msg, Kingdom & kingdom )
892 {
893     msg >> kingdom.modes >> kingdom.color >> kingdom.resource >> kingdom.lost_town_days >> kingdom.castles >> kingdom.heroes >> kingdom.recruits >> kingdom.lost_hero
894         >> kingdom.visit_object >> kingdom.puzzle_maps >> kingdom.visited_tents_colors >> kingdom.heroes_cond_loss >> kingdom._lastBattleWinHeroID;
895 
896     static_assert( LAST_SUPPORTED_FORMAT_VERSION < FORMAT_VERSION_097_RELEASE, "Remove the check below." );
897     if ( Game::GetLoadVersion() >= FORMAT_VERSION_097_RELEASE ) {
898         msg >> kingdom._topItemInKingdomView;
899     }
900     else {
901         kingdom._topItemInKingdomView = -1;
902     }
903 
904     return msg;
905 }
906 
operator <<(StreamBase & msg,const Kingdoms & obj)907 StreamBase & operator<<( StreamBase & msg, const Kingdoms & obj )
908 {
909     msg << Kingdoms::_size;
910     for ( const Kingdom & kingdom : obj.kingdoms )
911         msg << kingdom;
912 
913     return msg;
914 }
915 
operator >>(StreamBase & msg,Kingdoms & obj)916 StreamBase & operator>>( StreamBase & msg, Kingdoms & obj )
917 {
918     u32 kingdomscount = 0;
919     msg >> kingdomscount;
920 
921     if ( kingdomscount <= Kingdoms::_size ) {
922         for ( u32 i = 0; i < kingdomscount; ++i )
923             msg >> obj.kingdoms[i];
924     }
925 
926     return msg;
927 }
928 
operator >>(StreamBase & sb,LastLoseHero & st)929 StreamBase & operator>>( StreamBase & sb, LastLoseHero & st )
930 {
931     return sb >> st.id >> st.date;
932 }
933 
operator <<(StreamBase & sb,const LastLoseHero & hero)934 StreamBase & operator<<( StreamBase & sb, const LastLoseHero & hero )
935 {
936     return sb << hero.id << hero.date;
937 }
938