BattleResultWindow.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * BattleResultWindow.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "BattleResultWindow.h"
  12. #include "BattleWindow.h"
  13. #include "../GameInstance.h"
  14. #include "../Client.h"
  15. #include "../CServerHandler.h"
  16. #include "../CPlayerInterface.h"
  17. #include "../GameEngine.h"
  18. #include "../gui/Shortcut.h"
  19. #include "../gui/WindowHandler.h"
  20. #include "../media/IMusicPlayer.h"
  21. #include "../widgets/Buttons.h"
  22. #include "../widgets/Images.h"
  23. #include "../widgets/TextControls.h"
  24. #include "../widgets/VideoWidget.h"
  25. #include "../../lib/CStack.h"
  26. #include "../../lib/CPlayerState.h"
  27. #include "../../lib/ConditionalWait.h"
  28. #include "../../lib/GameLibrary.h"
  29. #include "../../lib/StartInfo.h"
  30. #include "../../lib/battle/CPlayerBattleCallback.h"
  31. #include "../../lib/callback/CCallback.h"
  32. #include "../../lib/gameState/InfoAboutArmy.h"
  33. #include "../../lib/mapObjects/CGHeroInstance.h"
  34. #include "../../lib/networkPacks/PacksForClientBattle.h"
  35. #include "../../lib/texts/CGeneralTextHandler.h"
  36. BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface & _owner, bool allowReplay)
  37. : owner(_owner)
  38. {
  39. OBJECT_CONSTRUCTION;
  40. background = std::make_shared<CPicture>(ImagePath::builtin("CPRESULT"));
  41. background->setPlayerColor(owner.playerID);
  42. pos = center(background->pos);
  43. exit = std::make_shared<CButton>(Point(384, 505), AnimationPath::builtin("iok6432.def"), std::make_pair("", ""), [this](){ bExitf();}, EShortcut::GLOBAL_ACCEPT);
  44. exit->setBorderColor(Colors::METALLIC_GOLD);
  45. auto battle = owner.cb->getBattle(br.battleID);
  46. const auto * attackerPlayer = GAME->server().client->gameInfo().getPlayerState(battle->sideToPlayer(BattleSide::ATTACKER));
  47. const auto * defenderPlayer = GAME->server().client->gameInfo().getPlayerState(battle->sideToPlayer(BattleSide::DEFENDER));
  48. bool isAttackerHuman = attackerPlayer && attackerPlayer->isHuman();
  49. bool isDefenderHuman = defenderPlayer && defenderPlayer->isHuman();
  50. bool onlyOnePlayerHuman = isAttackerHuman != isDefenderHuman;
  51. if((allowReplay || owner.cb->getStartInfo()->extraOptionsInfo.unlimitedReplay) && onlyOnePlayerHuman)
  52. {
  53. repeat = std::make_shared<CButton>(Point(24, 505), AnimationPath::builtin("icn6432.def"), std::make_pair("", ""), [this](){ bRepeatf();}, EShortcut::GLOBAL_CANCEL);
  54. repeat->setBorderColor(Colors::METALLIC_GOLD);
  55. labels.push_back(std::make_shared<CLabel>(232, 520, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->translate("vcmi.battleResultsWindow.applyResultsLabel")));
  56. }
  57. if(br.winner == BattleSide::ATTACKER)
  58. {
  59. labels.push_back(std::make_shared<CLabel>(59, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[410]));
  60. }
  61. else
  62. {
  63. labels.push_back(std::make_shared<CLabel>(59, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[411]));
  64. }
  65. if(br.winner == BattleSide::DEFENDER)
  66. {
  67. labels.push_back(std::make_shared<CLabel>(412, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[410]));
  68. }
  69. else
  70. {
  71. labels.push_back(std::make_shared<CLabel>(408, 124, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[411]));
  72. }
  73. labels.push_back(std::make_shared<CLabel>(232, 302, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, LIBRARY->generaltexth->allTexts[407]));
  74. labels.push_back(std::make_shared<CLabel>(232, 332, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[408]));
  75. labels.push_back(std::make_shared<CLabel>(232, 428, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[409]));
  76. std::array<std::string, 2> sideNames = {"N/A", "N/A"};
  77. for(auto i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  78. {
  79. auto heroInfo = owner.cb->getBattle(br.battleID)->battleGetHeroInfo(i);
  80. const std::array xs = {21, 392};
  81. if(heroInfo.portraitSource.isValid()) //attacking hero
  82. {
  83. icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsLarge"), heroInfo.getIconIndex(), 0, xs[static_cast<int>(i)], 38));
  84. sideNames[static_cast<int>(i)] = heroInfo.name;
  85. }
  86. else
  87. {
  88. auto stacks = owner.cb->getBattle(br.battleID)->battleGetAllStacks();
  89. vstd::erase_if(stacks, [i](const CStack * stack) //erase stack of other side and not coming from garrison
  90. {
  91. return stack->unitSide() != i || !stack->base;
  92. });
  93. auto best = vstd::maxElementByFun(stacks, [](const CStack * stack)
  94. {
  95. return stack->unitType()->getAIValue();
  96. });
  97. if(best != stacks.end()) //should be always but to be safe...
  98. {
  99. icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), (*best)->unitType()->getIconIndex(), 0, xs[static_cast<int>(i)], 38));
  100. sideNames[static_cast<int>(i)] = (*best)->unitType()->getNamePluralTranslated();
  101. }
  102. }
  103. }
  104. //printing attacker and defender's names
  105. labels.push_back(std::make_shared<CLabel>(89, 37, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, sideNames[0]));
  106. labels.push_back(std::make_shared<CLabel>(381, 53, FONT_SMALL, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, sideNames[1]));
  107. //printing casualties
  108. for(auto step : {BattleSide::ATTACKER, BattleSide::DEFENDER})
  109. {
  110. if(br.casualties[step].empty())
  111. {
  112. labels.push_back(std::make_shared<CLabel>(235, 360 + 97 * static_cast<int>(step), FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[523]));
  113. }
  114. else
  115. {
  116. int casualties = br.casualties[step].size();
  117. int xPos = 235 - (casualties*32 + (casualties - 1)*10)/2; //increment by 42 with each picture
  118. int yPos = 344 + static_cast<int>(step) * 97;
  119. for(auto & elem : br.casualties[step])
  120. {
  121. const auto * creature = elem.first.toEntity(LIBRARY);
  122. if (creature->getId() == CreatureID::ARROW_TOWERS )
  123. continue; // do not show destroyed towers in battle results
  124. icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("CPRSMALL"), creature->getIconIndex(), 0, xPos, yPos));
  125. std::ostringstream amount;
  126. amount<<elem.second;
  127. labels.push_back(std::make_shared<CLabel>(xPos + 16, yPos + 42, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, amount.str()));
  128. xPos += 42;
  129. }
  130. }
  131. }
  132. auto resources = getResources(br);
  133. description = std::make_shared<CTextBox>(resources.resultText.toString(), Rect(69, 203, 330, 68), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
  134. videoPlayer = std::make_shared<VideoWidget>(Point(107, 70), resources.prologueVideo, resources.loopedVideo, false);
  135. ENGINE->music().playMusic(resources.musicName, false, true);
  136. }
  137. BattleResultResources BattleResultWindow::getResources(const BattleResult & br)
  138. {
  139. //printing result description
  140. bool weAreAttacker = owner.cb->getBattle(br.battleID)->battleGetMySide() == BattleSide::ATTACKER;
  141. bool weAreDefender = !weAreAttacker;
  142. bool weWon = (br.winner == BattleSide::ATTACKER && weAreAttacker) || (br.winner == BattleSide::DEFENDER && !weAreAttacker);
  143. bool isSiege = owner.cb->getBattle(br.battleID)->battleGetDefendedTown() != nullptr;
  144. BattleResultResources resources;
  145. if(weWon)
  146. {
  147. if(isSiege && weAreDefender)
  148. {
  149. resources.musicName = AudioPath::builtin("Music/Defend Castle");
  150. resources.prologueVideo = VideoPath::builtin("DEFENDALL.BIK");
  151. resources.loopedVideo = VideoPath::builtin("defendloop.bik");
  152. }
  153. else
  154. {
  155. resources.musicName = AudioPath::builtin("Music/Win Battle");
  156. resources.prologueVideo = VideoPath::builtin("WIN3.BIK");
  157. resources.loopedVideo = VideoPath::builtin("WIN3.BIK");
  158. }
  159. switch(br.result)
  160. {
  161. case EBattleResult::NORMAL:
  162. resources.resultText.appendTextID("core.genrltxt.304");
  163. break;
  164. case EBattleResult::ESCAPE:
  165. resources.resultText.appendTextID("core.genrltxt.303");
  166. break;
  167. case EBattleResult::SURRENDER:
  168. resources.resultText.appendTextID("core.genrltxt.302");
  169. break;
  170. default:
  171. throw std::runtime_error("Invalid battle result!");
  172. }
  173. const CGHeroInstance * ourHero = owner.cb->getBattle(br.battleID)->battleGetMyHero();
  174. if(ourHero)
  175. {
  176. resources.resultText.appendTextID("core.genrltxt.305");
  177. resources.resultText.replaceTextID(ourHero->getNameTextID());
  178. resources.resultText.replaceNumber(br.exp[weAreAttacker ? BattleSide::ATTACKER : BattleSide::DEFENDER]);
  179. }
  180. }
  181. else // we lose
  182. {
  183. switch(br.result)
  184. {
  185. case EBattleResult::NORMAL:
  186. resources.resultText.appendTextID("core.genrltxt.311");
  187. resources.musicName = AudioPath::builtin("Music/LoseCombat");
  188. resources.prologueVideo = VideoPath::builtin("LBSTART.BIK");
  189. resources.loopedVideo = VideoPath::builtin("LBLOOP.BIK");
  190. break;
  191. case EBattleResult::ESCAPE:
  192. resources.resultText.appendTextID("core.genrltxt.310");
  193. resources.musicName = AudioPath::builtin("Music/Retreat Battle");
  194. resources.prologueVideo = VideoPath::builtin("RTSTART.BIK");
  195. resources.loopedVideo = VideoPath::builtin("RTLOOP.BIK");
  196. break;
  197. case EBattleResult::SURRENDER:
  198. resources.resultText.appendTextID("core.genrltxt.309");
  199. resources.musicName = AudioPath::builtin("Music/Surrender Battle");
  200. resources.prologueVideo = VideoPath::builtin("SURRENDER.BIK");
  201. resources.loopedVideo = VideoPath::builtin("SURRENDER.BIK");
  202. break;
  203. default:
  204. throw std::runtime_error("Invalid battle result!");
  205. }
  206. if(isSiege && weAreDefender)
  207. {
  208. resources.musicName = AudioPath::builtin("Music/LoseCastle");
  209. resources.prologueVideo = VideoPath::builtin("LOSECSTL.BIK");
  210. resources.loopedVideo = VideoPath::builtin("LOSECSLP.BIK");
  211. }
  212. }
  213. return resources;
  214. }
  215. void BattleResultWindow::activate()
  216. {
  217. owner.showingDialog->setBusy();
  218. CIntObject::activate();
  219. }
  220. void BattleResultWindow::buttonPressed(int button)
  221. {
  222. if(resultCallback)
  223. resultCallback(button);
  224. CPlayerInterface & intTmp = owner; //copy reference because "this" will be destructed soon
  225. close();
  226. if(ENGINE->windows().topWindow<BattleWindow>())
  227. ENGINE->windows().popWindows(1); //pop battle interface if present
  228. //Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
  229. //so we can be sure that there is no dialogs left on GUI stack.
  230. intTmp.showingDialog->setFree();
  231. }
  232. void BattleResultWindow::bExitf()
  233. {
  234. buttonPressed(0);
  235. }
  236. void BattleResultWindow::bRepeatf()
  237. {
  238. buttonPressed(1);
  239. }