CBattleControlPanel.cpp 11 KB

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