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 <string>
25
26 #include "agg_image.h"
27 #include "army.h"
28 #include "army_bar.h"
29 #include "army_troop.h"
30 #include "cursor.h"
31 #include "dialog.h"
32 #include "game.h"
33 #include "heroes.h"
34 #include "heroes_indicator.h"
35 #include "icn.h"
36 #include "logging.h"
37 #include "settings.h"
38 #include "skill_bar.h"
39 #include "text.h"
40 #include "tools.h"
41 #include "translations.h"
42 #include "ui_button.h"
43
44 namespace
45 {
createMoveButton(const int32_t icnId,const int32_t offsetX,const int32_t offsetY,const fheroes2::Image & display)46 fheroes2::ButtonSprite createMoveButton( const int32_t icnId, const int32_t offsetX, const int32_t offsetY, const fheroes2::Image & display )
47 {
48 const fheroes2::Sprite & originalReleasedImage = fheroes2::AGG::GetICN( icnId, 0 );
49 const fheroes2::Sprite & originalPressedImage = fheroes2::AGG::GetICN( icnId, 1 );
50
51 const int32_t minX = std::min( originalReleasedImage.x(), originalPressedImage.x() );
52 const int32_t minY = std::min( originalReleasedImage.y(), originalPressedImage.y() );
53
54 return fheroes2::makeButtonWithShadow( offsetX + minX, offsetY + minY, originalReleasedImage, originalPressedImage, display, fheroes2::Point( -3, 3 ) );
55 }
56
moveArtifacts(BagArtifacts & bagFrom,BagArtifacts & bagTo)57 void moveArtifacts( BagArtifacts & bagFrom, BagArtifacts & bagTo )
58 {
59 size_t toIdx = 0;
60
61 for ( size_t fromIdx = 0; fromIdx < bagFrom.size(); ++fromIdx ) {
62 if ( bagFrom[fromIdx].GetID() != Artifact::UNKNOWN && bagFrom[fromIdx].GetID() != Artifact::MAGIC_BOOK ) {
63 while ( toIdx < bagTo.size() ) {
64 if ( bagTo[toIdx].GetID() == Artifact::UNKNOWN )
65 break;
66
67 ++toIdx;
68 }
69
70 if ( toIdx == bagTo.size() )
71 break;
72
73 std::swap( bagFrom[fromIdx], bagTo[toIdx] );
74 }
75 }
76 }
77 }
78
79 class MeetingArmyBar : public ArmyBar
80 {
81 public:
MeetingArmyBar(Army * army)82 explicit MeetingArmyBar( Army * army )
83 : ArmyBar( army, true, false, false )
84 {}
85
RedrawBackground(const fheroes2::Rect & roi,fheroes2::Image & image)86 void RedrawBackground( const fheroes2::Rect & roi, fheroes2::Image & image ) override
87 {
88 if ( _cachedBackground.empty() ) {
89 _cachedBackground.resize( roi.width, roi.height );
90 fheroes2::Copy( image, roi.x, roi.y, _cachedBackground, 0, 0, roi.width, roi.height );
91 }
92
93 fheroes2::Blit( _cachedBackground, 0, 0, image, roi.x, roi.y, roi.width, roi.height );
94 }
95
RedrawItem(ArmyTroop & troop,const fheroes2::Rect & roi,bool isSelected,fheroes2::Image & image)96 void RedrawItem( ArmyTroop & troop, const fheroes2::Rect & roi, bool isSelected, fheroes2::Image & image ) override
97 {
98 if ( !troop.isValid() )
99 return;
100
101 Text text( std::to_string( troop.GetCount() ), Font::SMALL );
102
103 const fheroes2::Sprite & mons32 = fheroes2::AGG::GetICN( ICN::MONS32, troop.GetSpriteIndex() );
104 fheroes2::Rect srcrt( 0, 0, mons32.width(), mons32.height() );
105
106 if ( mons32.width() > roi.width ) {
107 srcrt.x = ( mons32.width() - roi.width ) / 2;
108 srcrt.width = roi.width;
109 }
110
111 if ( mons32.height() > roi.height ) {
112 srcrt.y = ( mons32.height() - roi.height ) / 2;
113 srcrt.height = roi.height;
114 }
115
116 int32_t offsetX = ( roi.width - mons32.width() ) / 2;
117 int32_t offsetY = roi.height - mons32.height() - 3;
118
119 if ( offsetX < 1 )
120 offsetX = 1;
121
122 if ( offsetY < 1 )
123 offsetY = 1;
124
125 fheroes2::Blit( mons32, srcrt.x, srcrt.y, image, roi.x + offsetX, roi.y + offsetY, srcrt.width, srcrt.height );
126
127 text.Blit( roi.x + ( roi.width - text.w() ) / 2, roi.y + roi.height - 1, image );
128
129 if ( isSelected ) {
130 spcursor.setPosition( roi.x, roi.y );
131 spcursor.show();
132 }
133 }
134
135 private:
136 fheroes2::Image _cachedBackground;
137 };
138
139 class MeetingArtifactBar : public ArtifactsBar
140 {
141 public:
MeetingArtifactBar(const Heroes * hero)142 explicit MeetingArtifactBar( const Heroes * hero )
143 : ArtifactsBar( hero, true, false, false, false, nullptr )
144 {}
145
RedrawBackground(const fheroes2::Rect & roi,fheroes2::Image & image)146 void RedrawBackground( const fheroes2::Rect & roi, fheroes2::Image & image ) override
147 {
148 if ( _cachedBackground.empty() ) {
149 _cachedBackground.resize( roi.width, roi.height );
150 fheroes2::Copy( image, roi.x, roi.y, _cachedBackground, 0, 0, roi.width, roi.height );
151 }
152
153 fheroes2::Blit( _cachedBackground, 0, 0, image, roi.x, roi.y, roi.width, roi.height );
154 }
155
RedrawItem(Artifact & arifact,const fheroes2::Rect & roi,bool isSelected,fheroes2::Image & image)156 void RedrawItem( Artifact & arifact, const fheroes2::Rect & roi, bool isSelected, fheroes2::Image & image ) override
157 {
158 if ( !arifact.isValid() )
159 return;
160
161 fheroes2::Blit( fheroes2::AGG::GetICN( ICN::ARTFX, arifact.IndexSprite32() ), image, roi.x + 1, roi.y + 1 );
162
163 if ( isSelected ) {
164 spcursor.setPosition( roi.x, roi.y );
165 spcursor.show();
166 }
167 }
168
169 private:
170 fheroes2::Image _cachedBackground;
171 };
172
173 class MeetingPrimarySkillsBar : public PrimarySkillsBar
174 {
175 public:
MeetingPrimarySkillsBar(const Heroes * hero)176 explicit MeetingPrimarySkillsBar( const Heroes * hero )
177 : PrimarySkillsBar( hero, true )
178 {}
179
RedrawBackground(const fheroes2::Rect &,fheroes2::Image &)180 void RedrawBackground( const fheroes2::Rect &, fheroes2::Image & ) override
181 {
182 // Just do nothing
183 }
184 };
185
186 class MeetingSecondarySkillsBar : public SecondarySkillsBar
187 {
188 public:
MeetingSecondarySkillsBar(const Heroes & hero)189 explicit MeetingSecondarySkillsBar( const Heroes & hero )
190 : SecondarySkillsBar( hero )
191 {}
192
RedrawBackground(const fheroes2::Rect & roi,fheroes2::Image & image)193 void RedrawBackground( const fheroes2::Rect & roi, fheroes2::Image & image ) override
194 {
195 if ( _cachedBackground.empty() ) {
196 _cachedBackground.resize( roi.width, roi.height );
197 fheroes2::Copy( image, roi.x, roi.y, _cachedBackground, 0, 0, roi.width, roi.height );
198 }
199
200 fheroes2::Blit( _cachedBackground, 0, 0, image, roi.x, roi.y, roi.width, roi.height );
201 }
202
RedrawItem(Skill::Secondary & skill,const fheroes2::Rect & roi,fheroes2::Image & image)203 void RedrawItem( Skill::Secondary & skill, const fheroes2::Rect & roi, fheroes2::Image & image ) override
204 {
205 if ( !skill.isValid() )
206 return;
207
208 const fheroes2::Sprite & sprite = fheroes2::AGG::GetICN( ICN::MINISS, skill.GetIndexSprite2() );
209 fheroes2::Blit( sprite, image, roi.x + ( roi.width - sprite.width() ) / 2, roi.y + ( roi.height - sprite.height() ) / 2 );
210
211 Text text( std::to_string( skill.Level() ), Font::SMALL );
212 text.Blit( roi.x + ( roi.width - text.w() ) - 3, roi.y + roi.height - text.h(), image );
213 }
214
215 private:
216 fheroes2::Image _cachedBackground;
217 };
218
MeetingDialog(Heroes & otherHero)219 void Heroes::MeetingDialog( Heroes & otherHero )
220 {
221 fheroes2::Display & display = fheroes2::Display::instance();
222
223 // setup cursor
224 const CursorRestorer cursorRestorer( true, Cursor::POINTER );
225
226 const fheroes2::Sprite & backSprite = fheroes2::AGG::GetICN( ICN::SWAPWIN, 0 );
227 const fheroes2::Point cur_pt( ( display.width() - backSprite.width() ) / 2, ( display.height() - backSprite.height() ) / 2 );
228 fheroes2::ImageRestorer restorer( display, cur_pt.x, cur_pt.y, backSprite.width(), backSprite.height() );
229 fheroes2::Point dst_pt( cur_pt );
230 std::string message;
231
232 fheroes2::Rect src_rt( 0, 0, fheroes2::Display::DEFAULT_WIDTH, fheroes2::Display::DEFAULT_HEIGHT );
233
234 // background
235 dst_pt.x = cur_pt.x;
236 dst_pt.y = cur_pt.y;
237 fheroes2::Blit( backSprite, src_rt.x, src_rt.y, display, dst_pt.x, dst_pt.y, src_rt.width, src_rt.height );
238
239 // header
240 message = _( "%{name1} meets %{name2}" );
241 StringReplace( message, "%{name1}", GetName() );
242 StringReplace( message, "%{name2}", otherHero.GetName() );
243 Text text( message, Font::BIG );
244 text.Blit( cur_pt.x + 320 - text.w() / 2, cur_pt.y + 27 );
245
246 const int iconsH1XOffset = 34;
247 const int iconsH2XOffset = 566;
248 const int portraitYOffset = 72;
249
250 // portrait
251 dst_pt.x = cur_pt.x + 93;
252 dst_pt.y = cur_pt.y + portraitYOffset;
253 const fheroes2::Sprite & portrait1 = GetPortrait( PORT_BIG );
254 fheroes2::Rect hero1Area( dst_pt.x, dst_pt.y, portrait1.width(), portrait1.height() );
255 PortraitRedraw( dst_pt.x, dst_pt.y, PORT_BIG, display );
256
257 dst_pt.x = cur_pt.x + 445;
258 dst_pt.y = cur_pt.y + portraitYOffset;
259 const fheroes2::Sprite & portrait2 = otherHero.GetPortrait( PORT_BIG );
260 fheroes2::Rect hero2Area( dst_pt.x, dst_pt.y, portrait2.width(), portrait2.height() );
261 otherHero.PortraitRedraw( dst_pt.x, dst_pt.y, PORT_BIG, display );
262
263 MoraleIndicator moraleIndicator1( this );
264 dst_pt.x = cur_pt.x + iconsH1XOffset;
265 dst_pt.y = cur_pt.y + portraitYOffset + moraleIndicator1.GetArea().height / 3;
266 moraleIndicator1.SetPos( dst_pt );
267 moraleIndicator1.Redraw();
268
269 LuckIndicator luckIndicator1( this );
270 dst_pt.x = cur_pt.x + iconsH1XOffset;
271 dst_pt.y = cur_pt.y + portraitYOffset + portrait1.height() - luckIndicator1.GetArea().height * 4 / 3;
272 luckIndicator1.SetPos( dst_pt );
273 luckIndicator1.Redraw();
274
275 MoraleIndicator moraleIndicator2( &otherHero );
276 dst_pt.x = cur_pt.x + iconsH2XOffset;
277 dst_pt.y = cur_pt.y + portraitYOffset + moraleIndicator2.GetArea().height / 3;
278 moraleIndicator2.SetPos( dst_pt );
279 moraleIndicator2.Redraw();
280
281 LuckIndicator luckIndicator2( &otherHero );
282 dst_pt.x = cur_pt.x + iconsH2XOffset;
283 dst_pt.y = cur_pt.y + portraitYOffset + portrait2.height() - luckIndicator2.GetArea().height * 4 / 3;
284 luckIndicator2.SetPos( dst_pt );
285 luckIndicator2.Redraw();
286
287 // primary skill
288 fheroes2::ImageRestorer backPrimary( display, cur_pt.x + 255, cur_pt.y + 50, 130, 135 );
289
290 MeetingPrimarySkillsBar primskill_bar1( this );
291 primskill_bar1.SetColRows( 1, 4 );
292 primskill_bar1.SetVSpace( -1 );
293 primskill_bar1.SetTextOff( 70, -25 );
294 primskill_bar1.SetPos( cur_pt.x + 216, cur_pt.y + 51 );
295
296 MeetingPrimarySkillsBar primskill_bar2( &otherHero );
297 primskill_bar2.SetColRows( 1, 4 );
298 primskill_bar2.SetVSpace( -1 );
299 primskill_bar2.SetTextOff( -70, -25 );
300 primskill_bar2.SetPos( cur_pt.x + 389, cur_pt.y + 51 );
301
302 fheroes2::RedrawPrimarySkillInfo( cur_pt, &primskill_bar1, &primskill_bar2 );
303
304 // secondary skill
305 MeetingSecondarySkillsBar secskill_bar1( *this );
306 secskill_bar1.SetColRows( 8, 1 );
307 secskill_bar1.SetHSpace( -1 );
308 secskill_bar1.SetContent( secondary_skills.ToVector() );
309 secskill_bar1.SetPos( cur_pt.x + 22, cur_pt.y + 199 );
310 secskill_bar1.Redraw();
311
312 MeetingSecondarySkillsBar secskill_bar2( otherHero );
313 secskill_bar2.SetColRows( 8, 1 );
314 secskill_bar2.SetHSpace( -1 );
315 secskill_bar2.SetContent( otherHero.GetSecondarySkills().ToVector() );
316 secskill_bar2.SetPos( cur_pt.x + 353, cur_pt.y + 199 );
317 secskill_bar2.Redraw();
318
319 const fheroes2::Sprite & moveButtonBackground = fheroes2::AGG::GetICN( ICN::STONEBAK, 0 );
320 fheroes2::Blit( moveButtonBackground, 292, 270, display, cur_pt.x + 292, cur_pt.y + 270, 48, 44 );
321
322 // The original resources do not have such animated buttons so we have to create those.
323 fheroes2::ButtonSprite moveArmyToHero2 = createMoveButton( ICN::SWAP_ARROW_LEFT_TO_RIGHT, cur_pt.x + 298, cur_pt.y + 267, display );
324 fheroes2::ButtonSprite moveArmyToHero1 = createMoveButton( ICN::SWAP_ARROW_RIGHT_TO_LEFT, cur_pt.x + 298, cur_pt.y + 290, display );
325
326 fheroes2::ImageRestorer armyCountBackgroundRestorerLeft( display, cur_pt.x + 36, cur_pt.y + 310, 223, 20 );
327 fheroes2::ImageRestorer armyCountBackgroundRestorerRight( display, cur_pt.x + 381, cur_pt.y + 310, 223, 20 );
328
329 // army
330 dst_pt.x = cur_pt.x + 36;
331 dst_pt.y = cur_pt.y + 267;
332
333 MeetingArmyBar selectArmy1( &GetArmy() );
334 selectArmy1.SetColRows( 5, 1 );
335 selectArmy1.SetPos( dst_pt.x, dst_pt.y );
336 selectArmy1.SetHSpace( 2 );
337 selectArmy1.Redraw();
338
339 dst_pt.x = cur_pt.x + 381;
340 dst_pt.y = cur_pt.y + 267;
341
342 MeetingArmyBar selectArmy2( &otherHero.GetArmy() );
343 selectArmy2.SetColRows( 5, 1 );
344 selectArmy2.SetPos( dst_pt.x, dst_pt.y );
345 selectArmy2.SetHSpace( 2 );
346 selectArmy2.Redraw();
347
348 // artifact
349 dst_pt.x = cur_pt.x + 23;
350 dst_pt.y = cur_pt.y + 347;
351
352 MeetingArtifactBar selectArtifacts1( this );
353 selectArtifacts1.SetColRows( 7, 2 );
354 selectArtifacts1.SetHSpace( 2 );
355 selectArtifacts1.SetVSpace( 2 );
356 selectArtifacts1.SetContent( GetBagArtifacts() );
357 selectArtifacts1.SetPos( dst_pt.x, dst_pt.y );
358 selectArtifacts1.Redraw();
359
360 dst_pt.x = cur_pt.x + 367;
361 dst_pt.y = cur_pt.y + 347;
362
363 MeetingArtifactBar selectArtifacts2( &otherHero );
364 selectArtifacts2.SetColRows( 7, 2 );
365 selectArtifacts2.SetHSpace( 2 );
366 selectArtifacts2.SetVSpace( 2 );
367 selectArtifacts2.SetContent( otherHero.GetBagArtifacts() );
368 selectArtifacts2.SetPos( dst_pt.x, dst_pt.y );
369 selectArtifacts2.Redraw();
370
371 fheroes2::Blit( moveButtonBackground, 292, 363, display, cur_pt.x + 292, cur_pt.y + 363, 48, 44 );
372 fheroes2::ButtonSprite moveArtifactsToHero2 = createMoveButton( ICN::SWAP_ARROW_LEFT_TO_RIGHT, cur_pt.x + 298, cur_pt.y + 361, display );
373 fheroes2::ButtonSprite moveArtifactsToHero1 = createMoveButton( ICN::SWAP_ARROW_RIGHT_TO_LEFT, cur_pt.x + 298, cur_pt.y + 384, display );
374
375 // button exit
376 dst_pt.x = cur_pt.x + 280;
377 dst_pt.y = cur_pt.y + 428;
378 fheroes2::Button buttonExit( dst_pt.x, dst_pt.y, ICN::SWAPBTN, 0, 1 );
379
380 moveArmyToHero2.draw();
381 moveArmyToHero1.draw();
382 moveArtifactsToHero2.draw();
383 moveArtifactsToHero1.draw();
384 buttonExit.draw();
385
386 display.render();
387
388 MovePointsScaleFixed();
389 otherHero.MovePointsScaleFixed();
390
391 // scholar action
392 if ( Settings::Get().ExtWorldEyeEagleAsScholar() )
393 Heroes::ScholarAction( *this, otherHero );
394
395 LocalEvent & le = LocalEvent::Get();
396
397 // message loop
398 while ( le.HandleEvents() ) {
399 le.MousePressLeft( buttonExit.area() ) ? buttonExit.drawOnPress() : buttonExit.drawOnRelease();
400
401 if ( le.MousePressLeft( moveArmyToHero2.area() ) ) {
402 moveArmyToHero2.drawOnPress();
403 moveArmyToHero1.drawOnRelease();
404 }
405 else if ( le.MousePressLeft( moveArmyToHero1.area() ) ) {
406 moveArmyToHero1.drawOnPress();
407 moveArmyToHero2.drawOnRelease();
408 }
409 else {
410 moveArmyToHero1.drawOnRelease();
411 moveArmyToHero2.drawOnRelease();
412 }
413
414 if ( le.MousePressLeft( moveArtifactsToHero2.area() ) ) {
415 moveArtifactsToHero2.drawOnPress();
416 moveArtifactsToHero1.drawOnRelease();
417 }
418 else if ( le.MousePressLeft( moveArtifactsToHero1.area() ) ) {
419 moveArtifactsToHero1.drawOnPress();
420 moveArtifactsToHero2.drawOnRelease();
421 }
422 else {
423 moveArtifactsToHero1.drawOnRelease();
424 moveArtifactsToHero2.drawOnRelease();
425 }
426
427 if ( le.MouseClickLeft( buttonExit.area() ) || HotKeyCloseWindow )
428 break;
429
430 // selector troops event
431 if ( ( le.MouseCursor( selectArmy1.GetArea() ) && selectArmy1.QueueEventProcessing( selectArmy2 ) )
432 || ( le.MouseCursor( selectArmy2.GetArea() ) && selectArmy2.QueueEventProcessing( selectArmy1 ) ) ) {
433 if ( selectArtifacts1.isSelected() )
434 selectArtifacts1.ResetSelected();
435 else if ( selectArtifacts2.isSelected() )
436 selectArtifacts2.ResetSelected();
437
438 armyCountBackgroundRestorerLeft.restore();
439 armyCountBackgroundRestorerRight.restore();
440
441 selectArmy1.Redraw();
442 selectArmy2.Redraw();
443
444 moraleIndicator1.Redraw();
445 moraleIndicator2.Redraw();
446 luckIndicator1.Redraw();
447 luckIndicator2.Redraw();
448
449 display.render();
450 }
451
452 // selector artifacts event
453 if ( ( le.MouseCursor( selectArtifacts1.GetArea() ) && selectArtifacts1.QueueEventProcessing( selectArtifacts2 ) )
454 || ( le.MouseCursor( selectArtifacts2.GetArea() ) && selectArtifacts2.QueueEventProcessing( selectArtifacts1 ) ) ) {
455 if ( selectArmy1.isSelected() )
456 selectArmy1.ResetSelected();
457 else if ( selectArmy2.isSelected() )
458 selectArmy2.ResetSelected();
459
460 std::set<ArtifactSetData> assembledArtifacts = bag_artifacts.assembleArtifactSetIfPossible();
461 const std::set<ArtifactSetData> otherHeroAssembledArtifacts = otherHero.bag_artifacts.assembleArtifactSetIfPossible();
462
463 // Use insert instead of std::merge to make appveyour happy
464 assembledArtifacts.insert( otherHeroAssembledArtifacts.begin(), otherHeroAssembledArtifacts.end() );
465
466 for ( const ArtifactSetData & artifactSetData : assembledArtifacts )
467 Dialog::ArtifactInfo( "", _( artifactSetData._assembleMessage ), artifactSetData._assembledArtifactID );
468
469 selectArtifacts1.Redraw();
470 selectArtifacts2.Redraw();
471
472 backPrimary.restore();
473 fheroes2::RedrawPrimarySkillInfo( cur_pt, &primskill_bar1, &primskill_bar2 );
474 moraleIndicator1.Redraw();
475 moraleIndicator2.Redraw();
476 luckIndicator1.Redraw();
477 luckIndicator2.Redraw();
478
479 display.render();
480 }
481
482 if ( ( le.MouseCursor( primskill_bar1.GetArea() ) && primskill_bar1.QueueEventProcessing() )
483 || ( le.MouseCursor( primskill_bar2.GetArea() ) && primskill_bar2.QueueEventProcessing() )
484 || ( le.MouseCursor( secskill_bar1.GetArea() ) && secskill_bar1.QueueEventProcessing() )
485 || ( le.MouseCursor( secskill_bar2.GetArea() ) && secskill_bar2.QueueEventProcessing() ) ) {
486 display.render();
487 }
488
489 if ( le.MouseCursor( moraleIndicator1.GetArea() ) ) {
490 MoraleIndicator::QueueEventProcessing( moraleIndicator1 );
491 }
492 else if ( le.MouseCursor( moraleIndicator2.GetArea() ) ) {
493 MoraleIndicator::QueueEventProcessing( moraleIndicator2 );
494 }
495 else if ( le.MouseCursor( luckIndicator1.GetArea() ) ) {
496 LuckIndicator::QueueEventProcessing( luckIndicator1 );
497 }
498 else if ( le.MouseCursor( luckIndicator2.GetArea() ) ) {
499 LuckIndicator::QueueEventProcessing( luckIndicator2 );
500 }
501
502 if ( le.MouseClickLeft( hero1Area ) ) {
503 Game::OpenHeroesDialog( *this, false, false, true );
504
505 armyCountBackgroundRestorerLeft.restore();
506 armyCountBackgroundRestorerRight.restore();
507
508 selectArtifacts1.ResetSelected();
509 selectArtifacts2.ResetSelected();
510 selectArtifacts1.Redraw();
511 selectArtifacts2.Redraw();
512
513 selectArmy1.ResetSelected();
514 selectArmy2.ResetSelected();
515 selectArmy1.Redraw();
516 selectArmy2.Redraw();
517
518 moraleIndicator1.Redraw();
519 luckIndicator1.Redraw();
520
521 display.render();
522 }
523 else if ( le.MouseClickLeft( hero2Area ) ) {
524 Game::OpenHeroesDialog( otherHero, false, false, true );
525
526 armyCountBackgroundRestorerLeft.restore();
527 armyCountBackgroundRestorerRight.restore();
528
529 selectArtifacts1.ResetSelected();
530 selectArtifacts2.ResetSelected();
531 selectArtifacts1.Redraw();
532 selectArtifacts2.Redraw();
533
534 selectArmy1.ResetSelected();
535 selectArmy2.ResetSelected();
536 selectArmy1.Redraw();
537 selectArmy2.Redraw();
538
539 moraleIndicator2.Redraw();
540 luckIndicator2.Redraw();
541
542 display.render();
543 }
544 else if ( le.MouseClickLeft( moveArmyToHero2.area() ) ) {
545 otherHero.GetArmy().MoveTroops( GetArmy() );
546
547 armyCountBackgroundRestorerLeft.restore();
548 armyCountBackgroundRestorerRight.restore();
549
550 selectArmy1.ResetSelected();
551 selectArmy2.ResetSelected();
552 selectArmy1.Redraw();
553 selectArmy2.Redraw();
554
555 moraleIndicator1.Redraw();
556 moraleIndicator2.Redraw();
557
558 display.render();
559 }
560 else if ( le.MouseClickLeft( moveArmyToHero1.area() ) ) {
561 GetArmy().MoveTroops( otherHero.GetArmy() );
562
563 armyCountBackgroundRestorerLeft.restore();
564 armyCountBackgroundRestorerRight.restore();
565
566 selectArmy1.ResetSelected();
567 selectArmy2.ResetSelected();
568 selectArmy1.Redraw();
569 selectArmy2.Redraw();
570
571 moraleIndicator1.Redraw();
572 moraleIndicator2.Redraw();
573
574 display.render();
575 }
576 else if ( le.MouseClickLeft( moveArtifactsToHero2.area() ) ) {
577 moveArtifacts( GetBagArtifacts(), otherHero.GetBagArtifacts() );
578
579 selectArtifacts1.ResetSelected();
580 selectArtifacts2.ResetSelected();
581 selectArtifacts1.Redraw();
582 selectArtifacts2.Redraw();
583
584 backPrimary.restore();
585 fheroes2::RedrawPrimarySkillInfo( cur_pt, &primskill_bar1, &primskill_bar2 );
586 moraleIndicator1.Redraw();
587 moraleIndicator2.Redraw();
588 luckIndicator1.Redraw();
589 luckIndicator2.Redraw();
590
591 display.render();
592 }
593 else if ( le.MouseClickLeft( moveArtifactsToHero1.area() ) ) {
594 moveArtifacts( otherHero.GetBagArtifacts(), GetBagArtifacts() );
595
596 selectArtifacts1.ResetSelected();
597 selectArtifacts2.ResetSelected();
598 selectArtifacts1.Redraw();
599 selectArtifacts2.Redraw();
600
601 backPrimary.restore();
602 fheroes2::RedrawPrimarySkillInfo( cur_pt, &primskill_bar1, &primskill_bar2 );
603 moraleIndicator1.Redraw();
604 moraleIndicator2.Redraw();
605 luckIndicator1.Redraw();
606 luckIndicator2.Redraw();
607
608 display.render();
609 }
610 }
611
612 selectArmy1.ResetSelected();
613 selectArmy2.ResetSelected();
614 selectArtifacts1.ResetSelected();
615 selectArtifacts2.ResetSelected();
616
617 backPrimary.reset();
618 armyCountBackgroundRestorerLeft.reset();
619 armyCountBackgroundRestorerRight.reset();
620 restorer.restore();
621 display.render();
622 }
623
ScholarAction(Heroes & hero1,Heroes & hero2)624 void Heroes::ScholarAction( Heroes & hero1, Heroes & hero2 )
625 {
626 if ( !hero1.HaveSpellBook() || !hero2.HaveSpellBook() ) {
627 DEBUG_LOG( DBG_GAME, DBG_INFO, "spell_book disabled" );
628 return;
629 }
630 else if ( !Settings::Get().ExtWorldEyeEagleAsScholar() ) {
631 DEBUG_LOG( DBG_GAME, DBG_WARN, "EyeEagleAsScholar settings disabled" );
632 return;
633 }
634
635 const int scholar1 = hero1.GetLevelSkill( Skill::Secondary::EAGLEEYE );
636 const int scholar2 = hero2.GetLevelSkill( Skill::Secondary::EAGLEEYE );
637 int scholar = 0;
638
639 Heroes * teacher = nullptr;
640 Heroes * learner = nullptr;
641
642 if ( scholar1 && scholar1 >= scholar2 ) {
643 teacher = &hero1;
644 learner = &hero2;
645 scholar = scholar1;
646 }
647 else if ( scholar2 && scholar2 >= scholar1 ) {
648 teacher = &hero2;
649 learner = &hero1;
650 scholar = scholar2;
651 }
652 else {
653 DEBUG_LOG( DBG_GAME, DBG_WARN, "Eagle Eye skill not found" );
654 return;
655 }
656
657 // skip bag artifacts
658 SpellStorage teach = teacher->spell_book.SetFilter( SpellBook::Filter::ALL );
659 SpellStorage learn = learner->spell_book.SetFilter( SpellBook::Filter::ALL );
660
661 // remove_if for learn spells
662 if ( !learn.empty() ) {
663 learn.erase( std::remove_if( learn.begin(), learn.end(),
664 [teacher]( const Spell & spell ) { return teacher->HaveSpell( spell ) || !teacher->CanTeachSpell( spell ); } ),
665 learn.end() );
666 }
667
668 // remove_if for teach spells
669 if ( !teach.empty() ) {
670 teach.erase( std::remove_if( teach.begin(), teach.end(),
671 [learner, teacher]( const Spell & spell ) { return learner->HaveSpell( spell ) || !teacher->CanTeachSpell( spell ); } ),
672 teach.end() );
673 }
674
675 std::string spells1;
676 std::string spells2;
677
678 // learning
679 for ( SpellStorage::const_iterator it = learn.begin(); it != learn.end(); ++it ) {
680 teacher->AppendSpellToBook( *it );
681 if ( !spells1.empty() )
682 spells1.append( it + 1 == learn.end() ? _( " and " ) : ", " );
683 spells1.append( ( *it ).GetName() );
684 }
685
686 // teacher
687 for ( SpellStorage::const_iterator it = teach.begin(); it != teach.end(); ++it ) {
688 learner->AppendSpellToBook( *it );
689 if ( !spells2.empty() )
690 spells2.append( it + 1 == teach.end() ? _( " and " ) : ", " );
691 spells2.append( ( *it ).GetName() );
692 }
693
694 if ( teacher->isControlHuman() || learner->isControlHuman() ) {
695 std::string message;
696
697 if ( !spells1.empty() && !spells2.empty() )
698 message = _( "%{teacher}, whose %{level} %{scholar} knows many magical secrets, learns %{spells1} from %{learner}, and teaches %{spells2} to %{learner}." );
699 else if ( !spells1.empty() )
700 message = _( "%{teacher}, whose %{level} %{scholar} knows many magical secrets, learns %{spells1} from %{learner}." );
701 else if ( !spells2.empty() )
702 message = _( "%{teacher}, whose %{level} %{scholar} knows many magical secrets, teaches %{spells2} to %{learner}." );
703
704 if ( !message.empty() ) {
705 StringReplace( message, "%{teacher}", teacher->GetName() );
706 StringReplace( message, "%{learner}", learner->GetName() );
707 StringReplace( message, "%{level}", Skill::Level::String( scholar ) );
708 StringReplace( message, "%{scholar}", Skill::Secondary::String( Skill::Secondary::EAGLEEYE ) );
709 StringReplace( message, "%{spells1}", spells1 );
710 StringReplace( message, "%{spells2}", spells2 );
711
712 Dialog::Message( _( "Scholar Ability" ), message, Font::BIG, Dialog::OK );
713 }
714 }
715 }
716