* Better support Double-Faced Cards This patch allows cards to be (virtually) transformed into other cards while preserving their state, essentially implemeting the MTG mechanic of the same name. On the server side, this is implemented by allowing cards to be "stashed away". A card that is stashed away is not in any zone, but is instead owned by another card. When a token is destroyed due to a zone change, if it had a card stashed away, that card is placed in the target zone instead of the token. On the database side, `attach="transform"` is used on `<reverse>` and `<reverse-related>` to indicate that the created token should be transformed this way. Old servers ignore the new field in `Command_CreateToken` and will perform a regular attachment, as currently. * Address review comments * Prevent tokens from being stashed * format.sh
3693 lines
125 KiB
C++
3693 lines
125 KiB
C++
#include "player.h"
|
|
|
|
#include "arrowitem.h"
|
|
#include "carddatabase.h"
|
|
#include "carditem.h"
|
|
#include "cardlist.h"
|
|
#include "cardzone.h"
|
|
#include "color.h"
|
|
#include "counter_general.h"
|
|
#include "deck_loader.h"
|
|
#include "dlg_create_token.h"
|
|
#include "gamescene.h"
|
|
#include "gettextwithmax.h"
|
|
#include "handcounter.h"
|
|
#include "handzone.h"
|
|
#include "main.h"
|
|
#include "pb/command_attach_card.pb.h"
|
|
#include "pb/command_change_zone_properties.pb.h"
|
|
#include "pb/command_concede.pb.h"
|
|
#include "pb/command_create_token.pb.h"
|
|
#include "pb/command_draw_cards.pb.h"
|
|
#include "pb/command_flip_card.pb.h"
|
|
#include "pb/command_game_say.pb.h"
|
|
#include "pb/command_move_card.pb.h"
|
|
#include "pb/command_mulligan.pb.h"
|
|
#include "pb/command_reveal_cards.pb.h"
|
|
#include "pb/command_roll_die.pb.h"
|
|
#include "pb/command_set_card_attr.pb.h"
|
|
#include "pb/command_set_card_counter.pb.h"
|
|
#include "pb/command_shuffle.pb.h"
|
|
#include "pb/command_undo_draw.pb.h"
|
|
#include "pb/context_move_card.pb.h"
|
|
#include "pb/context_undo_draw.pb.h"
|
|
#include "pb/event_attach_card.pb.h"
|
|
#include "pb/event_change_zone_properties.pb.h"
|
|
#include "pb/event_create_arrow.pb.h"
|
|
#include "pb/event_create_counter.pb.h"
|
|
#include "pb/event_create_token.pb.h"
|
|
#include "pb/event_del_counter.pb.h"
|
|
#include "pb/event_delete_arrow.pb.h"
|
|
#include "pb/event_destroy_card.pb.h"
|
|
#include "pb/event_draw_cards.pb.h"
|
|
#include "pb/event_dump_zone.pb.h"
|
|
#include "pb/event_flip_card.pb.h"
|
|
#include "pb/event_game_say.pb.h"
|
|
#include "pb/event_move_card.pb.h"
|
|
#include "pb/event_reveal_cards.pb.h"
|
|
#include "pb/event_roll_die.pb.h"
|
|
#include "pb/event_set_card_attr.pb.h"
|
|
#include "pb/event_set_card_counter.pb.h"
|
|
#include "pb/event_set_counter.pb.h"
|
|
#include "pb/event_shuffle.pb.h"
|
|
#include "pb/serverinfo_player.pb.h"
|
|
#include "pb/serverinfo_user.pb.h"
|
|
#include "pb/serverinfo_zone.pb.h"
|
|
#include "pilezone.h"
|
|
#include "playertarget.h"
|
|
#include "settingscache.h"
|
|
#include "stackzone.h"
|
|
#include "stringsizes.h"
|
|
#include "tab_game.h"
|
|
#include "tablezone.h"
|
|
#include "thememanager.h"
|
|
#include "zoneviewwidget.h"
|
|
#include "zoneviewzone.h"
|
|
|
|
#include <QDebug>
|
|
#include <QMenu>
|
|
#include <QMetaType>
|
|
#include <QPainter>
|
|
#include <QRegularExpression>
|
|
#include <QRegularExpressionMatch>
|
|
|
|
PlayerArea::PlayerArea(QGraphicsItem *parentItem) : QObject(), QGraphicsItem(parentItem)
|
|
{
|
|
setCacheMode(DeviceCoordinateCache);
|
|
connect(themeManager, SIGNAL(themeChanged()), this, SLOT(updateBg()));
|
|
updateBg();
|
|
}
|
|
|
|
void PlayerArea::updateBg()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void PlayerArea::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
|
{
|
|
QBrush brush = themeManager->getPlayerBgBrush();
|
|
|
|
if (playerZoneId > 0) {
|
|
// If the extra image is not found, load the default one
|
|
brush = themeManager->getExtraPlayerBgBrush(QString::number(playerZoneId), brush);
|
|
}
|
|
painter->fillRect(boundingRect(), brush);
|
|
}
|
|
|
|
void PlayerArea::setSize(qreal width, qreal height)
|
|
{
|
|
prepareGeometryChange();
|
|
bRect = QRectF(0, 0, width, height);
|
|
}
|
|
|
|
void PlayerArea::setPlayerZoneId(int _playerZoneId)
|
|
{
|
|
playerZoneId = _playerZoneId;
|
|
}
|
|
|
|
Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, TabGame *_parent)
|
|
: QObject(_parent), game(_parent), shortcutsActive(false), lastTokenDestroy(true), lastTokenTableRow(0), id(_id),
|
|
active(false), local(_local), judge(_judge), mirrored(false), handVisible(false), conceded(false), zoneId(0),
|
|
dialogSemaphore(false), deck(nullptr)
|
|
{
|
|
userInfo = new ServerInfo_User;
|
|
userInfo->CopyFrom(info);
|
|
|
|
connect(&SettingsCache::instance(), SIGNAL(horizontalHandChanged()), this, SLOT(rearrangeZones()));
|
|
connect(&SettingsCache::instance(), SIGNAL(handJustificationChanged()), this, SLOT(rearrangeZones()));
|
|
|
|
playerArea = new PlayerArea(this);
|
|
|
|
playerTarget = new PlayerTarget(this, playerArea, game);
|
|
qreal avatarMargin = (counterAreaWidth + CARD_HEIGHT + 15 - playerTarget->boundingRect().width()) / 2.0;
|
|
playerTarget->setPos(QPointF(avatarMargin, avatarMargin));
|
|
|
|
PileZone *deck = new PileZone(this, "deck", true, false, playerArea);
|
|
QPointF base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0,
|
|
10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0);
|
|
deck->setPos(base);
|
|
|
|
qreal h = deck->boundingRect().width() + 5;
|
|
|
|
auto *handCounter = new HandCounter(playerArea);
|
|
handCounter->setPos(base + QPointF(0, h + 10));
|
|
qreal h2 = handCounter->boundingRect().height();
|
|
|
|
PileZone *grave = new PileZone(this, "grave", false, true, playerArea);
|
|
grave->setPos(base + QPointF(0, h + h2 + 10));
|
|
|
|
PileZone *rfg = new PileZone(this, "rfg", false, true, playerArea);
|
|
rfg->setPos(base + QPointF(0, 2 * h + h2 + 10));
|
|
|
|
PileZone *sb = new PileZone(this, "sb", false, false, playerArea);
|
|
sb->setVisible(false);
|
|
|
|
table = new TableZone(this, this);
|
|
connect(table, SIGNAL(sizeChanged()), this, SLOT(updateBoundingRect()));
|
|
|
|
stack = new StackZone(this, (int)table->boundingRect().height(), this);
|
|
|
|
hand = new HandZone(this, _local || (_parent->getSpectator() && _parent->getSpectatorsSeeEverything()),
|
|
(int)table->boundingRect().height(), this);
|
|
connect(hand, SIGNAL(cardCountChanged()), handCounter, SLOT(updateNumber()));
|
|
connect(handCounter, SIGNAL(showContextMenu(const QPoint &)), hand, SLOT(showContextMenu(const QPoint &)));
|
|
|
|
updateBoundingRect();
|
|
|
|
if (local || judge) {
|
|
connect(_parent, SIGNAL(playerAdded(Player *)), this, SLOT(addPlayer(Player *)));
|
|
connect(_parent, SIGNAL(playerRemoved(Player *)), this, SLOT(removePlayer(Player *)));
|
|
}
|
|
|
|
if (local || judge) {
|
|
aMoveHandToTopLibrary = new QAction(this);
|
|
aMoveHandToTopLibrary->setData(QList<QVariant>() << "deck" << 0);
|
|
aMoveHandToBottomLibrary = new QAction(this);
|
|
aMoveHandToBottomLibrary->setData(QList<QVariant>() << "deck" << -1);
|
|
aMoveHandToGrave = new QAction(this);
|
|
aMoveHandToGrave->setData(QList<QVariant>() << "grave" << 0);
|
|
aMoveHandToRfg = new QAction(this);
|
|
aMoveHandToRfg->setData(QList<QVariant>() << "rfg" << 0);
|
|
|
|
connect(aMoveHandToTopLibrary, SIGNAL(triggered()), hand, SLOT(moveAllToZone()));
|
|
connect(aMoveHandToBottomLibrary, SIGNAL(triggered()), hand, SLOT(moveAllToZone()));
|
|
connect(aMoveHandToGrave, SIGNAL(triggered()), hand, SLOT(moveAllToZone()));
|
|
connect(aMoveHandToRfg, SIGNAL(triggered()), hand, SLOT(moveAllToZone()));
|
|
|
|
aMoveGraveToTopLibrary = new QAction(this);
|
|
aMoveGraveToTopLibrary->setData(QList<QVariant>() << "deck" << 0);
|
|
aMoveGraveToBottomLibrary = new QAction(this);
|
|
aMoveGraveToBottomLibrary->setData(QList<QVariant>() << "deck" << -1);
|
|
aMoveGraveToHand = new QAction(this);
|
|
aMoveGraveToHand->setData(QList<QVariant>() << "hand" << 0);
|
|
aMoveGraveToRfg = new QAction(this);
|
|
aMoveGraveToRfg->setData(QList<QVariant>() << "rfg" << 0);
|
|
|
|
connect(aMoveGraveToTopLibrary, SIGNAL(triggered()), grave, SLOT(moveAllToZone()));
|
|
connect(aMoveGraveToBottomLibrary, SIGNAL(triggered()), grave, SLOT(moveAllToZone()));
|
|
connect(aMoveGraveToHand, SIGNAL(triggered()), grave, SLOT(moveAllToZone()));
|
|
connect(aMoveGraveToRfg, SIGNAL(triggered()), grave, SLOT(moveAllToZone()));
|
|
|
|
aMoveRfgToTopLibrary = new QAction(this);
|
|
aMoveRfgToTopLibrary->setData(QList<QVariant>() << "deck" << 0);
|
|
aMoveRfgToBottomLibrary = new QAction(this);
|
|
aMoveRfgToBottomLibrary->setData(QList<QVariant>() << "deck" << -1);
|
|
aMoveRfgToHand = new QAction(this);
|
|
aMoveRfgToHand->setData(QList<QVariant>() << "hand" << 0);
|
|
aMoveRfgToGrave = new QAction(this);
|
|
aMoveRfgToGrave->setData(QList<QVariant>() << "grave" << 0);
|
|
|
|
connect(aMoveRfgToTopLibrary, SIGNAL(triggered()), rfg, SLOT(moveAllToZone()));
|
|
connect(aMoveRfgToBottomLibrary, SIGNAL(triggered()), rfg, SLOT(moveAllToZone()));
|
|
connect(aMoveRfgToHand, SIGNAL(triggered()), rfg, SLOT(moveAllToZone()));
|
|
connect(aMoveRfgToGrave, SIGNAL(triggered()), rfg, SLOT(moveAllToZone()));
|
|
|
|
aViewLibrary = new QAction(this);
|
|
connect(aViewLibrary, SIGNAL(triggered()), this, SLOT(actViewLibrary()));
|
|
aViewHand = new QAction(this);
|
|
connect(aViewHand, SIGNAL(triggered()), this, SLOT(actViewHand()));
|
|
|
|
aViewTopCards = new QAction(this);
|
|
connect(aViewTopCards, SIGNAL(triggered()), this, SLOT(actViewTopCards()));
|
|
aAlwaysRevealTopCard = new QAction(this);
|
|
aAlwaysRevealTopCard->setCheckable(true);
|
|
connect(aAlwaysRevealTopCard, SIGNAL(triggered()), this, SLOT(actAlwaysRevealTopCard()));
|
|
aAlwaysLookAtTopCard = new QAction(this);
|
|
aAlwaysLookAtTopCard->setCheckable(true);
|
|
connect(aAlwaysLookAtTopCard, SIGNAL(triggered()), this, SLOT(actAlwaysLookAtTopCard()));
|
|
aOpenDeckInDeckEditor = new QAction(this);
|
|
aOpenDeckInDeckEditor->setEnabled(false);
|
|
connect(aOpenDeckInDeckEditor, SIGNAL(triggered()), this, SLOT(actOpenDeckInDeckEditor()));
|
|
}
|
|
|
|
aViewGraveyard = new QAction(this);
|
|
connect(aViewGraveyard, SIGNAL(triggered()), this, SLOT(actViewGraveyard()));
|
|
|
|
aViewRfg = new QAction(this);
|
|
connect(aViewRfg, SIGNAL(triggered()), this, SLOT(actViewRfg()));
|
|
|
|
if (local || judge) {
|
|
aViewSideboard = new QAction(this);
|
|
connect(aViewSideboard, SIGNAL(triggered()), this, SLOT(actViewSideboard()));
|
|
|
|
aDrawCard = new QAction(this);
|
|
connect(aDrawCard, SIGNAL(triggered()), this, SLOT(actDrawCard()));
|
|
aDrawCards = new QAction(this);
|
|
connect(aDrawCards, SIGNAL(triggered()), this, SLOT(actDrawCards()));
|
|
aUndoDraw = new QAction(this);
|
|
connect(aUndoDraw, SIGNAL(triggered()), this, SLOT(actUndoDraw()));
|
|
aShuffle = new QAction(this);
|
|
connect(aShuffle, SIGNAL(triggered()), this, SLOT(actShuffle()));
|
|
aMulligan = new QAction(this);
|
|
connect(aMulligan, SIGNAL(triggered()), this, SLOT(actMulligan()));
|
|
|
|
aMoveTopToPlay = new QAction(this);
|
|
connect(aMoveTopToPlay, SIGNAL(triggered()), this, SLOT(actMoveTopCardToPlay()));
|
|
aMoveTopToPlayFaceDown = new QAction(this);
|
|
connect(aMoveTopToPlayFaceDown, SIGNAL(triggered()), this, SLOT(actMoveTopCardToPlayFaceDown()));
|
|
aMoveTopCardToGraveyard = new QAction(this);
|
|
connect(aMoveTopCardToGraveyard, SIGNAL(triggered()), this, SLOT(actMoveTopCardToGrave()));
|
|
aMoveTopCardToExile = new QAction(this);
|
|
connect(aMoveTopCardToExile, SIGNAL(triggered()), this, SLOT(actMoveTopCardToExile()));
|
|
aMoveTopCardsToGraveyard = new QAction(this);
|
|
connect(aMoveTopCardsToGraveyard, SIGNAL(triggered()), this, SLOT(actMoveTopCardsToGrave()));
|
|
aMoveTopCardsToExile = new QAction(this);
|
|
connect(aMoveTopCardsToExile, SIGNAL(triggered()), this, SLOT(actMoveTopCardsToExile()));
|
|
aMoveTopCardToBottom = new QAction(this);
|
|
connect(aMoveTopCardToBottom, SIGNAL(triggered()), this, SLOT(actMoveTopCardToBottom()));
|
|
|
|
aDrawBottomCard = new QAction(this);
|
|
connect(aDrawBottomCard, SIGNAL(triggered()), this, SLOT(actDrawBottomCard()));
|
|
aDrawBottomCards = new QAction(this);
|
|
connect(aDrawBottomCards, SIGNAL(triggered()), this, SLOT(actDrawBottomCards()));
|
|
aMoveBottomToPlay = new QAction(this);
|
|
connect(aMoveBottomToPlay, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToPlay()));
|
|
aMoveBottomToPlayFaceDown = new QAction(this);
|
|
connect(aMoveBottomToPlayFaceDown, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToPlayFaceDown()));
|
|
aMoveBottomCardToGraveyard = new QAction(this);
|
|
connect(aMoveBottomCardToGraveyard, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToGrave()));
|
|
aMoveBottomCardToExile = new QAction(this);
|
|
connect(aMoveBottomCardToExile, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToExile()));
|
|
aMoveBottomCardsToGraveyard = new QAction(this);
|
|
connect(aMoveBottomCardsToGraveyard, SIGNAL(triggered()), this, SLOT(actMoveBottomCardsToGrave()));
|
|
aMoveBottomCardsToExile = new QAction(this);
|
|
connect(aMoveBottomCardsToExile, SIGNAL(triggered()), this, SLOT(actMoveBottomCardsToExile()));
|
|
aMoveBottomCardToTop = new QAction(this);
|
|
connect(aMoveBottomCardToTop, SIGNAL(triggered()), this, SLOT(actMoveBottomCardToTop()));
|
|
}
|
|
|
|
playerMenu = new TearOffMenu();
|
|
table->setMenu(playerMenu);
|
|
|
|
if (local || judge) {
|
|
handMenu = playerMenu->addTearOffMenu(QString());
|
|
handMenu->addAction(aViewHand);
|
|
playerLists.append(mRevealHand = handMenu->addMenu(QString()));
|
|
playerLists.append(mRevealRandomHandCard = handMenu->addMenu(QString()));
|
|
handMenu->addSeparator();
|
|
handMenu->addAction(aMulligan);
|
|
handMenu->addSeparator();
|
|
moveHandMenu = handMenu->addTearOffMenu(QString());
|
|
moveHandMenu->addAction(aMoveHandToTopLibrary);
|
|
moveHandMenu->addAction(aMoveHandToBottomLibrary);
|
|
moveHandMenu->addSeparator();
|
|
moveHandMenu->addAction(aMoveHandToGrave);
|
|
moveHandMenu->addSeparator();
|
|
moveHandMenu->addAction(aMoveHandToRfg);
|
|
hand->setMenu(handMenu);
|
|
|
|
libraryMenu = playerMenu->addTearOffMenu(QString());
|
|
libraryMenu->addAction(aDrawCard);
|
|
libraryMenu->addAction(aDrawCards);
|
|
libraryMenu->addAction(aUndoDraw);
|
|
libraryMenu->addSeparator();
|
|
libraryMenu->addAction(aShuffle);
|
|
libraryMenu->addSeparator();
|
|
libraryMenu->addAction(aViewLibrary);
|
|
libraryMenu->addAction(aViewTopCards);
|
|
libraryMenu->addSeparator();
|
|
playerLists.append(mRevealLibrary = libraryMenu->addMenu(QString()));
|
|
playerLists.append(mRevealTopCard = libraryMenu->addMenu(QString()));
|
|
libraryMenu->addAction(aAlwaysRevealTopCard);
|
|
libraryMenu->addAction(aAlwaysLookAtTopCard);
|
|
libraryMenu->addSeparator();
|
|
topLibraryMenu = libraryMenu->addTearOffMenu(QString());
|
|
bottomLibraryMenu = libraryMenu->addTearOffMenu(QString());
|
|
libraryMenu->addSeparator();
|
|
libraryMenu->addAction(aOpenDeckInDeckEditor);
|
|
deck->setMenu(libraryMenu, aDrawCard);
|
|
|
|
topLibraryMenu->addAction(aMoveTopToPlay);
|
|
topLibraryMenu->addAction(aMoveTopToPlayFaceDown);
|
|
topLibraryMenu->addAction(aMoveTopCardToBottom);
|
|
topLibraryMenu->addSeparator();
|
|
topLibraryMenu->addAction(aMoveTopCardToGraveyard);
|
|
topLibraryMenu->addAction(aMoveTopCardsToGraveyard);
|
|
topLibraryMenu->addAction(aMoveTopCardToExile);
|
|
topLibraryMenu->addAction(aMoveTopCardsToExile);
|
|
|
|
bottomLibraryMenu->addAction(aDrawBottomCard);
|
|
bottomLibraryMenu->addAction(aDrawBottomCards);
|
|
bottomLibraryMenu->addSeparator();
|
|
bottomLibraryMenu->addAction(aMoveBottomToPlay);
|
|
bottomLibraryMenu->addAction(aMoveBottomToPlayFaceDown);
|
|
bottomLibraryMenu->addAction(aMoveBottomCardToTop);
|
|
bottomLibraryMenu->addSeparator();
|
|
bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard);
|
|
bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard);
|
|
bottomLibraryMenu->addAction(aMoveBottomCardToExile);
|
|
bottomLibraryMenu->addAction(aMoveBottomCardsToExile);
|
|
}
|
|
|
|
graveMenu = playerMenu->addTearOffMenu(QString());
|
|
graveMenu->addAction(aViewGraveyard);
|
|
|
|
if (local || judge) {
|
|
mRevealRandomGraveyardCard = graveMenu->addMenu(QString());
|
|
QAction *newAction = mRevealRandomGraveyardCard->addAction(QString());
|
|
newAction->setData(-1);
|
|
connect(newAction, SIGNAL(triggered()), this, SLOT(actRevealRandomGraveyardCard()));
|
|
allPlayersActions.append(newAction);
|
|
mRevealRandomGraveyardCard->addSeparator();
|
|
}
|
|
grave->setMenu(graveMenu, aViewGraveyard);
|
|
|
|
rfgMenu = playerMenu->addTearOffMenu(QString());
|
|
rfgMenu->addAction(aViewRfg);
|
|
rfg->setMenu(rfgMenu, aViewRfg);
|
|
|
|
if (local || judge) {
|
|
graveMenu->addSeparator();
|
|
moveGraveMenu = graveMenu->addTearOffMenu(QString());
|
|
moveGraveMenu->addAction(aMoveGraveToTopLibrary);
|
|
moveGraveMenu->addAction(aMoveGraveToBottomLibrary);
|
|
moveGraveMenu->addSeparator();
|
|
moveGraveMenu->addAction(aMoveGraveToHand);
|
|
moveGraveMenu->addSeparator();
|
|
moveGraveMenu->addAction(aMoveGraveToRfg);
|
|
|
|
rfgMenu->addSeparator();
|
|
moveRfgMenu = rfgMenu->addTearOffMenu(QString());
|
|
moveRfgMenu->addAction(aMoveRfgToTopLibrary);
|
|
moveRfgMenu->addAction(aMoveRfgToBottomLibrary);
|
|
moveRfgMenu->addSeparator();
|
|
moveRfgMenu->addAction(aMoveRfgToHand);
|
|
moveRfgMenu->addSeparator();
|
|
moveRfgMenu->addAction(aMoveRfgToGrave);
|
|
|
|
sbMenu = playerMenu->addMenu(QString());
|
|
sbMenu->addAction(aViewSideboard);
|
|
sb->setMenu(sbMenu, aViewSideboard);
|
|
|
|
aUntapAll = new QAction(this);
|
|
connect(aUntapAll, SIGNAL(triggered()), this, SLOT(actUntapAll()));
|
|
|
|
aRollDie = new QAction(this);
|
|
connect(aRollDie, SIGNAL(triggered()), this, SLOT(actRollDie()));
|
|
|
|
aCreateToken = new QAction(this);
|
|
connect(aCreateToken, SIGNAL(triggered()), this, SLOT(actCreateToken()));
|
|
|
|
aCreateAnotherToken = new QAction(this);
|
|
connect(aCreateAnotherToken, SIGNAL(triggered()), this, SLOT(actCreateAnotherToken()));
|
|
aCreateAnotherToken->setEnabled(false);
|
|
|
|
createPredefinedTokenMenu = new QMenu(QString());
|
|
createPredefinedTokenMenu->setEnabled(false);
|
|
|
|
playerMenu->addSeparator();
|
|
countersMenu = playerMenu->addMenu(QString());
|
|
playerMenu->addSeparator();
|
|
playerMenu->addAction(aUntapAll);
|
|
playerMenu->addSeparator();
|
|
playerMenu->addAction(aRollDie);
|
|
playerMenu->addSeparator();
|
|
playerMenu->addAction(aCreateToken);
|
|
playerMenu->addAction(aCreateAnotherToken);
|
|
playerMenu->addMenu(createPredefinedTokenMenu);
|
|
playerMenu->addSeparator();
|
|
}
|
|
|
|
if (local) {
|
|
sayMenu = playerMenu->addMenu(QString());
|
|
initSayMenu();
|
|
}
|
|
|
|
if (local || judge) {
|
|
aCardMenu = new QAction(this);
|
|
aCardMenu->setEnabled(false);
|
|
playerMenu->addSeparator();
|
|
playerMenu->addAction(aCardMenu);
|
|
} else {
|
|
aCardMenu = nullptr;
|
|
}
|
|
|
|
if (local || judge) {
|
|
|
|
for (auto &playerList : playerLists) {
|
|
QAction *newAction = playerList->addAction(QString());
|
|
newAction->setData(-1);
|
|
connect(newAction, SIGNAL(triggered()), this, SLOT(playerListActionTriggered()));
|
|
allPlayersActions.append(newAction);
|
|
playerList->addSeparator();
|
|
}
|
|
}
|
|
|
|
if (!local && !judge) {
|
|
countersMenu = nullptr;
|
|
sbMenu = nullptr;
|
|
aCreateAnotherToken = nullptr;
|
|
createPredefinedTokenMenu = nullptr;
|
|
}
|
|
|
|
aTap = new QAction(this);
|
|
aTap->setData(cmTap);
|
|
connect(aTap, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
aDoesntUntap = new QAction(this);
|
|
aDoesntUntap->setData(cmDoesntUntap);
|
|
connect(aDoesntUntap, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
aAttach = new QAction(this);
|
|
connect(aAttach, SIGNAL(triggered()), this, SLOT(actAttach()));
|
|
aUnattach = new QAction(this);
|
|
connect(aUnattach, SIGNAL(triggered()), this, SLOT(actUnattach()));
|
|
aDrawArrow = new QAction(this);
|
|
connect(aDrawArrow, SIGNAL(triggered()), this, SLOT(actDrawArrow()));
|
|
aIncP = new QAction(this);
|
|
connect(aIncP, SIGNAL(triggered()), this, SLOT(actIncP()));
|
|
aDecP = new QAction(this);
|
|
connect(aDecP, SIGNAL(triggered()), this, SLOT(actDecP()));
|
|
aIncT = new QAction(this);
|
|
connect(aIncT, SIGNAL(triggered()), this, SLOT(actIncT()));
|
|
aDecT = new QAction(this);
|
|
connect(aDecT, SIGNAL(triggered()), this, SLOT(actDecT()));
|
|
aIncPT = new QAction(this);
|
|
connect(aIncPT, SIGNAL(triggered()), this, SLOT(actIncPT()));
|
|
aDecPT = new QAction(this);
|
|
connect(aDecPT, SIGNAL(triggered()), this, SLOT(actDecPT()));
|
|
aFlowP = new QAction(this);
|
|
connect(aFlowP, SIGNAL(triggered()), this, SLOT(actFlowP()));
|
|
aFlowT = new QAction(this);
|
|
connect(aFlowT, SIGNAL(triggered()), this, SLOT(actFlowT()));
|
|
aSetPT = new QAction(this);
|
|
connect(aSetPT, SIGNAL(triggered()), this, SLOT(actSetPT()));
|
|
aResetPT = new QAction(this);
|
|
connect(aResetPT, SIGNAL(triggered()), this, SLOT(actResetPT()));
|
|
aSetAnnotation = new QAction(this);
|
|
connect(aSetAnnotation, SIGNAL(triggered()), this, SLOT(actSetAnnotation()));
|
|
aFlip = new QAction(this);
|
|
aFlip->setData(cmFlip);
|
|
connect(aFlip, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
aPeek = new QAction(this);
|
|
aPeek->setData(cmPeek);
|
|
connect(aPeek, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
aClone = new QAction(this);
|
|
aClone->setData(cmClone);
|
|
connect(aClone, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
aMoveToTopLibrary = new QAction(this);
|
|
aMoveToTopLibrary->setData(cmMoveToTopLibrary);
|
|
aMoveToBottomLibrary = new QAction(this);
|
|
aMoveToBottomLibrary->setData(cmMoveToBottomLibrary);
|
|
aMoveToXfromTopOfLibrary = new QAction(this);
|
|
aMoveToGraveyard = new QAction(this);
|
|
aMoveToHand = new QAction(this);
|
|
aMoveToHand->setData(cmMoveToHand);
|
|
aMoveToGraveyard->setData(cmMoveToGraveyard);
|
|
aMoveToExile = new QAction(this);
|
|
aMoveToExile->setData(cmMoveToExile);
|
|
connect(aMoveToTopLibrary, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
connect(aMoveToBottomLibrary, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
connect(aMoveToXfromTopOfLibrary, SIGNAL(triggered()), this, SLOT(actMoveCardXCardsFromTop()));
|
|
connect(aMoveToHand, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
connect(aMoveToGraveyard, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
connect(aMoveToExile, SIGNAL(triggered()), this, SLOT(cardMenuAction()));
|
|
|
|
aPlay = new QAction(this);
|
|
connect(aPlay, SIGNAL(triggered()), this, SLOT(actPlay()));
|
|
aHide = new QAction(this);
|
|
connect(aHide, SIGNAL(triggered()), this, SLOT(actHide()));
|
|
aPlayFacedown = new QAction(this);
|
|
connect(aPlayFacedown, SIGNAL(triggered()), this, SLOT(actPlayFacedown()));
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
auto *tempAddCounter = new QAction(this);
|
|
tempAddCounter->setData(9 + i * 1000);
|
|
auto *tempRemoveCounter = new QAction(this);
|
|
tempRemoveCounter->setData(10 + i * 1000);
|
|
auto *tempSetCounter = new QAction(this);
|
|
tempSetCounter->setData(11 + i * 1000);
|
|
aAddCounter.append(tempAddCounter);
|
|
aRemoveCounter.append(tempRemoveCounter);
|
|
aSetCounter.append(tempSetCounter);
|
|
connect(tempAddCounter, SIGNAL(triggered()), this, SLOT(actCardCounterTrigger()));
|
|
connect(tempRemoveCounter, SIGNAL(triggered()), this, SLOT(actCardCounterTrigger()));
|
|
connect(tempSetCounter, SIGNAL(triggered()), this, SLOT(actCardCounterTrigger()));
|
|
}
|
|
|
|
const QList<Player *> &players = game->getPlayers().values();
|
|
for (const auto player : players) {
|
|
addPlayer(player);
|
|
}
|
|
|
|
rearrangeZones();
|
|
retranslateUi();
|
|
connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts()));
|
|
refreshShortcuts();
|
|
}
|
|
|
|
Player::~Player()
|
|
{
|
|
qDebug() << "Player destructor:" << getName();
|
|
|
|
static_cast<GameScene *>(scene())->removePlayer(this);
|
|
|
|
clear();
|
|
QMapIterator<QString, CardZone *> i(zones);
|
|
while (i.hasNext())
|
|
delete i.next().value();
|
|
zones.clear();
|
|
|
|
delete playerMenu;
|
|
delete userInfo;
|
|
}
|
|
|
|
void Player::clear()
|
|
{
|
|
clearArrows();
|
|
|
|
QMapIterator<QString, CardZone *> i(zones);
|
|
while (i.hasNext()) {
|
|
i.next().value()->clearContents();
|
|
}
|
|
|
|
clearCounters();
|
|
}
|
|
|
|
void Player::addPlayer(Player *player)
|
|
{
|
|
if (player == nullptr || player == this) {
|
|
return;
|
|
}
|
|
|
|
for (auto &playerList : playerLists) {
|
|
QAction *newAction = playerList->addAction(player->getName());
|
|
newAction->setData(player->getId());
|
|
connect(newAction, SIGNAL(triggered()), this, SLOT(playerListActionTriggered()));
|
|
}
|
|
|
|
playersInfo.append(qMakePair(player->getName(), player->getId()));
|
|
}
|
|
|
|
void Player::removePlayer(Player *player)
|
|
{
|
|
if (player == nullptr) {
|
|
return;
|
|
}
|
|
|
|
for (auto &playerList : playerLists) {
|
|
QList<QAction *> actionList = playerList->actions();
|
|
for (auto &j : actionList)
|
|
if (j->data().toInt() == player->getId()) {
|
|
playerList->removeAction(j);
|
|
j->deleteLater();
|
|
}
|
|
}
|
|
|
|
for (auto it = playersInfo.begin(); it != playersInfo.end();) {
|
|
if (it->second == player->getId()) {
|
|
it = playersInfo.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::playerListActionTriggered()
|
|
{
|
|
auto *action = static_cast<QAction *>(sender());
|
|
auto *menu = static_cast<QMenu *>(action->parent());
|
|
|
|
Command_RevealCards cmd;
|
|
const int otherPlayerId = action->data().toInt();
|
|
if (otherPlayerId != -1) {
|
|
cmd.set_player_id(otherPlayerId);
|
|
}
|
|
|
|
if (menu == mRevealLibrary) {
|
|
cmd.set_zone_name("deck");
|
|
} else if (menu == mRevealTopCard) {
|
|
int deckSize = zones.value("deck")->getCards().size();
|
|
bool ok;
|
|
int number = QInputDialog::getInt(game, tr("Reveal top cards of library"),
|
|
tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberTopCards, 1,
|
|
deckSize, 1, &ok);
|
|
if (ok) {
|
|
cmd.set_zone_name("deck");
|
|
cmd.set_top_cards(number);
|
|
// backward compatibility: servers before #1051 only permits to reveal the first card
|
|
cmd.add_card_id(0);
|
|
}
|
|
|
|
} else if (menu == mRevealHand) {
|
|
cmd.set_zone_name("hand");
|
|
} else if (menu == mRevealRandomHandCard) {
|
|
cmd.set_zone_name("hand");
|
|
cmd.add_card_id(RANDOM_CARD_FROM_ZONE);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::rearrangeZones()
|
|
{
|
|
QPointF base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0);
|
|
if (SettingsCache::instance().getHorizontalHand()) {
|
|
if (mirrored) {
|
|
if (hand->contentsKnown()) {
|
|
handVisible = true;
|
|
hand->setPos(base);
|
|
base += QPointF(0, hand->boundingRect().height());
|
|
} else
|
|
handVisible = false;
|
|
|
|
stack->setPos(base);
|
|
base += QPointF(stack->boundingRect().width(), 0);
|
|
|
|
table->setPos(base);
|
|
} else {
|
|
stack->setPos(base);
|
|
|
|
table->setPos(base.x() + stack->boundingRect().width(), 0);
|
|
base += QPointF(0, table->boundingRect().height());
|
|
|
|
if (hand->contentsKnown()) {
|
|
handVisible = true;
|
|
hand->setPos(base);
|
|
} else
|
|
handVisible = false;
|
|
}
|
|
hand->setWidth(table->getWidth() + stack->boundingRect().width());
|
|
} else {
|
|
handVisible = true;
|
|
|
|
hand->setPos(base);
|
|
base += QPointF(hand->boundingRect().width(), 0);
|
|
|
|
stack->setPos(base);
|
|
base += QPointF(stack->boundingRect().width(), 0);
|
|
|
|
table->setPos(base);
|
|
}
|
|
hand->setVisible(handVisible);
|
|
hand->updateOrientation();
|
|
table->reorganizeCards();
|
|
updateBoundingRect();
|
|
rearrangeCounters();
|
|
}
|
|
|
|
void Player::updateZones()
|
|
{
|
|
table->reorganizeCards();
|
|
}
|
|
|
|
void Player::updateBoundingRect()
|
|
{
|
|
prepareGeometryChange();
|
|
qreal width = CARD_HEIGHT + 15 + counterAreaWidth + stack->boundingRect().width();
|
|
if (SettingsCache::instance().getHorizontalHand()) {
|
|
qreal handHeight = handVisible ? hand->boundingRect().height() : 0;
|
|
bRect = QRectF(0, 0, width + table->boundingRect().width(), table->boundingRect().height() + handHeight);
|
|
} else {
|
|
bRect = QRectF(0, 0, width + hand->boundingRect().width() + table->boundingRect().width(),
|
|
table->boundingRect().height());
|
|
}
|
|
playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height());
|
|
|
|
emit sizeChanged();
|
|
}
|
|
|
|
void Player::retranslateUi()
|
|
{
|
|
aViewGraveyard->setText(tr("&View graveyard"));
|
|
aViewRfg->setText(tr("&View exile"));
|
|
|
|
playerMenu->setTitle(tr("Player \"%1\"").arg(QString::fromStdString(userInfo->name())));
|
|
graveMenu->setTitle(tr("&Graveyard"));
|
|
rfgMenu->setTitle(tr("&Exile"));
|
|
|
|
if (local || judge) {
|
|
moveHandMenu->setTitle(tr("&Move hand to..."));
|
|
aMoveHandToTopLibrary->setText(tr("&Top of library"));
|
|
aMoveHandToBottomLibrary->setText(tr("&Bottom of library"));
|
|
aMoveHandToGrave->setText(tr("&Graveyard"));
|
|
aMoveHandToRfg->setText(tr("&Exile"));
|
|
|
|
moveGraveMenu->setTitle(tr("&Move graveyard to..."));
|
|
aMoveGraveToTopLibrary->setText(tr("&Top of library"));
|
|
aMoveGraveToBottomLibrary->setText(tr("&Bottom of library"));
|
|
aMoveGraveToHand->setText(tr("&Hand"));
|
|
aMoveGraveToRfg->setText(tr("&Exile"));
|
|
|
|
moveRfgMenu->setTitle(tr("&Move exile to..."));
|
|
aMoveRfgToTopLibrary->setText(tr("&Top of library"));
|
|
aMoveRfgToBottomLibrary->setText(tr("&Bottom of library"));
|
|
aMoveRfgToHand->setText(tr("&Hand"));
|
|
aMoveRfgToGrave->setText(tr("&Graveyard"));
|
|
|
|
aViewLibrary->setText(tr("&View library"));
|
|
aViewHand->setText(tr("&View hand"));
|
|
aViewTopCards->setText(tr("View &top cards of library..."));
|
|
mRevealLibrary->setTitle(tr("Reveal &library to..."));
|
|
mRevealTopCard->setTitle(tr("Reveal &top cards to..."));
|
|
topLibraryMenu->setTitle(tr("&Top of library..."));
|
|
bottomLibraryMenu->setTitle(tr("&Bottom of library..."));
|
|
aAlwaysRevealTopCard->setText(tr("&Always reveal top card"));
|
|
aAlwaysLookAtTopCard->setText(tr("&Always look at top card"));
|
|
aOpenDeckInDeckEditor->setText(tr("&Open deck in deck editor"));
|
|
aViewSideboard->setText(tr("&View sideboard"));
|
|
aDrawCard->setText(tr("&Draw card"));
|
|
aDrawCards->setText(tr("D&raw cards..."));
|
|
aUndoDraw->setText(tr("&Undo last draw"));
|
|
aMulligan->setText(tr("Take &mulligan"));
|
|
aShuffle->setText(tr("&Shuffle"));
|
|
|
|
aMoveTopToPlay->setText(tr("&Play top card"));
|
|
aMoveTopToPlayFaceDown->setText(tr("Play top card &face down"));
|
|
aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard"));
|
|
aMoveTopCardToExile->setText(tr("Move top card to e&xile"));
|
|
aMoveTopCardsToGraveyard->setText(tr("Move top cards to &graveyard..."));
|
|
aMoveTopCardsToExile->setText(tr("Move top cards to &exile..."));
|
|
aMoveTopCardToBottom->setText(tr("Put top card on &bottom"));
|
|
|
|
aDrawBottomCard->setText(tr("&Draw bottom card"));
|
|
aDrawBottomCards->setText(tr("D&raw bottom cards..."));
|
|
aMoveBottomToPlay->setText(tr("&Play bottom card"));
|
|
aMoveBottomToPlayFaceDown->setText(tr("Play bottom card &face down"));
|
|
aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard"));
|
|
aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile"));
|
|
aMoveBottomCardsToGraveyard->setText(tr("Move bottom cards to &graveyard..."));
|
|
aMoveBottomCardsToExile->setText(tr("Move bottom cards to &exile..."));
|
|
aMoveBottomCardToTop->setText(tr("Put bottom card on &top"));
|
|
|
|
handMenu->setTitle(tr("&Hand"));
|
|
mRevealHand->setTitle(tr("&Reveal hand to..."));
|
|
mRevealRandomHandCard->setTitle(tr("Reveal r&andom card to..."));
|
|
mRevealRandomGraveyardCard->setTitle(tr("Reveal random card to..."));
|
|
sbMenu->setTitle(tr("&Sideboard"));
|
|
libraryMenu->setTitle(tr("&Library"));
|
|
countersMenu->setTitle(tr("&Counters"));
|
|
|
|
aUntapAll->setText(tr("&Untap all permanents"));
|
|
aRollDie->setText(tr("R&oll die..."));
|
|
aCreateToken->setText(tr("&Create token..."));
|
|
aCreateAnotherToken->setText(tr("C&reate another token"));
|
|
createPredefinedTokenMenu->setTitle(tr("Cr&eate predefined token"));
|
|
|
|
QMapIterator<int, AbstractCounter *> counterIterator(counters);
|
|
while (counterIterator.hasNext()) {
|
|
counterIterator.next().value()->retranslateUi();
|
|
}
|
|
|
|
aCardMenu->setText(tr("Selec&ted cards"));
|
|
|
|
for (auto &allPlayersAction : allPlayersActions) {
|
|
allPlayersAction->setText(tr("&All players"));
|
|
}
|
|
}
|
|
|
|
if (local) {
|
|
sayMenu->setTitle(tr("S&ay"));
|
|
}
|
|
|
|
aPlay->setText(tr("&Play"));
|
|
aHide->setText(tr("&Hide"));
|
|
aPlayFacedown->setText(tr("Play &Face Down"));
|
|
//: Turn sideways or back again
|
|
aTap->setText(tr("&Tap / Untap"));
|
|
aDoesntUntap->setText(tr("Toggle &normal untapping"));
|
|
//: Turn face up/face down
|
|
aFlip->setText(tr("T&urn Over")); // Only the user facing names in client got renamed to "turn over"
|
|
// All code and proto bits are still unchanged (flip) for compatibility reasons
|
|
// A protocol rewrite with v3 could incorporate that, see #3100
|
|
aPeek->setText(tr("&Peek at card face"));
|
|
aClone->setText(tr("&Clone"));
|
|
aAttach->setText(tr("Attac&h to card..."));
|
|
aUnattach->setText(tr("Unattac&h"));
|
|
aDrawArrow->setText(tr("&Draw arrow..."));
|
|
aIncP->setText(tr("&Increase power"));
|
|
aDecP->setText(tr("&Decrease power"));
|
|
aIncT->setText(tr("I&ncrease toughness"));
|
|
aDecT->setText(tr("D&ecrease toughness"));
|
|
aIncPT->setText(tr("In&crease power and toughness"));
|
|
aDecPT->setText(tr("Dec&rease power and toughness"));
|
|
aFlowP->setText(tr("Increase power and decrease toughness"));
|
|
aFlowT->setText(tr("Decrease power and increase toughness"));
|
|
aSetPT->setText(tr("Set &power and toughness..."));
|
|
aResetPT->setText(tr("Reset p&ower and toughness"));
|
|
aSetAnnotation->setText(tr("&Set annotation..."));
|
|
|
|
QStringList counterColors;
|
|
counterColors.append(tr("Red"));
|
|
counterColors.append(tr("Yellow"));
|
|
counterColors.append(tr("Green"));
|
|
|
|
for (int i = 0; i < aAddCounter.size(); ++i) {
|
|
aAddCounter[i]->setText(tr("&Add counter (%1)").arg(counterColors[i]));
|
|
}
|
|
for (int i = 0; i < aRemoveCounter.size(); ++i) {
|
|
aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(counterColors[i]));
|
|
}
|
|
for (int i = 0; i < aSetCounter.size(); ++i) {
|
|
aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(counterColors[i]));
|
|
}
|
|
|
|
aMoveToTopLibrary->setText(tr("&Top of library"));
|
|
aMoveToXfromTopOfLibrary->setText(tr("X cards from the top of library..."));
|
|
aMoveToBottomLibrary->setText(tr("&Bottom of library in random order"));
|
|
aMoveToHand->setText(tr("&Hand"));
|
|
aMoveToGraveyard->setText(tr("&Graveyard"));
|
|
aMoveToExile->setText(tr("&Exile"));
|
|
|
|
QMapIterator<QString, CardZone *> zoneIterator(zones);
|
|
while (zoneIterator.hasNext()) {
|
|
zoneIterator.next().value()->retranslateUi();
|
|
}
|
|
}
|
|
|
|
void Player::setShortcutsActive()
|
|
{
|
|
shortcutsActive = true;
|
|
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
|
|
|
aPlay->setShortcuts(shortcuts.getShortcut("Player/aPlay"));
|
|
aTap->setShortcuts(shortcuts.getShortcut("Player/aTap"));
|
|
aDoesntUntap->setShortcuts(shortcuts.getShortcut("Player/aDoesntUntap"));
|
|
aFlip->setShortcuts(shortcuts.getShortcut("Player/aFlip"));
|
|
aPeek->setShortcuts(shortcuts.getShortcut("Player/aPeek"));
|
|
aClone->setShortcuts(shortcuts.getShortcut("Player/aClone"));
|
|
aAttach->setShortcuts(shortcuts.getShortcut("Player/aAttach"));
|
|
aUnattach->setShortcuts(shortcuts.getShortcut("Player/aUnattach"));
|
|
aDrawArrow->setShortcuts(shortcuts.getShortcut("Player/aDrawArrow"));
|
|
aIncP->setShortcuts(shortcuts.getShortcut("Player/aIncP"));
|
|
aDecP->setShortcuts(shortcuts.getShortcut("Player/aDecP"));
|
|
aIncT->setShortcuts(shortcuts.getShortcut("Player/aIncT"));
|
|
aDecT->setShortcuts(shortcuts.getShortcut("Player/aDecT"));
|
|
aIncPT->setShortcuts(shortcuts.getShortcut("Player/aIncPT"));
|
|
aDecPT->setShortcuts(shortcuts.getShortcut("Player/aDecPT"));
|
|
aFlowP->setShortcuts(shortcuts.getShortcut("Player/aFlowP"));
|
|
aFlowT->setShortcuts(shortcuts.getShortcut("Player/aFlowT"));
|
|
aSetPT->setShortcuts(shortcuts.getShortcut("Player/aSetPT"));
|
|
aResetPT->setShortcuts(shortcuts.getShortcut("Player/aResetPT"));
|
|
aSetAnnotation->setShortcuts(shortcuts.getShortcut("Player/aSetAnnotation"));
|
|
aMoveToTopLibrary->setShortcuts(shortcuts.getShortcut("Player/aMoveToTopLibrary"));
|
|
aMoveToBottomLibrary->setShortcuts(shortcuts.getShortcut("Player/aMoveToBottomLibrary"));
|
|
aMoveToHand->setShortcuts(shortcuts.getShortcut("Player/aMoveToHand"));
|
|
aMoveToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveToGraveyard"));
|
|
aMoveToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveToExile"));
|
|
|
|
QList<QKeySequence> addCCShortCuts;
|
|
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCRed"));
|
|
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCYellow"));
|
|
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCGreen"));
|
|
|
|
QList<QKeySequence> removeCCShortCuts;
|
|
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCRed"));
|
|
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCYellow"));
|
|
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCGreen"));
|
|
|
|
QList<QKeySequence> setCCShortCuts;
|
|
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCRed"));
|
|
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCYellow"));
|
|
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCGreen"));
|
|
|
|
for (int i = 0; i < aAddCounter.size(); ++i) {
|
|
aAddCounter[i]->setShortcut(addCCShortCuts.at(i));
|
|
}
|
|
for (int i = 0; i < aRemoveCounter.size(); ++i) {
|
|
aRemoveCounter[i]->setShortcut(removeCCShortCuts.at(i));
|
|
}
|
|
for (int i = 0; i < aSetCounter.size(); ++i) {
|
|
aSetCounter[i]->setShortcut(setCCShortCuts.at(i));
|
|
}
|
|
|
|
QMapIterator<int, AbstractCounter *> counterIterator(counters);
|
|
while (counterIterator.hasNext()) {
|
|
counterIterator.next().value()->setShortcutsActive();
|
|
}
|
|
|
|
aViewSideboard->setShortcut(shortcuts.getSingleShortcut("Player/aViewSideboard"));
|
|
aViewLibrary->setShortcut(shortcuts.getSingleShortcut("Player/aViewLibrary"));
|
|
aViewHand->setShortcut(shortcuts.getSingleShortcut("Player/aViewHand"));
|
|
aViewTopCards->setShortcut(shortcuts.getSingleShortcut("Player/aViewTopCards"));
|
|
aViewGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aViewGraveyard"));
|
|
aDrawCard->setShortcut(shortcuts.getSingleShortcut("Player/aDrawCard"));
|
|
aDrawCards->setShortcut(shortcuts.getSingleShortcut("Player/aDrawCards"));
|
|
aUndoDraw->setShortcut(shortcuts.getSingleShortcut("Player/aUndoDraw"));
|
|
aMulligan->setShortcut(shortcuts.getSingleShortcut("Player/aMulligan"));
|
|
aShuffle->setShortcut(shortcuts.getSingleShortcut("Player/aShuffle"));
|
|
aUntapAll->setShortcut(shortcuts.getSingleShortcut("Player/aUntapAll"));
|
|
aRollDie->setShortcut(shortcuts.getSingleShortcut("Player/aRollDie"));
|
|
aCreateToken->setShortcut(shortcuts.getSingleShortcut("Player/aCreateToken"));
|
|
aCreateAnotherToken->setShortcut(shortcuts.getSingleShortcut("Player/aCreateAnotherToken"));
|
|
aAlwaysRevealTopCard->setShortcut(shortcuts.getSingleShortcut("Player/aAlwaysRevealTopCard"));
|
|
aAlwaysLookAtTopCard->setShortcut(shortcuts.getSingleShortcut("Player/aAlwaysLookAtTopCard"));
|
|
aMoveTopToPlay->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopToPlay"));
|
|
aMoveTopToPlayFaceDown->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopToPlayFaceDown"));
|
|
aMoveTopCardToGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardToGraveyard"));
|
|
aMoveTopCardsToGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardsToGraveyard"));
|
|
aMoveTopCardToExile->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardToExile"));
|
|
aMoveTopCardsToExile->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardsToExile"));
|
|
aMoveTopCardToBottom->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardToBottom"));
|
|
aDrawBottomCard->setShortcut(shortcuts.getSingleShortcut("Player/aDrawBottomCard"));
|
|
aDrawBottomCards->setShortcut(shortcuts.getSingleShortcut("Player/aDrawBottomCards"));
|
|
aMoveBottomToPlay->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomToPlay"));
|
|
aMoveBottomToPlayFaceDown->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomToPlayFaceDown"));
|
|
aMoveBottomCardToGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomCardToGrave"));
|
|
aMoveBottomCardsToGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomCardsToGrave"));
|
|
aMoveBottomCardToExile->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomCardToExile"));
|
|
aMoveBottomCardsToExile->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomCardsToExile"));
|
|
aMoveBottomCardToTop->setShortcut(shortcuts.getSingleShortcut("Player/aMoveBottomCardToTop"));
|
|
aPlayFacedown->setShortcut(shortcuts.getSingleShortcut("Player/aPlayFacedown"));
|
|
aPlay->setShortcut(shortcuts.getSingleShortcut("Player/aPlay"));
|
|
}
|
|
|
|
void Player::setShortcutsInactive()
|
|
{
|
|
shortcutsActive = false;
|
|
|
|
aViewSideboard->setShortcut(QKeySequence());
|
|
aViewLibrary->setShortcut(QKeySequence());
|
|
aViewHand->setShortcut(QKeySequence());
|
|
aViewTopCards->setShortcut(QKeySequence());
|
|
aViewGraveyard->setShortcut(QKeySequence());
|
|
aDrawCard->setShortcut(QKeySequence());
|
|
aDrawCards->setShortcut(QKeySequence());
|
|
aUndoDraw->setShortcut(QKeySequence());
|
|
aMulligan->setShortcut(QKeySequence());
|
|
aShuffle->setShortcut(QKeySequence());
|
|
aUntapAll->setShortcut(QKeySequence());
|
|
aRollDie->setShortcut(QKeySequence());
|
|
aCreateToken->setShortcut(QKeySequence());
|
|
aCreateAnotherToken->setShortcut(QKeySequence());
|
|
aAlwaysRevealTopCard->setShortcut(QKeySequence());
|
|
aAlwaysLookAtTopCard->setShortcut(QKeySequence());
|
|
aMoveTopToPlay->setShortcut(QKeySequence());
|
|
aMoveTopToPlayFaceDown->setShortcut(QKeySequence());
|
|
aMoveTopCardToGraveyard->setShortcut(QKeySequence());
|
|
aMoveTopCardsToGraveyard->setShortcut(QKeySequence());
|
|
aMoveTopCardToExile->setShortcut(QKeySequence());
|
|
aMoveTopCardsToExile->setShortcut(QKeySequence());
|
|
aDrawBottomCard->setShortcut(QKeySequence());
|
|
aDrawBottomCards->setShortcut(QKeySequence());
|
|
aMoveBottomToPlay->setShortcut(QKeySequence());
|
|
aMoveBottomToPlayFaceDown->setShortcut(QKeySequence());
|
|
aMoveBottomCardToGraveyard->setShortcut(QKeySequence());
|
|
aMoveBottomCardsToGraveyard->setShortcut(QKeySequence());
|
|
aMoveBottomCardToExile->setShortcut(QKeySequence());
|
|
aMoveBottomCardsToExile->setShortcut(QKeySequence());
|
|
|
|
QMapIterator<int, AbstractCounter *> counterIterator(counters);
|
|
while (counterIterator.hasNext()) {
|
|
counterIterator.next().value()->setShortcutsInactive();
|
|
}
|
|
}
|
|
|
|
void Player::initSayMenu()
|
|
{
|
|
sayMenu->clear();
|
|
|
|
int count = SettingsCache::instance().messages().getCount();
|
|
sayMenu->setEnabled(count > 0);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
auto *newAction = new QAction(SettingsCache::instance().messages().getMessageAt(i), this);
|
|
if (i <= 10) {
|
|
newAction->setShortcut(QKeySequence("Ctrl+" + QString::number((i + 1) % 10)));
|
|
}
|
|
connect(newAction, SIGNAL(triggered()), this, SLOT(actSayMessage()));
|
|
sayMenu->addAction(newAction);
|
|
}
|
|
}
|
|
|
|
void Player::initContextualPlayersMenu(QMenu *menu)
|
|
{
|
|
menu->addAction(tr("&All players"))->setData(-1);
|
|
menu->addSeparator();
|
|
|
|
for (const auto &playerInfo : playersInfo) {
|
|
menu->addAction(playerInfo.first)->setData(playerInfo.second);
|
|
}
|
|
}
|
|
|
|
void Player::setDeck(const DeckLoader &_deck)
|
|
{
|
|
deck = new DeckLoader(_deck);
|
|
aOpenDeckInDeckEditor->setEnabled(deck);
|
|
|
|
createPredefinedTokenMenu->clear();
|
|
createPredefinedTokenMenu->setEnabled(false);
|
|
predefinedTokens.clear();
|
|
InnerDecklistNode *tokenZone = dynamic_cast<InnerDecklistNode *>(deck->getRoot()->findChild(DECK_ZONE_TOKENS));
|
|
|
|
if (tokenZone) {
|
|
if (tokenZone->size() > 0)
|
|
createPredefinedTokenMenu->setEnabled(true);
|
|
|
|
for (int i = 0; i < tokenZone->size(); ++i) {
|
|
const QString tokenName = tokenZone->at(i)->getName();
|
|
predefinedTokens.append(tokenName);
|
|
QAction *a = createPredefinedTokenMenu->addAction(tokenName);
|
|
if (i < 10) {
|
|
a->setShortcut(QKeySequence("Alt+" + QString::number((i + 1) % 10)));
|
|
}
|
|
connect(a, SIGNAL(triggered()), this, SLOT(actCreatePredefinedToken()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::actViewLibrary()
|
|
{
|
|
static_cast<GameScene *>(scene())->toggleZoneView(this, "deck", -1);
|
|
}
|
|
|
|
void Player::actViewHand()
|
|
{
|
|
static_cast<GameScene *>(scene())->toggleZoneView(this, "hand", -1);
|
|
}
|
|
|
|
void Player::actViewTopCards()
|
|
{
|
|
int deckSize = zones.value("deck")->getCards().size();
|
|
bool ok;
|
|
int number =
|
|
QInputDialog::getInt(game, tr("View top cards of library"), tr("Number of cards: (max. %1)").arg(deckSize),
|
|
defaultNumberTopCards, 1, deckSize, 1, &ok);
|
|
if (ok) {
|
|
defaultNumberTopCards = number;
|
|
static_cast<GameScene *>(scene())->toggleZoneView(this, "deck", number);
|
|
}
|
|
}
|
|
|
|
void Player::actAlwaysRevealTopCard()
|
|
{
|
|
Command_ChangeZoneProperties cmd;
|
|
cmd.set_zone_name("deck");
|
|
cmd.set_always_reveal_top_card(aAlwaysRevealTopCard->isChecked());
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actAlwaysLookAtTopCard()
|
|
{
|
|
Command_ChangeZoneProperties cmd;
|
|
cmd.set_zone_name("deck");
|
|
cmd.set_always_look_at_top_card(aAlwaysLookAtTopCard->isChecked());
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actOpenDeckInDeckEditor()
|
|
{
|
|
emit openDeckEditor(deck);
|
|
}
|
|
|
|
void Player::actViewGraveyard()
|
|
{
|
|
dynamic_cast<GameScene *>(scene())->toggleZoneView(this, "grave", -1);
|
|
}
|
|
|
|
void Player::actRevealRandomGraveyardCard()
|
|
{
|
|
Command_RevealCards cmd;
|
|
auto *action = dynamic_cast<QAction *>(sender());
|
|
const int otherPlayerId = action->data().toInt();
|
|
if (otherPlayerId != -1) {
|
|
cmd.set_player_id(otherPlayerId);
|
|
}
|
|
cmd.set_zone_name("grave");
|
|
cmd.add_card_id(RANDOM_CARD_FROM_ZONE);
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actViewRfg()
|
|
{
|
|
static_cast<GameScene *>(scene())->toggleZoneView(this, "rfg", -1);
|
|
}
|
|
|
|
void Player::actViewSideboard()
|
|
{
|
|
static_cast<GameScene *>(scene())->toggleZoneView(this, "sb", -1);
|
|
}
|
|
|
|
void Player::actShuffle()
|
|
{
|
|
sendGameCommand(Command_Shuffle());
|
|
}
|
|
|
|
void Player::actDrawCard()
|
|
{
|
|
Command_DrawCards cmd;
|
|
cmd.set_number(1);
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMulligan()
|
|
{
|
|
int startSize = SettingsCache::instance().getStartingHandSize();
|
|
int handSize = zones.value("hand")->getCards().size();
|
|
int deckSize = zones.value("deck")->getCards().size() + handSize; // hand is shuffled back into the deck
|
|
bool ok;
|
|
int number = QInputDialog::getInt(game, tr("Draw hand"),
|
|
tr("Number of cards: (max. %1)").arg(deckSize) + '\n' +
|
|
tr("0 and lower are in comparison to current hand size"),
|
|
startSize, -handSize, deckSize, 1, &ok);
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
Command_Mulligan cmd;
|
|
if (number < 1) {
|
|
if (handSize == 0) {
|
|
return;
|
|
}
|
|
cmd.set_number(handSize + number);
|
|
} else {
|
|
cmd.set_number(number);
|
|
}
|
|
sendGameCommand(cmd);
|
|
if (startSize != number) {
|
|
SettingsCache::instance().setStartingHandSize(number);
|
|
}
|
|
}
|
|
|
|
void Player::actDrawCards()
|
|
{
|
|
int deckSize = zones.value("deck")->getCards().size();
|
|
bool ok;
|
|
int number = QInputDialog::getInt(game, tr("Draw cards"), tr("Number of cards: (max. %1)").arg(deckSize),
|
|
defaultNumberTopCards, 1, deckSize, 1, &ok);
|
|
if (ok) {
|
|
defaultNumberTopCards = number;
|
|
Command_DrawCards cmd;
|
|
cmd.set_number(static_cast<google::protobuf::uint32>(number));
|
|
sendGameCommand(cmd);
|
|
}
|
|
}
|
|
|
|
void Player::actUndoDraw()
|
|
{
|
|
sendGameCommand(Command_UndoDraw());
|
|
}
|
|
|
|
void Player::cmdSetTopCard(Command_MoveCard &cmd)
|
|
{
|
|
cmd.set_start_zone("deck");
|
|
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
|
cardToMove->set_card_id(0);
|
|
cmd.set_target_player_id(getId());
|
|
}
|
|
|
|
void Player::cmdSetBottomCard(Command_MoveCard &cmd)
|
|
{
|
|
CardZone *zone = zones.value("deck");
|
|
int lastCard = zone->getCards().size() - 1;
|
|
cmd.set_start_zone("deck");
|
|
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
|
cardToMove->set_card_id(lastCard);
|
|
cmd.set_target_player_id(getId());
|
|
}
|
|
|
|
void Player::actMoveTopCardToGrave()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetTopCard(cmd);
|
|
cmd.set_target_zone("grave");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveTopCardToExile()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetTopCard(cmd);
|
|
cmd.set_target_zone("rfg");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveTopCardsToGrave()
|
|
{
|
|
const int maxCards = zones.value("deck")->getCards().size();
|
|
if (maxCards == 0) {
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
int number =
|
|
QInputDialog::getInt(game, tr("Move top cards to grave"), tr("Number of cards: (max. %1)").arg(maxCards),
|
|
defaultNumberTopCards, 1, maxCards, 1, &ok);
|
|
if (!ok) {
|
|
return;
|
|
} else if (number > maxCards) {
|
|
number = maxCards;
|
|
}
|
|
defaultNumberTopCards = number;
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("grave");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
for (int i = number - 1; i >= 0; --i) {
|
|
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveTopCardsToExile()
|
|
{
|
|
const int maxCards = zones.value("deck")->getCards().size();
|
|
if (maxCards == 0) {
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
int number =
|
|
QInputDialog::getInt(game, tr("Move top cards to exile"), tr("Number of cards: (max. %1)").arg(maxCards),
|
|
defaultNumberTopCards, 1, maxCards, 1, &ok);
|
|
if (!ok) {
|
|
return;
|
|
} else if (number > maxCards) {
|
|
number = maxCards;
|
|
}
|
|
defaultNumberTopCards = number;
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("rfg");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
for (int i = number - 1; i >= 0; --i) {
|
|
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveTopCardToBottom()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetTopCard(cmd);
|
|
cmd.set_target_zone("deck");
|
|
cmd.set_x(-1); // bottom of deck
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveTopCardToPlay()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetTopCard(cmd);
|
|
cmd.set_target_zone("stack");
|
|
cmd.set_x(-1);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveTopCardToPlayFaceDown()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
|
cardToMove->set_card_id(0);
|
|
cardToMove->set_face_down(true);
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("table");
|
|
cmd.set_x(-1);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardToGrave()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetBottomCard(cmd);
|
|
cmd.set_target_zone("grave");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardToExile()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetBottomCard(cmd);
|
|
cmd.set_target_zone("rfg");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardsToGrave()
|
|
{
|
|
const int maxCards = zones.value("deck")->getCards().size();
|
|
if (maxCards == 0) {
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
int number =
|
|
QInputDialog::getInt(game, tr("Move bottom cards to grave"), tr("Number of cards: (max. %1)").arg(maxCards),
|
|
defaultNumberBottomCards, 1, maxCards, 1, &ok);
|
|
if (!ok) {
|
|
return;
|
|
} else if (number > maxCards) {
|
|
number = maxCards;
|
|
}
|
|
defaultNumberBottomCards = number;
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("grave");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
for (int i = maxCards - number; i < maxCards; ++i) {
|
|
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardsToExile()
|
|
{
|
|
const int maxCards = zones.value("deck")->getCards().size();
|
|
if (maxCards == 0) {
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
int number =
|
|
QInputDialog::getInt(game, tr("Move bottom cards to exile"), tr("Number of cards: (max. %1)").arg(maxCards),
|
|
defaultNumberBottomCards, 1, maxCards, 1, &ok);
|
|
if (!ok) {
|
|
return;
|
|
} else if (number > maxCards) {
|
|
number = maxCards;
|
|
}
|
|
defaultNumberBottomCards = number;
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("rfg");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
for (int i = maxCards - number; i < maxCards; ++i) {
|
|
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardToTop()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetBottomCard(cmd);
|
|
cmd.set_target_zone("deck");
|
|
cmd.set_x(0); // top of deck
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actDrawBottomCard()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetBottomCard(cmd);
|
|
cmd.set_target_zone("hand");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actDrawBottomCards()
|
|
{
|
|
const int maxCards = zones.value("deck")->getCards().size();
|
|
if (maxCards == 0) {
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
int number = QInputDialog::getInt(game, tr("Draw bottom cards"), tr("Number of cards: (max. %1)").arg(maxCards),
|
|
defaultNumberBottomCards, 1, maxCards, 1, &ok);
|
|
if (!ok) {
|
|
return;
|
|
} else if (number > maxCards) {
|
|
number = maxCards;
|
|
}
|
|
defaultNumberBottomCards = number;
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("hand");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
|
|
for (int i = maxCards - number; i < maxCards; ++i) {
|
|
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardToPlay()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmdSetBottomCard(cmd);
|
|
cmd.set_target_zone("stack");
|
|
cmd.set_x(-1);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actMoveBottomCardToPlayFaceDown()
|
|
{
|
|
if (zones.value("deck")->getCards().empty()) {
|
|
return;
|
|
}
|
|
|
|
CardZone *zone = zones.value("deck");
|
|
int lastCard = zone->getCards().size() - 1;
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_zone("deck");
|
|
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
|
cardToMove->set_card_id(lastCard);
|
|
cardToMove->set_face_down(true);
|
|
|
|
cmd.set_target_player_id(getId());
|
|
cmd.set_target_zone("table");
|
|
cmd.set_x(-1);
|
|
cmd.set_y(0);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actUntapAll()
|
|
{
|
|
Command_SetCardAttr cmd;
|
|
cmd.set_zone("table");
|
|
cmd.set_attribute(AttrTapped);
|
|
cmd.set_attr_value("0");
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actRollDie()
|
|
{
|
|
bool ok;
|
|
int sides = QInputDialog::getInt(game, tr("Roll die"), tr("Number of sides:"), defaultNumberDieRoll, minDieRoll,
|
|
maxDieRoll, 1, &ok);
|
|
if (ok) {
|
|
defaultNumberDieRoll = sides;
|
|
Command_RollDie cmd;
|
|
cmd.set_sides(static_cast<google::protobuf::uint32>(sides));
|
|
sendGameCommand(cmd);
|
|
}
|
|
}
|
|
|
|
void Player::actCreateToken()
|
|
{
|
|
DlgCreateToken dlg(predefinedTokens, game);
|
|
if (!dlg.exec()) {
|
|
return;
|
|
}
|
|
|
|
lastTokenName = dlg.getName();
|
|
lastTokenPT = dlg.getPT();
|
|
CardInfoPtr correctedCard = db->guessCard(lastTokenName);
|
|
if (correctedCard) {
|
|
lastTokenName = correctedCard->getName();
|
|
lastTokenTableRow = TableZone::clampValidTableRow(2 - correctedCard->getTableRow());
|
|
if (lastTokenPT.isEmpty()) {
|
|
lastTokenPT = correctedCard->getPowTough();
|
|
}
|
|
}
|
|
lastTokenColor = dlg.getColor();
|
|
lastTokenAnnotation = dlg.getAnnotation();
|
|
lastTokenDestroy = dlg.getDestroy();
|
|
aCreateAnotherToken->setEnabled(true);
|
|
aCreateAnotherToken->setText(tr("C&reate another %1 token").arg(lastTokenName));
|
|
actCreateAnotherToken();
|
|
}
|
|
|
|
void Player::actCreateAnotherToken()
|
|
{
|
|
if (lastTokenName.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
Command_CreateToken cmd;
|
|
cmd.set_zone("table");
|
|
cmd.set_card_name(lastTokenName.toStdString());
|
|
cmd.set_color(lastTokenColor.toStdString());
|
|
cmd.set_pt(lastTokenPT.toStdString());
|
|
cmd.set_annotation(lastTokenAnnotation.toStdString());
|
|
cmd.set_destroy_on_zone_change(lastTokenDestroy);
|
|
cmd.set_x(-1);
|
|
cmd.set_y(lastTokenTableRow);
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actCreatePredefinedToken()
|
|
{
|
|
auto *action = static_cast<QAction *>(sender());
|
|
CardInfoPtr cardInfo = db->getCard(action->text());
|
|
if (!cardInfo) {
|
|
return;
|
|
}
|
|
|
|
setLastToken(cardInfo);
|
|
|
|
actCreateAnotherToken();
|
|
}
|
|
|
|
void Player::actCreateRelatedCard()
|
|
{
|
|
CardItem *sourceCard = game->getActiveCard();
|
|
if (!sourceCard) {
|
|
return;
|
|
}
|
|
auto *action = static_cast<QAction *>(sender());
|
|
// If there is a better way of passing a CardRelation through a QAction, please add it here.
|
|
auto relatedCards = sourceCard->getInfo()->getAllRelatedCards();
|
|
CardRelation *cardRelation = relatedCards.at(action->data().toInt());
|
|
|
|
/*
|
|
* If we make a token via "Token: TokenName"
|
|
* then let's allow it to be created via "create another token"
|
|
*/
|
|
if (createRelatedFromRelation(sourceCard, cardRelation) && cardRelation->getCanCreateAnother()) {
|
|
CardInfoPtr cardInfo = db->getCard(cardRelation->getName());
|
|
setLastToken(cardInfo);
|
|
}
|
|
}
|
|
|
|
void Player::actCreateAllRelatedCards()
|
|
{
|
|
CardItem *sourceCard = game->getActiveCard();
|
|
if (!sourceCard) {
|
|
return;
|
|
}
|
|
|
|
auto relatedCards = sourceCard->getInfo()->getAllRelatedCards();
|
|
if (relatedCards.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
CardRelation *cardRelation = nullptr;
|
|
int tokensTypesCreated = 0;
|
|
|
|
if (relatedCards.length() == 1) {
|
|
cardRelation = relatedCards.at(0);
|
|
if (createRelatedFromRelation(sourceCard, cardRelation)) {
|
|
++tokensTypesCreated;
|
|
}
|
|
} else {
|
|
QList<CardRelation *> nonExcludedRelatedCards;
|
|
QString dbName;
|
|
for (CardRelation *cardRelationTemp : relatedCards) {
|
|
if (!cardRelationTemp->getIsCreateAllExclusion() && !cardRelationTemp->getDoesAttach()) {
|
|
nonExcludedRelatedCards.append(cardRelationTemp);
|
|
}
|
|
}
|
|
switch (nonExcludedRelatedCards.length()) {
|
|
case 1: // if nonExcludedRelatedCards == 1
|
|
cardRelation = nonExcludedRelatedCards.at(0);
|
|
if (createRelatedFromRelation(sourceCard, cardRelation)) {
|
|
++tokensTypesCreated;
|
|
}
|
|
break;
|
|
// If all are marked "Exclude", then treat the situation as if none of them are.
|
|
// We won't accept "garbage in, garbage out", here.
|
|
case 0: // else if nonExcludedRelatedCards == 0
|
|
for (CardRelation *cardRelationAll : relatedCards) {
|
|
if (!cardRelationAll->getDoesAttach() && !cardRelationAll->getIsVariable()) {
|
|
dbName = cardRelationAll->getName();
|
|
bool persistent = cardRelationAll->getIsPersistent();
|
|
for (int i = 0; i < cardRelationAll->getDefaultCount(); ++i) {
|
|
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
|
}
|
|
++tokensTypesCreated;
|
|
if (tokensTypesCreated == 1) {
|
|
cardRelation = cardRelationAll;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default: // else
|
|
for (CardRelation *cardRelationNotExcluded : nonExcludedRelatedCards) {
|
|
if (!cardRelationNotExcluded->getDoesAttach() && !cardRelationNotExcluded->getIsVariable()) {
|
|
dbName = cardRelationNotExcluded->getName();
|
|
bool persistent = cardRelationNotExcluded->getIsPersistent();
|
|
for (int i = 0; i < cardRelationNotExcluded->getDefaultCount(); ++i) {
|
|
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
|
}
|
|
++tokensTypesCreated;
|
|
if (tokensTypesCreated == 1) {
|
|
cardRelation = cardRelationNotExcluded;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we made at least one token via "Create All Tokens"
|
|
* then assign the first to the "Create another" shortcut.
|
|
*/
|
|
if (cardRelation != nullptr && cardRelation->getCanCreateAnother()) {
|
|
CardInfoPtr cardInfo = db->getCard(cardRelation->getName());
|
|
setLastToken(cardInfo);
|
|
}
|
|
}
|
|
|
|
bool Player::createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation)
|
|
{
|
|
if (sourceCard == nullptr || cardRelation == nullptr) {
|
|
return false;
|
|
}
|
|
QString dbName = cardRelation->getName();
|
|
bool persistent = cardRelation->getIsPersistent();
|
|
if (cardRelation->getIsVariable()) {
|
|
bool ok;
|
|
dialogSemaphore = true;
|
|
int count = QInputDialog::getInt(game, tr("Create tokens"), tr("Number:"), cardRelation->getDefaultCount(), 1,
|
|
MAX_TOKENS_PER_DIALOG, 1, &ok);
|
|
dialogSemaphore = false;
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < count; ++i) {
|
|
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
|
}
|
|
} else if (cardRelation->getDefaultCount() > 1) {
|
|
for (int i = 0; i < cardRelation->getDefaultCount(); ++i) {
|
|
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
|
}
|
|
} else {
|
|
createCard(sourceCard, dbName, cardRelation->getAttachType(), persistent);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Player::createCard(const CardItem *sourceCard,
|
|
const QString &dbCardName,
|
|
CardRelation::AttachType attachType,
|
|
bool persistent)
|
|
{
|
|
CardInfoPtr cardInfo = db->getCard(dbCardName);
|
|
|
|
if (cardInfo == nullptr || sourceCard == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// get the target token's location
|
|
// TODO: Define this QPoint into its own function along with the one below
|
|
QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - cardInfo->getTableRow()));
|
|
|
|
// create the token for the related card
|
|
Command_CreateToken cmd;
|
|
cmd.set_zone("table");
|
|
cmd.set_card_name(cardInfo->getName().toStdString());
|
|
switch (cardInfo->getColors().size()) {
|
|
case 0:
|
|
cmd.set_color("");
|
|
break;
|
|
case 1:
|
|
cmd.set_color("m");
|
|
break;
|
|
default:
|
|
cmd.set_color(cardInfo->getColors().left(1).toLower().toStdString());
|
|
break;
|
|
}
|
|
|
|
cmd.set_pt(cardInfo->getPowTough().toStdString());
|
|
if (SettingsCache::instance().getAnnotateTokens()) {
|
|
cmd.set_annotation(cardInfo->getText().toStdString());
|
|
} else {
|
|
cmd.set_annotation("");
|
|
}
|
|
cmd.set_destroy_on_zone_change(!persistent);
|
|
cmd.set_target_zone(sourceCard->getZone()->getName().toStdString());
|
|
cmd.set_x(gridPoint.x());
|
|
cmd.set_y(gridPoint.y());
|
|
|
|
switch (attachType) {
|
|
case CardRelation::DoesNotAttach:
|
|
break;
|
|
|
|
case CardRelation::AttachTo:
|
|
cmd.set_target_card_id(sourceCard->getId());
|
|
cmd.set_target_mode(Command_CreateToken::ATTACH_TO);
|
|
break;
|
|
|
|
case CardRelation::TransformInto:
|
|
cmd.set_target_card_id(sourceCard->getId());
|
|
cmd.set_target_mode(Command_CreateToken::TRANSFORM_INTO);
|
|
break;
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::actSayMessage()
|
|
{
|
|
auto *a = qobject_cast<QAction *>(sender());
|
|
Command_GameSay cmd;
|
|
cmd.set_message(a->text().toStdString());
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::setCardAttrHelper(const GameEventContext &context,
|
|
CardItem *card,
|
|
CardAttribute attribute,
|
|
const QString &avalue,
|
|
bool allCards)
|
|
{
|
|
if (card == nullptr) {
|
|
return;
|
|
}
|
|
|
|
bool moveCardContext = context.HasExtension(Context_MoveCard::ext);
|
|
switch (attribute) {
|
|
case AttrTapped: {
|
|
bool tapped = avalue == "1";
|
|
if (!(!tapped && card->getDoesntUntap() && allCards)) {
|
|
if (!allCards) {
|
|
emit logSetTapped(this, card, tapped);
|
|
}
|
|
card->setTapped(tapped, !moveCardContext);
|
|
}
|
|
break;
|
|
}
|
|
case AttrAttacking: {
|
|
card->setAttacking(avalue == "1");
|
|
break;
|
|
}
|
|
case AttrFaceDown: {
|
|
card->setFaceDown(avalue == "1");
|
|
break;
|
|
}
|
|
case AttrColor: {
|
|
card->setColor(avalue);
|
|
break;
|
|
}
|
|
case AttrAnnotation: {
|
|
emit logSetAnnotation(this, card, avalue);
|
|
card->setAnnotation(avalue);
|
|
break;
|
|
}
|
|
case AttrDoesntUntap: {
|
|
bool value = (avalue == "1");
|
|
emit logSetDoesntUntap(this, card, value);
|
|
card->setDoesntUntap(value);
|
|
break;
|
|
}
|
|
case AttrPT: {
|
|
emit logSetPT(this, card, avalue);
|
|
card->setPT(avalue);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::eventGameSay(const Event_GameSay &event)
|
|
{
|
|
emit logSay(this, QString::fromStdString(event.message()));
|
|
}
|
|
|
|
void Player::eventShuffle(const Event_Shuffle &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()));
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
auto &cardList = zone->getCards();
|
|
int absStart = event.start();
|
|
if (absStart < 0) { // negative indexes start from the end
|
|
absStart += cardList.length();
|
|
}
|
|
|
|
// close all views that contain shuffled cards
|
|
for (auto *view : zone->getViews()) {
|
|
if (view != nullptr) {
|
|
int length = view->getCards().length();
|
|
// we want to close empty views as well
|
|
if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library
|
|
view->deleteLater();
|
|
break;
|
|
}
|
|
} else {
|
|
qWarning() << zone->getName() << "of" << getName() << "holds empty zoneview!";
|
|
}
|
|
}
|
|
|
|
// remove revealed card name on top of decks
|
|
if (absStart == 0 && !cardList.isEmpty()) {
|
|
cardList.first()->setName("");
|
|
zone->update();
|
|
}
|
|
|
|
emit logShuffle(this, zone, event.start(), event.end());
|
|
}
|
|
|
|
void Player::eventRollDie(const Event_RollDie &event)
|
|
{
|
|
emit logRollDie(this, event.sides(), event.value());
|
|
}
|
|
|
|
void Player::eventCreateArrow(const Event_CreateArrow &event)
|
|
{
|
|
ArrowItem *arrow = addArrow(event.arrow_info());
|
|
if (!arrow) {
|
|
return;
|
|
}
|
|
|
|
auto *startCard = static_cast<CardItem *>(arrow->getStartItem());
|
|
auto *targetCard = qgraphicsitem_cast<CardItem *>(arrow->getTargetItem());
|
|
if (targetCard) {
|
|
emit logCreateArrow(this, startCard->getOwner(), startCard->getName(), targetCard->getOwner(),
|
|
targetCard->getName(), false);
|
|
} else {
|
|
emit logCreateArrow(this, startCard->getOwner(), startCard->getName(), arrow->getTargetItem()->getOwner(),
|
|
QString(), true);
|
|
}
|
|
}
|
|
|
|
void Player::eventDeleteArrow(const Event_DeleteArrow &event)
|
|
{
|
|
delArrow(event.arrow_id());
|
|
}
|
|
|
|
void Player::eventCreateToken(const Event_CreateToken &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0);
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
|
|
CardItem *card = new CardItem(this, QString::fromStdString(event.card_name()), event.card_id());
|
|
// use db PT if not provided in event
|
|
if (!QString::fromStdString(event.pt()).isEmpty()) {
|
|
card->setPT(QString::fromStdString(event.pt()));
|
|
} else {
|
|
CardInfoPtr dbCard = card->getInfo();
|
|
if (dbCard) {
|
|
card->setPT(dbCard->getPowTough());
|
|
}
|
|
}
|
|
card->setColor(QString::fromStdString(event.color()));
|
|
card->setAnnotation(QString::fromStdString(event.annotation()));
|
|
card->setDestroyOnZoneChange(event.destroy_on_zone_change());
|
|
|
|
emit logCreateToken(this, card->getName(), card->getPT());
|
|
zone->addCard(card, true, event.x(), event.y());
|
|
}
|
|
|
|
void Player::eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0);
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
|
|
if (!event.has_card_id()) {
|
|
const CardList &cards = zone->getCards();
|
|
for (int i = 0; i < cards.size(); ++i) {
|
|
setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()),
|
|
true);
|
|
}
|
|
if (event.attribute() == AttrTapped) {
|
|
emit logSetTapped(this, nullptr, event.attr_value() == "1");
|
|
}
|
|
} else {
|
|
CardItem *card = zone->getCard(event.card_id(), QString());
|
|
if (!card) {
|
|
qWarning() << "Player::eventSetCardAttr: card id=" << event.card_id() << "not found";
|
|
return;
|
|
}
|
|
setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false);
|
|
}
|
|
}
|
|
|
|
void Player::eventSetCardCounter(const Event_SetCardCounter &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0);
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
|
|
CardItem *card = zone->getCard(event.card_id(), QString());
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
int oldValue = card->getCounters().value(event.counter_id(), 0);
|
|
card->setCounter(event.counter_id(), event.counter_value());
|
|
updateCardMenu(card);
|
|
emit logSetCardCounter(this, card->getName(), event.counter_id(), event.counter_value(), oldValue);
|
|
}
|
|
|
|
void Player::eventCreateCounter(const Event_CreateCounter &event)
|
|
{
|
|
addCounter(event.counter_info());
|
|
}
|
|
|
|
void Player::eventSetCounter(const Event_SetCounter &event)
|
|
{
|
|
AbstractCounter *ctr = counters.value(event.counter_id(), 0);
|
|
if (!ctr) {
|
|
return;
|
|
}
|
|
int oldValue = ctr->getValue();
|
|
ctr->setValue(event.value());
|
|
emit logSetCounter(this, ctr->getName(), event.value(), oldValue);
|
|
}
|
|
|
|
void Player::eventDelCounter(const Event_DelCounter &event)
|
|
{
|
|
delCounter(event.counter_id());
|
|
}
|
|
|
|
void Player::eventDumpZone(const Event_DumpZone &event)
|
|
{
|
|
Player *zoneOwner = game->getPlayers().value(event.zone_owner_id(), 0);
|
|
if (!zoneOwner) {
|
|
return;
|
|
}
|
|
CardZone *zone = zoneOwner->getZones().value(QString::fromStdString(event.zone_name()), 0);
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
emit logDumpZone(this, zone, event.number_cards());
|
|
}
|
|
|
|
void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context)
|
|
{
|
|
Player *startPlayer = game->getPlayers().value(event.start_player_id());
|
|
if (!startPlayer) {
|
|
return;
|
|
}
|
|
CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(event.start_zone()), 0);
|
|
Player *targetPlayer = game->getPlayers().value(event.target_player_id());
|
|
if (!targetPlayer) {
|
|
return;
|
|
}
|
|
CardZone *targetZone;
|
|
if (event.has_target_zone()) {
|
|
targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0);
|
|
} else {
|
|
targetZone = startZone;
|
|
}
|
|
if (!startZone || !targetZone) {
|
|
return;
|
|
}
|
|
|
|
int position = event.position();
|
|
int x = event.x();
|
|
int y = event.y();
|
|
|
|
int logPosition = position;
|
|
int logX = x;
|
|
if (x == -1) {
|
|
x = 0;
|
|
}
|
|
CardItem *card = startZone->takeCard(position, event.card_id(), startZone != targetZone);
|
|
if (card == nullptr) {
|
|
return;
|
|
}
|
|
if (startZone != targetZone) {
|
|
card->deleteCardInfoPopup();
|
|
}
|
|
if (event.has_card_name()) {
|
|
card->setName(QString::fromStdString(event.card_name()));
|
|
}
|
|
|
|
if (card->getAttachedTo() && (startZone != targetZone)) {
|
|
CardItem *parentCard = card->getAttachedTo();
|
|
card->setAttachedTo(nullptr);
|
|
parentCard->getZone()->reorganizeCards();
|
|
}
|
|
|
|
card->deleteDragItem();
|
|
|
|
card->setId(event.new_card_id());
|
|
card->setFaceDown(event.face_down());
|
|
if (startZone != targetZone) {
|
|
card->setBeingPointedAt(false);
|
|
card->setHovered(false);
|
|
|
|
const QList<CardItem *> &attachedCards = card->getAttachedCards();
|
|
for (auto attachedCard : attachedCards) {
|
|
attachedCard->setParentItem(targetZone);
|
|
}
|
|
|
|
if (startZone->getPlayer() != targetZone->getPlayer()) {
|
|
card->setOwner(targetZone->getPlayer());
|
|
}
|
|
}
|
|
|
|
// The log event has to be sent before the card is added to the target zone
|
|
// because the addCard function can modify the card object.
|
|
if (context.HasExtension(Context_UndoDraw::ext)) {
|
|
emit logUndoDraw(this, card->getName());
|
|
} else {
|
|
emit logMoveCard(this, card, startZone, logPosition, targetZone, logX);
|
|
}
|
|
|
|
targetZone->addCard(card, true, x, y);
|
|
|
|
// Look at all arrows from and to the card.
|
|
// If the card was moved to another zone, delete the arrows, otherwise update them.
|
|
QMapIterator<int, Player *> playerIterator(game->getPlayers());
|
|
while (playerIterator.hasNext()) {
|
|
Player *p = playerIterator.next().value();
|
|
|
|
QList<ArrowItem *> arrowsToDelete;
|
|
QMapIterator<int, ArrowItem *> arrowIterator(p->getArrows());
|
|
while (arrowIterator.hasNext()) {
|
|
ArrowItem *arrow = arrowIterator.next().value();
|
|
if ((arrow->getStartItem() == card) || (arrow->getTargetItem() == card)) {
|
|
if (startZone == targetZone) {
|
|
arrow->updatePath();
|
|
} else {
|
|
arrowsToDelete.append(arrow);
|
|
}
|
|
}
|
|
}
|
|
for (auto &i : arrowsToDelete) {
|
|
i->delArrow();
|
|
}
|
|
}
|
|
updateCardMenu(card);
|
|
}
|
|
|
|
void Player::eventFlipCard(const Event_FlipCard &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0);
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
CardItem *card = zone->getCard(event.card_id(), QString::fromStdString(event.card_name()));
|
|
if (!card) {
|
|
return;
|
|
}
|
|
emit logFlipCard(this, card->getName(), event.face_down());
|
|
card->setFaceDown(event.face_down());
|
|
updateCardMenu(card);
|
|
}
|
|
|
|
void Player::eventDestroyCard(const Event_DestroyCard &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0);
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
|
|
CardItem *card = zone->getCard(event.card_id(), QString());
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
QList<CardItem *> attachedCards = card->getAttachedCards();
|
|
// This list is always empty except for buggy server implementations.
|
|
for (auto &attachedCard : attachedCards) {
|
|
attachedCard->setAttachedTo(nullptr);
|
|
}
|
|
|
|
emit logDestroyCard(this, card->getName());
|
|
zone->takeCard(-1, event.card_id(), true);
|
|
card->deleteLater();
|
|
}
|
|
|
|
void Player::eventAttachCard(const Event_AttachCard &event)
|
|
{
|
|
const QMap<int, Player *> &playerList = game->getPlayers();
|
|
Player *targetPlayer = nullptr;
|
|
CardZone *targetZone = nullptr;
|
|
CardItem *targetCard = nullptr;
|
|
if (event.has_target_player_id()) {
|
|
targetPlayer = playerList.value(event.target_player_id(), 0);
|
|
if (targetPlayer) {
|
|
targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0);
|
|
if (targetZone) {
|
|
targetCard = targetZone->getCard(event.target_card_id(), QString());
|
|
}
|
|
}
|
|
}
|
|
|
|
CardZone *startZone = getZones().value(QString::fromStdString(event.start_zone()), 0);
|
|
if (!startZone) {
|
|
return;
|
|
}
|
|
|
|
CardItem *startCard = startZone->getCard(event.card_id(), QString());
|
|
if (!startCard) {
|
|
return;
|
|
}
|
|
|
|
CardItem *oldParent = startCard->getAttachedTo();
|
|
|
|
startCard->setAttachedTo(targetCard);
|
|
|
|
startZone->reorganizeCards();
|
|
if ((startZone != targetZone) && targetZone) {
|
|
targetZone->reorganizeCards();
|
|
}
|
|
if (oldParent) {
|
|
oldParent->getZone()->reorganizeCards();
|
|
}
|
|
|
|
if (targetCard) {
|
|
emit logAttachCard(this, startCard->getName(), targetPlayer, targetCard->getName());
|
|
} else {
|
|
emit logUnattachCard(this, startCard->getName());
|
|
}
|
|
updateCardMenu(startCard);
|
|
}
|
|
|
|
void Player::eventDrawCards(const Event_DrawCards &event)
|
|
{
|
|
CardZone *deck = zones.value("deck");
|
|
CardZone *hand = zones.value("hand");
|
|
|
|
const int listSize = event.cards_size();
|
|
if (listSize) {
|
|
for (int i = 0; i < listSize; ++i) {
|
|
const ServerInfo_Card &cardInfo = event.cards(i);
|
|
CardItem *card = deck->takeCard(0, cardInfo.id());
|
|
card->setName(QString::fromStdString(cardInfo.name()));
|
|
hand->addCard(card, false, -1);
|
|
}
|
|
} else {
|
|
const int number = event.number();
|
|
for (int i = 0; i < number; ++i) {
|
|
hand->addCard(deck->takeCard(0, -1), false, -1);
|
|
}
|
|
}
|
|
|
|
hand->reorganizeCards();
|
|
deck->reorganizeCards();
|
|
emit logDrawCards(this, event.number(), deck->getCards().size() == 0);
|
|
}
|
|
|
|
void Player::eventRevealCards(const Event_RevealCards &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()));
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
Player *otherPlayer = nullptr;
|
|
if (event.has_other_player_id()) {
|
|
otherPlayer = game->getPlayers().value(event.other_player_id());
|
|
if (!otherPlayer) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool peeking = false;
|
|
QList<const ServerInfo_Card *> cardList;
|
|
const int cardListSize = event.cards_size();
|
|
for (int i = 0; i < cardListSize; ++i) {
|
|
const ServerInfo_Card *temp = &event.cards(i);
|
|
if (temp->face_down()) {
|
|
peeking = true;
|
|
}
|
|
cardList.append(temp);
|
|
}
|
|
|
|
if (peeking) {
|
|
for (const auto &card : cardList) {
|
|
QString cardName = QString::fromStdString(card->name());
|
|
CardItem *cardItem = zone->getCard(card->id(), QString());
|
|
if (!cardItem) {
|
|
continue;
|
|
}
|
|
cardItem->setName(cardName);
|
|
emit logRevealCards(this, zone, card->id(), cardName, this, true, 1);
|
|
}
|
|
} else {
|
|
bool showZoneView = true;
|
|
QString cardName;
|
|
auto cardId = event.card_id_size() == 0 ? -1 : event.card_id(0);
|
|
if (cardList.size() == 1) {
|
|
cardName = QString::fromStdString(cardList.first()->name());
|
|
if ((cardId == 0) && dynamic_cast<PileZone *>(zone)) {
|
|
zone->getCards().first()->setName(cardName);
|
|
zone->update();
|
|
showZoneView = false;
|
|
}
|
|
}
|
|
if (showZoneView && !cardList.isEmpty()) {
|
|
static_cast<GameScene *>(scene())->addRevealedZoneView(this, zone, cardList, event.grant_write_access());
|
|
}
|
|
|
|
emit logRevealCards(this, zone, cardId, cardName, otherPlayer, false,
|
|
event.has_number_of_cards() ? event.number_of_cards() : cardList.size());
|
|
}
|
|
}
|
|
|
|
void Player::eventChangeZoneProperties(const Event_ChangeZoneProperties &event)
|
|
{
|
|
CardZone *zone = zones.value(QString::fromStdString(event.zone_name()));
|
|
if (!zone) {
|
|
return;
|
|
}
|
|
|
|
if (event.has_always_reveal_top_card()) {
|
|
zone->setAlwaysRevealTopCard(event.always_reveal_top_card());
|
|
emit logAlwaysRevealTopCard(this, zone, event.always_reveal_top_card());
|
|
}
|
|
if (event.has_always_look_at_top_card()) {
|
|
zone->setAlwaysRevealTopCard(event.always_look_at_top_card());
|
|
emit logAlwaysLookAtTopCard(this, zone, event.always_look_at_top_card());
|
|
}
|
|
}
|
|
|
|
void Player::processGameEvent(GameEvent::GameEventType type, const GameEvent &event, const GameEventContext &context)
|
|
{
|
|
switch (type) {
|
|
case GameEvent::GAME_SAY:
|
|
eventGameSay(event.GetExtension(Event_GameSay::ext));
|
|
break;
|
|
case GameEvent::SHUFFLE:
|
|
eventShuffle(event.GetExtension(Event_Shuffle::ext));
|
|
break;
|
|
case GameEvent::ROLL_DIE:
|
|
eventRollDie(event.GetExtension(Event_RollDie::ext));
|
|
break;
|
|
case GameEvent::CREATE_ARROW:
|
|
eventCreateArrow(event.GetExtension(Event_CreateArrow::ext));
|
|
break;
|
|
case GameEvent::DELETE_ARROW:
|
|
eventDeleteArrow(event.GetExtension(Event_DeleteArrow::ext));
|
|
break;
|
|
case GameEvent::CREATE_TOKEN:
|
|
eventCreateToken(event.GetExtension(Event_CreateToken::ext));
|
|
break;
|
|
case GameEvent::SET_CARD_ATTR:
|
|
eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context);
|
|
break;
|
|
case GameEvent::SET_CARD_COUNTER:
|
|
eventSetCardCounter(event.GetExtension(Event_SetCardCounter::ext));
|
|
break;
|
|
case GameEvent::CREATE_COUNTER:
|
|
eventCreateCounter(event.GetExtension(Event_CreateCounter::ext));
|
|
break;
|
|
case GameEvent::SET_COUNTER:
|
|
eventSetCounter(event.GetExtension(Event_SetCounter::ext));
|
|
break;
|
|
case GameEvent::DEL_COUNTER:
|
|
eventDelCounter(event.GetExtension(Event_DelCounter::ext));
|
|
break;
|
|
case GameEvent::DUMP_ZONE:
|
|
eventDumpZone(event.GetExtension(Event_DumpZone::ext));
|
|
break;
|
|
case GameEvent::MOVE_CARD:
|
|
eventMoveCard(event.GetExtension(Event_MoveCard::ext), context);
|
|
break;
|
|
case GameEvent::FLIP_CARD:
|
|
eventFlipCard(event.GetExtension(Event_FlipCard::ext));
|
|
break;
|
|
case GameEvent::DESTROY_CARD:
|
|
eventDestroyCard(event.GetExtension(Event_DestroyCard::ext));
|
|
break;
|
|
case GameEvent::ATTACH_CARD:
|
|
eventAttachCard(event.GetExtension(Event_AttachCard::ext));
|
|
break;
|
|
case GameEvent::DRAW_CARDS:
|
|
eventDrawCards(event.GetExtension(Event_DrawCards::ext));
|
|
break;
|
|
case GameEvent::REVEAL_CARDS:
|
|
eventRevealCards(event.GetExtension(Event_RevealCards::ext));
|
|
break;
|
|
case GameEvent::CHANGE_ZONE_PROPERTIES:
|
|
eventChangeZoneProperties(event.GetExtension(Event_ChangeZoneProperties::ext));
|
|
break;
|
|
default: {
|
|
qWarning() << "unhandled game event" << type;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::setActive(bool _active)
|
|
{
|
|
active = _active;
|
|
table->setActive(active);
|
|
update();
|
|
}
|
|
|
|
QRectF Player::boundingRect() const
|
|
{
|
|
return bRect;
|
|
}
|
|
|
|
void Player::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
|
{
|
|
}
|
|
|
|
void Player::processPlayerInfo(const ServerInfo_Player &info)
|
|
{
|
|
clearCounters();
|
|
clearArrows();
|
|
|
|
QMapIterator<QString, CardZone *> zoneIt(zones);
|
|
while (zoneIt.hasNext()) {
|
|
zoneIt.next().value()->clearContents();
|
|
}
|
|
|
|
const int zoneListSize = info.zone_list_size();
|
|
for (int i = 0; i < zoneListSize; ++i) {
|
|
const ServerInfo_Zone &zoneInfo = info.zone_list(i);
|
|
CardZone *zone = zones.value(QString::fromStdString(zoneInfo.name()), 0);
|
|
if (!zone) {
|
|
continue;
|
|
}
|
|
|
|
const int cardListSize = zoneInfo.card_list_size();
|
|
if (!cardListSize) {
|
|
for (int j = 0; j < zoneInfo.card_count(); ++j) {
|
|
zone->addCard(new CardItem(this), false, -1);
|
|
}
|
|
} else {
|
|
for (int j = 0; j < cardListSize; ++j) {
|
|
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
|
|
CardItem *card = new CardItem(this);
|
|
card->processCardInfo(cardInfo);
|
|
zone->addCard(card, false, cardInfo.x(), cardInfo.y());
|
|
}
|
|
}
|
|
if (zoneInfo.has_always_reveal_top_card()) {
|
|
zone->setAlwaysRevealTopCard(zoneInfo.always_reveal_top_card());
|
|
}
|
|
|
|
zone->reorganizeCards();
|
|
}
|
|
|
|
const int counterListSize = info.counter_list_size();
|
|
for (int i = 0; i < counterListSize; ++i) {
|
|
addCounter(info.counter_list(i));
|
|
}
|
|
|
|
setConceded(info.properties().conceded());
|
|
}
|
|
|
|
void Player::processCardAttachment(const ServerInfo_Player &info)
|
|
{
|
|
const int zoneListSize = info.zone_list_size();
|
|
for (int i = 0; i < zoneListSize; ++i) {
|
|
const ServerInfo_Zone &zoneInfo = info.zone_list(i);
|
|
CardZone *zone = zones.value(QString::fromStdString(zoneInfo.name()), 0);
|
|
if (!zone) {
|
|
continue;
|
|
}
|
|
|
|
const int cardListSize = zoneInfo.card_list_size();
|
|
for (int j = 0; j < cardListSize; ++j) {
|
|
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
|
|
if (cardInfo.has_attach_player_id()) {
|
|
CardItem *startCard = zone->getCard(cardInfo.id(), QString());
|
|
CardItem *targetCard =
|
|
game->getCard(cardInfo.attach_player_id(), QString::fromStdString(cardInfo.attach_zone()),
|
|
cardInfo.attach_card_id());
|
|
if (!targetCard) {
|
|
continue;
|
|
}
|
|
|
|
startCard->setAttachedTo(targetCard);
|
|
}
|
|
}
|
|
}
|
|
|
|
const int arrowListSize = info.arrow_list_size();
|
|
for (int i = 0; i < arrowListSize; ++i) {
|
|
addArrow(info.arrow_list(i));
|
|
}
|
|
}
|
|
|
|
void Player::playCard(CardItem *card, bool faceDown, bool tapped)
|
|
{
|
|
if (card == nullptr) {
|
|
return;
|
|
}
|
|
|
|
Command_MoveCard cmd;
|
|
cmd.set_start_player_id(card->getZone()->getPlayer()->getId());
|
|
cmd.set_start_zone(card->getZone()->getName().toStdString());
|
|
cmd.set_target_player_id(getId());
|
|
CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
|
cardToMove->set_card_id(card->getId());
|
|
|
|
CardInfoPtr info = card->getInfo();
|
|
if (!info) {
|
|
return;
|
|
}
|
|
|
|
int tableRow = info->getTableRow();
|
|
bool playToStack = SettingsCache::instance().getPlayToStack();
|
|
QString currentZone = card->getZone()->getName();
|
|
if (currentZone == "stack" && tableRow == 3) {
|
|
cmd.set_target_zone("grave");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
} else if (!faceDown &&
|
|
((!playToStack && tableRow == 3) || ((playToStack && tableRow != 0) && currentZone != "stack"))) {
|
|
cmd.set_target_zone("stack");
|
|
cmd.set_x(0);
|
|
cmd.set_y(0);
|
|
} else {
|
|
tableRow = faceDown ? 2 : info->getTableRow();
|
|
QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow));
|
|
cardToMove->set_face_down(faceDown);
|
|
if (!faceDown) {
|
|
cardToMove->set_pt(info->getPowTough().toStdString());
|
|
}
|
|
cardToMove->set_tapped(faceDown ? false : tapped);
|
|
if (tableRow != 3)
|
|
cmd.set_target_zone("table");
|
|
cmd.set_x(gridPoint.x());
|
|
cmd.set_y(gridPoint.y());
|
|
}
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::addCard(CardItem *card)
|
|
{
|
|
emit newCardAdded(card);
|
|
}
|
|
|
|
void Player::deleteCard(CardItem *card)
|
|
{
|
|
if (card == nullptr) {
|
|
return;
|
|
} else if (dialogSemaphore) {
|
|
cardsToDelete.append(card);
|
|
} else {
|
|
card->deleteLater();
|
|
}
|
|
}
|
|
|
|
void Player::addZone(CardZone *zone)
|
|
{
|
|
zones.insert(zone->getName(), zone);
|
|
}
|
|
|
|
AbstractCounter *Player::addCounter(const ServerInfo_Counter &counter)
|
|
{
|
|
return addCounter(counter.id(), QString::fromStdString(counter.name()),
|
|
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count());
|
|
}
|
|
|
|
AbstractCounter *Player::addCounter(int counterId, const QString &name, QColor color, int radius, int value)
|
|
{
|
|
if (counters.contains(counterId)) {
|
|
return nullptr;
|
|
}
|
|
|
|
AbstractCounter *ctr;
|
|
if (name == "life") {
|
|
ctr = playerTarget->addCounter(counterId, name, value);
|
|
} else {
|
|
ctr = new GeneralCounter(this, counterId, name, color, radius, value, true, this, game);
|
|
}
|
|
counters.insert(counterId, ctr);
|
|
if (countersMenu && ctr->getMenu()) {
|
|
countersMenu->addMenu(ctr->getMenu());
|
|
}
|
|
if (shortcutsActive) {
|
|
ctr->setShortcutsActive();
|
|
}
|
|
rearrangeCounters();
|
|
return ctr;
|
|
}
|
|
|
|
void Player::delCounter(int counterId)
|
|
{
|
|
AbstractCounter *ctr = counters.value(counterId, 0);
|
|
if (!ctr) {
|
|
return;
|
|
}
|
|
|
|
ctr->delCounter();
|
|
counters.remove(counterId);
|
|
rearrangeCounters();
|
|
}
|
|
|
|
void Player::clearCounters()
|
|
{
|
|
QMapIterator<int, AbstractCounter *> counterIterator(counters);
|
|
while (counterIterator.hasNext()) {
|
|
counterIterator.next().value()->delCounter();
|
|
}
|
|
counters.clear();
|
|
}
|
|
|
|
ArrowItem *Player::addArrow(const ServerInfo_Arrow &arrow)
|
|
{
|
|
const QMap<int, Player *> &playerList = game->getPlayers();
|
|
Player *startPlayer = playerList.value(arrow.start_player_id(), 0);
|
|
Player *targetPlayer = playerList.value(arrow.target_player_id(), 0);
|
|
if (!startPlayer || !targetPlayer) {
|
|
return nullptr;
|
|
}
|
|
|
|
CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(arrow.start_zone()), 0);
|
|
CardZone *targetZone = nullptr;
|
|
if (arrow.has_target_zone()) {
|
|
targetZone = targetPlayer->getZones().value(QString::fromStdString(arrow.target_zone()), 0);
|
|
}
|
|
if (!startZone || (!targetZone && arrow.has_target_zone())) {
|
|
return nullptr;
|
|
}
|
|
|
|
CardItem *startCard = startZone->getCard(arrow.start_card_id(), QString());
|
|
CardItem *targetCard = nullptr;
|
|
if (targetZone) {
|
|
targetCard = targetZone->getCard(arrow.target_card_id(), QString());
|
|
}
|
|
if (!startCard || (!targetCard && arrow.has_target_card_id())) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (targetCard) {
|
|
return addArrow(arrow.id(), startCard, targetCard, convertColorToQColor(arrow.arrow_color()));
|
|
} else {
|
|
return addArrow(arrow.id(), startCard, targetPlayer->getPlayerTarget(),
|
|
convertColorToQColor(arrow.arrow_color()));
|
|
}
|
|
}
|
|
|
|
ArrowItem *Player::addArrow(int arrowId, CardItem *startCard, ArrowTarget *targetItem, const QColor &color)
|
|
{
|
|
auto *arrow = new ArrowItem(this, arrowId, startCard, targetItem, color);
|
|
arrows.insert(arrowId, arrow);
|
|
scene()->addItem(arrow);
|
|
return arrow;
|
|
}
|
|
|
|
void Player::delArrow(int arrowId)
|
|
{
|
|
ArrowItem *arr = arrows.value(arrowId, 0);
|
|
if (!arr) {
|
|
return;
|
|
}
|
|
arr->delArrow();
|
|
}
|
|
|
|
void Player::removeArrow(ArrowItem *arrow)
|
|
{
|
|
if (arrow->getId() != -1) {
|
|
arrows.remove(arrow->getId());
|
|
}
|
|
}
|
|
|
|
void Player::clearArrows()
|
|
{
|
|
QMapIterator<int, ArrowItem *> arrowIterator(arrows);
|
|
while (arrowIterator.hasNext()) {
|
|
arrowIterator.next().value()->delArrow();
|
|
}
|
|
arrows.clear();
|
|
}
|
|
|
|
void Player::rearrangeCounters()
|
|
{
|
|
qreal marginTop = 80;
|
|
const qreal padding = 5;
|
|
qreal ySize = boundingRect().y() + marginTop;
|
|
|
|
// Place objects
|
|
for (const auto &counter : counters) {
|
|
AbstractCounter *ctr = counter;
|
|
|
|
if (!ctr->getShownInCounterArea()) {
|
|
continue;
|
|
}
|
|
|
|
QRectF br = ctr->boundingRect();
|
|
ctr->setPos((counterAreaWidth - br.width()) / 2, ySize);
|
|
ySize += br.height() + padding;
|
|
}
|
|
}
|
|
|
|
PendingCommand *Player::prepareGameCommand(const google::protobuf::Message &cmd)
|
|
{
|
|
|
|
if (judge && !local) {
|
|
Command_Judge base;
|
|
GameCommand *c = base.add_game_command();
|
|
base.set_target_id(id);
|
|
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
|
|
return game->prepareGameCommand(base);
|
|
} else {
|
|
return game->prepareGameCommand(cmd);
|
|
}
|
|
}
|
|
|
|
PendingCommand *Player::prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList)
|
|
{
|
|
if (judge && !local) {
|
|
Command_Judge base;
|
|
base.set_target_id(id);
|
|
for (int i = 0; i < cmdList.size(); ++i) {
|
|
GameCommand *c = base.add_game_command();
|
|
c->GetReflection()
|
|
->MutableMessage(c, cmdList[i]->GetDescriptor()->FindExtensionByName("ext"))
|
|
->CopyFrom(*cmdList[i]);
|
|
delete cmdList[i];
|
|
}
|
|
return game->prepareGameCommand(base);
|
|
} else {
|
|
return game->prepareGameCommand(cmdList);
|
|
}
|
|
}
|
|
|
|
void Player::sendGameCommand(const google::protobuf::Message &command)
|
|
{
|
|
if (judge && !local) {
|
|
Command_Judge base;
|
|
GameCommand *c = base.add_game_command();
|
|
base.set_target_id(id);
|
|
c->GetReflection()->MutableMessage(c, command.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(command);
|
|
game->sendGameCommand(base, id);
|
|
} else {
|
|
game->sendGameCommand(command, id);
|
|
}
|
|
}
|
|
|
|
void Player::sendGameCommand(PendingCommand *pend)
|
|
{
|
|
game->sendGameCommand(pend, id);
|
|
}
|
|
|
|
bool Player::clearCardsToDelete()
|
|
{
|
|
if (cardsToDelete.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
for (auto &i : cardsToDelete) {
|
|
if (i != nullptr) {
|
|
i->deleteLater();
|
|
}
|
|
}
|
|
cardsToDelete.clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Player::actMoveCardXCardsFromTop()
|
|
{
|
|
int deckSize = zones.value("deck")->getCards().size() + 1; // add the card to move to the deck
|
|
bool ok;
|
|
int number =
|
|
QInputDialog::getInt(game, tr("Place card X cards from top of library"),
|
|
tr("Which position should this card be placed:") + "\n" + tr("(max. %1)").arg(deckSize),
|
|
defaultNumberTopCardsToPlaceBelow, 1, deckSize, 1, &ok);
|
|
number -= 1; // indexes start at 0
|
|
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
|
|
defaultNumberTopCardsToPlaceBelow = number;
|
|
|
|
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
|
if (sel.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QList<CardItem *> cardList;
|
|
while (!sel.isEmpty()) {
|
|
cardList.append(qgraphicsitem_cast<CardItem *>(sel.takeFirst()));
|
|
}
|
|
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
ListOfCardsToMove idList;
|
|
for (const auto &i : cardList) {
|
|
idList.add_card()->set_card_id(i->getId());
|
|
}
|
|
|
|
int startPlayerId = cardList[0]->getZone()->getPlayer()->getId();
|
|
QString startZone = cardList[0]->getZone()->getName();
|
|
|
|
auto *cmd = new Command_MoveCard;
|
|
cmd->set_start_player_id(startPlayerId);
|
|
cmd->set_start_zone(startZone.toStdString());
|
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
|
cmd->set_target_player_id(getId());
|
|
cmd->set_target_zone("deck");
|
|
cmd->set_x(number);
|
|
cmd->set_y(0);
|
|
commandList.append(cmd);
|
|
|
|
if (local) {
|
|
sendGameCommand(prepareGameCommand(commandList));
|
|
} else {
|
|
game->sendGameCommand(prepareGameCommand(commandList));
|
|
}
|
|
}
|
|
|
|
void Player::cardMenuAction()
|
|
{
|
|
auto *a = dynamic_cast<QAction *>(sender());
|
|
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
|
QList<CardItem *> cardList;
|
|
while (!sel.isEmpty()) {
|
|
cardList.append(qgraphicsitem_cast<CardItem *>(sel.takeFirst()));
|
|
}
|
|
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
if (a->data().toInt() <= (int)cmClone) {
|
|
for (const auto &card : cardList) {
|
|
switch (static_cast<CardMenuActionType>(a->data().toInt())) {
|
|
// Leaving both for compatibility with server
|
|
case cmUntap:
|
|
// fallthrough
|
|
case cmTap: {
|
|
auto *cmd = new Command_SetCardAttr;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_attribute(AttrTapped);
|
|
cmd->set_attr_value(std::to_string(1 - static_cast<int>(card->getTapped())));
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmDoesntUntap: {
|
|
auto *cmd = new Command_SetCardAttr;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_attribute(AttrDoesntUntap);
|
|
cmd->set_attr_value(card->getDoesntUntap() ? "0" : "1");
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmFlip: {
|
|
auto *cmd = new Command_FlipCard;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_face_down(!card->getFaceDown());
|
|
if (card->getFaceDown()) {
|
|
CardInfoPtr ci = card->getInfo();
|
|
if (ci) {
|
|
cmd->set_pt(ci->getPowTough().toStdString());
|
|
}
|
|
}
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmPeek: {
|
|
auto *cmd = new Command_RevealCards;
|
|
cmd->set_zone_name(card->getZone()->getName().toStdString());
|
|
cmd->add_card_id(card->getId());
|
|
cmd->set_player_id(id);
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmClone: {
|
|
auto *cmd = new Command_CreateToken;
|
|
cmd->set_zone("table");
|
|
cmd->set_card_name(card->getName().toStdString());
|
|
cmd->set_color(card->getColor().toStdString());
|
|
cmd->set_pt(card->getPT().toStdString());
|
|
cmd->set_annotation(card->getAnnotation().toStdString());
|
|
cmd->set_destroy_on_zone_change(true);
|
|
cmd->set_x(-1);
|
|
cmd->set_y(card->getGridPoint().y());
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
ListOfCardsToMove idList;
|
|
for (const auto &i : cardList) {
|
|
idList.add_card()->set_card_id(i->getId());
|
|
}
|
|
int startPlayerId = cardList[0]->getZone()->getPlayer()->getId();
|
|
QString startZone = cardList[0]->getZone()->getName();
|
|
|
|
switch (static_cast<CardMenuActionType>(a->data().toInt())) {
|
|
case cmMoveToTopLibrary: {
|
|
auto *cmd = new Command_MoveCard;
|
|
cmd->set_start_player_id(startPlayerId);
|
|
cmd->set_start_zone(startZone.toStdString());
|
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
|
cmd->set_target_player_id(getId());
|
|
cmd->set_target_zone("deck");
|
|
cmd->set_x(0);
|
|
cmd->set_y(0);
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmMoveToBottomLibrary: {
|
|
auto *cmd = new Command_MoveCard;
|
|
cmd->set_start_player_id(startPlayerId);
|
|
cmd->set_start_zone(startZone.toStdString());
|
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
|
cmd->set_target_player_id(getId());
|
|
cmd->set_target_zone("deck");
|
|
cmd->set_x(-1);
|
|
cmd->set_y(0);
|
|
|
|
if (idList.card_size() > 1) {
|
|
auto *scmd = new Command_Shuffle;
|
|
scmd->set_zone_name("deck");
|
|
scmd->set_start(-idList.card_size());
|
|
scmd->set_end(-1);
|
|
// Server process events backwards, so...
|
|
commandList.append(scmd);
|
|
}
|
|
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmMoveToHand: {
|
|
auto *cmd = new Command_MoveCard;
|
|
cmd->set_start_player_id(startPlayerId);
|
|
cmd->set_start_zone(startZone.toStdString());
|
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
|
cmd->set_target_player_id(getId());
|
|
cmd->set_target_zone("hand");
|
|
cmd->set_x(0);
|
|
cmd->set_y(0);
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmMoveToGraveyard: {
|
|
auto *cmd = new Command_MoveCard;
|
|
cmd->set_start_player_id(startPlayerId);
|
|
cmd->set_start_zone(startZone.toStdString());
|
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
|
cmd->set_target_player_id(getId());
|
|
cmd->set_target_zone("grave");
|
|
cmd->set_x(0);
|
|
cmd->set_y(0);
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
case cmMoveToExile: {
|
|
auto *cmd = new Command_MoveCard;
|
|
cmd->set_start_player_id(startPlayerId);
|
|
cmd->set_start_zone(startZone.toStdString());
|
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
|
cmd->set_target_player_id(getId());
|
|
cmd->set_target_zone("rfg");
|
|
cmd->set_x(0);
|
|
cmd->set_y(0);
|
|
commandList.append(cmd);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (local) {
|
|
sendGameCommand(prepareGameCommand(commandList));
|
|
} else {
|
|
game->sendGameCommand(prepareGameCommand(commandList));
|
|
}
|
|
}
|
|
|
|
void Player::actIncPT(int deltaP, int deltaT)
|
|
{
|
|
int playerid = id;
|
|
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
for (const auto &item : scene()->selectedItems()) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
QString pt = card->getPT();
|
|
const auto ptList = parsePT(pt);
|
|
QString newpt;
|
|
if (ptList.isEmpty()) {
|
|
newpt = QString::number(deltaP) + (deltaT ? "/" + QString::number(deltaT) : "");
|
|
} else if (ptList.size() == 1) {
|
|
newpt = QString::number(ptList.at(0).toInt() + deltaP) + (deltaT ? "/" + QString::number(deltaT) : "");
|
|
} else {
|
|
newpt =
|
|
QString::number(ptList.at(0).toInt() + deltaP) + "/" + QString::number(ptList.at(1).toInt() + deltaT);
|
|
}
|
|
|
|
auto *cmd = new Command_SetCardAttr;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_attribute(AttrPT);
|
|
cmd->set_attr_value(newpt.toStdString());
|
|
commandList.append(cmd);
|
|
|
|
if (local) {
|
|
playerid = card->getZone()->getPlayer()->getId();
|
|
}
|
|
}
|
|
|
|
game->sendGameCommand(prepareGameCommand(commandList), playerid);
|
|
}
|
|
|
|
void Player::actResetPT()
|
|
{
|
|
int playerid = id;
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
for (const auto &item : scene()->selectedItems()) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
QString ptString;
|
|
if (!card->getFaceDown()) { // leave the pt empty if the card is face down
|
|
CardInfoPtr info = card->getInfo();
|
|
if (info) {
|
|
ptString = info->getPowTough();
|
|
}
|
|
}
|
|
if (ptString == card->getPT()) {
|
|
continue;
|
|
}
|
|
QString zoneName = card->getZone()->getName();
|
|
auto *cmd = new Command_SetCardAttr;
|
|
cmd->set_zone(zoneName.toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_attribute(AttrPT);
|
|
cmd->set_attr_value(ptString.toStdString());
|
|
commandList.append(cmd);
|
|
|
|
if (local) {
|
|
playerid = card->getZone()->getPlayer()->getId();
|
|
}
|
|
}
|
|
|
|
if (!commandList.empty()) {
|
|
game->sendGameCommand(prepareGameCommand(commandList), playerid);
|
|
}
|
|
}
|
|
|
|
QVariantList Player::parsePT(const QString &pt)
|
|
{
|
|
QVariantList ptList = QVariantList();
|
|
if (!pt.isEmpty()) {
|
|
int sep = pt.indexOf('/');
|
|
if (sep == 0) {
|
|
ptList.append(QVariant(pt.mid(1))); // cut off starting '/' and take full string
|
|
} else {
|
|
int start = 0;
|
|
for (;;) {
|
|
QString item = pt.mid(start, sep - start);
|
|
if (item.isEmpty()) {
|
|
ptList.append(QVariant(QString()));
|
|
} else if (item[0] == '+') {
|
|
ptList.append(QVariant(item.mid(1).toInt())); // add as int
|
|
} else if (item[0] == '-') {
|
|
ptList.append(QVariant(item.toInt())); // add as int
|
|
} else {
|
|
ptList.append(QVariant(item)); // add as qstring
|
|
}
|
|
if (sep == -1) {
|
|
break;
|
|
}
|
|
start = sep + 1;
|
|
sep = pt.indexOf('/', start);
|
|
}
|
|
}
|
|
}
|
|
return ptList;
|
|
}
|
|
|
|
void Player::actSetPT()
|
|
{
|
|
QString oldPT;
|
|
int playerid = id;
|
|
|
|
auto sel = scene()->selectedItems();
|
|
for (const auto &item : sel) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
if (!card->getPT().isEmpty()) {
|
|
oldPT = card->getPT();
|
|
}
|
|
}
|
|
bool ok;
|
|
dialogSemaphore = true;
|
|
QString pt =
|
|
getTextWithMax(game, tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal, oldPT, &ok);
|
|
dialogSemaphore = false;
|
|
if (clearCardsToDelete() || !ok) {
|
|
return;
|
|
}
|
|
|
|
const auto ptList = parsePT(pt);
|
|
bool empty = ptList.isEmpty();
|
|
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
for (const auto &item : sel) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
auto *cmd = new Command_SetCardAttr;
|
|
QString newpt = QString();
|
|
if (!empty) {
|
|
const auto oldpt = parsePT(card->getPT());
|
|
int ptIter = 0;
|
|
for (const auto &item : ptList) {
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
if (item.typeId() == QMetaType::Type::Int) {
|
|
#else
|
|
if (item.type() == QVariant::Int) {
|
|
#endif
|
|
int oldItem = ptIter < oldpt.size() ? oldpt.at(ptIter).toInt() : 0;
|
|
newpt += '/' + QString::number(oldItem + item.toInt());
|
|
} else {
|
|
newpt += '/' + item.toString();
|
|
}
|
|
++ptIter;
|
|
}
|
|
newpt = newpt.mid(1);
|
|
}
|
|
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_attribute(AttrPT);
|
|
cmd->set_attr_value(newpt.toStdString());
|
|
commandList.append(cmd);
|
|
|
|
if (local) {
|
|
playerid = card->getZone()->getPlayer()->getId();
|
|
}
|
|
}
|
|
|
|
game->sendGameCommand(prepareGameCommand(commandList), playerid);
|
|
}
|
|
|
|
void Player::actDrawArrow()
|
|
{
|
|
if (!game->getActiveCard()) {
|
|
return;
|
|
}
|
|
|
|
game->getActiveCard()->drawArrow(Qt::red);
|
|
}
|
|
|
|
void Player::actIncP()
|
|
{
|
|
actIncPT(1, 0);
|
|
}
|
|
|
|
void Player::actDecP()
|
|
{
|
|
actIncPT(-1, 0);
|
|
}
|
|
|
|
void Player::actIncT()
|
|
{
|
|
actIncPT(0, 1);
|
|
}
|
|
|
|
void Player::actDecT()
|
|
{
|
|
actIncPT(0, -1);
|
|
}
|
|
|
|
void Player::actIncPT()
|
|
{
|
|
actIncPT(1, 1);
|
|
}
|
|
|
|
void Player::actDecPT()
|
|
{
|
|
actIncPT(-1, -1);
|
|
}
|
|
|
|
void Player::actFlowP()
|
|
{
|
|
actIncPT(1, -1);
|
|
}
|
|
|
|
void Player::actFlowT()
|
|
{
|
|
actIncPT(-1, 1);
|
|
}
|
|
|
|
void Player::actSetAnnotation()
|
|
{
|
|
QString oldAnnotation;
|
|
auto sel = scene()->selectedItems();
|
|
for (const auto &item : sel) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
if (!card->getAnnotation().isEmpty()) {
|
|
oldAnnotation = card->getAnnotation();
|
|
}
|
|
}
|
|
|
|
bool ok;
|
|
dialogSemaphore = true;
|
|
QString annotation = QInputDialog::getMultiLineText(game, tr("Set annotation"),
|
|
tr("Please enter the new annotation:"), oldAnnotation, &ok)
|
|
.left(MAX_NAME_LENGTH);
|
|
dialogSemaphore = false;
|
|
if (clearCardsToDelete() || !ok) {
|
|
return;
|
|
}
|
|
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
for (const auto &item : sel) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
auto *cmd = new Command_SetCardAttr;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_attribute(AttrAnnotation);
|
|
cmd->set_attr_value(annotation.toStdString());
|
|
commandList.append(cmd);
|
|
}
|
|
sendGameCommand(prepareGameCommand(commandList));
|
|
}
|
|
|
|
void Player::actAttach()
|
|
{
|
|
auto *card = game->getActiveCard();
|
|
if (!card) {
|
|
return;
|
|
}
|
|
|
|
card->drawAttachArrow();
|
|
}
|
|
|
|
void Player::actUnattach()
|
|
{
|
|
if (!game->getActiveCard()) {
|
|
return;
|
|
}
|
|
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
for (QGraphicsItem *item : scene()->selectedItems()) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
auto *cmd = new Command_AttachCard;
|
|
cmd->set_start_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
commandList.append(cmd);
|
|
}
|
|
sendGameCommand(prepareGameCommand(commandList));
|
|
}
|
|
|
|
void Player::actCardCounterTrigger()
|
|
{
|
|
auto *action = static_cast<QAction *>(sender());
|
|
int counterId = action->data().toInt() / 1000;
|
|
QList<const ::google::protobuf::Message *> commandList;
|
|
switch (action->data().toInt() % 1000) {
|
|
case 9: { // increment counter
|
|
for (const auto &item : scene()->selectedItems()) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
if (card->getCounters().value(counterId, 0) < MAX_COUNTERS_ON_CARD) {
|
|
auto *cmd = new Command_SetCardCounter;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_counter_id(counterId);
|
|
cmd->set_counter_value(card->getCounters().value(counterId, 0) + 1);
|
|
commandList.append(cmd);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 10: { // decrement counter
|
|
for (const auto &item : scene()->selectedItems()) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
if (card->getCounters().value(counterId, 0)) {
|
|
auto *cmd = new Command_SetCardCounter;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_counter_id(counterId);
|
|
cmd->set_counter_value(card->getCounters().value(counterId, 0) - 1);
|
|
commandList.append(cmd);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 11: { // set counter with dialog
|
|
bool ok;
|
|
dialogSemaphore = true;
|
|
|
|
int oldValue = 0;
|
|
if (scene()->selectedItems().size() == 1) {
|
|
auto *card = static_cast<CardItem *>(scene()->selectedItems().first());
|
|
oldValue = card->getCounters().value(counterId, 0);
|
|
}
|
|
int number = QInputDialog::getInt(game, tr("Set counters"), tr("Number:"), oldValue, 0,
|
|
MAX_COUNTERS_ON_CARD, 1, &ok);
|
|
dialogSemaphore = false;
|
|
if (clearCardsToDelete() || !ok) {
|
|
return;
|
|
}
|
|
|
|
for (const auto &item : scene()->selectedItems()) {
|
|
auto *card = static_cast<CardItem *>(item);
|
|
auto *cmd = new Command_SetCardCounter;
|
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
|
cmd->set_card_id(card->getId());
|
|
cmd->set_counter_id(counterId);
|
|
cmd->set_counter_value(number);
|
|
commandList.append(cmd);
|
|
}
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
sendGameCommand(prepareGameCommand(commandList));
|
|
}
|
|
|
|
void Player::actPlay()
|
|
{
|
|
if (!game->getActiveCard()) {
|
|
return;
|
|
}
|
|
|
|
bool cipt = game->getActiveCard()->getInfo() ? game->getActiveCard()->getInfo()->getCipt() : false;
|
|
playCard(game->getActiveCard(), false, cipt);
|
|
}
|
|
|
|
void Player::actHide()
|
|
{
|
|
if (!game->getActiveCard()) {
|
|
return;
|
|
}
|
|
|
|
game->getActiveCard()->getZone()->removeCard(game->getActiveCard());
|
|
}
|
|
|
|
void Player::actPlayFacedown()
|
|
{
|
|
if (!game->getActiveCard()) {
|
|
return;
|
|
}
|
|
|
|
playCard(game->getActiveCard(), true, false);
|
|
}
|
|
|
|
void Player::actReveal(QAction *action)
|
|
{
|
|
const int otherPlayerId = action->data().toInt();
|
|
|
|
Command_RevealCards cmd;
|
|
if (otherPlayerId != -1) {
|
|
cmd.set_player_id(otherPlayerId);
|
|
}
|
|
|
|
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
|
while (!sel.isEmpty()) {
|
|
const auto *card = qgraphicsitem_cast<CardItem *>(sel.takeFirst());
|
|
if (!cmd.has_zone_name()) {
|
|
cmd.set_zone_name(card->getZone()->getName().toStdString());
|
|
}
|
|
cmd.add_card_id(card->getId());
|
|
}
|
|
|
|
sendGameCommand(cmd);
|
|
}
|
|
|
|
void Player::refreshShortcuts()
|
|
{
|
|
if (shortcutsActive) {
|
|
setShortcutsActive();
|
|
}
|
|
}
|
|
|
|
void Player::updateCardMenu(const CardItem *card)
|
|
{
|
|
// If bad card OR is a spectator (as spectators don't need card menus), return
|
|
// only update the menu if the card is actually selected
|
|
if (card == nullptr || (game->isSpectator() && !judge) || game->getActiveCard() != card) {
|
|
return;
|
|
}
|
|
|
|
QMenu *cardMenu = card->getCardMenu();
|
|
QMenu *ptMenu = card->getPTMenu();
|
|
QMenu *moveMenu = card->getMoveMenu();
|
|
|
|
cardMenu->clear();
|
|
|
|
bool revealedCard = false;
|
|
bool writeableCard = getLocalOrJudge();
|
|
if (card->getZone() && card->getZone()->getIsView()) {
|
|
auto *view = dynamic_cast<ZoneViewZone *>(card->getZone());
|
|
if (view->getRevealZone()) {
|
|
if (view->getWriteableRevealZone()) {
|
|
writeableCard = true;
|
|
} else {
|
|
revealedCard = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (revealedCard) {
|
|
cardMenu->addAction(aHide);
|
|
addRelatedCardView(card, cardMenu);
|
|
} else if (writeableCard) {
|
|
if (moveMenu->isEmpty()) {
|
|
moveMenu->addAction(aMoveToTopLibrary);
|
|
moveMenu->addAction(aMoveToXfromTopOfLibrary);
|
|
moveMenu->addAction(aMoveToBottomLibrary);
|
|
moveMenu->addSeparator();
|
|
moveMenu->addAction(aMoveToHand);
|
|
moveMenu->addSeparator();
|
|
moveMenu->addAction(aMoveToGraveyard);
|
|
moveMenu->addSeparator();
|
|
moveMenu->addAction(aMoveToExile);
|
|
}
|
|
|
|
if (card->getZone()) {
|
|
if (card->getZone()->getName() == "table") {
|
|
// Card is on the battlefield
|
|
|
|
if (ptMenu->isEmpty()) {
|
|
ptMenu->addAction(aIncP);
|
|
ptMenu->addAction(aDecP);
|
|
ptMenu->addAction(aFlowP);
|
|
ptMenu->addSeparator();
|
|
ptMenu->addAction(aIncT);
|
|
ptMenu->addAction(aDecT);
|
|
ptMenu->addAction(aFlowT);
|
|
ptMenu->addSeparator();
|
|
ptMenu->addAction(aIncPT);
|
|
ptMenu->addAction(aDecPT);
|
|
ptMenu->addSeparator();
|
|
ptMenu->addAction(aSetPT);
|
|
ptMenu->addAction(aResetPT);
|
|
}
|
|
|
|
cardMenu->addAction(aTap);
|
|
cardMenu->addAction(aDoesntUntap);
|
|
cardMenu->addAction(aFlip);
|
|
if (card->getFaceDown()) {
|
|
cardMenu->addAction(aPeek);
|
|
}
|
|
|
|
addRelatedCardView(card, cardMenu);
|
|
addRelatedCardActions(card, cardMenu);
|
|
|
|
cardMenu->addSeparator();
|
|
cardMenu->addAction(aAttach);
|
|
if (card->getAttachedTo()) {
|
|
cardMenu->addAction(aUnattach);
|
|
}
|
|
cardMenu->addAction(aDrawArrow);
|
|
cardMenu->addSeparator();
|
|
cardMenu->addMenu(ptMenu);
|
|
cardMenu->addAction(aSetAnnotation);
|
|
cardMenu->addSeparator();
|
|
cardMenu->addAction(aClone);
|
|
cardMenu->addMenu(moveMenu);
|
|
|
|
for (int i = 0; i < aAddCounter.size(); ++i) {
|
|
cardMenu->addSeparator();
|
|
cardMenu->addAction(aAddCounter[i]);
|
|
if (card->getCounters().contains(i)) {
|
|
cardMenu->addAction(aRemoveCounter[i]);
|
|
}
|
|
cardMenu->addAction(aSetCounter[i]);
|
|
}
|
|
cardMenu->addSeparator();
|
|
} else if (card->getZone()->getName() == "stack") {
|
|
// Card is on the stack
|
|
cardMenu->addAction(aDrawArrow);
|
|
cardMenu->addSeparator();
|
|
cardMenu->addAction(aClone);
|
|
cardMenu->addMenu(moveMenu);
|
|
|
|
addRelatedCardView(card, cardMenu);
|
|
addRelatedCardActions(card, cardMenu);
|
|
} else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") {
|
|
// Card is in the graveyard or exile
|
|
cardMenu->addAction(aPlay);
|
|
cardMenu->addAction(aPlayFacedown);
|
|
cardMenu->addSeparator();
|
|
cardMenu->addAction(aClone);
|
|
cardMenu->addMenu(moveMenu);
|
|
|
|
addRelatedCardView(card, cardMenu);
|
|
addRelatedCardActions(card, cardMenu);
|
|
} else {
|
|
// Card is in hand or a custom zone specified by server
|
|
cardMenu->addAction(aPlay);
|
|
cardMenu->addAction(aPlayFacedown);
|
|
|
|
QMenu *revealMenu = cardMenu->addMenu(tr("Re&veal to..."));
|
|
initContextualPlayersMenu(revealMenu);
|
|
connect(revealMenu, &QMenu::triggered, this, &Player::actReveal);
|
|
|
|
cardMenu->addMenu(moveMenu);
|
|
addRelatedCardView(card, cardMenu);
|
|
}
|
|
} else {
|
|
cardMenu->addMenu(moveMenu);
|
|
}
|
|
} else {
|
|
if (card->getZone() && card->getZone()->getName() != "hand") {
|
|
cardMenu->addAction(aDrawArrow);
|
|
cardMenu->addSeparator();
|
|
addRelatedCardView(card, cardMenu);
|
|
addRelatedCardActions(card, cardMenu);
|
|
cardMenu->addSeparator();
|
|
cardMenu->addAction(aClone);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::addRelatedCardView(const CardItem *card, QMenu *cardMenu)
|
|
{
|
|
if (!card || !cardMenu) {
|
|
return;
|
|
}
|
|
auto cardInfo = card->getInfo();
|
|
if (!cardInfo) {
|
|
return;
|
|
}
|
|
|
|
bool atLeastOneGoodRelationFound = false;
|
|
QList<CardRelation *> relatedCards = cardInfo->getAllRelatedCards();
|
|
for (const CardRelation *cardRelation : relatedCards) {
|
|
CardInfoPtr relatedCard = db->getCard(cardRelation->getName());
|
|
if (relatedCard != nullptr) {
|
|
atLeastOneGoodRelationFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!atLeastOneGoodRelationFound) {
|
|
return;
|
|
}
|
|
|
|
cardMenu->addSeparator();
|
|
auto viewRelatedCards = new QMenu(tr("View related cards"));
|
|
cardMenu->addMenu(viewRelatedCards);
|
|
for (const CardRelation *relatedCard : relatedCards) {
|
|
QString relatedCardName = relatedCard->getName();
|
|
QAction *viewCard = viewRelatedCards->addAction(relatedCardName);
|
|
connect(viewCard, &QAction::triggered, game, [this, relatedCardName] { game->viewCardInfo(relatedCardName); });
|
|
}
|
|
}
|
|
|
|
void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu)
|
|
{
|
|
if (!card || !cardMenu) {
|
|
return;
|
|
}
|
|
auto cardInfo = card->getInfo();
|
|
if (!cardInfo) {
|
|
return;
|
|
}
|
|
|
|
QList<CardRelation *> relatedCards = cardInfo->getAllRelatedCards();
|
|
if (relatedCards.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
cardMenu->addSeparator();
|
|
int index = 0;
|
|
QAction *createRelatedCards = nullptr;
|
|
for (const CardRelation *cardRelation : relatedCards) {
|
|
CardInfoPtr relatedCard = db->getCard(cardRelation->getName());
|
|
if (relatedCard == nullptr)
|
|
continue;
|
|
|
|
QString relatedCardName;
|
|
if (relatedCard->getPowTough().size() > 0) {
|
|
relatedCardName = relatedCard->getPowTough() + " " + relatedCard->getName(); // "n/n name"
|
|
} else {
|
|
relatedCardName = relatedCard->getName(); // "name"
|
|
}
|
|
|
|
QString text = tr("Token: ");
|
|
if (cardRelation->getDoesAttach()) {
|
|
text +=
|
|
tr(cardRelation->getDoesTransform() ? "Transform into " : "Attach to ") + "\"" + relatedCardName + "\"";
|
|
} else if (cardRelation->getIsVariable()) {
|
|
text += "X " + relatedCardName;
|
|
} else if (cardRelation->getDefaultCount() != 1) {
|
|
text += QString::number(cardRelation->getDefaultCount()) + "x " + relatedCardName;
|
|
} else {
|
|
text += relatedCardName;
|
|
}
|
|
|
|
if (createRelatedCards == nullptr) {
|
|
if (relatedCards.length() == 1) {
|
|
createRelatedCards = new QAction(text, this); // set actCreateAllRelatedCards with this text
|
|
break; // do not set an individual entry as there is only one entry
|
|
} else {
|
|
createRelatedCards = new QAction(tr("All tokens"), this);
|
|
}
|
|
}
|
|
|
|
auto *createRelated = new QAction(text, this);
|
|
createRelated->setData(QVariant(index++));
|
|
connect(createRelated, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard()));
|
|
cardMenu->addAction(createRelated);
|
|
}
|
|
|
|
if (createRelatedCards) {
|
|
if (shortcutsActive) {
|
|
createRelatedCards->setShortcut(
|
|
SettingsCache::instance().shortcuts().getSingleShortcut("Player/aCreateRelatedTokens"));
|
|
}
|
|
connect(createRelatedCards, SIGNAL(triggered()), this, SLOT(actCreateAllRelatedCards()));
|
|
cardMenu->addAction(createRelatedCards);
|
|
}
|
|
}
|
|
|
|
void Player::setCardMenu(QMenu *menu)
|
|
{
|
|
if (aCardMenu != nullptr) {
|
|
aCardMenu->setEnabled(menu != nullptr);
|
|
aCardMenu->setMenu(menu);
|
|
}
|
|
}
|
|
|
|
QMenu *Player::getCardMenu() const
|
|
{
|
|
if (aCardMenu != nullptr) {
|
|
return aCardMenu->menu();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
QString Player::getName() const
|
|
{
|
|
return QString::fromStdString(userInfo->name());
|
|
}
|
|
|
|
qreal Player::getMinimumWidth() const
|
|
{
|
|
qreal result = table->getMinimumWidth() + CARD_HEIGHT + 15 + counterAreaWidth + stack->boundingRect().width();
|
|
if (!SettingsCache::instance().getHorizontalHand()) {
|
|
result += hand->boundingRect().width();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void Player::setGameStarted()
|
|
{
|
|
if (local) {
|
|
aAlwaysRevealTopCard->setChecked(false);
|
|
aAlwaysLookAtTopCard->setChecked(false);
|
|
}
|
|
setConceded(false);
|
|
}
|
|
|
|
void Player::setConceded(bool _conceded)
|
|
{
|
|
conceded = _conceded;
|
|
setVisible(!conceded);
|
|
if (conceded) {
|
|
clear();
|
|
}
|
|
emit playerCountChanged();
|
|
}
|
|
|
|
void Player::setZoneId(int _zoneId)
|
|
{
|
|
zoneId = _zoneId;
|
|
playerArea->setPlayerZoneId(_zoneId);
|
|
}
|
|
|
|
void Player::setMirrored(bool _mirrored)
|
|
{
|
|
if (mirrored != _mirrored) {
|
|
mirrored = _mirrored;
|
|
rearrangeZones();
|
|
}
|
|
}
|
|
|
|
void Player::processSceneSizeChange(int newPlayerWidth)
|
|
{
|
|
// Extend table (and hand, if horizontal) to accommodate the new player width.
|
|
qreal tableWidth = newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stack->boundingRect().width();
|
|
if (!SettingsCache::instance().getHorizontalHand()) {
|
|
tableWidth -= hand->boundingRect().width();
|
|
}
|
|
|
|
table->setWidth(tableWidth);
|
|
hand->setWidth(tableWidth + stack->boundingRect().width());
|
|
}
|
|
|
|
void Player::setLastToken(CardInfoPtr cardInfo)
|
|
{
|
|
if (cardInfo == nullptr || aCreateAnotherToken == nullptr) {
|
|
return;
|
|
}
|
|
|
|
lastTokenName = cardInfo->getName();
|
|
lastTokenColor = cardInfo->getColors().isEmpty() ? QString() : cardInfo->getColors().left(1).toLower();
|
|
lastTokenPT = cardInfo->getPowTough();
|
|
lastTokenAnnotation = SettingsCache::instance().getAnnotateTokens() ? cardInfo->getText() : "";
|
|
lastTokenTableRow = TableZone::clampValidTableRow(2 - cardInfo->getTableRow());
|
|
lastTokenDestroy = true;
|
|
aCreateAnotherToken->setText(tr("C&reate another %1 token").arg(lastTokenName));
|
|
aCreateAnotherToken->setEnabled(true);
|
|
}
|