* move message_log_widget to game

* move files

* update headers

* fix cmakelists

* oracle fixes

* split implementation out to cpp

* fix recursive import

* fix main file

* format
This commit is contained in:
ebbit1q 2025-09-20 14:35:52 +02:00 committed by GitHub
parent f484c98152
commit 17dcaf9afa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
337 changed files with 728 additions and 721 deletions

View file

@ -1,9 +1,9 @@
#include "chat_view.h"
#include "../../client/sound_engine.h"
#include "../../client/tabs/tab_account.h"
#include "../../client/ui/pixel_map_generator.h"
#include "../../interface/pixel_map_generator.h"
#include "../../settings/cache_settings.h"
#include "../../tabs/tab_account.h"
#include "../user/user_context_menu.h"
#include "../user/user_list_manager.h"
#include "../user/user_list_proxy.h"

View file

@ -1,7 +1,7 @@
#ifndef CHATVIEW_H
#define CHATVIEW_H
#include "../../client/tabs/tab_supervisor.h"
#include "../../tabs/tab_supervisor.h"
#include "../user/user_list_widget.h"
#include "room_message_type.h"
#include "user_level.h"

View file

@ -0,0 +1,393 @@
#include "game_selector.h"
#include "../client/get_text_with_max.h"
#include "../dialogs/dlg_create_game.h"
#include "../dialogs/dlg_filter_games.h"
#include "../tabs/tab_account.h"
#include "../tabs/tab_game.h"
#include "../tabs/tab_room.h"
#include "../tabs/tab_supervisor.h"
#include "abstract_client.h"
#include "games_model.h"
#include "pb/response.pb.h"
#include "pb/room_commands.pb.h"
#include "pb/serverinfo_game.pb.h"
#include "pending_command.h"
#include "user/user_list_manager.h"
#include <QApplication>
#include <QCheckBox>
#include <QDebug>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QInputDialog>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QTreeView>
#include <QVBoxLayout>
GameSelector::GameSelector(AbstractClient *_client,
TabSupervisor *_tabSupervisor,
TabRoom *_room,
const QMap<int, QString> &_rooms,
const QMap<int, GameTypeMap> &_gameTypes,
const bool restoresettings,
const bool _showfilters,
QWidget *parent)
: QGroupBox(parent), client(_client), tabSupervisor(_tabSupervisor), room(_room), showFilters(_showfilters)
{
gameListView = new QTreeView;
gameListView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(gameListView, &QTreeView::customContextMenuRequested, this, &GameSelector::customContextMenu);
gameListModel = new GamesModel(_rooms, _gameTypes, this);
if (showFilters) {
gameListProxyModel = new GamesProxyModel(this, tabSupervisor->getUserListManager());
gameListProxyModel->setSourceModel(gameListModel);
gameListProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
gameListView->setModel(gameListProxyModel);
} else {
gameListView->setModel(gameListModel);
}
gameListView->setIconSize(QSize(13, 13));
gameListView->setSortingEnabled(true);
gameListView->sortByColumn(gameListModel->startTimeColIndex(), Qt::AscendingOrder);
gameListView->setAlternatingRowColors(true);
gameListView->setRootIsDecorated(true);
// game created width
gameListView->setColumnWidth(1, gameListView->columnWidth(2) * 0.7);
// players width
gameListView->resizeColumnToContents(6);
// description width
gameListView->setColumnWidth(2, gameListView->columnWidth(2) * 1.7);
// creator width
gameListView->setColumnWidth(3, gameListView->columnWidth(3) * 1.2);
// game type width
gameListView->setColumnWidth(4, gameListView->columnWidth(4) * 1.4);
if (_room)
gameListView->header()->hideSection(gameListModel->roomColIndex());
if (room)
gameTypeMap = gameListModel->getGameTypes().value(room->getRoomId());
if (showFilters && restoresettings)
gameListProxyModel->loadFilterParameters(gameTypeMap);
gameListView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
filterButton = new QPushButton;
filterButton->setIcon(QPixmap("theme:icons/search"));
connect(filterButton, &QPushButton::clicked, this, &GameSelector::actSetFilter);
clearFilterButton = new QPushButton;
clearFilterButton->setIcon(QPixmap("theme:icons/clearsearch"));
bool filtersSetToDefault = showFilters && gameListProxyModel->areFilterParametersSetToDefaults();
clearFilterButton->setEnabled(!filtersSetToDefault);
connect(clearFilterButton, &QPushButton::clicked, this, &GameSelector::actClearFilter);
if (room) {
createButton = new QPushButton;
connect(createButton, &QPushButton::clicked, this, &GameSelector::actCreate);
} else {
createButton = nullptr;
}
joinButton = new QPushButton;
spectateButton = new QPushButton;
QHBoxLayout *buttonLayout = new QHBoxLayout;
if (showFilters) {
buttonLayout->addWidget(filterButton);
buttonLayout->addWidget(clearFilterButton);
}
buttonLayout->addStretch();
if (room)
buttonLayout->addWidget(createButton);
buttonLayout->addWidget(joinButton);
buttonLayout->addWidget(spectateButton);
buttonLayout->setAlignment(Qt::AlignTop);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(gameListView);
mainLayout->addLayout(buttonLayout);
retranslateUi();
setLayout(mainLayout);
setMinimumWidth((qreal)(gameListView->columnWidth(0) * gameListModel->columnCount()) / 1.5);
setMinimumHeight(200);
connect(joinButton, &QPushButton::clicked, this, &GameSelector::actJoin);
connect(spectateButton, &QPushButton::clicked, this, &GameSelector::actSpectate);
connect(gameListView->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
&GameSelector::actSelectedGameChanged);
connect(gameListView, &QTreeView::activated, this, &GameSelector::actJoin);
connect(client, &AbstractClient::ignoreListReceived, this, &GameSelector::ignoreListReceived);
connect(client, &AbstractClient::addToListEventReceived, this, &GameSelector::processAddToListEvent);
connect(client, &AbstractClient::removeFromListEventReceived, this, &GameSelector::processRemoveFromListEvent);
}
void GameSelector::ignoreListReceived(const QList<ServerInfo_User> &)
{
gameListProxyModel->refresh();
}
void GameSelector::processAddToListEvent(const Event_AddToList &event)
{
if (event.list_name() == "ignore") {
gameListProxyModel->refresh();
}
updateTitle();
}
void GameSelector::processRemoveFromListEvent(const Event_RemoveFromList &event)
{
if (event.list_name() == "ignore") {
gameListProxyModel->refresh();
}
updateTitle();
}
void GameSelector::actSetFilter()
{
DlgFilterGames dlg(gameTypeMap, gameListProxyModel, this);
if (!dlg.exec())
return;
gameListProxyModel->setHideBuddiesOnlyGames(dlg.getHideBuddiesOnlyGames());
gameListProxyModel->setHideFullGames(dlg.getHideFullGames());
gameListProxyModel->setHideGamesThatStarted(dlg.getHideGamesThatStarted());
gameListProxyModel->setHidePasswordProtectedGames(dlg.getHidePasswordProtectedGames());
gameListProxyModel->setHideIgnoredUserGames(dlg.getHideIgnoredUserGames());
gameListProxyModel->setHideNotBuddyCreatedGames(dlg.getHideNotBuddyCreatedGames());
gameListProxyModel->setHideOpenDecklistGames(dlg.getHideOpenDecklistGames());
gameListProxyModel->setGameNameFilter(dlg.getGameNameFilter());
gameListProxyModel->setCreatorNameFilter(dlg.getCreatorNameFilter());
gameListProxyModel->setGameTypeFilter(dlg.getGameTypeFilter());
gameListProxyModel->setMaxPlayersFilter(dlg.getMaxPlayersFilterMin(), dlg.getMaxPlayersFilterMax());
gameListProxyModel->setMaxGameAge(dlg.getMaxGameAge());
gameListProxyModel->setShowOnlyIfSpectatorsCanWatch(dlg.getShowOnlyIfSpectatorsCanWatch());
gameListProxyModel->setShowSpectatorPasswordProtected(dlg.getShowSpectatorPasswordProtected());
gameListProxyModel->setShowOnlyIfSpectatorsCanChat(dlg.getShowOnlyIfSpectatorsCanChat());
gameListProxyModel->setShowOnlyIfSpectatorsCanSeeHands(dlg.getShowOnlyIfSpectatorsCanSeeHands());
gameListProxyModel->saveFilterParameters(gameTypeMap);
clearFilterButton->setEnabled(!gameListProxyModel->areFilterParametersSetToDefaults());
updateTitle();
}
void GameSelector::actClearFilter()
{
clearFilterButton->setEnabled(false);
gameListProxyModel->resetFilterParameters();
gameListProxyModel->saveFilterParameters(gameTypeMap);
updateTitle();
}
void GameSelector::actCreate()
{
if (room == nullptr) {
qWarning() << "Attempted to create game, but the room was null";
return;
}
DlgCreateGame dlg(room, room->getGameTypes(), this);
dlg.exec();
updateTitle();
}
void GameSelector::checkResponse(const Response &response)
{
// NB: We re-enable buttons for the currently selected game, which may not
// be the same game as the one for which we are processing a response.
// This could lead to situations where we join a game, select a different
// game, join the new game, then receive the response for the original
// join, which would re-activate the buttons for the second game too soon.
// However, that is better than doing things the other ways, because then
// the response to the first game could lock us out of join/join as
// spectator for the second game!
//
// Ideally we should have a way to figure out if the current game is the
// same as the one we are getting a response for, but it's probably not
// worth the trouble.
enableButtons();
switch (response.response_code()) {
case Response::RespNotInRoom:
QMessageBox::critical(this, tr("Error"), tr("Please join the appropriate room first."));
break;
case Response::RespWrongPassword:
QMessageBox::critical(this, tr("Error"), tr("Wrong password."));
break;
case Response::RespSpectatorsNotAllowed:
QMessageBox::critical(this, tr("Error"), tr("Spectators are not allowed in this game."));
break;
case Response::RespGameFull:
QMessageBox::critical(this, tr("Error"), tr("The game is already full."));
break;
case Response::RespNameNotFound:
QMessageBox::critical(this, tr("Error"), tr("The game does not exist any more."));
break;
case Response::RespUserLevelTooLow:
QMessageBox::critical(this, tr("Error"), tr("This game is only open to registered users."));
break;
case Response::RespOnlyBuddies:
QMessageBox::critical(this, tr("Error"), tr("This game is only open to its creator's buddies."));
break;
case Response::RespInIgnoreList:
QMessageBox::critical(this, tr("Error"), tr("You are being ignored by the creator of this game."));
break;
default:;
}
}
void GameSelector::actJoin()
{
return joinGame(false);
}
void GameSelector::actSpectate()
{
return joinGame(true);
}
void GameSelector::customContextMenu(const QPoint &point)
{
const auto &index = gameListView->indexAt(point);
if (!index.isValid()) {
return;
}
QAction joinGame(tr("Join Game"));
connect(&joinGame, &QAction::triggered, this, &GameSelector::actJoin);
QAction spectateGame(tr("Spectate Game"));
connect(&spectateGame, &QAction::triggered, this, &GameSelector::actSpectate);
QAction getGameInfo(tr("Game Information"));
connect(&getGameInfo, &QAction::triggered, this, [=, this]() {
const ServerInfo_Game &gameInfo = gameListModel->getGame(index.data(Qt::UserRole).toInt());
const QMap<int, QString> &gameTypes = gameListModel->getGameTypes().value(gameInfo.room_id());
DlgCreateGame dlg(gameInfo, gameTypes, this);
dlg.exec();
});
QMenu menu;
menu.addAction(&joinGame);
menu.addAction(&spectateGame);
menu.addAction(&getGameInfo);
menu.exec(gameListView->mapToGlobal(point));
}
void GameSelector::joinGame(const bool isSpectator)
{
QModelIndex ind = gameListView->currentIndex();
if (!ind.isValid()) {
return;
}
const ServerInfo_Game &game = gameListModel->getGame(ind.data(Qt::UserRole).toInt());
if (tabSupervisor->switchToGameTabIfAlreadyExists(game.game_id())) {
return;
}
bool spectator = isSpectator || game.player_count() == game.max_players();
bool overrideRestrictions = !tabSupervisor->getAdminLocked();
QString password;
if (game.with_password() && !(spectator && !game.spectators_need_password()) && !overrideRestrictions) {
bool ok;
password = getTextWithMax(this, tr("Join game"), tr("Password:"), QLineEdit::Password, QString(), &ok);
if (!ok) {
return;
}
}
Command_JoinGame cmd;
cmd.set_game_id(game.game_id());
cmd.set_password(password.toStdString());
cmd.set_spectator(spectator);
cmd.set_override_restrictions(overrideRestrictions);
cmd.set_join_as_judge((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0);
TabRoom *r = tabSupervisor->getRoomTabs().value(game.room_id());
if (!r) {
QMessageBox::critical(this, tr("Error"), tr("Please join the respective room first."));
return;
}
PendingCommand *pend = r->prepareRoomCommand(cmd);
connect(pend, &PendingCommand::finished, this, &GameSelector::checkResponse);
r->sendRoomCommand(pend);
disableButtons();
}
void GameSelector::disableButtons()
{
if (createButton)
createButton->setEnabled(false);
joinButton->setEnabled(false);
spectateButton->setEnabled(false);
}
void GameSelector::enableButtons()
{
if (createButton)
createButton->setEnabled(true);
// Enable buttons for the currently selected game
enableButtonsForIndex(gameListView->currentIndex());
}
void GameSelector::enableButtonsForIndex(const QModelIndex &current)
{
if (!current.isValid())
return;
const ServerInfo_Game &game = gameListModel->getGame(current.data(Qt::UserRole).toInt());
bool overrideRestrictions = !tabSupervisor->getAdminLocked();
spectateButton->setEnabled(game.spectators_allowed() || overrideRestrictions);
joinButton->setEnabled(game.player_count() < game.max_players() || overrideRestrictions);
}
void GameSelector::retranslateUi()
{
filterButton->setText(tr("&Filter games"));
clearFilterButton->setText(tr("C&lear filter"));
if (createButton)
createButton->setText(tr("C&reate"));
joinButton->setText(tr("&Join"));
spectateButton->setText(tr("J&oin as spectator"));
updateTitle();
}
void GameSelector::processGameInfo(const ServerInfo_Game &info)
{
gameListModel->updateGameList(info);
updateTitle();
}
void GameSelector::actSelectedGameChanged(const QModelIndex &current, const QModelIndex & /* previous */)
{
enableButtonsForIndex(current);
}
void GameSelector::updateTitle()
{
if (showFilters) {
const int totalGames = gameListModel->rowCount();
const int shownGames = totalGames - gameListProxyModel->getNumFilteredGames();
setTitle(tr("Games shown: %1 / %2").arg(shownGames).arg(totalGames));
} else {
setTitle(tr("Games"));
}
}

View file

@ -0,0 +1,75 @@
#ifndef GAMESELECTOR_H
#define GAMESELECTOR_H
#include "game_type_map.h"
#include <QGroupBox>
#include <common/pb/event_add_to_list.pb.h>
#include <common/pb/event_remove_from_list.pb.h>
#include <pb/commands.pb.h>
class QTreeView;
class GamesModel;
class GamesProxyModel;
class QPushButton;
class QCheckBox;
class QLabel;
class AbstractClient;
class TabSupervisor;
class TabRoom;
class ServerInfo_Game;
class Response;
class GameSelector : public QGroupBox
{
Q_OBJECT
private slots:
void actSetFilter();
void actClearFilter();
void actCreate();
void actJoin();
void actSpectate();
void customContextMenu(const QPoint &point);
void actSelectedGameChanged(const QModelIndex &current, const QModelIndex &previous);
void checkResponse(const Response &response);
void ignoreListReceived(const QList<ServerInfo_User> &_ignoreList);
void processAddToListEvent(const Event_AddToList &event);
void processRemoveFromListEvent(const Event_RemoveFromList &event);
signals:
void gameJoined(int gameId);
private:
AbstractClient *client;
TabSupervisor *tabSupervisor;
TabRoom *room;
QTreeView *gameListView;
GamesModel *gameListModel;
GamesProxyModel *gameListProxyModel;
QPushButton *filterButton, *clearFilterButton, *createButton, *joinButton, *spectateButton;
const bool showFilters;
GameTypeMap gameTypeMap;
void updateTitle();
void disableButtons();
void enableButtons();
void enableButtonsForIndex(const QModelIndex &current);
void joinGame(const bool isSpectator);
public:
GameSelector(AbstractClient *_client,
TabSupervisor *_tabSupervisor,
TabRoom *_room,
const QMap<int, QString> &_rooms,
const QMap<int, GameTypeMap> &_gameTypes,
const bool restoresettings,
const bool _showfilters,
QWidget *parent = nullptr);
void retranslateUi();
void processGameInfo(const ServerInfo_Game &info);
};
#endif

View file

@ -0,0 +1,8 @@
#ifndef GAMETYPEMAP_H
#define GAMETYPEMAP_H
#include <QMap>
typedef QMap<int, QString> GameTypeMap;
#endif

View file

@ -0,0 +1,581 @@
#include "games_model.h"
#include "../interface/pixel_map_generator.h"
#include "../settings/cache_settings.h"
#include "../tabs/tab_account.h"
#include "pb/serverinfo_game.pb.h"
#include "user/user_list_manager.h"
#include "user/user_list_widget.h"
#include <QDateTime>
#include <QDebug>
#include <QIcon>
#include <QStringList>
#include <QTime>
#include <QTimeZone>
enum GameListColumn
{
ROOM,
CREATED,
DESCRIPTION,
CREATOR,
GAME_TYPE,
RESTRICTIONS,
PLAYERS,
SPECTATORS
};
const int DEFAULT_MAX_PLAYERS_MIN = 1;
const int DEFAULT_MAX_PLAYERS_MAX = 99;
constexpr QTime DEFAULT_MAX_GAME_AGE = QTime();
const QString GamesModel::getGameCreatedString(const int secs)
{
static const QTime zeroTime{0, 0};
static const int halfHourSecs = zeroTime.secsTo(QTime(1, 0)) / 2;
static const int halfMinSecs = zeroTime.secsTo(QTime(0, 1)) / 2;
static const int wrapSeconds = zeroTime.secsTo(zeroTime.addSecs(-halfHourSecs)); // round up
if (secs >= wrapSeconds) { // QTime wraps after a day
return tr(">1 day");
}
QTime total = zeroTime.addSecs(secs);
QTime totalRounded = total.addSecs(halfMinSecs); // round up
QString form;
int amount;
if (totalRounded.hour()) {
amount = total.addSecs(halfHourSecs).hour(); // round up separately
form = tr("%1%2 hr", "short age in hours", amount);
} else if (total.minute() < 2) { // games are new during their first minute
return tr("new");
} else {
amount = totalRounded.minute();
form = tr("%1%2 min", "short age in minutes", amount);
}
for (int aggregate : {40, 20, 10, 5}) { // floor to values in this list
if (amount >= aggregate) {
return form.arg(">").arg(aggregate);
}
}
return form.arg("").arg(amount);
}
GamesModel::GamesModel(const QMap<int, QString> &_rooms, const QMap<int, GameTypeMap> &_gameTypes, QObject *parent)
: QAbstractTableModel(parent), rooms(_rooms), gameTypes(_gameTypes)
{
}
QVariant GamesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::UserRole)
return index.row();
if (role != Qt::DisplayRole && role != SORT_ROLE && role != Qt::DecorationRole && role != Qt::TextAlignmentRole)
return QVariant();
if ((index.row() >= gameList.size()) || (index.column() >= columnCount()))
return QVariant();
const ServerInfo_Game &gameentry = gameList[index.row()];
switch (index.column()) {
case ROOM:
return rooms.value(gameentry.room_id());
case CREATED: {
switch (role) {
case Qt::DisplayRole: {
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
auto then = QDateTime::fromSecsSinceEpoch(gameentry.start_time(), QTimeZone::UTC);
#else
auto then = QDateTime::fromSecsSinceEpoch(gameentry.start_time(), Qt::UTC);
#endif
int secs = then.secsTo(QDateTime::currentDateTimeUtc());
return getGameCreatedString(secs);
}
case SORT_ROLE:
return QVariant(-static_cast<qint64>(gameentry.start_time()));
case Qt::TextAlignmentRole:
return Qt::AlignCenter;
default:
return QVariant();
}
}
case DESCRIPTION:
switch (role) {
case SORT_ROLE:
case Qt::DisplayRole:
return QString::fromStdString(gameentry.description());
case Qt::TextAlignmentRole:
return Qt::AlignLeft;
default:
return QVariant();
}
case CREATOR: {
switch (role) {
case SORT_ROLE:
case Qt::DisplayRole:
return QString::fromStdString(gameentry.creator_info().name());
case Qt::DecorationRole: {
return UserLevelPixmapGenerator::generateIcon(
13, UserLevelFlags(gameentry.creator_info().user_level()),
gameentry.creator_info().pawn_colors(), false,
QString::fromStdString(gameentry.creator_info().privlevel()));
}
default:
return QVariant();
}
}
case GAME_TYPE:
switch (role) {
case SORT_ROLE:
case Qt::DisplayRole: {
QStringList result;
GameTypeMap gameTypeMap = gameTypes.value(gameentry.room_id());
for (int i = gameentry.game_types_size() - 1; i >= 0; --i)
result.append(gameTypeMap.value(gameentry.game_types(i)));
return result.join(", ");
}
case Qt::TextAlignmentRole:
return Qt::AlignLeft;
default:
return QVariant();
}
case RESTRICTIONS:
switch (role) {
case SORT_ROLE:
case Qt::DisplayRole: {
QStringList result;
if (gameentry.with_password())
result.append(tr("password"));
if (gameentry.only_buddies())
result.append(tr("buddies only"));
if (gameentry.only_registered())
result.append(tr("reg. users only"));
if (gameentry.share_decklists_on_load())
result.append(tr("open decklists"));
return result.join(", ");
}
case Qt::DecorationRole: {
return gameentry.with_password() ? QIcon(LockPixmapGenerator::generatePixmap(13)) : QVariant();
case Qt::TextAlignmentRole:
return Qt::AlignLeft;
default:
return QVariant();
}
}
case PLAYERS:
switch (role) {
case SORT_ROLE:
case Qt::DisplayRole:
return QString("%1/%2").arg(gameentry.player_count()).arg(gameentry.max_players());
case Qt::TextAlignmentRole:
return Qt::AlignCenter;
default:
return QVariant();
}
case SPECTATORS:
switch (role) {
case SORT_ROLE:
case Qt::DisplayRole: {
if (gameentry.spectators_allowed()) {
QString result;
result.append(QString::number(gameentry.spectators_count()));
if (gameentry.spectators_can_chat() && gameentry.spectators_omniscient()) {
result.append(" (")
.append(tr("can chat"))
.append(" & ")
.append(tr("see hands"))
.append(")");
} else if (gameentry.spectators_can_chat()) {
result.append(" (").append(tr("can chat")).append(")");
} else if (gameentry.spectators_omniscient()) {
result.append(" (").append(tr("can see hands")).append(")");
}
return result;
}
return QVariant(tr("not allowed"));
}
case Qt::TextAlignmentRole:
return Qt::AlignLeft;
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant GamesModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
if ((role != Qt::DisplayRole) && (role != Qt::TextAlignmentRole))
return QVariant();
switch (section) {
case ROOM:
return tr("Room");
case CREATED: {
switch (role) {
case Qt::DisplayRole:
return tr("Age");
case Qt::TextAlignmentRole:
return Qt::AlignCenter;
default:
return QVariant();
}
}
case DESCRIPTION:
return tr("Description");
case CREATOR:
return tr("Creator");
case GAME_TYPE:
return tr("Type");
case RESTRICTIONS:
return tr("Restrictions");
case PLAYERS: {
switch (role) {
case Qt::DisplayRole:
return tr("Players");
case Qt::TextAlignmentRole:
return Qt::AlignCenter;
default:
return QVariant();
}
}
case SPECTATORS:
return tr("Spectators");
default:
return QVariant();
}
}
const ServerInfo_Game &GamesModel::getGame(int row)
{
Q_ASSERT(row < gameList.size());
return gameList[row];
}
void GamesModel::updateGameList(const ServerInfo_Game &game)
{
for (int i = 0; i < gameList.size(); i++) {
if (gameList[i].game_id() == game.game_id()) {
if (game.closed()) {
beginRemoveRows(QModelIndex(), i, i);
gameList.removeAt(i);
endRemoveRows();
} else {
gameList[i].MergeFrom(game);
emit dataChanged(index(i, 0), index(i, NUM_COLS - 1));
}
return;
}
}
beginInsertRows(QModelIndex(), gameList.size(), gameList.size());
gameList.append(game);
endInsertRows();
}
GamesProxyModel::GamesProxyModel(QObject *parent, const UserListProxy *_userListProxy)
: QSortFilterProxyModel(parent), userListProxy(_userListProxy)
{
resetFilterParameters();
setSortRole(GamesModel::SORT_ROLE);
setDynamicSortFilter(true);
}
void GamesProxyModel::setHideBuddiesOnlyGames(bool _showBuddiesOnlyGames)
{
hideBuddiesOnlyGames = _showBuddiesOnlyGames;
invalidateFilter();
}
void GamesProxyModel::setHideIgnoredUserGames(bool _hideIgnoredUserGames)
{
hideIgnoredUserGames = _hideIgnoredUserGames;
invalidateFilter();
}
void GamesProxyModel::setHideFullGames(bool _showFullGames)
{
hideFullGames = _showFullGames;
invalidateFilter();
}
void GamesProxyModel::setHideGamesThatStarted(bool _showGamesThatStarted)
{
hideGamesThatStarted = _showGamesThatStarted;
invalidateFilter();
}
void GamesProxyModel::setHidePasswordProtectedGames(bool _showPasswordProtectedGames)
{
hidePasswordProtectedGames = _showPasswordProtectedGames;
invalidateFilter();
}
void GamesProxyModel::setHideNotBuddyCreatedGames(bool value)
{
hideNotBuddyCreatedGames = value;
invalidateFilter();
}
void GamesProxyModel::setHideOpenDecklistGames(bool _hideOpenDecklistGames)
{
hideOpenDecklistGames = _hideOpenDecklistGames;
invalidateFilter();
}
void GamesProxyModel::setGameNameFilter(const QString &_gameNameFilter)
{
gameNameFilter = _gameNameFilter;
invalidateFilter();
}
void GamesProxyModel::setCreatorNameFilter(const QString &_creatorNameFilter)
{
creatorNameFilter = _creatorNameFilter;
invalidateFilter();
}
void GamesProxyModel::setGameTypeFilter(const QSet<int> &_gameTypeFilter)
{
gameTypeFilter = _gameTypeFilter;
invalidateFilter();
}
void GamesProxyModel::setMaxPlayersFilter(int _maxPlayersFilterMin, int _maxPlayersFilterMax)
{
maxPlayersFilterMin = _maxPlayersFilterMin;
maxPlayersFilterMax = _maxPlayersFilterMax;
invalidateFilter();
}
void GamesProxyModel::setMaxGameAge(const QTime &_maxGameAge)
{
maxGameAge = _maxGameAge;
invalidateFilter();
}
void GamesProxyModel::setShowOnlyIfSpectatorsCanWatch(bool _showOnlyIfSpectatorsCanWatch)
{
showOnlyIfSpectatorsCanWatch = _showOnlyIfSpectatorsCanWatch;
invalidateFilter();
}
void GamesProxyModel::setShowSpectatorPasswordProtected(bool _showSpectatorPasswordProtected)
{
showSpectatorPasswordProtected = _showSpectatorPasswordProtected;
invalidateFilter();
}
void GamesProxyModel::setShowOnlyIfSpectatorsCanChat(bool _showOnlyIfSpectatorsCanChat)
{
showOnlyIfSpectatorsCanChat = _showOnlyIfSpectatorsCanChat;
invalidateFilter();
}
void GamesProxyModel::setShowOnlyIfSpectatorsCanSeeHands(bool _showOnlyIfSpectatorsCanSeeHands)
{
showOnlyIfSpectatorsCanSeeHands = _showOnlyIfSpectatorsCanSeeHands;
invalidateFilter();
}
int GamesProxyModel::getNumFilteredGames() const
{
GamesModel *model = qobject_cast<GamesModel *>(sourceModel());
if (!model)
return 0;
int numFilteredGames = 0;
for (int row = 0; row < model->rowCount(); ++row) {
if (!filterAcceptsRow(row)) {
++numFilteredGames;
}
}
return numFilteredGames;
}
void GamesProxyModel::resetFilterParameters()
{
hideFullGames = false;
hideGamesThatStarted = false;
hidePasswordProtectedGames = false;
hideBuddiesOnlyGames = false;
hideIgnoredUserGames = false;
hideNotBuddyCreatedGames = false;
hideOpenDecklistGames = false;
gameNameFilter = QString();
creatorNameFilter = QString();
gameTypeFilter.clear();
maxPlayersFilterMin = DEFAULT_MAX_PLAYERS_MIN;
maxPlayersFilterMax = DEFAULT_MAX_PLAYERS_MAX;
maxGameAge = DEFAULT_MAX_GAME_AGE;
showOnlyIfSpectatorsCanWatch = false;
showSpectatorPasswordProtected = false;
showOnlyIfSpectatorsCanChat = false;
showOnlyIfSpectatorsCanSeeHands = false;
invalidateFilter();
}
bool GamesProxyModel::areFilterParametersSetToDefaults() const
{
return !hideFullGames && !hideGamesThatStarted && !hidePasswordProtectedGames && !hideBuddiesOnlyGames &&
!hideOpenDecklistGames && !hideIgnoredUserGames && !hideNotBuddyCreatedGames && gameNameFilter.isEmpty() &&
creatorNameFilter.isEmpty() && gameTypeFilter.isEmpty() && maxPlayersFilterMin == DEFAULT_MAX_PLAYERS_MIN &&
maxPlayersFilterMax == DEFAULT_MAX_PLAYERS_MAX && maxGameAge == DEFAULT_MAX_GAME_AGE &&
!showOnlyIfSpectatorsCanWatch && !showSpectatorPasswordProtected && !showOnlyIfSpectatorsCanChat &&
!showOnlyIfSpectatorsCanSeeHands;
}
void GamesProxyModel::loadFilterParameters(const QMap<int, QString> &allGameTypes)
{
GameFiltersSettings &gameFilters = SettingsCache::instance().gameFilters();
hideFullGames = gameFilters.isHideFullGames();
hideGamesThatStarted = gameFilters.isHideGamesThatStarted();
hidePasswordProtectedGames = gameFilters.isHidePasswordProtectedGames();
hideIgnoredUserGames = gameFilters.isHideIgnoredUserGames();
hideBuddiesOnlyGames = gameFilters.isHideBuddiesOnlyGames();
hideNotBuddyCreatedGames = gameFilters.isHideNotBuddyCreatedGames();
hideOpenDecklistGames = gameFilters.isHideOpenDecklistGames();
gameNameFilter = gameFilters.getGameNameFilter();
creatorNameFilter = gameFilters.getCreatorNameFilter();
maxPlayersFilterMin = gameFilters.getMinPlayers();
maxPlayersFilterMax = gameFilters.getMaxPlayers();
maxGameAge = gameFilters.getMaxGameAge();
showOnlyIfSpectatorsCanWatch = gameFilters.isShowOnlyIfSpectatorsCanWatch();
showSpectatorPasswordProtected = gameFilters.isShowSpectatorPasswordProtected();
showOnlyIfSpectatorsCanChat = gameFilters.isShowOnlyIfSpectatorsCanChat();
showOnlyIfSpectatorsCanSeeHands = gameFilters.isShowOnlyIfSpectatorsCanSeeHands();
QMapIterator<int, QString> gameTypesIterator(allGameTypes);
while (gameTypesIterator.hasNext()) {
gameTypesIterator.next();
if (gameFilters.isGameTypeEnabled(gameTypesIterator.value())) {
gameTypeFilter.insert(gameTypesIterator.key());
}
}
invalidateFilter();
}
void GamesProxyModel::saveFilterParameters(const QMap<int, QString> &allGameTypes)
{
GameFiltersSettings &gameFilters = SettingsCache::instance().gameFilters();
gameFilters.setHideBuddiesOnlyGames(hideBuddiesOnlyGames);
gameFilters.setHideFullGames(hideFullGames);
gameFilters.setHideGamesThatStarted(hideGamesThatStarted);
gameFilters.setHidePasswordProtectedGames(hidePasswordProtectedGames);
gameFilters.setHideIgnoredUserGames(hideIgnoredUserGames);
gameFilters.setHideNotBuddyCreatedGames(hideNotBuddyCreatedGames);
gameFilters.setHideOpenDecklistGames(hideOpenDecklistGames);
gameFilters.setGameNameFilter(gameNameFilter);
gameFilters.setCreatorNameFilter(creatorNameFilter);
QMapIterator<int, QString> gameTypeIterator(allGameTypes);
while (gameTypeIterator.hasNext()) {
gameTypeIterator.next();
bool enabled = gameTypeFilter.contains(gameTypeIterator.key());
gameFilters.setGameTypeEnabled(gameTypeIterator.value(), enabled);
}
gameFilters.setMinPlayers(maxPlayersFilterMin);
gameFilters.setMaxPlayers(maxPlayersFilterMax);
gameFilters.setMaxGameAge(maxGameAge);
gameFilters.setShowOnlyIfSpectatorsCanWatch(showOnlyIfSpectatorsCanWatch);
gameFilters.setShowSpectatorPasswordProtected(showSpectatorPasswordProtected);
gameFilters.setShowOnlyIfSpectatorsCanChat(showOnlyIfSpectatorsCanChat);
gameFilters.setShowOnlyIfSpectatorsCanSeeHands(showOnlyIfSpectatorsCanSeeHands);
}
bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
{
return filterAcceptsRow(sourceRow);
}
bool GamesProxyModel::filterAcceptsRow(int sourceRow) const
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, QTimeZone::UTC).date();
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
static const QDate epochDate = QDateTime::fromSecsSinceEpoch(0, Qt::UTC).date();
#else
static const QDate epochDate = QDateTime::fromTime_t(0, Qt::UTC).date();
#endif
auto *model = qobject_cast<GamesModel *>(sourceModel());
if (!model)
return false;
const ServerInfo_Game &game = model->getGame(sourceRow);
if (hideBuddiesOnlyGames && game.only_buddies()) {
return false;
}
if (hideOpenDecklistGames && game.share_decklists_on_load()) {
return false;
}
if (hideIgnoredUserGames && userListProxy->isUserIgnored(QString::fromStdString(game.creator_info().name()))) {
return false;
}
if (hideNotBuddyCreatedGames && !userListProxy->isUserBuddy(QString::fromStdString(game.creator_info().name()))) {
return false;
}
if (hideFullGames && game.player_count() == game.max_players())
return false;
if (hideGamesThatStarted && game.started())
return false;
if (!userListProxy->isOwnUserRegistered())
if (game.only_registered())
return false;
if (hidePasswordProtectedGames && game.with_password())
return false;
if (!gameNameFilter.isEmpty())
if (!QString::fromStdString(game.description()).contains(gameNameFilter, Qt::CaseInsensitive))
return false;
if (!creatorNameFilter.isEmpty())
if (!QString::fromStdString(game.creator_info().name()).contains(creatorNameFilter, Qt::CaseInsensitive))
return false;
QSet<int> gameTypes;
for (int i = 0; i < game.game_types_size(); ++i)
gameTypes.insert(game.game_types(i));
if (!gameTypeFilter.isEmpty() && gameTypes.intersect(gameTypeFilter).isEmpty())
return false;
if (game.max_players() < maxPlayersFilterMin)
return false;
if (game.max_players() > maxPlayersFilterMax)
return false;
if (maxGameAge.isValid()) {
QDateTime now = QDateTime::currentDateTimeUtc();
qint64 signed_start_time = game.start_time(); // cast to 64 bit value to allow signed value
QDateTime total = now.addSecs(-signed_start_time); // a 32 bit value would wrap at 2038-1-19
// games shouldn't have negative ages but we'll not filter them
// because qtime wraps after a day we consider all games older than a day to be too old
if (total.isValid() && total.date() >= epochDate && (total.date() > epochDate || total.time() > maxGameAge)) {
return false;
}
}
if (showOnlyIfSpectatorsCanWatch) {
if (!game.spectators_allowed())
return false;
if (!showSpectatorPasswordProtected && game.spectators_need_password())
return false;
if (showOnlyIfSpectatorsCanChat && !game.spectators_can_chat())
return false;
if (showOnlyIfSpectatorsCanSeeHands && !game.spectators_omniscient())
return false;
}
return true;
}
void GamesProxyModel::refresh()
{
invalidateFilter();
}

View file

@ -0,0 +1,192 @@
#ifndef GAMESMODEL_H
#define GAMESMODEL_H
#include "game_type_map.h"
#include "pb/serverinfo_game.pb.h"
#include <QAbstractTableModel>
#include <QList>
#include <QSet>
#include <QSortFilterProxyModel>
#include <QStringList>
#include <QTime>
class UserListProxy;
class GamesModel : public QAbstractTableModel
{
Q_OBJECT
private:
QList<ServerInfo_Game> gameList;
QMap<int, QString> rooms;
QMap<int, GameTypeMap> gameTypes;
static const int NUM_COLS = 8;
public:
static const int SORT_ROLE = Qt::UserRole + 1;
GamesModel(const QMap<int, QString> &_rooms, const QMap<int, GameTypeMap> &_gameTypes, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
return parent.isValid() ? 0 : gameList.size();
}
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override
{
return NUM_COLS;
}
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
static const QString getGameCreatedString(const int secs);
const ServerInfo_Game &getGame(int row);
/**
* Update game list with a (possibly new) game.
*/
void updateGameList(const ServerInfo_Game &game);
int roomColIndex()
{
return 0;
}
int startTimeColIndex()
{
return 1;
}
const QMap<int, GameTypeMap> &getGameTypes()
{
return gameTypes;
}
};
class ServerInfo_User;
class GamesProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
private:
const UserListProxy *userListProxy;
// If adding any additional filters, make sure to update:
// - GamesProxyModel()
// - resetFilterParameters()
// - areFilterParametersSetToDefaults()
// - loadFilterParameters()
// - saveFilterParameters()
// - filterAcceptsRow()
bool hideBuddiesOnlyGames;
bool hideIgnoredUserGames;
bool hideFullGames;
bool hideGamesThatStarted;
bool hidePasswordProtectedGames;
bool hideNotBuddyCreatedGames;
bool hideOpenDecklistGames;
QString gameNameFilter, creatorNameFilter;
QSet<int> gameTypeFilter;
quint32 maxPlayersFilterMin, maxPlayersFilterMax;
QTime maxGameAge;
bool showOnlyIfSpectatorsCanWatch, showSpectatorPasswordProtected, showOnlyIfSpectatorsCanChat,
showOnlyIfSpectatorsCanSeeHands;
public:
explicit GamesProxyModel(QObject *parent = nullptr, const UserListProxy *_userListProxy = nullptr);
bool getHideBuddiesOnlyGames() const
{
return hideBuddiesOnlyGames;
}
void setHideBuddiesOnlyGames(bool _showBuddiesOnlyGames);
bool getHideIgnoredUserGames() const
{
return hideIgnoredUserGames;
}
void setHideIgnoredUserGames(bool _hideIgnoredUserGames);
bool getHideFullGames() const
{
return hideFullGames;
}
void setHideFullGames(bool _showFullGames);
bool getHideGamesThatStarted() const
{
return hideGamesThatStarted;
}
void setHideGamesThatStarted(bool _showGamesThatStarted);
bool getHidePasswordProtectedGames() const
{
return hidePasswordProtectedGames;
}
void setHidePasswordProtectedGames(bool _showPasswordProtectedGames);
bool getHideNotBuddyCreatedGames() const
{
return hideNotBuddyCreatedGames;
}
void setHideNotBuddyCreatedGames(bool value);
bool getHideOpenDecklistGames() const
{
return hideOpenDecklistGames;
}
void setHideOpenDecklistGames(bool _hideOpenDecklistGames);
QString getGameNameFilter() const
{
return gameNameFilter;
}
void setGameNameFilter(const QString &_gameNameFilter);
QString getCreatorNameFilter() const
{
return creatorNameFilter;
}
void setCreatorNameFilter(const QString &_creatorNameFilter);
QSet<int> getGameTypeFilter() const
{
return gameTypeFilter;
}
void setGameTypeFilter(const QSet<int> &_gameTypeFilter);
int getMaxPlayersFilterMin() const
{
return maxPlayersFilterMin;
}
int getMaxPlayersFilterMax() const
{
return maxPlayersFilterMax;
}
void setMaxPlayersFilter(int _maxPlayersFilterMin, int _maxPlayersFilterMax);
const QTime &getMaxGameAge() const
{
return maxGameAge;
}
void setMaxGameAge(const QTime &_maxGameAge);
bool getShowOnlyIfSpectatorsCanWatch() const
{
return showOnlyIfSpectatorsCanWatch;
}
void setShowOnlyIfSpectatorsCanWatch(bool _showOnlyIfSpectatorsCanWatch);
bool getShowSpectatorPasswordProtected() const
{
return showSpectatorPasswordProtected;
}
void setShowSpectatorPasswordProtected(bool _showSpectatorPasswordProtected);
bool getShowOnlyIfSpectatorsCanChat() const
{
return showOnlyIfSpectatorsCanChat;
}
void setShowOnlyIfSpectatorsCanChat(bool _showOnlyIfSpectatorsCanChat);
bool getShowOnlyIfSpectatorsCanSeeHands() const
{
return showOnlyIfSpectatorsCanSeeHands;
}
void setShowOnlyIfSpectatorsCanSeeHands(bool _showOnlyIfSpectatorsCanSeeHands);
int getNumFilteredGames() const;
void resetFilterParameters();
bool areFilterParametersSetToDefaults() const;
void loadFilterParameters(const QMap<int, QString> &allGameTypes);
void saveFilterParameters(const QMap<int, QString> &allGameTypes);
void refresh();
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool filterAcceptsRow(int sourceRow) const;
};
#endif

View file

@ -1,842 +0,0 @@
#include "message_log_widget.h"
#include "../client/sound_engine.h"
#include "../client/tabs/tab_game.h"
#include "../client/translate_counter_name.h"
#include "../game/board/card_item.h"
#include "../game/phase.h"
#include "../game/player/player.h"
#include "../game/zones/card_zone.h"
#include "../settings/card_counter_settings.h"
#include "pb/context_move_card.pb.h"
#include "pb/context_mulligan.pb.h"
#include "pb/serverinfo_user.pb.h"
#include <utility>
static const QString TABLE_ZONE_NAME = "table";
static const QString GRAVE_ZONE_NAME = "grave";
static const QString EXILE_ZONE_NAME = "rfg";
static const QString HAND_ZONE_NAME = "hand";
static const QString DECK_ZONE_NAME = "deck";
static const QString SIDEBOARD_ZONE_NAME = "sb";
static const QString STACK_ZONE_NAME = "stack";
static QString sanitizeHtml(QString dirty)
{
return dirty.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;");
}
static QString cardLink(const QString &cardName)
{
return QString("<i><a href=\"card://%1\">%2</a></i>").arg(cardName).arg(cardName);
}
QPair<QString, QString>
MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position, bool ownerChange)
{
bool cardNameContainsStartZone = false;
QString fromStr;
QString zoneName = zone->getName();
if (zoneName == TABLE_ZONE_NAME) {
fromStr = tr(" from play");
} else if (zoneName == GRAVE_ZONE_NAME) {
fromStr = tr(" from their graveyard");
} else if (zoneName == EXILE_ZONE_NAME) {
fromStr = tr(" from exile");
} else if (zoneName == HAND_ZONE_NAME) {
fromStr = tr(" from their hand");
} else if (zoneName == DECK_ZONE_NAME) {
if (position == 0) {
if (cardName.isEmpty()) {
if (ownerChange) {
cardName = tr("the top card of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
} else {
cardName = tr("the top card of their library");
}
cardNameContainsStartZone = true;
} else {
if (ownerChange) {
fromStr = tr(" from the top of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
} else {
fromStr = tr(" from the top of their library");
}
}
} else if (position >= zone->getCards().size() - 1) {
if (cardName.isEmpty()) {
if (ownerChange) {
cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
} else {
cardName = tr("the bottom card of their library");
}
cardNameContainsStartZone = true;
} else {
if (ownerChange) {
fromStr = tr(" from the bottom of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
} else {
fromStr = tr(" from the bottom of their library");
}
}
} else {
if (ownerChange) {
fromStr = tr(" from %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
} else {
fromStr = tr(" from their library");
}
}
} else if (zoneName == SIDEBOARD_ZONE_NAME) {
fromStr = tr(" from sideboard");
} else if (zoneName == STACK_ZONE_NAME) {
fromStr = tr(" from the stack");
} else {
fromStr = tr(" from custom zone '%1'").arg(zoneName);
}
if (!cardNameContainsStartZone) {
cardName.clear();
}
return {cardName, fromStr};
}
void MessageLogWidget::containerProcessingDone()
{
currentContext = MessageContext_None;
messageSuffix = messagePrefix = QString();
}
void MessageLogWidget::containerProcessingStarted(const GameEventContext &context)
{
if (context.HasExtension(Context_MoveCard::ext)) {
currentContext = MessageContext_MoveCard;
} else if (context.HasExtension(Context_Mulligan::ext)) {
currentContext = MessageContext_Mulligan;
}
}
void MessageLogWidget::logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal)
{
appendHtmlServerMessage((reveal ? tr("%1 is now keeping the top card %2 revealed.")
: tr("%1 is not revealing the top card %2 any longer."))
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseTopCardsOfZone)));
}
void MessageLogWidget::logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal)
{
appendHtmlServerMessage((reveal ? tr("%1 can now look at top card %2 at any time.")
: tr("%1 no longer can look at top card %2 at any time."))
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseTopCardsOfZone)));
}
void MessageLogWidget::logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName)
{
appendHtmlServerMessage(tr("%1 attaches %2 to %3's %4.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardLink(std::move(cardName)))
.arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName()))
.arg(cardLink(std::move(targetCardName))));
}
void MessageLogWidget::logConcede(int playerId)
{
soundEngine->playSound("player_concede");
appendHtmlServerMessage(
tr("%1 has conceded the game.")
.arg(sanitizeHtml(game->getPlayerManager()->getPlayer(playerId)->getPlayerInfo()->getName())),
true);
}
void MessageLogWidget::logUnconcede(int playerId)
{
soundEngine->playSound("player_concede");
appendHtmlServerMessage(
tr("%1 has unconceded the game.")
.arg(sanitizeHtml(game->getPlayerManager()->getPlayer(playerId)->getPlayerInfo()->getName())),
true);
}
void MessageLogWidget::logConnectionStateChanged(Player *player, bool connectionState)
{
if (connectionState) {
soundEngine->playSound("player_reconnect");
appendHtmlServerMessage(
tr("%1 has restored connection to the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName())), true);
} else {
soundEngine->playSound("player_disconnect");
appendHtmlServerMessage(
tr("%1 has lost connection to the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName())), true);
}
}
void MessageLogWidget::logCreateArrow(Player *player,
Player *startPlayer,
QString startCard,
Player *targetPlayer,
QString targetCard,
bool playerTarget)
{
startCard = cardLink(startCard);
targetCard = cardLink(targetCard);
QString str;
if (playerTarget) {
if (player == startPlayer && player == targetPlayer) {
str = tr("%1 points from their %2 to themselves.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(startCard));
} else if (player == startPlayer) {
str = tr("%1 points from their %2 to %3.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(startCard)
.arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName())));
} else if (player == targetPlayer) {
str = tr("%1 points from %2's %3 to themselves.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName()))
.arg(startCard));
} else {
str = tr("%1 points from %2's %3 to %4.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName()))
.arg(startCard)
.arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName())));
}
} else {
if (player == startPlayer && player == targetPlayer) {
str = tr("%1 points from their %2 to their %3.");
appendHtmlServerMessage(
str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(startCard).arg(targetCard));
} else if (player == startPlayer) {
str = tr("%1 points from their %2 to %3's %4.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(startCard)
.arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName()))
.arg(targetCard));
} else if (player == targetPlayer) {
str = tr("%1 points from %2's %3 to their own %4.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName()))
.arg(startCard)
.arg(targetCard));
} else {
str = tr("%1 points from %2's %3 to %4's %5.");
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName()))
.arg(startCard)
.arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName()))
.arg(targetCard));
}
}
}
void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt, bool faceDown)
{
if (faceDown) {
appendHtmlServerMessage(
tr("%1 creates a face down token.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(tr("%1 creates token: %2%3.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardLink(std::move(cardName)))
.arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
}
}
void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize)
{
if (sideboardSize < 0) {
appendHtmlServerMessage(
tr("%1 has loaded a deck (%2).").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(deckHash));
} else {
appendHtmlServerMessage(tr("%1 has loaded a deck with %2 sideboard cards (%3).")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + QString::number(sideboardSize) + "</font>")
.arg(deckHash));
}
}
void MessageLogWidget::logDestroyCard(Player *player, QString cardName)
{
appendHtmlServerMessage(
tr("%1 destroys %2.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(std::move(cardName))));
}
void MessageLogWidget::logMoveCard(Player *player,
CardItem *card,
CardZoneLogic *startZone,
int oldX,
CardZoneLogic *targetZone,
int newX)
{
if (currentContext == MessageContext_Mulligan) {
return;
}
QString startZoneName = startZone->getName();
QString targetZoneName = targetZone->getName();
bool ownerChanged = startZone->getPlayer() != targetZone->getPlayer();
// do not log if moved within the same zone
if ((startZoneName == TABLE_ZONE_NAME && targetZoneName == TABLE_ZONE_NAME && !ownerChanged) ||
(startZoneName == HAND_ZONE_NAME && targetZoneName == HAND_ZONE_NAME) ||
(startZoneName == EXILE_ZONE_NAME && targetZoneName == EXILE_ZONE_NAME)) {
return;
}
QString cardName = card->getName();
QPair<QString, QString> nameFrom = getFromStr(startZone, cardName, oldX, ownerChanged);
if (!nameFrom.first.isEmpty()) {
cardName = nameFrom.first;
}
QString cardStr;
if (!nameFrom.first.isEmpty()) {
cardStr = cardName;
} else if (cardName.isEmpty()) {
cardStr = tr("a card");
} else {
cardStr = cardLink(cardName);
}
if (ownerChanged && (startZone->getPlayer() == player)) {
appendHtmlServerMessage(tr("%1 gives %2 control over %3.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(sanitizeHtml(targetZone->getPlayer()->getPlayerInfo()->getName()))
.arg(cardStr));
return;
}
QString finalStr;
std::optional<QString> fourthArg;
if (targetZoneName == TABLE_ZONE_NAME) {
soundEngine->playSound("play_card");
if (card->getFaceDown()) {
finalStr = tr("%1 puts %2 into play%3 face down.");
} else {
finalStr = tr("%1 puts %2 into play%3.");
}
} else if (targetZoneName == GRAVE_ZONE_NAME) {
finalStr = tr("%1 puts %2%3 into their graveyard.");
} else if (targetZoneName == EXILE_ZONE_NAME) {
finalStr = tr("%1 exiles %2%3.");
} else if (targetZoneName == HAND_ZONE_NAME) {
finalStr = tr("%1 moves %2%3 to their hand.");
} else if (targetZoneName == DECK_ZONE_NAME) {
if (newX == -1) {
finalStr = tr("%1 puts %2%3 into their library.");
} else if (newX >= targetZone->getCards().size()) {
finalStr = tr("%1 puts %2%3 onto the bottom of their library.");
} else if (newX == 0) {
finalStr = tr("%1 puts %2%3 on top of their library.");
} else {
++newX;
fourthArg = QString::number(newX);
finalStr = tr("%1 puts %2%3 into their library %4 cards from the top.");
}
} else if (targetZoneName == SIDEBOARD_ZONE_NAME) {
finalStr = tr("%1 moves %2%3 to sideboard.");
} else if (targetZoneName == STACK_ZONE_NAME) {
soundEngine->playSound("play_card");
finalStr = tr("%1 plays %2%3.");
} else {
fourthArg = targetZoneName;
finalStr = tr("%1 moves %2%3 to custom zone '%4'.");
}
QString message = finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()), cardStr, nameFrom.second);
if (fourthArg.has_value()) {
message = message.arg(fourthArg.value());
}
appendHtmlServerMessage(message);
}
void MessageLogWidget::logDrawCards(Player *player, int number, bool deckIsEmpty)
{
soundEngine->playSound("draw_card");
if (currentContext == MessageContext_Mulligan) {
logMulligan(player, number);
} else {
if (deckIsEmpty && number == 0) {
appendHtmlServerMessage(
tr("%1 tries to draw from an empty library").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(tr("%1 draws %2 card(s).", "", number)
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + QString::number(number) + "</font>"));
}
}
}
void MessageLogWidget::logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed)
{
if (numberCards == -1) {
appendHtmlServerMessage(tr("%1 is looking at %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(zone->getPlayer() == player, CaseLookAtZone)));
} else {
appendHtmlServerMessage(
tr("%1 is looking at the %4 %3 card(s) %2.", "top card for singular, top %3 cards for plural", numberCards)
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(zone->getPlayer() == player, CaseTopCardsOfZone))
.arg("<font class=\"blue\">" + QString::number(numberCards) + "</font>")
.arg(isReversed ? tr("bottom") : tr("top")));
}
}
void MessageLogWidget::logFlipCard(Player *player, QString cardName, bool faceDown)
{
if (faceDown) {
appendHtmlServerMessage(
tr("%1 turns %2 face-down.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(cardName)));
} else {
appendHtmlServerMessage(
tr("%1 turns %2 face-up.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(cardName)));
}
}
void MessageLogWidget::logGameClosed()
{
appendHtmlServerMessage(tr("The game has been closed."));
}
void MessageLogWidget::logGameStart()
{
appendHtmlServerMessage(tr("The game has started."));
}
void MessageLogWidget::logGameFlooded()
{
appendMessage(tr("You are flooding the game. Please wait a couple of seconds."));
}
void MessageLogWidget::logJoin(Player *player)
{
soundEngine->playSound("player_join");
appendHtmlServerMessage(tr("%1 has joined the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
}
void MessageLogWidget::logJoinSpectator(QString name)
{
soundEngine->playSound("spectator_join");
appendHtmlServerMessage(tr("%1 is now watching the game.").arg(sanitizeHtml(std::move(name))));
}
void MessageLogWidget::logKicked()
{
appendHtmlServerMessage(tr("You have been kicked out of the game."), true);
}
void MessageLogWidget::logLeave(Player *player, QString reason)
{
soundEngine->playSound("player_leave");
appendHtmlServerMessage(tr("%1 has left the game (%2).")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()), sanitizeHtml(std::move(reason))),
true);
}
void MessageLogWidget::logLeaveSpectator(QString name, QString reason)
{
soundEngine->playSound("spectator_leave");
appendHtmlServerMessage(tr("%1 is not watching the game any more (%2).")
.arg(sanitizeHtml(std::move(name)), sanitizeHtml(std::move(reason))));
}
void MessageLogWidget::logNotReadyStart(Player *player)
{
appendHtmlServerMessage(
tr("%1 is not ready to start the game any more.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
}
void MessageLogWidget::logMulligan(Player *player, int number)
{
if (!player) {
return;
}
if (number > 0) {
appendHtmlServerMessage(tr("%1 shuffles their deck and draws a new hand of %2 card(s).", "", number)
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(number));
} else {
appendHtmlServerMessage(
tr("%1 shuffles their deck and draws a new hand.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
}
}
void MessageLogWidget::logReplayStarted(int gameId)
{
appendHtmlServerMessage(tr("You are watching a replay of game #%1.").arg(gameId));
}
void MessageLogWidget::logReadyStart(Player *player)
{
appendHtmlServerMessage(tr("%1 is ready to start the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
}
void MessageLogWidget::logRevealCards(Player *player,
CardZoneLogic *zone,
int cardId,
QString cardName,
Player *otherPlayer,
bool faceDown,
int amount,
bool isLentToAnotherPlayer)
{
// getFromStr uses cardname.empty() to check if it should contain the start zone, it's not actually used
QPair<QString, QString> temp = getFromStr(zone, amount == 1 ? cardName : QString::number(amount), cardId, false);
bool cardNameContainsStartZone = false;
if (!temp.first.isEmpty()) {
cardNameContainsStartZone = true;
cardName = temp.first;
}
QString fromStr = temp.second;
QString cardStr;
if (cardNameContainsStartZone) {
cardStr = cardName;
} else if (cardName.isEmpty()) {
if (amount == 0) {
cardStr = tr("cards", "an unknown amount of cards");
} else {
cardStr = tr("%1 card(s)", "a card for singular, %1 cards for plural", amount)
.arg("<font class=\"blue\">" + QString::number(amount) + "</font>");
}
} else {
cardStr = cardLink(cardName);
}
if (cardId == -1) {
if (otherPlayer) {
if (isLentToAnotherPlayer) {
appendHtmlServerMessage(tr("%1 lends %2 to %3.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseRevealZone))
.arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(tr("%1 reveals %2 to %3.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseRevealZone))
.arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName())));
}
} else {
appendHtmlServerMessage(tr("%1 reveals %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseRevealZone)));
}
} else if (cardId == -2) {
if (otherPlayer) {
appendHtmlServerMessage(tr("%1 randomly reveals %2%3 to %4.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardStr)
.arg(fromStr)
.arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(tr("%1 randomly reveals %2%3.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardStr)
.arg(fromStr));
}
} else {
if (faceDown && player == otherPlayer) {
if (cardName.isEmpty()) {
appendHtmlServerMessage(tr("%1 peeks at face down card #%2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardId));
} else {
appendHtmlServerMessage(tr("%1 peeks at face down card #%2: %3.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardId)
.arg(cardStr));
}
} else if (otherPlayer) {
appendHtmlServerMessage(tr("%1 reveals %2%3 to %4.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardStr)
.arg(fromStr)
.arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(
tr("%1 reveals %2%3.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardStr).arg(fromStr));
}
}
}
void MessageLogWidget::logReverseTurn(Player *player, bool reversed)
{
appendHtmlServerMessage(tr("%1 reversed turn order, now it's %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(reversed ? tr("reversed") : tr("normal")));
}
void MessageLogWidget::logRollDie(Player *player, int sides, const QList<uint> &rolls)
{
if (rolls.length() == 1) {
const auto roll = rolls.at(0);
if (sides == 2) {
QString coinOptions[2] = {tr("Heads") + " (1)", tr("Tails") + " (2)"};
appendHtmlServerMessage(tr("%1 flipped a coin. It landed as %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + coinOptions[roll - 1] + "</font>"));
} else {
appendHtmlServerMessage(tr("%1 rolls a %2 with a %3-sided die.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + QString::number(roll) + "</font>")
.arg("<font class=\"blue\">" + QString::number(sides) + "</font>"));
}
} else {
if (sides == 2) {
appendHtmlServerMessage(tr("%1 flips %2 coins. There are %3 heads and %4 tails.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + QString::number(rolls.length()) + "</font>")
.arg("<font class=\"blue\">" + QString::number(rolls.count(1)) + "</font>")
.arg("<font class=\"blue\">" + QString::number(rolls.count(2)) + "</font>"));
} else {
QStringList rollsStrings;
for (const auto &roll : rolls) {
rollsStrings.append(QString::number(roll));
}
appendHtmlServerMessage(tr("%1 rolls a %2-sided dice %3 times: %4.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + QString::number(sides) + "</font>")
.arg("<font class=\"blue\">" + QString::number(rolls.length()) + "</font>")
.arg("<font class=\"blue\">" + rollsStrings.join(", ") + "</font>"));
}
}
soundEngine->playSound("roll_dice");
}
void MessageLogWidget::logSay(Player *player, QString message)
{
appendMessage(std::move(message), {}, *player->getPlayerInfo()->getUserInfo(), true);
}
void MessageLogWidget::logSetActivePhase(int phaseNumber)
{
Phase phase = Phases::getPhase(phaseNumber);
soundEngine->playSound(phase.soundFileName);
appendHtml("<font color=\"" + phase.color + "\"><b>" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") +
phase.getName() + "</b></font>");
}
void MessageLogWidget::logSetActivePlayer(Player *player)
{
appendHtml("<br><font color=\"green\"><b>" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") +
QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "</b></font><br>");
}
void MessageLogWidget::logSetAnnotation(Player *player, CardItem *card, QString newAnnotation)
{
appendHtmlServerMessage(
QString(tr("%1 sets annotation of %2 to %3."))
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardLink(card->getName()))
.arg(QString("&quot;<font class=\"blue\">%1</font>&quot;").arg(sanitizeHtml(std::move(newAnnotation)))));
}
void MessageLogWidget::logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue)
{
QString finalStr;
int delta = abs(oldValue - value);
if (value > oldValue) {
finalStr = tr("%1 places %2 \"%3\" counter(s) on %4 (now %5).", "", delta);
} else {
finalStr = tr("%1 removes %2 \"%3\" counter(s) from %4 (now %5).", "", delta);
}
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg("<font class=\"blue\">" + QString::number(delta) + "</font>")
.arg(cardCounterSettings.displayName(counterId))
.arg(cardLink(std::move(cardName)))
.arg(value));
}
void MessageLogWidget::logSetCounter(Player *player, QString counterName, int value, int oldValue)
{
if (counterName == "life") {
soundEngine->playSound("life_change");
}
QString counterDisplayName = TranslateCounterName::getDisplayName(counterName);
appendHtmlServerMessage(tr("%1 sets counter %2 to %3 (%4%5).")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(QString("<font class=\"blue\">%1</font>").arg(sanitizeHtml(counterDisplayName)))
.arg(QString("<font class=\"blue\">%1</font>").arg(value))
.arg(value > oldValue ? "+" : "")
.arg(value - oldValue));
}
void MessageLogWidget::logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap)
{
QString str;
if (doesntUntap) {
str = tr("%1 sets %2 to not untap normally.");
} else {
str = tr("%1 sets %2 to untap normally.");
}
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName())));
}
void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT)
{
if (currentContext == MessageContext_MoveCard) {
return;
}
QString name = card->getName();
if (name.isEmpty()) {
name = QString("<font class=\"blue\">card #%1</font>").arg(sanitizeHtml(QString::number(card->getId())));
} else {
name = cardLink(name);
}
QString playerName = sanitizeHtml(player->getPlayerInfo()->getName());
if (newPT.isEmpty()) {
appendHtmlServerMessage(tr("%1 removes the PT of %2.").arg(playerName).arg(name));
} else {
QString oldPT = card->getPT();
if (oldPT.isEmpty()) {
appendHtmlServerMessage(
tr("%1 changes the PT of %2 from nothing to %4.").arg(playerName).arg(name).arg(newPT));
} else {
appendHtmlServerMessage(
tr("%1 changes the PT of %2 from %3 to %4.").arg(playerName).arg(name).arg(oldPT).arg(newPT));
}
}
}
void MessageLogWidget::logSetSideboardLock(Player *player, bool locked)
{
if (locked) {
appendHtmlServerMessage(
tr("%1 has locked their sideboard.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(
tr("%1 has unlocked their sideboard.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
}
}
void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped)
{
if (currentContext == MessageContext_MoveCard) {
return;
}
if (tapped) {
soundEngine->playSound("tap_card");
} else {
soundEngine->playSound("untap_card");
}
QString str;
if (!card) {
appendHtmlServerMessage((tapped ? tr("%1 taps their permanents.") : tr("%1 untaps their permanents."))
.arg(sanitizeHtml(player->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage((tapped ? tr("%1 taps %2.") : tr("%1 untaps %2."))
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardLink(card->getName())));
}
}
void MessageLogWidget::logShuffle(Player *player, CardZoneLogic *zone, int start, int end)
{
if (currentContext == MessageContext_Mulligan) {
return;
}
soundEngine->playSound("shuffle");
// start and end are indexes into the portion of the deck that was shuffled
// with negitive numbers counging from the bottom up.
if (start == 0 && end == -1) {
appendHtmlServerMessage(tr("%1 shuffles %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseShuffleZone)));
} else if (start < 0 && end == -1) {
appendHtmlServerMessage(tr("%1 shuffles the bottom %3 cards of %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseShuffleZone))
.arg(-start));
} else if (start < 0 && end > 0) {
appendHtmlServerMessage(tr("%1 shuffles the top %3 cards of %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseShuffleZone))
.arg(end + 1));
} else {
appendHtmlServerMessage(tr("%1 shuffles cards %3 - %4 of %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(zone->getTranslatedName(true, CaseShuffleZone))
.arg(start)
.arg(end));
}
}
void MessageLogWidget::logSpectatorSay(const ServerInfo_User &spectator, QString message)
{
appendMessage(std::move(message), {}, spectator, false);
}
void MessageLogWidget::logUnattachCard(Player *player, QString cardName)
{
appendHtmlServerMessage(tr("%1 unattaches %2.")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(cardLink(std::move(cardName))));
}
void MessageLogWidget::logUndoDraw(Player *player, QString cardName)
{
if (cardName.isEmpty()) {
appendHtmlServerMessage(tr("%1 undoes their last draw.").arg(sanitizeHtml(player->getPlayerInfo()->getName())));
} else {
appendHtmlServerMessage(
tr("%1 undoes their last draw (%2).")
.arg(sanitizeHtml(player->getPlayerInfo()->getName()))
.arg(QString("<a href=\"card://%1\">%2</a>").arg(sanitizeHtml(cardName)).arg(sanitizeHtml(cardName))));
}
}
void MessageLogWidget::setContextJudgeName(QString name)
{
messagePrefix = QString("<span style=\"color:black\">");
messageSuffix = QString("</span> [<img height=12 src=\"theme:icons/scales\"> %1]").arg(sanitizeHtml(name));
}
void MessageLogWidget::appendHtmlServerMessage(const QString &html, bool optionalIsBold, QString optionalFontColor)
{
ChatView::appendHtmlServerMessage(messagePrefix + html + messageSuffix, optionalIsBold, optionalFontColor);
}
void MessageLogWidget::connectToPlayerEventHandler(PlayerEventHandler *playerEventHandler)
{
connect(playerEventHandler, &PlayerEventHandler::logSay, this, &MessageLogWidget::logSay);
connect(playerEventHandler, &PlayerEventHandler::logShuffle, this, &MessageLogWidget::logShuffle);
connect(playerEventHandler, &PlayerEventHandler::logRollDie, this, &MessageLogWidget::logRollDie);
connect(playerEventHandler, &PlayerEventHandler::logCreateArrow, this, &MessageLogWidget::logCreateArrow);
connect(playerEventHandler, &PlayerEventHandler::logCreateToken, this, &MessageLogWidget::logCreateToken);
connect(playerEventHandler, &PlayerEventHandler::logSetCounter, this, &MessageLogWidget::logSetCounter);
connect(playerEventHandler, &PlayerEventHandler::logSetCardCounter, this, &MessageLogWidget::logSetCardCounter);
connect(playerEventHandler, &PlayerEventHandler::logSetTapped, this, &MessageLogWidget::logSetTapped);
connect(playerEventHandler, &PlayerEventHandler::logSetDoesntUntap, this, &MessageLogWidget::logSetDoesntUntap);
connect(playerEventHandler, &PlayerEventHandler::logSetPT, this, &MessageLogWidget::logSetPT);
connect(playerEventHandler, &PlayerEventHandler::logSetAnnotation, this, &MessageLogWidget::logSetAnnotation);
connect(playerEventHandler, &PlayerEventHandler::logMoveCard, this, &MessageLogWidget::logMoveCard);
connect(playerEventHandler, &PlayerEventHandler::logFlipCard, this, &MessageLogWidget::logFlipCard);
connect(playerEventHandler, &PlayerEventHandler::logDestroyCard, this, &MessageLogWidget::logDestroyCard);
connect(playerEventHandler, &PlayerEventHandler::logAttachCard, this, &MessageLogWidget::logAttachCard);
connect(playerEventHandler, &PlayerEventHandler::logUnattachCard, this, &MessageLogWidget::logUnattachCard);
connect(playerEventHandler, &PlayerEventHandler::logDumpZone, this, &MessageLogWidget::logDumpZone);
connect(playerEventHandler, &PlayerEventHandler::logDrawCards, this, &MessageLogWidget::logDrawCards);
connect(playerEventHandler, &PlayerEventHandler::logUndoDraw, this, &MessageLogWidget::logUndoDraw);
connect(playerEventHandler, &PlayerEventHandler::logRevealCards, this, &MessageLogWidget::logRevealCards);
connect(playerEventHandler, &PlayerEventHandler::logAlwaysRevealTopCard, this,
&MessageLogWidget::logAlwaysRevealTopCard);
connect(playerEventHandler, &PlayerEventHandler::logAlwaysLookAtTopCard, this,
&MessageLogWidget::logAlwaysLookAtTopCard);
}
MessageLogWidget::MessageLogWidget(TabSupervisor *_tabSupervisor, AbstractGame *_game, QWidget *parent)
: ChatView(_tabSupervisor, _game, true, parent), currentContext(MessageContext_None)
{
}

View file

@ -1,104 +0,0 @@
#ifndef MESSAGELOGWIDGET_H
#define MESSAGELOGWIDGET_H
#include "../client/translation.h"
#include "../game/zones/logic/card_zone_logic.h"
#include "chat_view/chat_view.h"
#include "user_level.h"
class AbstractGame;
class CardItem;
class GameEventContext;
class Player;
class PlayerEventHandler;
class MessageLogWidget : public ChatView
{
Q_OBJECT
private:
enum MessageContext
{
MessageContext_None,
MessageContext_MoveCard,
MessageContext_Mulligan
};
MessageContext currentContext;
QString messagePrefix, messageSuffix;
static QPair<QString, QString> getFromStr(CardZoneLogic *zone, QString cardName, int position, bool ownerChange);
public:
void connectToPlayerEventHandler(PlayerEventHandler *player);
MessageLogWidget(TabSupervisor *_tabSupervisor, AbstractGame *_game, QWidget *parent = nullptr);
public slots:
void containerProcessingDone();
void containerProcessingStarted(const GameEventContext &context);
void logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal);
void logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal);
void logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName);
void logConcede(int playerId);
void logUnconcede(int playerId);
void logConnectionStateChanged(Player *player, bool connectionState);
void logCreateArrow(Player *player,
Player *startPlayer,
QString startCard,
Player *targetPlayer,
QString targetCard,
bool playerTarget);
void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
void logDestroyCard(Player *player, QString cardName);
void logDrawCards(Player *player, int number, bool deckIsEmpty);
void logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed = false);
void logFlipCard(Player *player, QString cardName, bool faceDown);
void logGameClosed();
void logGameStart();
void logGameFlooded();
void logJoin(Player *player);
void logJoinSpectator(QString name);
void logKicked();
void logLeave(Player *player, QString reason);
void logLeaveSpectator(QString name, QString reason);
void logNotReadyStart(Player *player);
void logMoveCard(Player *player,
CardItem *card,
CardZoneLogic *startZone,
int oldX,
CardZoneLogic *targetZone,
int newX);
void logMulligan(Player *player, int number);
void logReplayStarted(int gameId);
void logReadyStart(Player *player);
void logRevealCards(Player *player,
CardZoneLogic *zone,
int cardId,
QString cardName,
Player *otherPlayer,
bool faceDown,
int amount,
bool isLentToAnotherPlayer);
void logReverseTurn(Player *player, bool reversed);
void logRollDie(Player *player, int sides, const QList<uint> &rolls);
void logSay(Player *player, QString message);
void logSetActivePhase(int phase);
void logSetActivePlayer(Player *player);
void logSetAnnotation(Player *player, CardItem *card, QString newAnnotation);
void logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue);
void logSetCounter(Player *player, QString counterName, int value, int oldValue);
void logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap);
void logSetPT(Player *player, CardItem *card, QString newPT);
void logSetSideboardLock(Player *player, bool locked);
void logSetTapped(Player *player, CardItem *card, bool tapped);
void logShuffle(Player *player, CardZoneLogic *zone, int start, int end);
void logSpectatorSay(const ServerInfo_User &spectator, QString message);
void logUnattachCard(Player *player, QString cardName);
void logUndoDraw(Player *player, QString cardName);
void setContextJudgeName(QString player);
void appendHtmlServerMessage(const QString &html,
bool optionalIsBold = false,
QString optionalFontColor = QString()) override;
};
#endif

View file

@ -1,11 +1,11 @@
#include "user_context_menu.h"
#include "../../client/tabs/tab_account.h"
#include "../../client/tabs/tab_game.h"
#include "../../client/tabs/tab_supervisor.h"
#include "../../game/game_selector.h"
#include "../../tabs/tab_account.h"
#include "../../tabs/tab_game.h"
#include "../../tabs/tab_supervisor.h"
#include "../abstract_client.h"
#include "../chat_view/chat_view.h"
#include "../game_selector.h"
#include "../pending_command.h"
#include "pb/command_kick_from_game.pb.h"
#include "pb/commands.pb.h"

View file

@ -1,10 +1,10 @@
#include "user_info_box.h"
#include "../../client/get_text_with_max.h"
#include "../../client/ui/pixel_map_generator.h"
#include "../../dialogs/dlg_edit_avatar.h"
#include "../../dialogs/dlg_edit_password.h"
#include "../../dialogs/dlg_edit_user.h"
#include "../../interface/pixel_map_generator.h"
#include "../abstract_client.h"
#include "../pending_command.h"
#include "passwordhasher.h"

View file

@ -1,11 +1,10 @@
#include "user_list_widget.h"
#include "../../client/tabs/tab_account.h"
#include "../../client/tabs/tab_supervisor.h"
#include "../../client/ui/pixel_map_generator.h"
#include "../../game/game_selector.h"
#include "../../server/user/user_list_manager.h"
#include "../../interface/pixel_map_generator.h"
#include "../../tabs/tab_account.h"
#include "../../tabs/tab_supervisor.h"
#include "../abstract_client.h"
#include "../game_selector.h"
#include "../pending_command.h"
#include "pb/moderator_commands.pb.h"
#include "pb/response_get_games_of_user.pb.h"
@ -13,6 +12,7 @@
#include "pb/session_commands.pb.h"
#include "trice_limits.h"
#include "user_context_menu.h"
#include "user_list_manager.h"
#include <QApplication>
#include <QCheckBox>