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