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