Cockatrice/cockatrice/src/tab_game.cpp
ebbit1q 06081bd940 add keybinds to mill cards (#3439)
* add keybinds to mill cards

Add functions to move single card from top of deck to the graveyard and
exile.
Add keybinds to move single or multiple cards from top of deck to exile
or graveyard.
Add new keybinds to settings menu.
Move settings menu items around for a better fit.
Rename a few of the items in the settings menu.
Add default keybinds: ctrl alt d/e for single/multiple cards from top of
deck to the graveyard. No defaults are set for moving to exile.

* fix shortcut menu

* fix missing tag

* rename mismatched functions

* fixed your typos

* refactor shortcutsettings

correct a lot of typos
optimize a lot of functions
this could merit a pr on its own

* set mill keybinds

* refactor add related card actions

I found a function that was completely unintelligible so I made it
remotely legible and removed the duplication.

* shorten line by 17 characters

replace a lot of function calls with just a single reference

* add brackets

add brackets to all single line if statements etc.
readability improvements
2018-11-23 22:21:20 -05:00

1837 lines
64 KiB
C++

#include <QAction>
#include <QCompleter>
#include <QDebug>
#include <QDockWidget>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QSplitter>
#include <QStackedWidget>
#include <QTimer>
#include <QToolButton>
#include <QWidget>
#include "abstractclient.h"
#include "arrowitem.h"
#include "carddatabase.h"
#include "cardframe.h"
#include "carditem.h"
#include "deck_loader.h"
#include "deckview.h"
#include "dlg_creategame.h"
#include "dlg_load_remote_deck.h"
#include "gamescene.h"
#include "gameview.h"
#include "lineeditcompleter.h"
#include "main.h"
#include "messagelogwidget.h"
#include "phasestoolbar.h"
#include "pictureloader.h"
#include "player.h"
#include "playerlistwidget.h"
#include "replay_timeline_widget.h"
#include "settingscache.h"
#include "tab_game.h"
#include "tab_supervisor.h"
#include "window_sets.h"
#include "zoneviewwidget.h"
#include "zoneviewzone.h"
#include "get_pb_extension.h"
#include "pb/command_concede.pb.h"
#include "pb/command_deck_select.pb.h"
#include "pb/command_delete_arrow.pb.h"
#include "pb/command_game_say.pb.h"
#include "pb/command_leave_game.pb.h"
#include "pb/command_next_turn.pb.h"
#include "pb/command_ready_start.pb.h"
#include "pb/command_set_active_phase.pb.h"
#include "pb/command_set_sideboard_lock.pb.h"
#include "pb/command_set_sideboard_plan.pb.h"
#include "pb/context_connection_state_changed.pb.h"
#include "pb/context_deck_select.pb.h"
#include "pb/context_ping_changed.pb.h"
#include "pb/event_game_closed.pb.h"
#include "pb/event_game_host_changed.pb.h"
#include "pb/event_game_joined.pb.h"
#include "pb/event_game_say.pb.h"
#include "pb/event_game_state_changed.pb.h"
#include "pb/event_join.pb.h"
#include "pb/event_kicked.pb.h"
#include "pb/event_leave.pb.h"
#include "pb/event_player_properties_changed.pb.h"
#include "pb/event_set_active_phase.pb.h"
#include "pb/event_set_active_player.pb.h"
#include "pb/game_event_container.pb.h"
#include "pb/game_replay.pb.h"
#include "pb/response_deck_download.pb.h"
#include "pending_command.h"
#include <google/protobuf/descriptor.h>
ToggleButton::ToggleButton(QWidget *parent) : QPushButton(parent), state(false)
{
}
void ToggleButton::paintEvent(QPaintEvent *event)
{
QPushButton::paintEvent(event);
QPainter painter(this);
QPen pen;
pen.setWidth(3);
pen.setJoinStyle(Qt::MiterJoin);
pen.setColor(state ? Qt::green : Qt::red);
painter.setPen(pen);
painter.drawRect(QRect(1, 1, width() - 3, height() - 3));
}
void ToggleButton::setState(bool _state)
{
state = _state;
emit stateChanged();
update();
}
DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent)
: QWidget(0), parentGame(parent), playerId(_playerId)
{
loadLocalButton = new QPushButton;
loadRemoteButton = new QPushButton;
if (parentGame->getIsLocalGame())
loadRemoteButton->setEnabled(false);
readyStartButton = new ToggleButton;
readyStartButton->setEnabled(false);
sideboardLockButton = new ToggleButton;
sideboardLockButton->setEnabled(false);
connect(loadLocalButton, SIGNAL(clicked()), this, SLOT(loadLocalDeck()));
connect(loadRemoteButton, SIGNAL(clicked()), this, SLOT(loadRemoteDeck()));
connect(readyStartButton, SIGNAL(clicked()), this, SLOT(readyStart()));
connect(sideboardLockButton, SIGNAL(clicked()), this, SLOT(sideboardLockButtonClicked()));
connect(sideboardLockButton, SIGNAL(stateChanged()), this, SLOT(updateSideboardLockButtonText()));
QHBoxLayout *buttonHBox = new QHBoxLayout;
buttonHBox->addWidget(loadLocalButton);
buttonHBox->addWidget(loadRemoteButton);
buttonHBox->addWidget(readyStartButton);
buttonHBox->addWidget(sideboardLockButton);
buttonHBox->setContentsMargins(0, 0, 0, 0);
buttonHBox->addStretch();
deckView = new DeckView;
connect(deckView, SIGNAL(newCardAdded(AbstractCardItem *)), this, SIGNAL(newCardAdded(AbstractCardItem *)));
connect(deckView, SIGNAL(sideboardPlanChanged()), this, SLOT(sideboardPlanChanged()));
QVBoxLayout *deckViewLayout = new QVBoxLayout;
deckViewLayout->addLayout(buttonHBox);
deckViewLayout->addWidget(deckView);
deckViewLayout->setContentsMargins(0, 0, 0, 0);
setLayout(deckViewLayout);
retranslateUi();
connect(&settingsCache->shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
refreshShortcuts();
}
void DeckViewContainer::retranslateUi()
{
loadLocalButton->setText(tr("Load deck..."));
loadRemoteButton->setText(tr("Load remote deck..."));
readyStartButton->setText(tr("Ready to s&tart"));
updateSideboardLockButtonText();
}
void DeckViewContainer::setButtonsVisible(bool _visible)
{
loadLocalButton->setVisible(_visible);
loadRemoteButton->setVisible(_visible);
readyStartButton->setVisible(_visible);
sideboardLockButton->setVisible(_visible);
}
void DeckViewContainer::updateSideboardLockButtonText()
{
if (sideboardLockButton->getState())
sideboardLockButton->setText(tr("S&ideboard unlocked"));
else
sideboardLockButton->setText(tr("S&ideboard locked"));
}
void DeckViewContainer::refreshShortcuts()
{
loadLocalButton->setShortcut(settingsCache->shortcuts().getSingleShortcut("DeckViewContainer/loadLocalButton"));
loadRemoteButton->setShortcut(settingsCache->shortcuts().getSingleShortcut("DeckViewContainer/loadRemoteButton"));
}
void TabGame::refreshShortcuts()
{
for (int i = 0; i < phaseActions.size(); ++i) {
QAction *temp = phaseActions.at(i);
switch (i) {
case 0:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase0"));
break;
case 1:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase1"));
break;
case 2:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase2"));
break;
case 3:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase3"));
break;
case 4:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase4"));
break;
case 5:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase5"));
break;
case 6:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase6"));
break;
case 7:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase7"));
break;
case 8:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase8"));
break;
case 9:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase9"));
break;
case 10:
temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase10"));
break;
default:;
}
}
if (aNextPhase) {
aNextPhase->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aNextPhase"));
}
if (aNextTurn) {
aNextTurn->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aNextTurn"));
}
if (aRemoveLocalArrows) {
aRemoveLocalArrows->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aRemoveLocalArrows"));
}
if (aRotateViewCW) {
aRotateViewCW->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aRotateViewCW"));
}
if (aRotateViewCCW) {
aRotateViewCCW->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aRotateViewCCW"));
}
if (aConcede) {
aConcede->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aConcede"));
}
if (aLeaveGame) {
aLeaveGame->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aLeaveGame"));
}
if (aCloseReplay) {
aCloseReplay->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aCloseReplay"));
}
if (aResetLayout) {
aResetLayout->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aResetLayout"));
}
}
void DeckViewContainer::loadLocalDeck()
{
QFileDialog dialog(this, tr("Load deck"));
dialog.setDirectory(settingsCache->getDeckPath());
dialog.setNameFilters(DeckLoader::fileNameFilters);
if (!dialog.exec())
return;
QString fileName = dialog.selectedFiles().at(0);
DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName);
DeckLoader deck;
if (!deck.loadFromFile(fileName, fmt)) {
QMessageBox::critical(this, tr("Error"), tr("The selected file could not be loaded."));
return;
}
Command_DeckSelect cmd;
cmd.set_deck(deck.writeToString_Native().toStdString());
PendingCommand *pend = parentGame->prepareGameCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(deckSelectFinished(const Response &)));
parentGame->sendGameCommand(pend, playerId);
}
void DeckViewContainer::loadRemoteDeck()
{
DlgLoadRemoteDeck dlg(parentGame->getClientForPlayer(playerId));
if (dlg.exec()) {
Command_DeckSelect cmd;
cmd.set_deck_id(dlg.getDeckId());
PendingCommand *pend = parentGame->prepareGameCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(deckSelectFinished(const Response &)));
parentGame->sendGameCommand(pend, playerId);
}
}
void DeckViewContainer::deckSelectFinished(const Response &r)
{
const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
DeckLoader newDeck(QString::fromStdString(resp.deck()));
PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList()));
setDeck(newDeck);
}
void DeckViewContainer::readyStart()
{
Command_ReadyStart cmd;
cmd.set_ready(!readyStartButton->getState());
parentGame->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::sideboardLockButtonClicked()
{
Command_SetSideboardLock cmd;
cmd.set_locked(sideboardLockButton->getState());
parentGame->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::sideboardPlanChanged()
{
Command_SetSideboardPlan cmd;
const QList<MoveCard_ToZone> &newPlan = deckView->getSideboardPlan();
for (int i = 0; i < newPlan.size(); ++i)
cmd.add_move_list()->CopyFrom(newPlan.at(i));
parentGame->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::setReadyStart(bool ready)
{
readyStartButton->setState(ready);
deckView->setLocked(ready || !sideboardLockButton->getState());
sideboardLockButton->setEnabled(!readyStartButton->getState() && readyStartButton->isEnabled());
}
void DeckViewContainer::setSideboardLocked(bool locked)
{
sideboardLockButton->setState(!locked);
deckView->setLocked(readyStartButton->getState() || !sideboardLockButton->getState());
if (locked)
deckView->resetSideboardPlan();
}
void DeckViewContainer::setDeck(const DeckLoader &deck)
{
deckView->setDeck(deck);
readyStartButton->setEnabled(true);
sideboardLockButton->setState(false);
sideboardLockButton->setEnabled(true);
}
TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
: Tab(_tabSupervisor), secondsElapsed(0), hostId(-1), localPlayerId(-1),
isLocalGame(_tabSupervisor->getIsLocalGame()), spectator(true), gameStateKnown(false), resuming(false),
currentPhase(-1), activeCard(0), gameClosed(false), replay(_replay), currentReplayStep(0), sayLabel(0), sayEdit(0)
{
// THIS CTOR IS USED ON REPLAY
gameInfo.CopyFrom(replay->game_info());
gameInfo.set_spectators_omniscient(true);
// Create list: event number -> time [ms]
// Distribute simultaneous events evenly across 1 second.
unsigned int lastEventTimestamp = 0;
const int eventCount = replay->event_list_size();
for (int i = 0; i < eventCount; ++i) {
int j = i + 1;
while ((j < eventCount) && (replay->event_list(j).seconds_elapsed() == lastEventTimestamp))
++j;
const int numberEventsThisSecond = j - i;
for (int k = 0; k < numberEventsThisSecond; ++k)
replayTimeline.append(replay->event_list(i + k).seconds_elapsed() * 1000 +
(int)((qreal)k / (qreal)numberEventsThisSecond * 1000));
if (j < eventCount)
lastEventTimestamp = replay->event_list(j).seconds_elapsed();
i += numberEventsThisSecond - 1;
}
createCardInfoDock(true);
createPlayerListDock(true);
createMessageDock(true);
createPlayAreaWidget(true);
createDeckViewContainerWidget(true);
createReplayDock();
addDockWidget(Qt::RightDockWidgetArea, cardInfoDock);
addDockWidget(Qt::RightDockWidgetArea, playerListDock);
addDockWidget(Qt::RightDockWidgetArea, messageLayoutDock);
addDockWidget(Qt::BottomDockWidgetArea, replayDock);
mainWidget = new QStackedWidget(this);
mainWidget->addWidget(deckViewContainerWidget);
mainWidget->addWidget(gamePlayAreaWidget);
setCentralWidget(mainWidget);
createReplayMenuItems();
createViewMenuItems();
retranslateUi();
connect(&settingsCache->shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
refreshShortcuts();
messageLog->logReplayStarted(gameInfo.game_id());
this->installEventFilter(this);
QTimer::singleShot(0, this, SLOT(loadLayout()));
}
TabGame::TabGame(TabSupervisor *_tabSupervisor,
QList<AbstractClient *> &_clients,
const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes)
: Tab(_tabSupervisor), clients(_clients), gameInfo(event.game_info()), roomGameTypes(_roomGameTypes),
hostId(event.host_id()), localPlayerId(event.player_id()), isLocalGame(_tabSupervisor->getIsLocalGame()),
spectator(event.spectator()), gameStateKnown(false), resuming(event.resuming()), currentPhase(-1), activeCard(0),
gameClosed(false), replay(0), replayDock(0)
{
// THIS CTOR IS USED ON GAMES
gameInfo.set_started(false);
createCardInfoDock();
createPlayerListDock();
createMessageDock();
createPlayAreaWidget();
createDeckViewContainerWidget();
addDockWidget(Qt::RightDockWidgetArea, cardInfoDock);
addDockWidget(Qt::RightDockWidgetArea, playerListDock);
addDockWidget(Qt::RightDockWidgetArea, messageLayoutDock);
mainWidget = new QStackedWidget(this);
mainWidget->addWidget(deckViewContainerWidget);
mainWidget->addWidget(gamePlayAreaWidget);
mainWidget->setContentsMargins(0, 0, 0, 0);
setCentralWidget(mainWidget);
createMenuItems();
createViewMenuItems();
retranslateUi();
connect(&settingsCache->shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
refreshShortcuts();
// append game to rooms game list for others to see
for (int i = gameInfo.game_types_size() - 1; i >= 0; i--)
gameTypes.append(roomGameTypes.find(gameInfo.game_types(i)).value());
this->installEventFilter(this);
QTimer::singleShot(0, this, SLOT(loadLayout()));
}
void TabGame::addMentionTag(QString value)
{
sayEdit->insert(value + " ");
sayEdit->setFocus();
}
void TabGame::linkCardToChat(QString cardName)
{
sayEdit->insert("[[" + cardName + "]] ");
sayEdit->setFocus();
}
void TabGame::emitUserEvent()
{
bool globalEvent = !spectator || settingsCache->getSpectatorNotificationsEnabled();
emit userEvent(globalEvent);
updatePlayerListDockTitle();
}
TabGame::~TabGame()
{
delete replay;
QMapIterator<int, Player *> i(players);
while (i.hasNext()) {
delete i.next().value();
}
players.clear();
emit gameClosing(this);
}
void TabGame::updatePlayerListDockTitle()
{
QString tabText = " | " + (replay ? tr("Replay") : tr("Game")) + " #" + QString::number(gameInfo.game_id());
QString userCountInfo = QString(" %1/%2").arg(players.size()).arg(gameInfo.max_players());
playerListDock->setWindowTitle(tr("Player List") + userCountInfo +
(playerListDock->isWindow() ? tabText : QString()));
}
void TabGame::retranslateUi()
{
QString tabText = " | " + (replay ? tr("Replay") : tr("Game")) + " #" + QString::number(gameInfo.game_id());
updatePlayerListDockTitle();
cardInfoDock->setWindowTitle(tr("Card Info") + (cardInfoDock->isWindow() ? tabText : QString()));
messageLayoutDock->setWindowTitle(tr("Messages") + (messageLayoutDock->isWindow() ? tabText : QString()));
if (replayDock)
replayDock->setWindowTitle(tr("Replay Timeline") + (replayDock->isWindow() ? tabText : QString()));
if (phasesMenu) {
for (int i = 0; i < phaseActions.size(); ++i)
phaseActions[i]->setText(phasesToolbar->getLongPhaseName(i));
phasesMenu->setTitle(tr("&Phases"));
}
gameMenu->setTitle(tr("&Game"));
if (aNextPhase) {
aNextPhase->setText(tr("Next &phase"));
}
if (aNextTurn) {
aNextTurn->setText(tr("Next &turn"));
}
if (aRemoveLocalArrows) {
aRemoveLocalArrows->setText(tr("&Remove all local arrows"));
}
if (aRotateViewCW) {
aRotateViewCW->setText(tr("Rotate View Cl&ockwise"));
}
if (aRotateViewCCW) {
aRotateViewCCW->setText(tr("Rotate View Co&unterclockwise"));
}
if (aGameInfo)
aGameInfo->setText(tr("Game &information"));
if (aConcede) {
aConcede->setText(tr("&Concede"));
}
if (aLeaveGame) {
aLeaveGame->setText(tr("&Leave game"));
}
if (aCloseReplay) {
aCloseReplay->setText(tr("C&lose replay"));
}
if (sayLabel) {
sayLabel->setText(tr("&Say:"));
}
viewMenu->setTitle(tr("&View"));
cardInfoDockMenu->setTitle(tr("Card Info"));
messageLayoutDockMenu->setTitle(tr("Messages"));
playerListDockMenu->setTitle(tr("Player List"));
aCardInfoDockVisible->setText(tr("Visible"));
aCardInfoDockFloating->setText(tr("Floating"));
aMessageLayoutDockVisible->setText(tr("Visible"));
aMessageLayoutDockFloating->setText(tr("Floating"));
aPlayerListDockVisible->setText(tr("Visible"));
aPlayerListDockFloating->setText(tr("Floating"));
if (replayDock) {
replayDockMenu->setTitle(tr("Replay Timeline"));
aReplayDockVisible->setText(tr("Visible"));
aReplayDockFloating->setText(tr("Floating"));
}
aResetLayout->setText(tr("Reset layout"));
cardInfo->retranslateUi();
QMapIterator<int, Player *> i(players);
while (i.hasNext())
i.next().value()->retranslateUi();
QMapIterator<int, DeckViewContainer *> j(deckViewContainers);
while (j.hasNext())
j.next().value()->retranslateUi();
scene->retranslateUi();
}
void TabGame::closeRequest()
{
actLeaveGame();
}
void TabGame::replayNextEvent()
{
processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), 0);
}
void TabGame::replayFinished()
{
replayStartButton->setEnabled(true);
replayPauseButton->setEnabled(false);
replayFastForwardButton->setEnabled(false);
}
void TabGame::replayStartButtonClicked()
{
replayStartButton->setEnabled(false);
replayPauseButton->setEnabled(true);
replayFastForwardButton->setEnabled(true);
timelineWidget->startReplay();
}
void TabGame::replayPauseButtonClicked()
{
replayStartButton->setEnabled(true);
replayPauseButton->setEnabled(false);
replayFastForwardButton->setEnabled(false);
timelineWidget->stopReplay();
}
void TabGame::replayFastForwardButtonToggled(bool checked)
{
timelineWidget->setTimeScaleFactor(checked ? 10.0 : 1.0);
}
void TabGame::incrementGameTime()
{
int seconds = ++secondsElapsed;
int minutes = seconds / 60;
seconds -= minutes * 60;
int hours = minutes / 60;
minutes -= hours * 60;
timeElapsedLabel->setText(QString::number(hours).rightJustified(2, '0') + ":" +
QString::number(minutes).rightJustified(2, '0') + ":" +
QString::number(seconds).rightJustified(2, '0'));
}
void TabGame::adminLockChanged(bool lock)
{
bool v = !(spectator && !gameInfo.spectators_can_chat() && lock);
sayLabel->setVisible(v);
sayEdit->setVisible(v);
}
bool TabGame::isSpectator()
{
return spectator;
}
void TabGame::actGameInfo()
{
DlgCreateGame dlg(gameInfo, roomGameTypes);
dlg.exec();
}
void TabGame::actConcede()
{
if (QMessageBox::question(this, tr("Concede"), tr("Are you sure you want to concede this game?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
return;
sendGameCommand(Command_Concede());
}
void TabGame::actLeaveGame()
{
if (!gameClosed) {
if (!spectator)
if (QMessageBox::question(this, tr("Leave game"), tr("Are you sure you want to leave this game?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
return;
if (!replay)
sendGameCommand(Command_LeaveGame());
}
scene->clearViews();
deleteLater();
}
void TabGame::actSay()
{
if (completer->popup()->isVisible())
return;
if (!sayEdit->text().isEmpty()) {
Command_GameSay cmd;
cmd.set_message(sayEdit->text().toStdString());
sendGameCommand(cmd);
sayEdit->clear();
}
}
void TabGame::actPhaseAction()
{
int phase = phaseActions.indexOf(static_cast<QAction *>(sender()));
Command_SetActivePhase cmd;
cmd.set_phase(phase);
sendGameCommand(cmd);
}
void TabGame::actNextPhase()
{
int phase = currentPhase;
if (++phase >= phasesToolbar->phaseCount())
phase = 0;
Command_SetActivePhase cmd;
cmd.set_phase(phase);
sendGameCommand(cmd);
}
void TabGame::actNextTurn()
{
sendGameCommand(Command_NextTurn());
}
void TabGame::actRemoveLocalArrows()
{
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext()) {
Player *player = playerIterator.next().value();
if (!player->getLocal())
continue;
QMapIterator<int, ArrowItem *> arrowIterator(player->getArrows());
while (arrowIterator.hasNext()) {
ArrowItem *a = arrowIterator.next().value();
Command_DeleteArrow cmd;
cmd.set_arrow_id(a->getId());
sendGameCommand(cmd);
}
}
}
void TabGame::actRotateViewCW()
{
scene->adjustPlayerRotation(-1);
}
void TabGame::actRotateViewCCW()
{
scene->adjustPlayerRotation(1);
}
void TabGame::actCompleterChanged()
{
settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1);
}
Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info)
{
bool local = ((clients.size() > 1) || (playerId == localPlayerId));
Player *newPlayer = new Player(info, playerId, local, this);
connect(newPlayer, SIGNAL(openDeckEditor(const DeckLoader *)), this, SIGNAL(openDeckEditor(const DeckLoader *)));
QString newPlayerName = "@" + newPlayer->getName();
if (sayEdit && !autocompleteUserList.contains(newPlayerName)) {
autocompleteUserList << newPlayerName;
sayEdit->setCompletionList(autocompleteUserList);
}
scene->addPlayer(newPlayer);
connect(newPlayer, SIGNAL(newCardAdded(AbstractCardItem *)), this, SLOT(newCardAdded(AbstractCardItem *)));
messageLog->connectToPlayer(newPlayer);
if (local && !spectator) {
if (clients.size() == 1)
newPlayer->setShortcutsActive();
DeckViewContainer *deckView = new DeckViewContainer(playerId, this);
connect(deckView, SIGNAL(newCardAdded(AbstractCardItem *)), this, SLOT(newCardAdded(AbstractCardItem *)));
deckViewContainers.insert(playerId, deckView);
deckViewContainerLayout->addWidget(deckView);
}
gameMenu->insertMenu(playersSeparator, newPlayer->getPlayerMenu());
players.insert(playerId, newPlayer);
emit playerAdded(newPlayer);
return newPlayer;
}
void TabGame::processGameEventContainer(const GameEventContainer &cont, AbstractClient *client)
{
const GameEventContext &context = cont.context();
messageLog->containerProcessingStarted(context);
const int eventListSize = cont.event_list_size();
for (int i = 0; i < eventListSize; ++i) {
const GameEvent &event = cont.event_list(i);
const int playerId = event.player_id();
const GameEvent::GameEventType eventType = static_cast<GameEvent::GameEventType>(getPbExtension(event));
if (spectators.contains(playerId)) {
switch (eventType) {
case GameEvent::GAME_SAY:
eventSpectatorSay(event.GetExtension(Event_GameSay::ext), playerId, context);
break;
case GameEvent::LEAVE:
eventSpectatorLeave(event.GetExtension(Event_Leave::ext), playerId, context);
break;
default: {
qDebug() << "unhandled spectator game event" << eventType;
break;
}
}
} else {
if ((clients.size() > 1) && (playerId != -1))
if (clients.at(playerId) != client)
continue;
switch (eventType) {
case GameEvent::GAME_STATE_CHANGED:
eventGameStateChanged(event.GetExtension(Event_GameStateChanged::ext), playerId, context);
break;
case GameEvent::PLAYER_PROPERTIES_CHANGED:
eventPlayerPropertiesChanged(event.GetExtension(Event_PlayerPropertiesChanged::ext), playerId,
context);
break;
case GameEvent::JOIN:
eventJoin(event.GetExtension(Event_Join::ext), playerId, context);
break;
case GameEvent::LEAVE:
eventLeave(event.GetExtension(Event_Leave::ext), playerId, context);
break;
case GameEvent::KICKED:
eventKicked(event.GetExtension(Event_Kicked::ext), playerId, context);
break;
case GameEvent::GAME_HOST_CHANGED:
eventGameHostChanged(event.GetExtension(Event_GameHostChanged::ext), playerId, context);
break;
case GameEvent::GAME_CLOSED:
eventGameClosed(event.GetExtension(Event_GameClosed::ext), playerId, context);
break;
case GameEvent::SET_ACTIVE_PLAYER:
eventSetActivePlayer(event.GetExtension(Event_SetActivePlayer::ext), playerId, context);
break;
case GameEvent::SET_ACTIVE_PHASE:
eventSetActivePhase(event.GetExtension(Event_SetActivePhase::ext), playerId, context);
break;
default: {
Player *player = players.value(playerId, 0);
if (!player) {
qDebug() << "unhandled game event: invalid player id";
break;
}
player->processGameEvent(eventType, event, context);
emitUserEvent();
}
}
}
}
messageLog->containerProcessingDone();
}
AbstractClient *TabGame::getClientForPlayer(int playerId) const
{
if (clients.size() > 1) {
if (playerId == -1)
playerId = getActiveLocalPlayer()->getId();
return clients.at(playerId);
} else if (clients.isEmpty())
return 0;
else
return clients.first();
}
void TabGame::sendGameCommand(PendingCommand *pend, int playerId)
{
AbstractClient *client = getClientForPlayer(playerId);
if (!client)
return;
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(commandFinished(const Response &)));
client->sendCommand(pend);
}
void TabGame::sendGameCommand(const google::protobuf::Message &command, int playerId)
{
AbstractClient *client = getClientForPlayer(playerId);
if (!client)
return;
PendingCommand *pend = prepareGameCommand(command);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(commandFinished(const Response &)));
client->sendCommand(pend);
}
void TabGame::commandFinished(const Response &response)
{
if (response.response_code() == Response::RespChatFlood)
messageLog->appendMessage(tr("You are flooding the game. Please wait a couple of seconds."));
}
PendingCommand *TabGame::prepareGameCommand(const ::google::protobuf::Message &cmd)
{
CommandContainer cont;
cont.set_game_id(gameInfo.game_id());
GameCommand *c = cont.add_game_command();
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
return new PendingCommand(cont);
}
PendingCommand *TabGame::prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList)
{
CommandContainer cont;
cont.set_game_id(gameInfo.game_id());
for (int i = 0; i < cmdList.size(); ++i) {
GameCommand *c = cont.add_game_command();
c->GetReflection()
->MutableMessage(c, cmdList[i]->GetDescriptor()->FindExtensionByName("ext"))
->CopyFrom(*cmdList[i]);
delete cmdList[i];
}
return new PendingCommand(cont);
}
void TabGame::startGame(bool resuming)
{
currentPhase = -1;
QMapIterator<int, DeckViewContainer *> i(deckViewContainers);
while (i.hasNext()) {
i.next();
i.value()->setReadyStart(false);
i.value()->hide();
}
mainWidget->setCurrentWidget(gamePlayAreaWidget);
if (!resuming) {
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext())
playerIterator.next().value()->setGameStarted();
}
playerListWidget->setGameStarted(true, resuming);
gameInfo.set_started(true);
static_cast<GameScene *>(gameView->scene())->rearrange();
if (sayEdit && players.size() > 1)
sayEdit->setFocus();
}
void TabGame::stopGame()
{
currentPhase = -1;
activePlayer = -1;
QMapIterator<int, DeckViewContainer *> i(deckViewContainers);
while (i.hasNext()) {
i.next();
i.value()->show();
}
mainWidget->setCurrentWidget(deckViewContainerWidget);
playerListWidget->setActivePlayer(-1);
playerListWidget->setGameStarted(false, false);
gameInfo.set_started(false);
}
void TabGame::closeGame()
{
gameInfo.set_started(false);
gameClosed = true;
gameMenu->clear();
gameMenu->addAction(aLeaveGame);
}
void TabGame::eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext & /*context*/)
{
const ServerInfo_User &userInfo = spectators.value(eventPlayerId);
messageLog->logSpectatorSay(QString::fromStdString(userInfo.name()), UserLevelFlags(userInfo.user_level()),
QString::fromStdString(userInfo.privlevel()), QString::fromStdString(event.message()));
}
void TabGame::eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/)
{
QString playerName = "@" + QString::fromStdString(spectators.value(eventPlayerId).name());
if (sayEdit && autocompleteUserList.removeOne(playerName))
sayEdit->setCompletionList(autocompleteUserList);
messageLog->logLeaveSpectator(QString::fromStdString(spectators.value(eventPlayerId).name()),
getLeaveReason(event.reason()));
playerListWidget->removePlayer(eventPlayerId);
spectators.remove(eventPlayerId);
emitUserEvent();
}
void TabGame::eventGameStateChanged(const Event_GameStateChanged &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
const int playerListSize = event.player_list_size();
for (int i = 0; i < playerListSize; ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
const int playerId = prop.player_id();
QString playerName = "@" + QString::fromStdString(prop.user_info().name());
if (sayEdit && !autocompleteUserList.contains(playerName)) {
autocompleteUserList << playerName;
sayEdit->setCompletionList(autocompleteUserList);
}
if (prop.spectator()) {
if (!spectators.contains(playerId)) {
spectators.insert(playerId, prop.user_info());
playerListWidget->addPlayer(prop);
}
} else {
Player *player = players.value(playerId, 0);
if (!player) {
player = addPlayer(playerId, prop.user_info());
playerListWidget->addPlayer(prop);
}
player->processPlayerInfo(playerInfo);
if (player->getLocal()) {
DeckViewContainer *deckViewContainer = deckViewContainers.value(playerId);
if (playerInfo.has_deck_list()) {
DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list()));
PictureLoader::cacheCardPixmaps(db->getCards(newDeck.getCardList()));
deckViewContainer->setDeck(newDeck);
player->setDeck(newDeck);
}
deckViewContainer->setReadyStart(prop.ready_start());
deckViewContainer->setSideboardLocked(prop.sideboard_locked());
}
}
}
for (int i = 0; i < playerListSize; ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
if (!prop.spectator()) {
Player *player = players.value(prop.player_id(), 0);
if (!player)
continue;
player->processCardAttachment(playerInfo);
}
}
secondsElapsed = event.seconds_elapsed();
if (event.game_started() && !gameInfo.started()) {
startGame(!gameStateKnown);
if (gameStateKnown)
messageLog->logGameStart();
setActivePlayer(event.active_player_id());
setActivePhase(event.active_phase());
} else if (!event.game_started() && gameInfo.started()) {
stopGame();
scene->clearViews();
}
gameStateKnown = true;
emitUserEvent();
}
void TabGame::eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context)
{
Player *player = players.value(eventPlayerId, 0);
if (!player)
return;
const ServerInfo_PlayerProperties &prop = event.player_properties();
playerListWidget->updatePlayerProperties(prop, eventPlayerId);
const GameEventContext::ContextType contextType =
static_cast<GameEventContext::ContextType>(getPbExtension(context));
switch (contextType) {
case GameEventContext::READY_START: {
bool ready = prop.ready_start();
if (player->getLocal())
deckViewContainers.value(player->getId())->setReadyStart(ready);
if (ready)
messageLog->logReadyStart(player);
else
messageLog->logNotReadyStart(player);
break;
}
case GameEventContext::CONCEDE: {
messageLog->logConcede(player);
player->setConceded(true);
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext())
playerIterator.next().value()->updateZones();
break;
}
case GameEventContext::DECK_SELECT: {
Context_DeckSelect deckSelect = context.GetExtension(Context_DeckSelect::ext);
messageLog->logDeckSelect(player, QString::fromStdString(deckSelect.deck_hash()),
deckSelect.sideboard_size());
break;
}
case GameEventContext::SET_SIDEBOARD_LOCK: {
if (player->getLocal())
deckViewContainers.value(player->getId())->setSideboardLocked(prop.sideboard_locked());
messageLog->logSetSideboardLock(player, prop.sideboard_locked());
break;
}
case GameEventContext::CONNECTION_STATE_CHANGED: {
messageLog->logConnectionStateChanged(player, prop.ping_seconds() != -1);
break;
}
default:;
}
}
void TabGame::eventJoin(const Event_Join &event, int /*eventPlayerId*/, const GameEventContext & /*context*/)
{
const ServerInfo_PlayerProperties &playerInfo = event.player_properties();
const int playerId = playerInfo.player_id();
QString playerName = QString::fromStdString(playerInfo.user_info().name());
if (sayEdit && !autocompleteUserList.contains("@" + playerName)) {
autocompleteUserList << "@" + playerName;
sayEdit->setCompletionList(autocompleteUserList);
}
if (players.contains(playerId))
return;
if (playerInfo.spectator()) {
spectators.insert(playerId, playerInfo.user_info());
messageLog->logJoinSpectator(playerName);
} else {
Player *newPlayer = addPlayer(playerId, playerInfo.user_info());
messageLog->logJoin(newPlayer);
}
playerListWidget->addPlayer(playerInfo);
emitUserEvent();
}
QString TabGame::getLeaveReason(Event_Leave::LeaveReason reason)
{
switch (reason) {
case Event_Leave::USER_KICKED:
return tr("kicked by game host or moderator");
break;
case Event_Leave::USER_LEFT:
return tr("player left the game");
break;
case Event_Leave::USER_DISCONNECTED:
return tr("player disconnected from server");
break;
case Event_Leave::OTHER:
default:
return tr("reason unknown");
break;
}
}
void TabGame::eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/)
{
Player *player = players.value(eventPlayerId, 0);
if (!player)
return;
QString playerName = "@" + player->getName();
if (sayEdit && autocompleteUserList.removeOne(playerName))
sayEdit->setCompletionList(autocompleteUserList);
messageLog->logLeave(player, getLeaveReason(event.reason()));
playerListWidget->removePlayer(eventPlayerId);
players.remove(eventPlayerId);
emit playerRemoved(player);
player->clear();
player->deleteLater();
// Rearrange all remaining zones so that attachment relationship updates take place
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext())
playerIterator.next().value()->updateZones();
emitUserEvent();
}
void TabGame::eventKicked(const Event_Kicked & /*event*/, int /*eventPlayerId*/, const GameEventContext & /*context*/)
{
closeGame();
tabSupervisor->setCurrentIndex(tabSupervisor->indexOf(this));
messageLog->logKicked();
QMessageBox msgBox(this);
msgBox.setWindowTitle(getTabText());
msgBox.setText(tr("You have been kicked out of the game."));
msgBox.setIcon(QMessageBox::Information);
msgBox.exec();
emitUserEvent();
}
void TabGame::eventGameHostChanged(const Event_GameHostChanged & /*event*/,
int eventPlayerId,
const GameEventContext & /*context*/)
{
hostId = eventPlayerId;
}
void TabGame::eventGameClosed(const Event_GameClosed & /*event*/,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
closeGame();
messageLog->logGameClosed();
emitUserEvent();
}
Player *TabGame::setActivePlayer(int id)
{
Player *player = players.value(id, 0);
if (!player)
return 0;
activePlayer = id;
playerListWidget->setActivePlayer(id);
QMapIterator<int, Player *> i(players);
while (i.hasNext()) {
i.next();
if (i.value() == player) {
i.value()->setActive(true);
if (clients.size() > 1)
i.value()->setShortcutsActive();
} else {
i.value()->setActive(false);
if (clients.size() > 1)
i.value()->setShortcutsInactive();
}
}
currentPhase = -1;
emitUserEvent();
return player;
}
void TabGame::eventSetActivePlayer(const Event_SetActivePlayer &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
Player *player = setActivePlayer(event.active_player_id());
if (!player)
return;
messageLog->logSetActivePlayer(player);
emitUserEvent();
}
void TabGame::setActivePhase(int phase)
{
if (currentPhase != phase) {
currentPhase = phase;
phasesToolbar->setActivePhase(phase);
}
}
void TabGame::eventSetActivePhase(const Event_SetActivePhase &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
const int phase = event.phase();
if (currentPhase != phase)
messageLog->logSetActivePhase(phase);
setActivePhase(phase);
emitUserEvent();
}
void TabGame::newCardAdded(AbstractCardItem *card)
{
connect(card, SIGNAL(hovered(AbstractCardItem *)), cardInfo, SLOT(setCard(AbstractCardItem *)));
connect(card, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(card, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
connect(card, SIGNAL(updateCardMenu(AbstractCardItem *)), this, SLOT(updateCardMenu(AbstractCardItem *)));
connect(card, SIGNAL(cardShiftClicked(QString)), this, SLOT(linkCardToChat(QString)));
}
CardItem *TabGame::getCard(int playerId, const QString &zoneName, int cardId) const
{
Player *player = players.value(playerId, 0);
if (!player)
return 0;
CardZone *zone = player->getZones().value(zoneName, 0);
if (!zone)
return 0;
return zone->getCard(cardId, QString());
}
QString TabGame::getTabText() const
{
QString gameTypeInfo;
if (gameTypes.size() != 0) {
gameTypeInfo = gameTypes.at(0);
if (gameTypes.size() > 1)
gameTypeInfo.append("...");
}
QString gameDesc(gameInfo.description().c_str());
QString gameId(QString::number(gameInfo.game_id()));
QString tabText;
if (replay)
tabText.append(tr("Replay") + " ");
if (!gameTypeInfo.isEmpty())
tabText.append(gameTypeInfo + " ");
if (!gameDesc.isEmpty()) {
if (gameDesc.length() >= 15)
tabText.append("| " + gameDesc.left(15) + "... ");
else
tabText.append("| " + gameDesc + " ");
}
if (!tabText.isEmpty())
tabText.append("| ");
tabText.append("#" + gameId);
return tabText;
}
Player *TabGame::getActiveLocalPlayer() const
{
Player *active = players.value(activePlayer, 0);
if (active)
if (active->getLocal())
return active;
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext()) {
Player *temp = playerIterator.next().value();
if (temp->getLocal())
return temp;
}
return 0;
}
void TabGame::updateCardMenu(AbstractCardItem *card)
{
Player *p;
if ((clients.size() > 1) || !players.contains(localPlayerId))
p = card->getOwner();
else
p = players.value(localPlayerId);
p->updateCardMenu(static_cast<CardItem *>(card));
}
void TabGame::createMenuItems()
{
aNextPhase = new QAction(this);
connect(aNextPhase, SIGNAL(triggered()), this, SLOT(actNextPhase()));
aNextTurn = new QAction(this);
connect(aNextTurn, SIGNAL(triggered()), this, SLOT(actNextTurn()));
aRemoveLocalArrows = new QAction(this);
connect(aRemoveLocalArrows, SIGNAL(triggered()), this, SLOT(actRemoveLocalArrows()));
aRotateViewCW = new QAction(this);
connect(aRotateViewCW, SIGNAL(triggered()), this, SLOT(actRotateViewCW()));
aRotateViewCCW = new QAction(this);
connect(aRotateViewCCW, SIGNAL(triggered()), this, SLOT(actRotateViewCCW()));
aGameInfo = new QAction(this);
connect(aGameInfo, SIGNAL(triggered()), this, SLOT(actGameInfo()));
aConcede = new QAction(this);
connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede()));
aLeaveGame = new QAction(this);
connect(aLeaveGame, SIGNAL(triggered()), this, SLOT(actLeaveGame()));
aCloseReplay = 0;
phasesMenu = new QMenu(this);
for (int i = 0; i < phasesToolbar->phaseCount(); ++i) {
QAction *temp = new QAction(QString(), this);
connect(temp, SIGNAL(triggered()), this, SLOT(actPhaseAction()));
phasesMenu->addAction(temp);
phaseActions.append(temp);
}
phasesMenu->addSeparator();
phasesMenu->addAction(aNextPhase);
gameMenu = new QMenu(this);
playersSeparator = gameMenu->addSeparator();
gameMenu->addMenu(phasesMenu);
gameMenu->addAction(aNextTurn);
gameMenu->addSeparator();
gameMenu->addAction(aRemoveLocalArrows);
gameMenu->addAction(aRotateViewCW);
gameMenu->addAction(aRotateViewCCW);
gameMenu->addSeparator();
gameMenu->addAction(aGameInfo);
gameMenu->addAction(aConcede);
gameMenu->addAction(aLeaveGame);
addTabMenu(gameMenu);
}
void TabGame::createReplayMenuItems()
{
aNextPhase = 0;
aNextTurn = 0;
aRemoveLocalArrows = 0;
aRotateViewCW = 0;
aRotateViewCCW = 0;
aResetLayout = 0;
aGameInfo = 0;
aConcede = 0;
aLeaveGame = 0;
aCloseReplay = new QAction(this);
connect(aCloseReplay, SIGNAL(triggered()), this, SLOT(actLeaveGame()));
phasesMenu = 0;
gameMenu = new QMenu(this);
gameMenu->addAction(aCloseReplay);
addTabMenu(gameMenu);
}
void TabGame::createViewMenuItems()
{
viewMenu = new QMenu(this);
cardInfoDockMenu = viewMenu->addMenu(QString());
messageLayoutDockMenu = viewMenu->addMenu(QString());
playerListDockMenu = viewMenu->addMenu(QString());
aCardInfoDockVisible = cardInfoDockMenu->addAction(QString());
aCardInfoDockVisible->setCheckable(true);
connect(aCardInfoDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
aCardInfoDockFloating = cardInfoDockMenu->addAction(QString());
aCardInfoDockFloating->setCheckable(true);
connect(aCardInfoDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
aMessageLayoutDockVisible = messageLayoutDockMenu->addAction(QString());
aMessageLayoutDockVisible->setCheckable(true);
connect(aMessageLayoutDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
aMessageLayoutDockFloating = messageLayoutDockMenu->addAction(QString());
aMessageLayoutDockFloating->setCheckable(true);
connect(aMessageLayoutDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
aPlayerListDockVisible = playerListDockMenu->addAction(QString());
aPlayerListDockVisible->setCheckable(true);
connect(aPlayerListDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
aPlayerListDockFloating = playerListDockMenu->addAction(QString());
aPlayerListDockFloating->setCheckable(true);
connect(aPlayerListDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
if (replayDock) {
replayDockMenu = viewMenu->addMenu(QString());
aReplayDockVisible = replayDockMenu->addAction(QString());
aReplayDockVisible->setCheckable(true);
connect(aReplayDockVisible, SIGNAL(triggered()), this, SLOT(dockVisibleTriggered()));
aReplayDockFloating = replayDockMenu->addAction(QString());
aReplayDockFloating->setCheckable(true);
connect(aReplayDockFloating, SIGNAL(triggered()), this, SLOT(dockFloatingTriggered()));
}
viewMenu->addSeparator();
aResetLayout = viewMenu->addAction(QString());
connect(aResetLayout, SIGNAL(triggered()), this, SLOT(actResetLayout()));
viewMenu->addAction(aResetLayout);
addTabMenu(viewMenu);
}
void TabGame::loadLayout()
{
if (replayDock) {
restoreGeometry(settingsCache->layouts().getReplayPlayAreaGeometry());
restoreState(settingsCache->layouts().getReplayPlayAreaLayoutState());
cardInfoDock->setMinimumSize(settingsCache->layouts().getReplayCardInfoSize());
cardInfoDock->setMaximumSize(settingsCache->layouts().getReplayCardInfoSize());
messageLayoutDock->setMinimumSize(settingsCache->layouts().getReplayMessageLayoutSize());
messageLayoutDock->setMaximumSize(settingsCache->layouts().getReplayMessageLayoutSize());
playerListDock->setMinimumSize(settingsCache->layouts().getReplayPlayerListSize());
playerListDock->setMaximumSize(settingsCache->layouts().getReplayPlayerListSize());
replayDock->setMinimumSize(settingsCache->layouts().getReplayReplaySize());
replayDock->setMaximumSize(settingsCache->layouts().getReplayReplaySize());
} else {
restoreGeometry(settingsCache->layouts().getGamePlayAreaGeometry());
restoreState(settingsCache->layouts().getGamePlayAreaLayoutState());
cardInfoDock->setMinimumSize(settingsCache->layouts().getGameCardInfoSize());
cardInfoDock->setMaximumSize(settingsCache->layouts().getGameCardInfoSize());
messageLayoutDock->setMinimumSize(settingsCache->layouts().getGameMessageLayoutSize());
messageLayoutDock->setMaximumSize(settingsCache->layouts().getGameMessageLayoutSize());
playerListDock->setMinimumSize(settingsCache->layouts().getGamePlayerListSize());
playerListDock->setMaximumSize(settingsCache->layouts().getGamePlayerListSize());
}
aCardInfoDockVisible->setChecked(cardInfoDock->isVisible());
aMessageLayoutDockVisible->setChecked(messageLayoutDock->isVisible());
aPlayerListDockVisible->setChecked(playerListDock->isVisible());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
aMessageLayoutDockFloating->setEnabled(aMessageLayoutDockVisible->isChecked());
aPlayerListDockFloating->setEnabled(aPlayerListDockVisible->isChecked());
aCardInfoDockFloating->setChecked(cardInfoDock->isFloating());
aMessageLayoutDockFloating->setChecked(messageLayoutDock->isFloating());
aPlayerListDockFloating->setChecked(playerListDock->isFloating());
if (replayDock) {
aReplayDockVisible->setChecked(replayDock->isVisible());
aReplayDockFloating->setEnabled(aReplayDockVisible->isChecked());
aReplayDockFloating->setChecked(replayDock->isFloating());
}
QTimer::singleShot(100, this, SLOT(freeDocksSize()));
}
void TabGame::freeDocksSize()
{
cardInfoDock->setMinimumSize(100, 100);
cardInfoDock->setMaximumSize(5000, 5000);
messageLayoutDock->setMinimumSize(100, 100);
messageLayoutDock->setMaximumSize(5000, 5000);
playerListDock->setMinimumSize(100, 100);
playerListDock->setMaximumSize(5000, 5000);
if (replayDock) {
replayDock->setMinimumSize(100, 100);
replayDock->setMaximumSize(5000, 5000);
}
}
void TabGame::actResetLayout()
{
cardInfoDock->setVisible(true);
playerListDock->setVisible(true);
messageLayoutDock->setVisible(true);
cardInfoDock->setFloating(false);
playerListDock->setFloating(false);
messageLayoutDock->setFloating(false);
aCardInfoDockVisible->setChecked(true);
aPlayerListDockVisible->setChecked(true);
aMessageLayoutDockVisible->setChecked(true);
aCardInfoDockFloating->setChecked(false);
aPlayerListDockFloating->setChecked(false);
aMessageLayoutDockFloating->setChecked(false);
addDockWidget(Qt::RightDockWidgetArea, cardInfoDock);
addDockWidget(Qt::RightDockWidgetArea, playerListDock);
addDockWidget(Qt::RightDockWidgetArea, messageLayoutDock);
if (replayDock) {
replayDock->setVisible(true);
replayDock->setFloating(false);
addDockWidget(Qt::BottomDockWidgetArea, replayDock);
aReplayDockVisible->setChecked(true);
aReplayDockFloating->setChecked(false);
cardInfoDock->setMinimumSize(250, 360);
cardInfoDock->setMaximumSize(250, 360);
messageLayoutDock->setMinimumSize(250, 200);
messageLayoutDock->setMaximumSize(250, 200);
playerListDock->setMinimumSize(250, 50);
playerListDock->setMaximumSize(250, 50);
replayDock->setMinimumSize(900, 100);
replayDock->setMaximumSize(900, 100);
} else {
cardInfoDock->setMinimumSize(250, 360);
cardInfoDock->setMaximumSize(250, 360);
messageLayoutDock->setMinimumSize(250, 250);
messageLayoutDock->setMaximumSize(250, 250);
playerListDock->setMinimumSize(250, 50);
playerListDock->setMaximumSize(250, 50);
}
QTimer::singleShot(100, this, SLOT(freeDocksSize()));
}
void TabGame::createPlayAreaWidget(bool bReplay)
{
phasesToolbar = new PhasesToolbar;
if (!bReplay)
connect(phasesToolbar, SIGNAL(sendGameCommand(const ::google::protobuf::Message &, int)), this,
SLOT(sendGameCommand(const ::google::protobuf::Message &, int)));
scene = new GameScene(phasesToolbar, this);
gameView = new GameView(scene);
gamePlayAreaVBox = new QVBoxLayout;
gamePlayAreaVBox->setContentsMargins(0, 0, 0, 0);
gamePlayAreaVBox->addWidget(gameView);
gamePlayAreaWidget = new QWidget;
gamePlayAreaWidget->setObjectName("gamePlayAreaWidget");
gamePlayAreaWidget->setLayout(gamePlayAreaVBox);
}
void TabGame::createReplayDock()
{
timelineWidget = new ReplayTimelineWidget;
timelineWidget->setTimeline(replayTimeline);
connect(timelineWidget, SIGNAL(processNextEvent()), this, SLOT(replayNextEvent()));
connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished()));
replayStartButton = new QToolButton;
replayStartButton->setIconSize(QSize(32, 32));
replayStartButton->setIcon(QPixmap("theme:replay/start"));
connect(replayStartButton, SIGNAL(clicked()), this, SLOT(replayStartButtonClicked()));
replayPauseButton = new QToolButton;
replayPauseButton->setIconSize(QSize(32, 32));
replayPauseButton->setEnabled(false);
replayPauseButton->setIcon(QPixmap("theme:replay/pause"));
connect(replayPauseButton, SIGNAL(clicked()), this, SLOT(replayPauseButtonClicked()));
replayFastForwardButton = new QToolButton;
replayFastForwardButton->setIconSize(QSize(32, 32));
replayFastForwardButton->setEnabled(false);
replayFastForwardButton->setIcon(QPixmap("theme:replay/fastforward"));
replayFastForwardButton->setCheckable(true);
connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool)));
replayControlLayout = new QHBoxLayout;
replayControlLayout->addWidget(timelineWidget, 10);
replayControlLayout->addWidget(replayStartButton);
replayControlLayout->addWidget(replayPauseButton);
replayControlLayout->addWidget(replayFastForwardButton);
replayControlWidget = new QWidget();
replayControlWidget->setObjectName("replayControlWidget");
replayControlWidget->setLayout(replayControlLayout);
replayDock = new QDockWidget(this);
replayDock->setObjectName("replayDock");
replayDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetMovable);
replayDock->setWidget(replayControlWidget);
replayDock->setFloating(false);
replayDock->installEventFilter(this);
connect(replayDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabGame::createDeckViewContainerWidget(bool bReplay)
{
Q_UNUSED(bReplay);
deckViewContainerWidget = new QWidget();
deckViewContainerWidget->setObjectName("deckViewContainerWidget");
deckViewContainerLayout = new QVBoxLayout;
deckViewContainerLayout->setContentsMargins(0, 0, 0, 0);
deckViewContainerWidget->setLayout(deckViewContainerLayout);
}
void TabGame::viewCardInfo(const QString &cardName)
{
cardInfo->setCard(cardName);
}
void TabGame::createCardInfoDock(bool bReplay)
{
Q_UNUSED(bReplay);
cardInfo = new CardFrame();
cardHInfoLayout = new QHBoxLayout;
cardVInfoLayout = new QVBoxLayout;
cardVInfoLayout->setContentsMargins(0, 0, 0, 0);
cardVInfoLayout->addWidget(cardInfo);
cardVInfoLayout->addLayout(cardHInfoLayout);
cardBoxLayoutWidget = new QWidget;
cardBoxLayoutWidget->setLayout(cardVInfoLayout);
cardInfoDock = new QDockWidget(this);
cardInfoDock->setObjectName("cardInfoDock");
cardInfoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetMovable);
cardInfoDock->setWidget(cardBoxLayoutWidget);
cardInfoDock->setFloating(false);
cardInfoDock->installEventFilter(this);
connect(cardInfoDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabGame::createPlayerListDock(bool bReplay)
{
if (bReplay) {
playerListWidget = new PlayerListWidget(0, 0, this);
} else {
playerListWidget = new PlayerListWidget(tabSupervisor, clients.first(), this);
connect(playerListWidget, SIGNAL(openMessageDialog(QString, bool)), this,
SIGNAL(openMessageDialog(QString, bool)));
}
playerListWidget->setFocusPolicy(Qt::NoFocus);
playerListDock = new QDockWidget(this);
playerListDock->setObjectName("playerListDock");
playerListDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetMovable);
playerListDock->setWidget(playerListWidget);
playerListDock->setFloating(false);
playerListDock->installEventFilter(this);
connect(playerListDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
void TabGame::createMessageDock(bool bReplay)
{
messageLog = new MessageLogWidget(tabSupervisor, tabSupervisor, this);
connect(messageLog, SIGNAL(cardNameHovered(QString)), cardInfo, SLOT(setCard(QString)));
connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
if (!bReplay) {
connect(messageLog, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool)));
connect(messageLog, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
timeElapsedLabel = new QLabel;
timeElapsedLabel->setAlignment(Qt::AlignCenter);
gameTimer = new QTimer(this);
gameTimer->setInterval(1000);
connect(gameTimer, SIGNAL(timeout()), this, SLOT(incrementGameTime()));
gameTimer->start();
sayLabel = new QLabel;
sayEdit = new LineEditCompleter;
sayLabel->setBuddy(sayEdit);
completer = new QCompleter(autocompleteUserList, sayEdit);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setMaxVisibleItems(5);
completer->setFilterMode(Qt::MatchStartsWith);
sayEdit->setCompleter(completer);
actCompleterChanged();
if (spectator && !gameInfo.spectators_can_chat() && tabSupervisor->getAdminLocked()) {
sayLabel->hide();
sayEdit->hide();
}
connect(tabSupervisor, SIGNAL(adminLockChanged(bool)), this, SLOT(adminLockChanged(bool)));
connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(actSay()));
sayHLayout = new QHBoxLayout;
sayHLayout->addWidget(sayLabel);
sayHLayout->addWidget(sayEdit);
}
messageLogLayout = new QVBoxLayout;
messageLogLayout->setContentsMargins(0, 0, 0, 0);
if (!bReplay)
messageLogLayout->addWidget(timeElapsedLabel);
messageLogLayout->addWidget(messageLog);
if (!bReplay)
messageLogLayout->addLayout(sayHLayout);
messageLogLayoutWidget = new QWidget;
messageLogLayoutWidget->setLayout(messageLogLayout);
messageLayoutDock = new QDockWidget(this);
messageLayoutDock->setObjectName("messageLayoutDock");
messageLayoutDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetMovable);
messageLayoutDock->setWidget(messageLogLayoutWidget);
messageLayoutDock->setFloating(false);
messageLayoutDock->installEventFilter(this);
connect(messageLayoutDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool)));
}
// Method uses to sync docks state with menu items state
bool TabGame::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::Close) {
if (o == cardInfoDock) {
aCardInfoDockVisible->setChecked(false);
aCardInfoDockFloating->setEnabled(false);
} else if (o == messageLayoutDock) {
aMessageLayoutDockVisible->setChecked(false);
aMessageLayoutDockFloating->setEnabled(false);
} else if (o == playerListDock) {
aPlayerListDockVisible->setChecked(false);
aPlayerListDockFloating->setEnabled(false);
} else if (o == replayDock) {
aReplayDockVisible->setChecked(false);
aReplayDockFloating->setEnabled(false);
}
}
if (o == this && e->type() == QEvent::Hide) {
if (replay) {
settingsCache->layouts().setReplayPlayAreaState(saveState());
settingsCache->layouts().setReplayPlayAreaGeometry(saveGeometry());
settingsCache->layouts().setReplayCardInfoSize(cardInfoDock->size());
settingsCache->layouts().setReplayMessageLayoutSize(messageLayoutDock->size());
settingsCache->layouts().setReplayPlayerListSize(playerListDock->size());
settingsCache->layouts().setReplayReplaySize(replayDock->size());
} else {
settingsCache->layouts().setGamePlayAreaState(saveState());
settingsCache->layouts().setGamePlayAreaGeometry(saveGeometry());
settingsCache->layouts().setGameCardInfoSize(cardInfoDock->size());
settingsCache->layouts().setGameMessageLayoutSize(messageLayoutDock->size());
settingsCache->layouts().setGamePlayerListSize(playerListDock->size());
}
}
return false;
}
void TabGame::dockVisibleTriggered()
{
QObject *o = sender();
if (o == aCardInfoDockVisible) {
cardInfoDock->setVisible(aCardInfoDockVisible->isChecked());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
return;
}
if (o == aMessageLayoutDockVisible) {
messageLayoutDock->setVisible(aMessageLayoutDockVisible->isChecked());
aMessageLayoutDockFloating->setEnabled(aMessageLayoutDockVisible->isChecked());
return;
}
if (o == aPlayerListDockVisible) {
playerListDock->setVisible(aPlayerListDockVisible->isChecked());
aPlayerListDockFloating->setEnabled(aPlayerListDockVisible->isChecked());
return;
}
if (o == aReplayDockVisible) {
replayDock->setVisible(aReplayDockVisible->isChecked());
aReplayDockFloating->setEnabled(aReplayDockVisible->isChecked());
return;
}
}
void TabGame::dockFloatingTriggered()
{
QObject *o = sender();
if (o == aCardInfoDockFloating) {
cardInfoDock->setFloating(aCardInfoDockFloating->isChecked());
return;
}
if (o == aMessageLayoutDockFloating) {
messageLayoutDock->setFloating(aMessageLayoutDockFloating->isChecked());
return;
}
if (o == aPlayerListDockFloating) {
playerListDock->setFloating(aPlayerListDockFloating->isChecked());
return;
}
if (o == aReplayDockFloating) {
replayDock->setFloating(aReplayDockFloating->isChecked());
return;
}
}
void TabGame::dockTopLevelChanged(bool topLevel)
{
retranslateUi();
QObject *o = sender();
if (o == cardInfoDock) {
aCardInfoDockFloating->setChecked(topLevel);
return;
}
if (o == messageLayoutDock) {
aMessageLayoutDockFloating->setChecked(topLevel);
return;
}
if (o == playerListDock) {
aPlayerListDockFloating->setChecked(topLevel);
return;
}
if (o == replayDock) {
aReplayDockFloating->setChecked(topLevel);
return;
}
}