TurnTimerWidget.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * TurnTimerWidget.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 "TurnTimerWidget.h"
  12. #include "../CPlayerInterface.h"
  13. #include "../battle/BattleInterface.h"
  14. #include "../battle/BattleStacksController.h"
  15. #include "../GameEngine.h"
  16. #include "../GameInstance.h"
  17. #include "../media/ISoundPlayer.h"
  18. #include "../render/Graphics.h"
  19. #include "../render/IFont.h"
  20. #include "../render/IRenderHandler.h"
  21. #include "../widgets/Images.h"
  22. #include "../widgets/GraphicalPrimitiveCanvas.h"
  23. #include "../widgets/TextControls.h"
  24. #include "../../lib/CPlayerState.h"
  25. #include "../../lib/CStack.h"
  26. #include "../../lib/StartInfo.h"
  27. #include "../../lib/battle/CPlayerBattleCallback.h"
  28. #include "../../lib/callback/CCallback.h"
  29. VerticalPercentBar::VerticalPercentBar(const Point & position, const Point & size, ColorRGBA barColor, ColorRGBA barColorBackground, ColorRGBA borderColor)
  30. : percent(1.0f)
  31. , barColor(barColor)
  32. , barColorBackground(barColorBackground)
  33. , borderColor(borderColor)
  34. {
  35. OBJECT_CONSTRUCTION;
  36. pos += position;
  37. pos.w = size.x;
  38. pos.h = size.y;
  39. back = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), barColorBackground, borderColor, 1);
  40. setFillColor(barColor);
  41. }
  42. void VerticalPercentBar::setPercent(int newPercent)
  43. {
  44. percent = std::clamp(newPercent, 0, 100);
  45. fill->pos.h = ((pos.h - 2) * percent) / 100;
  46. fill->moveTo(Point(pos.x + 1, pos.y + pos.h - fill->pos.h - 1));
  47. redraw();
  48. }
  49. void VerticalPercentBar::setFillColor(ColorRGBA fillColor)
  50. {
  51. OBJECT_CONSTRUCTION;
  52. fill = std::make_shared<TransparentFilledRectangle>(Rect(1, 1, pos.w - 2, pos.h - 2), fillColor, borderColor, 0);
  53. redraw();
  54. }
  55. TurnTimerWidget::TurnTimerWidget(const Point & position)
  56. : TurnTimerWidget(position, PlayerColor::NEUTRAL)
  57. {}
  58. TurnTimerWidget::TurnTimerWidget(const Point & position, PlayerColor player)
  59. : CIntObject(TIME)
  60. , lastSoundCheckSeconds(0)
  61. , isBattleMode(player.isValidPlayer())
  62. {
  63. OBJECT_CONSTRUCTION;
  64. pos += position;
  65. pos.w = 0;
  66. pos.h = 0;
  67. recActions &= ~DEACTIVATE;
  68. const auto & timers = GAME->interface()->cb->getStartInfo()->turnTimerInfo;
  69. backgroundTexture = std::make_shared<CFilledTexture>(ImagePath::builtin("DiBoxBck"), pos); // 1 px smaller on all sides
  70. backgroundBorder = std::make_shared<TransparentFilledRectangle>(pos, ColorRGBA(0, 0, 0, 128), Colors::BRIGHT_YELLOW);
  71. int bigFontHeight = ENGINE->renderHandler().loadFont(FONT_BIG)->getLineHeight();
  72. pos.h += 6;
  73. if (isBattleMode)
  74. {
  75. pos.w = 77;
  76. pos.h += bigFontHeight - 4;
  77. playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 2, FONT_BIG, ETextAlignment::BOTTOMCENTER, graphics->playerColors[player.getNum()], "");
  78. if (timers.battleTimer != 0)
  79. {
  80. pos.h += bigFontHeight;
  81. playerLabelsBattle[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 2, FONT_BIG, ETextAlignment::BOTTOMCENTER, graphics->playerColors[player.getNum()], "");
  82. }
  83. if (!timers.accumulatingUnitTimer && timers.unitTimer != 0)
  84. {
  85. pos.h += bigFontHeight;
  86. playerLabelsUnit[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 2, FONT_BIG, ETextAlignment::BOTTOMCENTER, graphics->playerColors[player.getNum()], "");
  87. }
  88. updateTextLabel(player, GAME->interface()->cb->getPlayerTurnTime(player));
  89. }
  90. else
  91. {
  92. if (!timers.accumulatingTurnTimer && timers.baseTimer != 0)
  93. pos.w = 142;
  94. else
  95. pos.w = 82;
  96. for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
  97. {
  98. if (GAME->interface()->cb->getStartInfo()->playerInfos.count(player) == 0)
  99. continue;
  100. if (!GAME->interface()->cb->getStartInfo()->playerInfos.at(player).isControlledByHuman())
  101. continue;
  102. pos.h += bigFontHeight - 4;
  103. playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 2, FONT_BIG, ETextAlignment::BOTTOMCENTER, graphics->playerColors[player.getNum()], "");
  104. constexpr int barWidth = 6;
  105. playerBarsMovement[player] = std::make_shared<VerticalPercentBar>(Point(0, pos.h - bigFontHeight + 2), Point(barWidth, bigFontHeight - 6), Colors::GREEN, ColorRGBA(50, 50, 50), Colors::BRIGHT_YELLOW);
  106. playerBarsBattle[player] = std::make_shared<VerticalPercentBar>(Point(pos.w - barWidth, pos.h - bigFontHeight + 2), Point(barWidth, bigFontHeight - 6), Colors::RED, ColorRGBA(50, 50, 50), Colors::BRIGHT_YELLOW);
  107. updateTextLabel(player, GAME->interface()->cb->getPlayerTurnTime(player));
  108. }
  109. }
  110. backgroundTexture->pos = Rect::createAround(pos, -1);
  111. backgroundBorder->pos = pos;
  112. }
  113. void TurnTimerWidget::show(Canvas & to)
  114. {
  115. showAll(to);
  116. }
  117. void TurnTimerWidget::updateNotifications(PlayerColor player, int timeMs)
  118. {
  119. if(player != GAME->interface()->playerID)
  120. return;
  121. int newTimeSeconds = timeMs / 1000;
  122. if (newTimeSeconds != lastSoundCheckSeconds && notificationThresholds.count(newTimeSeconds))
  123. ENGINE->sound().playSound(AudioPath::builtin("WE5"));
  124. lastSoundCheckSeconds = newTimeSeconds;
  125. }
  126. static std::string msToString(int timeMs)
  127. {
  128. int timeSeconds = timeMs / 1000;
  129. std::ostringstream oss;
  130. oss << timeSeconds / 60 << ":" << std::setw(2) << std::setfill('0') << timeSeconds % 60;
  131. return oss.str();
  132. }
  133. void TurnTimerWidget::updateTextLabel(PlayerColor player, const TurnTimerInfo & timer)
  134. {
  135. const auto & timerSettings = GAME->interface()->cb->getStartInfo()->turnTimerInfo;
  136. auto mainLabel = playerLabelsMain[player];
  137. if (isBattleMode)
  138. {
  139. mainLabel->setText(msToString(timer.baseTimer + timer.turnTimer));
  140. if (timerSettings.battleTimer != 0)
  141. {
  142. auto battleLabel = playerLabelsBattle[player];
  143. if (timer.battleTimer != 0)
  144. {
  145. if (timerSettings.accumulatingUnitTimer)
  146. battleLabel->setText("+" + msToString(timer.battleTimer + timer.unitTimer));
  147. else
  148. battleLabel->setText("+" + msToString(timer.battleTimer));
  149. }
  150. else
  151. battleLabel->setText("");
  152. }
  153. if (!timerSettings.accumulatingUnitTimer && timerSettings.unitTimer != 0)
  154. {
  155. auto unitLabel = playerLabelsUnit[player];
  156. if (timer.unitTimer != 0)
  157. unitLabel->setText("+" + msToString(timer.unitTimer));
  158. else
  159. unitLabel->setText("");
  160. }
  161. }
  162. else
  163. {
  164. if (!timerSettings.accumulatingTurnTimer && timerSettings.baseTimer != 0)
  165. mainLabel->setText(msToString(timer.baseTimer) + "+" + msToString(timer.turnTimer));
  166. else
  167. mainLabel->setText(msToString(timer.baseTimer + timer.turnTimer));
  168. playerBarsMovement[player]->setPercent(timer.remainingMovementPointsPercent);
  169. ColorRGBA barColor = Colors::TRANSPARENCY;
  170. if (timer.isBattle)
  171. barColor = Colors::RED;
  172. else if (timer.isTurnStart)
  173. barColor = Colors::WHITE;
  174. else if (timer.isTurnEnded)
  175. barColor = Colors::GREEN;
  176. else if (!timer.isActive)
  177. barColor = Colors::YELLOW;
  178. playerBarsBattle[player]->setFillColor(barColor);
  179. playerBarsBattle[player]->setPercent(barColor != Colors::TRANSPARENCY ? 1.0f : 0.0f);
  180. }
  181. }
  182. void TurnTimerWidget::updateTimer(PlayerColor player, uint32_t msPassed)
  183. {
  184. const auto & gamestateTimer = GAME->interface()->cb->getPlayerTurnTime(player);
  185. updateNotifications(player, gamestateTimer.valueMs());
  186. updateTextLabel(player, gamestateTimer);
  187. }
  188. void TurnTimerWidget::tick(uint32_t msPassed)
  189. {
  190. for(const auto & player : playerLabelsMain)
  191. {
  192. if (GAME->interface()->battleInt)
  193. {
  194. const auto & battle = GAME->interface()->battleInt->getBattle();
  195. bool isDefender = battle->sideToPlayer(BattleSide::DEFENDER) == player.first;
  196. bool isAttacker = battle->sideToPlayer(BattleSide::ATTACKER) == player.first;
  197. bool isMakingUnitTurn = battle->battleActiveUnit() && battle->battleActiveUnit()->unitOwner() == player.first;
  198. bool isEngagedInBattle = isDefender || isAttacker;
  199. // Due to way our network message queue works during battle animation
  200. // client actually does not receives updates from server as to which timer is active when game has battle animations playing
  201. // so during battle skip updating timer unless game is waiting for player to select action
  202. if (isEngagedInBattle && !isMakingUnitTurn)
  203. continue;
  204. }
  205. updateTimer(player.first, msPassed);
  206. }
  207. }