From e674a39b878d020275a5df6645336d271a4d7952 Mon Sep 17 00:00:00 2001 From: Christo Date: Tue, 9 Jun 2026 13:54:01 +0800 Subject: [PATCH 1/4] Fix #6952: prevent deck loss when saving to a full disk (#6978) * Fix #6952: prevent deck loss when saving to a full disk DeckLoader::saveToFile() opened the target with QFile in WriteOnly mode, which truncates the existing file to 0 bytes the moment it is opened. The serializers (DeckList::saveToFile_Native/_Plain) always return true and the result of flush() was ignored, so a write that failed part-way -- e.g. because the disk was full -- left a 0-byte file behind yet was still reported (and logged) as a successful save. The same truncate-then-write pattern in updateLastLoadedTimestamp() could destroy a deck on load. Switch both paths to QSaveFile, which writes to a temporary file and only atomically replaces the target if commit() succeeds. On any write or flush failure commit() returns false, the original deck is left untouched, and the failure is logged instead of being reported as success. * Use QSaveFile in convertToCockatriceFormat() too convertToCockatriceFormat() had the same data-loss pattern: QFile WriteOnly truncated the .cod, saveToFile_Native() always returns true, and the original file was then removed unconditionally -- so a full disk during conversion wrote a 0-byte .cod and then deleted the source deck. Switch to QSaveFile (write + atomic commit), remove the original only after a successful commit, and move the format check ahead of the file open so an already-Cockatrice or unsupported deck never truncates or deletes anything. Raised in review by ZeldaZach. --- .../src/interface/deck_loader/deck_loader.cpp | 137 ++++++++++-------- 1 file changed, 77 insertions(+), 60 deletions(-) diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp index e616c5eb5..39a0c1071 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.cpp +++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,10 @@ std::optional DeckLoader::loadFromRemote(const QString &nativeString std::optional DeckLoader::saveToFile(const DeckList &deck, const QString &fileName, DeckFileFormat::Format fmt) { - QFile file(fileName); + // Use QSaveFile so that a failed write (e.g. a full disk) leaves the existing deck untouched + // instead of truncating it to a 0-byte file. The target is only replaced once every byte has + // been flushed successfully in commit(). + QSaveFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(DeckLoaderLog) << "Could not create or open file:" << fileName; return std::nullopt; @@ -145,15 +149,19 @@ DeckLoader::saveToFile(const DeckList &deck, const QString &fileName, DeckFileFo break; } - file.flush(); - file.close(); - - qCInfo(DeckLoaderLog) << "Saved deck to " << fileName << "with format" << fmt << "-" << success; - if (!success) { + file.cancelWriting(); + qCWarning(DeckLoaderLog) << "Failed to serialize deck for file:" << fileName; return std::nullopt; } + if (!file.commit()) { + qCWarning(DeckLoaderLog) << "Failed to save deck to " << fileName << ":" << file.errorString(); + return std::nullopt; + } + + qCInfo(DeckLoaderLog) << "Saved deck to " << fileName << "with format" << fmt; + LoadedDeck::LoadInfo lastLoadInfo = {fileName, fmt}; return lastLoadInfo; } @@ -196,38 +204,44 @@ bool DeckLoader::updateLastLoadedTimestamp(LoadedDeck &deck) QDateTime originalTimestamp = fileInfo.lastModified(); - // Open the file for writing - QFile file(fileName); + // Use QSaveFile so that a failed write (e.g. a full disk) cannot truncate an existing deck to a + // 0-byte file while merely bumping its timestamp. + QSaveFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(DeckLoaderLog) << "Failed to open file for writing:" << fileName; return false; } - bool result = false; - // Perform file modifications deck.deckList.setLastLoadedTimestamp(QDateTime::currentDateTime().toString()); - result = deck.deckList.saveToFile_Native(&file); - file.close(); // Close the file to ensure changes are flushed - - if (result) { - // Re-open the file and set the original timestamp - if (!file.open(QIODevice::ReadWrite)) { - qCWarning(DeckLoaderLog) << "Failed to re-open file to set timestamp:" << fileName; - return false; - } - - if (!file.setFileTime(originalTimestamp, QFileDevice::FileModificationTime)) { - qCWarning(DeckLoaderLog) << "Failed to set modification time for file:" << fileName; - file.close(); - return false; - } - - file.close(); + if (!deck.deckList.saveToFile_Native(&file)) { + file.cancelWriting(); + qCWarning(DeckLoaderLog) << "Failed to serialize deck for file:" << fileName; + return false; } - return result; + if (!file.commit()) { + qCWarning(DeckLoaderLog) << "Failed to update timestamp for file:" << fileName << ":" << file.errorString(); + return false; + } + + // Re-open the file and restore the original timestamp, so that updating the lastLoadedTimestamp + // does not change the file's modification time. + QFile timestampFile(fileName); + if (!timestampFile.open(QIODevice::ReadWrite)) { + qCWarning(DeckLoaderLog) << "Failed to re-open file to set timestamp:" << fileName; + return false; + } + + if (!timestampFile.setFileTime(originalTimestamp, QFileDevice::FileModificationTime)) { + qCWarning(DeckLoaderLog) << "Failed to set modification time for file:" << fileName; + timestampFile.close(); + return false; + } + + timestampFile.close(); + return true; } static QString getDomainForWebsite(DeckLoader::DecklistWebsite website) @@ -444,51 +458,54 @@ bool DeckLoader::convertToCockatriceFormat(LoadedDeck &deck) return false; } + // Determine the format before touching any file, so an already-converted or + // unsupported deck never truncates or deletes anything. + switch (DeckFileFormat::getFormatFromName(fileName)) { + case DeckFileFormat::PlainText: + break; + case DeckFileFormat::Cockatrice: + qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed."; + return true; + default: + qCWarning(DeckLoaderLog) << "Unsupported file format for conversion:" << fileName; + return false; + } + // Change the file extension to .cod QFileInfo fileInfo(fileName); QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod"); - // Open the new file for writing - QFile file(newFileName); + // Use QSaveFile so a failed write (e.g. a full disk) cannot leave a 0-byte .cod + // behind and then delete the original deck. + QSaveFile file(newFileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(DeckLoaderLog) << "Failed to open file for writing:" << newFileName; return false; } - bool result = false; - - // Perform file modifications based on the detected format - switch (DeckFileFormat::getFormatFromName(fileName)) { - case DeckFileFormat::PlainText: - // Save in Cockatrice's native format - result = deck.deckList.saveToFile_Native(&file); - break; - case DeckFileFormat::Cockatrice: - qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed."; - result = true; - break; - default: - qCWarning(DeckLoaderLog) << "Unsupported file format for conversion:" << fileName; - result = false; - break; + if (!deck.deckList.saveToFile_Native(&file)) { + file.cancelWriting(); + qCWarning(DeckLoaderLog) << "Failed to serialize deck for file:" << newFileName; + return false; } - file.close(); - - // Delete the old file if conversion was successful - if (result) { - if (!QFile::remove(fileName)) { - qCWarning(DeckLoaderLog) << "Failed to delete original file:" << fileName; - } else { - qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName; - } - deck.lastLoadInfo = { - .fileName = newFileName, - .fileFormat = DeckFileFormat::Cockatrice, - }; + if (!file.commit()) { + qCWarning(DeckLoaderLog) << "Failed to convert deck to " << newFileName << ":" << file.errorString(); + return false; } - return result; + // Conversion succeeded: delete the original file. + if (!QFile::remove(fileName)) { + qCWarning(DeckLoaderLog) << "Failed to delete original file:" << fileName; + } else { + qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName; + } + deck.lastLoadInfo = { + .fileName = newFileName, + .fileFormat = DeckFileFormat::Cockatrice, + }; + + return true; } void DeckLoader::printDeckListNode(QTextCursor *cursor, const InnerDecklistNode *node) From 9e03f826165604f89c033fc368c3412652ff92f0 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 9 Jun 2026 08:05:39 +0200 Subject: [PATCH 2/4] [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem (#6944) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem Took 4 minutes Took 48 seconds * Drop early return. Took 1 hour 13 minutes Took 2 minutes Took 1 minute * Delete player view. Took 37 seconds * Restore card counter color in menu. Took 5 minutes --------- Co-authored-by: Lukas Brübach --- .../src/game/board/abstract_card_item.h | 5 + cockatrice/src/game/board/card_item.cpp | 39 ++-- cockatrice/src/game/game_scene.cpp | 101 ++++++++++- cockatrice/src/game/game_scene.h | 15 ++ cockatrice/src/game/player/menu/card_menu.cpp | 133 +++++++------- cockatrice/src/game/player/menu/card_menu.h | 9 +- .../src/game/player/menu/custom_zone_menu.cpp | 10 +- .../src/game/player/menu/custom_zone_menu.h | 6 +- cockatrice/src/game/player/menu/move_menu.cpp | 27 ++- cockatrice/src/game/player/menu/move_menu.h | 4 +- .../src/game/player/menu/player_menu.cpp | 50 +++--- cockatrice/src/game/player/menu/player_menu.h | 13 +- cockatrice/src/game/player/menu/pt_menu.cpp | 34 ++-- cockatrice/src/game/player/menu/pt_menu.h | 4 +- .../src/game/player/menu/utility_menu.cpp | 40 +++-- .../src/game/player/menu/utility_menu.h | 10 +- cockatrice/src/game/player/player_actions.cpp | 167 +++++++++--------- cockatrice/src/game/player/player_actions.h | 66 ++++--- .../src/game/player/player_event_handler.cpp | 13 +- .../src/game/player/player_event_handler.h | 1 + .../src/game/player/player_graphics_item.cpp | 31 ++-- .../src/game/player/player_graphics_item.h | 10 +- cockatrice/src/game/player/player_logic.cpp | 23 +-- cockatrice/src/game/player/player_logic.h | 18 +- .../src/game_graphics/zones/table_zone.cpp | 15 +- .../src/game_graphics/zones/table_zone.h | 4 +- .../src/interface/widgets/tabs/tab_game.cpp | 56 +++--- .../src/interface/widgets/tabs/tab_game.h | 2 +- 28 files changed, 538 insertions(+), 368 deletions(-) diff --git a/cockatrice/src/game/board/abstract_card_item.h b/cockatrice/src/game/board/abstract_card_item.h index ed545e1ab..863954b73 100644 --- a/cockatrice/src/game/board/abstract_card_item.h +++ b/cockatrice/src/game/board/abstract_card_item.h @@ -44,6 +44,11 @@ signals: void deleteCardInfoPopup(QString cardName); void sigPixmapUpdated(); void cardShiftClicked(QString cardName); + void rightClicked(AbstractCardItem *card, QPoint screenPos); + void playSelected(AbstractCardItem *card); + void playSelectedFaceDown(AbstractCardItem *card); + void hideSelected(AbstractCardItem *card); + void selectionChanged(AbstractCardItem *card, bool selected); public: enum diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index a08194540..16197ae16 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -40,7 +40,7 @@ void CardItem::prepareDelete() { if (owner != nullptr) { if (owner->getGame()->getActiveCard() == this) { - owner->getPlayerMenu()->updateCardMenu(nullptr); + emit owner->requestCardMenuUpdate(nullptr); owner->getGame()->setActiveCard(nullptr); } owner = nullptr; @@ -399,8 +399,11 @@ void CardItem::playCard(bool faceDown) emit tz->toggleTapped(); } else { if (SettingsCache::instance().getClickPlaysAllSelected()) { - faceDown ? state->getZone()->getPlayer()->getPlayerActions()->actPlayFacedown() - : state->getZone()->getPlayer()->getPlayerActions()->actPlay(); + if (faceDown) { + emit playSelectedFaceDown(this); + } else { + emit playSelected(this); + } } else { state->getZone()->getPlayer()->getPlayerActions()->playCard(this, faceDown); } @@ -460,7 +463,7 @@ void CardItem::handleClickedToPlay(bool shiftHeld) { if (isUnwritableRevealZone(state->getZone())) { if (SettingsCache::instance().getClickPlaysAllSelected()) { - state->getZone()->getPlayer()->getPlayerActions()->actHide(); + emit hideSelected(this); } else { state->getZone()->removeCard(this); } @@ -471,17 +474,11 @@ void CardItem::handleClickedToPlay(bool shiftHeld) void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - if (event->button() == Qt::RightButton) { - - if (owner != nullptr) { - owner->getGame()->setActiveCard(this); - if (QMenu *cardMenu = owner->getPlayerMenu()->updateCardMenu(this)) { - cardMenu->popup(event->screenPos()); - return; - } - } - } else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) && - (!SettingsCache::instance().getDoubleClickToPlay())) { + if (event->button() == Qt::RightButton && owner != nullptr) { + emit rightClicked(this, event->screenPos()); + } + if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) && + (!SettingsCache::instance().getDoubleClickToPlay())) { handleClickedToPlay(event->modifiers().testFlag(Qt::ShiftModifier)); } @@ -531,14 +528,14 @@ bool CardItem::animationEvent() QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value) { if ((change == ItemSelectedHasChanged) && owner != nullptr) { - if (value == true) { - owner->getGame()->setActiveCard(this); - owner->getPlayerMenu()->updateCardMenu(this); - } else if (owner->getGameScene()->selectedItems().isEmpty()) { + bool selected = value.toBool(); - owner->getGame()->setActiveCard(nullptr); - owner->getPlayerMenu()->updateCardMenu(nullptr); + if (selected) { + owner->getGame()->setActiveCard(this); } + + emit selectionChanged(this, selected); } + return AbstractCardItem::itemChange(change, value); } diff --git a/cockatrice/src/game/game_scene.cpp b/cockatrice/src/game/game_scene.cpp index 867869a3f..dc55ecfe9 100644 --- a/cockatrice/src/game/game_scene.cpp +++ b/cockatrice/src/game/game_scene.cpp @@ -4,8 +4,10 @@ #include "../game_graphics/zones/select_zone.h" #include "../game_graphics/zones/view_zone.h" #include "../game_graphics/zones/view_zone_widget.h" +#include "abstract_game.h" #include "board/card_item.h" #include "phases_toolbar.h" +#include "player/player_actions.h" #include "player/player_graphics_item.h" #include "player/player_logic.h" @@ -72,6 +74,80 @@ QList GameScene::selectedCards() const return selectedCards; } +void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool selected) +{ + CardItem *card = qobject_cast(abstractCard); + if (!card || !card->getOwner()) { + return; + } + + auto *owner = card->getOwner(); + + if (selected) { + owner->requestCardMenuUpdate(card); + return; + } + + if (selectedItems().isEmpty()) { + owner->getGame()->setActiveCard(nullptr); + owner->requestCardMenuUpdate(nullptr); + } +} + +void GameScene::onCardRightClicked(AbstractCardItem *abstractCard, QPoint screenPos) +{ + auto *card = qobject_cast(abstractCard); + if (!card) { + return; + } + if (!card->getOwner()) { + return; + } + auto *view = playerViews.value(card->getOwner()->getPlayerInfo()->getId()); + if (!view) { + return; + } + + card->getOwner()->getGame()->setActiveCard(card); + + if (auto *menu = view->getPlayerMenu()->updateCardMenu(card)) { + menu->popup(screenPos); + } +} + +void GameScene::playSelected(AbstractCardItem *card) +{ + if (!card) { + return; + } + if (!card->getOwner()) { + return; + } + card->getOwner()->getPlayerActions()->actPlay(selectedCards()); +} + +void GameScene::playSelectedFaceDown(AbstractCardItem *card) +{ + if (!card) { + return; + } + if (!card->getOwner()) { + return; + } + card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCards()); +} + +void GameScene::hideSelected(AbstractCardItem *card) +{ + if (!card) { + return; + } + if (!card->getOwner()) { + return; + } + card->getOwner()->getPlayerActions()->actHide(selectedCards()); +} + /** * @brief Adds a player to the scene and stores their graphics item. * @param player Player to add. @@ -82,9 +158,11 @@ void GameScene::addPlayer(PlayerLogic *player) { qCInfo(GameScenePlayerAdditionRemovalLog) << "GameScene::addPlayer name=" << player->getPlayerInfo()->getName(); - playerViews.insert(player->getPlayerInfo()->getId(), player->getGraphicsItem()); - addItem(player->getGraphicsItem()); - connect(player->getGraphicsItem(), &PlayerGraphicsItem::sizeChanged, this, &GameScene::rearrange); + auto *view = new PlayerGraphicsItem(player); + + playerViews.insert(player->getPlayerInfo()->getId(), view); + addItem(view); + connect(view, &PlayerGraphicsItem::sizeChanged, this, &GameScene::rearrange); connect(player, &PlayerLogic::concededChanged, this, [this](int id, bool conceded) { if (conceded) { @@ -93,6 +171,8 @@ void GameScene::addPlayer(PlayerLogic *player) rearrange(); }); + connect(player, &PlayerLogic::requestZoneViewToggle, this, &GameScene::toggleZoneView); + connect(player, &PlayerLogic::requestRevealedZoneView, this, &GameScene::addRevealedZoneView); connect(player, &PlayerLogic::arrowDeleted, this, &GameScene::deleteArrow); connect(player, &PlayerLogic::arrowCreateRequested, this, &GameScene::addArrow); connect(player, &PlayerLogic::arrowDeleteRequested, this, &GameScene::requestArrowDeletion); @@ -123,6 +203,7 @@ void GameScene::removePlayer(PlayerLogic *player) } auto *view = playerViews.take(player->getPlayerInfo()->getId()); removeItem(view); + view->deleteLater(); rearrange(); } @@ -204,7 +285,7 @@ QList GameScene::collectActivePlayers(int &firstPlayerIndex) cons bool firstPlayerFound = false; for (auto *pgItem : playerViews.values()) { - PlayerLogic *p = pgItem->getPlayer(); + PlayerLogic *p = pgItem->getLogic(); if (p && !p->getConceded()) { activePlayers.append(p); if (!firstPlayerFound && p->getPlayerInfo()->getLocal()) { @@ -275,12 +356,12 @@ QSizeF GameScene::computeSceneSizeAndPlayerLayout(const QList &pl for (int j = 0; j < rowsInColumn; ++j) { PlayerLogic *player = playersIter.next(); if (col == 0) { - playersByColumn[col].prepend(player->getGraphicsItem()); + playersByColumn[col].prepend(playerViews.value(player->getPlayerInfo()->getId())); } else { - playersByColumn[col].append(player->getGraphicsItem()); + playersByColumn[col].append(playerViews.value(player->getPlayerInfo()->getId())); } - auto *pgItem = player->getGraphicsItem(); + auto *pgItem = playerViews.value(player->getPlayerInfo()->getId()); thisColumnHeight += pgItem->boundingRect().height() + playerAreaSpacing; columnWidth[col] = std::max(columnWidth[col], (int)pgItem->boundingRect().width()); } @@ -375,7 +456,8 @@ void GameScene::addArrow(QSharedPointer data) return; } - auto *startZone = startView->getPlayer()->getZones().value(data->startZone); + PlayerLogic *startLogic = startView->getLogic(); + auto *startZone = startLogic->getZones().value(data->startZone); if (!startZone) { return; } @@ -389,7 +471,8 @@ void GameScene::addArrow(QSharedPointer data) if (data->isPlayerTargeted()) { targetItem = targetView->getPlayerTarget(); } else { - if (auto *zone = targetView->getPlayer()->getZones().value(data->targetZone)) { + auto *zone = targetView->getLogic()->getZones().value(data->targetZone); + if (zone) { targetItem = zone->getCard(data->targetCardId); } } diff --git a/cockatrice/src/game/game_scene.h b/cockatrice/src/game/game_scene.h index 567089fc0..0587566d0 100644 --- a/cockatrice/src/game/game_scene.h +++ b/cockatrice/src/game/game_scene.h @@ -97,6 +97,16 @@ public: */ void removePlayer(PlayerLogic *player); + QMap getPlayers() const + { + return playerViews; + } + + PlayerGraphicsItem *viewForPlayer(int playerId) + { + return playerViews.value(playerId); + } + /** * @brief Adjusts the global rotation offset for player layout. * @param rotationAdjustment Number of positions to rotate. @@ -182,6 +192,11 @@ public: void stopRubberBand(); public slots: + void onCardSelectionChanged(AbstractCardItem *card, bool selected); + void onCardRightClicked(AbstractCardItem *card, QPoint screenPos); + void playSelected(AbstractCardItem *card); + void playSelectedFaceDown(AbstractCardItem *card); + void hideSelected(AbstractCardItem *card); /** @brief Toggles a zone view for a player. */ void toggleZoneView(PlayerLogic *player, const QString &zoneName, int numberCards, bool isReversed = false); diff --git a/cockatrice/src/game/player/menu/card_menu.cpp b/cockatrice/src/game/player/menu/card_menu.cpp index 3b866d4e0..ba925afb0 100644 --- a/cockatrice/src/game/player/menu/card_menu.cpp +++ b/cockatrice/src/game/player/menu/card_menu.cpp @@ -31,93 +31,92 @@ static QIcon createCircleIcon(const QColor &color) return QIcon(pixmap); } -CardMenu::CardMenu(PlayerLogic *_player, const CardItem *_card, bool _shortcutsActive) +template +static QAction *makeAction(QObject *parent, Slot &&slot, bool checkable = false, bool checked = false) +{ + auto *a = new QAction(parent); + a->setCheckable(checkable); + if (checkable) { + a->setChecked(checked); + } + QObject::connect(a, &QAction::triggered, parent, std::forward(slot)); + return a; +} + +CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _shortcutsActive) : player(_player), card(_card), shortcutsActive(_shortcutsActive) { - auto playerActions = player->getPlayerActions(); - - const QList &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const QList &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto playerToAdd : players) { - if (playerToAdd == player) { + if (playerToAdd == player->getLogic()) { continue; } playersInfo.append(qMakePair(playerToAdd->getPlayerInfo()->getName(), playerToAdd->getPlayerInfo()->getId())); } - connect(player->getGame()->getPlayerManager(), &PlayerManager::playerRemoved, this, &CardMenu::removePlayer); + connect(player->getLogic()->getGame()->getPlayerManager(), &PlayerManager::playerRemoved, this, + &CardMenu::removePlayer); - aTap = new QAction(this); - aTap->setData(cmTap); - connect(aTap, &QAction::triggered, playerActions, &PlayerActions::cardMenuAction); - aDoesntUntap = new QAction(this); - aDoesntUntap->setData(cmDoesntUntap); - aDoesntUntap->setCheckable(true); - aDoesntUntap->setChecked(card != nullptr && card->getDoesntUntap()); - connect(aDoesntUntap, &QAction::triggered, playerActions, &PlayerActions::cardMenuAction); + auto *actions = player->getLogic()->getPlayerActions(); + auto *gameScene = player->getGameScene(); + + // Single selection resolver used by all lambdas — called at trigger time + auto sel = [gameScene]() { return gameScene->selectedCards(); }; + + // Unified dispatcher for card menu actions + auto invoke = [actions, sel](CardMenuActionType type) { + return [actions, sel, type]() { actions->cardMenuAction(sel(), type); }; + }; + + // Actions using invoke (type dispatch, need selection) + aTap = makeAction(this, invoke(cmTap)); + aDoesntUntap = makeAction(this, invoke(cmDoesntUntap), /*checkable=*/true, card && card->getDoesntUntap()); + aFlip = makeAction(this, invoke(cmFlip)); + aPeek = makeAction(this, invoke(cmPeek)); + aClone = makeAction(this, invoke(cmClone)); + + // Actions using selection directly + aUnattach = makeAction(this, [actions, sel]() { actions->actUnattach(sel()); }); + aSetAnnotation = makeAction(this, [actions, sel]() { actions->actSetAnnotation(sel()); }); + aPlay = makeAction(this, [actions, sel]() { actions->actPlay(sel()); }); + aPlayFacedown = makeAction(this, [actions, sel]() { actions->actPlayFacedown(sel()); }); + aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); }); + aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); }); + + // Actions that use activeCard, not selection — direct connection aAttach = new QAction(this); - connect(aAttach, &QAction::triggered, playerActions, &PlayerActions::actAttach); - aUnattach = new QAction(this); - connect(aUnattach, &QAction::triggered, playerActions, &PlayerActions::actUnattach); aDrawArrow = new QAction(this); - connect(aDrawArrow, &QAction::triggered, playerActions, &PlayerActions::actDrawArrow); - aSetAnnotation = new QAction(this); - connect(aSetAnnotation, &QAction::triggered, playerActions, &PlayerActions::actSetAnnotation); - aFlip = new QAction(this); - aFlip->setData(cmFlip); - connect(aFlip, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - aPeek = new QAction(this); - aPeek->setData(cmPeek); - connect(aPeek, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - aClone = new QAction(this); - aClone->setData(cmClone); - connect(aClone, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); aSelectAll = new QAction(this); - connect(aSelectAll, &QAction::triggered, playerActions, &PlayerActions::actSelectAll); aSelectRow = new QAction(this); - connect(aSelectRow, &QAction::triggered, playerActions, &PlayerActions::actSelectRow); aSelectColumn = new QAction(this); - connect(aSelectColumn, &QAction::triggered, playerActions, &PlayerActions::actSelectColumn); - aReduceLifeByPower = new QAction(this); - connect(aReduceLifeByPower, &QAction::triggered, playerActions, &PlayerActions::actReduceLifeByPower); - - aPlay = new QAction(this); - connect(aPlay, &QAction::triggered, playerActions, &PlayerActions::actPlay); - aHide = new QAction(this); - connect(aHide, &QAction::triggered, playerActions, &PlayerActions::actHide); - aPlayFacedown = new QAction(this); - connect(aPlayFacedown, &QAction::triggered, playerActions, &PlayerActions::actPlayFacedown); + connect(aAttach, &QAction::triggered, actions, &PlayerActions::actAttach); + connect(aDrawArrow, &QAction::triggered, actions, &PlayerActions::actDrawArrow); + connect(aSelectAll, &QAction::triggered, actions, &PlayerActions::actSelectAll); + connect(aSelectRow, &QAction::triggered, actions, &PlayerActions::actSelectRow); + connect(aSelectColumn, &QAction::triggered, actions, &PlayerActions::actSelectColumn); aRevealToAll = new QAction(this); mCardCounters = new QMenu; + // Card counters for (int i = 0; i < 6; ++i) { QColor color = SettingsCache::instance().cardCounters().color(i); QIcon circleIcon = createCircleIcon(color); - auto *tempAddCounter = new QAction(this); - tempAddCounter->setIconVisibleInMenu(true); - tempAddCounter->setIcon(circleIcon); + auto *addAction = makeAction(this, [actions, sel, i]() { actions->actAddCardCounter(sel(), i); }); + addAction->setIcon(circleIcon); + aAddCounter.append(addAction); - auto *tempRemoveCounter = new QAction(this); - tempRemoveCounter->setIconVisibleInMenu(true); - tempRemoveCounter->setIcon(circleIcon); + auto *removeAction = makeAction(this, [actions, sel, i]() { actions->actRemoveCardCounter(sel(), i); }); + removeAction->setIcon(circleIcon); + aRemoveCounter.append(removeAction); - auto *tempSetCounter = new QAction(this); - tempSetCounter->setIconVisibleInMenu(true); - tempSetCounter->setIcon(circleIcon); - - aAddCounter.append(tempAddCounter); - aRemoveCounter.append(tempRemoveCounter); - aSetCounter.append(tempSetCounter); - connect(tempAddCounter, &QAction::triggered, playerActions, - [playerActions, i] { playerActions->actAddCardCounter(i); }); - connect(tempRemoveCounter, &QAction::triggered, playerActions, - [playerActions, i] { playerActions->actRemoveCardCounter(i); }); - connect(tempSetCounter, &QAction::triggered, playerActions, - [playerActions, i] { playerActions->actSetCardCounter(i); }); + auto *setAction = makeAction(this, [actions, sel, i]() { actions->actSetCardCounter(sel(), i); }); + setAction->setIcon(circleIcon); + aSetCounter.append(setAction); } setShortcutsActive(); @@ -129,7 +128,7 @@ CardMenu::CardMenu(PlayerLogic *_player, const CardItem *_card, bool _shortcutsA } bool revealedCard = false; - bool writeableCard = player->getPlayerInfo()->getLocalOrJudge(); + bool writeableCard = player->getLogic()->getPlayerInfo()->getLocalOrJudge(); if (auto *view = qobject_cast(card->getZone())) { if (view->getRevealZone()) { if (view->getWriteableRevealZone()) { @@ -313,7 +312,9 @@ void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard) initContextualPlayersMenu(revealMenu, aRevealToAll); - connect(revealMenu, &QMenu::triggered, player->getPlayerActions(), &PlayerActions::actReveal); + connect(revealMenu, &QMenu::triggered, this, [this](QAction *action) { + player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCards(), action); + }); addSeparator(); addAction(aClone); @@ -398,8 +399,7 @@ void CardMenu::addRelatedCardView() QAction *viewCard = viewRelatedCards->addAction(relatedCardName); Q_UNUSED(viewCard); - connect(viewCard, &QAction::triggered, player->getGame(), - [this, cardRef] { player->getGame()->getTab()->viewCardInfo(cardRef); }); + connect(viewCard, &QAction::triggered, this, [this, cardRef] { emit cardInfoRequested(cardRef); }); } } @@ -461,7 +461,8 @@ void CardMenu::addRelatedCardActions() auto *createRelated = new QAction(text, this); createRelated->setData(QVariant(index++)); - connect(createRelated, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actCreateRelatedCard); + connect(createRelated, &QAction::triggered, player->getLogic()->getPlayerActions(), + &PlayerActions::actCreateRelatedCard); addAction(createRelated); } @@ -470,7 +471,7 @@ void CardMenu::addRelatedCardActions() createRelatedCards->setShortcuts( SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens")); } - connect(createRelatedCards, &QAction::triggered, player->getPlayerActions(), + connect(createRelatedCards, &QAction::triggered, player->getLogic()->getPlayerActions(), &PlayerActions::actCreateAllRelatedCards); addAction(createRelatedCards); } diff --git a/cockatrice/src/game/player/menu/card_menu.h b/cockatrice/src/game/player/menu/card_menu.h index ad3962caf..d67ef3876 100644 --- a/cockatrice/src/game/player/menu/card_menu.h +++ b/cockatrice/src/game/player/menu/card_menu.h @@ -8,15 +8,20 @@ #define COCKATRICE_CARD_MENU_H #include +#include class CardItem; +class PlayerGraphicsItem; class PlayerLogic; class CardMenu : public QMenu { Q_OBJECT +signals: + void cardInfoRequested(const CardRef &cardRef); + public: - explicit CardMenu(PlayerLogic *player, const CardItem *card, bool shortcutsActive); + explicit CardMenu(PlayerGraphicsItem *player, const CardItem *card, bool shortcutsActive); void removePlayer(PlayerLogic *playerToRemove); void createTableMenu(bool canModifyCard); void createStackMenu(bool canModifyCard); @@ -41,7 +46,7 @@ public: QList aAddCounter, aSetCounter, aRemoveCounter; private: - PlayerLogic *player; + PlayerGraphicsItem *player; const CardItem *card; QList> playersInfo; bool shortcutsActive; diff --git a/cockatrice/src/game/player/menu/custom_zone_menu.cpp b/cockatrice/src/game/player/menu/custom_zone_menu.cpp index 88b7f3710..106e646d9 100644 --- a/cockatrice/src/game/player/menu/custom_zone_menu.cpp +++ b/cockatrice/src/game/player/menu/custom_zone_menu.cpp @@ -2,12 +2,12 @@ #include "../player_logic.h" -CustomZoneMenu::CustomZoneMenu(PlayerLogic *_player) : player(_player) +CustomZoneMenu::CustomZoneMenu(PlayerGraphicsItem *_player) : player(_player) { menuAction()->setVisible(false); - connect(player, &PlayerLogic::clearCustomZonesMenu, this, &CustomZoneMenu::clearCustomZonesMenu); - connect(player, &PlayerLogic::addViewCustomZoneActionToCustomZoneMenu, this, + connect(player->getLogic(), &PlayerLogic::clearCustomZonesMenu, this, &CustomZoneMenu::clearCustomZonesMenu); + connect(player->getLogic(), &PlayerLogic::addViewCustomZoneActionToCustomZoneMenu, this, &CustomZoneMenu::addViewCustomZoneActionToCustomZoneMenu); retranslateUi(); @@ -17,7 +17,7 @@ void CustomZoneMenu::retranslateUi() { setTitle(tr("C&ustom Zones")); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { for (auto aViewZone : actions()) { aViewZone->setText(tr("View custom zone '%1'").arg(aViewZone->data().toString())); @@ -37,5 +37,5 @@ void CustomZoneMenu::addViewCustomZoneActionToCustomZoneMenu(QString zoneName) QAction *aViewZone = addAction(tr("View custom zone '%1'").arg(zoneName)); aViewZone->setData(zoneName); connect(aViewZone, &QAction::triggered, this, - [zoneName, this]() { player->getGameScene()->toggleZoneView(player, zoneName, -1); }); + [zoneName, this]() { player->getGameScene()->toggleZoneView(player->getLogic(), zoneName, -1); }); } \ No newline at end of file diff --git a/cockatrice/src/game/player/menu/custom_zone_menu.h b/cockatrice/src/game/player/menu/custom_zone_menu.h index e10f6a4f0..46dd58db6 100644 --- a/cockatrice/src/game/player/menu/custom_zone_menu.h +++ b/cockatrice/src/game/player/menu/custom_zone_menu.h @@ -11,12 +11,12 @@ #include -class PlayerLogic; +class PlayerGraphicsItem; class CustomZoneMenu : public QMenu, public AbstractPlayerComponent { Q_OBJECT public: - explicit CustomZoneMenu(PlayerLogic *player); + explicit CustomZoneMenu(PlayerGraphicsItem *player); void retranslateUi() override; void setShortcutsActive() override { @@ -26,7 +26,7 @@ public: } private: - PlayerLogic *player; + PlayerGraphicsItem *player; private slots: void clearCustomZonesMenu(); void addViewCustomZoneActionToCustomZoneMenu(QString zoneName); diff --git a/cockatrice/src/game/player/menu/move_menu.cpp b/cockatrice/src/game/player/menu/move_menu.cpp index 3a5ad4da3..4dfdee432 100644 --- a/cockatrice/src/game/player/menu/move_menu.cpp +++ b/cockatrice/src/game/player/menu/move_menu.cpp @@ -4,7 +4,7 @@ #include "../player_actions.h" #include "../player_logic.h" -MoveMenu::MoveMenu(PlayerLogic *player) : QMenu(tr("Move to")) +MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to")) { aMoveToTopLibrary = new QAction(this); aMoveToTopLibrary->setData(cmMoveToTopLibrary); @@ -20,14 +20,23 @@ MoveMenu::MoveMenu(PlayerLogic *player) : QMenu(tr("Move to")) aMoveToExile = new QAction(this); aMoveToExile->setData(cmMoveToExile); - connect(aMoveToTopLibrary, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - connect(aMoveToBottomLibrary, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - connect(aMoveToXfromTopOfLibrary, &QAction::triggered, player->getPlayerActions(), - &PlayerActions::actMoveCardXCardsFromTop); - connect(aMoveToTable, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - connect(aMoveToHand, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - connect(aMoveToGraveyard, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); - connect(aMoveToExile, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); + auto *actions = player->getLogic()->getPlayerActions(); + + auto invoke = [player](CardMenuActionType type) { + return [type, player]() { + player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCards(), type); + }; + }; + + connect(aMoveToTopLibrary, &QAction::triggered, actions, invoke(cmMoveToTopLibrary)); + connect(aMoveToBottomLibrary, &QAction::triggered, actions, invoke(cmMoveToBottomLibrary)); + connect(aMoveToXfromTopOfLibrary, &QAction::triggered, actions, [player]() { + player->getLogic()->getPlayerActions()->actMoveCardXCardsFromTop(player->getGameScene()->selectedCards()); + }); + connect(aMoveToTable, &QAction::triggered, actions, invoke(cmMoveToTable)); + connect(aMoveToHand, &QAction::triggered, actions, invoke(cmMoveToHand)); + connect(aMoveToGraveyard, &QAction::triggered, actions, invoke(cmMoveToGraveyard)); + connect(aMoveToExile, &QAction::triggered, actions, invoke(cmMoveToExile)); addAction(aMoveToTopLibrary); addAction(aMoveToXfromTopOfLibrary); diff --git a/cockatrice/src/game/player/menu/move_menu.h b/cockatrice/src/game/player/menu/move_menu.h index 4e257b7fb..150bdbd3c 100644 --- a/cockatrice/src/game/player/menu/move_menu.h +++ b/cockatrice/src/game/player/menu/move_menu.h @@ -8,13 +8,13 @@ #define COCKATRICE_MOVE_MENU_H #include -class PlayerLogic; +class PlayerGraphicsItem; class MoveMenu : public QMenu { Q_OBJECT public: - explicit MoveMenu(PlayerLogic *player); + explicit MoveMenu(PlayerGraphicsItem *player); void setShortcutsActive(); void retranslateUi(); diff --git a/cockatrice/src/game/player/menu/player_menu.cpp b/cockatrice/src/game/player/menu/player_menu.cpp index 9e7b91923..6687bbba8 100644 --- a/cockatrice/src/game/player/menu/player_menu.cpp +++ b/cockatrice/src/game/player/menu/player_menu.cpp @@ -10,23 +10,26 @@ #include -PlayerMenu::PlayerMenu(PlayerLogic *_player) : QObject(_player), player(_player) +PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_player) { + connect(player->getLogic(), &PlayerLogic::requestCardMenuUpdate, this, &PlayerMenu::updateCardMenu); + connect(this, &PlayerMenu::cardInfoRequested, player, &PlayerGraphicsItem::cardInfoRequested); + playerMenu = new TearOffMenu(); - if (player->getPlayerInfo()->getLocalOrJudge()) { - handMenu = addManagedMenu(player, player->getPlayerActions(), playerMenu); - libraryMenu = addManagedMenu(player, playerMenu); + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { + handMenu = addManagedMenu(player->getLogic(), player->getLogic()->getPlayerActions(), playerMenu); + libraryMenu = addManagedMenu(player->getLogic(), playerMenu); } else { handMenu = nullptr; libraryMenu = nullptr; } - graveMenu = addManagedMenu(player, playerMenu); - rfgMenu = addManagedMenu(player, playerMenu); + graveMenu = addManagedMenu(player->getLogic(), playerMenu); + rfgMenu = addManagedMenu(player->getLogic(), playerMenu); - if (player->getPlayerInfo()->getLocalOrJudge()) { - sideboardMenu = addManagedMenu(player, playerMenu); + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { + sideboardMenu = addManagedMenu(player->getLogic(), playerMenu); customZonesMenu = addManagedMenu(player); playerMenu->addSeparator(); @@ -40,8 +43,8 @@ PlayerMenu::PlayerMenu(PlayerLogic *_player) : QObject(_player), player(_player) utilityMenu = nullptr; } - if (player->getPlayerInfo()->getLocal()) { - sayMenu = addManagedMenu(player); + if (player->getLogic()->getPlayerInfo()->getLocal()) { + sayMenu = addManagedMenu(player->getLogic()); } else { sayMenu = nullptr; } @@ -55,13 +58,13 @@ PlayerMenu::PlayerMenu(PlayerLogic *_player) : QObject(_player), player(_player) void PlayerMenu::setMenusForGraphicItems() { - player->getGraphicsItem()->getTableZoneGraphicsItem()->setMenu(playerMenu); - player->getGraphicsItem()->getGraveyardZoneGraphicsItem()->setMenu(graveMenu, graveMenu->aViewGraveyard); - player->getGraphicsItem()->getRfgZoneGraphicsItem()->setMenu(rfgMenu, rfgMenu->aViewRfg); - if (player->getPlayerInfo()->getLocalOrJudge()) { - player->getGraphicsItem()->getHandZoneGraphicsItem()->setMenu(handMenu); - player->getGraphicsItem()->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard); - player->getGraphicsItem()->getSideboardZoneGraphicsItem()->setMenu(sideboardMenu); + player->getTableZoneGraphicsItem()->setMenu(playerMenu); + player->getGraveyardZoneGraphicsItem()->setMenu(graveMenu, graveMenu->aViewGraveyard); + player->getRfgZoneGraphicsItem()->setMenu(rfgMenu, rfgMenu->aViewRfg); + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { + player->getHandZoneGraphicsItem()->setMenu(handMenu); + player->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard); + player->getSideboardZoneGraphicsItem()->setMenu(sideboardMenu); } } @@ -74,12 +77,14 @@ QMenu *PlayerMenu::updateCardMenu(const CardItem *card) // If is spectator (as spectators don't need card menus), return // only update the menu if the card is actually selected - if ((player->getGame()->getPlayerManager()->isSpectator() && !player->getGame()->getPlayerManager()->isJudge()) || - player->getGame()->getActiveCard() != card) { + if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() && + !player->getLogic()->getGame()->getPlayerManager()->isJudge()) || + player->getLogic()->getGame()->getActiveCard() != card) { return nullptr; } - QMenu *menu = new CardMenu(player, card, shortcutsActive); + CardMenu *menu = new CardMenu(player, card, shortcutsActive); + connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested); emit cardMenuUpdated(menu); return menu; @@ -87,7 +92,7 @@ QMenu *PlayerMenu::updateCardMenu(const CardItem *card) void PlayerMenu::retranslateUi() { - playerMenu->setTitle(tr("Player \"%1\"").arg(player->getPlayerInfo()->getName())); + playerMenu->setTitle(tr("Player \"%1\"").arg(player->getLogic()->getPlayerInfo()->getName())); for (auto *component : managedComponents) { component->retranslateUi(); @@ -104,7 +109,8 @@ void PlayerMenu::refreshShortcuts() { if (shortcutsActive) { // Judges get access to every player's menus but only want shortcuts to be set for their own. - if (player->getPlayerInfo()->getLocalOrJudge() && !player->getPlayerInfo()->getLocal()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge() && + !player->getLogic()->getPlayerInfo()->getLocal()) { setShortcutsInactive(); } else { setShortcutsActive(); diff --git a/cockatrice/src/game/player/menu/player_menu.h b/cockatrice/src/game/player/menu/player_menu.h index d5c19df58..62ba66df7 100644 --- a/cockatrice/src/game/player/menu/player_menu.h +++ b/cockatrice/src/game/player/menu/player_menu.h @@ -8,7 +8,6 @@ #define COCKATRICE_PLAYER_MENU_H #include "../../../interface/widgets/menus/tearoff_menu.h" -#include "../player_logic.h" #include "custom_zone_menu.h" #include "grave_menu.h" #include "hand_menu.h" @@ -23,29 +22,31 @@ #include class CardItem; +class CardMenu; +class PlayerGraphicsItem; class PlayerMenu : public QObject { Q_OBJECT signals: - void cardMenuUpdated(QMenu *cardMenu); + void cardMenuUpdated(CardMenu *cardMenu); + void cardInfoRequested(const CardRef &cardRef); void shortcutsActivated(); void shortcutsDeactivated(); void retranslateRequested(); public slots: void setMenusForGraphicItems(); + QMenu *updateCardMenu(const CardItem *card); private slots: void refreshShortcuts(); public: - explicit PlayerMenu(PlayerLogic *player); + explicit PlayerMenu(PlayerGraphicsItem *player); /** @brief Retranslate all user-visible strings. Called on language change. */ void retranslateUi(); - QMenu *updateCardMenu(const CardItem *card); - [[nodiscard]] QMenu *getPlayerMenu() const { return playerMenu; @@ -77,7 +78,7 @@ public: void setShortcutsInactive(); private: - PlayerLogic *player; + PlayerGraphicsItem *player; TearOffMenu *playerMenu; QMenu *countersMenu; HandMenu *handMenu; diff --git a/cockatrice/src/game/player/menu/pt_menu.cpp b/cockatrice/src/game/player/menu/pt_menu.cpp index 7dc3035c1..846256e24 100644 --- a/cockatrice/src/game/player/menu/pt_menu.cpp +++ b/cockatrice/src/game/player/menu/pt_menu.cpp @@ -3,30 +3,40 @@ #include "../player_actions.h" #include "../player_logic.h" -PtMenu::PtMenu(PlayerLogic *player) : QMenu(tr("Power / toughness")) +PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness")) { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); aIncP = new QAction(this); - connect(aIncP, &QAction::triggered, playerActions, &PlayerActions::actIncP); + connect(aIncP, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCards()); }); aDecP = new QAction(this); - connect(aDecP, &QAction::triggered, playerActions, &PlayerActions::actDecP); + connect(aDecP, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCards()); }); aIncT = new QAction(this); - connect(aIncT, &QAction::triggered, playerActions, &PlayerActions::actIncT); + connect(aIncT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCards()); }); aDecT = new QAction(this); - connect(aDecT, &QAction::triggered, playerActions, &PlayerActions::actDecT); + connect(aDecT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCards()); }); aIncPT = new QAction(this); - connect(aIncPT, &QAction::triggered, playerActions, [playerActions] { playerActions->actIncPT(); }); + connect(aIncPT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCards()); }); aDecPT = new QAction(this); - connect(aDecPT, &QAction::triggered, playerActions, &PlayerActions::actDecPT); + connect(aDecPT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCards()); }); aFlowP = new QAction(this); - connect(aFlowP, &QAction::triggered, playerActions, &PlayerActions::actFlowP); + connect(aFlowP, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCards()); }); aFlowT = new QAction(this); - connect(aFlowT, &QAction::triggered, playerActions, &PlayerActions::actFlowT); + connect(aFlowT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCards()); }); aSetPT = new QAction(this); - connect(aSetPT, &QAction::triggered, playerActions, &PlayerActions::actSetPT); + connect(aSetPT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actSetPT(player->getGameScene()->selectedCards()); }); aResetPT = new QAction(this); - connect(aResetPT, &QAction::triggered, playerActions, &PlayerActions::actResetPT); + connect(aResetPT, &QAction::triggered, playerActions, + [player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCards()); }); addAction(aIncP); addAction(aDecP); diff --git a/cockatrice/src/game/player/menu/pt_menu.h b/cockatrice/src/game/player/menu/pt_menu.h index 645449586..72f828801 100644 --- a/cockatrice/src/game/player/menu/pt_menu.h +++ b/cockatrice/src/game/player/menu/pt_menu.h @@ -8,14 +8,14 @@ #define COCKATRICE_PT_MENU_H #include -class PlayerLogic; +class PlayerGraphicsItem; class PtMenu : public QMenu { Q_OBJECT public: - explicit PtMenu(PlayerLogic *player); + explicit PtMenu(PlayerGraphicsItem *player); void retranslateUi(); void setShortcutsActive(); diff --git a/cockatrice/src/game/player/menu/utility_menu.cpp b/cockatrice/src/game/player/menu/utility_menu.cpp index 6b33d7bde..005b38c3b 100644 --- a/cockatrice/src/game/player/menu/utility_menu.cpp +++ b/cockatrice/src/game/player/menu/utility_menu.cpp @@ -8,11 +8,14 @@ #include #include -UtilityMenu::UtilityMenu(PlayerLogic *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player) +UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player) { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); + connect(playerActions, &PlayerActions::requestEnableAndSetCreateAnotherTokenAction, this, + &UtilityMenu::setAndEnableCreateAnotherTokenAction); + connect(playerActions, &PlayerActions::requestSetLastToken, this, &UtilityMenu::setLastToken); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { aUntapAll = new QAction(this); connect(aUntapAll, &QAction::triggered, playerActions, &PlayerActions::actUntapAll); @@ -23,19 +26,22 @@ UtilityMenu::UtilityMenu(PlayerLogic *_player, QMenu *playerMenu) : QMenu(player connect(aFlipCoin, &QAction::triggered, playerActions, &PlayerActions::actFlipCoin); aCreateToken = new QAction(this); - connect(aCreateToken, &QAction::triggered, playerActions, &PlayerActions::actCreateToken); + connect(aCreateToken, &QAction::triggered, playerActions, + [this]() { player->getLogic()->getPlayerActions()->actCreateToken(getPredefinedTokens()); }); aCreateAnotherToken = new QAction(this); connect(aCreateAnotherToken, &QAction::triggered, playerActions, &PlayerActions::actCreateAnotherToken); aCreateAnotherToken->setEnabled(false); aIncrementAllCardCounters = new QAction(this); - connect(aIncrementAllCardCounters, &QAction::triggered, playerActions, - &PlayerActions::actIncrementAllCardCounters); + connect(aIncrementAllCardCounters, &QAction::triggered, playerActions, [this]() { + player->getLogic()->getPlayerActions()->actIncrementAllCardCounters( + player->getGameScene()->selectedCards()); + }); createPredefinedTokenMenu = new QMenu(QString()); createPredefinedTokenMenu->setEnabled(false); - connect(player, &PlayerLogic::deckChanged, this, &UtilityMenu::populatePredefinedTokensMenu); + connect(player->getLogic(), &PlayerLogic::deckChanged, this, &UtilityMenu::populatePredefinedTokensMenu); playerMenu->addAction(aIncrementAllCardCounters); playerMenu->addSeparator(); @@ -66,7 +72,7 @@ void UtilityMenu::populatePredefinedTokensMenu() clear(); setEnabled(false); predefinedTokens.clear(); - const DeckList &deckList = player->getDeck(); + const DeckList &deckList = player->getLogic()->getDeck(); if (deckList.isEmpty()) { return; @@ -84,14 +90,24 @@ void UtilityMenu::populatePredefinedTokensMenu() if (i < 10) { a->setShortcut(QKeySequence("Alt+" + QString::number((i + 1) % 10))); } - connect(a, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actCreatePredefinedToken); + connect(a, &QAction::triggered, player->getLogic()->getPlayerActions(), + &PlayerActions::actCreatePredefinedToken); } } } +void UtilityMenu::setLastToken(CardInfoPtr lastToken) +{ + if (!createAnotherTokenActionExists()) { + return; + } + + player->getLogic()->getPlayerActions()->setLastTokenInfo(lastToken); +} + void UtilityMenu::retranslateUi() { - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { aIncrementAllCardCounters->setText(tr("Increment all card counters")); aUntapAll->setText(tr("&Untap all permanents")); aRollDie->setText(tr("R&oll die...")); @@ -106,7 +122,7 @@ void UtilityMenu::setShortcutsActive() { ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { aIncrementAllCardCounters->setShortcuts(shortcuts.getShortcut("Player/aIncrementAllCardCounters")); aUntapAll->setShortcuts(shortcuts.getShortcut("Player/aUntapAll")); aRollDie->setShortcuts(shortcuts.getShortcut("Player/aRollDie")); @@ -118,7 +134,7 @@ void UtilityMenu::setShortcutsActive() void UtilityMenu::setShortcutsInactive() { - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { aUntapAll->setShortcut(QKeySequence()); aRollDie->setShortcut(QKeySequence()); aFlipCoin->setShortcut(QKeySequence()); diff --git a/cockatrice/src/game/player/menu/utility_menu.h b/cockatrice/src/game/player/menu/utility_menu.h index fab3211ca..bdc2a81a5 100644 --- a/cockatrice/src/game/player/menu/utility_menu.h +++ b/cockatrice/src/game/player/menu/utility_menu.h @@ -10,19 +10,21 @@ #include "abstract_player_component.h" #include +#include -class PlayerLogic; +class PlayerGraphicsItem; class UtilityMenu : public QMenu, public AbstractPlayerComponent { Q_OBJECT public slots: void populatePredefinedTokensMenu(); + void setLastToken(CardInfoPtr lastToken); void retranslateUi() override; void setShortcutsActive() override; void setShortcutsInactive() override; public: - explicit UtilityMenu(PlayerLogic *player, QMenu *playerMenu); + explicit UtilityMenu(PlayerGraphicsItem *player, QMenu *playerMenu); [[nodiscard]] bool createAnotherTokenActionExists() const { @@ -31,7 +33,7 @@ public: void setAndEnableCreateAnotherTokenAction(QString text) { - aCreateAnotherToken->setText(text); + aCreateAnotherToken->setText(tr("C&reate another %1 token").arg(text)); aCreateAnotherToken->setEnabled(true); } @@ -41,7 +43,7 @@ public: } private: - PlayerLogic *player; + PlayerGraphicsItem *player; QStringList predefinedTokens; QMenu *createPredefinedTokenMenu; diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 1706c44dc..7d58be31a 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -39,6 +39,8 @@ static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; PlayerActions::PlayerActions(PlayerLogic *_player) : QObject(_player), player(_player), lastTokenTableRow(0), movingCardsUntil(false) { + connect(this, &PlayerActions::requestZoneViewToggle, player, &PlayerLogic::onRequestZoneViewToggle); + moveTopCardTimer = new QTimer(this); moveTopCardTimer->setInterval(MOVE_TOP_CARD_UNTIL_INTERVAL); moveTopCardTimer->setSingleShot(true); @@ -133,12 +135,12 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown) void PlayerActions::actViewLibrary() { - player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, -1); + emit requestZoneViewToggle(ZoneNames::DECK, -1); } void PlayerActions::actViewHand() { - player->getGameScene()->toggleZoneView(player, ZoneNames::HAND, -1); + emit requestZoneViewToggle(ZoneNames::HAND, -1); } /** @@ -170,7 +172,7 @@ void PlayerActions::actSortHand() static QList defaultOptions = {CardList::SortByName, CardList::SortByPrinting}; - player->getGraphicsItem()->getHandZoneGraphicsItem()->sortHand(sortOptions + defaultOptions); + emit requestSortHand(sortOptions + defaultOptions); } void PlayerActions::actViewTopCards() @@ -182,7 +184,7 @@ void PlayerActions::actViewTopCards() deckSize, 1, &ok); if (ok) { defaultNumberTopCards = number; - player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number); + emit requestZoneViewToggle(ZoneNames::DECK, number); } } @@ -195,24 +197,24 @@ void PlayerActions::actViewBottomCards() deckSize, 1, &ok); if (ok) { defaultNumberBottomCards = number; - player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number, true); + emit requestZoneViewToggle(ZoneNames::DECK, number, true); } } -void PlayerActions::actAlwaysRevealTopCard() +void PlayerActions::actAlwaysRevealTopCard(bool alwaysRevealTopCard) { Command_ChangeZoneProperties cmd; cmd.set_zone_name(ZoneNames::DECK); - cmd.set_always_reveal_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysRevealTopCardChecked()); + cmd.set_always_reveal_top_card(alwaysRevealTopCard); sendGameCommand(cmd); } -void PlayerActions::actAlwaysLookAtTopCard() +void PlayerActions::actAlwaysLookAtTopCard(bool alwaysRevealTopCard) { Command_ChangeZoneProperties cmd; cmd.set_zone_name(ZoneNames::DECK); - cmd.set_always_look_at_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysLookAtTopCardChecked()); + cmd.set_always_look_at_top_card(alwaysRevealTopCard); sendGameCommand(cmd); } @@ -224,17 +226,17 @@ void PlayerActions::actOpenDeckInDeckEditor() void PlayerActions::actViewGraveyard() { - player->getGameScene()->toggleZoneView(player, ZoneNames::GRAVE, -1); + emit requestZoneViewToggle(ZoneNames::GRAVE, -1); } void PlayerActions::actViewRfg() { - player->getGameScene()->toggleZoneView(player, ZoneNames::EXILE, -1); + emit requestZoneViewToggle(ZoneNames::EXILE, -1); } void PlayerActions::actViewSideboard() { - player->getGameScene()->toggleZoneView(player, ZoneNames::SIDEBOARD, -1); + emit requestZoneViewToggle(ZoneNames::SIDEBOARD, -1); } void PlayerActions::actShuffle() @@ -862,9 +864,9 @@ void PlayerActions::actFlipCoin() sendGameCommand(cmd); } -void PlayerActions::actCreateToken() +void PlayerActions::actCreateToken(const QStringList &predefinedTokens) { - DlgCreateToken dlg(player->getPlayerMenu()->getUtilityMenu()->getPredefinedTokens(), player->getGame()->getTab()); + DlgCreateToken dlg(predefinedTokens, player->getGame()->getTab()); if (!dlg.exec()) { return; } @@ -880,8 +882,7 @@ void PlayerActions::actCreateToken() } } - player->getPlayerMenu()->getUtilityMenu()->setAndEnableCreateAnotherTokenAction( - tr("C&reate another %1 token").arg(lastTokenInfo.name)); + emit requestEnableAndSetCreateAnotherTokenAction(lastTokenInfo.name); actCreateAnotherToken(); } @@ -912,8 +913,12 @@ void PlayerActions::setLastToken(CardInfoPtr cardInfo) return; } - UtilityMenu *utilityMenu = player->getPlayerMenu()->getUtilityMenu(); - if (utilityMenu == nullptr || !utilityMenu->createAnotherTokenActionExists()) { + emit requestSetLastToken(cardInfo); +} + +void PlayerActions::setLastTokenInfo(CardInfoPtr cardInfo) +{ + if (cardInfo == nullptr) { return; } @@ -927,7 +932,7 @@ void PlayerActions::setLastToken(CardInfoPtr cardInfo) lastTokenTableRow = TableZone::tableRowToGridY(cardInfo->getUiAttributes().tableRow); - utilityMenu->setAndEnableCreateAnotherTokenAction(tr("C&reate another %1 token").arg(lastTokenInfo.name)); + emit requestEnableAndSetCreateAnotherTokenAction(lastTokenInfo.name); } void PlayerActions::actCreatePredefinedToken() @@ -1166,7 +1171,7 @@ void PlayerActions::actSayMessage() sendGameCommand(cmd); } -void PlayerActions::actMoveCardXCardsFromTop() +void PlayerActions::actMoveCardXCardsFromTop(QList selectedCards) { int deckSize = player->getDeckZone()->getCards().size() + 1; // add the card to move to the deck bool ok; @@ -1182,7 +1187,7 @@ void PlayerActions::actMoveCardXCardsFromTop() defaultNumberTopCardsToPlaceBelow = number; - QList cardList = player->getGameScene()->selectedCards(); + QList cardList = selectedCards; if (cardList.isEmpty()) { return; } @@ -1213,12 +1218,12 @@ void PlayerActions::actMoveCardXCardsFromTop() } } -void PlayerActions::actIncPT(int deltaP, int deltaT) +void PlayerActions::actIncPT(QList selectedCards, int deltaP, int deltaT) { int playerid = player->getPlayerInfo()->getId(); QList commandList; - for (auto card : player->getGameScene()->selectedCards()) { + for (auto card : selectedCards) { QString pt = card->getPT(); const auto ptList = CardItem::parsePT(pt); QString newpt; @@ -1246,11 +1251,11 @@ void PlayerActions::actIncPT(int deltaP, int deltaT) player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); } -void PlayerActions::actResetPT() +void PlayerActions::actResetPT(QList selectedCards) { int playerid = player->getPlayerInfo()->getId(); QList commandList; - for (auto card : player->getGameScene()->selectedCards()) { + for (auto card : selectedCards) { QString ptString; if (!card->getFaceDown()) { // leave the pt empty if the card is face down ExactCard ec = card->getCard(); @@ -1279,13 +1284,12 @@ void PlayerActions::actResetPT() } } -void PlayerActions::actSetPT() +void PlayerActions::actSetPT(QList selectedCards) { QString oldPT; int playerid = player->getPlayerInfo()->getId(); - auto cards = player->getGameScene()->selectedCards(); - for (auto card : cards) { + for (auto card : selectedCards) { if (!card->getPT().isEmpty()) { oldPT = card->getPT(); } @@ -1303,7 +1307,7 @@ void PlayerActions::actSetPT() bool empty = ptList.isEmpty(); QList commandList; - for (auto card : cards) { + for (auto card : selectedCards) { auto *cmd = new Command_SetCardAttr; QString newpt = QString(); if (!empty) { @@ -1347,47 +1351,47 @@ void PlayerActions::actDrawArrow() } } -void PlayerActions::actIncP() +void PlayerActions::actIncP(QList selectedCards) { - actIncPT(1, 0); + actIncPT(selectedCards, 1, 0); } -void PlayerActions::actDecP() +void PlayerActions::actDecP(QList selectedCards) { - actIncPT(-1, 0); + actIncPT(selectedCards, -1, 0); } -void PlayerActions::actIncT() +void PlayerActions::actIncT(QList selectedCards) { - actIncPT(0, 1); + actIncPT(selectedCards, 0, 1); } -void PlayerActions::actDecT() +void PlayerActions::actDecT(QList selectedCards) { - actIncPT(0, -1); + actIncPT(selectedCards, 0, -1); } -void PlayerActions::actIncPT() +void PlayerActions::actIncPT(QList selectedCards) { - actIncPT(1, 1); + actIncPT(selectedCards, 1, 1); } -void PlayerActions::actDecPT() +void PlayerActions::actDecPT(QList selectedCards) { - actIncPT(-1, -1); + actIncPT(selectedCards, -1, -1); } -void PlayerActions::actFlowP() +void PlayerActions::actFlowP(QList selectedCards) { - actIncPT(1, -1); + actIncPT(selectedCards, 1, -1); } -void PlayerActions::actFlowT() +void PlayerActions::actFlowT(QList selectedCards) { - actIncPT(-1, 1); + actIncPT(selectedCards, -1, 1); } -void PlayerActions::actReduceLifeByPower() +void PlayerActions::actReduceLifeByPower(QList selectedCards) { // find life counter auto lifeCounter = player->getLifeCounter(); @@ -1395,10 +1399,9 @@ void PlayerActions::actReduceLifeByPower() return; } - // calculate total power - auto cards = player->getGameScene()->selectedCards(); + // calculate total power; int total = 0; - for (auto card : cards) { + for (auto card : selectedCards) { QVariantList parsed = CardItem::parsePT(card->getPT()); if (!parsed.isEmpty()) { int power = parsed.first().toInt(); // toInt will default to 0 if it's not an int @@ -1423,11 +1426,10 @@ void AnnotationDialog::keyPressEvent(QKeyEvent *event) QInputDialog::keyPressEvent(event); } -void PlayerActions::actSetAnnotation() +void PlayerActions::actSetAnnotation(QList selectedCards) { QString oldAnnotation; - auto cards = player->getGameScene()->selectedCards(); - for (auto card : cards) { + for (auto card : selectedCards) { if (!card->getAnnotation().isEmpty()) { oldAnnotation = card->getAnnotation(); } @@ -1447,7 +1449,7 @@ void PlayerActions::actSetAnnotation() QString annotation = dialog->textValue().left(MAX_NAME_LENGTH); QList commandList; - for (auto card : cards) { + for (auto card : selectedCards) { auto *cmd = new Command_SetCardAttr; cmd->set_zone(card->getZone()->getName().toStdString()); cmd->set_card_id(card->getId()); @@ -1468,10 +1470,10 @@ void PlayerActions::actAttach() card->drawAttachArrow(); } -void PlayerActions::actUnattach() +void PlayerActions::actUnattach(QList selectedCards) { QList commandList; - for (auto card : player->getGameScene()->selectedCards()) { + for (auto card : selectedCards) { if (!card->getAttachedTo()) { continue; } @@ -1484,20 +1486,20 @@ void PlayerActions::actUnattach() sendGameCommand(prepareGameCommand(commandList)); } -void PlayerActions::actAddCardCounter(int counterId) +void PlayerActions::actAddCardCounter(QList selectedCards, int counterId) { - offsetCardCounter(counterId, 1); + offsetCardCounter(selectedCards, counterId, 1); } -void PlayerActions::actRemoveCardCounter(int counterId) +void PlayerActions::actRemoveCardCounter(QList selectedCards, int counterId) { - offsetCardCounter(counterId, -1); + offsetCardCounter(selectedCards, counterId, -1); } -void PlayerActions::offsetCardCounter(int counterId, int offset) +void PlayerActions::offsetCardCounter(QList selectedCards, int counterId, int offset) { QList commandList; - for (auto card : player->getGameScene()->selectedCards()) { + for (auto card : selectedCards) { int oldValue = card->getCounters().value(counterId, 0); int newValue = oldValue + offset; @@ -1517,15 +1519,14 @@ void PlayerActions::offsetCardCounter(int counterId, int offset) sendGameCommand(prepareGameCommand(commandList)); } -void PlayerActions::actSetCardCounter(int counterId) +void PlayerActions::actSetCardCounter(QList selectedCards, int counterId) { player->setDialogSemaphore(true); // If a single card is selected, we show the old value in the dialog. Otherwise, we show "x" - QList sel = player->getGameScene()->selectedCards(); QString oldValueForDlg = "x"; - if (sel.size() == 1) { - auto *card = sel.first(); + if (selectedCards.size() == 1) { + auto *card = selectedCards.first(); oldValueForDlg = QString::number(card->getCounters().value(counterId, 0)); } @@ -1541,7 +1542,7 @@ void PlayerActions::actSetCardCounter(int counterId) } QList commandList; - for (auto card : sel) { + for (auto card : selectedCards) { int oldValue = card->getCounters().value(counterId, 0); Expression exp(oldValue); double parsed = exp.parse(dialog.textValue()); @@ -1559,9 +1560,8 @@ void PlayerActions::actSetCardCounter(int counterId) sendGameCommand(prepareGameCommand(commandList)); } -void PlayerActions::actIncrementAllCardCounters() +void PlayerActions::actIncrementAllCardCounters(QList cardsToUpdate) { - auto cardsToUpdate = player->getGameScene()->selectedCards(); if (cardsToUpdate.isEmpty()) { // If no cards selected, update all cards on table cardsToUpdate = static_cast>(player->getTableZone()->getCards()); @@ -1607,10 +1607,8 @@ static bool isUnwritableRevealZone(CardZoneLogic *zone) return false; } -void PlayerActions::playSelectedCards(const bool faceDown) +void PlayerActions::playSelectedCards(QList selectedCards, const bool faceDown) { - QList selectedCards = player->getGameScene()->selectedCards(); - // CardIds will get shuffled downwards when cards leave the deck. // We need to iterate through the cards in reverse order so cardIds don't get changed out from under us as we play // out the cards one-by-one. @@ -1624,19 +1622,19 @@ void PlayerActions::playSelectedCards(const bool faceDown) } } -void PlayerActions::actPlay() +void PlayerActions::actPlay(QList selectedCards) { - playSelectedCards(false); + playSelectedCards(selectedCards, false); } -void PlayerActions::actPlayFacedown() +void PlayerActions::actPlayFacedown(QList selectedCards) { - playSelectedCards(true); + playSelectedCards(selectedCards, true); } -void PlayerActions::actHide() +void PlayerActions::actHide(QList selectedCards) { - for (const auto &item : player->getGameScene()->selectedCards()) { + for (const auto &item : selectedCards) { auto *card = static_cast(item); if (card && isUnwritableRevealZone(card->getZone())) { card->getZone()->removeCard(card); @@ -1644,7 +1642,7 @@ void PlayerActions::actHide() } } -void PlayerActions::actReveal(QAction *action) +void PlayerActions::actReveal(QList selectedCards, QAction *action) { const int otherPlayerId = action->data().toInt(); @@ -1653,7 +1651,7 @@ void PlayerActions::actReveal(QAction *action) cmd.set_player_id(otherPlayerId); } - for (auto card : player->getGameScene()->selectedCards()) { + for (auto card : selectedCards) { if (!cmd.has_zone_name()) { cmd.set_zone_name(card->getZone()->getName().toStdString()); } @@ -1735,15 +1733,14 @@ void PlayerActions::actRevealRandomGraveyardCard(int revealToPlayerId) sendGameCommand(cmd); } -void PlayerActions::cardMenuAction() +void PlayerActions::cardMenuAction(QList selectedCards, CardMenuActionType type) { - auto *a = dynamic_cast(sender()); - QList cardList = player->getGameScene()->selectedCards(); + QList cardList = selectedCards; QList commandList; - if (a->data().toInt() <= (int)cmClone) { + if (type <= cmClone) { for (const auto &card : cardList) { - switch (static_cast(a->data().toInt())) { + switch (type) { // Leaving both for compatibility with server case cmUntap: // fallthrough @@ -1824,7 +1821,7 @@ void PlayerActions::cardMenuAction() idList.add_card()->set_card_id(i->getId()); } - switch (static_cast(a->data().toInt())) { + switch (type) { case cmMoveToTopLibrary: { auto *cmd = new Command_MoveCard; cmd->set_start_player_id(startPlayerId); diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index 3b822b61a..940de610f 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -9,6 +9,7 @@ #define COCKATRICE_PLAYER_ACTIONS_H #include "../dialogs/dlg_create_token.h" #include "../dialogs/dlg_move_top_cards_until.h" +#include "card_menu_action_type.h" #include "event_processing_options.h" #include "player_logic.h" @@ -56,15 +57,22 @@ public: return movingCardsUntil; } +signals: + void requestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed = false); + void requestSortHand(const QList &options); + void requestEnableAndSetCreateAnotherTokenAction(const QString &lastTokenName); + void requestSetLastToken(CardInfoPtr lastToken); + public slots: void setLastToken(CardInfoPtr cardInfo); + void setLastTokenInfo(CardInfoPtr cardInfo); void playCard(CardItem *c, bool faceDown); void playCardToTable(const CardItem *c, bool faceDown); void actUntapAll(); void actRollDie(); void actFlipCoin(); - void actCreateToken(); + void actCreateToken(const QStringList &predefinedTokens); void actCreateAnotherToken(); void actShuffle(); void actShuffleTop(); @@ -77,9 +85,9 @@ public slots: void actMulliganMinusOne(); void doMulligan(int number); - void actPlay(); - void actPlayFacedown(); - void actHide(); + void actPlay(QList selectedCards); + void actPlayFacedown(QList selectedCards); + void actHide(QList selectedCards); void actMoveTopCardToPlay(); void actMoveTopCardToPlayFaceDown(); @@ -111,8 +119,8 @@ public slots: void actViewHand(); void actViewTopCards(); void actViewBottomCards(); - void actAlwaysRevealTopCard(); - void actAlwaysLookAtTopCard(); + void actAlwaysRevealTopCard(bool alwaysRevealTopCard); + void actAlwaysLookAtTopCard(bool alwaysRevealTopCard); void actViewGraveyard(); void actLendLibrary(int lendToPlayerId); void actRevealTopCards(int revealToPlayerId, int amount); @@ -127,37 +135,37 @@ public slots: void actCreateRelatedCard(); void actCreateAllRelatedCards(); - void actMoveCardXCardsFromTop(); - void actRemoveCardCounter(int counterId); - void actAddCardCounter(int counterId); - void actSetCardCounter(int counterId); - void actIncrementAllCardCounters(); + void actMoveCardXCardsFromTop(QList selectedCards); + void actRemoveCardCounter(QList selectedCards, int counterId); + void actAddCardCounter(QList selectedCards, int counterId); + void actSetCardCounter(QList selectedCards, int counterId); + void actIncrementAllCardCounters(QList cardsToUpdate); void actAttach(); - void actUnattach(); + void actUnattach(QList selectedCards); void actDrawArrow(); - void actIncPT(int deltaP, int deltaT); - void actResetPT(); - void actSetPT(); - void actIncP(); - void actDecP(); - void actIncT(); - void actDecT(); - void actIncPT(); - void actDecPT(); - void actFlowP(); - void actFlowT(); + void actIncPT(QList selectedCards, int deltaP, int deltaT); + void actResetPT(QList selectedCards); + void actSetPT(QList selectedCards); + void actIncP(QList selectedCards); + void actDecP(QList selectedCards); + void actIncT(QList selectedCards); + void actDecT(QList selectedCards); + void actIncPT(QList selectedCards); + void actDecPT(QList selectedCards); + void actFlowP(QList selectedCards); + void actFlowT(QList selectedCards); - void actReduceLifeByPower(); + void actReduceLifeByPower(QList selectedCards); - void actSetAnnotation(); - void actReveal(QAction *action); + void actSetAnnotation(QList selectedCards); + void actReveal(QList selectedCards, QAction *action); void actRevealHand(int revealToPlayerId); void actRevealRandomHandCard(int revealToPlayerId); void actRevealLibrary(int revealToPlayerId); void actSortHand(); - void cardMenuAction(); + void cardMenuAction(QList selectedCards, CardMenuActionType type); private: PlayerLogic *player; @@ -185,12 +193,12 @@ private: bool persistent = false); bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); - void playSelectedCards(bool faceDown = false); + void playSelectedCards(QList selectedCards, bool faceDown = false); void cmdSetTopCard(Command_MoveCard &cmd); void cmdSetBottomCard(Command_MoveCard &cmd); - void offsetCardCounter(int counterId, int offset); + void offsetCardCounter(QList selectedCards, int counterId, int offset); }; #endif // COCKATRICE_PLAYER_ACTIONS_H diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index debc6c8f7..aa751170b 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -6,7 +6,6 @@ #include "../board/arrow_item.h" #include "../board/card_item.h" #include "../board/card_list.h" -#include "libcockatrice/utility/color.h" #include "player_actions.h" #include "player_logic.h" @@ -33,10 +32,12 @@ #include #include #include +#include #include PlayerEventHandler::PlayerEventHandler(PlayerLogic *_player) : QObject(_player), player(_player) { + connect(this, &PlayerEventHandler::requestCardMenuUpdate, player, &PlayerLogic::requestCardMenuUpdate); } void PlayerEventHandler::eventGameSay(const Event_GameSay &event) @@ -252,7 +253,7 @@ void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event) int oldValue = card->getCounters().value(event.counter_id(), 0); card->setCounter(event.counter_id(), event.counter_value()); - player->getPlayerMenu()->updateCardMenu(card); + emit requestCardMenuUpdate(card); emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue); } @@ -370,7 +371,7 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv targetZone->addCard(card, true, x, y); emit cardZoneChanged(card, startZone == targetZone); - player->getPlayerMenu()->updateCardMenu(card); + emit requestCardMenuUpdate(card); if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK && targetZone->getName() == ZoneNames::STACK) { @@ -397,7 +398,7 @@ void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event) emit logFlipCard(player, card->getName(), event.face_down()); card->setFaceDown(event.face_down()); - player->getPlayerMenu()->updateCardMenu(card); + emit requestCardMenuUpdate(card); } void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event) @@ -466,7 +467,7 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event) } else { emit logUnattachCard(player, startCard->getName()); } - player->getPlayerMenu()->updateCardMenu(startCard); + emit requestCardMenuUpdate(startCard); } void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event) @@ -552,7 +553,7 @@ void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventP } if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) { - player->getGameScene()->addRevealedZoneView(player, zone, cardList, event.grant_write_access()); + emit player->requestRevealedZoneView(player, zone, cardList, event.grant_write_access()); } emit logRevealCards(player, zone, cardId, cardName, otherPlayer, false, diff --git a/cockatrice/src/game/player/player_event_handler.h b/cockatrice/src/game/player/player_event_handler.h index 958dee16b..cfd82933f 100644 --- a/cockatrice/src/game/player/player_event_handler.h +++ b/cockatrice/src/game/player/player_event_handler.h @@ -83,6 +83,7 @@ signals: void logAlwaysRevealTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal); void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal); void cardZoneChanged(CardItem *card, bool sameZone); + void requestCardMenuUpdate(const CardItem *card); public: PlayerEventHandler(PlayerLogic *player); diff --git a/cockatrice/src/game/player/player_graphics_item.cpp b/cockatrice/src/game/player/player_graphics_item.cpp index 0d4f8c3ed..d86fce86b 100644 --- a/cockatrice/src/game/player/player_graphics_item.cpp +++ b/cockatrice/src/game/player/player_graphics_item.cpp @@ -8,6 +8,9 @@ #include "../board/abstract_card_item.h" #include "../board/counter_general.h" #include "../hand_counter.h" +#include "player_actions.h" + +#include PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) { @@ -16,23 +19,26 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) connect(&SettingsCache::instance(), &SettingsCache::handJustificationChanged, this, &PlayerGraphicsItem::rearrangeZones); connect(player, &PlayerLogic::rearrangeCounters, this, &PlayerGraphicsItem::rearrangeCounters); + connect(player, &PlayerLogic::activeChanged, this, &PlayerGraphicsItem::onPlayerActiveChanged); connect(player, &PlayerLogic::concededChanged, this, [this](int, bool c) { setVisible(!c); }); connect(player, &PlayerLogic::zoneIdChanged, this, [this](int id) { playerArea->setPlayerZoneId(id); }); connect(player, &PlayerLogic::counterAdded, this, &PlayerGraphicsItem::onCounterAdded); connect(player, &PlayerLogic::counterRemoved, this, &PlayerGraphicsItem::onCounterRemoved); - connect(player->getPlayerMenu(), &PlayerMenu::shortcutsActivated, this, [this]() { + playerMenu = new PlayerMenu(this); + + connect(playerMenu, &PlayerMenu::shortcutsActivated, this, [this]() { for (auto *ctr : counterWidgets) { ctr->setShortcutsActive(); } }); - connect(player->getPlayerMenu(), &PlayerMenu::shortcutsDeactivated, this, [this]() { + connect(playerMenu, &PlayerMenu::shortcutsDeactivated, this, [this]() { for (auto *ctr : counterWidgets) { ctr->setShortcutsInactive(); } }); - connect(player->getPlayerMenu(), &PlayerMenu::retranslateRequested, this, [this]() { + connect(playerMenu, &PlayerMenu::retranslateRequested, this, [this]() { for (auto *ctr : counterWidgets) { ctr->retranslateUi(); } @@ -47,6 +53,8 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) initializeZones(); + playerMenu->setMenusForGraphicItems(); + connect(tableZoneGraphicsItem, &TableZone::sizeChanged, this, &PlayerGraphicsItem::updateBoundingRect); updateBoundingRect(); @@ -57,7 +65,7 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) void PlayerGraphicsItem::retranslateUi() { - player->getPlayerMenu()->retranslateUi(); + playerMenu->retranslateUi(); QMapIterator zoneIterator(player->getZones()); while (zoneIterator.hasNext()) { @@ -93,14 +101,16 @@ void PlayerGraphicsItem::initializeZones() rfgZoneGraphicsItem = new PileZone(player->getRfgZone(), this); rfgZoneGraphicsItem->setPos(base + QPointF(0, 2 * h + h2 + 10)); - tableZoneGraphicsItem = new TableZone(player->getTableZone(), this); + tableZoneGraphicsItem = new TableZone(player->getTableZone(), mirrored, this); connect(tableZoneGraphicsItem, &TableZone::sizeChanged, this, &PlayerGraphicsItem::updateBoundingRect); + connect(this, &PlayerGraphicsItem::mirroredChanged, tableZoneGraphicsItem, &TableZone::setMirrored); stackZoneGraphicsItem = new StackZone(player->getStackZone(), static_cast(tableZoneGraphicsItem->boundingRect().height()), this); handZoneGraphicsItem = new HandZone(player->getHandZone(), static_cast(tableZoneGraphicsItem->boundingRect().height()), this); + connect(player->getPlayerActions(), &PlayerActions::requestSortHand, handZoneGraphicsItem, &HandZone::sortHand); connect(handZoneGraphicsItem->getLogic(), &HandZoneLogic::cardCountChanged, handCounter, &HandCounter::updateNumber); @@ -145,6 +155,7 @@ void PlayerGraphicsItem::setMirrored(bool _mirrored) { if (mirrored != _mirrored) { mirrored = _mirrored; + emit mirroredChanged(mirrored); rearrangeZones(); } } @@ -159,11 +170,11 @@ void PlayerGraphicsItem::onCounterAdded(CounterState *state) } counterWidgets.insert(state->getId(), widget); - if (player->getPlayerMenu()->getCountersMenu() && widget->getMenu()) { - player->getPlayerMenu()->getCountersMenu()->addMenu(widget->getMenu()); + if (playerMenu->getCountersMenu() && widget->getMenu()) { + playerMenu->getCountersMenu()->addMenu(widget->getMenu()); } - if (player->getPlayerMenu()->getShortcutsActive()) { + if (playerMenu->getShortcutsActive()) { widget->setShortcutsActive(); } @@ -176,8 +187,8 @@ void PlayerGraphicsItem::onCounterRemoved(int counterId) if (!widget) { return; } - if (player->getPlayerMenu()->getCountersMenu() && widget->getMenu()) { - player->getPlayerMenu()->getCountersMenu()->removeAction(widget->getMenu()->menuAction()); + if (playerMenu->getCountersMenu() && widget->getMenu()) { + playerMenu->getCountersMenu()->removeAction(widget->getMenu()->menuAction()); } widget->delCounter(); rearrangeCounters(); diff --git a/cockatrice/src/game/player/player_graphics_item.h b/cockatrice/src/game/player/player_graphics_item.h index e37fe7290..1acb1520f 100644 --- a/cockatrice/src/game/player/player_graphics_item.h +++ b/cockatrice/src/game/player/player_graphics_item.h @@ -55,11 +55,16 @@ public: return static_cast(scene()); } - PlayerLogic *getPlayer() const + PlayerLogic *getLogic() const { return player; } + [[nodiscard]] PlayerMenu *getPlayerMenu() const + { + return playerMenu; + } + PlayerArea *getPlayerArea() const { return playerArea; @@ -111,9 +116,12 @@ public slots: signals: void sizeChanged(); void playerCountChanged(); + void mirroredChanged(bool isMirrored); + void cardInfoRequested(const CardRef &cardRef); private: PlayerLogic *player; + PlayerMenu *playerMenu; PlayerArea *playerArea; PlayerTarget *playerTarget; QMap counterWidgets; diff --git a/cockatrice/src/game/player/player_logic.cpp b/cockatrice/src/game/player/player_logic.cpp index 0210aa0c6..b748eb19a 100644 --- a/cockatrice/src/game/player/player_logic.cpp +++ b/cockatrice/src/game/player/player_logic.cpp @@ -35,14 +35,6 @@ PlayerLogic::PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool conceded(false), zoneId(0), dialogSemaphore(false) { initializeZones(); - - playerMenu = new PlayerMenu(this); - graphicsItem = new PlayerGraphicsItem(this); - playerMenu->setMenusForGraphicItems(); - - connect(this, &PlayerLogic::activeChanged, graphicsItem, &PlayerGraphicsItem::onPlayerActiveChanged); - - connect(this, &PlayerLogic::openDeckEditor, game->getTab(), &TabGame::openDeckEditor); } void PlayerLogic::initializeZones() @@ -68,7 +60,6 @@ PlayerLogic::~PlayerLogic() } zones.clear(); - delete playerMenu; delete getPlayerInfo()->userInfo; } @@ -326,22 +317,16 @@ void PlayerLogic::setActive(bool _active) active = _active; emit activeChanged(active); } +void PlayerLogic::onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed) +{ + emit requestZoneViewToggle(this, zoneName, numberCards, isReversed); +} void PlayerLogic::updateZones() { getTableZone()->reorganizeCards(); } -PlayerGraphicsItem *PlayerLogic::getGraphicsItem() -{ - return graphicsItem; -} - -GameScene *PlayerLogic::getGameScene() -{ - return getGraphicsItem()->getGameScene(); -} - void PlayerLogic::setGameStarted() { if (playerInfo->local) { diff --git a/cockatrice/src/game/player/player_logic.h b/cockatrice/src/game/player/player_logic.h index c3508d069..c83892dea 100644 --- a/cockatrice/src/game/player/player_logic.h +++ b/cockatrice/src/game/player/player_logic.h @@ -67,8 +67,14 @@ class PlayerLogic : public QObject signals: void openDeckEditor(const LoadedDeck &deck); + void requestZoneViewToggle(PlayerLogic *player, const QString &zoneName, int numberCards, bool isReversed); + void requestRevealedZoneView(PlayerLogic *player, + CardZoneLogic *zone, + const QList &cardList, + bool withWritePermission); void deckChanged(); void newCardAdded(AbstractCardItem *card); + void requestCardMenuUpdate(const CardItem *card); void counterAdded(CounterState *state); void counterRemoved(int counterId); void rearrangeCounters(); @@ -85,6 +91,7 @@ signals: public slots: void setActive(bool _active); + void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed); public: PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent); @@ -112,10 +119,6 @@ public: return game; } - GameScene *getGameScene(); - - [[nodiscard]] PlayerGraphicsItem *getGraphicsItem(); - [[nodiscard]] PlayerActions *getPlayerActions() const { return playerActions; @@ -131,11 +134,6 @@ public: return playerInfo; } - [[nodiscard]] PlayerMenu *getPlayerMenu() const - { - return playerMenu; - } - void setDeck(const DeckList &_deck); [[nodiscard]] const DeckList &getDeck() const @@ -234,8 +232,6 @@ private: PlayerInfo *playerInfo; PlayerEventHandler *playerEventHandler; PlayerActions *playerActions; - PlayerMenu *playerMenu; - PlayerGraphicsItem *graphicsItem; bool active; bool conceded; diff --git a/cockatrice/src/game_graphics/zones/table_zone.cpp b/cockatrice/src/game_graphics/zones/table_zone.cpp index ffb4adf5c..245de8281 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.cpp +++ b/cockatrice/src/game_graphics/zones/table_zone.cpp @@ -22,7 +22,8 @@ const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80); const QColor TableZone::GRADIENT_COLOR = QColor(255, 255, 255, 150); const QColor TableZone::GRADIENT_COLORLESS = QColor(255, 255, 255, 0); -TableZone::TableZone(TableZoneLogic *_logic, QGraphicsItem *parent) : SelectZone(_logic, parent), active(false) +TableZone::TableZone(TableZoneLogic *_logic, bool _mirrored, QGraphicsItem *parent) + : SelectZone(_logic, parent), active(false), mirrored(_mirrored) { connect(_logic, &TableZoneLogic::contentSizeChanged, this, &TableZone::resizeToContents); connect(_logic, &TableZoneLogic::toggleTapped, this, &TableZone::toggleTapped); @@ -50,12 +51,16 @@ QRectF TableZone::boundingRect() const return QRectF(0, 0, width, height); } +void TableZone::setMirrored(bool isMirrored) +{ + mirrored = isMirrored; + update(); +} + bool TableZone::isInverted() const { - return ((getLogic()->getPlayer()->getGraphicsItem()->getMirrored() && - !SettingsCache::instance().getInvertVerticalCoordinate()) || - (!getLogic()->getPlayer()->getGraphicsItem()->getMirrored() && - SettingsCache::instance().getInvertVerticalCoordinate())); + return ((mirrored && !SettingsCache::instance().getInvertVerticalCoordinate()) || + (!mirrored && SettingsCache::instance().getInvertVerticalCoordinate())); } void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) diff --git a/cockatrice/src/game_graphics/zones/table_zone.h b/cockatrice/src/game_graphics/zones/table_zone.h index f46531520..8a898173b 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.h +++ b/cockatrice/src/game_graphics/zones/table_zone.h @@ -82,6 +82,7 @@ private: If this TableZone is currently active */ bool active = false; + bool mirrored = false; [[nodiscard]] bool isInverted() const; @@ -96,6 +97,7 @@ public slots: Reorganizes CardItems in the TableZone */ void reorganizeCards() override; + void setMirrored(bool isMirrored); public: /** @@ -104,7 +106,7 @@ public: @param _p the Player @param parent defaults to null */ - explicit TableZone(TableZoneLogic *_logic, QGraphicsItem *parent = nullptr); + explicit TableZone(TableZoneLogic *_logic, bool mirrored, QGraphicsItem *parent = nullptr); /** @return a QRectF of the TableZone bounding box. diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index c52f73319..1e2bebd15 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -1,6 +1,7 @@ #include "tab_game.h" #include "../../../client/settings/cache_settings.h" +#include "../../../game/player/menu/card_menu.h" #include "../game/board/arrow_item.h" #include "../game/board/card_item.h" #include "../game/deckview/deck_view_container.h" @@ -363,11 +364,10 @@ void TabGame::retranslateUi() cardInfoFrameWidget->retranslateUi(); - QMapIterator i(game->getPlayerManager()->getPlayers()); - - while (i.hasNext()) { - i.next().value()->getGraphicsItem()->retranslateUi(); + for (auto playerView : scene->getPlayers().values()) { + playerView->retranslateUi(); } + QMapIterator j(deckViewContainers); while (j.hasNext()) { j.next().value()->playerDeckView->retranslateUi(); @@ -654,8 +654,12 @@ PlayerLogic *TabGame::addPlayer(PlayerLogic *newPlayer) scene->addPlayer(newPlayer); + auto *view = scene->viewForPlayer(newPlayer->getPlayerInfo()->getId()); + connect(newPlayer, &PlayerLogic::newCardAdded, this, &TabGame::newCardAdded); - connect(newPlayer->getPlayerMenu(), &PlayerMenu::cardMenuUpdated, this, &TabGame::setCardMenu); + connect(newPlayer, &PlayerLogic::openDeckEditor, this, &TabGame::openDeckEditor); + connect(view->getPlayerMenu(), &PlayerMenu::cardMenuUpdated, this, &TabGame::setCardMenu); + connect(view, &PlayerGraphicsItem::cardInfoRequested, this, &TabGame::viewCardInfo); messageLog->connectToPlayerEventHandler(newPlayer->getPlayerEventHandler()); @@ -668,7 +672,7 @@ PlayerLogic *TabGame::addPlayer(PlayerLogic *newPlayer) addLocalPlayer(newPlayer, newPlayer->getPlayerInfo()->getId()); } - gameMenu->insertMenu(playersSeparator, newPlayer->getPlayerMenu()->getPlayerMenu()); + gameMenu->insertMenu(playersSeparator, view->getPlayerMenu()->getPlayerMenu()); createZoneForPlayer(newPlayer, newPlayer->getPlayerInfo()->getId()); @@ -678,7 +682,7 @@ PlayerLogic *TabGame::addPlayer(PlayerLogic *newPlayer) void TabGame::addLocalPlayer(PlayerLogic *newPlayer, int playerId) { if (game->getGameState()->getClients().size() == 1) { - newPlayer->getPlayerMenu()->setShortcutsActive(); + scene->viewForPlayer(playerId)->getPlayerMenu()->setShortcutsActive(); } auto *deckView = new TabbedDeckViewContainer(playerId, this); @@ -698,27 +702,24 @@ void TabGame::addLocalPlayer(PlayerLogic *newPlayer, int playerId) void TabGame::processPlayerLeave(PlayerLogic *leavingPlayer) { - QString playerName = "@" + leavingPlayer->getPlayerInfo()->getName(); - removePlayerFromAutoCompleteList(playerName); - - scene->removePlayer(leavingPlayer); + removePlayerFromAutoCompleteList("@" + leavingPlayer->getPlayerInfo()->getName()); // When we inserted the playerMenu into the gameMenu earlier, Qt wrapped the playerMenu into a QAction*, which lives // independently and does not get cleaned up when the source menu gets destroyed. We have to manually clean here. - if (leavingPlayer->getPlayerMenu()) { - QMenu *menu = leavingPlayer->getPlayerMenu()->getPlayerMenu(); - if (menu) { - // Find and remove the QAction pointing to this menu - QList actions = gameMenu->actions(); - for (QAction *act : actions) { - if (act->menu() == menu) { - gameMenu->removeAction(act); - delete act; // deletes the QAction wrapper around the submenu - break; - } + auto *view = scene->viewForPlayer(leavingPlayer->getPlayerInfo()->getId()); + if (view) { + // Find and remove the QAction pointing to this menu + QMenu *menu = view->getPlayerMenu()->getPlayerMenu(); + for (QAction *act : gameMenu->actions()) { + if (act->menu() == menu) { + gameMenu->removeAction(act); + delete act; + break; } } } + + scene->removePlayer(leavingPlayer); } void TabGame::processRemotePlayerDeckSelect(QString deckList, int playerId, QString playerName) @@ -869,12 +870,12 @@ PlayerLogic *TabGame::setActivePlayer(int id) if (i.value() == player) { i.value()->setActive(true); if (game->getGameState()->getClients().size() > 1) { - i.value()->getPlayerMenu()->setShortcutsActive(); + scene->viewForPlayer(i.value()->getPlayerInfo()->getId())->getPlayerMenu()->setShortcutsActive(); } } else { i.value()->setActive(false); if (game->getGameState()->getClients().size() > 1) { - i.value()->getPlayerMenu()->setShortcutsInactive(); + scene->viewForPlayer(i.value()->getPlayerInfo()->getId())->getPlayerMenu()->setShortcutsInactive(); } } } @@ -890,8 +891,13 @@ void TabGame::setActivePhase(int phase) void TabGame::newCardAdded(AbstractCardItem *card) { + connect(card, &AbstractCardItem::rightClicked, scene, &GameScene::onCardRightClicked); + connect(card, &AbstractCardItem::playSelected, scene, &GameScene::playSelected); + connect(card, &AbstractCardItem::playSelectedFaceDown, scene, &GameScene::playSelectedFaceDown); + connect(card, &AbstractCardItem::hideSelected, scene, &GameScene::hideSelected); connect(card, &AbstractCardItem::hovered, cardInfoFrameWidget, qOverload(&CardInfoFrameWidget::setCard)); + connect(card, &AbstractCardItem::selectionChanged, scene, &GameScene::onCardSelectionChanged); connect(card, &AbstractCardItem::showCardInfoPopup, this, &TabGame::showCardInfoPopup); connect(card, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString))); connect(card, &AbstractCardItem::cardShiftClicked, this, &TabGame::linkCardToChat); @@ -935,7 +941,7 @@ QString TabGame::getTabText() const /** * @param menu The menu to set. Pass in nullptr to set the menu to empty. */ -void TabGame::setCardMenu(QMenu *menu) +void TabGame::setCardMenu(CardMenu *menu) { if (!aCardMenu) { return; diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.h b/cockatrice/src/interface/widgets/tabs/tab_game.h index 7f9392034..ddda4d9b9 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.h +++ b/cockatrice/src/interface/widgets/tabs/tab_game.h @@ -141,7 +141,7 @@ signals: private slots: void adminLockChanged(bool lock); void newCardAdded(AbstractCardItem *card); - void setCardMenu(QMenu *menu); + void setCardMenu(CardMenu *menu); void actGameInfo(); void actConcede(); From 487bb84b6f4473f230871461f4a1bae0de5edc46 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 9 Jun 2026 08:07:06 +0200 Subject: [PATCH 3/4] [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic (#6945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem Took 4 minutes Took 58 seconds Took 2 minutes * [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic Took 7 minutes Took 4 minutes Took 9 seconds Took 2 minutes Took 5 minutes Took 58 seconds --------- Co-authored-by: Lukas Brübach --- cockatrice/src/game/board/card_item.cpp | 1 + cockatrice/src/game/player/menu/card_menu.cpp | 1 + .../src/game/player/menu/grave_menu.cpp | 18 +++--- cockatrice/src/game/player/menu/grave_menu.h | 6 +- cockatrice/src/game/player/menu/hand_menu.cpp | 26 +++++---- cockatrice/src/game/player/menu/hand_menu.h | 6 +- .../src/game/player/menu/library_menu.cpp | 55 +++++++++++-------- .../src/game/player/menu/library_menu.h | 5 +- .../src/game/player/menu/player_menu.cpp | 12 ++-- cockatrice/src/game/player/menu/rfg_menu.cpp | 12 ++-- cockatrice/src/game/player/menu/rfg_menu.h | 6 +- cockatrice/src/game/player/menu/say_menu.cpp | 4 +- cockatrice/src/game/player/menu/say_menu.h | 6 +- .../src/game/player/menu/sideboard_menu.cpp | 7 ++- .../src/game/player/menu/sideboard_menu.h | 6 +- 15 files changed, 93 insertions(+), 78 deletions(-) diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index 16197ae16..029822805 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -476,6 +476,7 @@ void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::RightButton && owner != nullptr) { emit rightClicked(this, event->screenPos()); + return; } if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) && (!SettingsCache::instance().getDoubleClickToPlay())) { diff --git a/cockatrice/src/game/player/menu/card_menu.cpp b/cockatrice/src/game/player/menu/card_menu.cpp index ba925afb0..150c1c587 100644 --- a/cockatrice/src/game/player/menu/card_menu.cpp +++ b/cockatrice/src/game/player/menu/card_menu.cpp @@ -6,6 +6,7 @@ #include "../../zones/view_zone_logic.h" #include "../card_menu_action_type.h" #include "../player_actions.h" +#include "../player_graphics_item.h" #include "../player_logic.h" #include "move_menu.h" #include "pt_menu.h" diff --git a/cockatrice/src/game/player/menu/grave_menu.cpp b/cockatrice/src/game/player/menu/grave_menu.cpp index 16a5858ca..45762e900 100644 --- a/cockatrice/src/game/player/menu/grave_menu.cpp +++ b/cockatrice/src/game/player/menu/grave_menu.cpp @@ -8,14 +8,14 @@ #include #include -GraveyardMenu::GraveyardMenu(PlayerLogic *_player, QWidget *parent) : TearOffMenu(parent), player(_player) +GraveyardMenu::GraveyardMenu(PlayerGraphicsItem *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { createMoveActions(); createViewActions(); addAction(aViewGraveyard); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { mRevealRandomGraveyardCard = addMenu(QString()); connect(mRevealRandomGraveyardCard, &QMenu::aboutToShow, this, &GraveyardMenu::populateRevealRandomMenuWithActivePlayers); @@ -36,9 +36,9 @@ GraveyardMenu::GraveyardMenu(PlayerLogic *_player, QWidget *parent) : TearOffMen void GraveyardMenu::createMoveActions() { - auto grave = player->getGraveZone(); + auto grave = player->getLogic()->getGraveZone(); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aMoveGraveToTopLibrary = new QAction(this); aMoveGraveToTopLibrary->setData(QList() << ZoneNames::DECK << 0); @@ -60,7 +60,7 @@ void GraveyardMenu::createMoveActions() void GraveyardMenu::createViewActions() { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); aViewGraveyard = new QAction(this); connect(aViewGraveyard, &QAction::triggered, playerActions, &PlayerActions::actViewGraveyard); @@ -76,9 +76,9 @@ void GraveyardMenu::populateRevealRandomMenuWithActivePlayers() mRevealRandomGraveyardCard->addSeparator(); - const auto &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const auto &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto *other : players) { - if (other == player) { + if (other == player->getLogic()) { continue; } QAction *a = mRevealRandomGraveyardCard->addAction(other->getPlayerInfo()->getName()); @@ -90,7 +90,7 @@ void GraveyardMenu::populateRevealRandomMenuWithActivePlayers() void GraveyardMenu::onRevealRandomTriggered() { if (auto *a = qobject_cast(sender())) { - player->getPlayerActions()->actRevealRandomGraveyardCard(a->data().toInt()); + player->getLogic()->getPlayerActions()->actRevealRandomGraveyardCard(a->data().toInt()); } } @@ -100,7 +100,7 @@ void GraveyardMenu::retranslateUi() aViewGraveyard->setText(tr("&View graveyard")); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { moveGraveMenu->setTitle(tr("&Move graveyard to...")); aMoveGraveToTopLibrary->setText(tr("&Top of library")); aMoveGraveToBottomLibrary->setText(tr("&Bottom of library")); diff --git a/cockatrice/src/game/player/menu/grave_menu.h b/cockatrice/src/game/player/menu/grave_menu.h index d3d98802d..116261e9b 100644 --- a/cockatrice/src/game/player/menu/grave_menu.h +++ b/cockatrice/src/game/player/menu/grave_menu.h @@ -13,7 +13,7 @@ #include #include -class PlayerLogic; +class PlayerGraphicsItem; class GraveyardMenu : public TearOffMenu, public AbstractPlayerComponent { Q_OBJECT @@ -21,7 +21,7 @@ signals: void newPlayerActionCreated(QAction *action); public: - explicit GraveyardMenu(PlayerLogic *player, QWidget *parent = nullptr); + explicit GraveyardMenu(PlayerGraphicsItem *player, QWidget *parent = nullptr); void createMoveActions(); void createViewActions(); void populateRevealRandomMenuWithActivePlayers(); @@ -40,7 +40,7 @@ public: QAction *aMoveGraveToRfg = nullptr; private: - PlayerLogic *player; + PlayerGraphicsItem *player; }; #endif // COCKATRICE_GRAVE_MENU_H diff --git a/cockatrice/src/game/player/menu/hand_menu.cpp b/cockatrice/src/game/player/menu/hand_menu.cpp index 6ff177655..60899a27a 100644 --- a/cockatrice/src/game/player/menu/hand_menu.cpp +++ b/cockatrice/src/game/player/menu/hand_menu.cpp @@ -5,16 +5,20 @@ #include "../../../game_graphics/zones/hand_zone.h" #include "../../abstract_game.h" #include "../player_actions.h" +#include "../player_graphics_item.h" #include "../player_logic.h" #include #include #include -HandMenu::HandMenu(PlayerLogic *_player, PlayerActions *actions, QWidget *parent) : TearOffMenu(parent), player(_player) +HandMenu::HandMenu(PlayerGraphicsItem *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + auto *actions = player->getLogic()->getPlayerActions(); + + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aViewHand = new QAction(this); + connect(aViewHand, &QAction::triggered, actions, &PlayerActions::actViewHand); addAction(aViewHand); @@ -75,7 +79,7 @@ HandMenu::HandMenu(PlayerLogic *_player, PlayerActions *actions, QWidget *parent mMoveHandMenu = addTearOffMenu(QString()); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aMoveHandToTopLibrary = new QAction(this); aMoveHandToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveHandToBottomLibrary = new QAction(this); @@ -85,7 +89,7 @@ HandMenu::HandMenu(PlayerLogic *_player, PlayerActions *actions, QWidget *parent aMoveHandToRfg = new QAction(this); aMoveHandToRfg->setData(QList() << ZoneNames::EXILE << 0); - auto hand = player->getHandZone(); + auto hand = player->getLogic()->getHandZone(); connect(aMoveHandToTopLibrary, &QAction::triggered, hand, &HandZoneLogic::moveAllToZone); connect(aMoveHandToBottomLibrary, &QAction::triggered, hand, &HandZoneLogic::moveAllToZone); @@ -107,7 +111,7 @@ void HandMenu::retranslateUi() { setTitle(tr("&Hand")); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { aViewHand->setText(tr("&View hand")); mSortHand->setTitle(tr("Sort hand by...")); @@ -166,9 +170,9 @@ void HandMenu::populateRevealHandMenuWithActivePlayers() mRevealHand->addSeparator(); - const auto &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const auto &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto *other : players) { - if (other == player) { + if (other == player->getLogic()) { continue; } QAction *a = mRevealHand->addAction(other->getPlayerInfo()->getName()); @@ -185,9 +189,9 @@ void HandMenu::populateRevealRandomHandCardMenuWithActivePlayers() mRevealRandomHandCard->addSeparator(); - const auto &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const auto &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto *other : players) { - if (other == player) { + if (other == player->getLogic()) { continue; } QAction *a = mRevealRandomHandCard->addAction(other->getPlayerInfo()->getName()); @@ -204,7 +208,7 @@ void HandMenu::onRevealHandTriggered() } const int targetId = action->data().toInt(); - player->getPlayerActions()->actRevealHand(targetId); + player->getLogic()->getPlayerActions()->actRevealHand(targetId); } void HandMenu::onRevealRandomHandCardTriggered() @@ -215,5 +219,5 @@ void HandMenu::onRevealRandomHandCardTriggered() } const int targetId = action->data().toInt(); - player->getPlayerActions()->actRevealRandomHandCard(targetId); + player->getLogic()->getPlayerActions()->actRevealRandomHandCard(targetId); } diff --git a/cockatrice/src/game/player/menu/hand_menu.h b/cockatrice/src/game/player/menu/hand_menu.h index 1e2ddd95a..d5204612b 100644 --- a/cockatrice/src/game/player/menu/hand_menu.h +++ b/cockatrice/src/game/player/menu/hand_menu.h @@ -13,7 +13,7 @@ #include #include -class PlayerLogic; +class PlayerGraphicsItem; class PlayerActions; class HandMenu : public TearOffMenu, public AbstractPlayerComponent @@ -21,7 +21,7 @@ class HandMenu : public TearOffMenu, public AbstractPlayerComponent Q_OBJECT public: - HandMenu(PlayerLogic *player, PlayerActions *actions, QWidget *parent = nullptr); + HandMenu(PlayerGraphicsItem *player, QWidget *parent = nullptr); QMenu *revealHandMenu() const { @@ -43,7 +43,7 @@ private slots: void onRevealRandomHandCardTriggered(); private: - PlayerLogic *player; + PlayerGraphicsItem *player; QAction *aViewHand = nullptr; QAction *aMulligan = nullptr; diff --git a/cockatrice/src/game/player/menu/library_menu.cpp b/cockatrice/src/game/player/menu/library_menu.cpp index 8449af05a..cdc45ed7c 100644 --- a/cockatrice/src/game/player/menu/library_menu.cpp +++ b/cockatrice/src/game/player/menu/library_menu.cpp @@ -8,9 +8,10 @@ #include "../player_logic.h" #include +#include #include -LibraryMenu::LibraryMenu(PlayerLogic *_player, QWidget *parent) : TearOffMenu(parent), player(_player) +LibraryMenu::LibraryMenu(PlayerGraphicsItem *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { createDrawActions(); createShuffleActions(); @@ -75,8 +76,8 @@ LibraryMenu::LibraryMenu(PlayerLogic *_player, QWidget *parent) : TearOffMenu(pa bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addAction(aShuffleBottomCards); - connect(player, &PlayerLogic::resetTopCardMenuActions, this, &LibraryMenu::resetTopCardMenuActions); - connect(player, &PlayerLogic::deckChanged, this, &LibraryMenu::enableOpenInDeckEditorAction); + connect(player->getLogic(), &PlayerLogic::resetTopCardMenuActions, this, &LibraryMenu::resetTopCardMenuActions); + connect(player->getLogic(), &PlayerLogic::deckChanged, this, &LibraryMenu::enableOpenInDeckEditorAction); retranslateUi(); } @@ -94,9 +95,9 @@ void LibraryMenu::resetTopCardMenuActions() void LibraryMenu::createDrawActions() { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aDrawCard = new QAction(this); connect(aDrawCard, &QAction::triggered, playerActions, &PlayerActions::actDrawCard); aDrawCards = new QAction(this); @@ -112,9 +113,9 @@ void LibraryMenu::createDrawActions() void LibraryMenu::createShuffleActions() { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aShuffle = new QAction(this); connect(aShuffle, &QAction::triggered, playerActions, &PlayerActions::actShuffle); aShuffleTopCards = new QAction(this); @@ -126,9 +127,9 @@ void LibraryMenu::createShuffleActions() void LibraryMenu::createMoveActions() { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aMoveTopToPlay = new QAction(this); connect(aMoveTopToPlay, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToPlay); aMoveTopToPlayFaceDown = new QAction(this); @@ -181,9 +182,9 @@ void LibraryMenu::createMoveActions() void LibraryMenu::createViewActions() { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); - if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + if (player->getLogic()->getPlayerInfo()->local || player->getLogic()->getPlayerInfo()->judge) { aViewLibrary = new QAction(this); connect(aViewLibrary, &QAction::triggered, playerActions, &PlayerActions::actViewLibrary); @@ -207,7 +208,7 @@ void LibraryMenu::retranslateUi() { setTitle(tr("&Library")); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { aViewLibrary->setText(tr("&View library")); aViewTopCards->setText(tr("View &top cards of library...")); aViewBottomCards->setText(tr("View bottom cards of library...")); @@ -263,9 +264,9 @@ void LibraryMenu::populateRevealLibraryMenuWithActivePlayers() mRevealLibrary->addSeparator(); - const auto &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const auto &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto *other : players) { - if (other == player) { + if (other == player->getLogic()) { continue; } QAction *a = mRevealLibrary->addAction(other->getPlayerInfo()->getName()); @@ -278,9 +279,9 @@ void LibraryMenu::populateLendLibraryMenuWithActivePlayers() { mLendLibrary->clear(); - const auto &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const auto &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto *other : players) { - if (other == player) { + if (other == player->getLogic()) { continue; } QAction *a = mLendLibrary->addAction(other->getPlayerInfo()->getName()); @@ -299,9 +300,9 @@ void LibraryMenu::populateRevealTopCardMenuWithActivePlayers() mRevealTopCard->addSeparator(); - const auto &players = player->getGame()->getPlayerManager()->getPlayers().values(); + const auto &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); for (auto *other : players) { - if (other == player) { + if (other == player->getLogic()) { continue; } QAction *a = mRevealTopCard->addAction(other->getPlayerInfo()->getName()); @@ -313,27 +314,33 @@ void LibraryMenu::populateRevealTopCardMenuWithActivePlayers() void LibraryMenu::onRevealLibraryTriggered() { if (auto *a = qobject_cast(sender())) { - player->getPlayerActions()->actRevealLibrary(a->data().toInt()); + player->getLogic()->getPlayerActions()->actRevealLibrary(a->data().toInt()); } } void LibraryMenu::onLendLibraryTriggered() { if (auto *a = qobject_cast(sender())) { - player->getPlayerActions()->actLendLibrary(a->data().toInt()); + player->getLogic()->getPlayerActions()->actLendLibrary(a->data().toInt()); } } void LibraryMenu::onRevealTopCardTriggered() { + QWidget *parent = nullptr; + if (auto *view = player->scene() ? player->scene()->views().value(0) : nullptr) { + parent = view->window(); + } if (auto *a = qobject_cast(sender())) { - int deckSize = player->getDeckZone()->getCards().size(); - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Reveal top cards of library"), + + int deckSize = player->getLogic()->getDeckZone()->getCards().size(); + bool ok = true; + int number = QInputDialog::getInt(parent, tr("Reveal top cards of library"), tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberTopCards, 1, deckSize, 1, &ok); + if (ok) { - player->getPlayerActions()->actRevealTopCards(a->data().toInt(), number); + player->getLogic()->getPlayerActions()->actRevealTopCards(a->data().toInt(), number); defaultNumberTopCards = number; } } diff --git a/cockatrice/src/game/player/menu/library_menu.h b/cockatrice/src/game/player/menu/library_menu.h index a941c54b1..bc0e6fb8e 100644 --- a/cockatrice/src/game/player/menu/library_menu.h +++ b/cockatrice/src/game/player/menu/library_menu.h @@ -13,6 +13,7 @@ #include #include +class PlayerGraphicsItem; class PlayerLogic; class PlayerActions; @@ -24,7 +25,7 @@ public slots: void resetTopCardMenuActions(); public: - LibraryMenu(PlayerLogic *player, QWidget *parent = nullptr); + LibraryMenu(PlayerGraphicsItem *player, QWidget *parent = nullptr); void createDrawActions(); void createShuffleActions(); void createMoveActions(); @@ -111,7 +112,7 @@ public: int defaultNumberTopCards = 1; private: - PlayerLogic *player; + PlayerGraphicsItem *player; }; #endif // COCKATRICE_LIBRARY_MENU_H diff --git a/cockatrice/src/game/player/menu/player_menu.cpp b/cockatrice/src/game/player/menu/player_menu.cpp index 6687bbba8..041b41052 100644 --- a/cockatrice/src/game/player/menu/player_menu.cpp +++ b/cockatrice/src/game/player/menu/player_menu.cpp @@ -18,18 +18,18 @@ PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_ playerMenu = new TearOffMenu(); if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { - handMenu = addManagedMenu(player->getLogic(), player->getLogic()->getPlayerActions(), playerMenu); - libraryMenu = addManagedMenu(player->getLogic(), playerMenu); + handMenu = addManagedMenu(player, playerMenu); + libraryMenu = addManagedMenu(player, playerMenu); } else { handMenu = nullptr; libraryMenu = nullptr; } - graveMenu = addManagedMenu(player->getLogic(), playerMenu); - rfgMenu = addManagedMenu(player->getLogic(), playerMenu); + graveMenu = addManagedMenu(player, playerMenu); + rfgMenu = addManagedMenu(player, playerMenu); if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { - sideboardMenu = addManagedMenu(player->getLogic(), playerMenu); + sideboardMenu = addManagedMenu(player, playerMenu); customZonesMenu = addManagedMenu(player); playerMenu->addSeparator(); @@ -44,7 +44,7 @@ PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_ } if (player->getLogic()->getPlayerInfo()->getLocal()) { - sayMenu = addManagedMenu(player->getLogic()); + sayMenu = addManagedMenu(player); } else { sayMenu = nullptr; } diff --git a/cockatrice/src/game/player/menu/rfg_menu.cpp b/cockatrice/src/game/player/menu/rfg_menu.cpp index e8aca00cb..79fdebf48 100644 --- a/cockatrice/src/game/player/menu/rfg_menu.cpp +++ b/cockatrice/src/game/player/menu/rfg_menu.cpp @@ -5,14 +5,14 @@ #include -RfgMenu::RfgMenu(PlayerLogic *_player, QWidget *parent) : TearOffMenu(parent), player(_player) +RfgMenu::RfgMenu(PlayerGraphicsItem *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { createMoveActions(); createViewActions(); addAction(aViewRfg); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { addSeparator(); moveRfgMenu = addTearOffMenu(QString()); moveRfgMenu->addAction(aMoveRfgToTopLibrary); @@ -28,8 +28,8 @@ RfgMenu::RfgMenu(PlayerLogic *_player, QWidget *parent) : TearOffMenu(parent), p void RfgMenu::createMoveActions() { - if (player->getPlayerInfo()->getLocalOrJudge()) { - auto rfg = player->getRfgZone(); + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { + auto rfg = player->getLogic()->getRfgZone(); aMoveRfgToTopLibrary = new QAction(this); aMoveRfgToTopLibrary->setData(QList() << ZoneNames::DECK << 0); @@ -49,7 +49,7 @@ void RfgMenu::createMoveActions() void RfgMenu::createViewActions() { - PlayerActions *playerActions = player->getPlayerActions(); + PlayerActions *playerActions = player->getLogic()->getPlayerActions(); aViewRfg = new QAction(this); connect(aViewRfg, &QAction::triggered, playerActions, &PlayerActions::actViewRfg); @@ -61,7 +61,7 @@ void RfgMenu::retranslateUi() aViewRfg->setText(tr("&View exile")); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { moveRfgMenu->setTitle(tr("&Move exile to...")); aMoveRfgToTopLibrary->setText(tr("&Top of library")); aMoveRfgToBottomLibrary->setText(tr("&Bottom of library")); diff --git a/cockatrice/src/game/player/menu/rfg_menu.h b/cockatrice/src/game/player/menu/rfg_menu.h index 9e179f8fd..f5dd888e4 100644 --- a/cockatrice/src/game/player/menu/rfg_menu.h +++ b/cockatrice/src/game/player/menu/rfg_menu.h @@ -13,12 +13,12 @@ #include #include -class PlayerLogic; +class PlayerGraphicsItem; class RfgMenu : public TearOffMenu, public AbstractPlayerComponent { Q_OBJECT public: - explicit RfgMenu(PlayerLogic *player, QWidget *parent = nullptr); + explicit RfgMenu(PlayerGraphicsItem *player, QWidget *parent = nullptr); void createMoveActions(); void createViewActions(); void retranslateUi() override; @@ -38,7 +38,7 @@ public: QAction *aMoveRfgToGrave = nullptr; private: - PlayerLogic *player; + PlayerGraphicsItem *player; }; #endif // COCKATRICE_RFG_MENU_H diff --git a/cockatrice/src/game/player/menu/say_menu.cpp b/cockatrice/src/game/player/menu/say_menu.cpp index a2d5ab982..58bbd33aa 100644 --- a/cockatrice/src/game/player/menu/say_menu.cpp +++ b/cockatrice/src/game/player/menu/say_menu.cpp @@ -4,7 +4,7 @@ #include "../player_actions.h" #include "../player_logic.h" -SayMenu::SayMenu(PlayerLogic *_player) : player(_player) +SayMenu::SayMenu(PlayerGraphicsItem *_player) : player(_player) { connect(&SettingsCache::instance().messages(), &MessageSettings::messageMacrosChanged, this, &SayMenu::initSayMenu); initSayMenu(); @@ -44,7 +44,7 @@ void SayMenu::initSayMenu() for (int i = 0; i < count; ++i) { auto *newAction = new QAction(SettingsCache::instance().messages().getMessageAt(i), this); - connect(newAction, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actSayMessage); + connect(newAction, &QAction::triggered, player->getLogic()->getPlayerActions(), &PlayerActions::actSayMessage); addAction(newAction); } diff --git a/cockatrice/src/game/player/menu/say_menu.h b/cockatrice/src/game/player/menu/say_menu.h index 3de70e85c..3ff160d05 100644 --- a/cockatrice/src/game/player/menu/say_menu.h +++ b/cockatrice/src/game/player/menu/say_menu.h @@ -11,12 +11,12 @@ #include -class PlayerLogic; +class PlayerGraphicsItem; class SayMenu : public QMenu, public AbstractPlayerComponent { Q_OBJECT public: - explicit SayMenu(PlayerLogic *player); + explicit SayMenu(PlayerGraphicsItem *player); void retranslateUi() override; void setShortcutsActive() override; @@ -26,7 +26,7 @@ private slots: void initSayMenu(); private: - PlayerLogic *player; + PlayerGraphicsItem *player; bool shortcutsActive = false; }; diff --git a/cockatrice/src/game/player/menu/sideboard_menu.cpp b/cockatrice/src/game/player/menu/sideboard_menu.cpp index f88625a1f..27b50b570 100644 --- a/cockatrice/src/game/player/menu/sideboard_menu.cpp +++ b/cockatrice/src/game/player/menu/sideboard_menu.cpp @@ -3,12 +3,13 @@ #include "../player_actions.h" #include "../player_logic.h" -SideboardMenu::SideboardMenu(PlayerLogic *player, QMenu *playerMenu) : QMenu(playerMenu) +SideboardMenu::SideboardMenu(PlayerGraphicsItem *player, QMenu *playerMenu) : QMenu(playerMenu) { aViewSideboard = new QAction(this); - connect(aViewSideboard, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actViewSideboard); + connect(aViewSideboard, &QAction::triggered, player->getLogic()->getPlayerActions(), + &PlayerActions::actViewSideboard); - if (player->getPlayerInfo()->getLocalOrJudge()) { + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { addAction(aViewSideboard); } diff --git a/cockatrice/src/game/player/menu/sideboard_menu.h b/cockatrice/src/game/player/menu/sideboard_menu.h index 20a206782..b3b547291 100644 --- a/cockatrice/src/game/player/menu/sideboard_menu.h +++ b/cockatrice/src/game/player/menu/sideboard_menu.h @@ -11,19 +11,19 @@ #include -class PlayerLogic; +class PlayerGraphicsItem; class SideboardMenu : public QMenu, public AbstractPlayerComponent { Q_OBJECT public: - explicit SideboardMenu(PlayerLogic *player, QMenu *playerMenu); + explicit SideboardMenu(PlayerGraphicsItem *player, QMenu *playerMenu); void retranslateUi() override; void setShortcutsActive() override; void setShortcutsInactive() override; private: - PlayerLogic *player; + PlayerGraphicsItem *player; QAction *aViewSideboard; }; From cbfd28690842eb4348a38889ac83ad3b07cc7c91 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 9 Jun 2026 08:22:59 +0200 Subject: [PATCH 4/4] [Game][Player] Move dialog creation out of player_actions and into player_dialogs (#6946) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem Took 4 minutes Took 48 seconds Took 2 minutes * Drop early return. Took 1 hour 13 minutes Took 2 minutes Took 1 minute Took 24 seconds * [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem Took 4 minutes Took 58 seconds * [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic Took 7 minutes Took 4 minutes Took 9 seconds Took 2 minutes Took 5 minutes Took 58 seconds * [Game][Player] Split Player into PlayerLogic/PlayerGraphicsItem Took 4 minutes Took 2 minutes * [Game][Menus] Make Menus accept PlayerGraphicsItem instead of PlayerLogic Took 7 minutes Took 1 minute Took 57 seconds * [Game][Player] Move dialog creation out of player_actions and into player_dialogs Took 3 minutes Took 1 second * Fix typo. Took 5 minutes * Addressed comments. Took 16 minutes Took 11 seconds * Reintroduce clearCardsToDelete check. Took 3 minutes * Capture cards before semaphore. Took 1 minute --------- Co-authored-by: Lukas Brübach --- cockatrice/CMakeLists.txt | 1 + cockatrice/src/game/player/menu/card_menu.cpp | 4 +- cockatrice/src/game/player/menu/hand_menu.cpp | 2 +- .../src/game/player/menu/library_menu.cpp | 15 +- cockatrice/src/game/player/menu/move_menu.cpp | 5 +- cockatrice/src/game/player/menu/pt_menu.cpp | 2 +- .../src/game/player/menu/utility_menu.cpp | 7 +- cockatrice/src/game/player/player_actions.cpp | 410 +++++++++--------- cockatrice/src/game/player/player_actions.h | 82 +++- cockatrice/src/game/player/player_dialogs.cpp | 298 +++++++++++++ cockatrice/src/game/player/player_dialogs.h | 62 +++ .../src/game/player/player_graphics_item.cpp | 5 + .../src/game/player/player_graphics_item.h | 2 + 13 files changed, 664 insertions(+), 231 deletions(-) create mode 100644 cockatrice/src/game/player/player_dialogs.cpp create mode 100644 cockatrice/src/game/player/player_dialogs.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 028161ee0..f0e363e18 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -100,6 +100,7 @@ set(cockatrice_SOURCES src/game/player/menu/utility_menu.cpp src/game/player/player_actions.cpp src/game/player/player_area.cpp + src/game/player/player_dialogs.cpp src/game/player/player_event_handler.cpp src/game/player/player_graphics_item.cpp src/game/player/player_info.cpp diff --git a/cockatrice/src/game/player/menu/card_menu.cpp b/cockatrice/src/game/player/menu/card_menu.cpp index 150c1c587..c1c33e37d 100644 --- a/cockatrice/src/game/player/menu/card_menu.cpp +++ b/cockatrice/src/game/player/menu/card_menu.cpp @@ -79,7 +79,7 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho // Actions using selection directly aUnattach = makeAction(this, [actions, sel]() { actions->actUnattach(sel()); }); - aSetAnnotation = makeAction(this, [actions, sel]() { actions->actSetAnnotation(sel()); }); + aSetAnnotation = makeAction(this, [actions, sel]() { actions->actRequestSetAnnotationDialog(sel()); }); aPlay = makeAction(this, [actions, sel]() { actions->actPlay(sel()); }); aPlayFacedown = makeAction(this, [actions, sel]() { actions->actPlayFacedown(sel()); }); aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); }); @@ -115,7 +115,7 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho removeAction->setIcon(circleIcon); aRemoveCounter.append(removeAction); - auto *setAction = makeAction(this, [actions, sel, i]() { actions->actSetCardCounter(sel(), i); }); + auto *setAction = makeAction(this, [actions, sel, i]() { actions->actRequestSetCardCounterDialog(sel(), i); }); setAction->setIcon(circleIcon); aSetCounter.append(setAction); } diff --git a/cockatrice/src/game/player/menu/hand_menu.cpp b/cockatrice/src/game/player/menu/hand_menu.cpp index 60899a27a..64a8c5754 100644 --- a/cockatrice/src/game/player/menu/hand_menu.cpp +++ b/cockatrice/src/game/player/menu/hand_menu.cpp @@ -62,7 +62,7 @@ HandMenu::HandMenu(PlayerGraphicsItem *_player, QWidget *parent) : TearOffMenu(p addSeparator(); aMulligan = new QAction(this); - connect(aMulligan, &QAction::triggered, actions, &PlayerActions::actMulligan); + connect(aMulligan, &QAction::triggered, actions, &PlayerActions::actRequestMulliganDialog); addAction(aMulligan); // Mulligan same size diff --git a/cockatrice/src/game/player/menu/library_menu.cpp b/cockatrice/src/game/player/menu/library_menu.cpp index cdc45ed7c..00ab4592f 100644 --- a/cockatrice/src/game/player/menu/library_menu.cpp +++ b/cockatrice/src/game/player/menu/library_menu.cpp @@ -101,13 +101,13 @@ void LibraryMenu::createDrawActions() aDrawCard = new QAction(this); connect(aDrawCard, &QAction::triggered, playerActions, &PlayerActions::actDrawCard); aDrawCards = new QAction(this); - connect(aDrawCards, &QAction::triggered, playerActions, &PlayerActions::actDrawCards); + connect(aDrawCards, &QAction::triggered, playerActions, &PlayerActions::actRequestDrawCardsDialog); aUndoDraw = new QAction(this); connect(aUndoDraw, &QAction::triggered, playerActions, &PlayerActions::actUndoDraw); aDrawBottomCard = new QAction(this); connect(aDrawBottomCard, &QAction::triggered, playerActions, &PlayerActions::actDrawBottomCard); aDrawBottomCards = new QAction(this); - connect(aDrawBottomCards, &QAction::triggered, playerActions, &PlayerActions::actDrawBottomCards); + connect(aDrawBottomCards, &QAction::triggered, playerActions, &PlayerActions::actRequestDrawBottomCardsDialog); } } @@ -119,9 +119,9 @@ void LibraryMenu::createShuffleActions() aShuffle = new QAction(this); connect(aShuffle, &QAction::triggered, playerActions, &PlayerActions::actShuffle); aShuffleTopCards = new QAction(this); - connect(aShuffleTopCards, &QAction::triggered, playerActions, &PlayerActions::actShuffleTop); + connect(aShuffleTopCards, &QAction::triggered, playerActions, &PlayerActions::actRequestShuffleTopDialog); aShuffleBottomCards = new QAction(this); - connect(aShuffleBottomCards, &QAction::triggered, playerActions, &PlayerActions::actShuffleBottom); + connect(aShuffleBottomCards, &QAction::triggered, playerActions, &PlayerActions::actRequestShuffleBottomDialog); } } @@ -150,7 +150,8 @@ void LibraryMenu::createMoveActions() connect(aMoveTopCardsToExileFaceDown, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToExileFaceDown); aMoveTopCardsUntil = new QAction(this); - connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsUntil); + connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, + &PlayerActions::actRequestMoveTopCardsUntilDialog); aMoveTopCardToBottom = new QAction(this); connect(aMoveTopCardToBottom, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToBottom); @@ -189,9 +190,9 @@ void LibraryMenu::createViewActions() connect(aViewLibrary, &QAction::triggered, playerActions, &PlayerActions::actViewLibrary); aViewTopCards = new QAction(this); - connect(aViewTopCards, &QAction::triggered, playerActions, &PlayerActions::actViewTopCards); + connect(aViewTopCards, &QAction::triggered, playerActions, &PlayerActions::actRequestViewTopCardsDialog); aViewBottomCards = new QAction(this); - connect(aViewBottomCards, &QAction::triggered, playerActions, &PlayerActions::actViewBottomCards); + connect(aViewBottomCards, &QAction::triggered, playerActions, &PlayerActions::actRequestViewBottomCardsDialog); aAlwaysRevealTopCard = new QAction(this); aAlwaysRevealTopCard->setCheckable(true); connect(aAlwaysRevealTopCard, &QAction::triggered, playerActions, &PlayerActions::actAlwaysRevealTopCard); diff --git a/cockatrice/src/game/player/menu/move_menu.cpp b/cockatrice/src/game/player/menu/move_menu.cpp index 4dfdee432..9997aecf3 100644 --- a/cockatrice/src/game/player/menu/move_menu.cpp +++ b/cockatrice/src/game/player/menu/move_menu.cpp @@ -30,9 +30,8 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to")) connect(aMoveToTopLibrary, &QAction::triggered, actions, invoke(cmMoveToTopLibrary)); connect(aMoveToBottomLibrary, &QAction::triggered, actions, invoke(cmMoveToBottomLibrary)); - connect(aMoveToXfromTopOfLibrary, &QAction::triggered, actions, [player]() { - player->getLogic()->getPlayerActions()->actMoveCardXCardsFromTop(player->getGameScene()->selectedCards()); - }); + connect(aMoveToXfromTopOfLibrary, &QAction::triggered, actions, + &PlayerActions::actRequestMoveCardXCardsFromTopDialog); connect(aMoveToTable, &QAction::triggered, actions, invoke(cmMoveToTable)); connect(aMoveToHand, &QAction::triggered, actions, invoke(cmMoveToHand)); connect(aMoveToGraveyard, &QAction::triggered, actions, invoke(cmMoveToGraveyard)); diff --git a/cockatrice/src/game/player/menu/pt_menu.cpp b/cockatrice/src/game/player/menu/pt_menu.cpp index 846256e24..011271385 100644 --- a/cockatrice/src/game/player/menu/pt_menu.cpp +++ b/cockatrice/src/game/player/menu/pt_menu.cpp @@ -33,7 +33,7 @@ PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness")) [player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCards()); }); aSetPT = new QAction(this); connect(aSetPT, &QAction::triggered, playerActions, - [player, playerActions] { playerActions->actSetPT(player->getGameScene()->selectedCards()); }); + [player, playerActions] { playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCards()); }); aResetPT = new QAction(this); connect(aResetPT, &QAction::triggered, playerActions, [player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCards()); }); diff --git a/cockatrice/src/game/player/menu/utility_menu.cpp b/cockatrice/src/game/player/menu/utility_menu.cpp index 005b38c3b..9769a029e 100644 --- a/cockatrice/src/game/player/menu/utility_menu.cpp +++ b/cockatrice/src/game/player/menu/utility_menu.cpp @@ -20,14 +20,15 @@ UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu connect(aUntapAll, &QAction::triggered, playerActions, &PlayerActions::actUntapAll); aRollDie = new QAction(this); - connect(aRollDie, &QAction::triggered, playerActions, &PlayerActions::actRollDie); + connect(aRollDie, &QAction::triggered, playerActions, &PlayerActions::actRequestRollDieDialog); aFlipCoin = new QAction(this); connect(aFlipCoin, &QAction::triggered, playerActions, &PlayerActions::actFlipCoin); aCreateToken = new QAction(this); - connect(aCreateToken, &QAction::triggered, playerActions, - [this]() { player->getLogic()->getPlayerActions()->actCreateToken(getPredefinedTokens()); }); + connect(aCreateToken, &QAction::triggered, playerActions, [this]() { + player->getLogic()->getPlayerActions()->actRequestCreateTokenDialog(getPredefinedTokens()); + }); aCreateAnotherToken = new QAction(this); connect(aCreateAnotherToken, &QAction::triggered, playerActions, &PlayerActions::actCreateAnotherToken); diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 7d58be31a..2b0428dd8 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -5,7 +5,6 @@ #include "../../interface/widgets/tabs/tab_game.h" #include "../../interface/widgets/utility/get_text_with_max.h" #include "../board/card_item.h" -#include "../client/settings/card_counter_settings.h" #include "../dialogs/dlg_move_top_cards_until.h" #include "../dialogs/dlg_roll_dice.h" #include "../zones/view_zone_logic.h" @@ -175,30 +174,26 @@ void PlayerActions::actSortHand() emit requestSortHand(sortOptions + defaultOptions); } -void PlayerActions::actViewTopCards() +void PlayerActions::actRequestViewTopCardsDialog() { - int deckSize = player->getDeckZone()->getCards().size(); - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("View top cards of library"), - tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberTopCards, 1, - deckSize, 1, &ok); - if (ok) { - defaultNumberTopCards = number; - emit requestZoneViewToggle(ZoneNames::DECK, number); - } + emit requestViewTopCardsDialog(defaultNumberTopCards, player->getDeckZone()->getCards().size()); } -void PlayerActions::actViewBottomCards() +void PlayerActions::actViewTopCards(int number) { - int deckSize = player->getDeckZone()->getCards().size(); - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("View bottom cards of library"), - tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberBottomCards, 1, - deckSize, 1, &ok); - if (ok) { - defaultNumberBottomCards = number; - emit requestZoneViewToggle(ZoneNames::DECK, number, true); - } + defaultNumberTopCards = number; + emit requestZoneViewToggle(ZoneNames::DECK, number); +} + +void PlayerActions::actRequestViewBottomCardsDialog() +{ + emit requestViewBottomCardsDialog(defaultNumberBottomCards, player->getDeckZone()->getCards().size()); +} + +void PlayerActions::actViewBottomCards(int number) +{ + defaultNumberBottomCards = number; + emit requestZoneViewToggle(ZoneNames::DECK, number, true); } void PlayerActions::actAlwaysRevealTopCard(bool alwaysRevealTopCard) @@ -244,18 +239,20 @@ void PlayerActions::actShuffle() sendGameCommand(Command_Shuffle()); } -void PlayerActions::actShuffleTop() +void PlayerActions::actRequestShuffleTopDialog() { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { return; } - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Shuffle top cards of library"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, - maxCards, 1, &ok); - if (!ok) { + emit requestShuffleTopDialog(defaultNumberTopCards, maxCards); +} + +void PlayerActions::actShuffleTop(int number) +{ + const int maxCards = player->getDeckZone()->getCards().size(); + if (maxCards == 0) { return; } @@ -273,18 +270,20 @@ void PlayerActions::actShuffleTop() sendGameCommand(cmd); } -void PlayerActions::actShuffleBottom() +void PlayerActions::actRequestShuffleBottomDialog() { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { return; } - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Shuffle bottom cards of library"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, - maxCards, 1, &ok); - if (!ok) { + emit requestShuffleBottomDialog(defaultNumberBottomCards, maxCards); +} + +void PlayerActions::actShuffleBottom(int number) +{ + const int maxCards = player->getDeckZone()->getCards().size(); + if (maxCards == 0) { return; } @@ -309,21 +308,18 @@ void PlayerActions::actDrawCard() sendGameCommand(cmd); } -void PlayerActions::actMulligan() +void PlayerActions::actRequestMulliganDialog() { int startSize = SettingsCache::instance().getStartingHandSize(); int handSize = player->getHandZone()->getCards().size(); int deckSize = player->getDeckZone()->getCards().size() + handSize; - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), 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); + emit requestMulliganDialog(startSize, handSize, deckSize); +} - if (!ok) { - return; - } +void PlayerActions::actMulligan(int number) +{ + int handSize = player->getHandZone()->getCards().size(); if (number < 1) { number = handSize + number; @@ -357,19 +353,19 @@ void PlayerActions::doMulligan(int number) sendGameCommand(cmd); } -void PlayerActions::actDrawCards() +void PlayerActions::actRequestDrawCardsDialog() { int deckSize = player->getDeckZone()->getCards().size(); - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), 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); - } + + emit requestDrawCardsDialog(defaultNumberTopCards, deckSize); +} + +void PlayerActions::actDrawCards(int number) +{ + defaultNumberTopCards = number; + Command_DrawCards cmd; + cmd.set_number(static_cast(number)); + sendGameCommand(cmd); } void PlayerActions::actUndoDraw() @@ -427,36 +423,40 @@ void PlayerActions::actMoveTopCardToExile() void PlayerActions::actMoveTopCardsToGrave() { - moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), false); + actRequestMoveTopCardsToDialog(ZoneNames::GRAVE, tr("grave"), false); } void PlayerActions::actMoveTopCardsToGraveFaceDown() { - moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), true); + actRequestMoveTopCardsToDialog(ZoneNames::GRAVE, tr("grave"), true); } void PlayerActions::actMoveTopCardsToExile() { - moveTopCardsTo(ZoneNames::EXILE, tr("exile"), false); + actRequestMoveTopCardsToDialog(ZoneNames::EXILE, tr("exile"), false); } void PlayerActions::actMoveTopCardsToExileFaceDown() { - moveTopCardsTo(ZoneNames::EXILE, tr("exile"), true); + actRequestMoveTopCardsToDialog(ZoneNames::EXILE, tr("exile"), true); } -void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown) +void PlayerActions::actRequestMoveTopCardsToDialog(const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown) { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { return; } - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to %1").arg(zoneDisplayName), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, - maxCards, 1, &ok); - if (!ok) { + emit requestMoveTopCardsToDialog(defaultNumberTopCards, maxCards, targetZone, zoneDisplayName, faceDown); +} + +void PlayerActions::moveTopCardsTo(int number, const QString &targetZone, bool faceDown) +{ + const int maxCards = player->getDeckZone()->getCards().size(); + if (maxCards == 0) { return; } @@ -483,17 +483,16 @@ void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zon sendGameCommand(cmd); } -void PlayerActions::actMoveTopCardsUntil() +void PlayerActions::actRequestMoveTopCardsUntilDialog() { stopMoveTopCardsUntil(); - DlgMoveTopCardsUntil dlg(player->getGame()->getTab(), movingCardsUntilOptions); - if (!dlg.exec()) { - return; - } + emit requestMoveTopCardsUntilDialog(movingCardsUntilOptions); +} - auto expr = dlg.getExpr(); - movingCardsUntilOptions = dlg.getOptions(); +void PlayerActions::moveTopCardsUntil(const QString &expr, MoveTopCardsUntilOptions options) +{ + movingCardsUntilOptions = options; if (player->getDeckZone()->getCards().empty()) { stopMoveTopCardsUntil(); @@ -622,36 +621,40 @@ void PlayerActions::actMoveBottomCardToExile() void PlayerActions::actMoveBottomCardsToGrave() { - moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), false); + actRequestMoveBottomCardsToDialog(ZoneNames::GRAVE, tr("grave"), false); } void PlayerActions::actMoveBottomCardsToGraveFaceDown() { - moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), true); + actRequestMoveBottomCardsToDialog(ZoneNames::GRAVE, tr("grave"), true); } void PlayerActions::actMoveBottomCardsToExile() { - moveBottomCardsTo(ZoneNames::EXILE, tr("exile"), false); + actRequestMoveBottomCardsToDialog(ZoneNames::EXILE, tr("exile"), false); } void PlayerActions::actMoveBottomCardsToExileFaceDown() { - moveBottomCardsTo(ZoneNames::EXILE, tr("exile"), true); + actRequestMoveBottomCardsToDialog(ZoneNames::EXILE, tr("exile"), true); } -void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown) +void PlayerActions::actRequestMoveBottomCardsToDialog(const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown) { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { return; } - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to %1").arg(zoneDisplayName), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, - maxCards, 1, &ok); - if (!ok) { + emit requestMoveBottomCardsToDialog(defaultNumberBottomCards, maxCards, targetZone, zoneDisplayName, faceDown); +} + +void PlayerActions::moveBottomCardsTo(int number, const QString &targetZone, bool faceDown) +{ + const int maxCards = player->getDeckZone()->getCards().size(); + if (maxCards == 0) { return; } @@ -763,20 +766,24 @@ void PlayerActions::actDrawBottomCard() sendGameCommand(cmd); } -void PlayerActions::actDrawBottomCards() +void PlayerActions::actRequestDrawBottomCardsDialog() { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { return; } - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Draw bottom cards"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, - maxCards, 1, &ok); - if (!ok) { + emit requestDrawBottomCardsDialog(defaultNumberBottomCards, maxCards); +} + +void PlayerActions::actDrawBottomCards(int number) +{ + const int maxCards = player->getDeckZone()->getCards().size(); + if (maxCards == 0) { return; - } else if (number > maxCards) { + } + + if (number > maxCards) { number = maxCards; } defaultNumberBottomCards = number; @@ -843,16 +850,16 @@ void PlayerActions::actUntapAll() sendGameCommand(cmd); } -void PlayerActions::actRollDie() +void PlayerActions::actRequestRollDieDialog() { - DlgRollDice dlg(player->getGame()->getTab()); - if (!dlg.exec()) { - return; - } + emit requestRollDieDialog(); +} +void PlayerActions::actRollDie(int sides, int count) +{ Command_RollDie cmd; - cmd.set_sides(dlg.getDieSideCount()); - cmd.set_count(dlg.getDiceToRollCount()); + cmd.set_sides(sides); + cmd.set_count(count); sendGameCommand(cmd); } @@ -864,14 +871,14 @@ void PlayerActions::actFlipCoin() sendGameCommand(cmd); } -void PlayerActions::actCreateToken(const QStringList &predefinedTokens) +void PlayerActions::actRequestCreateTokenDialog(const QStringList &predefinedTokens) { - DlgCreateToken dlg(predefinedTokens, player->getGame()->getTab()); - if (!dlg.exec()) { - return; - } + emit requestCreateTokenDialog(predefinedTokens); +} - lastTokenInfo = dlg.getTokenInfo(); +void PlayerActions::actCreateToken(TokenInfo tokenToCreate) +{ + lastTokenInfo = tokenToCreate; ExactCard correctedCard = CardDatabaseManager::query()->guessCard({lastTokenInfo.name, lastTokenInfo.providerId}); if (correctedCard) { @@ -951,23 +958,17 @@ void PlayerActions::actCreatePredefinedToken() void PlayerActions::actCreateRelatedCard() { const CardItem *sourceCard = player->getGame()->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->getCardInfo().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()) { - ExactCard relatedCard = CardDatabaseManager::query()->getCardFromSameSet(cardRelation->getName(), - sourceCard->getCard().getPrinting()); - setLastToken(relatedCard.getCardPtr()); - } + CardRelation *cardRelation = relatedCards.at(action->data().toInt()); + actRequestCreateRelatedFromRelationDialog(sourceCard, cardRelation); } void PlayerActions::actCreateAllRelatedCards() @@ -987,7 +988,9 @@ void PlayerActions::actCreateAllRelatedCards() if (relatedCards.length() == 1) { cardRelation = relatedCards.at(0); - if (createRelatedFromRelation(sourceCard, cardRelation)) { + lastRelatedCreationSucceeded = false; // reset before emit + actRequestCreateRelatedFromRelationDialog(sourceCard, cardRelation); + if (lastRelatedCreationSucceeded) { ++tokensTypesCreated; } } else { @@ -999,15 +1002,18 @@ void PlayerActions::actCreateAllRelatedCards() } } switch (nonExcludedRelatedCards.length()) { - case 1: // if nonExcludedRelatedCards == 1 + case 1: cardRelation = nonExcludedRelatedCards.at(0); - if (createRelatedFromRelation(sourceCard, cardRelation)) { + lastRelatedCreationSucceeded = false; // reset before emit + actRequestCreateRelatedFromRelationDialog(sourceCard, cardRelation); + if (lastRelatedCreationSucceeded) { ++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 + case 0: for (CardRelation *cardRelationAll : relatedCards) { if (!cardRelationAll->getDoesAttach() && !cardRelationAll->getIsVariable()) { dbName = cardRelationAll->getName(); @@ -1022,7 +1028,8 @@ void PlayerActions::actCreateAllRelatedCards() } } break; - default: // else + + default: for (CardRelation *cardRelationNotExcluded : nonExcludedRelatedCards) { if (!cardRelationNotExcluded->getDoesAttach() && !cardRelationNotExcluded->getIsVariable()) { dbName = cardRelationNotExcluded->getName(); @@ -1050,50 +1057,83 @@ void PlayerActions::actCreateAllRelatedCards() } } -bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation) +void PlayerActions::actRequestCreateRelatedFromRelationDialog(const CardItem *sourceCard, + const CardRelation *cardRelation) +{ + emit requestCreateRelatedFromRelationDialog(sourceCard, cardRelation); +} + +bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, + const CardRelation *cardRelation, + int variableCount) { if (sourceCard == nullptr || cardRelation == nullptr) { return false; } - QString dbName = cardRelation->getName(); - bool persistent = cardRelation->getIsPersistent(); + + const QString dbName = cardRelation->getName(); + const bool persistent = cardRelation->getIsPersistent(); + + // Variable relations always use DoesNotAttach, regardless of the count the user + // entered. if (cardRelation->getIsVariable()) { - bool ok; - player->setDialogSemaphore(true); - int count = QInputDialog::getInt(player->getGame()->getTab(), tr("Create tokens"), tr("Number:"), - cardRelation->getDefaultCount(), 1, MAX_TOKENS_PER_DIALOG, 1, &ok); - player->setDialogSemaphore(false); - if (!ok) { + if (variableCount <= 0) { return false; } + for (int i = 0; i < variableCount; ++i) { + createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); + } + return true; + } + + const int count = cardRelation->getDefaultCount(); + + if (count > 1) { for (int i = 0; i < count; ++i) { createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); } - } else if (cardRelation->getDefaultCount() > 1) { - for (int i = 0; i < cardRelation->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, CardRelationType::DoesNotAttach, persistent); - } - } else { - CardRelationType attachType; - // do not attempt to attach to another player's cards, this causes the card to attempt to attach to the same - // cardid on the local player's field instead, which is an entirely different card! - if (player->getPlayerInfo()->getLocalOrJudge()) { - attachType = cardRelation->getAttachType(); - } else { - attachType = CardRelationType::DoesNotAttach; - } - - // move card onto table first if attaching from some other zone - // we only do this for AttachTo because cross-zone TransformInto is already handled server-side - if (attachType == CardRelationType::AttachTo && sourceCard->getZone()->getName() != ZoneNames::TABLE) { - playCardToTable(sourceCard, false); - } - - createCard(sourceCard, dbName, attachType, persistent); + return true; } + + CardRelationType attachType; + // do not attempt to attach to another player's cards, this causes the card to attempt to attach to the same + // cardid on the local player's field instead, which is an entirely different card! + if (player->getPlayerInfo()->getLocalOrJudge()) { + attachType = cardRelation->getAttachType(); + } else { + attachType = CardRelationType::DoesNotAttach; + } + + // move card onto table first if attaching from some other zone + // we only do this for AttachTo because cross-zone TransformInto is already handled server-side + if (attachType == CardRelationType::AttachTo && sourceCard->getZone()->getName() != ZoneNames::TABLE) { + playCardToTable(sourceCard, false); + } + + createCard(sourceCard, dbName, attachType, persistent); return true; } +void PlayerActions::onRelatedCardCreated(const CardItem *sourceCard, const CardRelation *cardRelation) +{ + if (sourceCard == nullptr || cardRelation == nullptr) { + return; + } + + /* + * If we make a token via "Token: TokenName" + * then let's allow it to be created via "create another token" + */ + if (!cardRelation->getCanCreateAnother()) { + return; + } + + ExactCard relatedCard = + CardDatabaseManager::query()->getCardFromSameSet(cardRelation->getName(), sourceCard->getCard().getPrinting()); + + setLastToken(relatedCard.getCardPtr()); +} + void PlayerActions::createCard(const CardItem *sourceCard, const QString &dbCardName, CardRelationType attachType, @@ -1171,35 +1211,29 @@ void PlayerActions::actSayMessage() sendGameCommand(cmd); } -void PlayerActions::actMoveCardXCardsFromTop(QList selectedCards) +void PlayerActions::actRequestMoveCardXCardsFromTopDialog() { int deckSize = player->getDeckZone()->getCards().size() + 1; // add the card to move to the deck - bool ok; - int number = - QInputDialog::getInt(player->getGame()->getTab(), 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; - } + emit requestMoveCardXCardsFromTopDialog(defaultNumberTopCardsToPlaceBelow, deckSize); +} +void PlayerActions::actMoveCardXCardsFromTop(QList selectedCards, int number) +{ defaultNumberTopCardsToPlaceBelow = number; - QList cardList = selectedCards; - if (cardList.isEmpty()) { + if (selectedCards.isEmpty()) { return; } QList commandList; ListOfCardsToMove idList; - for (const auto &i : cardList) { + for (const auto &i : selectedCards) { idList.add_card()->set_card_id(i->getId()); } - int startPlayerId = cardList[0]->getZone()->getPlayer()->getPlayerInfo()->getId(); - QString startZone = cardList[0]->getZone()->getName(); + int startPlayerId = selectedCards[0]->getZone()->getPlayer()->getPlayerInfo()->getId(); + QString startZone = selectedCards[0]->getZone()->getName(); auto *cmd = new Command_MoveCard; cmd->set_start_player_id(startPlayerId); @@ -1284,24 +1318,22 @@ void PlayerActions::actResetPT(QList selectedCards) } } -void PlayerActions::actSetPT(QList selectedCards) +void PlayerActions::actRequestSetPTDialog(QList selectedCards) { QString oldPT; - int playerid = player->getPlayerInfo()->getId(); for (auto card : selectedCards) { if (!card->getPT().isEmpty()) { oldPT = card->getPT(); } } - bool ok; - player->setDialogSemaphore(true); - QString pt = getTextWithMax(player->getGame()->getTab(), tr("Change power/toughness"), tr("Change stats to:"), - QLineEdit::Normal, oldPT, &ok); - player->setDialogSemaphore(false); - if (player->clearCardsToDelete() || !ok) { - return; - } + + emit requestSetPTDialog(oldPT); +} + +void PlayerActions::actSetPT(QList selectedCards, const QString &pt) +{ + int playerid = player->getPlayerInfo()->getId(); const auto ptList = CardItem::parsePT(pt); bool empty = ptList.isEmpty(); @@ -1426,7 +1458,7 @@ void AnnotationDialog::keyPressEvent(QKeyEvent *event) QInputDialog::keyPressEvent(event); } -void PlayerActions::actSetAnnotation(QList selectedCards) +void PlayerActions::actRequestSetAnnotationDialog(QList selectedCards) { QString oldAnnotation; for (auto card : selectedCards) { @@ -1435,19 +1467,11 @@ void PlayerActions::actSetAnnotation(QList selectedCards) } } - player->setDialogSemaphore(true); - AnnotationDialog *dialog = new AnnotationDialog(player->getGame()->getTab()); - dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput); - dialog->setWindowTitle(tr("Set annotation")); - dialog->setLabelText(tr("Please enter the new annotation:")); - dialog->setTextValue(oldAnnotation); - bool ok = dialog->exec(); - player->setDialogSemaphore(false); - if (player->clearCardsToDelete() || !ok) { - return; - } - QString annotation = dialog->textValue().left(MAX_NAME_LENGTH); + emit requestSetAnnotationDialog(oldAnnotation); +} +void PlayerActions::actSetAnnotation(QList selectedCards, const QString &annotation) +{ QList commandList; for (auto card : selectedCards) { auto *cmd = new Command_SetCardAttr; @@ -1519,10 +1543,8 @@ void PlayerActions::offsetCardCounter(QList selectedCards, int count sendGameCommand(prepareGameCommand(commandList)); } -void PlayerActions::actSetCardCounter(QList selectedCards, int counterId) +void PlayerActions::actRequestSetCardCounterDialog(QList selectedCards, int counterId) { - player->setDialogSemaphore(true); - // If a single card is selected, we show the old value in the dialog. Otherwise, we show "x" QString oldValueForDlg = "x"; if (selectedCards.size() == 1) { @@ -1530,22 +1552,16 @@ void PlayerActions::actSetCardCounter(QList selectedCards, int count oldValueForDlg = QString::number(card->getCounters().value(counterId, 0)); } - auto &cardCounterSettings = SettingsCache::instance().cardCounters(); - QString counterName = cardCounterSettings.displayName(counterId); - - AbstractCounterDialog dialog(counterName, oldValueForDlg, player->getGame()->getTab()); - int ok = dialog.exec(); - - player->setDialogSemaphore(false); - if (player->clearCardsToDelete() || !ok) { - return; - } + emit requestSetCardCounterDialog(counterId, oldValueForDlg); +} +void PlayerActions::actSetCardCounter(QList selectedCards, int counterId, const QString &counterValue) +{ QList commandList; for (auto card : selectedCards) { int oldValue = card->getCounters().value(counterId, 0); Expression exp(oldValue); - double parsed = exp.parse(dialog.textValue()); + double parsed = exp.parse(counterValue); // Clamp in double precision first to avoid UB, then cast int number = static_cast(qBound(0.0, parsed, static_cast(MAX_COUNTERS_ON_CARD))); diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index 940de610f..2779fa5aa 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -58,6 +58,31 @@ public: } signals: + void requestViewTopCardsDialog(int defaultNumberTopCards, int deckSize); + void requestViewBottomCardsDialog(int defaultNumberBottomCards, int deckSize); + void requestShuffleTopDialog(int defaultNumberTopCards, int maxCards); + void requestShuffleBottomDialog(int defaultNumberBottomCards, int maxCards); + void requestMulliganDialog(int startSize, int handSize, int deckSize); + void requestDrawCardsDialog(int defaultNumberTopCards, int deckSize); + void requestMoveTopCardsToDialog(int defaultNumberTopCards, + int maxCards, + const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown); + void requestMoveTopCardsUntilDialog(MoveTopCardsUntilOptions options); + void requestMoveBottomCardsToDialog(int defaultNumberBottomCards, + int maxCards, + const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown); + void requestDrawBottomCardsDialog(int defaultNumberBottomCards, int maxCards); + void requestRollDieDialog(); + void requestCreateTokenDialog(const QStringList &predefinedTokens); + void requestCreateRelatedFromRelationDialog(const CardItem *sourceCard, const CardRelation *cardRelation); + void requestMoveCardXCardsFromTopDialog(int defaultNumberTopCardsToPlaceBelow, int deckSize); + void requestSetPTDialog(const QString &oldPT); + void requestSetAnnotationDialog(const QString &oldAnnotation); + void requestSetCardCounterDialog(int counterId, const QString &oldValueForDlg); void requestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed = false); void requestSortHand(const QList &options); void requestEnableAndSetCreateAnotherTokenAction(const QString &lastTokenName); @@ -70,17 +95,30 @@ public slots: void playCardToTable(const CardItem *c, bool faceDown); void actUntapAll(); - void actRollDie(); + void actRequestRollDieDialog(); + void actRollDie(int sides, int count); void actFlipCoin(); - void actCreateToken(const QStringList &predefinedTokens); + void actRequestCreateTokenDialog(const QStringList &predefinedTokens); + void actCreateToken(TokenInfo tokenToCreate); void actCreateAnotherToken(); + void actRequestCreateRelatedFromRelationDialog(const CardItem *sourceCard, const CardRelation *cardRelation); + bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation, int variableCount); + void onRelatedCardCreated(const CardItem *sourceCard, const CardRelation *cardRelation); + void setLastRelatedCreationSucceeded(bool succeeded) + { + lastRelatedCreationSucceeded = succeeded; + } void actShuffle(); - void actShuffleTop(); - void actShuffleBottom(); + void actRequestShuffleTopDialog(); + void actShuffleTop(int number); + void actRequestShuffleBottomDialog(); + void actShuffleBottom(int number); void actDrawCard(); - void actDrawCards(); + void actRequestDrawCardsDialog(); + void actDrawCards(int number); void actUndoDraw(); - void actMulligan(); + void actRequestMulliganDialog(); + void actMulligan(int number); void actMulliganSameSize(); void actMulliganMinusOne(); void doMulligan(int number); @@ -97,10 +135,14 @@ public slots: void actMoveTopCardsToGraveFaceDown(); void actMoveTopCardsToExile(); void actMoveTopCardsToExileFaceDown(); - void actMoveTopCardsUntil(); + void actRequestMoveTopCardsUntilDialog(); + void moveTopCardsUntil(const QString &expr, MoveTopCardsUntilOptions options); void actMoveTopCardToBottom(); + void actRequestMoveTopCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); + void moveTopCardsTo(int number, const QString &targetZone, bool faceDown); void actDrawBottomCard(); - void actDrawBottomCards(); + void actRequestDrawBottomCardsDialog(); + void actDrawBottomCards(int number); void actMoveBottomCardToPlay(); void actMoveBottomCardToPlayFaceDown(); void actMoveBottomCardToGrave(); @@ -110,6 +152,8 @@ public slots: void actMoveBottomCardsToExile(); void actMoveBottomCardsToExileFaceDown(); void actMoveBottomCardToTop(); + void actRequestMoveBottomCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); + void moveBottomCardsTo(int number, const QString &targetZone, bool faceDown); void actSelectAll(); void actSelectRow(); @@ -117,8 +161,10 @@ public slots: void actViewLibrary(); void actViewHand(); - void actViewTopCards(); - void actViewBottomCards(); + void actRequestViewTopCardsDialog(); + void actViewTopCards(int number); + void actRequestViewBottomCardsDialog(); + void actViewBottomCards(int number); void actAlwaysRevealTopCard(bool alwaysRevealTopCard); void actAlwaysLookAtTopCard(bool alwaysRevealTopCard); void actViewGraveyard(); @@ -135,17 +181,20 @@ public slots: void actCreateRelatedCard(); void actCreateAllRelatedCards(); - void actMoveCardXCardsFromTop(QList selectedCards); + void actRequestMoveCardXCardsFromTopDialog(); + void actMoveCardXCardsFromTop(QList selectedCards, int number); void actRemoveCardCounter(QList selectedCards, int counterId); void actAddCardCounter(QList selectedCards, int counterId); - void actSetCardCounter(QList selectedCards, int counterId); + void actRequestSetCardCounterDialog(QList selectedCards, int counterId); + void actSetCardCounter(QList selectedCards, int counterId, const QString &counterValue); void actIncrementAllCardCounters(QList cardsToUpdate); void actAttach(); void actUnattach(QList selectedCards); void actDrawArrow(); void actIncPT(QList selectedCards, int deltaP, int deltaT); void actResetPT(QList selectedCards); - void actSetPT(QList selectedCards); + void actRequestSetPTDialog(QList selectedCards); + void actSetPT(QList selectedCards, const QString &pt); void actIncP(QList selectedCards); void actDecP(QList selectedCards); void actIncT(QList selectedCards); @@ -157,7 +206,8 @@ public slots: void actReduceLifeByPower(QList selectedCards); - void actSetAnnotation(QList selectedCards); + void actRequestSetAnnotationDialog(QList selectedCards); + void actSetAnnotation(QList selectedCards, const QString &annotation); void actReveal(QList selectedCards, QAction *action); void actRevealHand(int revealToPlayerId); void actRevealRandomHandCard(int revealToPlayerId); @@ -184,14 +234,12 @@ private: int movingCardsUntilCounter = 0; MoveTopCardsUntilOptions movingCardsUntilOptions; - void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); - void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); + bool lastRelatedCreationSucceeded = false; void createCard(const CardItem *sourceCard, const QString &dbCardName, CardRelationType attach = CardRelationType::DoesNotAttach, bool persistent = false); - bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); void playSelectedCards(QList selectedCards, bool faceDown = false); diff --git a/cockatrice/src/game/player/player_dialogs.cpp b/cockatrice/src/game/player/player_dialogs.cpp new file mode 100644 index 000000000..3c26ae1fe --- /dev/null +++ b/cockatrice/src/game/player/player_dialogs.cpp @@ -0,0 +1,298 @@ +#include "player_dialogs.h" + +#include "../../client/settings/card_counter_settings.h" +#include "../../interface/widgets/utility/get_text_with_max.h" +#include "../board/card_item.h" +#include "../dialogs/dlg_roll_dice.h" +#include "../player/player_graphics_item.h" + +#include +#include + +PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_playerActions) + : QObject(_player), player(_player), playerActions(_playerActions) +{ + connect(playerActions, &PlayerActions::requestViewTopCardsDialog, this, + &PlayerDialogs::onViewTopCardsDialogRequested); + + connect(playerActions, &PlayerActions::requestViewBottomCardsDialog, this, + &PlayerDialogs::onViewBottomCardsDialogRequested); + + connect(playerActions, &PlayerActions::requestShuffleTopDialog, this, &PlayerDialogs::onShuffleTopDialogRequested); + + connect(playerActions, &PlayerActions::requestShuffleBottomDialog, this, + &PlayerDialogs::onShuffleBottomDialogRequested); + + connect(playerActions, &PlayerActions::requestMulliganDialog, this, &PlayerDialogs::onMulliganDialogRequested); + + connect(playerActions, &PlayerActions::requestDrawCardsDialog, this, &PlayerDialogs::onDrawCardsDialogRequested); + + connect(playerActions, &PlayerActions::requestMoveTopCardsToDialog, this, + &PlayerDialogs::onMoveTopCardsToDialogRequested); + + connect(playerActions, &PlayerActions::requestMoveTopCardsUntilDialog, this, + &PlayerDialogs::onMoveTopCardsUntilDialogRequested); + + connect(playerActions, &PlayerActions::requestMoveBottomCardsToDialog, this, + &PlayerDialogs::onMoveBottomCardsToDialogRequested); + + connect(playerActions, &PlayerActions::requestDrawBottomCardsDialog, this, + &PlayerDialogs::onDrawBottomCardsDialogRequested); + + connect(playerActions, &PlayerActions::requestRollDieDialog, this, &PlayerDialogs::onRollDieDialogRequested); + + connect(playerActions, &PlayerActions::requestCreateTokenDialog, this, + &PlayerDialogs::onCreateTokenDialogRequested); + + connect(playerActions, &PlayerActions::requestCreateRelatedFromRelationDialog, this, + &PlayerDialogs::onCreateRelatedFromRelationDialogRequested); + + connect(playerActions, &PlayerActions::requestMoveCardXCardsFromTopDialog, this, + &PlayerDialogs::onMoveCardXCardsFromTopDialogRequested); + + connect(playerActions, &PlayerActions::requestSetPTDialog, this, &PlayerDialogs::onSetPTDialogRequested); + + connect(playerActions, &PlayerActions::requestSetAnnotationDialog, this, + &PlayerDialogs::onSetAnnotationDialogRequested); + + connect(playerActions, &PlayerActions::requestSetCardCounterDialog, this, + &PlayerDialogs::onSetCardCounterDialogRequested); +} + +void PlayerDialogs::onViewTopCardsDialogRequested(int defaultNumberTopCards, int deckSize) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("View top cards of library"), + tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberTopCards, 1, + deckSize, 1, &ok); + if (ok) { + playerActions->actViewTopCards(number); + } +} + +void PlayerDialogs::onViewBottomCardsDialogRequested(int defaultNumberBottomCards, int deckSize) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("View bottom cards of library"), + tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberBottomCards, 1, + deckSize, 1, &ok); + if (ok) { + playerActions->actViewBottomCards(number); + } +} + +void PlayerDialogs::onShuffleTopDialogRequested(int defaultNumberTopCards, int maxCards) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("Shuffle top cards of library"), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, + maxCards, 1, &ok); + if (ok) { + playerActions->actShuffleTop(number); + } +} + +void PlayerDialogs::onShuffleBottomDialogRequested(int defaultNumberBottomCards, int maxCards) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("Shuffle bottom cards of library"), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, + maxCards, 1, &ok); + if (ok) { + playerActions->actShuffleBottom(number); + } +} + +void PlayerDialogs::onMulliganDialogRequested(int startSize, int handSize, int deckSize) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), 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) { + playerActions->actMulligan(number); + } +} + +void PlayerDialogs::onDrawCardsDialogRequested(int defaultNumberTopCards, int deckSize) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("Draw cards"), tr("Number of cards: (max. %1)").arg(deckSize), + defaultNumberTopCards, 1, deckSize, 1, &ok); + + if (ok) { + playerActions->actDrawCards(number); + } +} + +void PlayerDialogs::onMoveTopCardsToDialogRequested(int defaultNumberTopCards, + int maxCards, + const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("Move top cards to %1").arg(zoneDisplayName), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, + maxCards, 1, &ok); + if (ok) { + playerActions->moveTopCardsTo(number, targetZone, faceDown); + } +} + +void PlayerDialogs::onMoveTopCardsUntilDialogRequested(MoveTopCardsUntilOptions options) +{ + DlgMoveTopCardsUntil dlg(dialogParent(), options); + if (!dlg.exec()) { + return; + } + playerActions->moveTopCardsUntil(dlg.getExpr(), dlg.getOptions()); +} + +void PlayerDialogs::onMoveBottomCardsToDialogRequested(int defaultNumberBottomCards, + int maxCards, + const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown) +{ + bool ok; + int number = QInputDialog::getInt(dialogParent(), tr("Move bottom cards to %1").arg(zoneDisplayName), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, + maxCards, 1, &ok); + if (ok) { + playerActions->moveBottomCardsTo(number, targetZone, faceDown); + } +} + +void PlayerDialogs::onDrawBottomCardsDialogRequested(int defaultNumberBottomCards, int maxCards) +{ + bool ok; + int number = + QInputDialog::getInt(dialogParent(), tr("Draw bottom cards"), tr("Number of cards: (max. %1)").arg(maxCards), + defaultNumberBottomCards, 1, maxCards, 1, &ok); + if (ok) { + playerActions->actDrawBottomCards(number); + } +} + +void PlayerDialogs::onRollDieDialogRequested() +{ + DlgRollDice dlg(dialogParent()); + if (!dlg.exec()) { + return; + } + playerActions->actRollDie(dlg.getDieSideCount(), dlg.getDiceToRollCount()); +} + +void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard, + const CardRelation *cardRelation) +{ + if (sourceCard == nullptr || cardRelation == nullptr) { + playerActions->setLastRelatedCreationSucceeded(false); + return; + } + + int variableCount = cardRelation->getDefaultCount(); + + if (cardRelation->getIsVariable()) { + bool ok; + + emit requestDialogSemaphore(true); + + variableCount = QInputDialog::getInt(dialogParent(), tr("Create tokens"), tr("Number:"), + cardRelation->getDefaultCount(), 1, MAX_TOKENS_PER_DIALOG, 1, &ok); + + emit requestDialogSemaphore(false); + + if (!ok) { + playerActions->setLastRelatedCreationSucceeded(false); // cancelled + return; + } + } + + const bool succeeded = playerActions->createRelatedFromRelation(sourceCard, cardRelation, variableCount); + + playerActions->setLastRelatedCreationSucceeded(succeeded); + + if (succeeded) { + playerActions->onRelatedCardCreated(sourceCard, cardRelation); // only on confirmed success + } +} + +void PlayerDialogs::onCreateTokenDialogRequested(const QStringList &predefinedTokens) +{ + DlgCreateToken dlg(predefinedTokens, dialogParent()); + if (!dlg.exec()) { + return; + } + + playerActions->actCreateToken(dlg.getTokenInfo()); +} + +void PlayerDialogs::onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopCardsToPlaceBelow, int deckSize) +{ + bool ok; + int number = + QInputDialog::getInt(dialogParent(), 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) { + playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCards(), number); + } +} + +void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT) +{ + bool ok; + auto cards = player->getGameScene()->selectedCards(); + emit requestDialogSemaphore(true); + QString pt = getTextWithMax(dialogParent(), tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal, + oldPT, &ok); + emit requestDialogSemaphore(false); + + if (!ok || player->getLogic()->clearCardsToDelete()) { + return; + } + + playerActions->actSetPT(cards, pt); +} + +void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation) +{ + auto cards = player->getGameScene()->selectedCards(); + emit requestDialogSemaphore(true); + AnnotationDialog *dialog = new AnnotationDialog(dialogParent()); + dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput); + dialog->setWindowTitle(tr("Set annotation")); + dialog->setLabelText(tr("Please enter the new annotation:")); + dialog->setTextValue(oldAnnotation); + bool ok = dialog->exec(); + emit requestDialogSemaphore(false); + if (!ok || player->getLogic()->clearCardsToDelete()) { + return; + } + QString annotation = dialog->textValue().left(MAX_NAME_LENGTH); + playerActions->actSetAnnotation(cards, annotation); +} + +void PlayerDialogs::onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg) +{ + auto cards = player->getGameScene()->selectedCards(); + emit requestDialogSemaphore(true); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + QString counterName = cardCounterSettings.displayName(counterId); + + AbstractCounterDialog dialog(counterName, oldValueForDlg, dialogParent()); + int ok = dialog.exec(); + + emit requestDialogSemaphore(false); + if (!ok || player->getLogic()->clearCardsToDelete()) { + return; + } + playerActions->actSetCardCounter(cards, counterId, dialog.textValue()); +} \ No newline at end of file diff --git a/cockatrice/src/game/player/player_dialogs.h b/cockatrice/src/game/player/player_dialogs.h new file mode 100644 index 000000000..a15c5174f --- /dev/null +++ b/cockatrice/src/game/player/player_dialogs.h @@ -0,0 +1,62 @@ +#ifndef COCKATRICE_PLAYER_DIALOGS_H +#define COCKATRICE_PLAYER_DIALOGS_H +#include "player_actions.h" + +#include +#include + +class PlayerGraphicsItem; +class PlayerDialogs : public QObject +{ + + Q_OBJECT + +public: + explicit PlayerDialogs(PlayerGraphicsItem *player, PlayerActions *playerActions); + +signals: + void requestDialogSemaphore(bool active); + +public slots: + void onViewTopCardsDialogRequested(int defaultNumberTopCards, int deckSize); + void onViewBottomCardsDialogRequested(int defaultNumberBottomCards, int deckSize); + void onShuffleTopDialogRequested(int defaultNumberTopCards, int maxCards); + void onShuffleBottomDialogRequested(int defaultNumberBottomCards, int maxCards); + void onMulliganDialogRequested(int startSize, int handSize, int deckSize); + void onDrawCardsDialogRequested(int defaultNumberTopCards, int deckSize); + void onMoveTopCardsToDialogRequested(int defaultNumberTopCards, + int maxCards, + const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown); + void onMoveTopCardsUntilDialogRequested(MoveTopCardsUntilOptions options); + void onMoveBottomCardsToDialogRequested(int defaultNumberBottomCards, + int maxCards, + const QString &targetZone, + const QString &zoneDisplayName, + bool faceDown); + void onDrawBottomCardsDialogRequested(int defaultNumberBottomCards, int maxCards); + void onRollDieDialogRequested(); + void onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard, const CardRelation *cardRelation); + void onCreateTokenDialogRequested(const QStringList &predefinedTokens); + void onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopCardsToPlaceBelow, int deckSize); + void onSetPTDialogRequested(const QString &oldPT); + void onSetAnnotationDialogRequested(const QString &oldAnnotation); + void onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg); + +private: + PlayerGraphicsItem *player; + PlayerActions *playerActions; + + QWidget *dialogParent() const + { + if (auto *s = player->scene()) { + if (auto *v = s->views().value(0)) { + return v->window(); + } + } + return nullptr; + } +}; + +#endif // COCKATRICE_PLAYER_DIALOGS_H diff --git a/cockatrice/src/game/player/player_graphics_item.cpp b/cockatrice/src/game/player/player_graphics_item.cpp index d86fce86b..b0a476d5a 100644 --- a/cockatrice/src/game/player/player_graphics_item.cpp +++ b/cockatrice/src/game/player/player_graphics_item.cpp @@ -9,6 +9,7 @@ #include "../board/counter_general.h" #include "../hand_counter.h" #include "player_actions.h" +#include "player_dialogs.h" #include @@ -44,6 +45,10 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) } }); + playerDialogs = new PlayerDialogs(this, player->getPlayerActions()); + + connect(playerDialogs, &PlayerDialogs::requestDialogSemaphore, player, &PlayerLogic::setDialogSemaphore); + playerArea = new PlayerArea(this); playerTarget = new PlayerTarget(player, playerArea); diff --git a/cockatrice/src/game/player/player_graphics_item.h b/cockatrice/src/game/player/player_graphics_item.h index 1acb1520f..c1fcb4ed8 100644 --- a/cockatrice/src/game/player/player_graphics_item.h +++ b/cockatrice/src/game/player/player_graphics_item.h @@ -14,6 +14,7 @@ class HandZone; class PileZone; +class PlayerDialogs; class PlayerTarget; class StackZone; class TableZone; @@ -122,6 +123,7 @@ signals: private: PlayerLogic *player; PlayerMenu *playerMenu; + PlayerDialogs *playerDialogs; PlayerArea *playerArea; PlayerTarget *playerTarget; QMap counterWidgets;