mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 17:43:23 -08:00
Merge remote-tracking branch 'upstream/master' into first-run-tutorial
This commit is contained in:
commit
f5826d9d6f
1395 changed files with 67177 additions and 10446 deletions
|
|
@ -2,11 +2,16 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/abstract_game.h"
|
||||
#include "../../../game/game_event_handler.h"
|
||||
#include "../../../game/game_meta_info.h"
|
||||
#include "../../../game/game_state.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game_graphics/board/abstract_card_item.h"
|
||||
#include "../game/game.h"
|
||||
#include "../game/player/player_logic.h"
|
||||
#include "../game/replay.h"
|
||||
#include "../game_graphics/board/arrow_item.h"
|
||||
#include "../game_graphics/board/card_item.h"
|
||||
#include "../game_graphics/deckview/deck_view_container.h"
|
||||
#include "../game_graphics/deckview/tabbed_deck_view_container.h"
|
||||
#include "../game_graphics/game_scene.h"
|
||||
|
|
@ -20,29 +25,53 @@
|
|||
#include "../interface/card_picture_loader/card_picture_loader.h"
|
||||
#include "../interface/widgets/cards/card_info_frame_widget.h"
|
||||
#include "../interface/widgets/dialogs/dlg_create_game.h"
|
||||
#include "../interface/widgets/dialogs/dlg_invite_to_game.h"
|
||||
#include "../interface/widgets/server/game_link.h"
|
||||
#include "../interface/widgets/server/user/user_list_manager.h"
|
||||
#include "../interface/widgets/utility/completer_utils.h"
|
||||
#include "../interface/widgets/utility/line_edit_completer.h"
|
||||
#include "../interface/window_main.h"
|
||||
#include "../main.h"
|
||||
#include "../menus/tearoff_menu.h"
|
||||
#include "../replay/replay_widget.h"
|
||||
#include "../utility/visibility_change_listener.h"
|
||||
#include "card/card_completer_proxy_model.h"
|
||||
#include "card/card_search_model.h"
|
||||
#include "card_database_display_model.h"
|
||||
#include "card_database_model.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
#include "libcockatrice/card/printing/exact_card.h"
|
||||
#include "libcockatrice/deck_list/deck_list.h"
|
||||
#include "libcockatrice/protocol/pb/serverinfo_game.pb.h"
|
||||
#include "libcockatrice/protocol/pb/serverinfo_playerproperties.pb.h"
|
||||
#include "libcockatrice/utility/card_ref.h"
|
||||
#include "tab_supervisor.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QCloseEvent>
|
||||
#include <QCompleter>
|
||||
#include <QDebug>
|
||||
#include <QDockWidget>
|
||||
#include <QFlags>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMapIterator>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPoint>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSharedPointer>
|
||||
#include <QStackedWidget>
|
||||
#include <QStringListModel>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/protocol/pb/event_game_joined.pb.h>
|
||||
#include <libcockatrice/protocol/pb/game_replay.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
#include <libcockatrice/settings/chat_settings.h>
|
||||
|
|
@ -50,6 +79,18 @@
|
|||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/layouts_settings.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobjectdefs.h>
|
||||
#include <utility>
|
||||
|
||||
class Event_GameJoined;
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
|
||||
: Tab(_tabSupervisor), sayLabel(nullptr), sayEdit(nullptr)
|
||||
|
|
@ -450,13 +491,20 @@ void TabGame::linkCardToChat(const QString &cardName)
|
|||
sayEdit->setFocus();
|
||||
}
|
||||
|
||||
void TabGame::resetChatAndPhase()
|
||||
void TabGame::resetForRewind()
|
||||
{
|
||||
// reset chat log
|
||||
messageLog->clearChat();
|
||||
|
||||
// reset phase markers
|
||||
game->getGameState()->setCurrentPhase(-1);
|
||||
|
||||
// reset view zones
|
||||
static_cast<GameScene *>(gameView->scene())->emptyActiveViews();
|
||||
|
||||
// reset spectator state so the replay can rebuild it from the start
|
||||
game->getPlayerManager()->clearSpectators();
|
||||
playerListWidget->clearSpectators();
|
||||
}
|
||||
|
||||
void TabGame::emitUserEvent()
|
||||
|
|
@ -490,6 +538,9 @@ void TabGame::retranslateUi()
|
|||
QString tabText = " | " + type + " #" + QString::number(game->getGameMetaInfo()->gameId());
|
||||
|
||||
updatePlayerListDockTitle();
|
||||
if (inviteButton) {
|
||||
inviteButton->setText(tr("Invite"));
|
||||
}
|
||||
cardInfoDock->setWindowTitle(tr("Card Info") + (cardInfoDock->isWindow() ? tabText : QString()));
|
||||
messageLayoutDock->setWindowTitle(tr("Messages") + (messageLayoutDock->isWindow() ? tabText : QString()));
|
||||
if (replayDock) {
|
||||
|
|
@ -528,6 +579,12 @@ void TabGame::retranslateUi()
|
|||
if (aGameInfo) {
|
||||
aGameInfo->setText(tr("Game &information"));
|
||||
}
|
||||
if (aCopyGameLink) {
|
||||
aCopyGameLink->setText(tr("Cop&y game link"));
|
||||
}
|
||||
if (aInviteToGame) {
|
||||
aInviteToGame->setText(tr("Invite to Game..."));
|
||||
}
|
||||
if (aConcede) {
|
||||
if (game->getPlayerManager()->isMainPlayerConceded()) {
|
||||
aConcede->setText(tr("Un&concede"));
|
||||
|
|
@ -695,6 +752,56 @@ void TabGame::actGameInfo()
|
|||
dlg.exec();
|
||||
}
|
||||
|
||||
void TabGame::actCopyGameLink()
|
||||
{
|
||||
const QString link =
|
||||
makeGameJoinLink(tabSupervisor->getClient()->serverName(), tabSupervisor->getClient()->serverPort(),
|
||||
game->getGameMetaInfo()->proto().room_id(), game->getGameMetaInfo()->gameId(),
|
||||
QString::fromStdString(game->getGameMetaInfo()->proto().description()));
|
||||
QApplication::clipboard()->setText(link);
|
||||
}
|
||||
|
||||
void TabGame::updateInviteButtonState()
|
||||
{
|
||||
// The dock button stays conservative (pre-start, not full); the menu action
|
||||
// additionally covers started/full games, which are legitimate spectate
|
||||
// invites, so it only needs the server-linked + not-closed conditions.
|
||||
const bool canInvite = !tabSupervisor->getIsLocalGame() && !game->getGameState()->isGameClosed() &&
|
||||
!game->getGameMetaInfo()->started() &&
|
||||
game->getPlayerManager()->getPlayerCount() < game->getGameMetaInfo()->maxPlayers();
|
||||
if (inviteButton) {
|
||||
inviteButton->setVisible(canInvite);
|
||||
}
|
||||
if (aInviteToGame) {
|
||||
aInviteToGame->setEnabled(!tabSupervisor->getIsLocalGame() && !game->getGameState()->isGameClosed());
|
||||
}
|
||||
}
|
||||
|
||||
void TabGame::actInviteToGame()
|
||||
{
|
||||
if (!tabSupervisor || tabSupervisor->getIsLocalGame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameMetaInfo *metaInfo = game->getGameMetaInfo();
|
||||
const QString inviteUrl = makeGameJoinLink(
|
||||
tabSupervisor->getClient()->serverName(), tabSupervisor->getClient()->serverPort(), metaInfo->proto().room_id(),
|
||||
metaInfo->gameId(), QString::fromStdString(metaInfo->proto().description()));
|
||||
|
||||
QStringList excludeUserNames;
|
||||
excludeUserNames << tabSupervisor->getUserListManager()->getOwnUsername();
|
||||
for (auto player : game->getPlayerManager()->getPlayers()) {
|
||||
excludeUserNames << player->getPlayerInfo()->getName();
|
||||
}
|
||||
for (auto it = game->getPlayerManager()->getSpectators().cbegin();
|
||||
it != game->getPlayerManager()->getSpectators().cend(); ++it) {
|
||||
excludeUserNames << QString::fromStdString(it.value().name());
|
||||
}
|
||||
|
||||
DlgInviteToGame dlg(tabSupervisor, inviteUrl, metaInfo->proto().only_buddies(), excludeUserNames, this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void TabGame::actConcede()
|
||||
{
|
||||
PlayerLogic *player = game->getPlayerManager()->getActiveLocalPlayer(game->getGameState()->getActivePlayer());
|
||||
|
|
@ -742,7 +849,8 @@ bool TabGame::leaveGame()
|
|||
|
||||
void TabGame::actSay()
|
||||
{
|
||||
if (completer->popup()->isVisible()) {
|
||||
if (sayEdit->hasVisibleCompleterPopup()) {
|
||||
sayEdit->hideCompleterPopups();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -762,14 +870,14 @@ void TabGame::addPlayerToAutoCompleteList(QString playerName)
|
|||
{
|
||||
if (sayEdit && !autocompleteUserList.contains(playerName)) {
|
||||
autocompleteUserList << playerName;
|
||||
sayEdit->setCompletionList(autocompleteUserList);
|
||||
mentionModel->setStringList(autocompleteUserList);
|
||||
}
|
||||
}
|
||||
|
||||
void TabGame::removePlayerFromAutoCompleteList(QString playerName)
|
||||
{
|
||||
if (sayEdit && autocompleteUserList.removeOne(playerName)) {
|
||||
sayEdit->setCompletionList(autocompleteUserList);
|
||||
mentionModel->setStringList(autocompleteUserList);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -832,8 +940,8 @@ void TabGame::actRotateViewCCW()
|
|||
|
||||
void TabGame::actCompleterChanged()
|
||||
{
|
||||
SettingsCache::instance().chat().getChatMentionCompleter() ? completer->setCompletionRole(2)
|
||||
: completer->setCompletionRole(1);
|
||||
SettingsCache::instance().chat().getChatMentionCompleter() ? mentionCompleter->setCompletionRole(2)
|
||||
: mentionCompleter->setCompletionRole(1);
|
||||
}
|
||||
|
||||
void TabGame::notifyPlayerJoin(QString playerName)
|
||||
|
|
@ -1045,6 +1153,7 @@ void TabGame::stopGame()
|
|||
QMapIterator<int, TabbedDeckViewContainer *> i(deckViewContainers);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
i.value()->playerDeckView->advancePlaymatRotation();
|
||||
i.value()->show();
|
||||
}
|
||||
|
||||
|
|
@ -1185,6 +1294,11 @@ void TabGame::createMenuItems()
|
|||
connect(aRotateViewCCW, &QAction::triggered, this, &TabGame::actRotateViewCCW);
|
||||
aGameInfo = new QAction(this);
|
||||
connect(aGameInfo, &QAction::triggered, this, &TabGame::actGameInfo);
|
||||
aCopyGameLink = new QAction(this);
|
||||
aCopyGameLink->setEnabled(!tabSupervisor->getIsLocalGame() && !tabSupervisor->getClient()->serverName().isEmpty());
|
||||
connect(aCopyGameLink, &QAction::triggered, this, &TabGame::actCopyGameLink);
|
||||
aInviteToGame = new QAction(this);
|
||||
connect(aInviteToGame, &QAction::triggered, this, &TabGame::actInviteToGame);
|
||||
aConcede = new QAction(this);
|
||||
connect(aConcede, &QAction::triggered, this, &TabGame::actConcede);
|
||||
if (!game->getGameMetaInfo()->started()) {
|
||||
|
|
@ -1223,6 +1337,8 @@ void TabGame::createMenuItems()
|
|||
gameMenu->addAction(aRotateViewCCW);
|
||||
gameMenu->addSeparator();
|
||||
gameMenu->addAction(aGameInfo);
|
||||
gameMenu->addAction(aCopyGameLink);
|
||||
gameMenu->addAction(aInviteToGame);
|
||||
gameMenu->addAction(aConcede);
|
||||
gameMenu->addAction(aFocusChat);
|
||||
gameMenu->addAction(aLeaveGame);
|
||||
|
|
@ -1231,6 +1347,9 @@ void TabGame::createMenuItems()
|
|||
|
||||
aCardMenu = gameMenu->addMenu(new QMenu(this));
|
||||
|
||||
// Sync the new action with the same state the dock button already shows.
|
||||
updateInviteButtonState();
|
||||
|
||||
addTabMenu(gameMenu);
|
||||
}
|
||||
|
||||
|
|
@ -1245,6 +1364,8 @@ void TabGame::createReplayMenuItems()
|
|||
aRotateViewCCW = nullptr;
|
||||
aResetLayout = nullptr;
|
||||
aGameInfo = nullptr;
|
||||
aCopyGameLink = nullptr;
|
||||
aInviteToGame = nullptr;
|
||||
aConcede = nullptr;
|
||||
aFocusChat = nullptr;
|
||||
aLeaveGame = new QAction(this);
|
||||
|
|
@ -1387,7 +1508,7 @@ void TabGame::createReplayDock(GameReplay *replay)
|
|||
replayDock->setWidget(replayWidget);
|
||||
replayDock->setFloating(false);
|
||||
|
||||
connect(replayWidget, &ReplayWidget::rewound, this, &TabGame::resetChatAndPhase);
|
||||
connect(replayWidget, &ReplayWidget::rewound, this, &TabGame::resetForRewind);
|
||||
connect(replayWidget, &ReplayWidget::eventReplayed, game->getGameEventHandler(),
|
||||
[this](const auto &event, auto options) {
|
||||
game->getGameEventHandler()->processGameEventContainer(event, nullptr, options);
|
||||
|
|
@ -1443,11 +1564,29 @@ void TabGame::createPlayerListDock(bool bReplay)
|
|||
}
|
||||
playerListWidget->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
auto *playerListBox = new QWidget(this);
|
||||
auto *vbox = new QVBoxLayout(playerListBox);
|
||||
vbox->setContentsMargins(0, 0, 0, 0);
|
||||
vbox->setSpacing(0);
|
||||
|
||||
vbox->addWidget(playerListWidget);
|
||||
|
||||
if (!bReplay) {
|
||||
inviteButton = new QPushButton(tr("Invite"), playerListBox);
|
||||
inviteButton->setVisible(false);
|
||||
connect(inviteButton, &QPushButton::clicked, this, &TabGame::actInviteToGame);
|
||||
vbox->addWidget(inviteButton);
|
||||
|
||||
connect(game->getGameMetaInfo(), &GameMetaInfo::startedChanged, this, &TabGame::updateInviteButtonState);
|
||||
connect(game->getPlayerManager(), &PlayerManager::playerCountChanged, this, &TabGame::updateInviteButtonState);
|
||||
updateInviteButtonState();
|
||||
}
|
||||
|
||||
playerListDock = new QDockWidget(this);
|
||||
playerListDock->setObjectName("playerListDock");
|
||||
playerListDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
|
||||
QDockWidget::DockWidgetMovable);
|
||||
playerListDock->setWidget(playerListWidget);
|
||||
playerListDock->setWidget(playerListBox);
|
||||
playerListDock->setFloating(false);
|
||||
}
|
||||
|
||||
|
|
@ -1471,6 +1610,7 @@ void TabGame::createMessageDock(bool bReplay)
|
|||
qOverload<const QString &>(&CardInfoFrameWidget::setCard));
|
||||
connect(messageLog, &MessageLogWidget::showCardInfoPopup, this, &TabGame::showCardInfoPopup);
|
||||
connect(messageLog, &MessageLogWidget::deleteCardInfoPopup, this, &TabGame::deleteCardInfoPopup);
|
||||
connect(messageLog, &MessageLogWidget::cockatriceLinkActivated, this, &TabGame::cockatriceLinkActivated);
|
||||
|
||||
if (!bReplay) {
|
||||
connect(messageLog, &MessageLogWidget::openMessageDialog, this, &TabGame::openMessageDialog);
|
||||
|
|
@ -1488,12 +1628,25 @@ void TabGame::createMessageDock(bool bReplay)
|
|||
sayEdit->setMaxLength(MAX_TEXT_LENGTH);
|
||||
sayLabel->setBuddy(sayEdit);
|
||||
connect(this, &TabGame::chatMessageSent, game->getGameEventHandler(), &GameEventHandler::handleChatMessageSent);
|
||||
completer = new QCompleter(autocompleteUserList, sayEdit);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setMaxVisibleItems(5);
|
||||
completer->setFilterMode(Qt::MatchStartsWith);
|
||||
mentionModel = new QStringListModel(autocompleteUserList, sayEdit);
|
||||
mentionCompleter = createMentionCompleter(mentionModel, sayEdit);
|
||||
sayEdit->addCompleter(mentionCompleter, CompleterTrigger::Mention);
|
||||
|
||||
auto *cardDatabaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), false, sayEdit);
|
||||
auto *displayModel = new CardDatabaseDisplayModel(sayEdit);
|
||||
displayModel->setSourceModel(cardDatabaseModel);
|
||||
const CardCompleterSetup cardSetup = createCardCompleter(displayModel, sayEdit);
|
||||
sayEdit->addCompleter(cardSetup.completer, CompleterTrigger::Card);
|
||||
|
||||
connect(sayEdit, &LineEditCompleter::cardPartialChanged, this, [this, cardSetup](const QString &text) {
|
||||
cardSetup.searchModel->updateSearchResults(text);
|
||||
cardSetup.proxyModel->setFilterRegularExpression(
|
||||
QRegularExpression(QRegularExpression::escape(text), QRegularExpression::CaseInsensitiveOption));
|
||||
if (sayEdit->hasFocus()) {
|
||||
cardSetup.completer->complete();
|
||||
}
|
||||
});
|
||||
|
||||
sayEdit->setCompleter(completer);
|
||||
actCompleterChanged();
|
||||
|
||||
if (game->getPlayerManager()->isSpectator()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue