1 ////////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2018 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software and
7 // you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ////////////////////////////////////////////////////////////////////////////////
10 
11 #include "modes/battle/finish/battle_victory.h"
12 
13 #include "modes/battle/battle.h"
14 #include "modes/battle/objects/battle_character.h"
15 #include "modes/battle/objects/battle_enemy.h"
16 
17 #include "modes/menu/menu_mode.h"
18 
19 #include "common/global/global.h"
20 #include "common/global/actors/global_character.h"
21 
22 #include "common/gui/menu_window.h"
23 
24 #include "engine/video/video_utils.h"
25 #include "engine/system.h"
26 #include "engine/input.h"
27 #include "engine/audio/audio.h"
28 
29 #include <assert.h>
30 
31 using namespace vt_gui;
32 using namespace vt_video;
33 using namespace vt_system;
34 using namespace vt_global;
35 using namespace vt_input;
36 using namespace vt_utils;
37 using namespace vt_audio;
38 
39 namespace vt_battle
40 {
41 
42 namespace private_battle
43 {
44 
45 //! \brief Draw position and dimension constants used for GUI objects
46 //@{
47 const float TOP_WINDOW_XPOS        = 240.0f;
48 const float TOP_WINDOW_YPOS        = 104.0f;
49 const float TOP_WINDOW_WIDTH       = 600.0f;
50 const float TOP_WINDOW_HEIGHT      = 64.0f;
51 
52 const float CHAR_WINDOW_XPOS       = 360.0f;
53 const float CHAR_WINDOW_YPOS       = 104.0f+ TOP_WINDOW_HEIGHT - 1.0f;
54 const float CHAR_WINDOW_WIDTH      = 380.0f;
55 const float CHAR_WINDOW_HEIGHT     = 118.0f;
56 
57 const float SPOILS_WINDOW_XPOS     = 550.0f;
58 const float SPOILS_WINDOW_YPOS     = 104.0f + TOP_WINDOW_HEIGHT - 1.0f;
59 const float SPOILS_WINDOW_WIDTH    = 360.0f;
60 const float SPOILS_WINDOW_HEIGHT   = 320.0f;
61 //@}
62 
63 //! \brief The set of victory options that the player can select
64 //@{
65 //! End battle
66 const uint32_t VICTORY_OPTION_END_BATTLE     = 0;
67 //! Improve skills
68 const uint32_t VICTORY_OPTION_IMPROVE_SKILLS = 1;
69 //@}
70 
BattleVictory()71 BattleVictory::BattleVictory() :
72     _state(VICTORY_INVALID),
73     _characters_number(0),
74     _xp_earned(0),
75     _drunes_dropped(0),
76     _begin_counting_xp(false),
77     _begin_counting_drunes(false),
78     _number_character_windows_created(0)
79 {
80     _outcome_text.SetPosition(212.0f, 30.0f);
81     _outcome_text.SetDimensions(512.0f, 50.0f);
82     _outcome_text.SetTextStyle(TextStyle("text28", Color::white));
83     _outcome_text.SetDisplayMode(VIDEO_TEXT_INSTANT);
84     _outcome_text.SetDisplayText(UTranslate("The heroes were victorious!"));
85 
86     _header_window.Create(TOP_WINDOW_WIDTH, TOP_WINDOW_HEIGHT, ~VIDEO_MENU_EDGE_BOTTOM, VIDEO_MENU_EDGE_BOTTOM);
87     _header_window.SetPosition(TOP_WINDOW_XPOS, TOP_WINDOW_YPOS);
88     _header_window.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
89     _header_window.Show();
90 
91     _spoils_window.Create(SPOILS_WINDOW_WIDTH, SPOILS_WINDOW_HEIGHT);
92     _spoils_window.SetPosition(SPOILS_WINDOW_XPOS, SPOILS_WINDOW_YPOS);
93     _spoils_window.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
94     _spoils_window.Show();
95 
96     _header_xp.SetOwner(&_header_window);
97     _header_xp.SetPosition(30.0f, 30.0f);
98     _header_xp.SetDimensions(TOP_WINDOW_WIDTH / 5.0f * 4.0f, 40.0f);
99     _header_xp.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_CENTER);
100     _header_xp.SetTextStyle(TextStyle("text20", Color::white));
101     _header_xp.SetDisplayMode(VIDEO_TEXT_INSTANT);
102 
103     // Reset raw XP bonus
104     for(uint32_t i = 0; i < 4; i++) {
105         _raw_xp_given[i] = true;
106         _raw_xp_won[i] = false;
107     }
108 
109     _drunes_dropped_text.SetOwner(&_header_window);
110     _drunes_dropped_text.SetPosition(300.0f, 30.0f);
111     _drunes_dropped_text.SetDimensions(TOP_WINDOW_WIDTH / 5.0f * 2.0f, 40.0f);
112     _drunes_dropped_text.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_CENTER);
113     _drunes_dropped_text.SetTextStyle(TextStyle("text20", Color::white));
114     _drunes_dropped_text.SetDisplayMode(VIDEO_TEXT_INSTANT);
115 
116     _total_drunes.SetOwner(&_spoils_window);
117     _total_drunes.SetPosition(SPOILS_WINDOW_WIDTH / 4.0f, 0.0f);
118     _total_drunes.SetDimensions(SPOILS_WINDOW_WIDTH / 4.0f * 3.0f, 40.0f);
119     _total_drunes.SetTextStyle(TextStyle("text20", Color::white));
120     _total_drunes.SetDisplayMode(VIDEO_TEXT_INSTANT);
121 
122     _object_header_text.SetOwner(&_spoils_window);
123     _object_header_text.SetPosition(SPOILS_WINDOW_WIDTH / 4.0f, 50.0f);
124     _object_header_text.SetDimensions(SPOILS_WINDOW_WIDTH / 4.0f * 3.0f, 40.0f);
125     _object_header_text.SetTextStyle(TextStyle("title20", Color::white));
126     _object_header_text.SetDisplayMode(VIDEO_TEXT_INSTANT);
127     _object_header_text.SetDisplayText(UTranslate("Items Found"));
128 
129     _object_list.SetOwner(&_spoils_window);
130     _object_list.SetPosition(50.0f, 100.0f);
131     _object_list.SetDimensions(250.0f, 180.0f, 1, 8, 1, 8);
132     _object_list.SetTextStyle(TextStyle("text20", Color::white));
133     _object_list.SetAlignment(VIDEO_X_LEFT, VIDEO_Y_TOP);
134     _object_list.SetOptionAlignment(VIDEO_X_LEFT, VIDEO_Y_CENTER);
135     _object_list.SetCursorState(VIDEO_CURSOR_STATE_HIDDEN);
136 
137     _victory_options.SetOwner(&_header_window);
138     _victory_options.SetPosition(TOP_WINDOW_WIDTH / 2, 30.0f);
139     _victory_options.SetDimensions(TOP_WINDOW_WIDTH, 50.0f, 2, 1, 2, 1);
140     _victory_options.SetTextStyle(TextStyle("text22", Color::white, VIDEO_TEXT_SHADOW_DARK));
141     _victory_options.SetAlignment(VIDEO_X_CENTER, VIDEO_Y_CENTER);
142     _victory_options.SetOptionAlignment(VIDEO_X_CENTER, VIDEO_Y_CENTER);
143     _victory_options.SetSelectMode(VIDEO_SELECT_SINGLE);
144     _victory_options.SetHorizontalWrapMode(VIDEO_WRAP_MODE_STRAIGHT);
145     _victory_options.SetCursorOffset(-60.0f, -25.0f);
146     _victory_options.AddOption(UTranslate("End Battle"));
147     _victory_options.AddOption(UTranslate("Improve Skills"));
148     _victory_options.SetSelection(0);
149 }
150 
~BattleVictory()151 BattleVictory::~BattleVictory()
152 {
153     _header_window.Destroy();
154     _spoils_window.Destroy();
155 
156     for (uint32_t i = 0; i < _number_character_windows_created; ++i) {
157         _character_window[i].Destroy();
158     }
159 
160     // Add all the objects that were dropped by enemies to the party's inventory.
161     for (auto i = _objects_dropped.begin(); i != _objects_dropped.end(); ++i) {
162 
163         // Add the item to the inventory.
164         assert(i->first != nullptr);
165         GlobalManager->GetInventoryHandler().AddToInventory(i->first->GetID(), i->second);
166     }
167     _objects_dropped.clear();
168 
169     // Update the HP and SP of all characters.
170     _SetCharacterStatus();
171 }
172 
Initialize()173 void BattleVictory::Initialize()
174 {
175     // Prepare all character data
176     std::deque<BattleCharacter *>& all_characters = BattleMode::CurrentInstance()->GetCharacterActors();
177     // Reinit the number of living characters
178     uint32_t alive_characters_number = 0;
179 
180     _characters_number = all_characters.size();
181 
182     for(uint32_t i = 0; i < _characters_number; ++i) {
183         _characters.push_back(all_characters[i]->GetGlobalCharacter());
184         _character_portraits[i] = all_characters[i]->GetPortrait();
185 
186         // Only size up non valid images
187         if(!_character_portraits[i].GetFilename().empty())
188             _character_portraits[i].SetDimensions(100.0f, 100.0f);
189 
190         if(all_characters[i]->IsAlive()) {
191             // Set up the victory animation for the living beings
192             all_characters[i]->ChangeSpriteAnimation("victory");
193             // Adds a living player to later split xp with.
194             ++alive_characters_number;
195         } else {
196             // Gray out portraits of deceased characters
197             _character_portraits[i].SetGrayscale(true);
198         }
199     }
200 
201     // Collect the XP, drunes, and dropped objects for each defeated enemy
202     std::deque<BattleEnemy*>& all_enemies = BattleMode::CurrentInstance()->GetEnemyActors();
203     for (uint32_t i = 0; i < all_enemies.size(); ++i) {
204         GlobalEnemy* enemy = all_enemies[i]->GetGlobalEnemy();
205         assert(enemy != nullptr);
206 
207         _xp_earned += enemy->GetExperiencePoints();
208         _drunes_dropped += enemy->GetDrunesDropped();
209 
210         std::vector<std::shared_ptr<GlobalObject>> objects = enemy->DetermineDroppedObjects();
211         for (uint32_t j = 0; j < objects.size(); ++j) {
212             // Check if the object to add is already in our list. If so, just increase the quantity of that object.
213             // iter = _objects_dropped.find(objects[j]); // Will not work since each item is created with new.
214             // Need to search for the item ID instead.
215             auto iter = _objects_dropped.begin();
216             while (iter != _objects_dropped.end()) {
217                 if (iter->first->GetID() == objects[j]->GetID()) {
218                     break;
219                 }
220                 ++iter;
221             }
222 
223             if (iter != _objects_dropped.end()) {
224                 ++iter->second;
225             } else {
226                 _objects_dropped.insert(std::make_pair(objects[j], 1));
227             }
228         }
229         objects.clear();
230     }
231 
232     // Divide up the XP earnings by the number of players (only living ones)
233     if (alive_characters_number > 0)
234         _xp_earned /= alive_characters_number;
235     else
236         _xp_earned /= 4; // Should never happen.
237 
238     // Compute the raw fighting XP bonus for each characters (20% of character's XP)
239     for(uint32_t i = 0; i < _characters.size() && i < 4; ++i) {
240         if (_characters[i]->HasEquipment()) {
241             _raw_xp_won[i] = false;
242             _raw_xp_given[i] = true;
243         }
244         else {
245             _raw_xp_won[i] = true;
246             _raw_xp_given[i] = false;
247         }
248     }
249 
250     _CreateCharacterGUIObjects();
251     _CreateObjectList();
252 
253     // Initialize dynamic texts
254     _header_xp.SetDisplayText(UTranslate("XP Earned: ") + MakeUnicodeString(NumberToString(_xp_earned)));
255     _drunes_dropped_text.SetDisplayText(UTranslate("Drunes Found: ") + MakeUnicodeString(NumberToString(_drunes_dropped)));
256     _total_drunes.SetDisplayText(UTranslate("Total Drunes: ") + MakeUnicodeString(NumberToString(GlobalManager->GetDrunes())));
257 
258     // Start victory
259     _state = VICTORY_START;
260 }
261 
Update()262 void BattleVictory::Update()
263 {
264     switch(_state) {
265     case VICTORY_START:
266         // Go to exit menu once count is done
267         if (_xp_earned == 0 && _drunes_dropped == 0) {
268             _state = VICTORY_END;
269         }
270 
271         _UpdateXP();
272         _UpdateSpoils();
273         break;
274 
275     case VICTORY_END:
276         _UpdateEndMenu();
277         break;
278 
279     default:
280         IF_PRINT_WARNING(BATTLE_DEBUG) << "invalid finish state: " << _state << std::endl;
281         break;
282     }
283 }
284 
Draw()285 void BattleVictory::Draw()
286 {
287     _outcome_text.Draw();
288 
289     // Header Windows
290     _header_window.Draw();
291 
292     if (_state == VICTORY_END) {
293         _victory_options.Draw();
294     }
295     else {
296         _header_xp.Draw();
297         _drunes_dropped_text.Draw();
298     }
299 
300     for(uint32_t i = 0; i < _characters_number; ++i) {
301         _character_window[i].Draw();
302         _DrawXP(i);
303     }
304 
305     // Items and drunes
306     _spoils_window.Draw();
307     _total_drunes.Draw();
308     _object_header_text.Draw();
309     _object_list.Draw();
310 }
311 
_CreateCharacterGUIObjects()312 void BattleVictory::_CreateCharacterGUIObjects()
313 {
314     // Create the character windows. The lowest one does not have its lower border removed
315     float next_ypos = CHAR_WINDOW_YPOS;
316     for(uint32_t i = 0; i < _characters_number; ++i) {
317         _number_character_windows_created++;
318         if((i + 1) >= _characters_number) {
319             _character_window[i].Create(CHAR_WINDOW_WIDTH, CHAR_WINDOW_HEIGHT);
320         } else {
321             _character_window[i].Create(CHAR_WINDOW_WIDTH, CHAR_WINDOW_HEIGHT, ~VIDEO_MENU_EDGE_BOTTOM, VIDEO_MENU_EDGE_BOTTOM);
322         }
323 
324         _character_window[i].SetPosition(CHAR_WINDOW_XPOS, next_ypos);
325         _character_window[i].SetAlignment(VIDEO_X_CENTER, VIDEO_Y_TOP);
326         _character_window[i].Show();
327         next_ypos += CHAR_WINDOW_HEIGHT;
328     }
329 
330     // Construct GUI objects that will fill each character window
331     for(uint32_t i = 0; i < _characters_number; ++i) {
332         _level_text[i].SetOwner(&_character_window[i]);
333         _level_text[i].SetPosition(130.0f, 20.0f);
334         _level_text[i].SetDimensions(300.0f, 25.0f);
335         _level_text[i].SetTextStyle(TextStyle("text20", Color::white));
336         _level_text[i].SetDisplayMode(VIDEO_TEXT_INSTANT);
337 
338         _xp_text[i].SetOwner(&_character_window[i]);
339         _xp_text[i].SetPosition(130.0f, 50.0f);
340         _xp_text[i].SetDimensions(300.0f, 25.0f);
341         _xp_text[i].SetTextStyle(TextStyle("text20", Color::white));
342         _xp_text[i].SetDisplayMode(VIDEO_TEXT_INSTANT);
343 
344         _unspent_xp[i].SetOwner(&_character_window[i]);
345         _unspent_xp[i].SetPosition(130.0f, 80.0f);
346         _unspent_xp[i].SetDimensions(300.0f, 25.0f);
347         _unspent_xp[i].SetTextStyle(TextStyle("text20", Color::white));
348         _unspent_xp[i].SetDisplayMode(VIDEO_TEXT_INSTANT);
349 
350         // Don't show XP when the maximum level has been reached.
351         if(_characters[i]->GetExperienceLevel() >= GlobalManager->GetMaxExperienceLevel()) {
352             _level_text[i].SetDisplayText(UTranslate("Level (Max): ")
353                                              + MakeUnicodeString(NumberToString(_characters[i]->GetExperienceLevel())));
354             _xp_text[i].SetDisplayText(" ");
355         } else {
356             _level_text[i].SetDisplayText(UTranslate("Level: ")
357                                              + MakeUnicodeString(NumberToString(_characters[i]->GetExperienceLevel())));
358             _xp_text[i].SetDisplayText(UTranslate("XP for level up: ")
359                                        + MakeUnicodeString(NumberToString(_characters[i]->GetExperienceForNextLevel())));
360             _unspent_xp[i].SetDisplayText(UTranslate("XP for skills: ")
361                                           + MakeUnicodeString(NumberToString(_characters[i]->GetUnspentExperiencePoints())));
362         }
363     }
364 }
365 
_CreateObjectList()366 void BattleVictory::_CreateObjectList()
367 {
368     // Add all dropped items in the list
369     for (auto i = _objects_dropped.begin(); i != _objects_dropped.end(); ++i) {
370         std::shared_ptr<GlobalObject> obj = i->first;
371         if (obj->GetIconImage().GetFilename().empty()) {
372             _object_list.AddOption(MakeUnicodeString("<30>") +
373                                    obj->GetName() + MakeUnicodeString("<R>x" + NumberToString(i->second)));
374         }
375         else {
376             _object_list.AddOption(MakeUnicodeString("<" + obj->GetIconImage().GetFilename() + "><30>") +
377                                    obj->GetName() + MakeUnicodeString("<R>x" + NumberToString(i->second)));
378         }
379     }
380 
381     // Resize all icon images so that they are the same height as the text
382     for(uint32_t i = 0; i < _object_list.GetNumberOptions(); ++i) {
383         StillImage* image = _object_list.GetEmbeddedImage(i);
384         if (image != nullptr)
385             _object_list.GetEmbeddedImage(i)->SetDimensions(30.0f, 30.0f);
386     }
387 
388     // Set item found title accordingly
389     const uint32_t no_of_options = _object_list.GetNumberOptions();
390     _object_header_text.SetDisplayText(no_of_options == 0 ?
391         // tr: Header in battle result screen: 0 items found
392             UTranslate("No Items Found") :
393                 no_of_options == 1 ?
394                     // tr: Header in battle result screen: 1 item found
395                     UTranslate("Item Found") :
396                     // tr: Header in battle result screen: more than 1 item found
397                     UTranslate("Items Found"));
398 }
399 
_SetCharacterStatus()400 void BattleVictory::_SetCharacterStatus()
401 {
402     std::deque<BattleCharacter *>& battle_characters = BattleMode::CurrentInstance()->GetCharacterActors();
403 
404     for(std::deque<BattleCharacter *>::iterator i = battle_characters.begin(); i != battle_characters.end(); ++i) {
405         GlobalCharacter *character = (*i)->GetGlobalCharacter();
406 
407         // Put back the current HP / SP onto the global characters.
408         character->SetHitPoints((*i)->GetHitPoints());
409         character->SetSkillPoints((*i)->GetSkillPoints());
410     }
411 }
412 
_UpdateXP()413 void BattleVictory::_UpdateXP()
414 {
415     // The number of milliseconds that we wait in between updating the XP count
416     const uint32_t UPDATE_PERIOD = 50;
417     // A simple counter used to keep track of when the next XP count should begin
418     static uint32_t time_counter = 0;
419 
420     // The amount of XP to add to each character this update cycle
421     uint32_t xp_to_add = 0;
422 
423     // Process confirm press inputs.
424     if(InputManager->ConfirmPress()) {
425         // Begin counting out XP earned
426         if(!_begin_counting_xp) {
427             _begin_counting_xp = true;
428         }
429         // If confirm received during counting, instantly add all remaining XP
430         else if(_xp_earned != 0) {
431             xp_to_add = _xp_earned;
432         }
433     }
434 
435     // If counting has not began or counting is alreasy finished, there is nothing more to do here
436     if(!_begin_counting_xp || (_xp_earned == 0))
437         return;
438 
439     // Update the timer and determine how much XP to add if the time has been reached
440     // We don't want to modify the XP to add if a confirm event occurred in step (1)
441     if(xp_to_add == 0) {
442         time_counter += SystemManager->GetUpdateTime();
443         if(time_counter >= UPDATE_PERIOD) {
444             time_counter -= UPDATE_PERIOD;
445 
446             // Determine an appropriate amount of XP to add here
447             if(_xp_earned > 10000)
448                 xp_to_add = 1000;
449             else if(_xp_earned > 1000)
450                 xp_to_add = 100;
451             else if(_xp_earned > 100)
452                 xp_to_add = 10;
453             else
454                 xp_to_add = 1;
455         }
456     }
457 
458     // If there is no XP to add this update cycle, there is nothing left for us to do
459     if(xp_to_add == 0)
460         return;
461 
462     // Add the XP amount to the characters appropriately
463     std::deque<BattleCharacter *>& battle_characters = BattleMode::CurrentInstance()->GetCharacterActors();
464     for(uint32_t i = 0; i < _characters_number; ++i) {
465         // Don't add experience points to dead characters
466         if(!battle_characters[i]->IsAlive())
467             continue;
468 
469         // Tells whether the character can receive XP
470         bool level_maxed_out = false;
471 
472         // Don't permit to earn XP when the maximum level has been reached.
473         if(battle_characters[i]->GetGlobalCharacter()->GetExperienceLevel() >= GlobalManager->GetMaxExperienceLevel())
474             level_maxed_out = true;
475 
476         uint32_t xp_added = xp_to_add;
477         // Add the raw bonus when not given yet (+20% XP)
478         if (_raw_xp_given[i] == false) {
479             if (_xp_earned > 100) {
480                 xp_added += (xp_to_add / 5);
481             }
482             else {
483                 // When giving one xp point at a time,
484                 // we give all the rest of the raw bonus and set it as done.
485                 xp_added += _xp_earned / 5;
486                 _raw_xp_given[i] = true;
487             }
488 
489         }
490 
491         if(!level_maxed_out && _characters[i]->AddExperiencePoints(xp_added)) {
492             AudioManager->PlaySound("data/sounds/levelup.wav");
493         }
494 
495         ustring level_text;
496         ustring xp_text;
497         ustring unspent_xp;
498         if(level_maxed_out) {
499             level_text = UTranslate("Level (Max): ") + MakeUnicodeString(NumberToString(_characters[i]->GetExperienceLevel()));
500         } else {
501             level_text = UTranslate("Level: ") + MakeUnicodeString(NumberToString(_characters[i]->GetExperienceLevel()));
502             xp_text = UTranslate("XP for level up: ") + MakeUnicodeString(NumberToString(_characters[i]->GetExperienceForNextLevel()));
503             if (_raw_xp_won[i])
504                 xp_text += UTranslate(" (+20%)");
505             unspent_xp = UTranslate("XP for skills: ")
506                          + MakeUnicodeString(NumberToString(_characters[i]->GetUnspentExperiencePoints()));
507         }
508 
509         _level_text[i].SetDisplayText(level_text);
510         _xp_text[i].SetDisplayText(xp_text);
511         _unspent_xp[i].SetDisplayText(unspent_xp);
512     }
513 
514     _xp_earned -= xp_to_add;
515     _header_xp.SetDisplayText(UTranslate("XP Earned: ") + MakeUnicodeString(NumberToString(_xp_earned)));
516 }
517 
_UpdateSpoils()518 void BattleVictory::_UpdateSpoils()
519 {
520     // The number of milliseconds that we wait in between updating the drunes count
521     const uint32_t UPDATE_PERIOD = 50;
522     // A simple counter used to keep track of when the next drunes count should begin
523     static uint32_t time_counter = 0;
524     // TODO: Add drunes gradually instead of all at once
525     static uint32_t drunes_to_add = 0;
526 
527     // Process confirm press inputs.
528     if(InputManager->ConfirmPress()) {
529         // Begin counting out drunes dropped
530         if(!_begin_counting_drunes)
531             _begin_counting_drunes = true;
532 
533         // If confirm received during counting, instantly add all remaining drunes
534         else if(_drunes_dropped != 0) {
535             drunes_to_add = _drunes_dropped;
536         }
537     }
538 
539     // If counting has not began or counting is alreasy finished, there is nothing more to do here
540     if(!_begin_counting_drunes || (_drunes_dropped == 0))
541         return;
542 
543     // Update the timer and determine how many drunes to add if the time has been reached
544     // We don't want to modify the drunes to add if a confirm event occurred in step (1)
545     if(drunes_to_add == 0) {
546         time_counter += SystemManager->GetUpdateTime();
547         if(time_counter >= UPDATE_PERIOD) {
548             time_counter -= UPDATE_PERIOD;
549 
550             // Determine an appropriate amount of drunes to add here
551             if(_drunes_dropped > 10000)
552                 drunes_to_add = 1000;
553             else if(_drunes_dropped > 1000)
554                 drunes_to_add = 100;
555             else if(_drunes_dropped > 100)
556                 drunes_to_add = 10;
557             else
558                 drunes_to_add = 1;
559         }
560     }
561 
562     // Add drunes and update the display
563     if(drunes_to_add != 0) {
564         // Avoid making _drunes_dropped a negative value
565         if(drunes_to_add > _drunes_dropped) {
566             drunes_to_add = _drunes_dropped;
567         }
568 
569         GlobalManager->AddDrunes(drunes_to_add);
570         _drunes_dropped -= drunes_to_add;
571         _drunes_dropped_text.SetDisplayText(UTranslate("Drunes Found: ") + MakeUnicodeString(NumberToString(_drunes_dropped)));
572         _total_drunes.SetDisplayText(UTranslate("Total Drunes: ") + MakeUnicodeString(NumberToString(GlobalManager->GetDrunes())));
573     }
574 }
575 
_UpdateEndMenu()576 void BattleVictory::_UpdateEndMenu()
577 {
578     _victory_options.Update();
579 
580     if (InputManager->LeftPress()) {
581         _victory_options.InputLeft();
582     }
583     else if (InputManager->RightPress()) {
584         _victory_options.InputRight();
585     }
586     else if (InputManager->ConfirmPress()) {
587         switch(_victory_options.GetSelection()) {
588         default:
589         case VICTORY_OPTION_END_BATTLE:
590             BattleMode::CurrentInstance()->ChangeState(BATTLE_STATE_EXITING);
591             break;
592         case VICTORY_OPTION_IMPROVE_SKILLS:
593             vt_menu::MenuMode* menu_mode = new vt_menu::MenuMode();
594             menu_mode->GoToImproveSkillMenu();
595             _victory_options.SetSelection(VICTORY_OPTION_END_BATTLE);
596             vt_mode_manager::ModeManager->Push(menu_mode);
597             break;
598         }
599     }
600 }
601 
_DrawXP(uint32_t index)602 void BattleVictory::_DrawXP(uint32_t index)
603 {
604     VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_TOP, 0);
605     VideoManager->Move(CHAR_WINDOW_XPOS - (CHAR_WINDOW_WIDTH / 2) + 20.0f,
606                        (CHAR_WINDOW_YPOS + 17.0f) + (CHAR_WINDOW_HEIGHT * index));
607     _character_portraits[index].Draw();
608 
609     _level_text[index].Draw();
610     _xp_text[index].Draw();
611     _unspent_xp[index].Draw();
612 }
613 
614 } // namespace private_battle
615 
616 } // namespace vt_battle
617