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 "agg_image.h"
24 #include "bin_info.h"
25 #include "cursor.h"
26 #include "dialog.h"
27 #include "game.h"
28 #include "icn.h"
29 #include "kingdom.h"
30 #include "monster.h"
31 #include "payment.h"
32 #include "settings.h"
33 #include "text.h"
34 #include "tools.h"
35 #include "translations.h"
36 #include "world.h"
37 
38 #include <cassert>
39 
RedrawCurrentInfo(const fheroes2::Point & pos,u32 result,const payment_t & paymentMonster,const payment_t & paymentCosts,const Funds & funds,const std::string & label)40 void RedrawCurrentInfo( const fheroes2::Point & pos, u32 result, const payment_t & paymentMonster, const payment_t & paymentCosts, const Funds & funds,
41                         const std::string & label )
42 {
43     Text text;
44 
45     text.Set( std::to_string( result ), Font::BIG );
46     text.Blit( pos.x + 167 - text.w() / 2, pos.y + 161 );
47     const std::string sgold = std::to_string( paymentCosts.gold ) + " " + "(" + std::to_string( funds.gold - paymentCosts.gold ) + ")";
48     int rsext = paymentMonster.GetValidItems() & ~Resource::GOLD;
49 
50     if ( rsext ) {
51         text.Set( sgold, Font::SMALL );
52         text.Blit( pos.x + 133 - text.w() / 2, pos.y + 228 );
53 
54         text.Set( std::to_string( paymentCosts.Get( rsext ) ) + " " + "(" + std::to_string( funds.Get( rsext ) - paymentCosts.Get( rsext ) ) + ")", Font::SMALL );
55         text.Blit( pos.x + 195 - text.w() / 2, pos.y + 228 );
56     }
57     else {
58         text.Set( sgold, Font::SMALL );
59         text.Blit( pos.x + 160 - text.w() / 2, pos.y + 228 );
60     }
61 
62     text.Set( label, Font::SMALL );
63     text.Blit( pos.x + 167 - text.w() / 2, pos.y + 180 );
64 }
65 
RedrawResourceInfo(const fheroes2::Image & sres,const fheroes2::Point & pos,s32 value,s32 px1,s32 py1,s32 px2,s32 py2)66 void RedrawResourceInfo( const fheroes2::Image & sres, const fheroes2::Point & pos, s32 value, s32 px1, s32 py1, s32 px2, s32 py2 )
67 {
68     fheroes2::Point dst_pt( pos.x + px1, pos.y + py1 );
69     fheroes2::Blit( sres, fheroes2::Display::instance(), dst_pt.x, dst_pt.y );
70 
71     const Text text( std::to_string( value ), Font::SMALL );
72     dst_pt.x = pos.x + px2 - text.w() / 2;
73     dst_pt.y = pos.y + py2;
74     text.Blit( dst_pt.x, dst_pt.y );
75 }
76 
RedrawMonsterInfo(const fheroes2::Rect & pos,const Monster & monster,u32 available,bool showTotalSum)77 void RedrawMonsterInfo( const fheroes2::Rect & pos, const Monster & monster, u32 available, bool showTotalSum )
78 {
79     fheroes2::Display & display = fheroes2::Display::instance();
80     const payment_t paymentMonster = monster.GetCost();
81     const bool extres = 2 == paymentMonster.GetValidItemsCount();
82 
83     // text recruit monster
84     std::string str = _( "Recruit %{name}" );
85     StringReplace( str, "%{name}", monster.GetMultiName() );
86     Text text( str, Font::YELLOW_BIG );
87     fheroes2::Point dst_pt( pos.x + ( pos.width - text.w() ) / 2, pos.y + 25 );
88     text.Blit( dst_pt.x, dst_pt.y );
89 
90     // sprite monster
91     const int monsterId = monster.GetID();
92     const Bin_Info::MonsterAnimInfo & monsterInfo = Bin_Info::GetMonsterInfo( monsterId );
93     assert( !monsterInfo.animationFrames[Bin_Info::MonsterAnimInfo::STATIC].empty() );
94 
95     const fheroes2::Sprite & smon = fheroes2::AGG::GetICN( monster.GetMonsterSprite(), monsterInfo.animationFrames[Bin_Info::MonsterAnimInfo::STATIC][0] );
96     dst_pt.x = pos.x + 80 + smon.x() - ( monster.isWide() ? 22 : 0 );
97     dst_pt.y = pos.y + 135 - smon.height();
98 
99     if ( monsterId == Monster::CHAMPION ) {
100         ++dst_pt.x;
101     }
102 
103     fheroes2::Blit( smon, display, dst_pt.x, dst_pt.y );
104 
105     // info resource
106     // gold
107     const fheroes2::Sprite & sgold = fheroes2::AGG::GetICN( ICN::RESOURCE, 6 );
108     RedrawResourceInfo( sgold, pos.getPosition(), paymentMonster.gold, extres ? 150 : 175, 75, extres ? 183 : 205, 103 );
109     if ( showTotalSum ) {
110         dst_pt.x = pos.x + ( extres ? 105 : 130 );
111         dst_pt.y = pos.y + 200;
112         fheroes2::Blit( sgold, display, dst_pt.x, dst_pt.y );
113     }
114 
115     if ( paymentMonster.crystal ) {
116         const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, 4 );
117         RedrawResourceInfo( sres, pos.getPosition(), paymentMonster.crystal, 222, 69, 240, 103 );
118         if ( showTotalSum ) {
119             dst_pt.x = pos.x + 177;
120             dst_pt.y = pos.y + 194;
121             fheroes2::Blit( sres, display, dst_pt.x, dst_pt.y );
122         }
123     }
124     else if ( paymentMonster.mercury ) {
125         const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, 1 );
126         RedrawResourceInfo( sres, pos.getPosition(), paymentMonster.mercury, 225, 72, 240, 103 );
127         if ( showTotalSum ) {
128             dst_pt.x = pos.x + 180;
129             dst_pt.y = pos.y + 197;
130             fheroes2::Blit( sres, display, dst_pt.x, dst_pt.y );
131         }
132     }
133     else if ( paymentMonster.wood ) {
134         const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, 0 );
135         RedrawResourceInfo( sres, pos.getPosition(), paymentMonster.wood, 225, 72, 240, 103 );
136         if ( showTotalSum ) {
137             dst_pt.x = pos.x + 180;
138             dst_pt.y = pos.y + 197;
139             fheroes2::Blit( sres, display, dst_pt.x, dst_pt.y );
140         }
141     }
142     else if ( paymentMonster.ore ) {
143         const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, 2 );
144         RedrawResourceInfo( sres, pos.getPosition(), paymentMonster.ore, 225, 72, 240, 103 );
145         if ( showTotalSum ) {
146             dst_pt.x = pos.x + 180;
147             dst_pt.y = pos.y + 197;
148             fheroes2::Blit( sres, display, dst_pt.x, dst_pt.y );
149         }
150     }
151     else if ( paymentMonster.sulfur ) {
152         const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, 3 );
153         RedrawResourceInfo( sres, pos.getPosition(), paymentMonster.sulfur, 225, 75, 240, 103 );
154         if ( showTotalSum ) {
155             dst_pt.x = pos.x + 180;
156             dst_pt.y = pos.y + 200;
157             fheroes2::Blit( sres, display, dst_pt.x, dst_pt.y );
158         }
159     }
160     else if ( paymentMonster.gems ) {
161         const fheroes2::Sprite & sres = fheroes2::AGG::GetICN( ICN::RESOURCE, 5 );
162         RedrawResourceInfo( sres, pos.getPosition(), paymentMonster.gems, 225, 75, 240, 103 );
163         if ( showTotalSum ) {
164             dst_pt.x = pos.x + 180;
165             dst_pt.y = pos.y + 200;
166             fheroes2::Blit( sres, display, dst_pt.x, dst_pt.y );
167         }
168     }
169 
170     str = _( "Available: %{count}" );
171     StringReplace( str, "%{count}", available );
172     text.Set( str, Font::SMALL );
173     text.Blit( pos.x + 80 - text.w() / 2, pos.y + 135 );
174 }
175 
RedrawStaticInfo(const fheroes2::Rect & pos,const Monster & monster,u32 available)176 void RedrawStaticInfo( const fheroes2::Rect & pos, const Monster & monster, u32 available )
177 {
178     fheroes2::Blit( fheroes2::AGG::GetICN( ICN::RECRBKG, 0 ), fheroes2::Display::instance(), pos.x, pos.y );
179 
180     RedrawMonsterInfo( pos, monster, available, true );
181 
182     // text number buy
183     Text text;
184     text.Set( _( "Number to buy:" ), Font::SMALL );
185     text.Blit( pos.x + 29, pos.y + 163 );
186 }
187 
SwitchMaxMinButtons(fheroes2::ButtonBase & btnMax,fheroes2::ButtonBase & btnMin,bool max)188 const char * SwitchMaxMinButtons( fheroes2::ButtonBase & btnMax, fheroes2::ButtonBase & btnMin, bool max )
189 {
190     if ( btnMax.isEnabled() || btnMin.isEnabled() ) {
191         if ( max ) {
192             btnMax.disable();
193             btnMin.enable();
194         }
195         else {
196             btnMin.disable();
197             btnMax.enable();
198         }
199 
200         return max ? _( "Max" ) : _( "Min" );
201     }
202 
203     return "";
204 }
205 
CalculateMax(const Monster & monster,const Kingdom & kingdom,u32 available)206 u32 CalculateMax( const Monster & monster, const Kingdom & kingdom, u32 available )
207 {
208     u32 max = 0;
209     while ( kingdom.AllowPayment( monster.GetCost() * ( max + 1 ) ) && ( max + 1 ) <= available )
210         ++max;
211 
212     return max;
213 }
214 
RecruitMonster(const Monster & monster0,u32 available,const bool allowDowngradedMonster,const int32_t windowOffsetY)215 Troop Dialog::RecruitMonster( const Monster & monster0, u32 available, const bool allowDowngradedMonster, const int32_t windowOffsetY )
216 {
217     fheroes2::Display & display = fheroes2::Display::instance();
218     LocalEvent & le = LocalEvent::Get();
219 
220     // setup cursor
221     const CursorRestorer cursorRestorer( true, Cursor::POINTER );
222 
223     // calculate max count
224     Monster monster = monster0;
225     payment_t paymentMonster = monster.GetCost();
226     const Kingdom & kingdom = world.GetKingdom( Settings::Get().CurrentColor() );
227 
228     u32 max = CalculateMax( monster, kingdom, available );
229     u32 result = max;
230 
231     payment_t paymentCosts( paymentMonster * result );
232     const fheroes2::Sprite & box = fheroes2::AGG::GetICN( ICN::RECRBKG, 0 );
233     const fheroes2::Sprite & boxShadow = fheroes2::AGG::GetICN( ICN::RECRBKG, 1 );
234 
235     const fheroes2::Point dialogOffset( ( display.width() - box.width() ) / 2, ( display.height() - box.height() ) / 2 + windowOffsetY );
236     const fheroes2::Point shadowOffset( dialogOffset.x - BORDERWIDTH, dialogOffset.y );
237 
238     fheroes2::ImageRestorer back( display, shadowOffset.x, shadowOffset.y, box.width() + BORDERWIDTH, box.height() + BORDERWIDTH );
239     const fheroes2::Rect pos( dialogOffset.x, dialogOffset.y, box.width(), box.height() );
240 
241     fheroes2::Blit( boxShadow, display, pos.x - BORDERWIDTH, pos.y + BORDERWIDTH );
242     fheroes2::Blit( box, display, pos.x, pos.y );
243 
244     RedrawStaticInfo( pos, monster, available );
245 
246     // buttons
247     fheroes2::Point dst_pt;
248 
249     dst_pt.x = pos.x + 34;
250     dst_pt.y = pos.y + 249;
251     fheroes2::Button buttonOk( dst_pt.x, dst_pt.y, ICN::RECRUIT, 8, 9 );
252 
253     dst_pt.x = pos.x + 187;
254     dst_pt.y = pos.y + 249;
255     fheroes2::Button buttonCancel( dst_pt.x, dst_pt.y, ICN::RECRUIT, 6, 7 );
256 
257     dst_pt.x = pos.x + 229;
258     dst_pt.y = pos.y + 156;
259     fheroes2::ButtonSprite buttonMax( dst_pt.x, dst_pt.y, fheroes2::AGG::GetICN( ICN::RECRUIT, 4 ), fheroes2::AGG::GetICN( ICN::RECRUIT, 5 ),
260                                       fheroes2::AGG::GetICN( ICN::MAX_DISABLED_BUTTON, 0 ) );
261     fheroes2::Button buttonMin( dst_pt.x, dst_pt.y, ICN::NON_UNIFORM_GOOD_MIN_BUTTON, 0, 1 );
262 
263     dst_pt.x = pos.x + 205;
264     dst_pt.y = pos.y + 154;
265     fheroes2::Button buttonUp( dst_pt.x, dst_pt.y, ICN::RECRUIT, 0, 1 );
266 
267     dst_pt.x = pos.x + 205;
268     dst_pt.y = pos.y + 169;
269     fheroes2::Button buttonDn( dst_pt.x, dst_pt.y, ICN::RECRUIT, 2, 3 );
270 
271     fheroes2::TimedEventValidator timedButtonUp( [&buttonUp]() { return buttonUp.isPressed(); } );
272     fheroes2::TimedEventValidator timedButtonDn( [&buttonDn]() { return buttonDn.isPressed(); } );
273 
274     buttonDn.subscribe( &timedButtonDn );
275     buttonUp.subscribe( &timedButtonUp );
276 
277     const fheroes2::Rect rtWheel( pos.x + 130, pos.y + 155, 100, 30 );
278 
279     // Create monster switching arrows
280     fheroes2::ButtonSprite monsterSwitchLeft;
281     fheroes2::ButtonSprite monsterSwitchRight;
282 
283     if ( allowDowngradedMonster && monster0.GetDowngrade() != monster0 ) {
284         monsterSwitchLeft.setSprite( fheroes2::AGG::GetICN( ICN::MONSTER_SWITCH_LEFT_ARROW, 0 ), fheroes2::AGG::GetICN( ICN::MONSTER_SWITCH_LEFT_ARROW, 1 ) );
285         monsterSwitchRight.setSprite( fheroes2::AGG::GetICN( ICN::MONSTER_SWITCH_RIGHT_ARROW, 0 ), fheroes2::AGG::GetICN( ICN::MONSTER_SWITCH_RIGHT_ARROW, 1 ) );
286 
287         monsterSwitchLeft.setPosition( pos.x + 24, pos.y + 80 );
288         monsterSwitchRight.setPosition( pos.x + 121, pos.y + 80 );
289     }
290     else {
291         monsterSwitchLeft.hide();
292         monsterSwitchRight.hide();
293 
294         monsterSwitchLeft.disable();
295         monsterSwitchRight.disable();
296     }
297 
298     const fheroes2::Rect monsterArea( pos.x + 40, pos.y + 35, 75, 95 );
299 
300     if ( 0 == result ) {
301         buttonOk.disable();
302         buttonMax.disable();
303         buttonMin.disable();
304         buttonMax.draw();
305     }
306 
307     const Funds & funds = kingdom.GetFunds();
308     std::string maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, true );
309     RedrawCurrentInfo( pos.getPosition(), result, paymentMonster, paymentCosts, funds, maxmin );
310 
311     buttonOk.draw();
312     buttonCancel.draw();
313     if ( buttonMax.isEnabled() )
314         buttonMax.draw();
315     if ( buttonMin.isEnabled() )
316         buttonMin.draw();
317     buttonUp.draw();
318     buttonDn.draw();
319     monsterSwitchLeft.draw();
320     monsterSwitchRight.draw();
321 
322     display.render();
323 
324     std::vector<Monster> upgrades = { monster0 };
325     while ( upgrades.back().GetDowngrade() != upgrades.back() ) {
326         upgrades.emplace_back( upgrades.back().GetDowngrade() );
327     }
328 
329     // str loop
330     while ( le.HandleEvents() ) {
331         bool redraw = false;
332 
333         if ( buttonOk.isEnabled() )
334             le.MousePressLeft( buttonOk.area() ) ? buttonOk.drawOnPress() : buttonOk.drawOnRelease();
335         le.MousePressLeft( buttonCancel.area() ) ? buttonCancel.drawOnPress() : buttonCancel.drawOnRelease();
336         le.MousePressLeft( buttonUp.area() ) ? buttonUp.drawOnPress() : buttonUp.drawOnRelease();
337         le.MousePressLeft( buttonDn.area() ) ? buttonDn.drawOnPress() : buttonDn.drawOnRelease();
338 
339         le.MousePressLeft( monsterSwitchLeft.area() ) ? monsterSwitchLeft.drawOnPress() : monsterSwitchLeft.drawOnRelease();
340         le.MousePressLeft( monsterSwitchRight.area() ) ? monsterSwitchRight.drawOnPress() : monsterSwitchRight.drawOnRelease();
341 
342         if ( buttonMax.isEnabled() )
343             le.MousePressLeft( buttonMax.area() ) ? buttonMax.drawOnPress() : buttonMax.drawOnRelease();
344         if ( buttonMin.isEnabled() )
345             le.MousePressLeft( buttonMin.area() ) ? buttonMin.drawOnPress() : buttonMin.drawOnRelease();
346 
347         bool updateCost = false;
348         if ( allowDowngradedMonster && upgrades.size() > 1 ) {
349             if ( le.MouseClickLeft( monsterSwitchLeft.area() ) || le.KeyPress( KEY_LEFT ) ) {
350                 for ( size_t i = 0; i < upgrades.size(); ++i ) {
351                     if ( upgrades[i] == monster ) {
352                         if ( i < upgrades.size() - 1 ) {
353                             monster = upgrades[i + 1];
354                         }
355                         else {
356                             monster = upgrades[0];
357                         }
358                         break;
359                     }
360                 }
361                 updateCost = true;
362             }
363             else if ( le.MouseClickLeft( monsterSwitchRight.area() ) || le.KeyPress( KEY_RIGHT ) ) {
364                 for ( size_t i = 0; i < upgrades.size(); ++i ) {
365                     if ( upgrades[i] == monster ) {
366                         if ( i > 0 ) {
367                             monster = upgrades[i - 1];
368                         }
369                         else {
370                             monster = upgrades.back();
371                         }
372                         break;
373                     }
374                 }
375                 updateCost = true;
376             }
377         }
378 
379         if ( updateCost ) {
380             max = CalculateMax( monster, kingdom, available );
381             result = max;
382             paymentMonster = monster.GetCost();
383             paymentCosts = paymentMonster * result;
384             redraw = true;
385             maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, true );
386         }
387 
388         bool skipEventCheck = false;
389         if ( le.MousePressRight( monsterArea ) ) {
390             Dialog::ArmyInfo( Troop( monster, available ), Dialog::READONLY );
391             redraw = true;
392         }
393         else if ( le.MouseClickLeft( monsterArea ) ) {
394             Dialog::ArmyInfo( Troop( monster, available ), Dialog::READONLY | Dialog::BUTTONS );
395             redraw = true;
396             skipEventCheck = true;
397         }
398 
399         if ( PressIntKey( max, result ) ) {
400             paymentCosts = paymentMonster * result;
401             redraw = true;
402             maxmin.clear();
403 
404             if ( result == max ) {
405                 maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, true );
406             }
407             else if ( result == 1 ) {
408                 maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, false );
409             }
410         }
411 
412         if ( ( le.MouseWheelUp( rtWheel ) || le.MouseClickLeft( buttonUp.area() ) || le.KeyPress( KEY_UP ) || timedButtonUp.isDelayPassed() ) && result < max ) {
413             ++result;
414             paymentCosts += paymentMonster;
415             redraw = true;
416             maxmin.clear();
417 
418             if ( result == max ) {
419                 maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, true );
420             }
421             else if ( result == 1 ) {
422                 maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, false );
423             }
424         }
425         else if ( ( le.MouseWheelDn( rtWheel ) || le.MouseClickLeft( buttonDn.area() ) || le.KeyPress( KEY_DOWN ) || timedButtonDn.isDelayPassed() ) && result ) {
426             --result;
427             paymentCosts -= paymentMonster;
428             redraw = true;
429             maxmin.clear();
430 
431             if ( result == max ) {
432                 maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, true );
433             }
434             else if ( result == 1 ) {
435                 maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, false );
436             }
437         }
438         else if ( buttonMax.isEnabled() && le.MouseClickLeft( buttonMax.area() ) && result != max ) {
439             maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, true );
440             result = max;
441             paymentCosts = paymentMonster * max;
442             redraw = true;
443         }
444         else if ( buttonMin.isEnabled() && le.MouseClickLeft( buttonMin.area() ) && result != 1 ) {
445             maxmin = SwitchMaxMinButtons( buttonMax, buttonMin, false );
446             result = 1;
447             paymentCosts = paymentMonster;
448             redraw = true;
449         }
450 
451         if ( redraw ) {
452             RedrawStaticInfo( pos, monster, available );
453             RedrawCurrentInfo( pos.getPosition(), result, paymentMonster, paymentCosts, funds, maxmin );
454 
455             if ( 0 == result ) {
456                 buttonOk.disable();
457                 buttonOk.draw();
458             }
459             else {
460                 buttonOk.enable();
461                 buttonOk.draw();
462             }
463 
464             if ( buttonMax.isEnabled() || max == 0 )
465                 buttonMax.draw();
466             if ( buttonMin.isEnabled() )
467                 buttonMin.draw();
468 
469             monsterSwitchLeft.draw();
470             monsterSwitchRight.draw();
471 
472             display.render();
473         }
474 
475         if ( buttonOk.isEnabled() && ( le.MouseClickLeft( buttonOk.area() ) || Game::HotKeyPressEvent( Game::EVENT_DEFAULT_READY ) ) )
476             break;
477 
478         if ( le.MouseClickLeft( buttonCancel.area() ) || ( Game::HotKeyPressEvent( Game::EVENT_DEFAULT_EXIT ) && !skipEventCheck ) ) {
479             result = 0;
480             break;
481         }
482     }
483 
484     back.restore();
485     display.render();
486 
487     return Troop( monster, result );
488 }
489 
DwellingInfo(const Monster & monster,u32 available)490 void Dialog::DwellingInfo( const Monster & monster, u32 available )
491 {
492     fheroes2::Display & display = fheroes2::Display::instance();
493 
494     // setup cursor
495     const CursorRestorer cursorRestorer( false, Cursor::POINTER );
496 
497     const fheroes2::Sprite & box = fheroes2::AGG::GetICN( ICN::RECR2BKG, 0 );
498     const fheroes2::Sprite & boxShadow = fheroes2::AGG::GetICN( ICN::RECR2BKG, 1 );
499 
500     const fheroes2::Point dialogOffset( ( display.width() - box.width() ) / 2, display.height() / 2 - display.DEFAULT_HEIGHT / 2 + BORDERWIDTH );
501     const fheroes2::Point shadowOffset( dialogOffset.x - BORDERWIDTH, dialogOffset.y );
502 
503     fheroes2::ImageRestorer back( display, shadowOffset.x, shadowOffset.y, box.width() + BORDERWIDTH, box.height() + BORDERWIDTH );
504     const fheroes2::Rect pos( dialogOffset.x, dialogOffset.y, box.width(), box.height() );
505 
506     fheroes2::Blit( boxShadow, display, pos.x - BORDERWIDTH, pos.y + BORDERWIDTH );
507     fheroes2::Blit( box, display, pos.x, pos.y );
508 
509     LocalEvent & le = LocalEvent::Get();
510 
511     RedrawMonsterInfo( pos, monster, available, false );
512 
513     display.render();
514 
515     while ( le.HandleEvents() && le.MousePressRight() )
516         ;
517 
518     back.restore();
519     display.render();
520 }
521