CBattleControlPanel.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * CBattleControlPanel.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 "CBattleControlPanel.h"
  12. #include "CBattleInterface.h"
  13. #include "CBattleInterfaceClasses.h"
  14. #include "CBattleStacksController.h"
  15. #include "../widgets/Buttons.h"
  16. #include "../CGameInfo.h"
  17. #include "../CBitmapHandler.h"
  18. #include "../../lib/CGeneralTextHandler.h"
  19. #include "../../lib/mapObjects/CGHeroInstance.h"
  20. #include "../CPlayerInterface.h"
  21. #include "../../CCallback.h"
  22. #include "../gui/CCursorHandler.h"
  23. #include "../gui/CGuiHandler.h"
  24. #include "../windows/CSpellWindow.h"
  25. #include "../../lib/CStack.h"
  26. #include "../../lib/CConfigHandler.h"
  27. CBattleControlPanel::CBattleControlPanel(CBattleInterface * owner):
  28. owner(owner)
  29. {
  30. OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
  31. //preparing buttons and console
  32. bOptions = std::make_shared<CButton> (Point( 3, 5), "icm003.def", CGI->generaltexth->zelp[381], std::bind(&CBattleControlPanel::bOptionsf,this), SDLK_o);
  33. bSurrender = std::make_shared<CButton> (Point( 54, 5), "icm001.def", CGI->generaltexth->zelp[379], std::bind(&CBattleControlPanel::bSurrenderf,this), SDLK_s);
  34. bFlee = std::make_shared<CButton> (Point(105, 5), "icm002.def", CGI->generaltexth->zelp[380], std::bind(&CBattleControlPanel::bFleef,this), SDLK_r);
  35. bAutofight = std::make_shared<CButton> (Point(157, 5), "icm004.def", CGI->generaltexth->zelp[382], std::bind(&CBattleControlPanel::bAutofightf,this), SDLK_a);
  36. bSpell = std::make_shared<CButton> (Point(645, 5), "icm005.def", CGI->generaltexth->zelp[385], std::bind(&CBattleControlPanel::bSpellf,this), SDLK_c);
  37. bWait = std::make_shared<CButton> (Point(696, 5), "icm006.def", CGI->generaltexth->zelp[386], std::bind(&CBattleControlPanel::bWaitf,this), SDLK_w);
  38. bDefence = std::make_shared<CButton> (Point(747, 5), "icm007.def", CGI->generaltexth->zelp[387], std::bind(&CBattleControlPanel::bDefencef,this), SDLK_d);
  39. bConsoleUp = std::make_shared<CButton> (Point(624, 5), "ComSlide.def", std::make_pair("", ""), std::bind(&CBattleControlPanel::bConsoleUpf,this), SDLK_UP);
  40. bConsoleDown = std::make_shared<CButton>(Point(624, 24), "ComSlide.def", std::make_pair("", ""), std::bind(&CBattleControlPanel::bConsoleDownf,this), SDLK_DOWN);
  41. bDefence->assignedKeys.insert(SDLK_SPACE);
  42. bConsoleUp->setImageOrder(0, 1, 0, 0);
  43. bConsoleDown->setImageOrder(2, 3, 2, 2);
  44. console = std::make_shared<CBattleConsole>(Rect(211, 4, 406,38));
  45. GH.statusbar = console;
  46. if ( owner->tacticsMode )
  47. tacticPhaseStarted();
  48. else
  49. tacticPhaseEnded();
  50. }
  51. void CBattleControlPanel::tacticPhaseStarted()
  52. {
  53. OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
  54. btactNext = std::make_shared<CButton>(Point(213, 4), "icm011.def", std::make_pair("", ""), [&]() { bTacticNextStack();}, SDLK_SPACE);
  55. btactEnd = std::make_shared<CButton>(Point(419, 4), "icm012.def", std::make_pair("", ""), [&](){ bTacticPhaseEnd();}, SDLK_RETURN);
  56. menu = std::make_shared<CPicture>("COPLACBR.BMP", 0, 0);
  57. menu->colorize(owner->curInt->playerID);
  58. }
  59. void CBattleControlPanel::tacticPhaseEnded()
  60. {
  61. OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
  62. menu = std::make_shared<CPicture>("CBAR.BMP", 0, 0);
  63. menu->colorize(owner->curInt->playerID);
  64. }
  65. void CBattleControlPanel::bOptionsf()
  66. {
  67. if (owner->spellDestSelectMode) //we are casting a spell
  68. return;
  69. CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
  70. Rect tempRect = genRect(431, 481, 160, 84);
  71. tempRect += pos.topLeft();
  72. GH.pushIntT<CBattleOptionsWindow>(tempRect, owner);
  73. }
  74. void CBattleControlPanel::bSurrenderf()
  75. {
  76. if(owner->spellDestSelectMode) //we are casting a spell
  77. return;
  78. int cost = owner->curInt->cb->battleGetSurrenderCost();
  79. if(cost >= 0)
  80. {
  81. std::string enemyHeroName = owner->curInt->cb->battleGetEnemyHero().name;
  82. if(enemyHeroName.empty())
  83. {
  84. logGlobal->warn("Surrender performed without enemy hero, should not happen!");
  85. enemyHeroName = "#ENEMY#";
  86. }
  87. std::string surrenderMessage = boost::str(boost::format(CGI->generaltexth->allTexts[32]) % enemyHeroName % cost); //%s states: "I will accept your surrender and grant you and your troops safe passage for the price of %d gold."
  88. owner->curInt->showYesNoDialog(surrenderMessage, [this](){ reallySurrender(); }, nullptr);
  89. }
  90. }
  91. void CBattleControlPanel::bFleef()
  92. {
  93. if (owner->spellDestSelectMode) //we are casting a spell
  94. return;
  95. if ( owner->curInt->cb->battleCanFlee() )
  96. {
  97. CFunctionList<void()> ony = std::bind(&CBattleControlPanel::reallyFlee,this);
  98. owner->curInt->showYesNoDialog(CGI->generaltexth->allTexts[28], ony, nullptr); //Are you sure you want to retreat?
  99. }
  100. else
  101. {
  102. std::vector<std::shared_ptr<CComponent>> comps;
  103. std::string heroName;
  104. //calculating fleeing hero's name
  105. if (owner->attackingHeroInstance)
  106. if (owner->attackingHeroInstance->tempOwner == owner->curInt->cb->getMyColor())
  107. heroName = owner->attackingHeroInstance->name;
  108. if (owner->defendingHeroInstance)
  109. if (owner->defendingHeroInstance->tempOwner == owner->curInt->cb->getMyColor())
  110. heroName = owner->defendingHeroInstance->name;
  111. //calculating text
  112. auto txt = boost::format(CGI->generaltexth->allTexts[340]) % heroName; //The Shackles of War are present. %s can not retreat!
  113. //printing message
  114. owner->curInt->showInfoDialog(boost::to_string(txt), comps);
  115. }
  116. }
  117. void CBattleControlPanel::reallyFlee()
  118. {
  119. owner->giveCommand(EActionType::RETREAT);
  120. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  121. }
  122. void CBattleControlPanel::reallySurrender()
  123. {
  124. if (owner->curInt->cb->getResourceAmount(Res::GOLD) < owner->curInt->cb->battleGetSurrenderCost())
  125. {
  126. owner->curInt->showInfoDialog(CGI->generaltexth->allTexts[29]); //You don't have enough gold!
  127. }
  128. else
  129. {
  130. owner->giveCommand(EActionType::SURRENDER);
  131. CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
  132. }
  133. }
  134. void CBattleControlPanel::bAutofightf()
  135. {
  136. if(owner->spellDestSelectMode) //we are casting a spell
  137. return;
  138. //Stop auto-fight mode
  139. if(owner->curInt->isAutoFightOn)
  140. {
  141. assert(owner->curInt->autofightingAI);
  142. owner->curInt->isAutoFightOn = false;
  143. logGlobal->trace("Stopping the autofight...");
  144. }
  145. else if(!owner->curInt->autofightingAI)
  146. {
  147. owner->curInt->isAutoFightOn = true;
  148. blockUI(true);
  149. auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
  150. ai->init(owner->curInt->env, owner->curInt->cb);
  151. ai->battleStart(owner->army1, owner->army2, int3(0,0,0), owner->attackingHeroInstance, owner->defendingHeroInstance, owner->curInt->cb->battleGetMySide());
  152. owner->curInt->autofightingAI = ai;
  153. owner->curInt->cb->registerBattleInterface(ai);
  154. owner->requestAutofightingAIToTakeAction();
  155. }
  156. }
  157. void CBattleControlPanel::bSpellf()
  158. {
  159. if (owner->spellDestSelectMode) //we are casting a spell
  160. return;
  161. if (!owner->myTurn)
  162. return;
  163. auto myHero = owner->currentHero();
  164. if(!myHero)
  165. return;
  166. CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
  167. ESpellCastProblem::ESpellCastProblem spellCastProblem = owner->curInt->cb->battleCanCastSpell(myHero, spells::Mode::HERO);
  168. if(spellCastProblem == ESpellCastProblem::OK)
  169. {
  170. GH.pushIntT<CSpellWindow>(myHero, owner->curInt.get());
  171. }
  172. else if (spellCastProblem == ESpellCastProblem::MAGIC_IS_BLOCKED)
  173. {
  174. //TODO: move to spell mechanics, add more information to spell cast problem
  175. //Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
  176. auto blockingBonus = owner->currentHero()->getBonusLocalFirst(Selector::type()(Bonus::BLOCK_ALL_MAGIC));
  177. if (!blockingBonus)
  178. return;
  179. if (blockingBonus->source == Bonus::ARTIFACT)
  180. {
  181. const int32_t artID = blockingBonus->sid;
  182. //If we have artifact, put name of our hero. Otherwise assume it's the enemy.
  183. //TODO check who *really* is source of bonus
  184. std::string heroName = myHero->hasArt(artID) ? myHero->name : owner->enemyHero().name;
  185. //%s wields the %s, an ancient artifact which creates a p dead to all magic.
  186. LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
  187. % heroName % CGI->artifacts()->getByIndex(artID)->getName()));
  188. }
  189. }
  190. }
  191. void CBattleControlPanel::bWaitf()
  192. {
  193. if (owner->spellDestSelectMode) //we are casting a spell
  194. return;
  195. if (owner->stacksController->getActiveStack() != nullptr)
  196. owner->giveCommand(EActionType::WAIT);
  197. }
  198. void CBattleControlPanel::bDefencef()
  199. {
  200. if (owner->spellDestSelectMode) //we are casting a spell
  201. return;
  202. if (owner->stacksController->getActiveStack() != nullptr)
  203. owner->giveCommand(EActionType::DEFEND);
  204. }
  205. void CBattleControlPanel::bConsoleUpf()
  206. {
  207. if (owner->spellDestSelectMode) //we are casting a spell
  208. return;
  209. console->scrollUp();
  210. }
  211. void CBattleControlPanel::bConsoleDownf()
  212. {
  213. if (owner->spellDestSelectMode) //we are casting a spell
  214. return;
  215. console->scrollDown();
  216. }
  217. void CBattleControlPanel::bTacticNextStack()
  218. {
  219. owner->tacticNextStack(nullptr);
  220. }
  221. void CBattleControlPanel::bTacticPhaseEnd()
  222. {
  223. owner->tacticPhaseEnd();
  224. }
  225. void CBattleControlPanel::blockUI(bool on)
  226. {
  227. bool canCastSpells = false;
  228. auto hero = owner->curInt->cb->battleGetMyHero();
  229. if(hero)
  230. {
  231. ESpellCastProblem::ESpellCastProblem spellcastingProblem = owner->curInt->cb->battleCanCastSpell(hero, spells::Mode::HERO);
  232. //if magic is blocked, we leave button active, so the message can be displayed after button click
  233. canCastSpells = spellcastingProblem == ESpellCastProblem::OK || spellcastingProblem == ESpellCastProblem::MAGIC_IS_BLOCKED;
  234. }
  235. bool canWait = owner->stacksController->getActiveStack() ? !owner->stacksController->getActiveStack()->waitedThisTurn : false;
  236. bOptions->block(on);
  237. bFlee->block(on || !owner->curInt->cb->battleCanFlee());
  238. bSurrender->block(on || owner->curInt->cb->battleGetSurrenderCost() < 0);
  239. // block only if during enemy turn and auto-fight is off
  240. // otherwise - crash on accessing non-exisiting active stack
  241. bAutofight->block(!owner->curInt->isAutoFightOn && !owner->stacksController->getActiveStack());
  242. if (owner->tacticsMode && btactEnd && btactNext)
  243. {
  244. btactNext->block(on);
  245. btactEnd->block(on);
  246. }
  247. else
  248. {
  249. bConsoleUp->block(on);
  250. bConsoleDown->block(on);
  251. }
  252. bSpell->block(on || owner->tacticsMode || !canCastSpells);
  253. bWait->block(on || owner->tacticsMode || !canWait);
  254. bDefence->block(on || owner->tacticsMode);
  255. }