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 <vector>
25 
26 #include "agg.h"
27 #include "agg_image.h"
28 #include "ai.h"
29 #include "audio.h"
30 #include "battle_only.h"
31 #include "castle.h"
32 #include "cursor.h"
33 #include "dialog.h"
34 #include "game.h"
35 #include "game_delays.h"
36 #include "game_interface.h"
37 #include "game_io.h"
38 #include "game_over.h"
39 #include "heroes.h"
40 #include "icn.h"
41 #include "kingdom.h"
42 #include "logging.h"
43 #include "m82.h"
44 #include "maps_tiles.h"
45 #include "mus.h"
46 #include "route.h"
47 #include "settings.h"
48 #include "text.h"
49 #include "tools.h"
50 #include "translations.h"
51 #include "world.h"
52 
53 namespace
54 {
SortPlayers(const Player * player1,const Player * player2)55     bool SortPlayers( const Player * player1, const Player * player2 )
56     {
57         return ( player1->isControlHuman() && !player2->isControlHuman() )
58                || ( ( player1->isControlHuman() == player2->isControlHuman() ) && ( player1->GetColor() < player2->GetColor() ) );
59     }
60 }
61 
StartBattleOnly(void)62 fheroes2::GameMode Game::StartBattleOnly( void )
63 {
64     Battle::Only main;
65 
66     if ( main.ChangeSettings() )
67         main.StartBattle();
68 
69     return fheroes2::GameMode::MAIN_MENU;
70 }
71 
StartGame()72 fheroes2::GameMode Game::StartGame()
73 {
74     AI::Get().Reset();
75 
76     const Settings & conf = Settings::Get();
77 
78     // setup cursor
79     const CursorRestorer cursorRestorer( true, Cursor::POINTER );
80 
81     if ( !conf.LoadedGameVersion() )
82         GameOver::Result::Get().Reset();
83 
84     AGG::ResetMixer( true );
85 
86     Interface::Basic::Get().Reset();
87 
88     return Interface::Basic::Get().StartGame();
89 }
90 
DialogPlayers(int color,std::string str)91 void Game::DialogPlayers( int color, std::string str )
92 {
93     const Player * player = Players::Get( color );
94     StringReplace( str, "%{color}", ( player ? player->GetName() : Color::String( color ) ) );
95 
96     const fheroes2::Sprite & border = fheroes2::AGG::GetICN( ICN::BRCREST, 6 );
97     fheroes2::Sprite sign = border;
98 
99     switch ( color ) {
100     case Color::BLUE:
101         fheroes2::Blit( fheroes2::AGG::GetICN( ICN::BRCREST, 0 ), sign, 4, 4 );
102         break;
103     case Color::GREEN:
104         fheroes2::Blit( fheroes2::AGG::GetICN( ICN::BRCREST, 1 ), sign, 4, 4 );
105         break;
106     case Color::RED:
107         fheroes2::Blit( fheroes2::AGG::GetICN( ICN::BRCREST, 2 ), sign, 4, 4 );
108         break;
109     case Color::YELLOW:
110         fheroes2::Blit( fheroes2::AGG::GetICN( ICN::BRCREST, 3 ), sign, 4, 4 );
111         break;
112     case Color::ORANGE:
113         fheroes2::Blit( fheroes2::AGG::GetICN( ICN::BRCREST, 4 ), sign, 4, 4 );
114         break;
115     case Color::PURPLE:
116         fheroes2::Blit( fheroes2::AGG::GetICN( ICN::BRCREST, 5 ), sign, 4, 4 );
117         break;
118     default:
119         break;
120     }
121 
122     Dialog::SpriteInfo( "", str, sign );
123 }
124 
125 /* open castle wrapper */
OpenCastleDialog(Castle & castle,bool updateFocus)126 void Game::OpenCastleDialog( Castle & castle, bool updateFocus /* = true */ )
127 {
128     // setup cursor
129     const CursorRestorer cursorRestorer( true, Cursor::POINTER );
130 
131     Mixer::Pause();
132 
133     const Settings & conf = Settings::Get();
134     Kingdom & myKingdom = world.GetKingdom( conf.CurrentColor() );
135     const KingdomCastles & myCastles = myKingdom.GetCastles();
136     KingdomCastles::const_iterator it = std::find( myCastles.begin(), myCastles.end(), &castle );
137     Interface::StatusWindow::ResetTimer();
138 
139     const size_t heroCountBefore = myKingdom.GetHeroes().size();
140 
141     if ( it != myCastles.end() ) {
142         Castle::CastleDialogReturnValue result = Castle::CastleDialogReturnValue::DoNothing;
143 
144         while ( result != Castle::CastleDialogReturnValue::Close ) {
145             assert( it != myCastles.end() );
146 
147             const bool openConstructionWindow
148                 = ( result == Castle::CastleDialogReturnValue::PreviousCostructionWindow ) || ( result == Castle::CastleDialogReturnValue::NextCostructionWindow );
149 
150             result = ( *it )->OpenDialog( false, openConstructionWindow );
151 
152             if ( result == Castle::CastleDialogReturnValue::PreviousCastle || result == Castle::CastleDialogReturnValue::PreviousCostructionWindow ) {
153                 if ( it == myCastles.begin() )
154                     it = myCastles.end();
155                 --it;
156             }
157             else if ( result == Castle::CastleDialogReturnValue::NextCastle || result == Castle::CastleDialogReturnValue::NextCostructionWindow ) {
158                 ++it;
159                 if ( it == myCastles.end() )
160                     it = myCastles.begin();
161             }
162         }
163     }
164     else if ( castle.isFriends( conf.CurrentColor() ) ) {
165         castle.OpenDialog( true, false );
166     }
167 
168     Interface::Basic & basicInterface = Interface::Basic::Get();
169 
170     if ( updateFocus ) {
171         if ( heroCountBefore < myKingdom.GetHeroes().size() ) {
172             basicInterface.SetFocus( myKingdom.GetHeroes()[heroCountBefore] );
173         }
174         else if ( it != myCastles.end() ) {
175             Heroes * heroInCastle = world.GetTiles( ( *it )->GetIndex() ).GetHeroes();
176             if ( heroInCastle == nullptr ) {
177                 basicInterface.SetFocus( *it );
178             }
179             else {
180                 basicInterface.SetFocus( heroInCastle );
181             }
182         }
183         else {
184             basicInterface.ResetFocus( GameFocus::HEROES );
185         }
186     }
187     else {
188         // If we don't update focus, we still have to restore environment sounds and terrain music theme
189         restoreSoundsForCurrentFocus();
190     }
191 
192     basicInterface.RedrawFocus();
193 }
194 
195 /* open heroes wrapper */
OpenHeroesDialog(Heroes & hero,bool updateFocus,bool windowIsGameWorld,bool disableDismiss)196 void Game::OpenHeroesDialog( Heroes & hero, bool updateFocus, bool windowIsGameWorld, bool disableDismiss /* = false */ )
197 {
198     // setup cursor
199     const CursorRestorer cursorRestorer( true, Cursor::POINTER );
200 
201     Interface::StatusWindow::ResetTimer();
202 
203     const Settings & conf = Settings::Get();
204     bool needFade = conf.ExtGameUseFade() && fheroes2::Display::instance().isDefaultSize();
205 
206     Interface::Basic & I = Interface::Basic::Get();
207     const Interface::GameArea & gameArea = I.GetGameArea();
208 
209     const KingdomHeroes & myHeroes = hero.GetKingdom().GetHeroes();
210     KingdomHeroes::const_iterator it = std::find( myHeroes.begin(), myHeroes.end(), &hero );
211 
212     int result = Dialog::ZERO;
213 
214     while ( it != myHeroes.end() && result != Dialog::CANCEL ) {
215         result = ( *it )->OpenDialog( false, needFade, disableDismiss );
216         if ( needFade )
217             needFade = false;
218 
219         switch ( result ) {
220         case Dialog::PREV:
221             if ( it == myHeroes.begin() )
222                 it = myHeroes.end();
223             --it;
224             break;
225 
226         case Dialog::NEXT:
227             ++it;
228             if ( it == myHeroes.end() )
229                 it = myHeroes.begin();
230             break;
231 
232         case Dialog::DISMISS:
233             AGG::PlaySound( M82::KILLFADE );
234 
235             ( *it )->GetPath().Hide();
236             gameArea.SetRedraw();
237 
238             if ( windowIsGameWorld ) {
239                 ( *it )->FadeOut();
240             }
241 
242             ( *it )->SetFreeman( 0 );
243             it = myHeroes.end();
244 
245             updateFocus = true;
246 
247             result = Dialog::CANCEL;
248             break;
249 
250         default:
251             break;
252         }
253     }
254 
255     if ( updateFocus ) {
256         if ( it != myHeroes.end() ) {
257             I.SetFocus( *it );
258         }
259         else {
260             I.ResetFocus( GameFocus::HEROES );
261         }
262     }
263 
264     I.RedrawFocus();
265 }
266 
ShowNewWeekDialog(void)267 void ShowNewWeekDialog( void )
268 {
269     // restore the original music on exit
270     const Game::MusicRestorer musicRestorer;
271 
272     AGG::PlayMusic( world.BeginMonth() ? MUS::NEW_MONTH : MUS::NEW_WEEK, false );
273 
274     const Week & week = world.GetWeekType();
275 
276     // head
277     std::string message = world.BeginMonth() ? _( "Astrologers proclaim Month of the %{name}." ) : _( "Astrologers proclaim Week of the %{name}." );
278     StringReplace( message, "%{name}", week.GetName() );
279     message += "\n \n";
280 
281     if ( week.GetType() == WeekName::MONSTERS ) {
282         const Monster monster( week.GetMonster() );
283         const u32 count = world.BeginMonth() ? Castle::GetGrownMonthOf() : Castle::GetGrownWeekOf();
284 
285         if ( monster.isValid() && count ) {
286             if ( world.BeginMonth() )
287                 message += 100 == Castle::GetGrownMonthOf() ? _( "After regular growth, the population of %{monster} is doubled!" )
288                                                             : _n( "After regular growth, the population of %{monster} increases by %{count} percent!",
289                                                                   "After regular growth, the population of %{monster} increases by %{count} percent!", count );
290             else
291                 message += _( "%{monster} population increases by +%{count}." );
292             StringReplace( message, "%{monster}", monster.GetMultiName() );
293             StringReplace( message, "%{count}", count );
294             message += "\n \n";
295         }
296     }
297 
298     if ( week.GetType() == WeekName::PLAGUE )
299         message += _( " All populations are halved." );
300     else
301         message += _( " All dwellings increase population." );
302 
303     Dialog::Message( "", message, Font::BIG, Dialog::OK );
304 }
305 
ShowEventDayDialog(void)306 void ShowEventDayDialog( void )
307 {
308     const Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() );
309     const EventsDate events = world.GetEventsDate( myKingdom.GetColor() );
310 
311     for ( const EventDate & event : events ) {
312         if ( event.resource.GetValidItemsCount() ) {
313             Dialog::ResourceInfo( event.title, event.message, event.resource );
314         }
315         else if ( !event.message.empty() ) {
316             Dialog::Message( event.title, event.message, Font::BIG, Dialog::OK );
317         }
318     }
319 }
320 
ShowWarningLostTownsDialog()321 void ShowWarningLostTownsDialog()
322 {
323     const Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() );
324     const uint32_t lostTownDays = myKingdom.GetLostTownDays();
325 
326     if ( lostTownDays == 1 ) {
327         Game::DialogPlayers( myKingdom.GetColor(), _( "%{color} player, this is your last day to capture a town, or you will be banished from this land." ) );
328     }
329     else if ( lostTownDays > 0 && lostTownDays <= Game::GetLostTownDays() ) {
330         std::string str = _( "%{color} player, you only have %{day} days left to capture a town, or you will be banished from this land." );
331         StringReplace( str, "%{day}", lostTownDays );
332         Game::DialogPlayers( myKingdom.GetColor(), str );
333     }
334 }
335 
GetCursorFocusCastle(const Castle & from_castle,const Maps::Tiles & tile)336 int Interface::Basic::GetCursorFocusCastle( const Castle & from_castle, const Maps::Tiles & tile )
337 {
338     switch ( tile.GetObject() ) {
339     case MP2::OBJN_CASTLE:
340     case MP2::OBJ_CASTLE: {
341         const Castle * to_castle = world.getCastle( tile.GetCenter() );
342 
343         if ( nullptr != to_castle )
344             return to_castle->GetColor() == from_castle.GetColor() ? Cursor::CASTLE : Cursor::POINTER;
345         break;
346     }
347 
348     case MP2::OBJ_HEROES: {
349         const Heroes * heroes = tile.GetHeroes();
350 
351         if ( nullptr != heroes )
352             return heroes->GetColor() == from_castle.GetColor() ? Cursor::HEROES : Cursor::POINTER;
353         break;
354     }
355 
356     default:
357         break;
358     }
359 
360     return Cursor::POINTER;
361 }
362 
GetCursorFocusShipmaster(const Heroes & from_hero,const Maps::Tiles & tile)363 int Interface::Basic::GetCursorFocusShipmaster( const Heroes & from_hero, const Maps::Tiles & tile )
364 {
365     const bool water = tile.isWater();
366 
367     switch ( tile.GetObject() ) {
368     case MP2::OBJ_MONSTER:
369         return water ? Cursor::DistanceThemes( Cursor::CURSOR_HERO_FIGHT, from_hero.GetRangeRouteDays( tile.GetIndex() ) ) : Cursor::POINTER;
370 
371     case MP2::OBJ_BOAT:
372         return Cursor::POINTER;
373 
374     case MP2::OBJN_CASTLE:
375     case MP2::OBJ_CASTLE: {
376         const Castle * castle = world.getCastle( tile.GetCenter() );
377 
378         if ( castle )
379             return from_hero.GetColor() == castle->GetColor() ? Cursor::CASTLE : Cursor::POINTER;
380         break;
381     }
382 
383     case MP2::OBJ_HEROES: {
384         const Heroes * to_hero = tile.GetHeroes();
385 
386         if ( to_hero ) {
387             if ( !to_hero->isShipMaster() )
388                 return from_hero.GetColor() == to_hero->GetColor() ? Cursor::HEROES : Cursor::POINTER;
389             else if ( to_hero->GetCenter() == from_hero.GetCenter() )
390                 return Cursor::HEROES;
391             else if ( from_hero.GetColor() == to_hero->GetColor() )
392                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_MEET, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
393             else if ( from_hero.isFriends( to_hero->GetColor() ) )
394                 return Cursor::POINTER;
395             else
396                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_FIGHT, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
397         }
398         break;
399     }
400 
401     case MP2::OBJ_COAST:
402         return Cursor::DistanceThemes( Cursor::CURSOR_HERO_ANCHOR, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
403 
404     default:
405         if ( water ) {
406             if ( MP2::isWaterActionObject( tile.GetObject() ) )
407                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_BOAT_ACTION, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
408             else if ( tile.isPassable( Direction::CENTER, true, false, from_hero.GetColor() ) )
409                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_BOAT, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
410         }
411         break;
412     }
413 
414     return Cursor::POINTER;
415 }
416 
GetCursorFocusHeroes(const Heroes & from_hero,const Maps::Tiles & tile)417 int Interface::Basic::GetCursorFocusHeroes( const Heroes & from_hero, const Maps::Tiles & tile )
418 {
419     if ( from_hero.Modes( Heroes::ENABLEMOVE ) )
420         return Cursor::Get().Themes();
421     else if ( from_hero.isShipMaster() )
422         return GetCursorFocusShipmaster( from_hero, tile );
423 
424     switch ( tile.GetObject() ) {
425     case MP2::OBJ_MONSTER:
426         if ( from_hero.Modes( Heroes::GUARDIAN ) )
427             return Cursor::POINTER;
428         else
429             return Cursor::DistanceThemes( Cursor::CURSOR_HERO_FIGHT, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
430 
431     case MP2::OBJN_CASTLE:
432     case MP2::OBJ_CASTLE: {
433         const Castle * castle = world.getCastle( tile.GetCenter() );
434 
435         if ( nullptr != castle ) {
436             if ( tile.GetObject() == MP2::OBJN_CASTLE ) {
437                 if ( tile.GetPassable() == 0 ) {
438                     return ( from_hero.GetColor() == castle->GetColor() ) ? Cursor::CASTLE : Cursor::POINTER;
439                 }
440                 else {
441                     const bool protection = Maps::TileIsUnderProtection( tile.GetIndex() );
442 
443                     return Cursor::DistanceThemes( ( protection ? Cursor::CURSOR_HERO_FIGHT : Cursor::CURSOR_HERO_MOVE ),
444                                                    from_hero.GetRangeRouteDays( tile.GetIndex() ) );
445                 }
446             }
447             else if ( from_hero.Modes( Heroes::GUARDIAN ) || from_hero.GetIndex() == castle->GetIndex() ) {
448                 return from_hero.GetColor() == castle->GetColor() ? Cursor::CASTLE : Cursor::POINTER;
449             }
450             else if ( from_hero.GetColor() == castle->GetColor() ) {
451                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_ACTION, from_hero.GetRangeRouteDays( castle->GetIndex() ) );
452             }
453             else if ( from_hero.isFriends( castle->GetColor() ) ) {
454                 return Cursor::POINTER;
455             }
456             else if ( castle->GetActualArmy().isValid() ) {
457                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_FIGHT, from_hero.GetRangeRouteDays( castle->GetIndex() ) );
458             }
459             else {
460                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_ACTION, from_hero.GetRangeRouteDays( castle->GetIndex() ) );
461             }
462         }
463         break;
464     }
465 
466     case MP2::OBJ_HEROES: {
467         const Heroes * to_hero = tile.GetHeroes();
468 
469         if ( nullptr != to_hero ) {
470             if ( from_hero.Modes( Heroes::GUARDIAN ) )
471                 return from_hero.GetColor() == to_hero->GetColor() ? Cursor::HEROES : Cursor::POINTER;
472             else if ( to_hero->GetCenter() == from_hero.GetCenter() )
473                 return Cursor::HEROES;
474             else if ( from_hero.GetColor() == to_hero->GetColor() ) {
475                 int newcur = Cursor::DistanceThemes( Cursor::CURSOR_HERO_MEET, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
476                 return newcur != Cursor::POINTER ? newcur : Cursor::HEROES;
477             }
478             else if ( from_hero.isFriends( to_hero->GetColor() ) ) {
479                 return Cursor::POINTER;
480             }
481             else
482                 return Cursor::DistanceThemes( Cursor::CURSOR_HERO_FIGHT, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
483         }
484         break;
485     }
486 
487     case MP2::OBJ_BOAT:
488         return from_hero.Modes( Heroes::GUARDIAN ) ? Cursor::POINTER : Cursor::DistanceThemes( Cursor::CURSOR_HERO_BOAT, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
489     case MP2::OBJ_BARRIER:
490         return Cursor::DistanceThemes( Cursor::CURSOR_HERO_ACTION, from_hero.GetRangeRouteDays( tile.GetIndex() ) );
491     default:
492         if ( from_hero.Modes( Heroes::GUARDIAN ) )
493             return Cursor::POINTER;
494         else if ( MP2::isActionObject( tile.GetObject() ) ) {
495             bool protection = false;
496             if ( !MP2::isPickupObject( tile.GetObject() ) && !MP2::isAbandonedMine( tile.GetObject() ) ) {
497                 protection = ( Maps::TileIsUnderProtection( tile.GetIndex() ) || ( !from_hero.isFriends( tile.QuantityColor() ) && tile.CaptureObjectIsProtection() ) );
498             }
499             else {
500                 protection = Maps::TileIsUnderProtection( tile.GetIndex() );
501             }
502 
503             return Cursor::DistanceThemes( ( protection ? Cursor::CURSOR_HERO_FIGHT : Cursor::CURSOR_HERO_ACTION ), from_hero.GetRangeRouteDays( tile.GetIndex() ) );
504         }
505         else if ( tile.isPassable( Direction::CENTER, from_hero.isShipMaster(), false, from_hero.GetColor() ) ) {
506             bool protection = Maps::TileIsUnderProtection( tile.GetIndex() );
507 
508             return Cursor::DistanceThemes( ( protection ? Cursor::CURSOR_HERO_FIGHT : Cursor::CURSOR_HERO_MOVE ), from_hero.GetRangeRouteDays( tile.GetIndex() ) );
509         }
510         break;
511     }
512 
513     return Cursor::POINTER;
514 }
515 
GetCursorTileIndex(s32 dst_index)516 int Interface::Basic::GetCursorTileIndex( s32 dst_index )
517 {
518     if ( dst_index < 0 || dst_index >= world.w() * world.h() )
519         return Cursor::POINTER;
520 
521     const Maps::Tiles & tile = world.GetTiles( dst_index );
522     if ( tile.isFog( Settings::Get().CurrentColor() ) )
523         return Cursor::POINTER;
524 
525     switch ( GetFocusType() ) {
526     case GameFocus::HEROES:
527         return GetCursorFocusHeroes( *GetFocusHeroes(), tile );
528 
529     case GameFocus::CASTLE:
530         return GetCursorFocusCastle( *GetFocusCastle(), tile );
531 
532     default:
533         break;
534     }
535 
536     return Cursor::POINTER;
537 }
538 
StartGame()539 fheroes2::GameMode Interface::Basic::StartGame()
540 {
541     Settings & conf = Settings::Get();
542     fheroes2::Display & display = fheroes2::Display::instance();
543 
544     // draw interface
545     gameArea.generate( { display.width(), display.height() }, conf.ExtGameHideInterface() );
546 
547     radar.Build();
548     radar.SetHide( true );
549 
550     iconsPanel.ResetIcons();
551     iconsPanel.HideIcons();
552 
553     statusWindow.Reset();
554 
555     if ( conf.ExtGameHideInterface() )
556         SetHideInterface( true );
557 
558     Redraw( REDRAW_RADAR | REDRAW_ICONS | REDRAW_BUTTONS | REDRAW_STATUS | REDRAW_BORDER );
559 
560     bool loadedFromSave = conf.LoadedGameVersion();
561     bool skipTurns = loadedFromSave;
562 
563     GameOver::Result & gameResult = GameOver::Result::Get();
564     fheroes2::GameMode res = fheroes2::GameMode::END_TURN;
565 
566     std::vector<Player *> sortedPlayers = conf.GetPlayers();
567     std::sort( sortedPlayers.begin(), sortedPlayers.end(), SortPlayers );
568 
569     while ( res == fheroes2::GameMode::END_TURN ) {
570         if ( !loadedFromSave ) {
571             world.NewDay();
572         }
573 
574         // check if the game is over at the beginning of a new day
575         res = gameResult.LocalCheckGameOver();
576 
577         if ( res != fheroes2::GameMode::CANCEL ) {
578             break;
579         }
580 
581         res = fheroes2::GameMode::END_TURN;
582 
583         for ( const Player * player : sortedPlayers ) {
584             assert( player != nullptr );
585 
586             Kingdom & kingdom = world.GetKingdom( player->GetColor() );
587 
588             if ( skipTurns && !player->isColor( conf.CurrentColor() ) ) {
589                 continue;
590             }
591 
592             // player with conf.CurrentColor() was found, there is no need for further skips
593             skipTurns = false;
594 
595             if ( kingdom.isPlay() ) {
596                 DEBUG_LOG( DBG_GAME, DBG_INFO,
597                            world.DateString() << ", color: " << Color::String( player->GetColor() ) << ", resource: " << kingdom.GetFunds().String() );
598 
599                 radar.SetHide( true );
600                 radar.SetRedraw();
601 
602                 switch ( kingdom.GetControl() ) {
603                 case CONTROL_HUMAN:
604                     if ( conf.IsGameType( Game::TYPE_HOTSEAT ) ) {
605                         // we need to hide the world map in hot seat mode
606                         conf.SetCurrentColor( -1 );
607 
608                         iconsPanel.HideIcons();
609                         statusWindow.Reset();
610 
611                         SetRedraw( REDRAW_GAMEAREA | REDRAW_STATUS | REDRAW_ICONS );
612                         Redraw();
613                         display.render();
614 
615                         Game::DialogPlayers( player->GetColor(), _( "%{color} player's turn." ) );
616                     }
617 
618                     conf.SetCurrentColor( player->GetColor() );
619 
620                     world.ClearFog( player->GetColor() );
621 
622                     kingdom.ActionBeforeTurn();
623 
624                     iconsPanel.ShowIcons();
625                     iconsPanel.SetRedraw();
626 
627                     res = HumanTurn( loadedFromSave );
628                     break;
629 
630                 // CONTROL_AI turn
631                 default:
632                     // TODO: remove this temporary assertion
633                     assert( res == fheroes2::GameMode::END_TURN );
634 
635                     Cursor::Get().SetThemes( Cursor::WAIT );
636 
637                     conf.SetCurrentColor( player->GetColor() );
638 
639                     statusWindow.Reset();
640                     statusWindow.SetState( StatusType::STATUS_AITURN );
641 
642                     Redraw();
643                     display.render();
644 
645                     world.ClearFog( player->GetColor() );
646 
647                     kingdom.ActionBeforeTurn();
648 
649                     AI::Get().KingdomTurn( kingdom );
650                     break;
651                 }
652 
653                 if ( res != fheroes2::GameMode::END_TURN ) {
654                     break;
655                 }
656 
657                 // check if the game is over after each player's turn
658                 res = gameResult.LocalCheckGameOver();
659 
660                 if ( res != fheroes2::GameMode::CANCEL ) {
661                     break;
662                 }
663 
664                 res = fheroes2::GameMode::END_TURN;
665             }
666 
667             // reset this after potential HumanTurn() call, but regardless of whether current kingdom
668             // is vanquished - next alive kingdom should start a new day from scratch
669             loadedFromSave = false;
670         }
671 
672         // we went through all the players, but the current player from the save file is still not found,
673         // something is clearly wrong here
674         if ( skipTurns ) {
675             DEBUG_LOG( DBG_GAME, DBG_WARN,
676                        "the current player from the save file was not found"
677                            << ", player color: " << Color::String( conf.CurrentColor() ) );
678 
679             res = fheroes2::GameMode::MAIN_MENU;
680         }
681 
682         // don't carry the current color from the last player to the next turn
683         conf.SetCurrentColor( -1 );
684     }
685 
686     // if we are here, the res value should never be fheroes2::GameMode::END_TURN
687     assert( res != fheroes2::GameMode::END_TURN );
688 
689     if ( conf.ExtGameUseFade() )
690         fheroes2::FadeDisplay();
691 
692     return res;
693 }
694 
HumanTurn(bool isload)695 fheroes2::GameMode Interface::Basic::HumanTurn( bool isload )
696 {
697     fheroes2::GameMode res = fheroes2::GameMode::CANCEL;
698 
699     const Settings & conf = Settings::Get();
700 
701     Kingdom & myKingdom = world.GetKingdom( conf.CurrentColor() );
702     const KingdomCastles & myCastles = myKingdom.GetCastles();
703 
704     // current music will be set along with the focus, reset environment sounds
705     // and terrain music theme from the previous turn
706     Game::SetCurrentMusic( MUS::UNKNOWN );
707     AGG::ResetMixer();
708 
709     // set focus
710     if ( conf.ExtGameRememberLastFocus() ) {
711         if ( GetFocusHeroes() != nullptr )
712             ResetFocus( GameFocus::HEROES );
713         else if ( GetFocusCastle() != nullptr )
714             ResetFocus( GameFocus::CASTLE );
715         else
716             ResetFocus( GameFocus::FIRSTHERO );
717     }
718     else {
719         ResetFocus( GameFocus::FIRSTHERO );
720     }
721 
722     radar.SetHide( false );
723     statusWindow.Reset();
724     gameArea.SetUpdateCursor();
725     Redraw( REDRAW_GAMEAREA | REDRAW_RADAR | REDRAW_ICONS | REDRAW_BUTTONS | REDRAW_STATUS | REDRAW_BORDER );
726 
727     Game::EnvironmentSoundMixer();
728 
729     fheroes2::Display & display = fheroes2::Display::instance();
730 
731     display.render();
732 
733     if ( !isload ) {
734         // new week dialog
735         if ( 1 < world.CountWeek() && world.BeginWeek() ) {
736             ShowNewWeekDialog();
737         }
738 
739         // show event day
740         ShowEventDayDialog();
741 
742         // autosave
743         if ( conf.ExtGameAutosaveOn() && conf.ExtGameAutosaveBeginOfDay() )
744             Game::AutoSave();
745     }
746 
747     GameOver::Result & gameResult = GameOver::Result::Get();
748 
749     // check if the game is over at the beginning of each human-controlled player's turn
750     res = gameResult.LocalCheckGameOver();
751 
752     // warn that all the towns are lost
753     if ( res == fheroes2::GameMode::CANCEL && myCastles.empty() ) {
754         ShowWarningLostTownsDialog();
755     }
756 
757     int fastScrollRepeatCount = 0;
758     const int fastScrollStartThreshold = 2;
759 
760     bool isMovingHero = false;
761     bool stopHero = false;
762 
763     int heroAnimationFrameCount = 0;
764     fheroes2::Point heroAnimationOffset;
765     int heroAnimationSpriteId = 0;
766 
767     bool isCursorOverButtons = false;
768 
769     const std::vector<Game::DelayType> delayTypes = { Game::CURRENT_HERO_DELAY, Game::MAPS_DELAY };
770 
771     LocalEvent & le = LocalEvent::Get();
772     Cursor & cursor = Cursor::Get();
773 
774     // startgame loop
775     while ( fheroes2::GameMode::CANCEL == res ) {
776         if ( !le.HandleEvents( Game::isDelayNeeded( delayTypes ), true ) ) {
777             if ( EventExit() == fheroes2::GameMode::QUIT_GAME ) {
778                 res = fheroes2::GameMode::QUIT_GAME;
779                 break;
780             }
781             else {
782                 continue;
783             }
784         }
785 
786         // hot keys
787         if ( le.KeyPress() ) {
788             // stop moving hero first if needed
789             if ( isMovingHero )
790                 stopHero = true;
791             // exit dialog
792             else if ( HotKeyPressEvent( Game::EVENT_DEFAULT_EXIT ) )
793                 res = EventExit();
794             // end turn
795             else if ( HotKeyPressEvent( Game::EVENT_ENDTURN ) )
796                 res = EventEndTurn();
797             // next hero
798             else if ( HotKeyPressEvent( Game::EVENT_NEXTHERO ) )
799                 EventNextHero();
800             // next town
801             else if ( HotKeyPressEvent( Game::EVENT_NEXTTOWN ) )
802                 EventNextTown();
803             // new game
804             else if ( HotKeyPressEvent( Game::EVENT_BUTTON_NEWGAME ) )
805                 res = EventNewGame();
806             // save game
807             else if ( HotKeyPressEvent( Game::EVENT_SAVEGAME ) )
808                 EventSaveGame();
809             // load game
810             else if ( HotKeyPressEvent( Game::EVENT_LOADGAME ) ) {
811                 res = EventLoadGame();
812             }
813             // file options
814             else if ( HotKeyPressEvent( Game::EVENT_FILEOPTIONS ) )
815                 res = EventFileDialog();
816             // system options
817             else if ( HotKeyPressEvent( Game::EVENT_SYSTEMOPTIONS ) )
818                 EventSystemDialog();
819             // puzzle map
820             else if ( HotKeyPressEvent( Game::EVENT_PUZZLEMAPS ) )
821                 EventPuzzleMaps();
822             // info game
823             else if ( HotKeyPressEvent( Game::EVENT_INFOGAME ) )
824                 res = EventGameInfo();
825             // cast spell
826             else if ( HotKeyPressEvent( Game::EVENT_CASTSPELL ) )
827                 EventCastSpell();
828             // kingdom overview
829             else if ( HotKeyPressEvent( Game::EVENT_KINGDOM_INFO ) )
830                 EventKingdomInfo();
831             // view world
832             else if ( HotKeyPressEvent( Game::EVENT_VIEW_WORLD ) )
833                 EventViewWorld();
834             // show/hide control panel
835             else if ( HotKeyPressEvent( Game::EVENT_CTRLPANEL ) )
836                 EventSwitchShowControlPanel();
837             // hide/show radar
838             else if ( HotKeyPressEvent( Game::EVENT_SHOWRADAR ) )
839                 EventSwitchShowRadar();
840             // hide/show buttons
841             else if ( HotKeyPressEvent( Game::EVENT_SHOWBUTTONS ) )
842                 EventSwitchShowButtons();
843             // hide/show status window
844             else if ( HotKeyPressEvent( Game::EVENT_SHOWSTATUS ) )
845                 EventSwitchShowStatus();
846             // hide/show hero/town icons
847             else if ( HotKeyPressEvent( Game::EVENT_SHOWICONS ) )
848                 EventSwitchShowIcons();
849             // hero movement
850             else if ( HotKeyPressEvent( Game::EVENT_CONTINUE ) )
851                 EventContinueMovement();
852             // dig artifact
853             else if ( HotKeyPressEvent( Game::EVENT_DIGARTIFACT ) )
854                 res = EventDigArtifact();
855             // sleep hero
856             else if ( HotKeyPressEvent( Game::EVENT_SLEEPHERO ) )
857                 EventSwitchHeroSleeping();
858             // move hero
859             else if ( HotKeyPressEvent( Game::EVENT_MOVELEFT ) )
860                 EventKeyArrowPress( Direction::LEFT );
861             else if ( HotKeyPressEvent( Game::EVENT_MOVERIGHT ) )
862                 EventKeyArrowPress( Direction::RIGHT );
863             else if ( HotKeyPressEvent( Game::EVENT_MOVETOP ) )
864                 EventKeyArrowPress( Direction::TOP );
865             else if ( HotKeyPressEvent( Game::EVENT_MOVEBOTTOM ) )
866                 EventKeyArrowPress( Direction::BOTTOM );
867             else if ( HotKeyPressEvent( Game::EVENT_MOVETOPLEFT ) )
868                 EventKeyArrowPress( Direction::TOP_LEFT );
869             else if ( HotKeyPressEvent( Game::EVENT_MOVETOPRIGHT ) )
870                 EventKeyArrowPress( Direction::TOP_RIGHT );
871             else if ( HotKeyPressEvent( Game::EVENT_MOVEBOTTOMLEFT ) )
872                 EventKeyArrowPress( Direction::BOTTOM_LEFT );
873             else if ( HotKeyPressEvent( Game::EVENT_MOVEBOTTOMRIGHT ) )
874                 EventKeyArrowPress( Direction::BOTTOM_RIGHT );
875             // scroll maps
876             else if ( HotKeyPressEvent( Game::EVENT_SCROLLLEFT ) )
877                 gameArea.SetScroll( SCROLL_LEFT );
878             else if ( HotKeyPressEvent( Game::EVENT_SCROLLRIGHT ) )
879                 gameArea.SetScroll( SCROLL_RIGHT );
880             else if ( HotKeyPressEvent( Game::EVENT_SCROLLUP ) )
881                 gameArea.SetScroll( SCROLL_TOP );
882             else if ( HotKeyPressEvent( Game::EVENT_SCROLLDOWN ) )
883                 gameArea.SetScroll( SCROLL_BOTTOM );
884             // default action
885             else if ( HotKeyPressEvent( Game::EVENT_DEFAULTACTION ) )
886                 res = EventDefaultAction( res );
887             // open focus
888             else if ( HotKeyPressEvent( Game::EVENT_OPENFOCUS ) )
889                 EventOpenFocus();
890         }
891 
892         if ( res != fheroes2::GameMode::CANCEL ) {
893             break;
894         }
895 
896         if ( fheroes2::cursor().isFocusActive() ) {
897             int scrollPosition = SCROLL_NONE;
898 
899             if ( le.MouseCursor( GetScrollLeft() ) )
900                 scrollPosition |= SCROLL_LEFT;
901             else if ( le.MouseCursor( GetScrollRight() ) )
902                 scrollPosition |= SCROLL_RIGHT;
903             if ( le.MouseCursor( GetScrollTop() ) )
904                 scrollPosition |= SCROLL_TOP;
905             else if ( le.MouseCursor( GetScrollBottom() ) )
906                 scrollPosition |= SCROLL_BOTTOM;
907 
908             if ( scrollPosition != SCROLL_NONE ) {
909                 if ( Game::validateAnimationDelay( Game::SCROLL_START_DELAY ) ) {
910                     if ( fastScrollRepeatCount < fastScrollStartThreshold ) {
911                         ++fastScrollRepeatCount;
912                     }
913                 }
914 
915                 if ( fastScrollRepeatCount >= fastScrollStartThreshold ) {
916                     gameArea.SetScroll( scrollPosition );
917                 }
918             }
919             else {
920                 fastScrollRepeatCount = 0;
921             }
922         }
923         else {
924             fastScrollRepeatCount = 0;
925         }
926 
927         const fheroes2::Rect displayArea( 0, 0, display.width(), display.height() );
928         const bool isHiddenInterface = conf.ExtGameHideInterface();
929         const bool prevIsCursorOverButtons = isCursorOverButtons;
930         isCursorOverButtons = false;
931 
932         if ( isMovingHero ) {
933             // hero is moving, set the appropriate cursor
934             if ( cursor.Themes() != Cursor::WAIT ) {
935                 cursor.SetThemes( Cursor::WAIT );
936             }
937 
938             // stop moving hero if needed
939             if ( le.MouseClickLeft( displayArea ) || le.MousePressRight( displayArea ) ) {
940                 stopHero = true;
941             }
942         }
943         // cursor over radar
944         else if ( ( !isHiddenInterface || conf.ShowRadar() ) && le.MouseCursor( radar.GetRect() ) ) {
945             if ( Cursor::POINTER != cursor.Themes() )
946                 cursor.SetThemes( Cursor::POINTER );
947             radar.QueueEventProcessing();
948         }
949         // cursor over icons panel
950         else if ( ( !isHiddenInterface || conf.ShowIcons() ) && le.MouseCursor( iconsPanel.GetRect() ) ) {
951             if ( Cursor::POINTER != cursor.Themes() )
952                 cursor.SetThemes( Cursor::POINTER );
953             iconsPanel.QueueEventProcessing();
954         }
955         // cursor over buttons area
956         else if ( ( !isHiddenInterface || conf.ShowButtons() ) && le.MouseCursor( buttonsArea.GetRect() ) ) {
957             if ( Cursor::POINTER != cursor.Themes() )
958                 cursor.SetThemes( Cursor::POINTER );
959             res = buttonsArea.QueueEventProcessing();
960             isCursorOverButtons = true;
961         }
962         // cursor over status area
963         else if ( ( !isHiddenInterface || conf.ShowStatus() ) && le.MouseCursor( statusWindow.GetRect() ) ) {
964             if ( Cursor::POINTER != cursor.Themes() )
965                 cursor.SetThemes( Cursor::POINTER );
966             statusWindow.QueueEventProcessing();
967         }
968         // cursor over control panel
969         else if ( isHiddenInterface && conf.ShowControlPanel() && le.MouseCursor( controlPanel.GetArea() ) ) {
970             if ( Cursor::POINTER != cursor.Themes() )
971                 cursor.SetThemes( Cursor::POINTER );
972             res = controlPanel.QueueEventProcessing();
973         }
974         // cursor over game area
975         else if ( le.MouseCursor( gameArea.GetROI() ) && !gameArea.NeedScroll() ) {
976             gameArea.QueueEventProcessing();
977         }
978         else if ( !gameArea.NeedScroll() ) { // empty interface area so we set cursor to a normal pointer
979             if ( Cursor::POINTER != cursor.Themes() )
980                 cursor.SetThemes( Cursor::POINTER );
981             gameArea.ResetCursorPosition();
982         }
983 
984         if ( prevIsCursorOverButtons && !isCursorOverButtons ) {
985             buttonsArea.ResetButtons();
986         }
987 
988         if ( res != fheroes2::GameMode::CANCEL ) {
989             break;
990         }
991 
992         // heroes move animation
993         if ( Game::validateAnimationDelay( Game::CURRENT_HERO_DELAY ) ) {
994             Heroes * hero = GetFocusHeroes();
995 
996             if ( hero ) {
997                 bool resetHeroSprite = false;
998                 if ( heroAnimationFrameCount > 0 ) {
999                     gameArea.ShiftCenter( fheroes2::Point( heroAnimationOffset.x * Game::HumanHeroAnimSkip(), heroAnimationOffset.y * Game::HumanHeroAnimSkip() ) );
1000                     gameArea.SetRedraw();
1001                     heroAnimationFrameCount -= Game::HumanHeroAnimSkip();
1002                     if ( ( heroAnimationFrameCount & 0x3 ) == 0 ) { // % 4
1003                         hero->SetSpriteIndex( heroAnimationSpriteId );
1004 
1005                         if ( heroAnimationFrameCount == 0 )
1006                             resetHeroSprite = true;
1007                         else
1008                             ++heroAnimationSpriteId;
1009                     }
1010                     const int offsetStep = ( ( 4 - ( heroAnimationFrameCount & 0x3 ) ) & 0x3 ); // % 4
1011                     hero->SetOffset( fheroes2::Point( heroAnimationOffset.x * offsetStep, heroAnimationOffset.y * offsetStep ) );
1012                 }
1013 
1014                 if ( heroAnimationFrameCount == 0 ) {
1015                     if ( resetHeroSprite ) {
1016                         hero->SetSpriteIndex( heroAnimationSpriteId - 1 );
1017                     }
1018                     if ( hero->isMoveEnabled() ) {
1019                         if ( hero->Move( 10 == conf.HeroesMoveSpeed() ) ) {
1020                             gameArea.SetCenter( hero->GetCenter() );
1021                             ResetFocus( GameFocus::HEROES );
1022                             RedrawFocus();
1023 
1024                             if ( stopHero ) {
1025                                 stopHero = false;
1026 
1027                                 hero->SetMove( false );
1028                             }
1029                         }
1030                         else {
1031                             fheroes2::Point movement( hero->MovementDirection() );
1032                             if ( movement != fheroes2::Point() ) { // don't waste resources for no movement
1033                                 heroAnimationOffset = movement;
1034                                 gameArea.ShiftCenter( movement );
1035                                 ResetFocus( GameFocus::HEROES );
1036                                 heroAnimationFrameCount = 32 - Game::HumanHeroAnimSkip();
1037                                 heroAnimationSpriteId = hero->GetSpriteIndex();
1038                                 if ( Game::HumanHeroAnimSkip() < 4 ) {
1039                                     hero->SetSpriteIndex( heroAnimationSpriteId - 1 );
1040                                     hero->SetOffset(
1041                                         fheroes2::Point( heroAnimationOffset.x * Game::HumanHeroAnimSkip(), heroAnimationOffset.y * Game::HumanHeroAnimSkip() ) );
1042                                 }
1043                                 else {
1044                                     ++heroAnimationSpriteId;
1045                                 }
1046                             }
1047 
1048                             gameArea.SetRedraw();
1049                         }
1050 
1051                         isMovingHero = true;
1052 
1053                         if ( hero->isAction() ) {
1054                             // check if the game is over after the hero's action
1055                             res = gameResult.LocalCheckGameOver();
1056 
1057                             hero->ResetAction();
1058                         }
1059                     }
1060                     else {
1061                         isMovingHero = false;
1062                         stopHero = false;
1063 
1064                         hero->SetMove( false );
1065 
1066                         gameArea.SetUpdateCursor();
1067                     }
1068                 }
1069             }
1070             else {
1071                 isMovingHero = false;
1072                 stopHero = false;
1073             }
1074         }
1075 
1076         // fast scroll
1077         if ( gameArea.NeedScroll() && !isMovingHero ) {
1078             if ( Game::validateAnimationDelay( Game::SCROLL_DELAY ) ) {
1079                 if ( le.MouseCursor( GetScrollLeft() ) || le.MouseCursor( GetScrollRight() ) || le.MouseCursor( GetScrollTop() )
1080                      || le.MouseCursor( GetScrollBottom() ) ) {
1081                     cursor.SetThemes( gameArea.GetScrollCursor() );
1082                 }
1083 
1084                 gameArea.Scroll();
1085 
1086                 gameArea.SetRedraw();
1087                 radar.SetRedraw();
1088             }
1089         }
1090 
1091         // slow maps objects animation
1092         if ( Game::validateAnimationDelay( Game::MAPS_DELAY ) ) {
1093             u32 & frame = Game::MapsAnimationFrame();
1094             ++frame;
1095             gameArea.SetRedraw();
1096         }
1097 
1098         // check that the kingdom is not vanquished yet (has at least one hero or castle)
1099         if ( res == fheroes2::GameMode::CANCEL && !myKingdom.isPlay() ) {
1100             res = fheroes2::GameMode::END_TURN;
1101         }
1102 
1103         if ( NeedRedraw() ) {
1104             Redraw();
1105             display.render();
1106         }
1107     }
1108 
1109     if ( fheroes2::GameMode::END_TURN == res ) {
1110         // these warnings should be shown at the end of the turn
1111         if ( myKingdom.isPlay() && myCastles.empty() ) {
1112             const uint32_t lostTownDays = myKingdom.GetLostTownDays();
1113 
1114             if ( lostTownDays > Game::GetLostTownDays() ) {
1115                 Game::DialogPlayers( conf.CurrentColor(),
1116                                      _( "%{color} player, you have lost your last town. If you do not conquer another town in next week, you will be eliminated." ) );
1117             }
1118             else if ( lostTownDays == 1 ) {
1119                 Game::DialogPlayers( conf.CurrentColor(), _( "%{color} player, your heroes abandon you, and you are banished from this land." ) );
1120             }
1121         }
1122 
1123         if ( GetFocusHeroes() ) {
1124             GetFocusHeroes()->ShowPath( false );
1125             RedrawFocus();
1126         }
1127 
1128         if ( conf.ExtGameAutosaveOn() && !conf.ExtGameAutosaveBeginOfDay() )
1129             Game::AutoSave();
1130     }
1131 
1132     // reset environment sounds and terrain music theme at the end of the human turn
1133     Game::SetCurrentMusic( MUS::UNKNOWN );
1134     AGG::ResetMixer();
1135 
1136     return res;
1137 }
1138 
MouseCursorAreaClickLeft(const int32_t index_maps)1139 void Interface::Basic::MouseCursorAreaClickLeft( const int32_t index_maps )
1140 {
1141     Heroes * from_hero = GetFocusHeroes();
1142     const Maps::Tiles & tile = world.GetTiles( index_maps );
1143 
1144     switch ( Cursor::WithoutDistanceThemes( Cursor::Get().Themes() ) ) {
1145     case Cursor::HEROES: {
1146         Heroes * to_hero = tile.GetHeroes();
1147         // focus change/open hero
1148         if ( nullptr != to_hero ) {
1149             if ( !from_hero || from_hero != to_hero ) {
1150                 SetFocus( to_hero );
1151                 RedrawFocus();
1152             }
1153             else {
1154                 Game::OpenHeroesDialog( *to_hero, true, true );
1155                 Cursor::Get().SetThemes( Cursor::HEROES );
1156             }
1157         }
1158         break;
1159     }
1160 
1161     case Cursor::CASTLE: {
1162         // correct index for castle
1163         const MP2::MapObjectType objectType = tile.GetObject();
1164         if ( MP2::OBJN_CASTLE != objectType && MP2::OBJ_CASTLE != objectType )
1165             break;
1166 
1167         Castle * to_castle = world.getCastle( tile.GetCenter() );
1168         if ( to_castle == nullptr )
1169             break;
1170 
1171         const Castle * from_castle = GetFocusCastle();
1172         if ( !from_castle || from_castle != to_castle ) {
1173             SetFocus( to_castle );
1174             RedrawFocus();
1175         }
1176         else {
1177             Game::OpenCastleDialog( *to_castle );
1178             Cursor::Get().SetThemes( Cursor::CASTLE );
1179         }
1180         break;
1181     }
1182     case Cursor::CURSOR_HERO_FIGHT:
1183     case Cursor::CURSOR_HERO_MOVE:
1184     case Cursor::CURSOR_HERO_BOAT:
1185     case Cursor::CURSOR_HERO_ANCHOR:
1186     case Cursor::CURSOR_HERO_MEET:
1187     case Cursor::CURSOR_HERO_ACTION:
1188     case Cursor::CURSOR_HERO_BOAT_ACTION:
1189         if ( from_hero == nullptr )
1190             break;
1191 
1192         if ( from_hero->isMoveEnabled() )
1193             from_hero->SetMove( false );
1194         else
1195             ShowPathOrStartMoveHero( from_hero, index_maps );
1196         break;
1197 
1198     default:
1199         if ( from_hero )
1200             from_hero->SetMove( false );
1201         break;
1202     }
1203 }
1204 
MouseCursorAreaPressRight(s32 index_maps) const1205 void Interface::Basic::MouseCursorAreaPressRight( s32 index_maps ) const
1206 {
1207     Heroes * hero = GetFocusHeroes();
1208 
1209     // stop hero
1210     if ( hero && hero->isMoveEnabled() ) {
1211         hero->SetMove( false );
1212         Cursor::Get().SetThemes( GetCursorTileIndex( index_maps ) );
1213     }
1214     else {
1215         const Settings & conf = Settings::Get();
1216         const Maps::Tiles & tile = world.GetTiles( index_maps );
1217 
1218         DEBUG_LOG( DBG_DEVEL, DBG_INFO, std::endl << tile.String() );
1219 
1220         if ( !IS_DEVEL() && tile.isFog( conf.CurrentColor() ) )
1221             Dialog::QuickInfo( tile );
1222         else
1223             switch ( tile.GetObject() ) {
1224             case MP2::OBJN_CASTLE:
1225             case MP2::OBJ_CASTLE: {
1226                 const Castle * castle = world.getCastle( tile.GetCenter() );
1227                 if ( castle )
1228                     Dialog::QuickInfo( *castle, fheroes2::Rect() );
1229                 else
1230                     Dialog::QuickInfo( tile );
1231                 break;
1232             }
1233 
1234             case MP2::OBJ_HEROES: {
1235                 const Heroes * heroes = tile.GetHeroes();
1236                 if ( heroes )
1237                     Dialog::QuickInfo( *heroes, fheroes2::Rect() );
1238                 break;
1239             }
1240 
1241             default:
1242                 Dialog::QuickInfo( tile );
1243                 break;
1244             }
1245     }
1246 }
1247