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