1 #include "messagelogwidget.h"
2
3 #include "carditem.h"
4 #include "cardzone.h"
5 #include "pb/context_move_card.pb.h"
6 #include "pb/context_mulligan.pb.h"
7 #include "pb/serverinfo_user.pb.h"
8 #include "phase.h"
9 #include "player.h"
10 #include "soundengine.h"
11 #include "translatecountername.h"
12
13 #include <utility>
14
tableConstant() const15 const QString &MessageLogWidget::tableConstant() const
16 {
17 static const QString constant("table");
18 return constant;
19 }
20
graveyardConstant() const21 const QString &MessageLogWidget::graveyardConstant() const
22 {
23 static const QString constant("grave");
24 return constant;
25 }
26
exileConstant() const27 const QString &MessageLogWidget::exileConstant() const
28 {
29 static const QString constant("rfg");
30 return constant;
31 }
32
handConstant() const33 const QString &MessageLogWidget::handConstant() const
34 {
35 static const QString constant("hand");
36 return constant;
37 }
38
deckConstant() const39 const QString &MessageLogWidget::deckConstant() const
40 {
41 static const QString constant("deck");
42 return constant;
43 }
44
sideboardConstant() const45 const QString &MessageLogWidget::sideboardConstant() const
46 {
47 static const QString constant("sb");
48 return constant;
49 }
50
stackConstant() const51 const QString &MessageLogWidget::stackConstant() const
52 {
53 static const QString constant("stack");
54 return constant;
55 }
56
sanitizeHtml(QString dirty) const57 QString MessageLogWidget::sanitizeHtml(QString dirty) const
58 {
59 return dirty.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
60 }
61
cardLink(const QString cardName) const62 QString MessageLogWidget::cardLink(const QString cardName) const
63 {
64 return QString("<i><a href=\"card://%1\">%2</a></i>").arg(cardName).arg(cardName);
65 }
66
67 QPair<QString, QString>
getFromStr(CardZone * zone,QString cardName,int position,bool ownerChange) const68 MessageLogWidget::getFromStr(CardZone *zone, QString cardName, int position, bool ownerChange) const
69 {
70 bool cardNameContainsStartZone = false;
71 QString fromStr;
72 QString zoneName = zone->getName();
73
74 if (zoneName == tableConstant()) {
75 fromStr = tr(" from play");
76 } else if (zoneName == graveyardConstant()) {
77 fromStr = tr(" from their graveyard");
78 } else if (zoneName == exileConstant()) {
79 fromStr = tr(" from exile");
80 } else if (zoneName == handConstant()) {
81 fromStr = tr(" from their hand");
82 } else if (zoneName == deckConstant()) {
83 if (position == 0) {
84 if (cardName.isEmpty()) {
85 if (ownerChange) {
86 cardName = tr("the top card of %1's library").arg(zone->getPlayer()->getName());
87 } else {
88 cardName = tr("the top card of their library");
89 }
90 cardNameContainsStartZone = true;
91 } else {
92 if (ownerChange) {
93 fromStr = tr(" from the top of %1's library").arg(zone->getPlayer()->getName());
94 } else {
95 fromStr = tr(" from the top of their library");
96 }
97 }
98 } else if (position >= zone->getCards().size() - 1) {
99 if (cardName.isEmpty()) {
100 if (ownerChange) {
101 cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getName());
102 } else {
103 cardName = tr("the bottom card of their library");
104 }
105 cardNameContainsStartZone = true;
106 } else {
107 if (ownerChange) {
108 fromStr = tr(" from the bottom of %1's library").arg(zone->getPlayer()->getName());
109 } else {
110 fromStr = tr(" from the bottom of their library");
111 }
112 }
113 } else {
114 if (ownerChange) {
115 fromStr = tr(" from %1's library").arg(zone->getPlayer()->getName());
116 } else {
117 fromStr = tr(" from their library");
118 }
119 }
120 } else if (zoneName == sideboardConstant()) {
121 fromStr = tr(" from sideboard");
122 } else if (zoneName == stackConstant()) {
123 fromStr = tr(" from the stack");
124 }
125
126 if (!cardNameContainsStartZone) {
127 cardName.clear();
128 }
129 return QPair<QString, QString>(cardName, fromStr);
130 }
131
containerProcessingDone()132 void MessageLogWidget::containerProcessingDone()
133 {
134 currentContext = MessageContext_None;
135 messageSuffix = messagePrefix = QString();
136 }
137
containerProcessingStarted(const GameEventContext & context)138 void MessageLogWidget::containerProcessingStarted(const GameEventContext &context)
139 {
140 if (context.HasExtension(Context_MoveCard::ext)) {
141 currentContext = MessageContext_MoveCard;
142 } else if (context.HasExtension(Context_Mulligan::ext)) {
143 currentContext = MessageContext_Mulligan;
144 }
145 }
146
logAlwaysRevealTopCard(Player * player,CardZone * zone,bool reveal)147 void MessageLogWidget::logAlwaysRevealTopCard(Player *player, CardZone *zone, bool reveal)
148 {
149 appendHtmlServerMessage((reveal ? tr("%1 is now keeping the top card %2 revealed.")
150 : tr("%1 is not revealing the top card %2 any longer."))
151 .arg(sanitizeHtml(player->getName()))
152 .arg(zone->getTranslatedName(true, CaseTopCardsOfZone)));
153 }
154
logAttachCard(Player * player,QString cardName,Player * targetPlayer,QString targetCardName)155 void MessageLogWidget::logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName)
156 {
157 appendHtmlServerMessage(tr("%1 attaches %2 to %3's %4.")
158 .arg(sanitizeHtml(player->getName()))
159 .arg(cardLink(std::move(cardName)))
160 .arg(sanitizeHtml(targetPlayer->getName()))
161 .arg(cardLink(std::move(targetCardName))));
162 }
163
logConcede(Player * player)164 void MessageLogWidget::logConcede(Player *player)
165 {
166 soundEngine->playSound("player_concede");
167 appendHtmlServerMessage(tr("%1 has conceded the game.").arg(sanitizeHtml(player->getName())), true);
168 }
169
logUnconcede(Player * player)170 void MessageLogWidget::logUnconcede(Player *player)
171 {
172 soundEngine->playSound("player_concede");
173 appendHtmlServerMessage(tr("%1 has unconceded the game.").arg(sanitizeHtml(player->getName())), true);
174 }
175
logConnectionStateChanged(Player * player,bool connectionState)176 void MessageLogWidget::logConnectionStateChanged(Player *player, bool connectionState)
177 {
178 if (connectionState) {
179 soundEngine->playSound("player_reconnect");
180 appendHtmlServerMessage(tr("%1 has restored connection to the game.").arg(sanitizeHtml(player->getName())),
181 true);
182 } else {
183 soundEngine->playSound("player_disconnect");
184 appendHtmlServerMessage(tr("%1 has lost connection to the game.").arg(sanitizeHtml(player->getName())), true);
185 }
186 }
187
logCreateArrow(Player * player,Player * startPlayer,QString startCard,Player * targetPlayer,QString targetCard,bool playerTarget)188 void MessageLogWidget::logCreateArrow(Player *player,
189 Player *startPlayer,
190 QString startCard,
191 Player *targetPlayer,
192 QString targetCard,
193 bool playerTarget)
194 {
195 startCard = cardLink(startCard);
196 targetCard = cardLink(targetCard);
197 QString str;
198 if (playerTarget) {
199 if (player == startPlayer && player == targetPlayer) {
200 str = tr("%1 points from their %2 to themselves.");
201 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())).arg(startCard));
202 } else if (player == startPlayer) {
203 str = tr("%1 points from their %2 to %3.");
204 appendHtmlServerMessage(
205 str.arg(sanitizeHtml(player->getName())).arg(startCard).arg(sanitizeHtml(targetPlayer->getName())));
206 } else if (player == targetPlayer) {
207 str = tr("%1 points from %2's %3 to themselves.");
208 appendHtmlServerMessage(
209 str.arg(sanitizeHtml(player->getName())).arg(sanitizeHtml(startPlayer->getName())).arg(startCard));
210 } else {
211 str = tr("%1 points from %2's %3 to %4.");
212 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName()))
213 .arg(sanitizeHtml(startPlayer->getName()))
214 .arg(startCard)
215 .arg(sanitizeHtml(targetPlayer->getName())));
216 }
217 } else {
218 if (player == startPlayer && player == targetPlayer) {
219 str = tr("%1 points from their %2 to their %3.");
220 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())).arg(startCard).arg(targetCard));
221 } else if (player == startPlayer) {
222 str = tr("%1 points from their %2 to %3's %4.");
223 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName()))
224 .arg(startCard)
225 .arg(sanitizeHtml(targetPlayer->getName()))
226 .arg(targetCard));
227 } else if (player == targetPlayer) {
228 str = tr("%1 points from %2's %3 to their own %4.");
229 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName()))
230 .arg(sanitizeHtml(startPlayer->getName()))
231 .arg(startCard)
232 .arg(targetCard));
233 } else {
234 str = tr("%1 points from %2's %3 to %4's %5.");
235 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName()))
236 .arg(sanitizeHtml(startPlayer->getName()))
237 .arg(startCard)
238 .arg(sanitizeHtml(targetPlayer->getName()))
239 .arg(targetCard));
240 }
241 }
242 }
243
logCreateToken(Player * player,QString cardName,QString pt)244 void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt)
245 {
246 appendHtmlServerMessage(tr("%1 creates token: %2%3.")
247 .arg(sanitizeHtml(player->getName()))
248 .arg(cardLink(std::move(cardName)))
249 .arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
250 }
251
logDeckSelect(Player * player,QString deckHash,int sideboardSize)252 void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize)
253 {
254 if (sideboardSize < 0) {
255 appendHtmlServerMessage(tr("%1 has loaded a deck (%2).").arg(sanitizeHtml(player->getName())).arg(deckHash));
256 } else {
257 appendHtmlServerMessage(tr("%1 has loaded a deck with %2 sideboard cards (%3).")
258 .arg(sanitizeHtml(player->getName()))
259 .arg("<font class=\"blue\">" + QString::number(sideboardSize) + "</font>")
260 .arg(deckHash));
261 }
262 }
263
logDestroyCard(Player * player,QString cardName)264 void MessageLogWidget::logDestroyCard(Player *player, QString cardName)
265 {
266 appendHtmlServerMessage(
267 tr("%1 destroys %2.").arg(sanitizeHtml(player->getName())).arg(cardLink(std::move(cardName))));
268 }
269
logMoveCard(Player * player,CardItem * card,CardZone * startZone,int oldX,CardZone * targetZone,int newX)270 void MessageLogWidget::logMoveCard(Player *player,
271 CardItem *card,
272 CardZone *startZone,
273 int oldX,
274 CardZone *targetZone,
275 int newX)
276 {
277 if (currentContext == MessageContext_Mulligan) {
278 return;
279 }
280
281 QString startZoneName = startZone->getName();
282 QString targetZoneName = targetZone->getName();
283 bool ownerChanged = startZone->getPlayer() != targetZone->getPlayer();
284
285 // do not log if moved within the same zone
286 if ((startZoneName == tableConstant() && targetZoneName == tableConstant() && !ownerChanged) ||
287 (startZoneName == handConstant() && targetZoneName == handConstant()) ||
288 (startZoneName == exileConstant() && targetZoneName == exileConstant())) {
289 return;
290 }
291
292 QString cardName = card->getName();
293 QPair<QString, QString> nameFrom = getFromStr(startZone, cardName, oldX, ownerChanged);
294 if (!nameFrom.first.isEmpty()) {
295 cardName = nameFrom.first;
296 }
297
298 QString cardStr;
299 if (!nameFrom.first.isEmpty()) {
300 cardStr = cardName;
301 } else if (cardName.isEmpty()) {
302 cardStr = tr("a card");
303 } else {
304 cardStr = cardLink(cardName);
305 }
306
307 if (ownerChanged && (startZone->getPlayer() == player)) {
308 appendHtmlServerMessage(tr("%1 gives %2 control over %3.")
309 .arg(sanitizeHtml(player->getName()))
310 .arg(sanitizeHtml(targetZone->getPlayer()->getName()))
311 .arg(cardStr));
312 return;
313 }
314
315 QString finalStr;
316 bool usesNewX = false;
317 if (targetZoneName == tableConstant()) {
318 soundEngine->playSound("play_card");
319 if (card->getFaceDown()) {
320 finalStr = tr("%1 puts %2 into play%3 face down.");
321 } else {
322 finalStr = tr("%1 puts %2 into play%3.");
323 }
324 } else if (targetZoneName == graveyardConstant()) {
325 finalStr = tr("%1 puts %2%3 into their graveyard.");
326 } else if (targetZoneName == exileConstant()) {
327 finalStr = tr("%1 exiles %2%3.");
328 } else if (targetZoneName == handConstant()) {
329 finalStr = tr("%1 moves %2%3 to their hand.");
330 } else if (targetZoneName == deckConstant()) {
331 if (newX == -1) {
332 finalStr = tr("%1 puts %2%3 into their library.");
333 } else if (newX >= targetZone->getCards().size()) {
334 finalStr = tr("%1 puts %2%3 onto the bottom of their library.");
335 } else if (newX == 0) {
336 finalStr = tr("%1 puts %2%3 on top of their library.");
337 } else {
338 ++newX;
339 usesNewX = true;
340 finalStr = tr("%1 puts %2%3 into their library %4 cards from the top.");
341 }
342 } else if (targetZoneName == sideboardConstant()) {
343 finalStr = tr("%1 moves %2%3 to sideboard.");
344 } else if (targetZoneName == stackConstant()) {
345 soundEngine->playSound("play_card");
346 finalStr = tr("%1 plays %2%3.");
347 }
348
349 if (usesNewX) {
350 appendHtmlServerMessage(
351 finalStr.arg(sanitizeHtml(player->getName())).arg(cardStr).arg(nameFrom.second).arg(newX));
352 } else {
353 appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getName())).arg(cardStr).arg(nameFrom.second));
354 }
355 }
356
logDrawCards(Player * player,int number)357 void MessageLogWidget::logDrawCards(Player *player, int number)
358 {
359 soundEngine->playSound("draw_card");
360 if (currentContext == MessageContext_Mulligan) {
361 logMulligan(player, number);
362 } else {
363 appendHtmlServerMessage(tr("%1 draws %2 card(s).", "", number)
364 .arg(sanitizeHtml(player->getName()))
365 .arg("<font class=\"blue\">" + QString::number(number) + "</font>"));
366 }
367 }
368
logDumpZone(Player * player,CardZone * zone,int numberCards)369 void MessageLogWidget::logDumpZone(Player *player, CardZone *zone, int numberCards)
370 {
371 if (numberCards == -1) {
372 appendHtmlServerMessage(tr("%1 is looking at %2.")
373 .arg(sanitizeHtml(player->getName()))
374 .arg(zone->getTranslatedName(zone->getPlayer() == player, CaseLookAtZone)));
375 } else {
376 appendHtmlServerMessage(
377 tr("%1 is looking at the top %3 card(s) %2.", "top card for singular, top %3 cards for plural", numberCards)
378 .arg(sanitizeHtml(player->getName()))
379 .arg(zone->getTranslatedName(zone->getPlayer() == player, CaseTopCardsOfZone))
380 .arg("<font class=\"blue\">" + QString::number(numberCards) + "</font>"));
381 }
382 }
383
logFlipCard(Player * player,QString cardName,bool faceDown)384 void MessageLogWidget::logFlipCard(Player *player, QString cardName, bool faceDown)
385 {
386 if (faceDown) {
387 appendHtmlServerMessage(
388 tr("%1 turns %2 face-down.").arg(sanitizeHtml(player->getName())).arg(cardLink(cardName)));
389 } else {
390 appendHtmlServerMessage(
391 tr("%1 turns %2 face-up.").arg(sanitizeHtml(player->getName())).arg(cardLink(cardName)));
392 }
393 }
394
logGameClosed()395 void MessageLogWidget::logGameClosed()
396 {
397 appendHtmlServerMessage(tr("The game has been closed."));
398 }
399
logGameStart()400 void MessageLogWidget::logGameStart()
401 {
402 appendHtmlServerMessage(tr("The game has started."));
403 }
404
logJoin(Player * player)405 void MessageLogWidget::logJoin(Player *player)
406 {
407 soundEngine->playSound("player_join");
408 appendHtmlServerMessage(tr("%1 has joined the game.").arg(sanitizeHtml(player->getName())));
409 }
410
logJoinSpectator(QString name)411 void MessageLogWidget::logJoinSpectator(QString name)
412 {
413 soundEngine->playSound("spectator_join");
414 appendHtmlServerMessage(tr("%1 is now watching the game.").arg(sanitizeHtml(std::move(name))));
415 }
416
logKicked()417 void MessageLogWidget::logKicked()
418 {
419 appendHtmlServerMessage(tr("You have been kicked out of the game."), true);
420 }
421
logLeave(Player * player,QString reason)422 void MessageLogWidget::logLeave(Player *player, QString reason)
423 {
424 soundEngine->playSound("player_leave");
425 appendHtmlServerMessage(
426 tr("%1 has left the game (%2).").arg(sanitizeHtml(player->getName()), sanitizeHtml(std::move(reason))), true);
427 }
428
logLeaveSpectator(QString name,QString reason)429 void MessageLogWidget::logLeaveSpectator(QString name, QString reason)
430 {
431 soundEngine->playSound("spectator_leave");
432 appendHtmlServerMessage(tr("%1 is not watching the game any more (%2).")
433 .arg(sanitizeHtml(std::move(name)), sanitizeHtml(std::move(reason))));
434 }
435
logNotReadyStart(Player * player)436 void MessageLogWidget::logNotReadyStart(Player *player)
437 {
438 appendHtmlServerMessage(tr("%1 is not ready to start the game any more.").arg(sanitizeHtml(player->getName())));
439 }
440
logMulligan(Player * player,int number)441 void MessageLogWidget::logMulligan(Player *player, int number)
442 {
443 if (!player) {
444 return;
445 }
446 if (number > 0) {
447 appendHtmlServerMessage(tr("%1 shuffles their deck and draws a new hand of %2 card(s).", "", number)
448 .arg(sanitizeHtml(player->getName()))
449 .arg(number));
450 } else {
451 appendHtmlServerMessage(
452 tr("%1 shuffles their deck and draws a new hand.").arg(sanitizeHtml(player->getName())));
453 }
454 }
455
logReplayStarted(int gameId)456 void MessageLogWidget::logReplayStarted(int gameId)
457 {
458 appendHtmlServerMessage(tr("You are watching a replay of game #%1.").arg(gameId));
459 }
460
logReadyStart(Player * player)461 void MessageLogWidget::logReadyStart(Player *player)
462 {
463 appendHtmlServerMessage(tr("%1 is ready to start the game.").arg(sanitizeHtml(player->getName())));
464 }
465
logRevealCards(Player * player,CardZone * zone,int cardId,QString cardName,Player * otherPlayer,bool faceDown,int amount)466 void MessageLogWidget::logRevealCards(Player *player,
467 CardZone *zone,
468 int cardId,
469 QString cardName,
470 Player *otherPlayer,
471 bool faceDown,
472 int amount)
473 {
474 // getFromStr uses cardname.empty() to check if it should contain the start zone, it's not actually used
475 QPair<QString, QString> temp = getFromStr(zone, amount == 1 ? cardName : QString::number(amount), cardId, false);
476 bool cardNameContainsStartZone = false;
477 if (!temp.first.isEmpty()) {
478 cardNameContainsStartZone = true;
479 cardName = temp.first;
480 }
481 QString fromStr = temp.second;
482
483 QString cardStr;
484 if (cardNameContainsStartZone) {
485 cardStr = cardName;
486 } else if (cardName.isEmpty()) {
487 if (amount == 0) {
488 cardStr = tr("cards", "an unknown amount of cards");
489 } else {
490 cardStr = tr("%1 card(s)", "a card for singular, %1 cards for plural", amount)
491 .arg("<font class=\"blue\">" + QString::number(amount) + "</font>");
492 }
493 } else {
494 cardStr = cardLink(cardName);
495 }
496 if (cardId == -1) {
497 if (otherPlayer) {
498 appendHtmlServerMessage(tr("%1 reveals %2 to %3.")
499 .arg(sanitizeHtml(player->getName()))
500 .arg(zone->getTranslatedName(true, CaseRevealZone))
501 .arg(sanitizeHtml(otherPlayer->getName())));
502 } else {
503 appendHtmlServerMessage(tr("%1 reveals %2.")
504 .arg(sanitizeHtml(player->getName()))
505 .arg(zone->getTranslatedName(true, CaseRevealZone)));
506 }
507 } else if (cardId == -2) {
508 if (otherPlayer) {
509 appendHtmlServerMessage(tr("%1 randomly reveals %2%3 to %4.")
510 .arg(sanitizeHtml(player->getName()))
511 .arg(cardStr)
512 .arg(fromStr)
513 .arg(sanitizeHtml(otherPlayer->getName())));
514 } else {
515 appendHtmlServerMessage(
516 tr("%1 randomly reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr));
517 }
518 } else {
519 if (faceDown && player == otherPlayer) {
520 if (cardName.isEmpty()) {
521 appendHtmlServerMessage(
522 tr("%1 peeks at face down card #%2.").arg(sanitizeHtml(player->getName())).arg(cardId));
523 } else {
524 appendHtmlServerMessage(tr("%1 peeks at face down card #%2: %3.")
525 .arg(sanitizeHtml(player->getName()))
526 .arg(cardId)
527 .arg(cardStr));
528 }
529 } else if (otherPlayer) {
530 appendHtmlServerMessage(tr("%1 reveals %2%3 to %4.")
531 .arg(sanitizeHtml(player->getName()))
532 .arg(cardStr)
533 .arg(fromStr)
534 .arg(sanitizeHtml(otherPlayer->getName())));
535 } else {
536 appendHtmlServerMessage(
537 tr("%1 reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr));
538 }
539 }
540 }
541
logReverseTurn(Player * player,bool reversed)542 void MessageLogWidget::logReverseTurn(Player *player, bool reversed)
543 {
544 appendHtmlServerMessage(tr("%1 reversed turn order, now it's %2.")
545 .arg(sanitizeHtml(player->getName()))
546 .arg(reversed ? tr("reversed") : tr("normal")));
547 }
548
logRollDie(Player * player,int sides,int roll)549 void MessageLogWidget::logRollDie(Player *player, int sides, int roll)
550 {
551 if (sides == 2) {
552 QString coinOptions[2] = {tr("Heads") + " (1)", tr("Tails") + " (2)"};
553 appendHtmlServerMessage(tr("%1 flipped a coin. It landed as %2.")
554 .arg(sanitizeHtml(player->getName()))
555 .arg("<font class=\"blue\">" + coinOptions[roll - 1] + "</font>"));
556 } else {
557 appendHtmlServerMessage(tr("%1 rolls a %2 with a %3-sided die.")
558 .arg(sanitizeHtml(player->getName()))
559 .arg("<font class=\"blue\">" + QString::number(roll) + "</font>")
560 .arg("<font class=\"blue\">" + QString::number(sides) + "</font>"));
561 }
562 soundEngine->playSound("roll_dice");
563 }
564
logSay(Player * player,QString message)565 void MessageLogWidget::logSay(Player *player, QString message)
566 {
567 appendMessage(std::move(message), {}, player->getName(), UserLevelFlags(player->getUserInfo()->user_level()),
568 QString::fromStdString(player->getUserInfo()->privlevel()), true);
569 }
570
logSetActivePhase(int phaseNumber)571 void MessageLogWidget::logSetActivePhase(int phaseNumber)
572 {
573 Phase phase = Phases::getPhase(phaseNumber);
574
575 soundEngine->playSound(phase.soundFileName);
576
577 appendHtml("<font color=\"" + phase.color + "\"><b>" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") +
578 phase.name + "</b></font>");
579 }
580
logSetActivePlayer(Player * player)581 void MessageLogWidget::logSetActivePlayer(Player *player)
582 {
583 appendHtml("<br><font color=\"green\"><b>" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") +
584 QString(tr("%1's turn.")).arg(player->getName()) + "</b></font><br>");
585 }
586
logSetAnnotation(Player * player,CardItem * card,QString newAnnotation)587 void MessageLogWidget::logSetAnnotation(Player *player, CardItem *card, QString newAnnotation)
588 {
589 appendHtmlServerMessage(
590 QString(tr("%1 sets annotation of %2 to %3."))
591 .arg(sanitizeHtml(player->getName()))
592 .arg(cardLink(card->getName()))
593 .arg(QString(""<font class=\"blue\">%1</font>"").arg(sanitizeHtml(std::move(newAnnotation)))));
594 }
595
logSetCardCounter(Player * player,QString cardName,int counterId,int value,int oldValue)596 void MessageLogWidget::logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue)
597 {
598 QString finalStr;
599 int delta = abs(oldValue - value);
600 if (value > oldValue) {
601 finalStr = tr("%1 places %2 %3 on %4 (now %5).");
602 } else {
603 finalStr = tr("%1 removes %2 %3 from %4 (now %5).");
604 }
605
606 QString colorStr;
607 switch (counterId) {
608 case 0:
609 colorStr = tr("red counter(s)", "", delta);
610 break;
611 case 1:
612 colorStr = tr("yellow counter(s)", "", delta);
613 break;
614 case 2:
615 colorStr = tr("green counter(s)", "", delta);
616 break;
617 default:;
618 }
619
620 appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getName()))
621 .arg("<font class=\"blue\">" + QString::number(delta) + "</font>")
622 .arg(colorStr)
623 .arg(cardLink(std::move(cardName)))
624 .arg(value));
625 }
626
logSetCounter(Player * player,QString counterName,int value,int oldValue)627 void MessageLogWidget::logSetCounter(Player *player, QString counterName, int value, int oldValue)
628 {
629 if (counterName == "life") {
630 soundEngine->playSound("life_change");
631 }
632
633 QString counterDisplayName = TranslateCounterName::getDisplayName(counterName);
634 appendHtmlServerMessage(tr("%1 sets counter %2 to %3 (%4%5).")
635 .arg(sanitizeHtml(player->getName()))
636 .arg(QString("<font class=\"blue\">%1</font>").arg(sanitizeHtml(counterDisplayName)))
637 .arg(QString("<font class=\"blue\">%1</font>").arg(value))
638 .arg(value > oldValue ? "+" : "")
639 .arg(value - oldValue));
640 }
641
logSetDoesntUntap(Player * player,CardItem * card,bool doesntUntap)642 void MessageLogWidget::logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap)
643 {
644 QString str;
645 if (doesntUntap) {
646 str = tr("%1 sets %2 to not untap normally.");
647 } else {
648 str = tr("%1 sets %2 to untap normally.");
649 }
650 appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())).arg(cardLink(card->getName())));
651 }
652
logSetPT(Player * player,CardItem * card,QString newPT)653 void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT)
654 {
655 if (currentContext == MessageContext_MoveCard) {
656 return;
657 }
658
659 QString name = card->getName();
660 if (name.isEmpty()) {
661 name = QString("<font class=\"blue\">card #%1</font>").arg(sanitizeHtml(QString::number(card->getId())));
662 } else {
663 name = cardLink(name);
664 }
665 QString playerName = sanitizeHtml(player->getName());
666 if (newPT.isEmpty()) {
667 appendHtmlServerMessage(tr("%1 removes the PT of %2.").arg(playerName).arg(name));
668 } else {
669 QString oldPT = card->getPT();
670 if (oldPT.isEmpty()) {
671 appendHtmlServerMessage(
672 tr("%1 changes the PT of %2 from nothing to %4.").arg(playerName).arg(name).arg(newPT));
673 } else {
674 appendHtmlServerMessage(
675 tr("%1 changes the PT of %2 from %3 to %4.").arg(playerName).arg(name).arg(oldPT).arg(newPT));
676 }
677 }
678 }
679
logSetSideboardLock(Player * player,bool locked)680 void MessageLogWidget::logSetSideboardLock(Player *player, bool locked)
681 {
682 if (locked) {
683 appendHtmlServerMessage(tr("%1 has locked their sideboard.").arg(sanitizeHtml(player->getName())));
684 } else {
685 appendHtmlServerMessage(tr("%1 has unlocked their sideboard.").arg(sanitizeHtml(player->getName())));
686 }
687 }
688
logSetTapped(Player * player,CardItem * card,bool tapped)689 void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped)
690 {
691 if (currentContext == MessageContext_MoveCard) {
692 return;
693 }
694
695 if (tapped) {
696 soundEngine->playSound("tap_card");
697 } else {
698 soundEngine->playSound("untap_card");
699 }
700
701 QString str;
702 if (!card) {
703 appendHtmlServerMessage((tapped ? tr("%1 taps their permanents.") : tr("%1 untaps their permanents."))
704 .arg(sanitizeHtml(player->getName())));
705 } else {
706 appendHtmlServerMessage((tapped ? tr("%1 taps %2.") : tr("%1 untaps %2."))
707 .arg(sanitizeHtml(player->getName()))
708 .arg(cardLink(card->getName())));
709 }
710 }
711
logShuffle(Player * player,CardZone * zone,int start,int end)712 void MessageLogWidget::logShuffle(Player *player, CardZone *zone, int start, int end)
713 {
714 if (currentContext == MessageContext_Mulligan) {
715 return;
716 }
717
718 soundEngine->playSound("shuffle");
719 // start and end are indexes into the portion of the deck that was shuffled
720 // with negitive numbers counging from the bottom up.
721 if (start == 0 && end == -1) {
722 appendHtmlServerMessage(tr("%1 shuffles %2.")
723 .arg(sanitizeHtml(player->getName()))
724 .arg(zone->getTranslatedName(true, CaseShuffleZone)));
725 } else if (start < 0 && end == -1) {
726 appendHtmlServerMessage(tr("%1 shuffles the bottom %3 cards of %2.")
727 .arg(sanitizeHtml(player->getName()))
728 .arg(zone->getTranslatedName(true, CaseShuffleZone))
729 .arg(-start));
730 } else if (start < 0 && end > 0) {
731 appendHtmlServerMessage(tr("%1 shuffles the top %3 cards of %2.")
732 .arg(sanitizeHtml(player->getName()))
733 .arg(zone->getTranslatedName(true, CaseShuffleZone))
734 .arg(end + 1));
735 } else {
736 appendHtmlServerMessage(tr("%1 shuffles cards %3 - %4 of %2.")
737 .arg(sanitizeHtml(player->getName()))
738 .arg(zone->getTranslatedName(true, CaseShuffleZone))
739 .arg(start)
740 .arg(end));
741 }
742 }
743
logSpectatorSay(QString spectatorName,UserLevelFlags spectatorUserLevel,QString userPrivLevel,QString message)744 void MessageLogWidget::logSpectatorSay(QString spectatorName,
745 UserLevelFlags spectatorUserLevel,
746 QString userPrivLevel,
747 QString message)
748 {
749 appendMessage(std::move(message), {}, spectatorName, spectatorUserLevel, userPrivLevel, false);
750 }
751
logStopDumpZone(Player * player,CardZone * zone)752 void MessageLogWidget::logStopDumpZone(Player *player, CardZone *zone)
753 {
754 appendHtmlServerMessage(tr("%1 stops looking at %2.")
755 .arg(sanitizeHtml(player->getName()))
756 .arg(zone->getTranslatedName(zone->getPlayer() == player, CaseLookAtZone)));
757 }
758
logUnattachCard(Player * player,QString cardName)759 void MessageLogWidget::logUnattachCard(Player *player, QString cardName)
760 {
761 appendHtmlServerMessage(
762 tr("%1 unattaches %2.").arg(sanitizeHtml(player->getName())).arg(cardLink(std::move(cardName))));
763 }
764
logUndoDraw(Player * player,QString cardName)765 void MessageLogWidget::logUndoDraw(Player *player, QString cardName)
766 {
767 if (cardName.isEmpty()) {
768 appendHtmlServerMessage(tr("%1 undoes their last draw.").arg(sanitizeHtml(player->getName())));
769 } else {
770 appendHtmlServerMessage(
771 tr("%1 undoes their last draw (%2).")
772 .arg(sanitizeHtml(player->getName()))
773 .arg(QString("<a href=\"card://%1\">%2</a>").arg(sanitizeHtml(cardName)).arg(sanitizeHtml(cardName))));
774 }
775 }
776
setContextJudgeName(QString name)777 void MessageLogWidget::setContextJudgeName(QString name)
778 {
779 messagePrefix = QString("<span style=\"color:black\">");
780 messageSuffix = QString("</span> [<img height=12 src=\"theme:icons/scales\"> %1]").arg(sanitizeHtml(name));
781 }
782
appendHtmlServerMessage(const QString & html,bool optionalIsBold,QString optionalFontColor)783 void MessageLogWidget::appendHtmlServerMessage(const QString &html, bool optionalIsBold, QString optionalFontColor)
784 {
785
786 ChatView::appendHtmlServerMessage(messagePrefix + html + messageSuffix, optionalIsBold, optionalFontColor);
787 }
788
connectToPlayer(Player * player)789 void MessageLogWidget::connectToPlayer(Player *player)
790 {
791
792 connect(player, SIGNAL(logSay(Player *, QString)), this, SLOT(logSay(Player *, QString)));
793 connect(player, &Player::logShuffle, this, &MessageLogWidget::logShuffle);
794 connect(player, SIGNAL(logRollDie(Player *, int, int)), this, SLOT(logRollDie(Player *, int, int)));
795 connect(player, SIGNAL(logCreateArrow(Player *, Player *, QString, Player *, QString, bool)), this,
796 SLOT(logCreateArrow(Player *, Player *, QString, Player *, QString, bool)));
797 connect(player, SIGNAL(logCreateToken(Player *, QString, QString)), this,
798 SLOT(logCreateToken(Player *, QString, QString)));
799 connect(player, SIGNAL(logSetCounter(Player *, QString, int, int)), this,
800 SLOT(logSetCounter(Player *, QString, int, int)));
801 connect(player, SIGNAL(logSetCardCounter(Player *, QString, int, int, int)), this,
802 SLOT(logSetCardCounter(Player *, QString, int, int, int)));
803 connect(player, SIGNAL(logSetTapped(Player *, CardItem *, bool)), this,
804 SLOT(logSetTapped(Player *, CardItem *, bool)));
805 connect(player, SIGNAL(logSetDoesntUntap(Player *, CardItem *, bool)), this,
806 SLOT(logSetDoesntUntap(Player *, CardItem *, bool)));
807 connect(player, SIGNAL(logSetPT(Player *, CardItem *, QString)), this,
808 SLOT(logSetPT(Player *, CardItem *, QString)));
809 connect(player, SIGNAL(logSetAnnotation(Player *, CardItem *, QString)), this,
810 SLOT(logSetAnnotation(Player *, CardItem *, QString)));
811 connect(player, SIGNAL(logMoveCard(Player *, CardItem *, CardZone *, int, CardZone *, int)), this,
812 SLOT(logMoveCard(Player *, CardItem *, CardZone *, int, CardZone *, int)));
813 connect(player, SIGNAL(logFlipCard(Player *, QString, bool)), this, SLOT(logFlipCard(Player *, QString, bool)));
814 connect(player, SIGNAL(logDestroyCard(Player *, QString)), this, SLOT(logDestroyCard(Player *, QString)));
815 connect(player, SIGNAL(logAttachCard(Player *, QString, Player *, QString)), this,
816 SLOT(logAttachCard(Player *, QString, Player *, QString)));
817 connect(player, SIGNAL(logUnattachCard(Player *, QString)), this, SLOT(logUnattachCard(Player *, QString)));
818 connect(player, SIGNAL(logDumpZone(Player *, CardZone *, int)), this, SLOT(logDumpZone(Player *, CardZone *, int)));
819 connect(player, SIGNAL(logStopDumpZone(Player *, CardZone *)), this, SLOT(logStopDumpZone(Player *, CardZone *)));
820 connect(player, SIGNAL(logDrawCards(Player *, int)), this, SLOT(logDrawCards(Player *, int)));
821 connect(player, SIGNAL(logUndoDraw(Player *, QString)), this, SLOT(logUndoDraw(Player *, QString)));
822 connect(player, SIGNAL(logRevealCards(Player *, CardZone *, int, QString, Player *, bool, int)), this,
823 SLOT(logRevealCards(Player *, CardZone *, int, QString, Player *, bool, int)));
824 connect(player, SIGNAL(logAlwaysRevealTopCard(Player *, CardZone *, bool)), this,
825 SLOT(logAlwaysRevealTopCard(Player *, CardZone *, bool)));
826 }
827
MessageLogWidget(const TabSupervisor * _tabSupervisor,const UserlistProxy * _userlistProxy,TabGame * _game,QWidget * parent)828 MessageLogWidget::MessageLogWidget(const TabSupervisor *_tabSupervisor,
829 const UserlistProxy *_userlistProxy,
830 TabGame *_game,
831 QWidget *parent)
832 : ChatView(_tabSupervisor, _userlistProxy, _game, true, parent), mulliganNumber(0),
833 currentContext(MessageContext_None)
834 {
835 }
836