#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 #include #include #include #include #include #include #include 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() << "deck" << 0); aMoveHandToBottomLibrary = new QAction(this); aMoveHandToBottomLibrary->setData(QList() << "deck" << -1); aMoveHandToGrave = new QAction(this); aMoveHandToGrave->setData(QList() << "grave" << 0); aMoveHandToRfg = new QAction(this); aMoveHandToRfg->setData(QList() << "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() << "deck" << 0); aMoveGraveToBottomLibrary = new QAction(this); aMoveGraveToBottomLibrary->setData(QList() << "deck" << -1); aMoveGraveToHand = new QAction(this); aMoveGraveToHand->setData(QList() << "hand" << 0); aMoveGraveToRfg = new QAction(this); aMoveGraveToRfg->setData(QList() << "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() << "deck" << 0); aMoveRfgToBottomLibrary = new QAction(this); aMoveRfgToBottomLibrary->setData(QList() << "deck" << -1); aMoveRfgToHand = new QAction(this); aMoveRfgToHand->setData(QList() << "hand" << 0); aMoveRfgToGrave = new QAction(this); aMoveRfgToGrave->setData(QList() << "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())); aMoveTopCardsUntil = new QAction(this); connect(aMoveTopCardsUntil, SIGNAL(triggered()), this, SLOT(actMoveTopCardsUntil())); 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); topLibraryMenu->addAction(aMoveTopCardsUntil); 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 &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(scene())->removePlayer(this); clear(); QMapIterator i(zones); while (i.hasNext()) delete i.next().value(); zones.clear(); delete playerMenu; delete userInfo; } void Player::clear() { clearArrows(); QMapIterator 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 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(sender()); auto *menu = static_cast(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")); aMoveTopCardToBottom->setText(tr("Put top card on &bottom")); 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...")); aMoveTopCardsUntil->setText(tr("Take top cards &until...")); 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 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 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 addCCShortCuts; addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCRed")); addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCYellow")); addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCGreen")); QList removeCCShortCuts; removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCRed")); removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCYellow")); removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCGreen")); QList 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 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")); aMoveTopCardsUntil->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardsUntil")); 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()); aMoveTopCardsUntil->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 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(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(scene())->toggleZoneView(this, "deck", -1); } void Player::actViewHand() { static_cast(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(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(scene())->toggleZoneView(this, "grave", -1); } void Player::actRevealRandomGraveyardCard() { Command_RevealCards cmd; auto *action = dynamic_cast(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(scene())->toggleZoneView(this, "rfg", -1); } void Player::actViewSideboard() { static_cast(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(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::actMoveTopCardsUntil() { QString expr = previousMovingCardsUntilExpr; for (;;) { bool ok; expr = QInputDialog::getText(game, "Take top cards until", "Select card (accepts search syntax)", {}, expr, &ok); if (!ok) { return; } movingCardsUntilFilter = FilterString(expr); if (movingCardsUntilFilter.valid()) { break; } else { auto button = QMessageBox::warning(game, "Invalid filter", movingCardsUntilFilter.error()); if (button != QMessageBox::Ok) { return; } } } previousMovingCardsUntilExpr = expr; if (zones.value("deck")->getCards().empty()) { movingCardsUntil = false; } else { movingCardsUntil = true; actMoveTopCardToPlay(); } } void Player::moveOneCardUntil(const QString &cardName) { auto card = db->getCard(cardName); if (zones.value("deck")->getCards().empty() || card.isNull() || movingCardsUntilFilter.check(card)) { movingCardsUntil = false; } else { QTimer::singleShot(100, [this]() { actMoveTopCardToPlay(); }); } } 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(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(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(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 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(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(arrow->getStartItem()); auto *targetCard = qgraphicsitem_cast(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; } QString startZoneString = QString::fromStdString(event.start_zone()); CardZone *startZone = startPlayer->getZones().value(startZoneString, 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 &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 playerIterator(game->getPlayers()); while (playerIterator.hasNext()) { Player *p = playerIterator.next().value(); QList arrowsToDelete; QMapIterator 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); if (movingCardsUntil && startZoneString == "deck" && targetZone->getName() == "stack") { moveOneCardUntil(card->getName()); } } 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 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 &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 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(zone)) { zone->getCards().first()->setName(cardName); zone->update(); showZoneView = false; } } if (showZoneView && !cardList.isEmpty()) { static_cast(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 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 counterIterator(counters); while (counterIterator.hasNext()) { counterIterator.next().value()->delCounter(); } counters.clear(); } ArrowItem *Player::addArrow(const ServerInfo_Arrow &arrow) { const QMap &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 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 &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 sel = scene()->selectedItems(); if (sel.isEmpty()) { return; } QList cardList; while (!sel.isEmpty()) { cardList.append(qgraphicsitem_cast(sel.takeFirst())); } QList 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(sender()); QList sel = scene()->selectedItems(); QList cardList; while (!sel.isEmpty()) { cardList.append(qgraphicsitem_cast(sel.takeFirst())); } QList commandList; if (a->data().toInt() <= (int)cmClone) { for (const auto &card : cardList) { switch (static_cast(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(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(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 commandList; for (const auto &item : scene()->selectedItems()) { auto *card = static_cast(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 commandList; for (const auto &item : scene()->selectedItems()) { auto *card = static_cast(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(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 commandList; for (const auto &item : sel) { auto *card = static_cast(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(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 commandList; for (const auto &item : sel) { auto *card = static_cast(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 commandList; for (QGraphicsItem *item : scene()->selectedItems()) { auto *card = static_cast(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(sender()); int counterId = action->data().toInt() / 1000; QList commandList; switch (action->data().toInt() % 1000) { case 9: { // increment counter for (const auto &item : scene()->selectedItems()) { auto *card = static_cast(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(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(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(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 sel = scene()->selectedItems(); while (!sel.isEmpty()) { const auto *card = qgraphicsitem_cast(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(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 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 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); }