1 /***************************************************************************
2  *   Copyright (C) 2012 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 "audio.h"
28 #include "cursor.h"
29 #include "dialog_system_options.h"
30 #include "game.h"
31 #include "game_interface.h"
32 #include "game_io.h"
33 #include "game_over.h"
34 #include "heroes.h"
35 #include "kingdom.h"
36 #include "logging.h"
37 #include "m82.h"
38 #include "settings.h"
39 #include "system.h"
40 #include "text.h"
41 #include "tools.h"
42 #include "translations.h"
43 #include "world.h"
44 
CalculateHeroPath(Heroes * hero,s32 destinationIdx) const45 void Interface::Basic::CalculateHeroPath( Heroes * hero, s32 destinationIdx ) const
46 {
47     if ( ( hero == nullptr ) || hero->Modes( Heroes::GUARDIAN ) )
48         return;
49 
50     hero->SetMove( false );
51 
52     const Route::Path & path = hero->GetPath();
53     if ( destinationIdx == -1 )
54         destinationIdx = path.GetDestinedIndex(); // returns -1 at the time of launching new game (because of no path history)
55 
56     if ( destinationIdx != -1 ) {
57         hero->GetPath().setPath( world.getPath( *hero, destinationIdx ), destinationIdx );
58         DEBUG_LOG( DBG_GAME, DBG_TRACE, hero->GetName() << ", distance: " << world.getDistance( *hero, destinationIdx ) << ", route: " << path.String() );
59         gameArea.SetRedraw();
60 
61         const fheroes2::Point & mousePos = LocalEvent::Get().GetMouseCursor();
62         if ( gameArea.GetROI() & mousePos ) {
63             const int32_t cursorIndex = gameArea.GetValidTileIdFromPoint( mousePos );
64             Cursor::Get().SetThemes( GetCursorTileIndex( cursorIndex ) );
65         }
66 
67         Interface::Basic::Get().buttonsArea.Redraw();
68     }
69 }
70 
ShowPathOrStartMoveHero(Heroes * hero,s32 destinationIdx)71 void Interface::Basic::ShowPathOrStartMoveHero( Heroes * hero, s32 destinationIdx )
72 {
73     if ( !hero || hero->Modes( Heroes::GUARDIAN ) )
74         return;
75 
76     const Route::Path & path = hero->GetPath();
77 
78     // show path
79     if ( path.GetDestinedIndex() != destinationIdx && path.GetDestinationIndex() != destinationIdx ) {
80         CalculateHeroPath( hero, destinationIdx );
81     }
82     // start move
83     else if ( path.isValid() && hero->MayStillMove( false, true ) ) {
84         SetFocus( hero );
85         RedrawFocus();
86 
87         hero->SetMove( true );
88     }
89 }
90 
MoveHeroFromArrowKeys(Heroes & hero,int direct)91 void Interface::Basic::MoveHeroFromArrowKeys( Heroes & hero, int direct )
92 {
93     const bool fromWater = hero.isShipMaster();
94     if ( Maps::isValidDirection( hero.GetIndex(), direct ) ) {
95         s32 dst = Maps::GetDirectionIndex( hero.GetIndex(), direct );
96         const Maps::Tiles & tile = world.GetTiles( dst );
97         bool allow = false;
98 
99         switch ( tile.GetObject() ) {
100         case MP2::OBJ_BOAT:
101         case MP2::OBJ_CASTLE:
102         case MP2::OBJ_HEROES:
103         case MP2::OBJ_MONSTER:
104             allow = true;
105             break;
106 
107         default:
108             allow = ( tile.isPassable( Direction::CENTER, fromWater, false, hero.GetColor() ) || MP2::isActionObject( tile.GetObject(), fromWater ) );
109             break;
110         }
111 
112         if ( allow )
113             ShowPathOrStartMoveHero( &hero, dst );
114     }
115 }
116 
EventNextHero(void)117 void Interface::Basic::EventNextHero( void )
118 {
119     const Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() );
120     const KingdomHeroes & myHeroes = myKingdom.GetHeroes();
121 
122     if ( myHeroes.empty() )
123         return;
124 
125     if ( GetFocusHeroes() ) {
126         KingdomHeroes::const_iterator it = std::find( myHeroes.begin(), myHeroes.end(), GetFocusHeroes() );
127         KingdomHeroes::const_iterator currentHero = it;
128         do {
129             ++it;
130             if ( it == myHeroes.end() )
131                 it = myHeroes.begin();
132             if ( ( *it )->MayStillMove( true, false ) ) {
133                 SetFocus( *it );
134                 CalculateHeroPath( *it, -1 );
135                 break;
136             }
137         } while ( it != currentHero );
138     }
139     else {
140         for ( Heroes * hero : myHeroes ) {
141             if ( hero->MayStillMove( true, false ) ) {
142                 SetFocus( hero );
143                 CalculateHeroPath( hero, -1 );
144                 break;
145             }
146         }
147     }
148     RedrawFocus();
149 }
150 
EventContinueMovement(void) const151 void Interface::Basic::EventContinueMovement( void ) const
152 {
153     Heroes * hero = GetFocusHeroes();
154 
155     if ( hero && hero->GetPath().isValid() && hero->MayStillMove( false, true ) ) {
156         hero->SetMove( true );
157     }
158 }
159 
EventKingdomInfo(void) const160 void Interface::Basic::EventKingdomInfo( void ) const
161 {
162     Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() );
163     myKingdom.openOverviewDialog();
164 
165     iconsPanel.SetRedraw();
166 }
167 
EventCastSpell(void)168 void Interface::Basic::EventCastSpell( void )
169 {
170     Heroes * hero = GetFocusHeroes();
171 
172     if ( hero ) {
173         SetRedraw( REDRAW_ALL );
174         ResetFocus( GameFocus::HEROES );
175         Redraw();
176 
177         const Spell spell = hero->OpenSpellBook( SpellBook::Filter::ADVN, true, nullptr );
178         // apply cast spell
179         if ( spell.isValid() ) {
180             hero->ActionSpellCast( spell );
181             iconsPanel.SetRedraw();
182         }
183     }
184 }
185 
EventEndTurn() const186 fheroes2::GameMode Interface::Basic::EventEndTurn() const
187 {
188     const Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() );
189 
190     if ( GetFocusHeroes() )
191         GetFocusHeroes()->SetMove( false );
192 
193     if ( !myKingdom.HeroesMayStillMove()
194          || Dialog::YES == Dialog::Message( "", _( "One or more heroes may still move, are you sure you want to end your turn?" ), Font::BIG, Dialog::YES | Dialog::NO ) )
195         return fheroes2::GameMode::END_TURN;
196 
197     return fheroes2::GameMode::CANCEL;
198 }
199 
EventAdventureDialog()200 fheroes2::GameMode Interface::Basic::EventAdventureDialog()
201 {
202     switch ( Dialog::AdventureOptions( GameFocus::HEROES == GetFocusType() ) ) {
203     case Dialog::WORLD:
204         EventViewWorld();
205         break;
206 
207     case Dialog::PUZZLE:
208         EventPuzzleMaps();
209         break;
210 
211     case Dialog::INFO:
212         return EventGameInfo();
213 
214     case Dialog::DIG:
215         return EventDigArtifact();
216 
217     default:
218         break;
219     }
220 
221     return fheroes2::GameMode::CANCEL;
222 }
223 
EventViewWorld()224 void Interface::Basic::EventViewWorld()
225 {
226     ViewWorld::ViewWorldWindow( Settings::Get().CurrentColor(), ViewWorldMode::OnlyVisible, *this );
227 }
228 
EventFileDialog() const229 fheroes2::GameMode Interface::Basic::EventFileDialog() const
230 {
231     return Dialog::FileOptions();
232 }
233 
EventSystemDialog() const234 void Interface::Basic::EventSystemDialog() const
235 {
236     fheroes2::showSystemOptionsDialog();
237 }
238 
EventExit()239 fheroes2::GameMode Interface::Basic::EventExit()
240 {
241     if ( Dialog::YES & Dialog::Message( "", _( "Are you sure you want to quit?" ), Font::BIG, Dialog::YES | Dialog::NO ) )
242         return fheroes2::GameMode::QUIT_GAME;
243 
244     return fheroes2::GameMode::CANCEL;
245 }
246 
EventNextTown(void)247 void Interface::Basic::EventNextTown( void )
248 {
249     Kingdom & myKingdom = world.GetKingdom( Settings::Get().CurrentColor() );
250     KingdomCastles & myCastles = myKingdom.GetCastles();
251 
252     if ( !myCastles.empty() ) {
253         if ( GetFocusCastle() ) {
254             KingdomCastles::const_iterator it = std::find( myCastles.begin(), myCastles.end(), GetFocusCastle() );
255             ++it;
256             if ( it == myCastles.end() )
257                 it = myCastles.begin();
258             SetFocus( *it );
259         }
260         else
261             ResetFocus( GameFocus::CASTLE );
262 
263         RedrawFocus();
264     }
265 }
266 
EventNewGame() const267 fheroes2::GameMode Interface::Basic::EventNewGame() const
268 {
269     return Dialog::YES == Dialog::Message( "", _( "Are you sure you want to restart? (Your current game will be lost.)" ), Font::BIG, Dialog::YES | Dialog::NO )
270                ? fheroes2::GameMode::NEW_GAME
271                : fheroes2::GameMode::CANCEL;
272 }
273 
EventSaveGame() const274 fheroes2::GameMode Interface::Basic::EventSaveGame() const
275 {
276     while ( true ) {
277         const std::string filename = Dialog::SelectFileSave();
278         if ( filename.empty() ) {
279             return fheroes2::GameMode::CANCEL;
280         }
281 
282         // ask overwrite?
283         const Settings & conf = Settings::Get();
284         if ( System::IsFile( filename ) && conf.ExtGameRewriteConfirm()
285              && Dialog::NO == Dialog::Message( "", _( "Are you sure you want to overwrite the save with this name?" ), Font::BIG, Dialog::YES | Dialog::NO ) ) {
286             continue;
287         }
288 
289         if ( Game::Save( filename ) ) {
290             Dialog::Message( "", _( "Game saved successfully." ), Font::BIG, Dialog::OK );
291         }
292         else {
293             Dialog::Message( "", _( "There was an issue during saving." ), Font::BIG, Dialog::OK );
294         }
295         return fheroes2::GameMode::CANCEL;
296     }
297 }
298 
EventLoadGame() const299 fheroes2::GameMode Interface::Basic::EventLoadGame() const
300 {
301     return Dialog::YES == Dialog::Message( "", _( "Are you sure you want to load a new game? (Your current game will be lost.)" ), Font::BIG, Dialog::YES | Dialog::NO )
302                ? fheroes2::GameMode::LOAD_GAME
303                : fheroes2::GameMode::CANCEL;
304 }
305 
EventPuzzleMaps(void) const306 void Interface::Basic::EventPuzzleMaps( void ) const
307 {
308     world.GetKingdom( Settings::Get().CurrentColor() ).PuzzleMaps().ShowMapsDialog();
309 }
310 
EventGameInfo()311 fheroes2::GameMode Interface::Basic::EventGameInfo()
312 {
313     if ( Settings::Get().isCampaignGameType() ) {
314         fheroes2::Display & display = fheroes2::Display::instance();
315         fheroes2::ImageRestorer saver( display, 0, 0, display.width(), display.height() );
316 
317         AGG::ResetMixer();
318 
319         const fheroes2::GameMode returnMode = Game::SelectCampaignScenario( fheroes2::GameMode::CANCEL, true );
320         if ( returnMode == fheroes2::GameMode::CANCEL ) {
321             saver.restore();
322 
323             Game::restoreSoundsForCurrentFocus();
324         }
325         else {
326             saver.reset();
327         }
328 
329         return returnMode;
330     }
331 
332     Dialog::GameInfo();
333     return fheroes2::GameMode::CANCEL;
334 }
335 
EventSwitchHeroSleeping(void)336 void Interface::Basic::EventSwitchHeroSleeping( void )
337 {
338     Heroes * hero = GetFocusHeroes();
339 
340     if ( hero ) {
341         hero->Modes( Heroes::SLEEPER ) ? hero->ResetModes( Heroes::SLEEPER ) : hero->SetModes( Heroes::SLEEPER );
342 
343         SetRedraw( REDRAW_HEROES );
344         buttonsArea.SetRedraw();
345     }
346 }
347 
EventDigArtifact()348 fheroes2::GameMode Interface::Basic::EventDigArtifact()
349 {
350     Heroes * hero = GetFocusHeroes();
351 
352     if ( hero ) {
353         if ( hero->isShipMaster() )
354             Dialog::Message( "", _( "Try looking on land!!!" ), Font::BIG, Dialog::OK );
355         else if ( hero->GetMaxMovePoints() <= hero->GetMovePoints() ) {
356             if ( world.GetTiles( hero->GetIndex() ).GoodForUltimateArtifact() ) {
357                 AGG::PlaySound( M82::DIGSOUND );
358 
359                 hero->ResetMovePoints();
360 
361                 if ( world.DiggingForUltimateArtifact( hero->GetCenter() ) ) {
362                     AGG::PlaySound( M82::TREASURE );
363                     const Artifact & ultimate = world.GetUltimateArtifact().GetArtifact();
364                     hero->PickupArtifact( ultimate );
365                     std::string msg( _( "After spending many hours digging here, you have uncovered the %{artifact}." ) );
366                     StringReplace( msg, "%{artifact}", ultimate.GetName() );
367                     Dialog::ArtifactInfo( _( "Congratulations!" ), msg, ultimate.GetID() );
368                 }
369                 else
370                     Dialog::Message( "", _( "Nothing here. Where could it be?" ), Font::BIG, Dialog::OK );
371 
372                 iconsPanel.RedrawIcons( ICON_HEROES );
373                 fheroes2::Display::instance().render();
374 
375                 // check if the game is over due to conditions related to the ultimate artifact
376                 return GameOver::Result::Get().LocalCheckGameOver();
377             }
378             else
379                 Dialog::Message( "", _( "Try searching on clear ground." ), Font::BIG, Dialog::OK );
380         }
381         else {
382             Dialog::Message( "", _( "Digging for artifacts requires a whole day, try again tomorrow." ), Font::BIG, Dialog::OK );
383         }
384     }
385 
386     return fheroes2::GameMode::CANCEL;
387 }
388 
EventDefaultAction(const fheroes2::GameMode gameMode)389 fheroes2::GameMode Interface::Basic::EventDefaultAction( const fheroes2::GameMode gameMode )
390 {
391     Heroes * hero = GetFocusHeroes();
392 
393     if ( hero ) {
394         // 1. action object
395         if ( MP2::isActionObject( hero->GetMapsObject(), hero->isShipMaster() ) ) {
396             hero->Action( hero->GetIndex(), true );
397 
398             // The action object (e.g. Stables or Well) can alter the status of the hero
399             iconsPanel.RedrawIcons( ICON_HEROES );
400 
401             // If a hero completed an action we must verify the condition for the scenario.
402             if ( hero->isAction() ) {
403                 hero->ResetAction();
404                 // check if the game is over after the hero's action
405                 return GameOver::Result::Get().LocalCheckGameOver();
406             }
407         }
408     }
409     else if ( GetFocusCastle() ) {
410         // 2. town dialog
411         Game::OpenCastleDialog( *GetFocusCastle() );
412     }
413 
414     return gameMode;
415 }
416 
EventOpenFocus(void) const417 void Interface::Basic::EventOpenFocus( void ) const
418 {
419     if ( GetFocusHeroes() )
420         Game::OpenHeroesDialog( *GetFocusHeroes(), true, true );
421     else if ( GetFocusCastle() )
422         Game::OpenCastleDialog( *GetFocusCastle() );
423 }
424 
EventSwitchShowRadar(void) const425 void Interface::Basic::EventSwitchShowRadar( void ) const
426 {
427     Settings & conf = Settings::Get();
428 
429     if ( conf.ExtGameHideInterface() ) {
430         if ( conf.ShowRadar() ) {
431             conf.SetShowRadar( false );
432             gameArea.SetRedraw();
433         }
434         else {
435             conf.SetShowRadar( true );
436             radar.SetRedraw();
437         }
438     }
439 }
440 
EventSwitchShowButtons(void) const441 void Interface::Basic::EventSwitchShowButtons( void ) const
442 {
443     Settings & conf = Settings::Get();
444 
445     if ( conf.ExtGameHideInterface() ) {
446         if ( conf.ShowButtons() ) {
447             conf.SetShowButtons( false );
448             gameArea.SetRedraw();
449         }
450         else {
451             conf.SetShowButtons( true );
452             buttonsArea.SetRedraw();
453         }
454     }
455 }
456 
EventSwitchShowStatus(void) const457 void Interface::Basic::EventSwitchShowStatus( void ) const
458 {
459     Settings & conf = Settings::Get();
460 
461     if ( conf.ExtGameHideInterface() ) {
462         if ( conf.ShowStatus() ) {
463             conf.SetShowStatus( false );
464             gameArea.SetRedraw();
465         }
466         else {
467             conf.SetShowStatus( true );
468             statusWindow.SetRedraw();
469         }
470     }
471 }
472 
EventSwitchShowIcons(void)473 void Interface::Basic::EventSwitchShowIcons( void )
474 {
475     Settings & conf = Settings::Get();
476 
477     if ( conf.ExtGameHideInterface() ) {
478         if ( conf.ShowIcons() ) {
479             conf.SetShowIcons( false );
480             gameArea.SetRedraw();
481         }
482         else {
483             conf.SetShowIcons( true );
484             iconsPanel.SetCurrentVisible();
485             iconsPanel.SetRedraw();
486         }
487     }
488 }
489 
EventSwitchShowControlPanel(void) const490 void Interface::Basic::EventSwitchShowControlPanel( void ) const
491 {
492     Settings & conf = Settings::Get();
493 
494     if ( conf.ExtGameHideInterface() ) {
495         conf.SetShowPanel( !conf.ShowControlPanel() );
496         gameArea.SetRedraw();
497     }
498 }
499 
EventKeyArrowPress(int dir)500 void Interface::Basic::EventKeyArrowPress( int dir )
501 {
502     Heroes * hero = GetFocusHeroes();
503 
504     // move hero
505     if ( hero )
506         MoveHeroFromArrowKeys( *hero, dir );
507     else
508         // scroll map
509         switch ( dir ) {
510         case Direction::TOP_LEFT:
511             gameArea.SetScroll( SCROLL_TOP );
512             gameArea.SetScroll( SCROLL_LEFT );
513             break;
514         case Direction::TOP:
515             gameArea.SetScroll( SCROLL_TOP );
516             break;
517         case Direction::TOP_RIGHT:
518             gameArea.SetScroll( SCROLL_TOP );
519             gameArea.SetScroll( SCROLL_RIGHT );
520             break;
521         case Direction::RIGHT:
522             gameArea.SetScroll( SCROLL_RIGHT );
523             break;
524         case Direction::BOTTOM_RIGHT:
525             gameArea.SetScroll( SCROLL_BOTTOM );
526             gameArea.SetScroll( SCROLL_RIGHT );
527             break;
528         case Direction::BOTTOM:
529             gameArea.SetScroll( SCROLL_BOTTOM );
530             break;
531         case Direction::BOTTOM_LEFT:
532             gameArea.SetScroll( SCROLL_BOTTOM );
533             gameArea.SetScroll( SCROLL_LEFT );
534             break;
535         case Direction::LEFT:
536             gameArea.SetScroll( SCROLL_LEFT );
537             break;
538         default:
539             break;
540         }
541 }
542