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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;");
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("&quot;<font class=\"blue\">%1</font>&quot;").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