diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index a263bd54b..01ae93962 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -207,6 +207,8 @@ set(cockatrice_SOURCES src/game/filters/filter_tree.cpp src/game/filters/filter_tree_model.cpp src/game/filters/syntax_help.cpp + src/game/abstract_game.cpp + src/game/game.cpp src/game/game_event_handler.cpp src/game/game_meta_info.cpp src/game/game_scene.cpp @@ -216,9 +218,18 @@ set(cockatrice_SOURCES src/game/games_model.cpp src/game/hand_counter.cpp src/game/phase.cpp + src/game/player/event_processing_options.h src/game/player/player.cpp + src/game/player/player_actions.cpp + src/game/player/player_area.cpp + src/game/player/player_event_handler.cpp + src/game/player/player_graphics_item.cpp + src/game/player/player_info.cpp src/game/player/player_list_widget.cpp + src/game/player/player_manager.cpp + src/game/player/player_menu.cpp src/game/player/player_target.cpp + src/game/replay.cpp src/game/zones/card_zone.cpp src/game/zones/hand_zone.cpp src/game/zones/pile_zone.cpp @@ -227,6 +238,12 @@ set(cockatrice_SOURCES src/game/zones/table_zone.cpp src/game/zones/view_zone.cpp src/game/zones/view_zone_widget.cpp + src/game/zones/logic/card_zone_logic.cpp + src/game/zones/logic/hand_zone_logic.cpp + src/game/zones/logic/pile_zone_logic.cpp + src/game/zones/logic/stack_zone_logic.cpp + src/game/zones/logic/table_zone_logic.cpp + src/game/zones/logic/view_zone_logic.cpp src/main.cpp src/server/abstract_client.cpp src/server/chat_view/chat_view.cpp diff --git a/cockatrice/src/client/network/replay_timeline_widget.cpp b/cockatrice/src/client/network/replay_timeline_widget.cpp index 919ee3d97..1f258bf68 100644 --- a/cockatrice/src/client/network/replay_timeline_widget.cpp +++ b/cockatrice/src/client/network/replay_timeline_widget.cpp @@ -1,5 +1,7 @@ #include "replay_timeline_widget.h" +#include "../../settings/cache_settings.h" + #include #include #include @@ -151,16 +153,16 @@ void ReplayTimelineWidget::processNewEvents(PlaybackMode playbackMode) currentProcessedTime = currentVisualTime; while ((currentEvent < replayTimeline.size()) && (replayTimeline[currentEvent] < currentProcessedTime)) { - Player::EventProcessingOptions options; + EventProcessingOptions options; // backwards skip => always skip reveal windows // forwards skip => skip reveal windows that don't happen within a big skip of the target if (playbackMode == BACKWARD_SKIP || currentProcessedTime - replayTimeline[currentEvent] > BIG_SKIP_MS) - options |= Player::EventProcessingOption::SKIP_REVEAL_WINDOW; + options |= SKIP_REVEAL_WINDOW; // backwards skip => always skip tap animation if (playbackMode == BACKWARD_SKIP) - options |= Player::EventProcessingOption::SKIP_TAP_ANIMATION; + options |= SKIP_TAP_ANIMATION; emit processNextEvent(options); ++currentEvent; diff --git a/cockatrice/src/client/network/replay_timeline_widget.h b/cockatrice/src/client/network/replay_timeline_widget.h index 9676e7b47..1ff995826 100644 --- a/cockatrice/src/client/network/replay_timeline_widget.h +++ b/cockatrice/src/client/network/replay_timeline_widget.h @@ -1,7 +1,7 @@ #ifndef REPLAY_TIMELINE_WIDGET #define REPLAY_TIMELINE_WIDGET -#include "../../game/player/player.h" +#include "../../game/player/event_processing_options.h" #include #include @@ -14,7 +14,7 @@ class ReplayTimelineWidget : public QWidget { Q_OBJECT signals: - void processNextEvent(Player::EventProcessingOptions options); + void processNextEvent(EventProcessingOptions options); void replayFinished(); void rewound(); diff --git a/cockatrice/src/client/replay_manager.cpp b/cockatrice/src/client/replay_manager.cpp index f410f75fa..1890d3308 100644 --- a/cockatrice/src/client/replay_manager.cpp +++ b/cockatrice/src/client/replay_manager.cpp @@ -11,7 +11,7 @@ ReplayManager::ReplayManager(TabGame *parent, GameReplay *_replay) aReplaySkipBackwardBig(nullptr) { if (replay) { - game->loadReplay(replay); + game->getGame()->loadReplay(replay); // Create list: event number -> time [ms] // Distribute simultaneous events evenly across 1 second. @@ -93,10 +93,10 @@ ReplayManager::ReplayManager(TabGame *parent, GameReplay *_replay) refreshShortcuts(); } -void ReplayManager::replayNextEvent(Player::EventProcessingOptions options) +void ReplayManager::replayNextEvent(EventProcessingOptions options) { - game->getGameEventHandler()->processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), - nullptr, options); + game->getGame()->getGameEventHandler()->processGameEventContainer( + replay->event_list(timelineWidget->getCurrentEvent()), nullptr, options); } void ReplayManager::replayFinished() diff --git a/cockatrice/src/client/replay_manager.h b/cockatrice/src/client/replay_manager.h index a95c8e178..4c5f0ea38 100644 --- a/cockatrice/src/client/replay_manager.h +++ b/cockatrice/src/client/replay_manager.h @@ -2,6 +2,7 @@ #define REPLAY_MANAGER_H #include "network/replay_timeline_widget.h" #include "pb/game_replay.pb.h" +#include "tabs/tab_game.h" #include @@ -26,7 +27,7 @@ private: QAction *aReplaySkipForward, *aReplaySkipBackward, *aReplaySkipForwardBig, *aReplaySkipBackwardBig; private slots: - void replayNextEvent(Player::EventProcessingOptions options); + void replayNextEvent(EventProcessingOptions options); void replayFinished(); void replayPlayButtonToggled(bool checked); void replayFastForwardButtonToggled(bool checked); diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index 9b8aa06c3..8b521634f 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -8,10 +8,12 @@ #include "../../game/cards/card_database_manager.h" #include "../../game/deckview/deck_view_container.h" #include "../../game/deckview/tabbed_deck_view_container.h" +#include "../../game/game.h" #include "../../game/game_scene.h" #include "../../game/game_view.h" #include "../../game/player/player.h" #include "../../game/player/player_list_widget.h" +#include "../../game/replay.h" #include "../../game/zones/card_zone.h" #include "../../main.h" #include "../../server/abstract_client.h" @@ -46,23 +48,14 @@ #include TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) - : Tab(_tabSupervisor), activeCard(nullptr), sayLabel(nullptr), sayEdit(nullptr) + : Tab(_tabSupervisor), sayLabel(nullptr), sayEdit(nullptr) { // THIS CTOR IS USED ON REPLAY - - gameMetaInfo = new GameMetaInfo(); - gameState = new GameState(0, -1, -1, _tabSupervisor->getIsLocalGame(), QList(), true, false, - false, false, -1, false); - connectToGameState(); - - gameEventHandler = new GameEventHandler(this); - connectToGameEventHandler(); + game = new Replay(this, _replay); createCardInfoDock(true); createPlayerListDock(true); - connectPlayerListToGameEventHandler(); createMessageDock(true); - connectMessageLogToGameEventHandler(); createPlayAreaWidget(true); createDeckViewContainerWidget(true); createReplayDock(_replay); @@ -79,11 +72,19 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) createReplayMenuItems(); createViewMenuItems(); + + connectToGameState(); + connectToPlayerManager(); + connectToGameEventHandler(); + connectPlayerListToGameEventHandler(); + connectMessageLogToGameEventHandler(); + connectMessageLogToPlayerHandler(); + retranslateUi(); connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, &TabGame::refreshShortcuts); refreshShortcuts(); - messageLog->logReplayStarted(gameMetaInfo->gameId()); + messageLog->logReplayStarted(game->getGameMetaInfo()->gameId()); QTimer::singleShot(0, this, &TabGame::loadLayout); } @@ -92,29 +93,14 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_clients, const Event_GameJoined &event, const QMap &_roomGameTypes) - : Tab(_tabSupervisor), userListProxy(_tabSupervisor->getUserListManager()), activeCard(nullptr) + : Tab(_tabSupervisor), userListProxy(_tabSupervisor->getUserListManager()) { - - gameMetaInfo = new GameMetaInfo(); - gameMetaInfo->setFromProto(event.game_info()); - gameMetaInfo->setRoomGameTypes(_roomGameTypes); - gameState = new GameState(0, event.host_id(), event.player_id(), _tabSupervisor->getIsLocalGame(), _clients, - event.spectator(), event.judge(), false, event.resuming(), -1, false); - connectToGameState(); - // THIS CTOR IS USED ON GAMES - gameMetaInfo->setStarted(false); - - connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); - - gameEventHandler = new GameEventHandler(this); - connectToGameEventHandler(); + game = new Game(this, _clients, event, _roomGameTypes); createCardInfoDock(); createPlayerListDock(); - connectPlayerListToGameEventHandler(); createMessageDock(); - connectMessageLogToGameEventHandler(); createPlayAreaWidget(); createDeckViewContainerWidget(); createReplayDock(nullptr); @@ -132,92 +118,116 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, createMenuItems(); createViewMenuItems(); + + connectToGameState(); + connectToPlayerManager(); + connectToGameEventHandler(); + connectPlayerListToGameEventHandler(); + connectMessageLogToGameEventHandler(); + connectMessageLogToPlayerHandler(); + retranslateUi(); connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, &TabGame::refreshShortcuts); refreshShortcuts(); // append game to rooms game list for others to see - for (int i = gameMetaInfo->gameTypesSize() - 1; i >= 0; i--) - gameTypes.append(gameMetaInfo->findRoomGameType(i)); + for (int i = game->getGameMetaInfo()->gameTypesSize() - 1; i >= 0; i--) + gameTypes.append(game->getGameMetaInfo()->findRoomGameType(i)); QTimer::singleShot(0, this, &TabGame::loadLayout); } void TabGame::connectToGameState() { - connect(gameState, &GameState::playerAdded, this, &TabGame::addPlayer); - connect(gameState, &GameState::gameStarted, this, &TabGame::startGame); - connect(gameState, &GameState::activePhaseChanged, this, &TabGame::setActivePhase); - connect(gameState, &GameState::activePlayerChanged, this, &TabGame::setActivePlayer); + connect(game->getGameState(), &GameState::gameStarted, this, &TabGame::startGame); + connect(game->getGameState(), &GameState::activePhaseChanged, this, &TabGame::setActivePhase); + connect(game->getGameState(), &GameState::activePlayerChanged, this, &TabGame::setActivePlayer); +} + +void TabGame::connectToPlayerManager() +{ + connect(game->getPlayerManager(), &PlayerManager::playerAdded, this, &TabGame::addPlayer); + // update menu text when player concedes so that "concede" gets updated to "unconcede" + connect(game->getPlayerManager(), &PlayerManager::playerConceded, this, &TabGame::retranslateUi); } void TabGame::connectToGameEventHandler() { - connect(this, &TabGame::gameLeft, gameEventHandler, &GameEventHandler::handleGameLeft); - connect(gameEventHandler, &GameEventHandler::gameStopped, this, &TabGame::stopGame); - connect(gameEventHandler, &GameEventHandler::gameClosed, this, &TabGame::closeGame); - connect(gameEventHandler, &GameEventHandler::localPlayerReadyStateChanged, this, + connect(this, &TabGame::gameLeft, game->getGameEventHandler(), &GameEventHandler::handleGameLeft); + connect(game->getGameEventHandler(), &GameEventHandler::emitUserEvent, this, &TabGame::emitUserEvent); + connect(game->getGameEventHandler(), &GameEventHandler::gameStopped, this, &TabGame::stopGame); + connect(game->getGameEventHandler(), &GameEventHandler::gameClosed, this, &TabGame::closeGame); + connect(game->getGameEventHandler(), &GameEventHandler::localPlayerReadyStateChanged, this, &TabGame::processLocalPlayerReadyStateChanged); - connect(gameEventHandler, &GameEventHandler::localPlayerSideboardLocked, this, + connect(game->getGameEventHandler(), &GameEventHandler::localPlayerSideboardLocked, this, &TabGame::processLocalPlayerSideboardLocked); - connect(gameEventHandler, &GameEventHandler::localPlayerDeckSelected, this, &TabGame::processLocalPlayerDeckSelect); + connect(game->getGameEventHandler(), &GameEventHandler::localPlayerDeckSelected, this, + &TabGame::processLocalPlayerDeckSelect); } void TabGame::connectMessageLogToGameEventHandler() { - // connect(gameEventHandler, &GameEventHandler:: , messageLog, &MessageLogWidget::); - connect(gameEventHandler, &GameEventHandler::gameFlooded, messageLog, &MessageLogWidget::logGameFlooded); - connect(gameEventHandler, &GameEventHandler::containerProcessingStarted, messageLog, + connect(game->getGameEventHandler(), &GameEventHandler::gameFlooded, messageLog, &MessageLogWidget::logGameFlooded); + connect(game->getGameEventHandler(), &GameEventHandler::containerProcessingStarted, messageLog, &MessageLogWidget::containerProcessingStarted); - connect(gameEventHandler, &GameEventHandler::containerProcessingDone, messageLog, + connect(game->getGameEventHandler(), &GameEventHandler::containerProcessingDone, messageLog, &MessageLogWidget::containerProcessingDone); - connect(gameEventHandler, &GameEventHandler::setContextJudgeName, messageLog, + connect(game->getGameEventHandler(), &GameEventHandler::setContextJudgeName, messageLog, &MessageLogWidget::setContextJudgeName); - connect(gameEventHandler, &GameEventHandler::logSpectatorSay, messageLog, &MessageLogWidget::logSpectatorSay); + connect(game->getGameEventHandler(), &GameEventHandler::logSpectatorSay, messageLog, + &MessageLogWidget::logSpectatorSay); - connect(gameEventHandler, &GameEventHandler::logJoinPlayer, messageLog, &MessageLogWidget::logJoin); - connect(gameEventHandler, &GameEventHandler::logJoinSpectator, messageLog, &MessageLogWidget::logJoinSpectator); - connect(gameEventHandler, &GameEventHandler::logLeave, messageLog, &MessageLogWidget::logLeave); - connect(gameEventHandler, &GameEventHandler::logKicked, messageLog, &MessageLogWidget::logKicked); - connect(gameEventHandler, &GameEventHandler::logConnectionStateChanged, messageLog, + connect(game->getGameEventHandler(), &GameEventHandler::logJoinPlayer, messageLog, &MessageLogWidget::logJoin); + connect(game->getGameEventHandler(), &GameEventHandler::logJoinSpectator, messageLog, + &MessageLogWidget::logJoinSpectator); + connect(game->getGameEventHandler(), &GameEventHandler::logLeave, messageLog, &MessageLogWidget::logLeave); + connect(game->getGameEventHandler(), &GameEventHandler::logKicked, messageLog, &MessageLogWidget::logKicked); + connect(game->getGameEventHandler(), &GameEventHandler::logConnectionStateChanged, messageLog, &MessageLogWidget::logConnectionStateChanged); - connect(gameEventHandler, &GameEventHandler::logDeckSelect, messageLog, &MessageLogWidget::logDeckSelect); - connect(gameEventHandler, &GameEventHandler::logSideboardLockSet, messageLog, + connect(game->getGameEventHandler(), &GameEventHandler::logDeckSelect, messageLog, + &MessageLogWidget::logDeckSelect); + connect(game->getGameEventHandler(), &GameEventHandler::logSideboardLockSet, messageLog, &MessageLogWidget::logSetSideboardLock); - connect(gameEventHandler, &GameEventHandler::logReadyStart, messageLog, &MessageLogWidget::logReadyStart); - connect(gameEventHandler, &GameEventHandler::logNotReadyStart, messageLog, &MessageLogWidget::logNotReadyStart); - connect(gameEventHandler, &GameEventHandler::logGameStart, messageLog, &MessageLogWidget::logGameStart); + connect(game->getGameEventHandler(), &GameEventHandler::logReadyStart, messageLog, + &MessageLogWidget::logReadyStart); + connect(game->getGameEventHandler(), &GameEventHandler::logNotReadyStart, messageLog, + &MessageLogWidget::logNotReadyStart); + connect(game->getGameEventHandler(), &GameEventHandler::logGameStart, messageLog, &MessageLogWidget::logGameStart); - connect(gameEventHandler, &GameEventHandler::playerConceded, messageLog, &MessageLogWidget::logConcede); - connect(gameEventHandler, &GameEventHandler::playerUnconceded, messageLog, &MessageLogWidget::logUnconcede); - - connect(gameEventHandler, &GameEventHandler::logActivePlayer, messageLog, &MessageLogWidget::logSetActivePlayer); - connect(gameEventHandler, &GameEventHandler::logActivePhaseChanged, messageLog, + connect(game->getGameEventHandler(), &GameEventHandler::logActivePlayer, messageLog, + &MessageLogWidget::logSetActivePlayer); + connect(game->getGameEventHandler(), &GameEventHandler::logActivePhaseChanged, messageLog, &MessageLogWidget::logSetActivePhase); - connect(gameEventHandler, &GameEventHandler::logTurnReversed, messageLog, &MessageLogWidget::logReverseTurn); + connect(game->getGameEventHandler(), &GameEventHandler::logTurnReversed, messageLog, + &MessageLogWidget::logReverseTurn); - connect(gameEventHandler, &GameEventHandler::logGameClosed, messageLog, &MessageLogWidget::logGameClosed); + connect(game->getGameEventHandler(), &GameEventHandler::logGameClosed, messageLog, + &MessageLogWidget::logGameClosed); +} + +void TabGame::connectMessageLogToPlayerHandler() +{ + connect(game->getPlayerManager(), &PlayerManager::playerConceded, messageLog, &MessageLogWidget::logConcede); + connect(game->getPlayerManager(), &PlayerManager::playerUnconceded, messageLog, &MessageLogWidget::logUnconcede); } void TabGame::connectPlayerListToGameEventHandler() { - connect(gameEventHandler, &GameEventHandler::playerJoined, playerListWidget, &PlayerListWidget::addPlayer); - connect(gameEventHandler, &GameEventHandler::playerLeft, playerListWidget, &PlayerListWidget::removePlayer); - connect(gameEventHandler, &GameEventHandler::spectatorJoined, playerListWidget, &PlayerListWidget::addPlayer); - connect(gameEventHandler, &GameEventHandler::spectatorLeft, playerListWidget, &PlayerListWidget::removePlayer); - connect(gameEventHandler, &GameEventHandler::playerPropertiesChanged, playerListWidget, + connect(game->getGameEventHandler(), &GameEventHandler::playerJoined, playerListWidget, + &PlayerListWidget::addPlayer); + connect(game->getGameEventHandler(), &GameEventHandler::playerLeft, playerListWidget, + &PlayerListWidget::removePlayer); + connect(game->getGameEventHandler(), &GameEventHandler::spectatorJoined, playerListWidget, + &PlayerListWidget::addPlayer); + connect(game->getGameEventHandler(), &GameEventHandler::spectatorLeft, playerListWidget, + &PlayerListWidget::removePlayer); + connect(game->getGameEventHandler(), &GameEventHandler::playerPropertiesChanged, playerListWidget, &PlayerListWidget::updatePlayerProperties); } -void TabGame::loadReplay(GameReplay *replay) -{ - gameMetaInfo->setFromProto(replay->game_info()); - gameMetaInfo->setSpectatorsOmniscient(true); -} - void TabGame::addMentionTag(const QString &value) { sayEdit->insert(value + " "); @@ -236,12 +246,13 @@ void TabGame::resetChatAndPhase() messageLog->clearChat(); // reset phase markers - gameState->setCurrentPhase(-1); + game->getGameState()->setCurrentPhase(-1); } void TabGame::emitUserEvent() { - bool globalEvent = !gameState->isSpectator() || SettingsCache::instance().getSpectatorNotificationsEnabled(); + bool globalEvent = + !game->getPlayerManager()->isSpectator() || SettingsCache::instance().getSpectatorNotificationsEnabled(); emit userEvent(globalEvent); updatePlayerListDockTitle(); } @@ -253,17 +264,18 @@ TabGame::~TabGame() void TabGame::updatePlayerListDockTitle() { - QString tabText = - " | " + (replayManager->replay ? tr("Replay") : tr("Game")) + " #" + QString::number(gameMetaInfo->gameId()); - QString userCountInfo = QString(" %1/%2").arg(gameState->getPlayerCount()).arg(gameMetaInfo->maxPlayers()); + QString tabText = " | " + (replayManager->replay ? tr("Replay") : tr("Game")) + " #" + + QString::number(game->getGameMetaInfo()->gameId()); + QString userCountInfo = + QString(" %1/%2").arg(game->getPlayerManager()->getPlayerCount()).arg(game->getGameMetaInfo()->maxPlayers()); playerListDock->setWindowTitle(tr("Player List") + userCountInfo + (playerListDock->isWindow() ? tabText : QString())); } void TabGame::retranslateUi() { - QString tabText = - " | " + (replayManager->replay ? tr("Replay") : tr("Game")) + " #" + QString::number(gameMetaInfo->gameId()); + QString tabText = " | " + (replayManager->replay ? tr("Replay") : tr("Game")) + " #" + + QString::number(game->getGameMetaInfo()->gameId()); updatePlayerListDockTitle(); cardInfoDock->setWindowTitle(tr("Card Info") + (cardInfoDock->isWindow() ? tabText : QString())); @@ -302,7 +314,7 @@ void TabGame::retranslateUi() if (aGameInfo) aGameInfo->setText(tr("Game &information")); if (aConcede) { - if (gameState->isMainPlayerConceded()) { + if (game->getPlayerManager()->isMainPlayerConceded()) { aConcede->setText(tr("Un&concede")); } else { aConcede->setText(tr("&Concede")); @@ -349,9 +361,10 @@ void TabGame::retranslateUi() cardInfoFrameWidget->retranslateUi(); - QMapIterator i(gameState->getPlayers()); + QMapIterator i(game->getPlayerManager()->getPlayers()); + while (i.hasNext()) - i.next().value()->retranslateUi(); + i.next().value()->getGraphicsItem()->retranslateUi(); QMapIterator j(deckViewContainers); while (j.hasNext()) j.next().value()->playerDeckView->retranslateUi(); @@ -462,35 +475,34 @@ void TabGame::updateTimeElapsedLabel(const QString newTime) void TabGame::adminLockChanged(bool lock) { - bool v = !(gameState->isSpectator() && !gameMetaInfo->spectatorsCanChat() && lock); + bool v = !(game->getPlayerManager()->isSpectator() && !game->getGameMetaInfo()->spectatorsCanChat() && lock); sayLabel->setVisible(v); sayEdit->setVisible(v); } void TabGame::actGameInfo() { - DlgCreateGame dlg(gameMetaInfo->proto(), gameMetaInfo->getRoomGameTypes(), this); + DlgCreateGame dlg(game->getGameMetaInfo()->proto(), game->getGameMetaInfo()->getRoomGameTypes(), this); dlg.exec(); } void TabGame::actConcede() { - Player *player = gameState->getActiveLocalPlayer(); + Player *player = game->getPlayerManager()->getActiveLocalPlayer(game->getGameState()->getActivePlayer()); if (player == nullptr) return; if (!player->getConceded()) { if (QMessageBox::question(this, tr("Concede"), tr("Are you sure you want to concede this game?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return; - - emit playerConceded(); + player->setConceded(true); } else { if (QMessageBox::question(this, tr("Unconcede"), tr("You have already conceded. Do you want to return to this game?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return; - emit playerUnconceded(); + player->setConceded(false); } } @@ -501,8 +513,8 @@ void TabGame::actConcede() */ bool TabGame::leaveGame() { - if (!gameState->isGameClosed()) { - if (!gameState->isSpectator()) { + if (!game->getGameState()->isGameClosed()) { + if (!game->getPlayerManager()->isSpectator()) { tabSupervisor->setCurrentWidget(this); if (QMessageBox::question(this, tr("Leave game"), tr("Are you sure you want to leave this game?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) @@ -550,7 +562,7 @@ void TabGame::removePlayerFromAutoCompleteList(QString playerName) void TabGame::removeSpectator(int spectatorId, ServerInfo_User spectator) { Q_UNUSED(spectator); - QString playerName = "@" + gameState->getSpectatorName(spectatorId); + QString playerName = "@" + game->getPlayerManager()->getSpectatorName(spectatorId); removePlayerFromAutoCompleteList(playerName); } @@ -562,7 +574,7 @@ void TabGame::actPhaseAction() void TabGame::actNextPhase() { - int phase = gameState->getCurrentPhase(); + int phase = game->getGameState()->getCurrentPhase(); if (++phase >= phasesToolbar->phaseCount()) phase = 0; @@ -571,7 +583,7 @@ void TabGame::actNextPhase() void TabGame::actNextPhaseAction() { - int phase = gameState->getCurrentPhase() + 1; + int phase = game->getGameState()->getCurrentPhase() + 1; if (phase >= phasesToolbar->phaseCount()) { phase = 0; } @@ -587,10 +599,10 @@ void TabGame::actNextPhaseAction() void TabGame::actRemoveLocalArrows() { - QMapIterator playerIterator(gameState->getPlayers()); + QMapIterator playerIterator(game->getPlayerManager()->getPlayers()); while (playerIterator.hasNext()) { Player *player = playerIterator.next().value(); - if (!player->getLocal()) + if (!player->getPlayerInfo()->getLocal()) continue; QMapIterator arrowIterator(player->getArrows()); while (arrowIterator.hasNext()) { @@ -619,7 +631,7 @@ void TabGame::actCompleterChanged() void TabGame::notifyPlayerJoin(QString playerName) { if (trayIcon) { - QString gameId(QString::number(gameMetaInfo->gameId())); + QString gameId(QString::number(game->getGameMetaInfo()->gameId())); trayIcon->showMessage(tr("A player has joined game #%1").arg(gameId), tr("%1 has joined the game").arg(playerName)); } @@ -637,7 +649,7 @@ void TabGame::notifyPlayerKicked() void TabGame::processPlayerLeave(Player *leavingPlayer) { - QString playerName = "@" + leavingPlayer->getName(); + QString playerName = "@" + leavingPlayer->getPlayerInfo()->getName(); removePlayerFromAutoCompleteList(playerName); scene->removePlayer(leavingPlayer); @@ -645,35 +657,36 @@ void TabGame::processPlayerLeave(Player *leavingPlayer) Player *TabGame::addPlayer(Player *newPlayer) { - QString newPlayerName = "@" + newPlayer->getName(); + QString newPlayerName = "@" + newPlayer->getPlayerInfo()->getName(); addPlayerToAutoCompleteList(newPlayerName); scene->addPlayer(newPlayer); connect(newPlayer, &Player::newCardAdded, this, &TabGame::newCardAdded); - // TODO - // connect(newPlayer, &Player::cardMenuUpdated, this, &TabGame::setCardMenu); - messageLog->connectToPlayer(newPlayer); + connect(newPlayer->getPlayerMenu(), &PlayerMenu::cardMenuUpdated, this, &TabGame::setCardMenu); - if (gameState->isLocalPlayer(newPlayer->getId()) && !gameState->isSpectator()) { - addLocalPlayer(newPlayer, newPlayer->getId()); + messageLog->connectToPlayerEventHandler(newPlayer->getPlayerEventHandler()); + + if (game->getGameState()->getIsLocalGame() || + (game->getPlayerManager()->isLocalPlayer(newPlayer->getPlayerInfo()->getId()) && + !game->getPlayerManager()->isSpectator())) { + if (game->getGameState()->getIsLocalGame()) { + newPlayer->getPlayerInfo()->setLocal(true); + } + addLocalPlayer(newPlayer, newPlayer->getPlayerInfo()->getId()); } - gameMenu->insertMenu(playersSeparator, newPlayer->getPlayerMenu()); + gameMenu->insertMenu(playersSeparator, newPlayer->getPlayerMenu()->getPlayerMenu()); - createZoneForPlayer(newPlayer, newPlayer->getId()); + createZoneForPlayer(newPlayer, newPlayer->getPlayerInfo()->getId()); - // update menu text when player concedes so that "concede" gets updated to "unconcede" - connect(newPlayer, &Player::playerCountChanged, this, &TabGame::retranslateUi); - - emit playerAdded(newPlayer); return newPlayer; } void TabGame::addLocalPlayer(Player *newPlayer, int playerId) { - if (gameState->getClients().size() == 1) { - newPlayer->setShortcutsActive(); + if (game->getGameState()->getClients().size() == 1) { + newPlayer->getPlayerMenu()->setShortcutsActive(); } auto *deckView = new TabbedDeckViewContainer(playerId, this); @@ -682,7 +695,7 @@ void TabGame::addLocalPlayer(Player *newPlayer, int playerId) deckViewContainerLayout->addWidget(deckView); // auto load deck for player if that debug setting is enabled - QString deckPath = SettingsCache::instance().debug().getDeckPathForPlayer(newPlayer->getName()); + QString deckPath = SettingsCache::instance().debug().getDeckPathForPlayer(newPlayer->getPlayerInfo()->getName()); if (!deckPath.isEmpty()) { QTimer::singleShot(0, this, [deckView, deckPath] { deckView->playerDeckView->loadDeckFromFile(deckPath); @@ -748,12 +761,12 @@ void TabGame::processLocalPlayerReadyStateChanged(int playerId, bool ready) void TabGame::createZoneForPlayer(Player *newPlayer, int playerId) { - if (!gameState->getSpectators().contains(playerId)) { + if (!game->getPlayerManager()->getSpectators().contains(playerId)) { // Loop for each player, the idea is to have one assigned zone for each non-spectator player - for (int i = 1; i <= gameState->getPlayerCount(); ++i) { + for (int i = 1; i <= game->getPlayerManager()->getPlayerCount(); ++i) { bool aPlayerHasThisZone = false; - for (auto &player : gameState->getPlayers()) { + for (auto &player : game->getPlayerManager()->getPlayers()) { if (player->getZoneId() == i) { aPlayerHasThisZone = true; break; @@ -767,22 +780,9 @@ void TabGame::createZoneForPlayer(Player *newPlayer, int playerId) } } -AbstractClient *TabGame::getClientForPlayer(int playerId) const -{ - if (gameState->getClients().size() > 1) { - if (playerId == -1) - playerId = gameState->getActiveLocalPlayer()->getId(); - - return gameState->getClients().at(playerId); - } else if (gameState->getClients().isEmpty()) - return nullptr; - else - return gameState->getClients().first(); -} - void TabGame::startGame(bool _resuming) { - gameState->setCurrentPhase(-1); + game->getGameState()->setCurrentPhase(-1); QMapIterator i(deckViewContainers); while (i.hasNext()) { @@ -795,13 +795,13 @@ void TabGame::startGame(bool _resuming) mainWidget->setCurrentWidget(gamePlayAreaWidget); if (!_resuming) { - QMapIterator playerIterator(gameState->getPlayers()); + QMapIterator playerIterator(game->getPlayerManager()->getPlayers()); while (playerIterator.hasNext()) playerIterator.next().value()->setGameStarted(); } - playerListWidget->setGameStarted(true, gameState->isResuming()); - gameMetaInfo->setStarted(true); + playerListWidget->setGameStarted(true, game->getGameState()->isResuming()); + game->getGameMetaInfo()->setStarted(true); static_cast(gameView->scene())->rearrange(); } @@ -829,25 +829,27 @@ void TabGame::closeGame() Player *TabGame::setActivePlayer(int id) { - Player *player = gameState->getPlayer(id); + Player *player = game->getPlayerManager()->getPlayer(id); if (!player) return nullptr; playerListWidget->setActivePlayer(id); - QMapIterator i(gameState->getPlayers()); + QMapIterator i(game->getPlayerManager()->getPlayers()); while (i.hasNext()) { i.next(); if (i.value() == player) { i.value()->setActive(true); - if (gameState->getClients().size() > 1) - i.value()->setShortcutsActive(); + if (game->getGameState()->getClients().size() > 1) { + i.value()->getPlayerMenu()->setShortcutsActive(); + } } else { i.value()->setActive(false); - if (gameState->getClients().size() > 1) - i.value()->setShortcutsInactive(); + if (game->getGameState()->getClients().size() > 1) { + i.value()->getPlayerMenu()->setShortcutsInactive(); + } } } - gameState->setCurrentPhase(-1); + game->getGameState()->setCurrentPhase(-1); emitUserEvent(); return player; } @@ -866,19 +868,6 @@ void TabGame::newCardAdded(AbstractCardItem *card) connect(card, &AbstractCardItem::cardShiftClicked, this, &TabGame::linkCardToChat); } -CardItem *TabGame::getCard(int playerId, const QString &zoneName, int cardId) const -{ - Player *player = gameState->getPlayer(playerId); - if (!player) - return nullptr; - - CardZone *zone = player->getZones().value(zoneName, 0); - if (!zone) - return nullptr; - - return zone->getCard(cardId); -} - QString TabGame::getTabText() const { QString gameTypeInfo; @@ -888,8 +877,8 @@ QString TabGame::getTabText() const gameTypeInfo.append("..."); } - QString gameDesc(gameMetaInfo->description()); - QString gameId(QString::number(gameMetaInfo->gameId())); + QString gameDesc(game->getGameMetaInfo()->description()); + QString gameId(QString::number(game->getGameMetaInfo()->gameId())); QString tabText; if (replayManager->replay) @@ -909,11 +898,6 @@ QString TabGame::getTabText() const return tabText; } -void TabGame::setActiveCard(CardItem *card) -{ - activeCard = card; -} - /** * @param menu The menu to set. Pass in nullptr to set the menu to empty. */ @@ -934,17 +918,18 @@ void TabGame::createMenuItems() { aNextPhase = new QAction(this); connect(aNextPhase, &QAction::triggered, this, &TabGame::actNextPhase); - connect(this, &TabGame::phaseChanged, gameEventHandler, &GameEventHandler::handleActivePhaseChanged); + connect(this, &TabGame::phaseChanged, game->getGameEventHandler(), &GameEventHandler::handleActivePhaseChanged); aNextPhaseAction = new QAction(this); connect(aNextPhaseAction, &QAction::triggered, this, &TabGame::actNextPhaseAction); - connect(this, &TabGame::turnAdvanced, gameEventHandler, &GameEventHandler::handleNextTurn); + connect(this, &TabGame::turnAdvanced, game->getGameEventHandler(), &GameEventHandler::handleNextTurn); aNextTurn = new QAction(this); - connect(aNextTurn, &QAction::triggered, gameEventHandler, &GameEventHandler::handleNextTurn); + connect(aNextTurn, &QAction::triggered, game->getGameEventHandler(), &GameEventHandler::handleNextTurn); aReverseTurn = new QAction(this); - connect(aReverseTurn, &QAction::triggered, gameEventHandler, &GameEventHandler::handleReverseTurn); + connect(aReverseTurn, &QAction::triggered, game->getGameEventHandler(), &GameEventHandler::handleReverseTurn); aRemoveLocalArrows = new QAction(this); connect(aRemoveLocalArrows, &QAction::triggered, this, &TabGame::actRemoveLocalArrows); - connect(this, &TabGame::arrowDeletionRequested, gameEventHandler, &GameEventHandler::handleArrowDeletion); + connect(this, &TabGame::arrowDeletionRequested, game->getGameEventHandler(), + &GameEventHandler::handleArrowDeletion); aRotateViewCW = new QAction(this); connect(aRotateViewCW, &QAction::triggered, this, &TabGame::actRotateViewCW); aRotateViewCCW = new QAction(this); @@ -953,8 +938,10 @@ void TabGame::createMenuItems() connect(aGameInfo, &QAction::triggered, this, &TabGame::actGameInfo); aConcede = new QAction(this); connect(aConcede, &QAction::triggered, this, &TabGame::actConcede); - connect(this, &TabGame::playerConceded, gameEventHandler, &GameEventHandler::handlePlayerConceded); - connect(this, &TabGame::playerUnconceded, gameEventHandler, &GameEventHandler::handlePlayerUnconceded); + connect(game->getPlayerManager(), &PlayerManager::activeLocalPlayerConceded, game->getGameEventHandler(), + &GameEventHandler::handleActiveLocalPlayerConceded); + connect(game->getPlayerManager(), &PlayerManager::activeLocalPlayerUnconceded, game->getGameEventHandler(), + &GameEventHandler::handleActiveLocalPlayerUnconceded); aLeaveGame = new QAction(this); connect(aLeaveGame, &QAction::triggered, this, &TabGame::closeRequest); aFocusChat = new QAction(this); @@ -1189,9 +1176,11 @@ void TabGame::createPlayAreaWidget(bool bReplay) { phasesToolbar = new PhasesToolbar; if (!bReplay) - connect(phasesToolbar, &PhasesToolbar::sendGameCommand, gameEventHandler, + connect(phasesToolbar, &PhasesToolbar::sendGameCommand, game->getGameEventHandler(), qOverload(&GameEventHandler::sendGameCommand)); scene = new GameScene(phasesToolbar, this); + connect(game->getPlayerManager(), &PlayerManager::playerConceded, scene, &GameScene::rearrange); + connect(game->getPlayerManager(), &PlayerManager::playerCountChanged, scene, &GameScene::rearrange); gameView = new GameView(scene); auto gamePlayAreaVBox = new QVBoxLayout; @@ -1262,9 +1251,9 @@ void TabGame::createCardInfoDock(bool bReplay) void TabGame::createPlayerListDock(bool bReplay) { if (bReplay) { - playerListWidget = new PlayerListWidget(nullptr, nullptr, this); + playerListWidget = new PlayerListWidget(nullptr, nullptr, game); } else { - playerListWidget = new PlayerListWidget(tabSupervisor, gameState->getClients().first(), this); + playerListWidget = new PlayerListWidget(tabSupervisor, game->getGameState()->getClients().first(), game); connect(playerListWidget, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool))); } @@ -1290,14 +1279,14 @@ void TabGame::createMessageDock(bool bReplay) if (!bReplay) { timeElapsedLabel = new QLabel; timeElapsedLabel->setAlignment(Qt::AlignCenter); - connect(gameState, &GameState::updateTimeElapsedLabel, this, &TabGame::updateTimeElapsedLabel); - gameState->startGameTimer(); + connect(game->getGameState(), &GameState::updateTimeElapsedLabel, this, &TabGame::updateTimeElapsedLabel); + game->getGameState()->startGameTimer(); messageLogLayout->addWidget(timeElapsedLabel); } // message log - messageLog = new MessageLogWidget(tabSupervisor, this); + messageLog = new MessageLogWidget(tabSupervisor, game); connect(messageLog, &MessageLogWidget::cardNameHovered, cardInfoFrameWidget, qOverload(&CardInfoFrameWidget::setCard)); connect(messageLog, &MessageLogWidget::showCardInfoPopup, this, &TabGame::showCardInfoPopup); @@ -1318,7 +1307,7 @@ void TabGame::createMessageDock(bool bReplay) sayEdit = new LineEditCompleter; sayEdit->setMaxLength(MAX_TEXT_LENGTH); sayLabel->setBuddy(sayEdit); - connect(this, &TabGame::chatMessageSent, gameEventHandler, &GameEventHandler::handleChatMessageSent); + connect(this, &TabGame::chatMessageSent, game->getGameEventHandler(), &GameEventHandler::handleChatMessageSent); completer = new QCompleter(autocompleteUserList, sayEdit); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setMaxVisibleItems(5); @@ -1327,14 +1316,14 @@ void TabGame::createMessageDock(bool bReplay) sayEdit->setCompleter(completer); actCompleterChanged(); - if (gameState->isSpectator()) { + if (game->getPlayerManager()->isSpectator()) { /* Spectators can only talk if: * (a) the game creator allows it * (b) the spectator is a moderator/administrator * (c) the spectator is a judge */ - bool isModOrJudge = !tabSupervisor->getAdminLocked() || gameState->isJudge(); - if (!isModOrJudge && !gameMetaInfo->spectatorsCanChat()) { + bool isModOrJudge = !tabSupervisor->getAdminLocked() || game->getPlayerManager()->isJudge(); + if (!isModOrJudge && !game->getGameMetaInfo()->spectatorsCanChat()) { sayLabel->hide(); sayEdit->hide(); } diff --git a/cockatrice/src/client/tabs/tab_game.h b/cockatrice/src/client/tabs/tab_game.h index a336ebd4a..1ebbef9d2 100644 --- a/cockatrice/src/client/tabs/tab_game.h +++ b/cockatrice/src/client/tabs/tab_game.h @@ -2,10 +2,9 @@ #define TAB_GAME_H #include "../../client/tearoff_menu.h" -#include "../../game/game_event_handler.h" -#include "../../game/game_meta_info.h" -#include "../../game/game_state.h" +#include "../../game/abstract_game.h" #include "../../game/player/player.h" +#include "../../server/message_log_widget.h" #include "../replay_manager.h" #include "../ui/widgets/visual_deck_storage/visual_deck_storage_widget.h" #include "pb/event_leave.pb.h" @@ -52,11 +51,8 @@ class TabGame : public Tab { Q_OBJECT private: - GameMetaInfo *gameMetaInfo; - GameState *gameState; - GameEventHandler *gameEventHandler; + AbstractGame *game; const UserListProxy *userListProxy; - CardItem *activeCard; ReplayManager *replayManager; QStringList gameTypes; QCompleter *completer; @@ -114,16 +110,12 @@ private: void createReplayDock(GameReplay *replay); signals: void gameClosing(TabGame *tab); - void playerAdded(Player *player); - void playerRemoved(Player *player); void containerProcessingStarted(const GameEventContext &context); void containerProcessingDone(); void openMessageDialog(const QString &userName, bool focus); void openDeckEditor(const DeckLoader *deck); void notIdle(); - void playerConceded(); - void playerUnconceded(); void phaseChanged(int phase); void gameLeft(); void chatMessageSent(QString chatMessage); @@ -173,41 +165,22 @@ public: const Event_GameJoined &event, const QMap &_roomGameTypes); void connectToGameState(); + void connectToPlayerManager(); void connectToGameEventHandler(); void connectMessageLogToGameEventHandler(); + void connectMessageLogToPlayerHandler(); void connectPlayerListToGameEventHandler(); - void loadReplay(GameReplay *replay); TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay); ~TabGame() override; void retranslateUi() override; void updatePlayerListDockTitle(); bool closeRequest() override; - GameMetaInfo *getGameMetaInfo() - { - return gameMetaInfo; - } - - GameState *getGameState() const - { - return gameState; - } - - GameEventHandler *getGameEventHandler() const - { - return gameEventHandler; - } - - CardItem *getCard(int playerId, const QString &zoneName, int cardId) const; - QString getTabText() const override; - AbstractClient *getClientForPlayer(int playerId) const; - - void setActiveCard(CardItem *card); - CardItem *getActiveCard() const + AbstractGame *getGame() const { - return activeCard; + return game; } public slots: diff --git a/cockatrice/src/client/tabs/tab_supervisor.cpp b/cockatrice/src/client/tabs/tab_supervisor.cpp index 2b7215375..9e834071c 100644 --- a/cockatrice/src/client/tabs/tab_supervisor.cpp +++ b/cockatrice/src/client/tabs/tab_supervisor.cpp @@ -707,7 +707,7 @@ void TabSupervisor::gameLeft(TabGame *tab) if (tab == currentWidget()) emit setMenu(); - gameTabs.remove(tab->getGameMetaInfo()->gameId()); + gameTabs.remove(tab->getGame()->getGameMetaInfo()->gameId()); removeTab(indexOf(tab)); if (!localClients.isEmpty()) @@ -916,7 +916,8 @@ void TabSupervisor::processGameEventContainer(const GameEventContainer &cont) { TabGame *tab = gameTabs.value(cont.game_id()); if (tab) - tab->getGameEventHandler()->processGameEventContainer(cont, qobject_cast(sender()), {}); + tab->getGame()->getGameEventHandler()->processGameEventContainer(cont, qobject_cast(sender()), + {}); else qCInfo(TabSupervisorLog) << "gameEvent: invalid gameId" << cont.game_id(); } diff --git a/cockatrice/src/game/abstract_game.cpp b/cockatrice/src/game/abstract_game.cpp new file mode 100644 index 000000000..505cd5fde --- /dev/null +++ b/cockatrice/src/game/abstract_game.cpp @@ -0,0 +1,54 @@ +#include "abstract_game.h" + +#include "player/player.h" + +AbstractGame::AbstractGame(TabGame *_tab) : tab(_tab) +{ + gameMetaInfo = new GameMetaInfo(this); + gameEventHandler = new GameEventHandler(this); + + activeCard = nullptr; +} + +bool AbstractGame::isHost() const +{ + return gameState->getHostId() == playerManager->getLocalPlayerId(); +} + +AbstractClient *AbstractGame::getClientForPlayer(int playerId) const +{ + if (gameState->getClients().size() > 1) { + if (playerId == -1) { + playerId = playerManager->getActiveLocalPlayer(gameState->getActivePlayer())->getPlayerInfo()->getId(); + } + + return gameState->getClients().at(playerId); + } else if (gameState->getClients().isEmpty()) + return nullptr; + else + return gameState->getClients().first(); +} + +void AbstractGame::loadReplay(GameReplay *replay) +{ + gameMetaInfo->setFromProto(replay->game_info()); + gameMetaInfo->setSpectatorsOmniscient(true); +} + +void AbstractGame::setActiveCard(CardItem *card) +{ + activeCard = card; +} + +CardItem *AbstractGame::getCard(int playerId, const QString &zoneName, int cardId) const +{ + Player *player = playerManager->getPlayer(playerId); + if (!player) + return nullptr; + + CardZoneLogic *zone = player->getZones().value(zoneName, 0); + if (!zone) + return nullptr; + + return zone->getCard(cardId); +} \ No newline at end of file diff --git a/cockatrice/src/game/abstract_game.h b/cockatrice/src/game/abstract_game.h new file mode 100644 index 000000000..386b2ee8e --- /dev/null +++ b/cockatrice/src/game/abstract_game.h @@ -0,0 +1,68 @@ +#ifndef COCKATRICE_ABSTRACT_GAME_H +#define COCKATRICE_ABSTRACT_GAME_H + +#include "../../../common/pb/game_replay.pb.h" +#include "game_event_handler.h" +#include "game_meta_info.h" +#include "game_state.h" +#include "player/player_manager.h" + +#include + +class CardItem; +class TabGame; +class AbstractGame : public QObject +{ + Q_OBJECT + +public: + explicit AbstractGame(TabGame *tab); + + TabGame *tab; + GameMetaInfo *gameMetaInfo; + GameState *gameState; + GameEventHandler *gameEventHandler; + PlayerManager *playerManager; + CardItem *activeCard; + + TabGame *getTab() const + { + return tab; + } + + GameMetaInfo *getGameMetaInfo() + { + return gameMetaInfo; + } + + GameState *getGameState() const + { + return gameState; + } + + GameEventHandler *getGameEventHandler() const + { + return gameEventHandler; + } + + PlayerManager *getPlayerManager() const + { + return playerManager; + } + + bool isHost() const; + + AbstractClient *getClientForPlayer(int playerId) const; + + void loadReplay(GameReplay *replay); + + CardItem *getCard(int playerId, const QString &zoneName, int cardId) const; + + void setActiveCard(CardItem *card); + CardItem *getActiveCard() const + { + return activeCard; + } +}; + +#endif // COCKATRICE_ABSTRACT_GAME_H diff --git a/cockatrice/src/game/board/abstract_card_item.h b/cockatrice/src/game/board/abstract_card_item.h index 72f641345..50b0ee2c9 100644 --- a/cockatrice/src/game/board/abstract_card_item.h +++ b/cockatrice/src/game/board/abstract_card_item.h @@ -4,6 +4,7 @@ #include "../cards/exact_card.h" #include "arrow_target.h" #include "card_ref.h" +#include "graphics_item_type.h" class Player; diff --git a/cockatrice/src/game/board/abstract_counter.cpp b/cockatrice/src/game/board/abstract_counter.cpp index b98647cdf..be5398029 100644 --- a/cockatrice/src/game/board/abstract_counter.cpp +++ b/cockatrice/src/game/board/abstract_counter.cpp @@ -1,5 +1,6 @@ #include "abstract_counter.h" +#include "../../client/tabs/tab_game.h" #include "../../client/translate_counter_name.h" #include "../../settings/cache_settings.h" #include "../player/player.h" @@ -22,17 +23,16 @@ AbstractCounter::AbstractCounter(Player *_player, bool _shownInCounterArea, int _value, bool _useNameForShortcut, - QGraphicsItem *parent, - QWidget *_game) + QGraphicsItem *parent) : QGraphicsItem(parent), player(_player), id(_id), name(_name), value(_value), useNameForShortcut(_useNameForShortcut), hovered(false), aDec(nullptr), aInc(nullptr), dialogSemaphore(false), - deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea), game(_game) + deleteAfterDialog(false), shownInCounterArea(_shownInCounterArea) { setAcceptHoverEvents(true); shortcutActive = false; - if (player->getLocalOrJudge()) { + if (player->getPlayerInfo()->getLocalOrJudge()) { QString displayName = TranslateCounterName::getDisplayName(_name); menu = new TearOffMenu(displayName); aSet = new QAction(this); @@ -85,7 +85,7 @@ void AbstractCounter::retranslateUi() void AbstractCounter::setShortcutsActive() { - if (!player->getLocal()) { + if (!player->getPlayerInfo()->getLocal()) { return; } ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); @@ -127,7 +127,7 @@ void AbstractCounter::setValue(int _value) void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (isUnderMouse() && player->getLocalOrJudge()) { + if (isUnderMouse() && player->getPlayerInfo()->getLocalOrJudge()) { if (event->button() == Qt::MiddleButton || (QApplication::keyboardModifiers() & Qt::ShiftModifier)) { if (menu) menu->exec(event->screenPos()); @@ -136,13 +136,13 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event) Command_IncCounter cmd; cmd.set_counter_id(id); cmd.set_delta(1); - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); event->accept(); } else if (event->button() == Qt::RightButton) { Command_IncCounter cmd; cmd.set_counter_id(id); cmd.set_delta(-1); - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); event->accept(); } } else @@ -167,13 +167,13 @@ void AbstractCounter::incrementCounter() Command_IncCounter cmd; cmd.set_counter_id(id); cmd.set_delta(delta); - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); } void AbstractCounter::setCounter() { dialogSemaphore = true; - AbstractCounterDialog dialog(name, QString::number(value), game); + AbstractCounterDialog dialog(name, QString::number(value), player->getGame()->getTab()); const int ok = dialog.exec(); if (deleteAfterDialog) { @@ -191,7 +191,7 @@ void AbstractCounter::setCounter() Command_SetCounter cmd; cmd.set_counter_id(id); cmd.set_value(newValue); - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); } AbstractCounterDialog::AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent) diff --git a/cockatrice/src/game/board/abstract_counter.h b/cockatrice/src/game/board/abstract_counter.h index 8f2ee2691..4c9ae064a 100644 --- a/cockatrice/src/game/board/abstract_counter.h +++ b/cockatrice/src/game/board/abstract_counter.h @@ -34,7 +34,6 @@ private: bool dialogSemaphore, deleteAfterDialog; bool shownInCounterArea; bool shortcutActive; - QWidget *game; private slots: void refreshShortcuts(); @@ -48,8 +47,7 @@ public: bool _shownInCounterArea, int _value, bool _useNameForShortcut = false, - QGraphicsItem *parent = nullptr, - QWidget *game = nullptr); + QGraphicsItem *parent = nullptr); ~AbstractCounter() override; void retranslateUi(); diff --git a/cockatrice/src/game/board/abstract_graphics_item.h b/cockatrice/src/game/board/abstract_graphics_item.h index 738be17d8..eaa0b881a 100644 --- a/cockatrice/src/game/board/abstract_graphics_item.h +++ b/cockatrice/src/game/board/abstract_graphics_item.h @@ -3,28 +3,18 @@ #include -enum GraphicsItemType -{ - typeCard = QGraphicsItem::UserType + 1, - typeCardDrag = QGraphicsItem::UserType + 2, - typeZone = QGraphicsItem::UserType + 3, - typePlayerTarget = QGraphicsItem::UserType + 4, - typeDeckViewCardContainer = QGraphicsItem::UserType + 5, - typeOther = QGraphicsItem::UserType + 6 -}; - /** * Parent class of all objects that appear in a game. */ -class AbstractGraphicsItem : public QObject, public QGraphicsItem +class AbstractGraphicsItem : public QGraphicsObject { Q_OBJECT - Q_INTERFACES(QGraphicsItem) + protected: void paintNumberEllipse(int number, int radius, const QColor &color, int position, int count, QPainter *painter); public: - explicit AbstractGraphicsItem(QGraphicsItem *parent = nullptr) : QGraphicsItem(parent) + explicit AbstractGraphicsItem(QGraphicsItem *parent = nullptr) : QGraphicsObject(parent) { } }; diff --git a/cockatrice/src/game/board/arrow_item.cpp b/cockatrice/src/game/board/arrow_item.cpp index ff44a7ae6..1763d26d7 100644 --- a/cockatrice/src/game/board/arrow_item.cpp +++ b/cockatrice/src/game/board/arrow_item.cpp @@ -130,7 +130,7 @@ void ArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (!player->getLocal()) { + if (!player->getPlayerInfo()->getLocal()) { event->ignore(); return; } @@ -147,7 +147,7 @@ void ArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event) if (event->button() == Qt::RightButton) { Command_DeleteArrow cmd; cmd.set_arrow_id(id); - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); } } @@ -214,7 +214,7 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) return; if (targetItem && (targetItem != startItem)) { - CardZone *startZone = static_cast(startItem)->getZone(); + CardZoneLogic *startZone = static_cast(startItem)->getZone(); // For now, we can safely assume that the start item is always a card. // The target item can be a player as well. CardItem *startCard = qgraphicsitem_cast(startItem); @@ -222,18 +222,18 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Command_CreateArrow cmd; cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(color)); - cmd.set_start_player_id(startZone->getPlayer()->getId()); + cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_zone(startZone->getName().toStdString()); cmd.set_start_card_id(startCard->getId()); if (targetCard) { - CardZone *targetZone = targetCard->getZone(); - cmd.set_target_player_id(targetZone->getPlayer()->getId()); + CardZoneLogic *targetZone = targetCard->getZone(); + cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_target_zone(targetZone->getName().toStdString()); cmd.set_target_card_id(targetCard->getId()); } else { PlayerTarget *targetPlayer = qgraphicsitem_cast(targetItem); - cmd.set_target_player_id(targetPlayer->getOwner()->getId()); + cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId()); } if (startZone->getName().compare("hand") == 0) { startCard->playCard(false); @@ -245,7 +245,7 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) else cmd.set_start_zone(SettingsCache::instance().getPlayToStack() ? "stack" : "table"); } - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); } delArrow(); @@ -312,22 +312,22 @@ void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCar return; } - CardZone *startZone = startCard->getZone(); - CardZone *targetZone = targetCard->getZone(); + CardZoneLogic *startZone = startCard->getZone(); + CardZoneLogic *targetZone = targetCard->getZone(); // move card onto table first if attaching from some other zone if (startZone->getName() != "table") { - player->playCardToTable(startCard, false); + player->getPlayerActions()->playCardToTable(startCard, false); } Command_AttachCard cmd; cmd.set_start_zone("table"); cmd.set_card_id(startCard->getId()); - cmd.set_target_player_id(targetZone->getPlayer()->getId()); + cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_target_zone(targetZone->getName().toStdString()); cmd.set_target_card_id(targetCard->getId()); - player->sendGameCommand(cmd); + player->getPlayerActions()->sendGameCommand(cmd); } void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) diff --git a/cockatrice/src/game/board/card_drag_item.cpp b/cockatrice/src/game/board/card_drag_item.cpp index ab4431958..9eadadeda 100644 --- a/cockatrice/src/game/board/card_drag_item.cpp +++ b/cockatrice/src/game/board/card_drag_item.cpp @@ -109,15 +109,16 @@ void CardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) sc->removeItem(this); QList dragItemList; - CardZone *startZone = static_cast(item)->getZone(); - if (currentZone && !(static_cast(item)->getAttachedTo() && (startZone == currentZone))) { + CardZoneLogic *startZone = static_cast(item)->getZone(); + if (currentZone && !(static_cast(item)->getAttachedTo() && (startZone == currentZone->getLogic()))) { if (!occupied) { dragItemList.append(this); } for (int i = 0; i < childDrags.size(); i++) { CardDragItem *c = static_cast(childDrags[i]); - if (!occupied && !(static_cast(c->item)->getAttachedTo() && (startZone == currentZone)) && + if (!occupied && + !(static_cast(c->item)->getAttachedTo() && (startZone == currentZone->getLogic())) && !c->occupied) { dragItemList.append(c); } diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index 2b40acd25..2b025bf14 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -7,6 +7,7 @@ #include "../game_scene.h" #include "../player/player.h" #include "../zones/card_zone.h" +#include "../zones/logic/view_zone_logic.h" #include "../zones/table_zone.h" #include "../zones/view_zone.h" #include "arrow_item.h" @@ -18,7 +19,7 @@ #include #include -CardItem::CardItem(Player *_owner, QGraphicsItem *parent, const CardRef &cardRef, int _cardid, CardZone *_zone) +CardItem::CardItem(Player *_owner, QGraphicsItem *parent, const CardRef &cardRef, int _cardid, CardZoneLogic *_zone) : AbstractCardItem(parent, cardRef, _owner, _cardid), zone(_zone), attacking(false), destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr) { @@ -34,7 +35,7 @@ void CardItem::prepareDelete() { if (owner != nullptr) { if (owner->getGame()->getActiveCard() == this) { - owner->updateCardMenu(nullptr); + owner->getPlayerMenu()->updateCardMenu(nullptr); owner->getGame()->setActiveCard(nullptr); } owner = nullptr; @@ -59,7 +60,7 @@ void CardItem::deleteLater() AbstractCardItem::deleteLater(); } -void CardItem::setZone(CardZone *_zone) +void CardItem::setZone(CardZoneLogic *_zone) { zone = _zone; } @@ -184,13 +185,13 @@ void CardItem::setAttachedTo(CardItem *_attachedTo) gridPoint.setX(-1); attachedTo = _attachedTo; if (attachedTo != nullptr) { - setParentItem(attachedTo->getZone()); + emit attachedTo->zone->cardAdded(this); attachedTo->addAttachedCard(this); if (zone != attachedTo->getZone()) { attachedTo->getZone()->reorganizeCards(); } } else { - setParentItem(zone); + emit zone->cardAdded(this); } if (zone != nullptr) { @@ -259,10 +260,14 @@ void CardItem::deleteDragItem() void CardItem::drawArrow(const QColor &arrowColor) { - if (static_cast(owner->parent())->getGameState()->isSpectator()) + if (static_cast(owner->parent())->getGame()->getPlayerManager()->isSpectator()) return; - Player *arrowOwner = static_cast(owner->parent())->getGameState()->getActiveLocalPlayer(); + Player *arrowOwner = static_cast(owner->parent()) + ->getGame() + ->getPlayerManager() + ->getActiveLocalPlayer( + static_cast(owner->parent())->getGame()->getGameState()->getActivePlayer()); ArrowDragItem *arrow = new ArrowDragItem(arrowOwner, this, arrowColor); scene()->addItem(arrow); arrow->grabMouse(); @@ -282,7 +287,7 @@ void CardItem::drawArrow(const QColor &arrowColor) void CardItem::drawAttachArrow() { - if (static_cast(owner->parent())->getGameState()->isSpectator()) + if (static_cast(owner->parent())->getGame()->getPlayerManager()->isSpectator()) return; auto *arrow = new ArrowAttachItem(this); @@ -322,10 +327,10 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() < 2 * QApplication::startDragDistance()) return; - if (const ZoneViewZone *view = qobject_cast(zone)) { + if (const ZoneViewZoneLogic *view = qobject_cast(zone)) { if (view->getRevealZone() && !view->getWriteableRevealZone()) return; - } else if (!owner->getLocalOrJudge()) + } else if (!owner->getPlayerInfo()->getLocalOrJudge()) return; bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); @@ -358,17 +363,18 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void CardItem::playCard(bool faceDown) { // Do nothing if the card belongs to another player - if (!owner->getLocalOrJudge()) + if (!owner->getPlayerInfo()->getLocalOrJudge()) return; - TableZone *tz = qobject_cast(zone); + TableZoneLogic *tz = qobject_cast(zone); if (tz) - tz->toggleTapped(); + emit tz->toggleTapped(); else { if (SettingsCache::instance().getClickPlaysAllSelected()) { - faceDown ? zone->getPlayer()->actPlayFacedown() : zone->getPlayer()->actPlay(); + faceDown ? zone->getPlayer()->getPlayerActions()->actPlayFacedown() + : zone->getPlayer()->getPlayerActions()->actPlay(); } else { - zone->getPlayer()->playCard(this, faceDown); + zone->getPlayer()->getPlayerActions()->playCard(this, faceDown); } } } @@ -377,9 +383,9 @@ void CardItem::playCard(bool faceDown) * @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone * is nullptr. */ -static bool isUnwritableRevealZone(CardZone *zone) +static bool isUnwritableRevealZone(CardZoneLogic *zone) { - if (auto *view = qobject_cast(zone)) { + if (auto *view = qobject_cast(zone)) { return view->getRevealZone() && !view->getWriteableRevealZone(); } return false; @@ -395,7 +401,7 @@ void CardItem::handleClickedToPlay(bool shiftHeld) { if (isUnwritableRevealZone(zone)) { if (SettingsCache::instance().getClickPlaysAllSelected()) { - zone->getPlayer()->actHide(); + zone->getPlayer()->getPlayerActions()->actHide(); } else { zone->removeCard(this); } @@ -410,7 +416,7 @@ void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (owner != nullptr) { owner->getGame()->setActiveCard(this); - if (QMenu *cardMenu = owner->updateCardMenu(this)) { + if (QMenu *cardMenu = owner->getPlayerMenu()->updateCardMenu(this)) { cardMenu->popup(event->screenPos()); return; } @@ -467,10 +473,11 @@ QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value) if ((change == ItemSelectedHasChanged) && owner != nullptr) { if (value == true) { owner->getGame()->setActiveCard(this); - owner->updateCardMenu(this); - } else if (owner->scene()->selectedItems().isEmpty()) { + owner->getPlayerMenu()->updateCardMenu(this); + } else if (owner->getGameScene()->selectedItems().isEmpty()) { + owner->getGame()->setActiveCard(nullptr); - owner->updateCardMenu(nullptr); + owner->getPlayerMenu()->updateCardMenu(nullptr); } } return AbstractCardItem::itemChange(change, value); diff --git a/cockatrice/src/game/board/card_item.h b/cockatrice/src/game/board/card_item.h index 569079092..acb2e5e04 100644 --- a/cockatrice/src/game/board/card_item.h +++ b/cockatrice/src/game/board/card_item.h @@ -1,6 +1,7 @@ #ifndef CARDITEM_H #define CARDITEM_H +#include "../zones/logic/card_zone_logic.h" #include "abstract_card_item.h" #include "server_card.h" @@ -21,7 +22,7 @@ class CardItem : public AbstractCardItem { Q_OBJECT private: - CardZone *zone; + CardZoneLogic *zone; bool attacking; QMap counters; QString annotation; @@ -51,14 +52,14 @@ public: QGraphicsItem *parent = nullptr, const CardRef &cardRef = {}, int _cardid = -1, - CardZone *_zone = nullptr); + CardZoneLogic *_zone = nullptr); void retranslateUi(); - CardZone *getZone() const + CardZoneLogic *getZone() const { return zone; } - void setZone(CardZone *_zone); + void setZone(CardZoneLogic *_zone); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; QPoint getGridPoint() const { diff --git a/cockatrice/src/game/board/counter_general.cpp b/cockatrice/src/game/board/counter_general.cpp index 3089dc3f1..6bd94cbee 100644 --- a/cockatrice/src/game/board/counter_general.cpp +++ b/cockatrice/src/game/board/counter_general.cpp @@ -12,10 +12,8 @@ GeneralCounter::GeneralCounter(Player *_player, int _radius, int _value, bool useNameForShortcut, - QGraphicsItem *parent, - QWidget *game) - : AbstractCounter(_player, _id, _name, true, _value, useNameForShortcut, parent, game), color(_color), - radius(_radius) + QGraphicsItem *parent) + : AbstractCounter(_player, _id, _name, true, _value, useNameForShortcut, parent), color(_color), radius(_radius) { setCacheMode(DeviceCoordinateCache); } diff --git a/cockatrice/src/game/board/counter_general.h b/cockatrice/src/game/board/counter_general.h index 0bee9c824..dcf93c72c 100644 --- a/cockatrice/src/game/board/counter_general.h +++ b/cockatrice/src/game/board/counter_general.h @@ -18,8 +18,7 @@ public: int _radius, int _value, bool useNameForShortcut = false, - QGraphicsItem *parent = nullptr, - QWidget *game = nullptr); + QGraphicsItem *parent = nullptr); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; }; diff --git a/cockatrice/src/game/board/graphics_item_type.h b/cockatrice/src/game/board/graphics_item_type.h new file mode 100644 index 000000000..eb81cc3aa --- /dev/null +++ b/cockatrice/src/game/board/graphics_item_type.h @@ -0,0 +1,16 @@ +#ifndef COCKATRICE_GRAPHICS_ITEM_TYPE_H +#define COCKATRICE_GRAPHICS_ITEM_TYPE_H + +#include + +enum GraphicsItemType +{ + typeCard = QGraphicsItem::UserType + 1, + typeCardDrag = QGraphicsItem::UserType + 2, + typeZone = QGraphicsItem::UserType + 3, + typePlayerTarget = QGraphicsItem::UserType + 4, + typeDeckViewCardContainer = QGraphicsItem::UserType + 5, + typeOther = QGraphicsItem::UserType + 6 +}; + +#endif // COCKATRICE_GRAPHICS_ITEM_TYPE_H diff --git a/cockatrice/src/game/deckview/deck_view_container.cpp b/cockatrice/src/game/deckview/deck_view_container.cpp index ccaeee596..f09ecb638 100644 --- a/cockatrice/src/game/deckview/deck_view_container.cpp +++ b/cockatrice/src/game/deckview/deck_view_container.cpp @@ -157,7 +157,7 @@ void DeckViewContainer::switchToDeckSelectView() deckViewLayout->update(); setVisibility(loadLocalButton, true); - setVisibility(loadRemoteButton, !parentGame->getGameState()->getIsLocalGame()); + setVisibility(loadRemoteButton, !parentGame->getGame()->getGameState()->getIsLocalGame()); setVisibility(loadFromClipboardButton, true); setVisibility(loadFromWebsiteButton, true); setVisibility(unloadDeckButton, false); @@ -190,7 +190,7 @@ void DeckViewContainer::switchToDeckLoadedView() setVisibility(readyStartButton, true); setVisibility(sideboardLockButton, true); - if (parentGame->getGameState()->isHost()) { + if (parentGame->getGame()->isHost()) { setVisibility(forceStartGameButton, true); } } @@ -287,20 +287,20 @@ void DeckViewContainer::loadDeckFromDeckLoader(const DeckLoader *deck) Command_DeckSelect cmd; cmd.set_deck(deckString.toStdString()); - PendingCommand *pend = parentGame->getGameEventHandler()->prepareGameCommand(cmd); + PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd); connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished); - parentGame->getGameEventHandler()->sendGameCommand(pend, playerId); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId); } void DeckViewContainer::loadRemoteDeck() { - DlgLoadRemoteDeck dlg(parentGame->getClientForPlayer(playerId), this); + DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this); if (dlg.exec()) { Command_DeckSelect cmd; cmd.set_deck_id(dlg.getDeckId()); - PendingCommand *pend = parentGame->getGameEventHandler()->prepareGameCommand(cmd); + PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd); connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished); - parentGame->getGameEventHandler()->sendGameCommand(pend, playerId); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId); } } @@ -354,7 +354,7 @@ void DeckViewContainer::forceStart() Command_ReadyStart cmd; cmd.set_force_start(true); cmd.set_ready(true); - parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId); } void DeckViewContainer::sideboardLockButtonClicked() @@ -362,7 +362,7 @@ void DeckViewContainer::sideboardLockButtonClicked() Command_SetSideboardLock cmd; cmd.set_locked(sideboardLockButton->getState()); - parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId); } void DeckViewContainer::sideboardPlanChanged() @@ -371,7 +371,7 @@ void DeckViewContainer::sideboardPlanChanged() const QList &newPlan = deckView->getSideboardPlan(); for (const auto &i : newPlan) cmd.add_move_list()->CopyFrom(i); - parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId); } /** @@ -381,7 +381,7 @@ void DeckViewContainer::sendReadyStartCommand(bool ready) { Command_ReadyStart cmd; cmd.set_ready(ready); - parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId); } /** diff --git a/cockatrice/src/game/game.cpp b/cockatrice/src/game/game.cpp new file mode 100644 index 000000000..89009f8d6 --- /dev/null +++ b/cockatrice/src/game/game.cpp @@ -0,0 +1,19 @@ +#include "game.h" + +#include "../client/tabs/tab_game.h" +#include "pb/event_game_joined.pb.h" + +Game::Game(TabGame *_tab, + QList &_clients, + const Event_GameJoined &event, + const QMap &_roomGameTypes) + : AbstractGame(_tab) +{ + gameMetaInfo->setFromProto(event.game_info()); + gameMetaInfo->setRoomGameTypes(_roomGameTypes); + gameState = new GameState(this, 0, event.host_id(), tab->getTabSupervisor()->getIsLocalGame(), _clients, false, + event.resuming(), -1, false); + connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); + playerManager = new PlayerManager(this, event.player_id(), event.judge(), event.spectator()); + gameMetaInfo->setStarted(false); +} diff --git a/cockatrice/src/game/game.h b/cockatrice/src/game/game.h new file mode 100644 index 000000000..123d18378 --- /dev/null +++ b/cockatrice/src/game/game.h @@ -0,0 +1,19 @@ +#ifndef COCKATRICE_GAME_H +#define COCKATRICE_GAME_H + +#include "abstract_game.h" + +#include + +class Game : public AbstractGame +{ + Q_OBJECT + +public: + Game(TabGame *tab, + QList &_clients, + const Event_GameJoined &event, + const QMap &_roomGameTypes); +}; + +#endif // COCKATRICE_GAME_H diff --git a/cockatrice/src/game/game_event_handler.cpp b/cockatrice/src/game/game_event_handler.cpp index ffa3f720e..d06ae828e 100644 --- a/cockatrice/src/game/game_event_handler.cpp +++ b/cockatrice/src/game/game_event_handler.cpp @@ -4,6 +4,7 @@ #include "../server/abstract_client.h" #include "../server/message_log_widget.h" #include "../server/pending_command.h" +#include "abstract_game.h" #include "get_pb_extension.h" #include "pb/command_concede.pb.h" #include "pb/command_delete_arrow.pb.h" @@ -28,7 +29,7 @@ #include "pb/event_set_active_player.pb.h" #include "pb/game_event_container.pb.h" -GameEventHandler::GameEventHandler(TabGame *_game) : game(_game), gameState(_game->getGameState()) +GameEventHandler::GameEventHandler(AbstractGame *_game) : QObject(_game), game(_game) { } @@ -82,7 +83,7 @@ PendingCommand *GameEventHandler::prepareGameCommand(const QListgetPlayers().value(id, nullptr); + Player *judgep = game->getPlayerManager()->getPlayers().value(id, nullptr); if (judgep) { - emit setContextJudgeName(judgep->getName()); - } else if (gameState->getSpectators().contains(id)) { - emit setContextJudgeName(QString::fromStdString(gameState->getSpectators().value(id).name())); + emit setContextJudgeName(judgep->getPlayerInfo()->getName()); + } else if (game->getPlayerManager()->getSpectators().contains(id)) { + emit setContextJudgeName( + QString::fromStdString(game->getPlayerManager()->getSpectators().value(id).name())); } } - if (gameState->getSpectators().contains(playerId)) { + if (game->getPlayerManager()->getSpectators().contains(playerId)) { switch (eventType) { case GameEvent::GAME_SAY: eventSpectatorSay(event.GetExtension(Event_GameSay::ext), playerId, context); @@ -115,8 +117,8 @@ void GameEventHandler::processGameEventContainer(const GameEventContainer &cont, break; } } else { - if ((gameState->getClients().size() > 1) && (playerId != -1)) - if (gameState->getClients().at(playerId) != client) + if ((game->getGameState()->getClients().size() > 1) && (playerId != -1)) + if (game->getGameState()->getClients().at(playerId) != client) continue; switch (eventType) { @@ -163,13 +165,13 @@ void GameEventHandler::processGameEventContainer(const GameEventContainer &cont, break; default: { - Player *player = gameState->getPlayers().value(playerId, 0); + Player *player = game->getPlayerManager()->getPlayers().value(playerId, 0); if (!player) { - // qCWarning(GameEventHandlerLog) << "unhandled game event: invalid player id"; + qCWarning(GameEventHandlerLog) << "unhandled game event: invalid player id"; break; } - player->processGameEvent(eventType, event, context, options); - game->emitUserEvent(); + player->getPlayerEventHandler()->processGameEvent(eventType, event, context, options); + emitUserEvent(); } } } @@ -187,12 +189,12 @@ void GameEventHandler::handleReverseTurn() sendGameCommand(Command_ReverseTurn()); } -void GameEventHandler::handlePlayerConceded() +void GameEventHandler::handleActiveLocalPlayerConceded() { sendGameCommand(Command_Concede()); } -void GameEventHandler::handlePlayerUnconceded() +void GameEventHandler::handleActiveLocalPlayerUnconceded() { sendGameCommand(Command_Unconcede()); } @@ -227,7 +229,7 @@ void GameEventHandler::eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext & /*context*/) { - const ServerInfo_User &userInfo = gameState->getSpectators().value(eventPlayerId); + const ServerInfo_User &userInfo = game->getPlayerManager()->getSpectators().value(eventPlayerId); emit logSpectatorSay(userInfo, QString::fromStdString(event.message())); } @@ -235,13 +237,13 @@ void GameEventHandler::eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/) { - emit logSpectatorLeave(gameState->getSpectatorName(eventPlayerId), getLeaveReason(event.reason())); + emit logSpectatorLeave(game->getPlayerManager()->getSpectatorName(eventPlayerId), getLeaveReason(event.reason())); emit spectatorLeft(eventPlayerId); - gameState->removeSpectator(eventPlayerId); + game->getPlayerManager()->removeSpectator(eventPlayerId); - game->emitUserEvent(); + emitUserEvent(); } void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event, @@ -257,18 +259,18 @@ void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event const ServerInfo_PlayerProperties &prop = playerInfo.properties(); const int playerId = prop.player_id(); QString playerName = "@" + QString::fromStdString(prop.user_info().name()); - game->addPlayerToAutoCompleteList(playerName); + emit addPlayerToAutoCompleteList(playerName); if (prop.spectator()) { - gameState->addSpectator(playerId, prop); + game->getPlayerManager()->addSpectator(playerId, prop); } else { - Player *player = gameState->getPlayers().value(playerId, 0); + Player *player = game->getPlayerManager()->getPlayers().value(playerId, 0); if (!player) { - player = gameState->addPlayer(playerId, prop.user_info(), game); + player = game->getPlayerManager()->addPlayer(playerId, prop.user_info()); emit playerJoined(prop); emit logJoinPlayer(player); } player->processPlayerInfo(playerInfo); - if (player->getLocal()) { + if (player->getPlayerInfo()->getLocal()) { emit localPlayerDeckSelected(player, playerId, playerInfo); } else { if (!game->getGameMetaInfo()->proto().share_decklists_on_load()) { @@ -285,23 +287,23 @@ void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event emit remotePlayersDecksSelected(opponentDecksToDisplay); - gameState->setGameTime(event.seconds_elapsed()); + game->getGameState()->setGameTime(event.seconds_elapsed()); if (event.game_started() && !game->getGameMetaInfo()->started()) { - gameState->setResuming(!gameState->isGameStateKnown()); + game->getGameState()->setResuming(!game->getGameState()->isGameStateKnown()); game->getGameMetaInfo()->setStarted(event.game_started()); - if (gameState->isGameStateKnown()) + if (game->getGameState()->isGameStateKnown()) emit logGameStart(); - gameState->setActivePlayer(event.active_player_id()); - gameState->setCurrentPhase(event.active_phase()); + game->getGameState()->setActivePlayer(event.active_player_id()); + game->getGameState()->setCurrentPhase(event.active_phase()); } else if (!event.game_started() && game->getGameMetaInfo()->started()) { - gameState->setCurrentPhase(-1); - gameState->setActivePlayer(-1); + game->getGameState()->setCurrentPhase(-1); + game->getGameState()->setActivePlayer(-1); game->getGameMetaInfo()->setStarted(false); emit gameStopped(); } - gameState->setGameStateKnown(true); - game->emitUserEvent(); + game->getGameState()->setGameStateKnown(true); + emitUserEvent(); } void GameEventHandler::processCardAttachmentsForPlayers(const Event_GameStateChanged &event) @@ -310,7 +312,7 @@ void GameEventHandler::processCardAttachmentsForPlayers(const Event_GameStateCha const ServerInfo_Player &playerInfo = event.player_list(i); const ServerInfo_PlayerProperties &prop = playerInfo.properties(); if (!prop.spectator()) { - Player *player = gameState->getPlayers().value(prop.player_id(), 0); + Player *player = game->getPlayerManager()->getPlayers().value(prop.player_id(), 0); if (!player) continue; player->processCardAttachment(playerInfo); @@ -322,7 +324,7 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties int eventPlayerId, const GameEventContext &context) { - Player *player = gameState->getPlayers().value(eventPlayerId, 0); + Player *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0); if (!player) return; const ServerInfo_PlayerProperties &prop = event.player_properties(); @@ -332,8 +334,8 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties switch (contextType) { case GameEventContext::READY_START: { bool ready = prop.ready_start(); - if (player->getLocal()) - emit localPlayerReadyStateChanged(player->getId(), ready); + if (player->getPlayerInfo()->getLocal()) + emit localPlayerReadyStateChanged(player->getPlayerInfo()->getId(), ready); if (ready) { emit logReadyStart(player); } else { @@ -342,20 +344,18 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties break; } case GameEventContext::CONCEDE: { - emit playerConceded(player); player->setConceded(true); - QMapIterator playerIterator(gameState->getPlayers()); + QMapIterator playerIterator(game->getPlayerManager()->getPlayers()); while (playerIterator.hasNext()) playerIterator.next().value()->updateZones(); break; } case GameEventContext::UNCONCEDE: { - emit playerUnconceded(player); player->setConceded(false); - QMapIterator playerIterator(gameState->getPlayers()); + QMapIterator playerIterator(game->getPlayerManager()->getPlayers()); while (playerIterator.hasNext()) playerIterator.next().value()->updateZones(); @@ -365,15 +365,15 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties Context_DeckSelect deckSelect = context.GetExtension(Context_DeckSelect::ext); emit logDeckSelect(player, QString::fromStdString(deckSelect.deck_hash()), deckSelect.sideboard_size()); if (game->getGameMetaInfo()->proto().share_decklists_on_load() && deckSelect.has_deck_list() && - eventPlayerId != gameState->getLocalPlayerId()) { + eventPlayerId != game->getPlayerManager()->getLocalPlayerId()) { emit remotePlayerDeckSelected(QString::fromStdString(deckSelect.deck_list()), eventPlayerId, - player->getName()); + player->getPlayerInfo()->getName()); } break; } case GameEventContext::SET_SIDEBOARD_LOCK: { - if (player->getLocal()) { - emit localPlayerSideboardLocked(player->getId(), prop.sideboard_locked()); + if (player->getPlayerInfo()->getLocal()) { + emit localPlayerSideboardLocked(player->getPlayerInfo()->getId(), prop.sideboard_locked()); } emit logSideboardLockSet(player, prop.sideboard_locked()); break; @@ -391,22 +391,22 @@ void GameEventHandler::eventJoin(const Event_Join &event, int /*eventPlayerId*/, const ServerInfo_PlayerProperties &playerInfo = event.player_properties(); const int playerId = playerInfo.player_id(); QString playerName = QString::fromStdString(playerInfo.user_info().name()); - game->addPlayerToAutoCompleteList(playerName); + emit addPlayerToAutoCompleteList(playerName); - if (gameState->getPlayers().contains(playerId)) + if (game->getPlayerManager()->getPlayers().contains(playerId)) return; if (playerInfo.spectator()) { - gameState->addSpectator(playerId, playerInfo); + game->getPlayerManager()->addSpectator(playerId, playerInfo); emit logJoinSpectator(playerName); emit spectatorJoined(playerInfo); } else { - Player *newPlayer = gameState->addPlayer(playerId, playerInfo.user_info(), game); + Player *newPlayer = game->getPlayerManager()->addPlayer(playerId, playerInfo.user_info()); emit logJoinPlayer(newPlayer); emit playerJoined(playerInfo); } - game->emitUserEvent(); + emitUserEvent(); } QString GameEventHandler::getLeaveReason(Event_Leave::LeaveReason reason) @@ -429,7 +429,7 @@ QString GameEventHandler::getLeaveReason(Event_Leave::LeaveReason reason) } void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/) { - Player *player = gameState->getPlayers().value(eventPlayerId, 0); + Player *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0); if (!player) return; @@ -437,17 +437,17 @@ void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, c emit logLeave(player, getLeaveReason(event.reason())); - gameState->removePlayer(eventPlayerId); + game->getPlayerManager()->removePlayer(eventPlayerId); player->clear(); player->deleteLater(); // Rearrange all remaining zones so that attachment relationship updates take place - QMapIterator playerIterator(gameState->getPlayers()); + QMapIterator playerIterator(game->getPlayerManager()->getPlayers()); while (playerIterator.hasNext()) playerIterator.next().value()->updateZones(); - game->emitUserEvent(); + emitUserEvent(); } void GameEventHandler::eventKicked(const Event_Kicked & /*event*/, @@ -455,19 +455,16 @@ void GameEventHandler::eventKicked(const Event_Kicked & /*event*/, const GameEventContext & /*context*/) { emit gameClosed(); - emit logKicked(); - emit playerKicked(); - - game->emitUserEvent(); + emitUserEvent(); } void GameEventHandler::eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext & /*context*/) { - Player *player = gameState->getPlayers().value(eventPlayerId, 0); + Player *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0); if (!player) return; @@ -478,7 +475,7 @@ void GameEventHandler::eventGameHostChanged(const Event_GameHostChanged & /*even int eventPlayerId, const GameEventContext & /*context*/) { - gameState->setHostId(eventPlayerId); + game->getGameState()->setHostId(eventPlayerId); } void GameEventHandler::eventGameClosed(const Event_GameClosed & /*event*/, @@ -486,22 +483,22 @@ void GameEventHandler::eventGameClosed(const Event_GameClosed & /*event*/, const GameEventContext & /*context*/) { game->getGameMetaInfo()->setStarted(false); - gameState->setGameClosed(true); + game->getGameState()->setGameClosed(true); emit gameClosed(); emit logGameClosed(); - game->emitUserEvent(); + emitUserEvent(); } void GameEventHandler::eventSetActivePlayer(const Event_SetActivePlayer &event, int /*eventPlayerId*/, const GameEventContext & /*context*/) { - gameState->setActivePlayer(event.active_player_id()); - Player *player = gameState->getPlayer(event.active_player_id()); + game->getGameState()->setActivePlayer(event.active_player_id()); + Player *player = game->getPlayerManager()->getPlayer(event.active_player_id()); if (!player) return; emit logActivePlayer(player); - game->emitUserEvent(); + emitUserEvent(); } void GameEventHandler::eventSetActivePhase(const Event_SetActivePhase &event, @@ -509,9 +506,9 @@ void GameEventHandler::eventSetActivePhase(const Event_SetActivePhase &event, const GameEventContext & /*context*/) { const int phase = event.phase(); - if (gameState->getCurrentPhase() != phase) { + if (game->getGameState()->getCurrentPhase() != phase) { emit logActivePhaseChanged(phase); } - gameState->setCurrentPhase(phase); - game->emitUserEvent(); + game->getGameState()->setCurrentPhase(phase); + emitUserEvent(); } \ No newline at end of file diff --git a/cockatrice/src/game/game_event_handler.h b/cockatrice/src/game/game_event_handler.h index 3ce036e12..251273dc7 100644 --- a/cockatrice/src/game/game_event_handler.h +++ b/cockatrice/src/game/game_event_handler.h @@ -3,12 +3,12 @@ #include "pb/event_leave.pb.h" #include "pb/serverinfo_player.pb.h" -#include "player/player.h" +#include "player/event_processing_options.h" +#include #include class AbstractClient; -class TabGame; class Response; class GameEventContainer; class GameEventContext; @@ -30,24 +30,27 @@ class Event_Ping; class Event_GameSay; class Event_Kicked; class Event_ReverseTurn; +class AbstractGame; class PendingCommand; +class Player; + +inline Q_LOGGING_CATEGORY(GameEventHandlerLog, "tab_game"); class GameEventHandler : public QObject { Q_OBJECT private: - TabGame *game; - GameState *gameState; + AbstractGame *game; public: - GameEventHandler(TabGame *game); + explicit GameEventHandler(AbstractGame *_game); void handleNextTurn(); void handleReverseTurn(); - void handlePlayerConceded(); - void handlePlayerUnconceded(); + void handleActiveLocalPlayerConceded(); + void handleActiveLocalPlayerUnconceded(); void handleActivePhaseChanged(int phase); void handleGameLeft(); void handleChatMessageSent(const QString &chatMessage); @@ -75,9 +78,8 @@ public: void commandFinished(const Response &response); - void processGameEventContainer(const GameEventContainer &cont, - AbstractClient *client, - Player::EventProcessingOptions options); + void + processGameEventContainer(const GameEventContainer &cont, AbstractClient *client, EventProcessingOptions options); PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd); PendingCommand *prepareGameCommand(const QList &cmdList); public slots: @@ -85,6 +87,8 @@ public slots: void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1); signals: + void emitUserEvent(); + void addPlayerToAutoCompleteList(QString playerName); void localPlayerDeckSelected(Player *localPlayer, int playerId, ServerInfo_Player playerInfo); void remotePlayerDeckSelected(QString deckList, int playerId, QString playerName); void remotePlayersDecksSelected(QVector>> opponentDecks); @@ -107,8 +111,6 @@ signals: void logGameStart(); void logReadyStart(Player *player); void logNotReadyStart(Player *player); - void playerConceded(Player *player); - void playerUnconceded(Player *player); void logDeckSelect(Player *player, QString deckHash, int sideboardSize); void logSideboardLockSet(Player *player, bool sideboardLocked); void logConnectionStateChanged(Player *player, bool connected); diff --git a/cockatrice/src/game/game_meta_info.cpp b/cockatrice/src/game/game_meta_info.cpp index 90a6cbeab..45146dad9 100644 --- a/cockatrice/src/game/game_meta_info.cpp +++ b/cockatrice/src/game/game_meta_info.cpp @@ -1 +1,7 @@ #include "game_meta_info.h" + +#include "abstract_game.h" + +GameMetaInfo::GameMetaInfo(AbstractGame *_game) : QObject(_game), game(_game) +{ +} diff --git a/cockatrice/src/game/game_meta_info.h b/cockatrice/src/game/game_meta_info.h index 08600423e..39d589c65 100644 --- a/cockatrice/src/game/game_meta_info.h +++ b/cockatrice/src/game/game_meta_info.h @@ -10,13 +10,12 @@ // This class de-couples the domain object (i.e. the GameMetaInfo) from the network object. // If the network object changes, only this class needs to be adjusted. +class AbstractGame; class GameMetaInfo : public QObject { Q_OBJECT public: - explicit GameMetaInfo(QObject *parent = nullptr) : QObject(parent) - { - } + explicit GameMetaInfo(AbstractGame *_game); QMap roomGameTypes; @@ -80,6 +79,11 @@ public: return roomGameTypes.find(gameInfo_.game_types(index)).value(); } + AbstractGame *getGame() const + { + return game; + } + public slots: void setStarted(bool s) { @@ -101,6 +105,7 @@ signals: void spectatorsOmniscienceChanged(bool omniscient); private: + AbstractGame *game; ServerInfo_Game gameInfo_; }; diff --git a/cockatrice/src/game/game_scene.cpp b/cockatrice/src/game/game_scene.cpp index ba54d5b0c..1494661d7 100644 --- a/cockatrice/src/game/game_scene.cpp +++ b/cockatrice/src/game/game_scene.cpp @@ -4,6 +4,7 @@ #include "../settings/cache_settings.h" #include "board/card_item.h" #include "player/player.h" +#include "player/player_graphics_item.h" #include "zones/view_zone.h" #include "zones/view_zone_widget.h" @@ -46,23 +47,22 @@ void GameScene::retranslateUi() void GameScene::addPlayer(Player *player) { - qCInfo(GameScenePlayerAdditionRemovalLog) << "GameScene::addPlayer name=" << player->getName(); - players << player; - addItem(player); - connect(player, &Player::sizeChanged, this, &GameScene::rearrange); - connect(player, &Player::playerCountChanged, this, &GameScene::rearrange); + qCInfo(GameScenePlayerAdditionRemovalLog) << "GameScene::addPlayer name=" << player->getPlayerInfo()->getName(); + players << player->getGraphicsItem(); + addItem(player->getGraphicsItem()); + connect(player->getGraphicsItem(), &PlayerGraphicsItem::sizeChanged, this, &GameScene::rearrange); } void GameScene::removePlayer(Player *player) { - qCInfo(GameScenePlayerAdditionRemovalLog) << "GameScene::removePlayer name=" << player->getName(); + qCInfo(GameScenePlayerAdditionRemovalLog) << "GameScene::removePlayer name=" << player->getPlayerInfo()->getName(); for (ZoneViewWidget *zone : zoneViews) { if (zone->getPlayer() == player) { zone->close(); } } - players.removeOne(player); - removeItem(player); + players.removeOne(player->getGraphicsItem()); + removeItem(player->getGraphicsItem()); rearrange(); } @@ -80,12 +80,12 @@ void GameScene::rearrange() QList playersPlaying; int firstPlayerIndex = 0; bool firstPlayerFound = false; - QListIterator playersIter(players); + QListIterator playersIter(players); while (playersIter.hasNext()) { - Player *p = playersIter.next(); + Player *p = playersIter.next()->getPlayer(); if (p && !p->getConceded()) { playersPlaying.append(p); - if (!firstPlayerFound && (p->getLocal())) { + if (!firstPlayerFound && (p->getPlayerInfo()->getLocal())) { firstPlayerIndex = playersPlaying.size() - 1; firstPlayerFound = true; } @@ -111,19 +111,19 @@ void GameScene::rearrange() QListIterator playersPlayingIter(playersPlaying); for (int col = 0; col < columns; ++col) { - playersByColumn.append(QList()); + playersByColumn.append(QList()); columnWidth.append(0); qreal thisColumnHeight = -playerAreaSpacing; const int rowsInColumn = rows - (playersCount % columns) * col; // only correct for max. 2 cols for (int j = 0; j < rowsInColumn; ++j) { Player *player = playersPlayingIter.next(); if (col == 0) - playersByColumn[col].prepend(player); + playersByColumn[col].prepend(player->getGraphicsItem()); else - playersByColumn[col].append(player); - thisColumnHeight += player->boundingRect().height() + playerAreaSpacing; - if (player->boundingRect().width() > columnWidth[col]) - columnWidth[col] = player->boundingRect().width(); + playersByColumn[col].append(player->getGraphicsItem()); + thisColumnHeight += player->getGraphicsItem()->boundingRect().height() + playerAreaSpacing; + if (player->getGraphicsItem()->boundingRect().width() > columnWidth[col]) + columnWidth[col] = player->getGraphicsItem()->boundingRect().width(); } if (thisColumnHeight > sceneHeight) sceneHeight = thisColumnHeight; @@ -138,7 +138,7 @@ void GameScene::rearrange() for (int col = 0; col < columns; ++col) { qreal y = 0; for (int row = 0; row < playersByColumn[col].size(); ++row) { - Player *player = playersByColumn[col][row]; + PlayerGraphicsItem *player = playersByColumn[col][row]; player->setPos(x, y); player->setMirrored(row != rows - 1); y += player->boundingRect().height() + playerAreaSpacing; @@ -154,7 +154,8 @@ void GameScene::toggleZoneView(Player *player, const QString &zoneName, int numb { for (auto &view : zoneViews) { ZoneViewZone *temp = view->getZone(); - if (temp->getName() == zoneName && temp->getPlayer() == player && temp->getNumberCards() == numberCards) { + if (temp->getLogic()->getName() == zoneName && temp->getLogic()->getPlayer() == player && + qobject_cast(temp->getLogic())->getNumberCards() == numberCards) { view->close(); } } @@ -174,7 +175,7 @@ void GameScene::toggleZoneView(Player *player, const QString &zoneName, int numb } void GameScene::addRevealedZoneView(Player *player, - CardZone *zone, + CardZoneLogic *zone, const QList &cardList, bool withWritePermission) { @@ -272,9 +273,9 @@ void GameScene::updateHover(const QPointF &scenePos) if (!card) continue; if (card->getAttachedTo()) { - if (card->getAttachedTo()->getZone() != zone) + if (card->getAttachedTo()->getZone() != zone->getLogic()) continue; - } else if (card->getZone() != zone) + } else if (card->getZone() != zone->getLogic()) continue; if (card->getRealZValue() > maxZ) { diff --git a/cockatrice/src/game/game_scene.h b/cockatrice/src/game/game_scene.h index 801acacee..32721d0b0 100644 --- a/cockatrice/src/game/game_scene.h +++ b/cockatrice/src/game/game_scene.h @@ -1,6 +1,8 @@ #ifndef GAMESCENE_H #define GAMESCENE_H +#include "zones/logic/card_zone_logic.h" + #include #include #include @@ -11,6 +13,7 @@ inline Q_LOGGING_CATEGORY(GameSceneLog, "game_scene"); inline Q_LOGGING_CATEGORY(GameScenePlayerAdditionRemovalLog, "game_scene.player_addition_removal"); class Player; +class PlayerGraphicsItem; class ZoneViewWidget; class CardZone; class AbstractCardItem; @@ -26,8 +29,8 @@ private: static const int playerAreaSpacing = 5; PhasesToolbar *phasesToolbar; - QList players; - QList> playersByColumn; + QList players; + QList> playersByColumn; QList zoneViews; QSize viewSize; QPointer hoveredCard; @@ -53,7 +56,7 @@ public: public slots: void toggleZoneView(Player *player, const QString &zoneName, int numberCards, bool isReversed = false); void addRevealedZoneView(Player *player, - CardZone *zone, + CardZoneLogic *zone, const QList &cardList, bool withWritePermission); void removeZoneView(ZoneViewWidget *item); diff --git a/cockatrice/src/game/game_state.cpp b/cockatrice/src/game/game_state.cpp index 91ed3e356..4d86a16d4 100644 --- a/cockatrice/src/game/game_state.cpp +++ b/cockatrice/src/game/game_state.cpp @@ -1,19 +1,19 @@ #include "game_state.h" -GameState::GameState(int _secondsElapsed, +#include "abstract_game.h" + +GameState::GameState(AbstractGame *_game, + int _secondsElapsed, int _hostId, - int _localPlayerId, bool _isLocalGame, const QList _clients, - bool _spectator, - bool _judge, bool _gameStateKnown, bool _resuming, int _currentPhase, bool _gameClosed) - : secondsElapsed(_secondsElapsed), hostId(_hostId), localPlayerId(_localPlayerId), isLocalGame(_isLocalGame), - clients(_clients), spectator(_spectator), judge(_judge), gameStateKnown(_gameStateKnown), resuming(_resuming), - currentPhase(_currentPhase), gameClosed(_gameClosed) + : QObject(_game), game(_game), secondsElapsed(_secondsElapsed), hostId(_hostId), isLocalGame(_isLocalGame), + clients(_clients), gameStateKnown(_gameStateKnown), resuming(_resuming), currentPhase(_currentPhase), + gameClosed(_gameClosed) { } diff --git a/cockatrice/src/game/game_state.h b/cockatrice/src/game/game_state.h index ed6fdbc49..4f1cd4b17 100644 --- a/cockatrice/src/game/game_state.h +++ b/cockatrice/src/game/game_state.h @@ -1,13 +1,13 @@ #ifndef COCKATRICE_GAME_STATE_H #define COCKATRICE_GAME_STATE_H -#include "../client/tabs/tab_game.h" #include "../server/abstract_client.h" #include "pb/serverinfo_game.pb.h" -#include "pb/serverinfo_playerproperties.pb.h" #include +#include +class AbstractGame; class ServerInfo_PlayerProperties; class ServerInfo_User; @@ -16,149 +16,31 @@ class GameState : public QObject Q_OBJECT public: - explicit GameState(int secondsElapsed, + explicit GameState(AbstractGame *_game, + int secondsElapsed, int hostId, - int localPlayerId, bool isLocalGame, QList clients, - bool spectator, - bool judge, bool gameStateKnown, bool resuming, int currentPhase, bool gameClosed); - const QMap &getPlayers() const - { - return players; - } - - int getPlayerCount() const - { - return players.size(); - } - - const QMap &getSpectators() const - { - return spectators; - } - - ServerInfo_User getSpectator(int playerId) const - { - return spectators.value(playerId); - } - - QString getSpectatorName(int spectatorId) const - { - return QString::fromStdString(spectators.value(spectatorId).name()); - } - - void addSpectator(int spectatorId, const ServerInfo_PlayerProperties &prop) - { - if (!spectators.contains(spectatorId)) { - spectators.insert(spectatorId, prop.user_info()); - emit spectatorAdded(prop); - } - } - - void removeSpectator(int spectatorId) - { - ServerInfo_User spectatorInfo = spectators.value(spectatorId); - spectators.remove(spectatorId); - emit spectatorRemoved(spectatorId, spectatorInfo); - } - - bool isHost() const - { - return hostId == localPlayerId; - } - void setHostId(int _hostId) { hostId = _hostId; } - bool isJudge() const - { - return judge; - } - - int getLocalPlayerId() const - { - return localPlayerId; - } - QList getClients() const { return clients; } - bool isLocalPlayer(int playerId) const - { - return clients.size() > 1 || playerId == getLocalPlayerId(); - } - - Player *addPlayer(int playerId, const ServerInfo_User &info, TabGame *game) - { - auto *newPlayer = new Player(info, playerId, isLocalPlayer(playerId), isJudge(), game); - // TODO - // connect(newPlayer, &Player::openDeckEditor, game, &TabGame::openDeckEditor); - players.insert(playerId, newPlayer); - emit playerAdded(newPlayer); - return newPlayer; - } - - void removePlayer(int playerId) - { - Player *player = getPlayer(playerId); - if (!player) { - return; - } - players.remove(playerId); - emit playerRemoved(player); - } - - Player *getPlayer(int playerId) - { - Player *player = players.value(playerId, 0); - if (!player) - return nullptr; - return player; - } - - Player *getActiveLocalPlayer() const - { - Player *active = players.value(activePlayer, 0); - if (active) - if (active->getLocal()) - return active; - - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Player *temp = playerIterator.next().value(); - if (temp->getLocal()) - return temp; - } - - return nullptr; - } - - void setActivePlayer(int activePlayerId) - { - activePlayer = activePlayerId; - emit activePlayerChanged(activePlayer); - } - bool getIsLocalGame() const { return isLocalGame; } - bool isSpectator() const - { - return spectator; - } - bool isResuming() const { return resuming; @@ -185,10 +67,15 @@ public: emit activePhaseChanged(phase); } - bool isMainPlayerConceded() const + void setActivePlayer(int activePlayerId) { - Player *player = players.value(localPlayerId, nullptr); - return player && player->getConceded(); + activePlayer = activePlayerId; + emit activePlayerChanged(activePlayer); + } + + int getActivePlayer() const + { + return activePlayer; } void setGameClosed(bool closed) @@ -213,17 +100,28 @@ public: void startGameTimer(); + QMap getRoomGameTypes() const + { + return roomGameTypes; + } + + void setRoomGameTypes(QMap _roomGameTypes) + { + roomGameTypes = _roomGameTypes; + } + void setGameStateKnown(bool known) { gameStateKnown = known; } + int getHostId() const + { + return hostId; + } + signals: void updateTimeElapsedLabel(QString newTime); - void playerAdded(Player *player); - void playerRemoved(Player *player); - void spectatorAdded(ServerInfo_PlayerProperties spectator); - void spectatorRemoved(int spectatorId, ServerInfo_User spectator); void gameStarted(bool resuming); void gameStopped(); void activePhaseChanged(int activePhase); @@ -234,16 +132,13 @@ public slots: void setGameTime(int _secondsElapsed); private: + AbstractGame *game; QTimer *gameTimer; int secondsElapsed; + QMap roomGameTypes; int hostId; - int localPlayerId; const bool isLocalGame; - QMap players; - QMap spectators; QList clients; - bool spectator; - bool judge; bool gameStateKnown; bool resuming; QStringList phasesList; diff --git a/cockatrice/src/game/hand_counter.cpp b/cockatrice/src/game/hand_counter.cpp index a5c1f1a77..35989ff38 100644 --- a/cockatrice/src/game/hand_counter.cpp +++ b/cockatrice/src/game/hand_counter.cpp @@ -17,7 +17,7 @@ HandCounter::~HandCounter() void HandCounter::updateNumber() { - number = static_cast(sender())->getCards().size(); + number = static_cast(sender())->getCards().size(); update(); } diff --git a/cockatrice/src/game/hand_counter.h b/cockatrice/src/game/hand_counter.h index 2a6633b63..feb536fce 100644 --- a/cockatrice/src/game/hand_counter.h +++ b/cockatrice/src/game/hand_counter.h @@ -2,6 +2,7 @@ #define HANDCOUNTER_H #include "../game/board/abstract_graphics_item.h" +#include "board/graphics_item_type.h" #include diff --git a/cockatrice/src/game/player/card_menu_action_type.h b/cockatrice/src/game/player/card_menu_action_type.h new file mode 100644 index 000000000..157164024 --- /dev/null +++ b/cockatrice/src/game/player/card_menu_action_type.h @@ -0,0 +1,19 @@ +#ifndef COCKATRICE_CARD_MENU_ACTION_TYPE_H +#define COCKATRICE_CARD_MENU_ACTION_TYPE_H + +enum CardMenuActionType +{ + cmTap, + cmUntap, + cmDoesntUntap, + cmFlip, + cmPeek, + cmClone, + cmMoveToTopLibrary, + cmMoveToBottomLibrary, + cmMoveToHand, + cmMoveToGraveyard, + cmMoveToExile +}; + +#endif // COCKATRICE_CARD_MENU_ACTION_TYPE_H diff --git a/cockatrice/src/game/player/event_processing_options.h b/cockatrice/src/game/player/event_processing_options.h new file mode 100644 index 000000000..c5c345ebc --- /dev/null +++ b/cockatrice/src/game/player/event_processing_options.h @@ -0,0 +1,19 @@ +#ifndef COCKATRICE_EVENT_PROCESSING_OPTIONS_H +#define COCKATRICE_EVENT_PROCESSING_OPTIONS_H + +#include + +// Define the base enum +enum EventProcessingOption +{ + SKIP_REVEAL_WINDOW = 0x0001, + SKIP_TAP_ANIMATION = 0x0002 +}; + +// Wrap it in a QFlags typedef +Q_DECLARE_FLAGS(EventProcessingOptions, EventProcessingOption) + +// Add operator overloads (|, &, etc.) +Q_DECLARE_OPERATORS_FOR_FLAGS(EventProcessingOptions) + +#endif // COCKATRICE_EVENT_PROCESSING_OPTIONS_H diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index 25181a624..e9a1b52c8 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -1,601 +1,82 @@ #include "player.h" -#include "../../client/get_text_with_max.h" #include "../../client/tabs/tab_game.h" #include "../../client/ui/theme_manager.h" -#include "../../deck/deck_loader.h" -#include "../../dialogs/dlg_move_top_cards_until.h" -#include "../../dialogs/dlg_roll_dice.h" -#include "../../main.h" -#include "../../settings/cache_settings.h" -#include "../../settings/card_counter_settings.h" #include "../board/arrow_item.h" #include "../board/card_item.h" #include "../board/card_list.h" #include "../board/counter_general.h" -#include "../cards/card_database.h" -#include "../cards/card_database_manager.h" #include "../game_scene.h" -#include "../hand_counter.h" -#include "../zones/card_zone.h" #include "../zones/hand_zone.h" #include "../zones/pile_zone.h" #include "../zones/stack_zone.h" #include "../zones/table_zone.h" #include "../zones/view_zone.h" -#include "../zones/view_zone_widget.h" #include "color.h" #include "pb/command_attach_card.pb.h" -#include "pb/command_change_zone_properties.pb.h" -#include "pb/command_concede.pb.h" -#include "pb/command_create_token.pb.h" -#include "pb/command_draw_cards.pb.h" -#include "pb/command_flip_card.pb.h" -#include "pb/command_game_say.pb.h" -#include "pb/command_move_card.pb.h" -#include "pb/command_mulligan.pb.h" -#include "pb/command_reveal_cards.pb.h" -#include "pb/command_roll_die.pb.h" -#include "pb/command_set_card_attr.pb.h" #include "pb/command_set_card_counter.pb.h" -#include "pb/command_shuffle.pb.h" -#include "pb/command_undo_draw.pb.h" -#include "pb/context_move_card.pb.h" -#include "pb/context_undo_draw.pb.h" -#include "pb/event_attach_card.pb.h" -#include "pb/event_change_zone_properties.pb.h" #include "pb/event_create_arrow.pb.h" #include "pb/event_create_counter.pb.h" -#include "pb/event_create_token.pb.h" -#include "pb/event_del_counter.pb.h" -#include "pb/event_delete_arrow.pb.h" -#include "pb/event_destroy_card.pb.h" #include "pb/event_draw_cards.pb.h" -#include "pb/event_dump_zone.pb.h" -#include "pb/event_flip_card.pb.h" -#include "pb/event_game_say.pb.h" -#include "pb/event_move_card.pb.h" -#include "pb/event_reveal_cards.pb.h" -#include "pb/event_roll_die.pb.h" -#include "pb/event_set_card_attr.pb.h" -#include "pb/event_set_card_counter.pb.h" -#include "pb/event_set_counter.pb.h" -#include "pb/event_shuffle.pb.h" #include "pb/serverinfo_player.pb.h" #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_zone.pb.h" #include "player_target.h" -#include "trice_limits.h" #include #include -#include #include #include -#include -#include #include -// milliseconds in between triggers of the move top cards until action -static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; - -PlayerArea::PlayerArea(QGraphicsItem *parentItem) : QObject(), QGraphicsItem(parentItem) +Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent) + : QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)), + playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false), + conceded(false), deck(nullptr), zoneId(0), dialogSemaphore(false) { - setCacheMode(DeviceCoordinateCache); - connect(themeManager, &ThemeManager::themeChanged, this, &PlayerArea::updateBg); - updateBg(); + initializeZones(); + + playerMenu = new PlayerMenu(this); + graphicsItem = new PlayerGraphicsItem(this); + playerMenu->setMenusForGraphicItems(); + + connect(this, &Player::activeChanged, graphicsItem, &PlayerGraphicsItem::onPlayerActiveChanged); + + connect(this, &Player::deckChanged, playerMenu, &PlayerMenu::enableOpenInDeckEditorAction); + connect(this, &Player::deckChanged, playerMenu, &PlayerMenu::populatePredefinedTokensMenu); + + connect(this, &Player::openDeckEditor, game->getTab(), &TabGame::openDeckEditor); } -void PlayerArea::updateBg() +void Player::initializeZones() { - update(); -} - -void PlayerArea::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) -{ - QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Player, playerZoneId); - painter->fillRect(boundingRect(), brush); -} - -void PlayerArea::setSize(qreal width, qreal height) -{ - prepareGeometryChange(); - bRect = QRectF(0, 0, width, height); -} - -void PlayerArea::setPlayerZoneId(int _playerZoneId) -{ - playerZoneId = _playerZoneId; -} - -Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, TabGame *_parent) - : QObject(_parent), game(_parent), movingCardsUntil(false), shortcutsActive(false), lastTokenTableRow(0), id(_id), - active(false), local(_local), judge(_judge), mirrored(false), handVisible(false), conceded(false), zoneId(0), - dialogSemaphore(false), deck(nullptr) -{ - userInfo = new ServerInfo_User; - userInfo->CopyFrom(info); - - connect(&SettingsCache::instance(), &SettingsCache::horizontalHandChanged, this, &Player::rearrangeZones); - connect(&SettingsCache::instance(), &SettingsCache::handJustificationChanged, this, &Player::rearrangeZones); - - playerArea = new PlayerArea(this); - - playerTarget = new PlayerTarget(this, playerArea, game); - qreal avatarMargin = (counterAreaWidth + CARD_HEIGHT + 15 - playerTarget->boundingRect().width()) / 2.0; - playerTarget->setPos(QPointF(avatarMargin, avatarMargin)); - - auto *_deck = addZone(new PileZone(this, "deck", true, false, playerArea)); - QPointF base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0, - 10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0); - _deck->setPos(base); - - qreal h = _deck->boundingRect().width() + 5; - - auto *handCounter = new HandCounter(playerArea); - handCounter->setPos(base + QPointF(0, h + 10)); - qreal h2 = handCounter->boundingRect().height(); - - PileZone *grave = addZone(new PileZone(this, "grave", false, true, playerArea)); - grave->setPos(base + QPointF(0, h + h2 + 10)); - - PileZone *rfg = addZone(new PileZone(this, "rfg", false, true, playerArea)); - rfg->setPos(base + QPointF(0, 2 * h + h2 + 10)); - - PileZone *sb = addZone(new PileZone(this, "sb", false, false, playerArea)); - sb->setVisible(false); - - table = addZone(new TableZone(this, "table", this)); - connect(table, &TableZone::sizeChanged, this, &Player::updateBoundingRect); - - stack = addZone(new StackZone(this, (int)table->boundingRect().height(), this)); - - hand = addZone( - new HandZone(this, - _local || _judge || - (_parent->getGameState()->isSpectator() && _parent->getGameMetaInfo()->spectatorsOmniscient()), - (int)table->boundingRect().height(), this)); - connect(hand, &HandZone::cardCountChanged, handCounter, &HandCounter::updateNumber); - connect(handCounter, &HandCounter::showContextMenu, hand, &HandZone::showContextMenu); - - updateBoundingRect(); - - if (local || judge) { - connect(_parent->getGameState(), &GameState::playerAdded, this, &Player::addPlayer); - connect(_parent->getGameState(), &GameState::playerRemoved, this, &Player::removePlayer); - } - - if (local || judge) { - aMoveHandToTopLibrary = new QAction(this); - aMoveHandToTopLibrary->setData(QList() << "deck" << 0); - aMoveHandToBottomLibrary = new QAction(this); - aMoveHandToBottomLibrary->setData(QList() << "deck" << -1); - aMoveHandToGrave = new QAction(this); - aMoveHandToGrave->setData(QList() << "grave" << 0); - aMoveHandToRfg = new QAction(this); - aMoveHandToRfg->setData(QList() << "rfg" << 0); - - connect(aMoveHandToTopLibrary, &QAction::triggered, hand, &HandZone::moveAllToZone); - connect(aMoveHandToBottomLibrary, &QAction::triggered, hand, &HandZone::moveAllToZone); - connect(aMoveHandToGrave, &QAction::triggered, hand, &HandZone::moveAllToZone); - connect(aMoveHandToRfg, &QAction::triggered, hand, &HandZone::moveAllToZone); - - aMoveGraveToTopLibrary = new QAction(this); - aMoveGraveToTopLibrary->setData(QList() << "deck" << 0); - aMoveGraveToBottomLibrary = new QAction(this); - aMoveGraveToBottomLibrary->setData(QList() << "deck" << -1); - aMoveGraveToHand = new QAction(this); - aMoveGraveToHand->setData(QList() << "hand" << 0); - aMoveGraveToRfg = new QAction(this); - aMoveGraveToRfg->setData(QList() << "rfg" << 0); - - connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZone::moveAllToZone); - connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZone::moveAllToZone); - connect(aMoveGraveToHand, &QAction::triggered, grave, &PileZone::moveAllToZone); - connect(aMoveGraveToRfg, &QAction::triggered, grave, &PileZone::moveAllToZone); - - aMoveRfgToTopLibrary = new QAction(this); - aMoveRfgToTopLibrary->setData(QList() << "deck" << 0); - aMoveRfgToBottomLibrary = new QAction(this); - aMoveRfgToBottomLibrary->setData(QList() << "deck" << -1); - aMoveRfgToHand = new QAction(this); - aMoveRfgToHand->setData(QList() << "hand" << 0); - aMoveRfgToGrave = new QAction(this); - aMoveRfgToGrave->setData(QList() << "grave" << 0); - - connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZone::moveAllToZone); - connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZone::moveAllToZone); - connect(aMoveRfgToHand, &QAction::triggered, rfg, &PileZone::moveAllToZone); - connect(aMoveRfgToGrave, &QAction::triggered, rfg, &PileZone::moveAllToZone); - - aViewLibrary = new QAction(this); - connect(aViewLibrary, &QAction::triggered, this, &Player::actViewLibrary); - aViewHand = new QAction(this); - connect(aViewHand, &QAction::triggered, this, &Player::actViewHand); - aSortHand = new QAction(this); - connect(aSortHand, &QAction::triggered, this, &Player::actSortHand); - - aViewTopCards = new QAction(this); - connect(aViewTopCards, &QAction::triggered, this, &Player::actViewTopCards); - aViewBottomCards = new QAction(this); - connect(aViewBottomCards, &QAction::triggered, this, &Player::actViewBottomCards); - aAlwaysRevealTopCard = new QAction(this); - aAlwaysRevealTopCard->setCheckable(true); - connect(aAlwaysRevealTopCard, &QAction::triggered, this, &Player::actAlwaysRevealTopCard); - aAlwaysLookAtTopCard = new QAction(this); - aAlwaysLookAtTopCard->setCheckable(true); - connect(aAlwaysLookAtTopCard, &QAction::triggered, this, &Player::actAlwaysLookAtTopCard); - aOpenDeckInDeckEditor = new QAction(this); - aOpenDeckInDeckEditor->setEnabled(false); - connect(aOpenDeckInDeckEditor, &QAction::triggered, this, &Player::actOpenDeckInDeckEditor); - } - - aViewGraveyard = new QAction(this); - connect(aViewGraveyard, &QAction::triggered, this, &Player::actViewGraveyard); - - aViewRfg = new QAction(this); - connect(aViewRfg, &QAction::triggered, this, &Player::actViewRfg); - - if (local || judge) { - aViewSideboard = new QAction(this); - connect(aViewSideboard, &QAction::triggered, this, &Player::actViewSideboard); - - aDrawCard = new QAction(this); - connect(aDrawCard, &QAction::triggered, this, &Player::actDrawCard); - aDrawCards = new QAction(this); - connect(aDrawCards, &QAction::triggered, this, &Player::actDrawCards); - aUndoDraw = new QAction(this); - connect(aUndoDraw, &QAction::triggered, this, &Player::actUndoDraw); - - aShuffle = new QAction(this); - connect(aShuffle, &QAction::triggered, this, &Player::actShuffle); - - aMulligan = new QAction(this); - connect(aMulligan, &QAction::triggered, this, &Player::actMulligan); - - aMoveTopToPlay = new QAction(this); - connect(aMoveTopToPlay, &QAction::triggered, this, &Player::actMoveTopCardToPlay); - aMoveTopToPlayFaceDown = new QAction(this); - connect(aMoveTopToPlayFaceDown, &QAction::triggered, this, &Player::actMoveTopCardToPlayFaceDown); - aMoveTopCardToGraveyard = new QAction(this); - connect(aMoveTopCardToGraveyard, &QAction::triggered, this, &Player::actMoveTopCardToGrave); - aMoveTopCardToExile = new QAction(this); - connect(aMoveTopCardToExile, &QAction::triggered, this, &Player::actMoveTopCardToExile); - aMoveTopCardsToGraveyard = new QAction(this); - connect(aMoveTopCardsToGraveyard, &QAction::triggered, this, &Player::actMoveTopCardsToGrave); - aMoveTopCardsToExile = new QAction(this); - connect(aMoveTopCardsToExile, &QAction::triggered, this, &Player::actMoveTopCardsToExile); - aMoveTopCardsUntil = new QAction(this); - connect(aMoveTopCardsUntil, &QAction::triggered, this, &Player::actMoveTopCardsUntil); - aMoveTopCardToBottom = new QAction(this); - connect(aMoveTopCardToBottom, &QAction::triggered, this, &Player::actMoveTopCardToBottom); - - aShuffleTopCards = new QAction(this); - connect(aShuffleTopCards, &QAction::triggered, this, &Player::actShuffleTop); - - aDrawBottomCard = new QAction(this); - connect(aDrawBottomCard, &QAction::triggered, this, &Player::actDrawBottomCard); - aDrawBottomCards = new QAction(this); - connect(aDrawBottomCards, &QAction::triggered, this, &Player::actDrawBottomCards); - aMoveBottomToPlay = new QAction(this); - connect(aMoveBottomToPlay, &QAction::triggered, this, &Player::actMoveBottomCardToPlay); - aMoveBottomToPlayFaceDown = new QAction(this); - connect(aMoveBottomToPlayFaceDown, &QAction::triggered, this, &Player::actMoveBottomCardToPlayFaceDown); - aMoveBottomCardToGraveyard = new QAction(this); - connect(aMoveBottomCardToGraveyard, &QAction::triggered, this, &Player::actMoveBottomCardToGrave); - aMoveBottomCardToExile = new QAction(this); - connect(aMoveBottomCardToExile, &QAction::triggered, this, &Player::actMoveBottomCardToExile); - aMoveBottomCardsToGraveyard = new QAction(this); - connect(aMoveBottomCardsToGraveyard, &QAction::triggered, this, &Player::actMoveBottomCardsToGrave); - aMoveBottomCardsToExile = new QAction(this); - connect(aMoveBottomCardsToExile, &QAction::triggered, this, &Player::actMoveBottomCardsToExile); - aMoveBottomCardToTop = new QAction(this); - connect(aMoveBottomCardToTop, &QAction::triggered, this, &Player::actMoveBottomCardToTop); - - aShuffleBottomCards = new QAction(this); - connect(aShuffleBottomCards, &QAction::triggered, this, &Player::actShuffleBottom); - } - - playerMenu = new TearOffMenu(); - table->setMenu(playerMenu); - - if (local || judge) { - handMenu = playerMenu->addTearOffMenu(QString()); - handMenu->addAction(aViewHand); - handMenu->addAction(aSortHand); - playerLists.append(mRevealHand = handMenu->addMenu(QString())); - playerLists.append(mRevealRandomHandCard = handMenu->addMenu(QString())); - handMenu->addSeparator(); - handMenu->addAction(aMulligan); - handMenu->addSeparator(); - moveHandMenu = handMenu->addTearOffMenu(QString()); - moveHandMenu->addAction(aMoveHandToTopLibrary); - moveHandMenu->addAction(aMoveHandToBottomLibrary); - moveHandMenu->addSeparator(); - moveHandMenu->addAction(aMoveHandToGrave); - moveHandMenu->addSeparator(); - moveHandMenu->addAction(aMoveHandToRfg); - hand->setMenu(handMenu); - - libraryMenu = playerMenu->addTearOffMenu(QString()); - libraryMenu->addAction(aDrawCard); - libraryMenu->addAction(aDrawCards); - libraryMenu->addAction(aUndoDraw); - libraryMenu->addSeparator(); - libraryMenu->addAction(aShuffle); - libraryMenu->addSeparator(); - libraryMenu->addAction(aViewLibrary); - libraryMenu->addAction(aViewTopCards); - libraryMenu->addAction(aViewBottomCards); - libraryMenu->addSeparator(); - playerLists.append(mRevealLibrary = libraryMenu->addMenu(QString())); - singlePlayerLists.append(mLendLibrary = libraryMenu->addMenu(QString())); - playerLists.append(mRevealTopCard = libraryMenu->addMenu(QString())); - libraryMenu->addAction(aAlwaysRevealTopCard); - libraryMenu->addAction(aAlwaysLookAtTopCard); - libraryMenu->addSeparator(); - topLibraryMenu = libraryMenu->addTearOffMenu(QString()); - bottomLibraryMenu = libraryMenu->addTearOffMenu(QString()); - libraryMenu->addSeparator(); - libraryMenu->addAction(aOpenDeckInDeckEditor); - _deck->setMenu(libraryMenu, aDrawCard); - topLibraryMenu->addAction(aMoveTopToPlay); - topLibraryMenu->addAction(aMoveTopToPlayFaceDown); - topLibraryMenu->addAction(aMoveTopCardToBottom); - topLibraryMenu->addSeparator(); - topLibraryMenu->addAction(aMoveTopCardToGraveyard); - topLibraryMenu->addAction(aMoveTopCardsToGraveyard); - topLibraryMenu->addAction(aMoveTopCardToExile); - topLibraryMenu->addAction(aMoveTopCardsToExile); - topLibraryMenu->addAction(aMoveTopCardsUntil); - topLibraryMenu->addSeparator(); - topLibraryMenu->addAction(aShuffleTopCards); - - bottomLibraryMenu->addAction(aDrawBottomCard); - bottomLibraryMenu->addAction(aDrawBottomCards); - bottomLibraryMenu->addSeparator(); - bottomLibraryMenu->addAction(aMoveBottomToPlay); - bottomLibraryMenu->addAction(aMoveBottomToPlayFaceDown); - bottomLibraryMenu->addAction(aMoveBottomCardToTop); - bottomLibraryMenu->addSeparator(); - bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard); - bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard); - bottomLibraryMenu->addAction(aMoveBottomCardToExile); - bottomLibraryMenu->addAction(aMoveBottomCardsToExile); - bottomLibraryMenu->addSeparator(); - bottomLibraryMenu->addAction(aShuffleBottomCards); - } - - graveMenu = playerMenu->addTearOffMenu(QString()); - graveMenu->addAction(aViewGraveyard); - - if (local || judge) { - mRevealRandomGraveyardCard = graveMenu->addMenu(QString()); - QAction *newAction = mRevealRandomGraveyardCard->addAction(QString()); - newAction->setData(-1); - connect(newAction, &QAction::triggered, this, &Player::actRevealRandomGraveyardCard); - allPlayersActions.append(newAction); - mRevealRandomGraveyardCard->addSeparator(); - } - grave->setMenu(graveMenu, aViewGraveyard); - - rfgMenu = playerMenu->addTearOffMenu(QString()); - rfgMenu->addAction(aViewRfg); - rfg->setMenu(rfgMenu, aViewRfg); - - if (local || judge) { - graveMenu->addSeparator(); - moveGraveMenu = graveMenu->addTearOffMenu(QString()); - moveGraveMenu->addAction(aMoveGraveToTopLibrary); - moveGraveMenu->addAction(aMoveGraveToBottomLibrary); - moveGraveMenu->addSeparator(); - moveGraveMenu->addAction(aMoveGraveToHand); - moveGraveMenu->addSeparator(); - moveGraveMenu->addAction(aMoveGraveToRfg); - - rfgMenu->addSeparator(); - moveRfgMenu = rfgMenu->addTearOffMenu(QString()); - moveRfgMenu->addAction(aMoveRfgToTopLibrary); - moveRfgMenu->addAction(aMoveRfgToBottomLibrary); - moveRfgMenu->addSeparator(); - moveRfgMenu->addAction(aMoveRfgToHand); - moveRfgMenu->addSeparator(); - moveRfgMenu->addAction(aMoveRfgToGrave); - - sbMenu = playerMenu->addMenu(QString()); - sbMenu->addAction(aViewSideboard); - sb->setMenu(sbMenu, aViewSideboard); - - mCustomZones = playerMenu->addMenu(QString()); - mCustomZones->menuAction()->setVisible(false); - - aUntapAll = new QAction(this); - connect(aUntapAll, &QAction::triggered, this, &Player::actUntapAll); - - aRollDie = new QAction(this); - connect(aRollDie, &QAction::triggered, this, &Player::actRollDie); - - aCreateToken = new QAction(this); - connect(aCreateToken, &QAction::triggered, this, &Player::actCreateToken); - - aCreateAnotherToken = new QAction(this); - connect(aCreateAnotherToken, &QAction::triggered, this, &Player::actCreateAnotherToken); - aCreateAnotherToken->setEnabled(false); - - aIncrementAllCardCounters = new QAction(this); - connect(aIncrementAllCardCounters, &QAction::triggered, this, &Player::incrementAllCardCounters); - - createPredefinedTokenMenu = new QMenu(QString()); - createPredefinedTokenMenu->setEnabled(false); - - mCardCounters = new QMenu; - - playerMenu->addSeparator(); - countersMenu = playerMenu->addMenu(QString()); - playerMenu->addAction(aIncrementAllCardCounters); - playerMenu->addSeparator(); - playerMenu->addAction(aUntapAll); - playerMenu->addSeparator(); - playerMenu->addAction(aRollDie); - playerMenu->addSeparator(); - playerMenu->addAction(aCreateToken); - playerMenu->addAction(aCreateAnotherToken); - playerMenu->addMenu(createPredefinedTokenMenu); - playerMenu->addSeparator(); - } - - if (local) { - sayMenu = playerMenu->addMenu(QString()); - connect(&SettingsCache::instance().messages(), &MessageSettings::messageMacrosChanged, this, - &Player::initSayMenu); - initSayMenu(); - } - - if (local || judge) { - - for (auto &playerList : playerLists) { - QAction *newAction = playerList->addAction(QString()); - newAction->setData(-1); - connect(newAction, &QAction::triggered, this, &Player::playerListActionTriggered); - allPlayersActions.append(newAction); - playerList->addSeparator(); - } - } - - if (!local && !judge) { - countersMenu = nullptr; - sbMenu = nullptr; - mCustomZones = nullptr; - aCreateAnotherToken = nullptr; - createPredefinedTokenMenu = nullptr; - mCardCounters = nullptr; - } - - aTap = new QAction(this); - aTap->setData(cmTap); - connect(aTap, &QAction::triggered, this, &Player::cardMenuAction); - aDoesntUntap = new QAction(this); - aDoesntUntap->setData(cmDoesntUntap); - connect(aDoesntUntap, &QAction::triggered, this, &Player::cardMenuAction); - aAttach = new QAction(this); - connect(aAttach, &QAction::triggered, this, &Player::actAttach); - aUnattach = new QAction(this); - connect(aUnattach, &QAction::triggered, this, &Player::actUnattach); - aDrawArrow = new QAction(this); - connect(aDrawArrow, &QAction::triggered, this, &Player::actDrawArrow); - aIncP = new QAction(this); - connect(aIncP, &QAction::triggered, this, &Player::actIncP); - aDecP = new QAction(this); - connect(aDecP, &QAction::triggered, this, &Player::actDecP); - aIncT = new QAction(this); - connect(aIncT, &QAction::triggered, this, &Player::actIncT); - aDecT = new QAction(this); - connect(aDecT, &QAction::triggered, this, &Player::actDecT); - aIncPT = new QAction(this); - connect(aIncPT, &QAction::triggered, this, [this] { actIncPT(); }); - aDecPT = new QAction(this); - connect(aDecPT, &QAction::triggered, this, &Player::actDecPT); - aFlowP = new QAction(this); - connect(aFlowP, &QAction::triggered, this, &Player::actFlowP); - aFlowT = new QAction(this); - connect(aFlowT, &QAction::triggered, this, &Player::actFlowT); - aSetPT = new QAction(this); - connect(aSetPT, &QAction::triggered, this, &Player::actSetPT); - aResetPT = new QAction(this); - connect(aResetPT, &QAction::triggered, this, &Player::actResetPT); - aSetAnnotation = new QAction(this); - connect(aSetAnnotation, &QAction::triggered, this, &Player::actSetAnnotation); - aFlip = new QAction(this); - aFlip->setData(cmFlip); - connect(aFlip, &QAction::triggered, this, &Player::cardMenuAction); - aPeek = new QAction(this); - aPeek->setData(cmPeek); - connect(aPeek, &QAction::triggered, this, &Player::cardMenuAction); - aClone = new QAction(this); - aClone->setData(cmClone); - connect(aClone, &QAction::triggered, this, &Player::cardMenuAction); - aMoveToTopLibrary = new QAction(this); - aMoveToTopLibrary->setData(cmMoveToTopLibrary); - aMoveToBottomLibrary = new QAction(this); - aMoveToBottomLibrary->setData(cmMoveToBottomLibrary); - aMoveToXfromTopOfLibrary = new QAction(this); - aMoveToGraveyard = new QAction(this); - aMoveToHand = new QAction(this); - aMoveToHand->setData(cmMoveToHand); - aMoveToGraveyard->setData(cmMoveToGraveyard); - aMoveToExile = new QAction(this); - aMoveToExile->setData(cmMoveToExile); - connect(aMoveToTopLibrary, &QAction::triggered, this, &Player::cardMenuAction); - connect(aMoveToBottomLibrary, &QAction::triggered, this, &Player::cardMenuAction); - connect(aMoveToXfromTopOfLibrary, &QAction::triggered, this, &Player::actMoveCardXCardsFromTop); - connect(aMoveToHand, &QAction::triggered, this, &Player::cardMenuAction); - connect(aMoveToGraveyard, &QAction::triggered, this, &Player::cardMenuAction); - connect(aMoveToExile, &QAction::triggered, this, &Player::cardMenuAction); - - aSelectAll = new QAction(this); - connect(aSelectAll, &QAction::triggered, this, &Player::actSelectAll); - aSelectRow = new QAction(this); - connect(aSelectRow, &QAction::triggered, this, &Player::actSelectRow); - aSelectColumn = new QAction(this); - connect(aSelectColumn, &QAction::triggered, this, &Player::actSelectColumn); - - aPlay = new QAction(this); - connect(aPlay, &QAction::triggered, this, &Player::actPlay); - aHide = new QAction(this); - connect(aHide, &QAction::triggered, this, &Player::actHide); - aPlayFacedown = new QAction(this); - connect(aPlayFacedown, &QAction::triggered, this, &Player::actPlayFacedown); - - for (int i = 0; i < 6; ++i) { - auto *tempAddCounter = new QAction(this); - tempAddCounter->setData(9 + i * 1000); - auto *tempRemoveCounter = new QAction(this); - tempRemoveCounter->setData(10 + i * 1000); - auto *tempSetCounter = new QAction(this); - tempSetCounter->setData(11 + i * 1000); - aAddCounter.append(tempAddCounter); - aRemoveCounter.append(tempRemoveCounter); - aSetCounter.append(tempSetCounter); - connect(tempAddCounter, &QAction::triggered, this, &Player::actCardCounterTrigger); - connect(tempRemoveCounter, &QAction::triggered, this, &Player::actCardCounterTrigger); - connect(tempSetCounter, &QAction::triggered, this, &Player::actCardCounterTrigger); - } - - const QList &players = game->getGameState()->getPlayers().values(); - for (const auto player : players) { - addPlayer(player); - } - - moveTopCardTimer = new QTimer(this); - moveTopCardTimer->setInterval(MOVE_TOP_CARD_UNTIL_INTERVAL); - moveTopCardTimer->setSingleShot(true); - connect(moveTopCardTimer, &QTimer::timeout, [this]() { actMoveTopCardToPlay(); }); - - rearrangeZones(); - retranslateUi(); - connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, - &Player::refreshShortcuts); - refreshShortcuts(); + addZone(new PileZoneLogic(this, "deck", false, true, false, this)); + addZone(new PileZoneLogic(this, "grave", false, false, true, this)); + addZone(new PileZoneLogic(this, "rfg", false, false, true, this)); + addZone(new PileZoneLogic(this, "sb", false, false, false, this)); + addZone(new TableZoneLogic(this, "table", true, false, true, this)); + addZone(new StackZoneLogic(this, "stack", true, false, true, this)); + addZone(new HandZoneLogic(this, "hand", false, false, true, this)); } Player::~Player() { - qCInfo(PlayerLog) << "Player destructor:" << getName(); + qCInfo(PlayerLog) << "Player destructor:" << getPlayerInfo()->getName(); - QMapIterator i(zones); + QMapIterator i(zones); while (i.hasNext()) delete i.next().value(); zones.clear(); delete playerMenu; - delete userInfo; + delete getPlayerInfo()->userInfo; } void Player::clear() { clearArrows(); - QMapIterator i(zones); + QMapIterator i(zones); while (i.hasNext()) { i.next().value()->clearContents(); } @@ -603,2146 +84,23 @@ void Player::clear() clearCounters(); } -void Player::addPlayer(Player *player) +void Player::setConceded(bool _conceded) { - if (player == nullptr || player == this) { - return; - } + if (conceded != _conceded) { + conceded = _conceded; - for (auto &playerList : playerLists) { - addPlayerToList(playerList, player); - } - for (auto &playerList : singlePlayerLists) { - addPlayerToList(playerList, player); - } - - playersInfo.append(qMakePair(player->getName(), player->getId())); -} - -void Player::addPlayerToList(QMenu *playerList, Player *player) -{ - QAction *newAction = playerList->addAction(player->getName()); - newAction->setData(player->getId()); - connect(newAction, &QAction::triggered, this, &Player::playerListActionTriggered); -} - -void Player::removePlayer(Player *player) -{ - if (player == nullptr) { - return; - } - - for (auto &playerList : playerLists) { - removePlayerFromList(playerList, player); - } - for (auto &playerList : singlePlayerLists) { - removePlayerFromList(playerList, player); - } - - for (auto it = playersInfo.begin(); it != playersInfo.end();) { - if (it->second == player->getId()) { - it = playersInfo.erase(it); - } else { - ++it; + getGraphicsItem()->setVisible(!conceded); + if (conceded) { + clear(); } + emit concededChanged(getPlayerInfo()->getId(), conceded); } } -void Player::removePlayerFromList(QMenu *playerList, Player *player) -{ - QList actionList = playerList->actions(); - for (auto &j : actionList) - if (j->data().toInt() == player->getId()) { - playerList->removeAction(j); - j->deleteLater(); - } -} - -void Player::playerListActionTriggered() -{ - auto *action = static_cast(sender()); - auto *menu = static_cast(action->parent()); - - Command_RevealCards cmd; - const int otherPlayerId = action->data().toInt(); - if (otherPlayerId != -1) { - cmd.set_player_id(otherPlayerId); - } - - if (menu == mRevealLibrary || menu == mLendLibrary) { - cmd.set_zone_name("deck"); - cmd.set_grant_write_access(menu == mLendLibrary); - } else if (menu == mRevealTopCard) { - int deckSize = zones.value("deck")->getCards().size(); - bool ok; - int number = QInputDialog::getInt(game, tr("Reveal top cards of library"), - tr("Number of cards: (max. %1)").arg(deckSize), defaultNumberTopCards, 1, - deckSize, 1, &ok); - if (ok) { - cmd.set_zone_name("deck"); - cmd.set_top_cards(number); - // backward compatibility: servers before #1051 only permits to reveal the first card - cmd.add_card_id(0); - defaultNumberTopCards = number; - } - } else if (menu == mRevealHand) { - cmd.set_zone_name("hand"); - } else if (menu == mRevealRandomHandCard) { - cmd.set_zone_name("hand"); - cmd.add_card_id(RANDOM_CARD_FROM_ZONE); - } else { - return; - } - - sendGameCommand(cmd); -} - -void Player::rearrangeZones() -{ - QPointF base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0); - if (SettingsCache::instance().getHorizontalHand()) { - if (mirrored) { - if (hand->contentsKnown()) { - handVisible = true; - hand->setPos(base); - base += QPointF(0, hand->boundingRect().height()); - } else - handVisible = false; - - stack->setPos(base); - base += QPointF(stack->boundingRect().width(), 0); - - table->setPos(base); - } else { - stack->setPos(base); - - table->setPos(base.x() + stack->boundingRect().width(), 0); - base += QPointF(0, table->boundingRect().height()); - - if (hand->contentsKnown()) { - handVisible = true; - hand->setPos(base); - } else - handVisible = false; - } - hand->setWidth(table->getWidth() + stack->boundingRect().width()); - } else { - handVisible = true; - - hand->setPos(base); - base += QPointF(hand->boundingRect().width(), 0); - - stack->setPos(base); - base += QPointF(stack->boundingRect().width(), 0); - - table->setPos(base); - } - hand->setVisible(handVisible); - hand->updateOrientation(); - table->reorganizeCards(); - updateBoundingRect(); - rearrangeCounters(); -} - -void Player::updateZones() -{ - table->reorganizeCards(); -} - -void Player::updateBoundingRect() -{ - prepareGeometryChange(); - qreal width = CARD_HEIGHT + 15 + counterAreaWidth + stack->boundingRect().width(); - if (SettingsCache::instance().getHorizontalHand()) { - qreal handHeight = handVisible ? hand->boundingRect().height() : 0; - bRect = QRectF(0, 0, width + table->boundingRect().width(), table->boundingRect().height() + handHeight); - } else { - bRect = QRectF(0, 0, width + hand->boundingRect().width() + table->boundingRect().width(), - table->boundingRect().height()); - } - playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height()); - - emit sizeChanged(); -} - -void Player::retranslateUi() -{ - aViewGraveyard->setText(tr("&View graveyard")); - aViewRfg->setText(tr("&View exile")); - - playerMenu->setTitle(tr("Player \"%1\"").arg(QString::fromStdString(userInfo->name()))); - graveMenu->setTitle(tr("&Graveyard")); - rfgMenu->setTitle(tr("&Exile")); - - if (local || judge) { - moveHandMenu->setTitle(tr("&Move hand to...")); - aMoveHandToTopLibrary->setText(tr("&Top of library")); - aMoveHandToBottomLibrary->setText(tr("&Bottom of library")); - aMoveHandToGrave->setText(tr("&Graveyard")); - aMoveHandToRfg->setText(tr("&Exile")); - - moveGraveMenu->setTitle(tr("&Move graveyard to...")); - aMoveGraveToTopLibrary->setText(tr("&Top of library")); - aMoveGraveToBottomLibrary->setText(tr("&Bottom of library")); - aMoveGraveToHand->setText(tr("&Hand")); - aMoveGraveToRfg->setText(tr("&Exile")); - - moveRfgMenu->setTitle(tr("&Move exile to...")); - aMoveRfgToTopLibrary->setText(tr("&Top of library")); - aMoveRfgToBottomLibrary->setText(tr("&Bottom of library")); - aMoveRfgToHand->setText(tr("&Hand")); - aMoveRfgToGrave->setText(tr("&Graveyard")); - - aViewLibrary->setText(tr("&View library")); - aViewHand->setText(tr("&View hand")); - aSortHand->setText(tr("&Sort hand")); - aViewTopCards->setText(tr("View &top cards of library...")); - aViewBottomCards->setText(tr("View bottom cards of library...")); - mRevealLibrary->setTitle(tr("Reveal &library to...")); - mLendLibrary->setTitle(tr("Lend library to...")); - mRevealTopCard->setTitle(tr("Reveal &top cards to...")); - topLibraryMenu->setTitle(tr("&Top of library...")); - bottomLibraryMenu->setTitle(tr("&Bottom of library...")); - aAlwaysRevealTopCard->setText(tr("&Always reveal top card")); - aAlwaysLookAtTopCard->setText(tr("&Always look at top card")); - aOpenDeckInDeckEditor->setText(tr("&Open deck in deck editor")); - aViewSideboard->setText(tr("&View sideboard")); - aDrawCard->setText(tr("&Draw card")); - aDrawCards->setText(tr("D&raw cards...")); - aUndoDraw->setText(tr("&Undo last draw")); - aMulligan->setText(tr("Take &mulligan")); - - aShuffle->setText(tr("Shuffle")); - - aMoveTopToPlay->setText(tr("&Play top card")); - aMoveTopToPlayFaceDown->setText(tr("Play top card &face down")); - aMoveTopCardToBottom->setText(tr("Put top card on &bottom")); - aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard")); - aMoveTopCardToExile->setText(tr("Move top card to e&xile")); - aMoveTopCardsToGraveyard->setText(tr("Move top cards to &graveyard...")); - aMoveTopCardsToExile->setText(tr("Move top cards to &exile...")); - aMoveTopCardsUntil->setText(tr("Put top cards on stack &until...")); - aShuffleTopCards->setText(tr("Shuffle top cards...")); - - aDrawBottomCard->setText(tr("&Draw bottom card")); - aDrawBottomCards->setText(tr("D&raw bottom cards...")); - aMoveBottomToPlay->setText(tr("&Play bottom card")); - aMoveBottomToPlayFaceDown->setText(tr("Play bottom card &face down")); - aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard")); - aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile")); - aMoveBottomCardsToGraveyard->setText(tr("Move bottom cards to &graveyard...")); - aMoveBottomCardsToExile->setText(tr("Move bottom cards to &exile...")); - aMoveBottomCardToTop->setText(tr("Put bottom card on &top")); - aShuffleBottomCards->setText(tr("Shuffle bottom cards...")); - - handMenu->setTitle(tr("&Hand")); - mRevealHand->setTitle(tr("&Reveal hand to...")); - mRevealRandomHandCard->setTitle(tr("Reveal r&andom card to...")); - mRevealRandomGraveyardCard->setTitle(tr("Reveal random card to...")); - sbMenu->setTitle(tr("&Sideboard")); - libraryMenu->setTitle(tr("&Library")); - countersMenu->setTitle(tr("&Counters")); - mCustomZones->setTitle(tr("C&ustom Zones")); - - for (auto aViewZone : mCustomZones->actions()) { - aViewZone->setText(tr("View custom zone '%1'").arg(aViewZone->data().toString())); - } - - aIncrementAllCardCounters->setText(tr("Increment all card counters")); - aUntapAll->setText(tr("&Untap all permanents")); - aRollDie->setText(tr("R&oll die...")); - aCreateToken->setText(tr("&Create token...")); - aCreateAnotherToken->setText(tr("C&reate another token")); - createPredefinedTokenMenu->setTitle(tr("Cr&eate predefined token")); - - mCardCounters->setTitle(tr("Ca&rd counters")); - - QMapIterator counterIterator(counters); - while (counterIterator.hasNext()) { - counterIterator.next().value()->retranslateUi(); - } - - for (auto &allPlayersAction : allPlayersActions) { - allPlayersAction->setText(tr("&All players")); - } - } - - if (local) { - sayMenu->setTitle(tr("S&ay")); - } - - aSelectAll->setText(tr("&Select All")); - aSelectRow->setText(tr("S&elect Row")); - aSelectColumn->setText(tr("S&elect Column")); - - aPlay->setText(tr("&Play")); - aHide->setText(tr("&Hide")); - aPlayFacedown->setText(tr("Play &Face Down")); - //: Turn sideways or back again - aTap->setText(tr("&Tap / Untap")); - aDoesntUntap->setText(tr("Toggle &normal untapping")); - //: Turn face up/face down - aFlip->setText(tr("T&urn Over")); // Only the user facing names in client got renamed to "turn over" - // All code and proto bits are still unchanged (flip) for compatibility reasons - // A protocol rewrite with v3 could incorporate that, see #3100 - aPeek->setText(tr("&Peek at card face")); - aClone->setText(tr("&Clone")); - aAttach->setText(tr("Attac&h to card...")); - aUnattach->setText(tr("Unattac&h")); - aDrawArrow->setText(tr("&Draw arrow...")); - aIncP->setText(tr("&Increase power")); - aDecP->setText(tr("&Decrease power")); - aIncT->setText(tr("I&ncrease toughness")); - aDecT->setText(tr("D&ecrease toughness")); - aIncPT->setText(tr("In&crease power and toughness")); - aDecPT->setText(tr("Dec&rease power and toughness")); - aFlowP->setText(tr("Increase power and decrease toughness")); - aFlowT->setText(tr("Decrease power and increase toughness")); - aSetPT->setText(tr("Set &power and toughness...")); - aResetPT->setText(tr("Reset p&ower and toughness")); - aSetAnnotation->setText(tr("&Set annotation...")); - - auto &cardCounterSettings = SettingsCache::instance().cardCounters(); - - for (int i = 0; i < aAddCounter.size(); ++i) { - aAddCounter[i]->setText(tr("&Add counter (%1)").arg(cardCounterSettings.displayName(i))); - } - for (int i = 0; i < aRemoveCounter.size(); ++i) { - aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(cardCounterSettings.displayName(i))); - } - for (int i = 0; i < aSetCounter.size(); ++i) { - aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(cardCounterSettings.displayName(i))); - } - - aMoveToTopLibrary->setText(tr("&Top of library in random order")); - aMoveToXfromTopOfLibrary->setText(tr("X cards from the top of library...")); - aMoveToBottomLibrary->setText(tr("&Bottom of library in random order")); - aMoveToHand->setText(tr("&Hand")); - aMoveToGraveyard->setText(tr("&Graveyard")); - aMoveToExile->setText(tr("&Exile")); - - QMapIterator zoneIterator(zones); - while (zoneIterator.hasNext()) { - zoneIterator.next().value()->retranslateUi(); - } -} - -void Player::setShortcutsActive() -{ - shortcutsActive = true; - ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); - - aHide->setShortcuts(shortcuts.getShortcut("Player/aHide")); - aPlay->setShortcuts(shortcuts.getShortcut("Player/aPlay")); - aTap->setShortcuts(shortcuts.getShortcut("Player/aTap")); - aDoesntUntap->setShortcuts(shortcuts.getShortcut("Player/aDoesntUntap")); - aFlip->setShortcuts(shortcuts.getShortcut("Player/aFlip")); - aPeek->setShortcuts(shortcuts.getShortcut("Player/aPeek")); - aClone->setShortcuts(shortcuts.getShortcut("Player/aClone")); - aAttach->setShortcuts(shortcuts.getShortcut("Player/aAttach")); - aUnattach->setShortcuts(shortcuts.getShortcut("Player/aUnattach")); - aDrawArrow->setShortcuts(shortcuts.getShortcut("Player/aDrawArrow")); - aIncP->setShortcuts(shortcuts.getShortcut("Player/aIncP")); - aDecP->setShortcuts(shortcuts.getShortcut("Player/aDecP")); - aIncT->setShortcuts(shortcuts.getShortcut("Player/aIncT")); - aDecT->setShortcuts(shortcuts.getShortcut("Player/aDecT")); - aIncPT->setShortcuts(shortcuts.getShortcut("Player/aIncPT")); - aDecPT->setShortcuts(shortcuts.getShortcut("Player/aDecPT")); - aFlowP->setShortcuts(shortcuts.getShortcut("Player/aFlowP")); - aFlowT->setShortcuts(shortcuts.getShortcut("Player/aFlowT")); - aSetPT->setShortcuts(shortcuts.getShortcut("Player/aSetPT")); - aResetPT->setShortcuts(shortcuts.getShortcut("Player/aResetPT")); - aSetAnnotation->setShortcuts(shortcuts.getShortcut("Player/aSetAnnotation")); - aMoveToTopLibrary->setShortcuts(shortcuts.getShortcut("Player/aMoveToTopLibrary")); - aMoveToBottomLibrary->setShortcuts(shortcuts.getShortcut("Player/aMoveToBottomLibrary")); - aMoveToHand->setShortcuts(shortcuts.getShortcut("Player/aMoveToHand")); - aMoveToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveToGraveyard")); - aMoveToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveToExile")); - aSortHand->setShortcuts(shortcuts.getShortcut("Player/aSortHand")); - - aSelectAll->setShortcuts(shortcuts.getShortcut("Player/aSelectAll")); - aSelectRow->setShortcuts(shortcuts.getShortcut("Player/aSelectRow")); - aSelectColumn->setShortcuts(shortcuts.getShortcut("Player/aSelectColumn")); - - static const QStringList colorWords = {"Red", "Yellow", "Green", "Cyan", "Purple", "Magenta"}; - for (int i = 0; i < aAddCounter.size(); i++) { - aAddCounter[i]->setShortcuts(shortcuts.getShortcut("Player/aCC" + colorWords[i])); - aRemoveCounter[i]->setShortcuts(shortcuts.getShortcut("Player/aRC" + colorWords[i])); - aSetCounter[i]->setShortcuts(shortcuts.getShortcut("Player/aSC" + colorWords[i])); - } - - QMapIterator counterIterator(counters); - while (counterIterator.hasNext()) { - counterIterator.next().value()->setShortcutsActive(); - } - - aIncrementAllCardCounters->setShortcuts(shortcuts.getShortcut("Player/aIncrementAllCardCounters")); - aViewSideboard->setShortcuts(shortcuts.getShortcut("Player/aViewSideboard")); - aViewLibrary->setShortcuts(shortcuts.getShortcut("Player/aViewLibrary")); - aViewHand->setShortcuts(shortcuts.getShortcut("Player/aViewHand")); - aViewTopCards->setShortcuts(shortcuts.getShortcut("Player/aViewTopCards")); - aViewBottomCards->setShortcuts(shortcuts.getShortcut("Player/aViewBottomCards")); - aViewGraveyard->setShortcuts(shortcuts.getShortcut("Player/aViewGraveyard")); - aDrawCard->setShortcuts(shortcuts.getShortcut("Player/aDrawCard")); - aDrawCards->setShortcuts(shortcuts.getShortcut("Player/aDrawCards")); - aUndoDraw->setShortcuts(shortcuts.getShortcut("Player/aUndoDraw")); - aMulligan->setShortcuts(shortcuts.getShortcut("Player/aMulligan")); - aShuffle->setShortcuts(shortcuts.getShortcut("Player/aShuffle")); - aShuffleTopCards->setShortcuts(shortcuts.getShortcut("Player/aShuffleTopCards")); - aShuffleBottomCards->setShortcuts(shortcuts.getShortcut("Player/aShuffleBottomCards")); - aUntapAll->setShortcuts(shortcuts.getShortcut("Player/aUntapAll")); - aRollDie->setShortcuts(shortcuts.getShortcut("Player/aRollDie")); - aCreateToken->setShortcuts(shortcuts.getShortcut("Player/aCreateToken")); - aCreateAnotherToken->setShortcuts(shortcuts.getShortcut("Player/aCreateAnotherToken")); - aAlwaysRevealTopCard->setShortcuts(shortcuts.getShortcut("Player/aAlwaysRevealTopCard")); - aAlwaysLookAtTopCard->setShortcuts(shortcuts.getShortcut("Player/aAlwaysLookAtTopCard")); - aMoveTopToPlay->setShortcuts(shortcuts.getShortcut("Player/aMoveTopToPlay")); - aMoveTopToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopToPlayFaceDown")); - aMoveTopCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToGraveyard")); - aMoveTopCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyard")); - aMoveTopCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToExile")); - aMoveTopCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExile")); - aMoveTopCardsUntil->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsUntil")); - aMoveTopCardToBottom->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToBottom")); - aDrawBottomCard->setShortcuts(shortcuts.getShortcut("Player/aDrawBottomCard")); - aDrawBottomCards->setShortcuts(shortcuts.getShortcut("Player/aDrawBottomCards")); - aMoveBottomToPlay->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomToPlay")); - aMoveBottomToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomToPlayFaceDown")); - aMoveBottomCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToGrave")); - aMoveBottomCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGrave")); - aMoveBottomCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToExile")); - aMoveBottomCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExile")); - aMoveBottomCardToTop->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToTop")); - aPlayFacedown->setShortcuts(shortcuts.getShortcut("Player/aPlayFacedown")); - aPlay->setShortcuts(shortcuts.getShortcut("Player/aPlay")); - - // Don't enable always-active shortcuts in local games, since it causes keyboard shortcuts to work inconsistently - // when there are more than 1 player. - if (!game->getGameState()->getIsLocalGame()) { - // unattach action is only active in card menu if the active card is attached. - // make unattach shortcut always active so that it consistently works when multiple cards are selected. - game->addAction(aUnattach); - } -} - -void Player::setShortcutsInactive() -{ - shortcutsActive = false; - - aViewSideboard->setShortcut(QKeySequence()); - aViewLibrary->setShortcut(QKeySequence()); - aViewHand->setShortcut(QKeySequence()); - aViewTopCards->setShortcut(QKeySequence()); - aViewBottomCards->setShortcut(QKeySequence()); - aViewGraveyard->setShortcut(QKeySequence()); - aDrawCard->setShortcut(QKeySequence()); - aDrawCards->setShortcut(QKeySequence()); - aUndoDraw->setShortcut(QKeySequence()); - aMulligan->setShortcut(QKeySequence()); - aShuffle->setShortcut(QKeySequence()); - aShuffleTopCards->setShortcut(QKeySequence()); - aShuffleBottomCards->setShortcut(QKeySequence()); - aUntapAll->setShortcut(QKeySequence()); - aRollDie->setShortcut(QKeySequence()); - aCreateToken->setShortcut(QKeySequence()); - aCreateAnotherToken->setShortcut(QKeySequence()); - aAlwaysRevealTopCard->setShortcut(QKeySequence()); - aAlwaysLookAtTopCard->setShortcut(QKeySequence()); - aMoveTopToPlay->setShortcut(QKeySequence()); - aMoveTopToPlayFaceDown->setShortcut(QKeySequence()); - aMoveTopCardToGraveyard->setShortcut(QKeySequence()); - aMoveTopCardsToGraveyard->setShortcut(QKeySequence()); - aMoveTopCardToExile->setShortcut(QKeySequence()); - aMoveTopCardsToExile->setShortcut(QKeySequence()); - aMoveTopCardsUntil->setShortcut(QKeySequence()); - aDrawBottomCard->setShortcut(QKeySequence()); - aDrawBottomCards->setShortcut(QKeySequence()); - aMoveBottomToPlay->setShortcut(QKeySequence()); - aMoveBottomToPlayFaceDown->setShortcut(QKeySequence()); - aMoveBottomCardToGraveyard->setShortcut(QKeySequence()); - aMoveBottomCardsToGraveyard->setShortcut(QKeySequence()); - aMoveBottomCardToExile->setShortcut(QKeySequence()); - aMoveBottomCardsToExile->setShortcut(QKeySequence()); - aIncrementAllCardCounters->setShortcut(QKeySequence()); - aSortHand->setShortcut(QKeySequence()); - - QMapIterator counterIterator(counters); - while (counterIterator.hasNext()) { - counterIterator.next().value()->setShortcutsInactive(); - } -} - -void Player::initSayMenu() -{ - sayMenu->clear(); - - int count = SettingsCache::instance().messages().getCount(); - sayMenu->setEnabled(count > 0); - - for (int i = 0; i < count; ++i) { - auto *newAction = new QAction(SettingsCache::instance().messages().getMessageAt(i), sayMenu); - if (i < 10) { - newAction->setShortcut(QKeySequence("Ctrl+" + QString::number((i + 1) % 10))); - } - connect(newAction, &QAction::triggered, this, &Player::actSayMessage); - sayMenu->addAction(newAction); - } -} - -void Player::initContextualPlayersMenu(QMenu *menu) -{ - menu->addAction(tr("&All players"))->setData(-1); - menu->addSeparator(); - - for (const auto &playerInfo : playersInfo) { - menu->addAction(playerInfo.first)->setData(playerInfo.second); - } -} - -void Player::setDeck(const DeckLoader &_deck) -{ - deck = new DeckLoader(_deck); - aOpenDeckInDeckEditor->setEnabled(deck); - - createPredefinedTokenMenu->clear(); - createPredefinedTokenMenu->setEnabled(false); - predefinedTokens.clear(); - InnerDecklistNode *tokenZone = dynamic_cast(deck->getRoot()->findChild(DECK_ZONE_TOKENS)); - - if (tokenZone) { - if (tokenZone->size() > 0) - createPredefinedTokenMenu->setEnabled(true); - - for (int i = 0; i < tokenZone->size(); ++i) { - const QString tokenName = tokenZone->at(i)->getName(); - predefinedTokens.append(tokenName); - QAction *a = createPredefinedTokenMenu->addAction(tokenName); - if (i < 10) { - a->setShortcut(QKeySequence("Alt+" + QString::number((i + 1) % 10))); - } - connect(a, &QAction::triggered, this, &Player::actCreatePredefinedToken); - } - } -} - -void Player::actViewLibrary() -{ - static_cast(scene())->toggleZoneView(this, "deck", -1); -} - -void Player::actViewHand() -{ - static_cast(scene())->toggleZoneView(this, "hand", -1); -} - -void Player::actSortHand() -{ - hand->sortHand(); -} - -void Player::actViewTopCards() -{ - int deckSize = zones.value("deck")->getCards().size(); - bool ok; - int number = - QInputDialog::getInt(game, tr("View top cards of library"), tr("Number of cards: (max. %1)").arg(deckSize), - defaultNumberTopCards, 1, deckSize, 1, &ok); - if (ok) { - defaultNumberTopCards = number; - static_cast(scene())->toggleZoneView(this, "deck", number); - } -} - -void Player::actViewBottomCards() -{ - int deckSize = zones.value("deck")->getCards().size(); - bool ok; - int number = - QInputDialog::getInt(game, tr("View bottom cards of library"), tr("Number of cards: (max. %1)").arg(deckSize), - defaultNumberBottomCards, 1, deckSize, 1, &ok); - if (ok) { - defaultNumberBottomCards = number; - static_cast(scene())->toggleZoneView(this, "deck", number, true); - } -} - -void Player::actAlwaysRevealTopCard() -{ - Command_ChangeZoneProperties cmd; - cmd.set_zone_name("deck"); - cmd.set_always_reveal_top_card(aAlwaysRevealTopCard->isChecked()); - - sendGameCommand(cmd); -} - -void Player::actAlwaysLookAtTopCard() -{ - Command_ChangeZoneProperties cmd; - cmd.set_zone_name("deck"); - cmd.set_always_look_at_top_card(aAlwaysLookAtTopCard->isChecked()); - - sendGameCommand(cmd); -} - -void Player::actOpenDeckInDeckEditor() -{ - emit openDeckEditor(deck); -} - -void Player::actViewGraveyard() -{ - dynamic_cast(scene())->toggleZoneView(this, "grave", -1); -} - -void Player::actRevealRandomGraveyardCard() -{ - Command_RevealCards cmd; - auto *action = dynamic_cast(sender()); - const int otherPlayerId = action->data().toInt(); - if (otherPlayerId != -1) { - cmd.set_player_id(otherPlayerId); - } - cmd.set_zone_name("grave"); - cmd.add_card_id(RANDOM_CARD_FROM_ZONE); - sendGameCommand(cmd); -} - -void Player::actViewRfg() -{ - static_cast(scene())->toggleZoneView(this, "rfg", -1); -} - -void Player::actViewSideboard() -{ - static_cast(scene())->toggleZoneView(this, "sb", -1); -} - -void Player::actShuffle() -{ - sendGameCommand(Command_Shuffle()); -} - -void Player::actShuffleTop() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = - QInputDialog::getInt(game, tr("Shuffle top cards of library"), tr("Number of cards: (max. %1)").arg(maxCards), - defaultNumberTopCards, 1, maxCards, 1, &ok); - if (!ok) { - return; - } - - if (number > maxCards) { - number = maxCards; - } - - defaultNumberTopCards = number; - - Command_Shuffle cmd; - cmd.set_zone_name("deck"); - cmd.set_start(0); - cmd.set_end(number - 1); // inclusive, the indexed card at end will be shuffled - - sendGameCommand(cmd); -} - -void Player::actShuffleBottom() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = QInputDialog::getInt(game, tr("Shuffle bottom cards of library"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, - maxCards, 1, &ok); - if (!ok) { - return; - } - - if (number > maxCards) { - number = maxCards; - } - - defaultNumberBottomCards = number; - - Command_Shuffle cmd; - cmd.set_zone_name("deck"); - cmd.set_start(-number); - cmd.set_end(-1); - - sendGameCommand(cmd); -} - -void Player::actDrawCard() -{ - Command_DrawCards cmd; - cmd.set_number(1); - sendGameCommand(cmd); -} - -void Player::actMulligan() -{ - int startSize = SettingsCache::instance().getStartingHandSize(); - int handSize = zones.value("hand")->getCards().size(); - int deckSize = zones.value("deck")->getCards().size() + handSize; // hand is shuffled back into the deck - bool ok; - int number = QInputDialog::getInt(game, tr("Draw hand"), - tr("Number of cards: (max. %1)").arg(deckSize) + '\n' + - tr("0 and lower are in comparison to current hand size"), - startSize, -handSize, deckSize, 1, &ok); - if (!ok) { - return; - } - Command_Mulligan cmd; - if (number < 1) { - if (handSize == 0) { - return; - } - cmd.set_number(handSize + number); - } else { - cmd.set_number(number); - } - sendGameCommand(cmd); - if (startSize != number) { - SettingsCache::instance().setStartingHandSize(number); - } -} - -void Player::actDrawCards() -{ - int deckSize = zones.value("deck")->getCards().size(); - bool ok; - int number = QInputDialog::getInt(game, tr("Draw cards"), tr("Number of cards: (max. %1)").arg(deckSize), - defaultNumberTopCards, 1, deckSize, 1, &ok); - if (ok) { - defaultNumberTopCards = number; - Command_DrawCards cmd; - cmd.set_number(static_cast(number)); - sendGameCommand(cmd); - } -} - -void Player::actUndoDraw() -{ - sendGameCommand(Command_UndoDraw()); -} - -void Player::cmdSetTopCard(Command_MoveCard &cmd) -{ - cmd.set_start_zone("deck"); - auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); - cardToMove->set_card_id(0); - cmd.set_target_player_id(getId()); -} - -void Player::cmdSetBottomCard(Command_MoveCard &cmd) -{ - CardZone *zone = zones.value("deck"); - int lastCard = zone->getCards().size() - 1; - cmd.set_start_zone("deck"); - auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); - cardToMove->set_card_id(lastCard); - cmd.set_target_player_id(getId()); -} - -void Player::actMoveTopCardToGrave() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetTopCard(cmd); - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveTopCardToExile() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetTopCard(cmd); - cmd.set_target_zone("rfg"); - cmd.set_x(0); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveTopCardsToGrave() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = - QInputDialog::getInt(game, tr("Move top cards to grave"), tr("Number of cards: (max. %1)").arg(maxCards), - defaultNumberTopCards, 1, maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberTopCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(getId()); - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = number - 1; i >= 0; --i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); -} - -void Player::actMoveTopCardsToExile() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = - QInputDialog::getInt(game, tr("Move top cards to exile"), tr("Number of cards: (max. %1)").arg(maxCards), - defaultNumberTopCards, 1, maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberTopCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(getId()); - cmd.set_target_zone("rfg"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = number - 1; i >= 0; --i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); -} - -void Player::actMoveTopCardsUntil() -{ - stopMoveTopCardsUntil(); - - DlgMoveTopCardsUntil dlg(game, movingCardsUntilExprs, movingCardsUntilNumberOfHits, movingCardsUntilAutoPlay); - if (!dlg.exec()) { - return; - } - - auto expr = dlg.getExpr(); - movingCardsUntilExprs = dlg.getExprs(); - movingCardsUntilNumberOfHits = dlg.getNumberOfHits(); - movingCardsUntilAutoPlay = dlg.isAutoPlay(); - - if (zones.value("deck")->getCards().empty()) { - stopMoveTopCardsUntil(); - } else { - movingCardsUntilFilter = FilterString(expr); - movingCardsUntilCounter = movingCardsUntilNumberOfHits; - movingCardsUntil = true; - actMoveTopCardToPlay(); - } -} - -void Player::moveOneCardUntil(CardItem *card) -{ - moveTopCardTimer->stop(); - - const bool isMatch = card && movingCardsUntilFilter.check(card->getCard().getCardPtr()); - - if (isMatch && movingCardsUntilAutoPlay) { - // Directly calling playCard will deadlock, since we are already in the middle of processing an event. - // Use QTimer::singleShot to queue up the playCard on the event loop. - QTimer::singleShot(0, this, [card, this] { playCard(card, false); }); - } - - if (zones.value("deck")->getCards().empty() || !card) { - stopMoveTopCardsUntil(); - } else if (isMatch) { - --movingCardsUntilCounter; - if (movingCardsUntilCounter > 0) { - moveTopCardTimer->start(); - } else { - stopMoveTopCardsUntil(); - } - } else { - moveTopCardTimer->start(); - } -} - -/** - * @brief Immediately stops any ongoing `play top card to stack until...` process, resetting all variables involved. - */ -void Player::stopMoveTopCardsUntil() -{ - moveTopCardTimer->stop(); - movingCardsUntilCounter = 0; - movingCardsUntil = false; -} - -void Player::actMoveTopCardToBottom() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetTopCard(cmd); - cmd.set_target_zone("deck"); - cmd.set_x(-1); // bottom of deck - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveTopCardToPlay() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetTopCard(cmd); - cmd.set_target_zone("stack"); - cmd.set_x(-1); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveTopCardToPlayFaceDown() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); - cardToMove->set_card_id(0); - cardToMove->set_face_down(true); - cmd.set_target_player_id(getId()); - cmd.set_target_zone("table"); - cmd.set_x(-1); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardToGrave() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetBottomCard(cmd); - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardToExile() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetBottomCard(cmd); - cmd.set_target_zone("rfg"); - cmd.set_x(0); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardsToGrave() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = - QInputDialog::getInt(game, tr("Move bottom cards to grave"), tr("Number of cards: (max. %1)").arg(maxCards), - defaultNumberBottomCards, 1, maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberBottomCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(getId()); - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = maxCards - number; i < maxCards; ++i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardsToExile() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = - QInputDialog::getInt(game, tr("Move bottom cards to exile"), tr("Number of cards: (max. %1)").arg(maxCards), - defaultNumberBottomCards, 1, maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberBottomCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(getId()); - cmd.set_target_zone("rfg"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = maxCards - number; i < maxCards; ++i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardToTop() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetBottomCard(cmd); - cmd.set_target_zone("deck"); - cmd.set_x(0); // top of deck - cmd.set_y(0); - - sendGameCommand(cmd); -} - -/** - * Selects all cards in the given zone. - * - * @param zone The zone to select from - * @param filter A predicate to filter which cards are selected. Defaults to always returning true. - */ -static void selectCardsInZone( - const CardZone *zone, - std::function filter = [](const CardItem *) { return true; }) -{ - if (!zone) { - return; - } - - for (auto &cardItem : zone->getCards()) { - if (cardItem && filter(cardItem)) { - cardItem->setSelected(true); - } - } -} - -void Player::actSelectAll() -{ - const CardItem *card = game->getActiveCard(); - if (!card) { - return; - } - - selectCardsInZone(card->getZone()); -} - -void Player::actSelectRow() -{ - const CardItem *card = game->getActiveCard(); - if (!card) { - return; - } - - auto isSameRow = [card](const CardItem *cardItem) { - return qAbs(card->scenePos().y() - cardItem->scenePos().y()) < 50; - }; - selectCardsInZone(card->getZone(), isSameRow); -} - -void Player::actSelectColumn() -{ - const CardItem *card = game->getActiveCard(); - if (!card) { - return; - } - - auto isSameColumn = [card](const CardItem *cardItem) { return cardItem->x() == card->x(); }; - selectCardsInZone(card->getZone(), isSameColumn); -} - -void Player::actDrawBottomCard() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetBottomCard(cmd); - cmd.set_target_zone("hand"); - cmd.set_x(0); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actDrawBottomCards() -{ - const int maxCards = zones.value("deck")->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = QInputDialog::getInt(game, tr("Draw bottom cards"), tr("Number of cards: (max. %1)").arg(maxCards), - defaultNumberBottomCards, 1, maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberBottomCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(getId()); - cmd.set_target_zone("hand"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = maxCards - number; i < maxCards; ++i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardToPlay() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - Command_MoveCard cmd; - cmdSetBottomCard(cmd); - cmd.set_target_zone("stack"); - cmd.set_x(-1); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actMoveBottomCardToPlayFaceDown() -{ - if (zones.value("deck")->getCards().empty()) { - return; - } - - CardZone *zone = zones.value("deck"); - int lastCard = zone->getCards().size() - 1; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); - cardToMove->set_card_id(lastCard); - cardToMove->set_face_down(true); - - cmd.set_target_player_id(getId()); - cmd.set_target_zone("table"); - cmd.set_x(-1); - cmd.set_y(0); - - sendGameCommand(cmd); -} - -void Player::actUntapAll() -{ - Command_SetCardAttr cmd; - cmd.set_zone("table"); - cmd.set_attribute(AttrTapped); - cmd.set_attr_value("0"); - - sendGameCommand(cmd); -} - -void Player::actRollDie() -{ - DlgRollDice dlg(game); - if (!dlg.exec()) { - return; - } - - Command_RollDie cmd; - cmd.set_sides(dlg.getDieSideCount()); - cmd.set_count(dlg.getDiceToRollCount()); - sendGameCommand(cmd); -} - -void Player::actCreateToken() -{ - DlgCreateToken dlg(predefinedTokens, game); - if (!dlg.exec()) { - return; - } - - lastTokenInfo = dlg.getTokenInfo(); - - ExactCard correctedCard = - CardDatabaseManager::getInstance()->guessCard({lastTokenInfo.name, lastTokenInfo.providerId}); - if (correctedCard) { - lastTokenInfo.name = correctedCard.getName(); - lastTokenTableRow = TableZone::clampValidTableRow(2 - correctedCard.getInfo().getTableRow()); - if (lastTokenInfo.pt.isEmpty()) { - lastTokenInfo.pt = correctedCard.getInfo().getPowTough(); - } - } - - aCreateAnotherToken->setEnabled(true); - aCreateAnotherToken->setText(tr("C&reate another %1 token").arg(lastTokenInfo.name)); - actCreateAnotherToken(); -} - -void Player::actCreateAnotherToken() -{ - if (lastTokenInfo.name.isEmpty()) { - return; - } - - Command_CreateToken cmd; - cmd.set_zone("table"); - cmd.set_card_name(lastTokenInfo.name.toStdString()); - cmd.set_card_provider_id(lastTokenInfo.providerId.toStdString()); - cmd.set_color(lastTokenInfo.color.toStdString()); - cmd.set_pt(lastTokenInfo.pt.toStdString()); - cmd.set_annotation(lastTokenInfo.annotation.toStdString()); - cmd.set_destroy_on_zone_change(lastTokenInfo.destroy); - cmd.set_face_down(lastTokenInfo.faceDown); - cmd.set_x(-1); - cmd.set_y(lastTokenTableRow); - - sendGameCommand(cmd); -} - -void Player::actCreatePredefinedToken() -{ - auto *action = static_cast(sender()); - CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardInfo(action->text()); - if (!cardInfo) { - return; - } - - setLastToken(cardInfo); - - actCreateAnotherToken(); -} - -void Player::actCreateRelatedCard() -{ - const CardItem *sourceCard = game->getActiveCard(); - if (!sourceCard) { - return; - } - auto *action = static_cast(sender()); - // If there is a better way of passing a CardRelation through a QAction, please add it here. - auto relatedCards = sourceCard->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::getInstance()->getCardFromSameSet( - cardRelation->getName(), sourceCard->getCard().getPrinting()); - - if (!relatedCard) { - return; - } - - setLastToken(relatedCard.getCardPtr()); - } -} - -void Player::actCreateAllRelatedCards() -{ - const CardItem *sourceCard = game->getActiveCard(); - if (!sourceCard) { - return; - } - - auto relatedCards = sourceCard->getCardInfo().getAllRelatedCards(); - if (relatedCards.isEmpty()) { - return; - } - - CardRelation *cardRelation = nullptr; - int tokensTypesCreated = 0; - - if (relatedCards.length() == 1) { - cardRelation = relatedCards.at(0); - if (createRelatedFromRelation(sourceCard, cardRelation)) { - ++tokensTypesCreated; - } - } else { - QList nonExcludedRelatedCards; - QString dbName; - for (CardRelation *cardRelationTemp : relatedCards) { - if (!cardRelationTemp->getIsCreateAllExclusion() && !cardRelationTemp->getDoesAttach()) { - nonExcludedRelatedCards.append(cardRelationTemp); - } - } - switch (nonExcludedRelatedCards.length()) { - case 1: // if nonExcludedRelatedCards == 1 - cardRelation = nonExcludedRelatedCards.at(0); - if (createRelatedFromRelation(sourceCard, cardRelation)) { - ++tokensTypesCreated; - } - break; - // If all are marked "Exclude", then treat the situation as if none of them are. - // We won't accept "garbage in, garbage out", here. - case 0: // else if nonExcludedRelatedCards == 0 - for (CardRelation *cardRelationAll : relatedCards) { - if (!cardRelationAll->getDoesAttach() && !cardRelationAll->getIsVariable()) { - dbName = cardRelationAll->getName(); - bool persistent = cardRelationAll->getIsPersistent(); - for (int i = 0; i < cardRelationAll->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); - } - ++tokensTypesCreated; - if (tokensTypesCreated == 1) { - cardRelation = cardRelationAll; - } - } - } - break; - default: // else - for (CardRelation *cardRelationNotExcluded : nonExcludedRelatedCards) { - if (!cardRelationNotExcluded->getDoesAttach() && !cardRelationNotExcluded->getIsVariable()) { - dbName = cardRelationNotExcluded->getName(); - bool persistent = cardRelationNotExcluded->getIsPersistent(); - for (int i = 0; i < cardRelationNotExcluded->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); - } - ++tokensTypesCreated; - if (tokensTypesCreated == 1) { - cardRelation = cardRelationNotExcluded; - } - } - } - break; - } - } - - /* - * If we made at least one token via "Create All Tokens" - * then assign the first to the "Create another" shortcut. - */ - if (cardRelation != nullptr && cardRelation->getCanCreateAnother()) { - CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardInfo(cardRelation->getName()); - setLastToken(cardInfo); - } -} - -bool Player::createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation) -{ - if (sourceCard == nullptr || cardRelation == nullptr) { - return false; - } - QString dbName = cardRelation->getName(); - bool persistent = cardRelation->getIsPersistent(); - if (cardRelation->getIsVariable()) { - bool ok; - dialogSemaphore = true; - int count = QInputDialog::getInt(game, tr("Create tokens"), tr("Number:"), cardRelation->getDefaultCount(), 1, - MAX_TOKENS_PER_DIALOG, 1, &ok); - dialogSemaphore = false; - if (!ok) { - return false; - } - for (int i = 0; i < count; ++i) { - createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); - } - } else if (cardRelation->getDefaultCount() > 1) { - for (int i = 0; i < cardRelation->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); - } - } else { - auto attachType = cardRelation->getAttachType(); - - // 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 == CardRelation::AttachTo && sourceCard->getZone()->getName() != "table") { - playCardToTable(sourceCard, false); - } - - createCard(sourceCard, dbName, attachType, persistent); - } - return true; -} - -void Player::createCard(const CardItem *sourceCard, - const QString &dbCardName, - CardRelation::AttachType attachType, - bool persistent) -{ - CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardInfo(dbCardName); - - if (cardInfo == nullptr || sourceCard == nullptr) { - return; - } - - // get the target token's location - // TODO: Define this QPoint into its own function along with the one below - QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - cardInfo->getTableRow())); - - // create the token for the related card - Command_CreateToken cmd; - cmd.set_zone("table"); - cmd.set_card_name(cardInfo->getName().toStdString()); - switch (cardInfo->getColors().size()) { - case 0: - cmd.set_color(""); - break; - case 1: - cmd.set_color("m"); - break; - default: - cmd.set_color(cardInfo->getColors().left(1).toLower().toStdString()); - break; - } - - cmd.set_pt(cardInfo->getPowTough().toStdString()); - if (SettingsCache::instance().getAnnotateTokens()) { - cmd.set_annotation(cardInfo->getText().toStdString()); - } else { - cmd.set_annotation(""); - } - cmd.set_destroy_on_zone_change(!persistent); - cmd.set_x(gridPoint.x()); - cmd.set_y(gridPoint.y()); - - ExactCard relatedCard = CardDatabaseManager::getInstance()->getCardFromSameSet(cardInfo->getName(), - sourceCard->getCard().getPrinting()); - - if (!relatedCard) { - return; - } - - switch (attachType) { - case CardRelation::DoesNotAttach: - cmd.set_target_zone("table"); - cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); - break; - - case CardRelation::AttachTo: - cmd.set_target_zone("table"); // We currently only support creating tokens on the table - cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); - cmd.set_target_card_id(sourceCard->getId()); - cmd.set_target_mode(Command_CreateToken::ATTACH_TO); - break; - - case CardRelation::TransformInto: - // allow cards to directly transform on stack - cmd.set_zone(sourceCard->getZone()->getName() == "stack" ? "stack" : "table"); - // Transform card zone changes are handled server-side - cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); - cmd.set_target_card_id(sourceCard->getId()); - cmd.set_target_mode(Command_CreateToken::TRANSFORM_INTO); - cmd.set_card_provider_id(sourceCard->getProviderId().toStdString()); - break; - } - - sendGameCommand(cmd); -} - -void Player::actSayMessage() -{ - auto *a = qobject_cast(sender()); - Command_GameSay cmd; - cmd.set_message(a->text().toStdString()); - sendGameCommand(cmd); -} - -void Player::setCardAttrHelper(const GameEventContext &context, - CardItem *card, - CardAttribute attribute, - const QString &avalue, - bool allCards, - EventProcessingOptions options) -{ - if (card == nullptr) { - return; - } - - bool moveCardContext = context.HasExtension(Context_MoveCard::ext); - switch (attribute) { - case AttrTapped: { - bool tapped = avalue == "1"; - if (!(!tapped && card->getDoesntUntap() && allCards)) { - if (!allCards) { - emit logSetTapped(this, card, tapped); - } - bool canAnimate = !options.testFlag(SKIP_TAP_ANIMATION) && !moveCardContext; - card->setTapped(tapped, canAnimate); - } - break; - } - case AttrAttacking: { - card->setAttacking(avalue == "1"); - break; - } - case AttrFaceDown: { - card->setFaceDown(avalue == "1"); - break; - } - case AttrColor: { - card->setColor(avalue); - break; - } - case AttrAnnotation: { - emit logSetAnnotation(this, card, avalue); - card->setAnnotation(avalue); - break; - } - case AttrDoesntUntap: { - bool value = (avalue == "1"); - emit logSetDoesntUntap(this, card, value); - card->setDoesntUntap(value); - break; - } - case AttrPT: { - emit logSetPT(this, card, avalue); - card->setPT(avalue); - break; - } - } -} - -void Player::eventGameSay(const Event_GameSay &event) -{ - emit logSay(this, QString::fromStdString(event.message())); -} - -void Player::eventShuffle(const Event_Shuffle &event) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name())); - if (!zone) { - return; - } - auto &cardList = zone->getCards(); - int absStart = event.start(); - if (absStart < 0) { // negative indexes start from the end - absStart += cardList.length(); - } - - // close all views that contain shuffled cards - for (auto *view : zone->getViews()) { - if (view != nullptr) { - int length = view->getCards().length(); - // we want to close empty views as well - if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library - view->close(); - break; - } - } else { - qWarning() << zone->getName() << "of" << getName() << "holds empty zoneview!"; - } - } - - // remove revealed card name on top of decks - if (absStart == 0 && !cardList.isEmpty()) { - cardList.first()->setCardRef({}); - zone->update(); - } - - emit logShuffle(this, zone, event.start(), event.end()); -} - -void Player::eventRollDie(const Event_RollDie &event) -{ - if (!event.values().empty()) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QList rolls(event.values().begin(), event.values().end()); -#else - QList rolls; - for (const auto &value : event.values()) { - rolls.append(value); - } -#endif - std::sort(rolls.begin(), rolls.end()); - emit logRollDie(this, static_cast(event.sides()), rolls); - } else if (event.value()) { - // Backwards compatibility for old clients - emit logRollDie(this, static_cast(event.sides()), {event.value()}); - } -} - -void Player::eventCreateArrow(const Event_CreateArrow &event) -{ - ArrowItem *arrow = addArrow(event.arrow_info()); - if (!arrow) { - return; - } - - auto *startCard = static_cast(arrow->getStartItem()); - auto *targetCard = qgraphicsitem_cast(arrow->getTargetItem()); - if (targetCard) { - emit logCreateArrow(this, startCard->getOwner(), startCard->getName(), targetCard->getOwner(), - targetCard->getName(), false); - } else { - emit logCreateArrow(this, startCard->getOwner(), startCard->getName(), arrow->getTargetItem()->getOwner(), - QString(), true); - } -} - -void Player::eventDeleteArrow(const Event_DeleteArrow &event) -{ - delArrow(event.arrow_id()); -} - -void Player::eventCreateToken(const Event_CreateToken &event) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0); - if (!zone) { - return; - } - - CardRef cardRef = {QString::fromStdString(event.card_name()), QString::fromStdString(event.card_provider_id())}; - CardItem *card = new CardItem(this, nullptr, cardRef, event.card_id()); - // use db PT if not provided in event and not face-down - if (!QString::fromStdString(event.pt()).isEmpty()) { - card->setPT(QString::fromStdString(event.pt())); - } else if (!event.face_down()) { - ExactCard dbCard = card->getCard(); - if (dbCard) { - card->setPT(dbCard.getInfo().getPowTough()); - } - } - card->setColor(QString::fromStdString(event.color())); - card->setAnnotation(QString::fromStdString(event.annotation())); - card->setDestroyOnZoneChange(event.destroy_on_zone_change()); - card->setFaceDown(event.face_down()); - - emit logCreateToken(this, card->getName(), card->getPT(), card->getFaceDown()); - zone->addCard(card, true, event.x(), event.y()); -} - -void Player::eventSetCardAttr(const Event_SetCardAttr &event, - const GameEventContext &context, - EventProcessingOptions options) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0); - if (!zone) { - return; - } - - if (!event.has_card_id()) { - const CardList &cards = zone->getCards(); - for (int i = 0; i < cards.size(); ++i) { - setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()), true, - options); - } - if (event.attribute() == AttrTapped) { - emit logSetTapped(this, nullptr, event.attr_value() == "1"); - } - } else { - CardItem *card = zone->getCard(event.card_id()); - if (!card) { - qWarning() << "Player::eventSetCardAttr: card id=" << event.card_id() << "not found"; - return; - } - setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false, options); - } -} - -void Player::eventSetCardCounter(const Event_SetCardCounter &event) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0); - if (!zone) { - return; - } - - CardItem *card = zone->getCard(event.card_id()); - if (!card) { - return; - } - - int oldValue = card->getCounters().value(event.counter_id(), 0); - card->setCounter(event.counter_id(), event.counter_value()); - updateCardMenu(card); - emit logSetCardCounter(this, card->getName(), event.counter_id(), event.counter_value(), oldValue); -} - -void Player::eventCreateCounter(const Event_CreateCounter &event) -{ - addCounter(event.counter_info()); -} - -void Player::eventSetCounter(const Event_SetCounter &event) -{ - AbstractCounter *ctr = counters.value(event.counter_id(), 0); - if (!ctr) { - return; - } - int oldValue = ctr->getValue(); - ctr->setValue(event.value()); - emit logSetCounter(this, ctr->getName(), event.value(), oldValue); -} - -void Player::eventDelCounter(const Event_DelCounter &event) -{ - delCounter(event.counter_id()); -} - -void Player::eventDumpZone(const Event_DumpZone &event) -{ - Player *zoneOwner = game->getGameState()->getPlayers().value(event.zone_owner_id(), 0); - if (!zoneOwner) { - return; - } - CardZone *zone = zoneOwner->getZones().value(QString::fromStdString(event.zone_name()), 0); - if (!zone) { - return; - } - emit logDumpZone(this, zone, event.number_cards(), event.is_reversed()); -} - -void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context) -{ - Player *startPlayer = game->getGameState()->getPlayers().value(event.start_player_id()); - if (!startPlayer) { - return; - } - QString startZoneString = QString::fromStdString(event.start_zone()); - CardZone *startZone = startPlayer->getZones().value(startZoneString, 0); - Player *targetPlayer = game->getGameState()->getPlayers().value(event.target_player_id()); - if (!targetPlayer) { - return; - } - CardZone *targetZone; - if (event.has_target_zone()) { - targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0); - } else { - targetZone = startZone; - } - if (!startZone || !targetZone) { - return; - } - - int position = event.position(); - int x = event.x(); - int y = event.y(); - - int logPosition = position; - int logX = x; - if (x == -1) { - x = 0; - } - CardItem *card = startZone->takeCard(position, event.card_id(), startZone != targetZone); - if (card == nullptr) { - return; - } - if (startZone != targetZone) { - card->deleteCardInfoPopup(); - } - if (event.has_card_name()) { - QString name = QString::fromStdString(event.card_name()); - QString providerId = - event.has_new_card_provider_id() ? QString::fromStdString(event.new_card_provider_id()) : ""; - card->setCardRef({name, providerId}); - } - - if (card->getAttachedTo() && (startZone != targetZone)) { - CardItem *parentCard = card->getAttachedTo(); - card->setAttachedTo(nullptr); - parentCard->getZone()->reorganizeCards(); - } - - card->deleteDragItem(); - - card->setId(event.new_card_id()); - card->setFaceDown(event.face_down()); - if (startZone != targetZone) { - card->setBeingPointedAt(false); - card->setHovered(false); - - const QList &attachedCards = card->getAttachedCards(); - for (auto attachedCard : attachedCards) { - attachedCard->setParentItem(targetZone); - } - - if (startZone->getPlayer() != targetZone->getPlayer()) { - card->setOwner(targetZone->getPlayer()); - } - } - - // The log event has to be sent before the card is added to the target zone - // because the addCard function can modify the card object. - if (context.HasExtension(Context_UndoDraw::ext)) { - emit logUndoDraw(this, card->getName()); - } else { - emit logMoveCard(this, card, startZone, logPosition, targetZone, logX); - } - - targetZone->addCard(card, true, x, y); - - // Look at all arrows from and to the card. - // If the card was moved to another zone, delete the arrows, otherwise update them. - QMapIterator playerIterator(game->getGameState()->getPlayers()); - while (playerIterator.hasNext()) { - Player *p = playerIterator.next().value(); - - QList arrowsToDelete; - QMapIterator arrowIterator(p->getArrows()); - while (arrowIterator.hasNext()) { - ArrowItem *arrow = arrowIterator.next().value(); - if ((arrow->getStartItem() == card) || (arrow->getTargetItem() == card)) { - if (startZone == targetZone) { - arrow->updatePath(); - } else { - arrowsToDelete.append(arrow); - } - } - } - for (auto &i : arrowsToDelete) { - i->delArrow(); - } - } - updateCardMenu(card); - - if (movingCardsUntil && startZoneString == "deck" && targetZone->getName() == "stack") { - moveOneCardUntil(card); - } -} - -void Player::eventFlipCard(const Event_FlipCard &event) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0); - if (!zone) { - return; - } - CardItem *card = zone->getCard(event.card_id()); - if (!card) { - return; - } - - if (!event.face_down()) { - QString cardName = QString::fromStdString(event.card_name()); - QString providerId = QString::fromStdString(event.card_provider_id()); - card->setCardRef({cardName, providerId}); - } - - emit logFlipCard(this, card->getName(), event.face_down()); - card->setFaceDown(event.face_down()); - updateCardMenu(card); -} - -void Player::eventDestroyCard(const Event_DestroyCard &event) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name()), 0); - if (!zone) { - return; - } - - CardItem *card = zone->getCard(event.card_id()); - if (!card) { - return; - } - - QList attachedCards = card->getAttachedCards(); - // This list is always empty except for buggy server implementations. - for (auto &attachedCard : attachedCards) { - attachedCard->setAttachedTo(nullptr); - } - - emit logDestroyCard(this, card->getName()); - zone->takeCard(-1, event.card_id(), true); - card->deleteLater(); -} - -void Player::eventAttachCard(const Event_AttachCard &event) -{ - const QMap &playerList = game->getGameState()->getPlayers(); - Player *targetPlayer = nullptr; - CardZone *targetZone = nullptr; - CardItem *targetCard = nullptr; - if (event.has_target_player_id()) { - targetPlayer = playerList.value(event.target_player_id(), 0); - if (targetPlayer) { - targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0); - if (targetZone) { - targetCard = targetZone->getCard(event.target_card_id()); - } - } - } - - CardZone *startZone = getZones().value(QString::fromStdString(event.start_zone()), 0); - if (!startZone) { - return; - } - - CardItem *startCard = startZone->getCard(event.card_id()); - if (!startCard) { - return; - } - - CardItem *oldParent = startCard->getAttachedTo(); - - startCard->setAttachedTo(targetCard); - - startZone->reorganizeCards(); - if ((startZone != targetZone) && targetZone) { - targetZone->reorganizeCards(); - } - if (oldParent) { - oldParent->getZone()->reorganizeCards(); - } - - if (targetCard) { - emit logAttachCard(this, startCard->getName(), targetPlayer, targetCard->getName()); - } else { - emit logUnattachCard(this, startCard->getName()); - } - updateCardMenu(startCard); -} - -void Player::eventDrawCards(const Event_DrawCards &event) -{ - CardZone *_deck = zones.value("deck"); - CardZone *_hand = zones.value("hand"); - - const int listSize = event.cards_size(); - if (listSize) { - for (int i = 0; i < listSize; ++i) { - const ServerInfo_Card &cardInfo = event.cards(i); - CardItem *card = _deck->takeCard(0, cardInfo.id()); - QString cardName = QString::fromStdString(cardInfo.name()); - QString providerId = QString::fromStdString(cardInfo.provider_id()); - card->setCardRef({cardName, providerId}); - _hand->addCard(card, false, -1); - } - } else { - const int number = event.number(); - for (int i = 0; i < number; ++i) { - _hand->addCard(_deck->takeCard(0, -1), false, -1); - } - } - - _hand->reorganizeCards(); - _deck->reorganizeCards(); - emit logDrawCards(this, event.number(), _deck->getCards().size() == 0); -} - -void Player::eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name())); - if (!zone) { - return; - } - Player *otherPlayer = nullptr; - if (event.has_other_player_id()) { - otherPlayer = game->getGameState()->getPlayers().value(event.other_player_id()); - if (!otherPlayer) { - return; - } - } - - bool peeking = false; - QList cardList; - const int cardListSize = event.cards_size(); - for (int i = 0; i < cardListSize; ++i) { - const ServerInfo_Card *temp = &event.cards(i); - if (temp->face_down()) { - peeking = true; - } - cardList.append(temp); - } - - if (peeking) { - for (const auto &card : cardList) { - QString cardName = QString::fromStdString(card->name()); - QString providerId = QString::fromStdString(card->provider_id()); - CardItem *cardItem = zone->getCard(card->id()); - if (!cardItem) { - continue; - } - cardItem->setCardRef({cardName, providerId}); - emit logRevealCards(this, zone, card->id(), cardName, this, true, 1); - } - } else { - bool showZoneView = true; - QString cardName; - auto cardId = event.card_id_size() == 0 ? -1 : event.card_id(0); - if (cardList.size() == 1) { - cardName = QString::fromStdString(cardList.first()->name()); - - // Handle case of revealing top card of library in-place - if (cardId == 0 && dynamic_cast(zone)) { - auto card = zone->getCards().first(); - QString providerId = QString::fromStdString(cardList.first()->provider_id()); - card->setCardRef({cardName, providerId}); - zone->update(); - showZoneView = false; - } - } - if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) { - static_cast(scene())->addRevealedZoneView(this, zone, cardList, event.grant_write_access()); - } - - emit logRevealCards(this, zone, cardId, cardName, otherPlayer, false, - event.has_number_of_cards() ? event.number_of_cards() : cardList.size(), - event.grant_write_access()); - } -} - -void Player::eventChangeZoneProperties(const Event_ChangeZoneProperties &event) -{ - CardZone *zone = zones.value(QString::fromStdString(event.zone_name())); - if (!zone) { - return; - } - - if (event.has_always_reveal_top_card()) { - zone->setAlwaysRevealTopCard(event.always_reveal_top_card()); - emit logAlwaysRevealTopCard(this, zone, event.always_reveal_top_card()); - } - if (event.has_always_look_at_top_card()) { - zone->setAlwaysRevealTopCard(event.always_look_at_top_card()); - emit logAlwaysLookAtTopCard(this, zone, event.always_look_at_top_card()); - } -} - -void Player::processGameEvent(GameEvent::GameEventType type, - const GameEvent &event, - const GameEventContext &context, - EventProcessingOptions options) -{ - switch (type) { - case GameEvent::GAME_SAY: - eventGameSay(event.GetExtension(Event_GameSay::ext)); - break; - case GameEvent::SHUFFLE: - eventShuffle(event.GetExtension(Event_Shuffle::ext)); - break; - case GameEvent::ROLL_DIE: - eventRollDie(event.GetExtension(Event_RollDie::ext)); - break; - case GameEvent::CREATE_ARROW: - eventCreateArrow(event.GetExtension(Event_CreateArrow::ext)); - break; - case GameEvent::DELETE_ARROW: - eventDeleteArrow(event.GetExtension(Event_DeleteArrow::ext)); - break; - case GameEvent::CREATE_TOKEN: - eventCreateToken(event.GetExtension(Event_CreateToken::ext)); - break; - case GameEvent::SET_CARD_ATTR: - eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context, options); - break; - case GameEvent::SET_CARD_COUNTER: - eventSetCardCounter(event.GetExtension(Event_SetCardCounter::ext)); - break; - case GameEvent::CREATE_COUNTER: - eventCreateCounter(event.GetExtension(Event_CreateCounter::ext)); - break; - case GameEvent::SET_COUNTER: - eventSetCounter(event.GetExtension(Event_SetCounter::ext)); - break; - case GameEvent::DEL_COUNTER: - eventDelCounter(event.GetExtension(Event_DelCounter::ext)); - break; - case GameEvent::DUMP_ZONE: - eventDumpZone(event.GetExtension(Event_DumpZone::ext)); - break; - case GameEvent::MOVE_CARD: - eventMoveCard(event.GetExtension(Event_MoveCard::ext), context); - break; - case GameEvent::FLIP_CARD: - eventFlipCard(event.GetExtension(Event_FlipCard::ext)); - break; - case GameEvent::DESTROY_CARD: - eventDestroyCard(event.GetExtension(Event_DestroyCard::ext)); - break; - case GameEvent::ATTACH_CARD: - eventAttachCard(event.GetExtension(Event_AttachCard::ext)); - break; - case GameEvent::DRAW_CARDS: - eventDrawCards(event.GetExtension(Event_DrawCards::ext)); - break; - case GameEvent::REVEAL_CARDS: - eventRevealCards(event.GetExtension(Event_RevealCards::ext), options); - break; - case GameEvent::CHANGE_ZONE_PROPERTIES: - eventChangeZoneProperties(event.GetExtension(Event_ChangeZoneProperties::ext)); - break; - default: { - qWarning() << "unhandled game event" << type; - } - } -} - -void Player::setActive(bool _active) -{ - active = _active; - table->setActive(active); - update(); -} - -QRectF Player::boundingRect() const -{ - return bRect; -} - -void Player::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) +void Player::setZoneId(int _zoneId) { + zoneId = _zoneId; + graphicsItem->getPlayerArea()->setPlayerZoneId(zoneId); } void Player::processPlayerInfo(const ServerInfo_Player &info) @@ -2758,7 +116,7 @@ void Player::processPlayerInfo(const ServerInfo_Player &info) clearCounters(); clearArrows(); - QMutableMapIterator zoneIt(zones); + QMutableMapIterator zoneIt(zones); while (zoneIt.hasNext()) { zoneIt.next().value()->clearContents(); @@ -2767,24 +125,20 @@ void Player::processPlayerInfo(const ServerInfo_Player &info) } } - // Can be null if we are not the local player! - if (mCustomZones) { - mCustomZones->clear(); - mCustomZones->menuAction()->setVisible(false); - } + emit clearCustomZonesMenu(); const int zoneListSize = info.zone_list_size(); for (int i = 0; i < zoneListSize; ++i) { const ServerInfo_Zone &zoneInfo = info.zone_list(i); QString zoneName = QString::fromStdString(zoneInfo.name()); - CardZone *zone = zones.value(zoneName, 0); + CardZoneLogic *zone = zones.value(zoneName, 0); if (!zone) { // Create a new CardZone if it doesn't exist if (zoneInfo.with_coords()) { // Visibility not currently supported for TableZone - zone = addZone(new TableZone(this, zoneName, this)); + zone = addZone(new TableZoneLogic(this, zoneName, true, false, true, this)); } else { // Zones without coordinats are always treated as non-shufflable // PileZones, although supporting alternate hand or stack zones @@ -2794,8 +148,8 @@ void Player::processPlayerInfo(const ServerInfo_Player &info) switch (zoneInfo.type()) { case ServerInfo_Zone::PrivateZone: contentsKnown = - local || judge || - (game->getGameState()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient()); + playerInfo->getLocalOrJudge() || (game->getPlayerManager()->isSpectator() && + game->getGameMetaInfo()->spectatorsOmniscient()); break; case ServerInfo_Zone::PublicZone: @@ -2807,20 +161,14 @@ void Player::processPlayerInfo(const ServerInfo_Player &info) break; } - zone = addZone(new PileZone(this, zoneName, /* isShufflable */ false, contentsKnown, this)); + zone = addZone(new PileZoneLogic(this, zoneName, false, /* isShufflable */ false, contentsKnown, this)); } // Non-builtin zones are hidden by default and can't be interacted // with, except through menus. - zone->setVisible(false); + emit zone->setGraphicsVisibility(false); - if (mCustomZones) { - mCustomZones->menuAction()->setVisible(true); - QAction *aViewZone = mCustomZones->addAction(tr("View custom zone '%1'").arg(zoneName)); - aViewZone->setData(zoneName); - connect(aViewZone, &QAction::triggered, this, - [zoneName, this]() { static_cast(scene())->toggleZoneView(this, zoneName, -1); }); - } + emit addViewCustomZoneActionToCustomZoneMenu(zoneName); continue; } @@ -2858,7 +206,7 @@ void Player::processCardAttachment(const ServerInfo_Player &info) const int zoneListSize = info.zone_list_size(); for (int i = 0; i < zoneListSize; ++i) { const ServerInfo_Zone &zoneInfo = info.zone_list(i); - CardZone *zone = zones.value(QString::fromStdString(zoneInfo.name()), 0); + CardZoneLogic *zone = zones.value(QString::fromStdString(zoneInfo.name()), 0); if (!zone) { continue; } @@ -2886,96 +234,6 @@ void Player::processCardAttachment(const ServerInfo_Player &info) } } -void Player::playCard(CardItem *card, bool faceDown) -{ - if (card == nullptr) { - return; - } - - Command_MoveCard cmd; - cmd.set_start_player_id(card->getZone()->getPlayer()->getId()); - cmd.set_start_zone(card->getZone()->getName().toStdString()); - cmd.set_target_player_id(getId()); - CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); - cardToMove->set_card_id(card->getId()); - - ExactCard exactCard = card->getCard(); - if (!exactCard) { - return; - } - - const CardInfo &info = exactCard.getInfo(); - - int tableRow = info.getTableRow(); - bool playToStack = SettingsCache::instance().getPlayToStack(); - QString currentZone = card->getZone()->getName(); - if (currentZone == "stack" && tableRow == 3) { - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - } else if (!faceDown && - ((!playToStack && tableRow == 3) || ((playToStack && tableRow != 0) && currentZone != "stack"))) { - cmd.set_target_zone("stack"); - cmd.set_x(-1); - cmd.set_y(0); - } else { - tableRow = faceDown ? 2 : info.getTableRow(); - QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow)); - cardToMove->set_face_down(faceDown); - if (!faceDown) { - cardToMove->set_pt(info.getPowTough().toStdString()); - } - cardToMove->set_tapped(!faceDown && info.getCipt()); - if (tableRow != 3) - cmd.set_target_zone("table"); - cmd.set_x(gridPoint.x()); - cmd.set_y(gridPoint.y()); - } - sendGameCommand(cmd); -} - -/** - * Like {@link Player::playCard}, but forces the card to be played to the table zone. - * Cards with tablerow 3 (the stack) will be played to tablerow 1 (the noncreatures row). - */ -void Player::playCardToTable(const CardItem *card, bool faceDown) -{ - if (card == nullptr) { - return; - } - - Command_MoveCard cmd; - cmd.set_start_player_id(card->getZone()->getPlayer()->getId()); - cmd.set_start_zone(card->getZone()->getName().toStdString()); - cmd.set_target_player_id(getId()); - CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); - cardToMove->set_card_id(card->getId()); - - ExactCard exactCard = card->getCard(); - if (!exactCard) { - return; - } - - const CardInfo &info = exactCard.getInfo(); - - int tableRow = faceDown ? 2 : info.getTableRow(); - // default instant/sorcery cards to the noncreatures row - if (tableRow > 2) { - tableRow = 1; - } - - QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow)); - cardToMove->set_face_down(faceDown); - if (!faceDown) { - cardToMove->set_pt(info.getPowTough().toStdString()); - } - cardToMove->set_tapped(!faceDown && info.getCipt()); - cmd.set_target_zone("table"); - cmd.set_x(gridPoint.x()); - cmd.set_y(gridPoint.y()); - sendGameCommand(cmd); -} - void Player::addCard(CardItem *card) { emit newCardAdded(card); @@ -2992,6 +250,13 @@ void Player::deleteCard(CardItem *card) } } +void Player::setDeck(const DeckLoader &_deck) +{ + deck = new DeckLoader(_deck); + + emit deckChanged(); +} + AbstractCounter *Player::addCounter(const ServerInfo_Counter &counter) { return addCounter(counter.id(), QString::fromStdString(counter.name()), @@ -3006,18 +271,19 @@ AbstractCounter *Player::addCounter(int counterId, const QString &name, QColor c AbstractCounter *ctr; if (name == "life") { - ctr = playerTarget->addCounter(counterId, name, value); + ctr = getGraphicsItem()->getPlayerTarget()->addCounter(counterId, name, value); } else { - ctr = new GeneralCounter(this, counterId, name, color, radius, value, true, this, game); + ctr = new GeneralCounter(this, counterId, name, color, radius, value, true, graphicsItem); } counters.insert(counterId, ctr); - if (countersMenu && ctr->getMenu()) { - countersMenu->addMenu(ctr->getMenu()); + + if (playerMenu->getCountersMenu() && ctr->getMenu()) { + playerMenu->getCountersMenu()->addMenu(ctr->getMenu()); } - if (shortcutsActive) { + if (playerMenu->getShortcutsActive()) { ctr->setShortcutsActive(); } - rearrangeCounters(); + emit rearrangeCounters(); return ctr; } @@ -3030,7 +296,7 @@ void Player::delCounter(int counterId) ctr->delCounter(); counters.remove(counterId); - rearrangeCounters(); + emit rearrangeCounters(); } void Player::clearCounters() @@ -3046,7 +312,7 @@ void Player::incrementAllCardCounters() { QList cardsToUpdate; - auto selectedItems = scene()->selectedItems(); + auto selectedItems = getGameScene()->selectedItems(); if (!selectedItems.isEmpty()) { // If cards are selected, only update those for (const auto &item : selectedItems) { @@ -3055,7 +321,7 @@ void Player::incrementAllCardCounters() } } else { // If no cards selected, update all cards on table - const CardList &tableCards = table->getCards(); + const CardList &tableCards = getTableZone()->getCards(); cardsToUpdate = tableCards; } @@ -3083,21 +349,21 @@ void Player::incrementAllCardCounters() } if (!commandList.isEmpty()) { - sendGameCommand(prepareGameCommand(commandList)); + playerActions->sendGameCommand(playerActions->prepareGameCommand(commandList)); } } ArrowItem *Player::addArrow(const ServerInfo_Arrow &arrow) { - const QMap &playerList = game->getGameState()->getPlayers(); + const QMap &playerList = game->getPlayerManager()->getPlayers(); Player *startPlayer = playerList.value(arrow.start_player_id(), 0); Player *targetPlayer = playerList.value(arrow.target_player_id(), 0); if (!startPlayer || !targetPlayer) { return nullptr; } - CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(arrow.start_zone()), 0); - CardZone *targetZone = nullptr; + CardZoneLogic *startZone = startPlayer->getZones().value(QString::fromStdString(arrow.start_zone()), 0); + CardZoneLogic *targetZone = nullptr; if (arrow.has_target_zone()) { targetZone = targetPlayer->getZones().value(QString::fromStdString(arrow.target_zone()), 0); } @@ -3117,7 +383,7 @@ ArrowItem *Player::addArrow(const ServerInfo_Arrow &arrow) if (targetCard) { return addArrow(arrow.id(), startCard, targetCard, convertColorToQColor(arrow.arrow_color())); } else { - return addArrow(arrow.id(), startCard, targetPlayer->getPlayerTarget(), + return addArrow(arrow.id(), startCard, targetPlayer->getGraphicsItem()->getPlayerTarget(), convertColorToQColor(arrow.arrow_color())); } } @@ -3126,7 +392,8 @@ ArrowItem *Player::addArrow(int arrowId, CardItem *startCard, ArrowTarget *targe { auto *arrow = new ArrowItem(this, arrowId, startCard, targetItem, color); arrows.insert(arrowId, arrow); - scene()->addItem(arrow); + + getGameScene()->addItem(arrow); return arrow; } @@ -3155,76 +422,6 @@ void Player::clearArrows() arrows.clear(); } -void Player::rearrangeCounters() -{ - qreal marginTop = 80; - const qreal padding = 5; - qreal ySize = boundingRect().y() + marginTop; - - // Place objects - for (const auto &counter : counters) { - AbstractCounter *ctr = counter; - - if (!ctr->getShownInCounterArea()) { - continue; - } - - QRectF br = ctr->boundingRect(); - ctr->setPos((counterAreaWidth - br.width()) / 2, ySize); - ySize += br.height() + padding; - } -} - -PendingCommand *Player::prepareGameCommand(const google::protobuf::Message &cmd) -{ - - if (judge && !local) { - Command_Judge base; - GameCommand *c = base.add_game_command(); - base.set_target_id(id); - c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd); - return game->getGameEventHandler()->prepareGameCommand(base); - } else { - return game->getGameEventHandler()->prepareGameCommand(cmd); - } -} - -PendingCommand *Player::prepareGameCommand(const QList &cmdList) -{ - if (judge && !local) { - Command_Judge base; - base.set_target_id(id); - for (int i = 0; i < cmdList.size(); ++i) { - GameCommand *c = base.add_game_command(); - c->GetReflection() - ->MutableMessage(c, cmdList[i]->GetDescriptor()->FindExtensionByName("ext")) - ->CopyFrom(*cmdList[i]); - delete cmdList[i]; - } - return game->getGameEventHandler()->prepareGameCommand(base); - } else { - return game->getGameEventHandler()->prepareGameCommand(cmdList); - } -} - -void Player::sendGameCommand(const google::protobuf::Message &command) -{ - if (judge && !local) { - Command_Judge base; - GameCommand *c = base.add_game_command(); - base.set_target_id(id); - c->GetReflection()->MutableMessage(c, command.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(command); - game->getGameEventHandler()->sendGameCommand(base, id); - } else { - game->getGameEventHandler()->sendGameCommand(command, id); - } -} - -void Player::sendGameCommand(PendingCommand *pend) -{ - game->getGameEventHandler()->sendGameCommand(pend, id); -} - bool Player::clearCardsToDelete() { if (cardsToDelete.isEmpty()) { @@ -3241,998 +438,6 @@ bool Player::clearCardsToDelete() return true; } -void Player::actMoveCardXCardsFromTop() -{ - int deckSize = zones.value("deck")->getCards().size() + 1; // add the card to move to the deck - bool ok; - int number = - QInputDialog::getInt(game, tr("Place card X cards from top of library"), - tr("Which position should this card be placed:") + "\n" + tr("(max. %1)").arg(deckSize), - defaultNumberTopCardsToPlaceBelow, 1, deckSize, 1, &ok); - number -= 1; // indexes start at 0 - - if (!ok) { - return; - } - - defaultNumberTopCardsToPlaceBelow = number; - - QList sel = scene()->selectedItems(); - if (sel.isEmpty()) { - return; - } - - QList cardList; - while (!sel.isEmpty()) { - cardList.append(qgraphicsitem_cast(sel.takeFirst())); - } - - QList commandList; - ListOfCardsToMove idList; - for (const auto &i : cardList) { - idList.add_card()->set_card_id(i->getId()); - } - - int startPlayerId = cardList[0]->getZone()->getPlayer()->getId(); - QString startZone = cardList[0]->getZone()->getName(); - - auto *cmd = new Command_MoveCard; - cmd->set_start_player_id(startPlayerId); - cmd->set_start_zone(startZone.toStdString()); - cmd->mutable_cards_to_move()->CopyFrom(idList); - cmd->set_target_player_id(getId()); - cmd->set_target_zone("deck"); - cmd->set_x(number); - cmd->set_y(0); - commandList.append(cmd); - - if (local) { - sendGameCommand(prepareGameCommand(commandList)); - } else { - game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList)); - } -} - -void Player::cardMenuAction() -{ - auto *a = dynamic_cast(sender()); - QList sel = scene()->selectedItems(); - QList cardList; - while (!sel.isEmpty()) { - cardList.append(qgraphicsitem_cast(sel.takeFirst())); - } - - QList commandList; - if (a->data().toInt() <= (int)cmClone) { - for (const auto &card : cardList) { - switch (static_cast(a->data().toInt())) { - // Leaving both for compatibility with server - case cmUntap: - // fallthrough - case cmTap: { - auto *cmd = new Command_SetCardAttr; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_attribute(AttrTapped); - cmd->set_attr_value(std::to_string(1 - static_cast(card->getTapped()))); - commandList.append(cmd); - break; - } - case cmDoesntUntap: { - auto *cmd = new Command_SetCardAttr; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_attribute(AttrDoesntUntap); - cmd->set_attr_value(card->getDoesntUntap() ? "0" : "1"); - commandList.append(cmd); - break; - } - case cmFlip: { - auto *cmd = new Command_FlipCard; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_face_down(!card->getFaceDown()); - if (card->getFaceDown()) { - ExactCard ec = card->getCard(); - if (ec) { - cmd->set_pt(ec.getInfo().getPowTough().toStdString()); - } - } - commandList.append(cmd); - break; - } - case cmPeek: { - auto *cmd = new Command_RevealCards; - cmd->set_zone_name(card->getZone()->getName().toStdString()); - cmd->add_card_id(card->getId()); - cmd->set_player_id(id); - commandList.append(cmd); - break; - } - case cmClone: { - auto *cmd = new Command_CreateToken; - cmd->set_zone("table"); - cmd->set_card_name(card->getName().toStdString()); - cmd->set_card_provider_id(card->getProviderId().toStdString()); - cmd->set_color(card->getColor().toStdString()); - cmd->set_pt(card->getPT().toStdString()); - cmd->set_annotation(card->getAnnotation().toStdString()); - cmd->set_destroy_on_zone_change(true); - cmd->set_x(-1); - cmd->set_y(card->getGridPoint().y()); - commandList.append(cmd); - break; - } - default: - break; - } - } - } else { - CardZone *zone = cardList[0]->getZone(); - if (!zone) { - return; - } - - Player *startPlayer = zone->getPlayer(); - if (!startPlayer) { - return; - } - - int startPlayerId = startPlayer->getId(); - QString startZone = zone->getName(); - - ListOfCardsToMove idList; - for (const auto &i : cardList) { - idList.add_card()->set_card_id(i->getId()); - } - - switch (static_cast(a->data().toInt())) { - case cmMoveToTopLibrary: { - auto *cmd = new Command_MoveCard; - cmd->set_start_player_id(startPlayerId); - cmd->set_start_zone(startZone.toStdString()); - cmd->mutable_cards_to_move()->CopyFrom(idList); - cmd->set_target_player_id(getId()); - cmd->set_target_zone("deck"); - cmd->set_x(0); - cmd->set_y(0); - - if (idList.card_size() > 1) { - auto *scmd = new Command_Shuffle; - scmd->set_zone_name("deck"); - scmd->set_start(0); - scmd->set_end(idList.card_size() - 1); // inclusive, the indexed card at end will be shuffled - // Server process events backwards, so... - commandList.append(scmd); - } - - commandList.append(cmd); - break; - } - case cmMoveToBottomLibrary: { - auto *cmd = new Command_MoveCard; - cmd->set_start_player_id(startPlayerId); - cmd->set_start_zone(startZone.toStdString()); - cmd->mutable_cards_to_move()->CopyFrom(idList); - cmd->set_target_player_id(getId()); - cmd->set_target_zone("deck"); - cmd->set_x(-1); - cmd->set_y(0); - - if (idList.card_size() > 1) { - auto *scmd = new Command_Shuffle; - scmd->set_zone_name("deck"); - scmd->set_start(-idList.card_size()); - scmd->set_end(-1); - // Server process events backwards, so... - commandList.append(scmd); - } - - commandList.append(cmd); - break; - } - case cmMoveToHand: { - auto *cmd = new Command_MoveCard; - cmd->set_start_player_id(startPlayerId); - cmd->set_start_zone(startZone.toStdString()); - cmd->mutable_cards_to_move()->CopyFrom(idList); - cmd->set_target_player_id(getId()); - cmd->set_target_zone("hand"); - cmd->set_x(0); - cmd->set_y(0); - commandList.append(cmd); - break; - } - case cmMoveToGraveyard: { - auto *cmd = new Command_MoveCard; - cmd->set_start_player_id(startPlayerId); - cmd->set_start_zone(startZone.toStdString()); - cmd->mutable_cards_to_move()->CopyFrom(idList); - cmd->set_target_player_id(getId()); - cmd->set_target_zone("grave"); - cmd->set_x(0); - cmd->set_y(0); - commandList.append(cmd); - break; - } - case cmMoveToExile: { - auto *cmd = new Command_MoveCard; - cmd->set_start_player_id(startPlayerId); - cmd->set_start_zone(startZone.toStdString()); - cmd->mutable_cards_to_move()->CopyFrom(idList); - cmd->set_target_player_id(getId()); - cmd->set_target_zone("rfg"); - cmd->set_x(0); - cmd->set_y(0); - commandList.append(cmd); - break; - } - default: - break; - } - } - - if (local) { - sendGameCommand(prepareGameCommand(commandList)); - } else { - game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList)); - } -} - -void Player::actIncPT(int deltaP, int deltaT) -{ - int playerid = id; - - QList commandList; - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - QString pt = card->getPT(); - const auto ptList = parsePT(pt); - QString newpt; - if (ptList.isEmpty()) { - newpt = QString::number(deltaP) + (deltaT ? "/" + QString::number(deltaT) : ""); - } else if (ptList.size() == 1) { - newpt = QString::number(ptList.at(0).toInt() + deltaP) + (deltaT ? "/" + QString::number(deltaT) : ""); - } else { - newpt = - QString::number(ptList.at(0).toInt() + deltaP) + "/" + QString::number(ptList.at(1).toInt() + deltaT); - } - - auto *cmd = new Command_SetCardAttr; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_attribute(AttrPT); - cmd->set_attr_value(newpt.toStdString()); - commandList.append(cmd); - - if (local) { - playerid = card->getZone()->getPlayer()->getId(); - } - } - - game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); -} - -void Player::actResetPT() -{ - int playerid = id; - QList commandList; - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - QString ptString; - if (!card->getFaceDown()) { // leave the pt empty if the card is face down - ExactCard ec = card->getCard(); - if (ec) { - ptString = ec.getInfo().getPowTough(); - } - } - if (ptString == card->getPT()) { - continue; - } - QString zoneName = card->getZone()->getName(); - auto *cmd = new Command_SetCardAttr; - cmd->set_zone(zoneName.toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_attribute(AttrPT); - cmd->set_attr_value(ptString.toStdString()); - commandList.append(cmd); - - if (local) { - playerid = card->getZone()->getPlayer()->getId(); - } - } - - if (!commandList.empty()) { - game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); - } -} - -QVariantList Player::parsePT(const QString &pt) -{ - QVariantList ptList = QVariantList(); - if (!pt.isEmpty()) { - int sep = pt.indexOf('/'); - if (sep == 0) { - ptList.append(QVariant(pt.mid(1))); // cut off starting '/' and take full string - } else { - int start = 0; - for (;;) { - QString item = pt.mid(start, sep - start); - if (item.isEmpty()) { - ptList.append(QVariant(QString())); - } else if (item[0] == '+') { - ptList.append(QVariant(item.mid(1).toInt())); // add as int - } else if (item[0] == '-') { - ptList.append(QVariant(item.toInt())); // add as int - } else { - ptList.append(QVariant(item)); // add as qstring - } - if (sep == -1) { - break; - } - start = sep + 1; - sep = pt.indexOf('/', start); - } - } - } - return ptList; -} - -void Player::actSetPT() -{ - QString oldPT; - int playerid = id; - - auto sel = scene()->selectedItems(); - for (const auto &item : sel) { - auto *card = static_cast(item); - if (!card->getPT().isEmpty()) { - oldPT = card->getPT(); - } - } - bool ok; - dialogSemaphore = true; - QString pt = - getTextWithMax(game, tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal, oldPT, &ok); - dialogSemaphore = false; - if (clearCardsToDelete() || !ok) { - return; - } - - const auto ptList = parsePT(pt); - bool empty = ptList.isEmpty(); - - QList commandList; - for (const auto &item : sel) { - auto *card = static_cast(item); - auto *cmd = new Command_SetCardAttr; - QString newpt = QString(); - if (!empty) { - const auto oldpt = parsePT(card->getPT()); - int ptIter = 0; - for (const auto &_item : ptList) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - if (_item.typeId() == QMetaType::Type::Int) { -#else - if (_item.type() == QVariant::Int) { -#endif - int oldItem = ptIter < oldpt.size() ? oldpt.at(ptIter).toInt() : 0; - newpt += '/' + QString::number(oldItem + _item.toInt()); - } else { - newpt += '/' + _item.toString(); - } - ++ptIter; - } - newpt = newpt.mid(1); - } - - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_attribute(AttrPT); - cmd->set_attr_value(newpt.toStdString()); - commandList.append(cmd); - - if (local) { - playerid = card->getZone()->getPlayer()->getId(); - } - } - - game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); -} - -void Player::actDrawArrow() -{ - auto *card = game->getActiveCard(); - if (card) { - card->drawArrow(Qt::red); - } -} - -void Player::actIncP() -{ - actIncPT(1, 0); -} - -void Player::actDecP() -{ - actIncPT(-1, 0); -} - -void Player::actIncT() -{ - actIncPT(0, 1); -} - -void Player::actDecT() -{ - actIncPT(0, -1); -} - -void Player::actIncPT() -{ - actIncPT(1, 1); -} - -void Player::actDecPT() -{ - actIncPT(-1, -1); -} - -void Player::actFlowP() -{ - actIncPT(1, -1); -} - -void Player::actFlowT() -{ - actIncPT(-1, 1); -} - -void AnnotationDialog::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) { - event->accept(); - accept(); - return; - } - QInputDialog::keyPressEvent(event); -} - -void Player::actSetAnnotation() -{ - QString oldAnnotation; - auto sel = scene()->selectedItems(); - for (const auto &item : sel) { - auto *card = static_cast(item); - if (!card->getAnnotation().isEmpty()) { - oldAnnotation = card->getAnnotation(); - } - } - - dialogSemaphore = true; - AnnotationDialog *dialog = new AnnotationDialog(game); - dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput); - dialog->setWindowTitle(tr("Set annotation")); - dialog->setLabelText(tr("Please enter the new annotation:")); - dialog->setTextValue(oldAnnotation); - bool ok = dialog->exec(); - dialogSemaphore = false; - if (clearCardsToDelete() || !ok) { - return; - } - QString annotation = dialog->textValue().left(MAX_NAME_LENGTH); - - QList commandList; - for (const auto &item : sel) { - auto *card = static_cast(item); - auto *cmd = new Command_SetCardAttr; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_attribute(AttrAnnotation); - cmd->set_attr_value(annotation.toStdString()); - commandList.append(cmd); - } - sendGameCommand(prepareGameCommand(commandList)); -} - -void Player::actAttach() -{ - auto *card = game->getActiveCard(); - if (!card) { - return; - } - - card->drawAttachArrow(); -} - -void Player::actUnattach() -{ - QList commandList; - for (QGraphicsItem *item : scene()->selectedItems()) { - auto *card = static_cast(item); - - if (!card->getAttachedTo()) { - continue; - } - - auto *cmd = new Command_AttachCard; - cmd->set_start_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - commandList.append(cmd); - } - sendGameCommand(prepareGameCommand(commandList)); -} - -void Player::actCardCounterTrigger() -{ - auto *action = static_cast(sender()); - int counterId = action->data().toInt() / 1000; - QList commandList; - switch (action->data().toInt() % 1000) { - case 9: { // increment counter - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - if (card->getCounters().value(counterId, 0) < MAX_COUNTERS_ON_CARD) { - auto *cmd = new Command_SetCardCounter; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_counter_id(counterId); - cmd->set_counter_value(card->getCounters().value(counterId, 0) + 1); - commandList.append(cmd); - } - } - break; - } - case 10: { // decrement counter - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - if (card->getCounters().value(counterId, 0)) { - auto *cmd = new Command_SetCardCounter; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_counter_id(counterId); - cmd->set_counter_value(card->getCounters().value(counterId, 0) - 1); - commandList.append(cmd); - } - } - break; - } - case 11: { // set counter with dialog - bool ok; - dialogSemaphore = true; - - int oldValue = 0; - if (scene()->selectedItems().size() == 1) { - auto *card = static_cast(scene()->selectedItems().first()); - oldValue = card->getCounters().value(counterId, 0); - } - int number = QInputDialog::getInt(game, tr("Set counters"), tr("Number:"), oldValue, 0, - MAX_COUNTERS_ON_CARD, 1, &ok); - dialogSemaphore = false; - if (clearCardsToDelete() || !ok) { - return; - } - - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - auto *cmd = new Command_SetCardCounter; - cmd->set_zone(card->getZone()->getName().toStdString()); - cmd->set_card_id(card->getId()); - cmd->set_counter_id(counterId); - cmd->set_counter_value(number); - commandList.append(cmd); - } - break; - } - default:; - } - sendGameCommand(prepareGameCommand(commandList)); -} - -/** - * @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone - * is nullptr. - */ -static bool isUnwritableRevealZone(CardZone *zone) -{ - if (auto *view = qobject_cast(zone)) { - return view->getRevealZone() && !view->getWriteableRevealZone(); - } - return false; -} - -void Player::playSelectedCards(const bool faceDown) -{ - QList selectedCards; - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - selectedCards.append(card); - } - - // 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. - std::sort(selectedCards.begin(), selectedCards.end(), - [](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); }); - - for (auto &card : selectedCards) { - if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != "table") { - playCard(card, faceDown); - } - } -} - -void Player::actPlay() -{ - playSelectedCards(false); -} - -void Player::actPlayFacedown() -{ - playSelectedCards(true); -} - -void Player::actHide() -{ - for (const auto &item : scene()->selectedItems()) { - auto *card = static_cast(item); - if (card && isUnwritableRevealZone(card->getZone())) { - card->getZone()->removeCard(card); - } - } -} - -void Player::actReveal(QAction *action) -{ - const int otherPlayerId = action->data().toInt(); - - Command_RevealCards cmd; - if (otherPlayerId != -1) { - cmd.set_player_id(otherPlayerId); - } - - QList sel = scene()->selectedItems(); - while (!sel.isEmpty()) { - const auto *card = qgraphicsitem_cast(sel.takeFirst()); - if (!cmd.has_zone_name()) { - cmd.set_zone_name(card->getZone()->getName().toStdString()); - } - cmd.add_card_id(card->getId()); - } - - sendGameCommand(cmd); -} - -void Player::refreshShortcuts() -{ - if (shortcutsActive) { - setShortcutsActive(); - } -} - -QMenu *Player::createCardMenu(const CardItem *card) -{ - if (card == nullptr) { - return nullptr; - } - - bool revealedCard = false; - bool writeableCard = getLocalOrJudge(); - if (auto *view = qobject_cast(card->getZone())) { - if (view->getRevealZone()) { - if (view->getWriteableRevealZone()) { - writeableCard = true; - } else { - revealedCard = true; - } - } - } - - QMenu *cardMenu = new QMenu; - - if (revealedCard) { - cardMenu->addAction(aHide); - cardMenu->addAction(aClone); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - cardMenu->addAction(aSelectColumn); - addRelatedCardView(card, cardMenu); - } else if (writeableCard) { - bool canModifyCard = judge || card->getOwner() == this; - - if (card->getZone()) { - if (card->getZone()->getName() == "table") { - // Card is on the battlefield - - if (!canModifyCard) { - addRelatedCardView(card, cardMenu); - addRelatedCardActions(card, cardMenu); - - cardMenu->addSeparator(); - cardMenu->addAction(aDrawArrow); - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - cardMenu->addAction(aSelectRow); - return cardMenu; - } - - cardMenu->addAction(aTap); - cardMenu->addAction(aDoesntUntap); - cardMenu->addAction(aFlip); - if (card->getFaceDown()) { - cardMenu->addAction(aPeek); - } - - addRelatedCardView(card, cardMenu); - addRelatedCardActions(card, cardMenu); - - cardMenu->addSeparator(); - cardMenu->addAction(aAttach); - if (card->getAttachedTo()) { - cardMenu->addAction(aUnattach); - } - cardMenu->addAction(aDrawArrow); - cardMenu->addSeparator(); - cardMenu->addMenu(createPtMenu()); - cardMenu->addAction(aSetAnnotation); - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addMenu(createMoveMenu()); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - cardMenu->addAction(aSelectRow); - - cardMenu->addSeparator(); - mCardCounters->clear(); - for (int i = 0; i < aAddCounter.size(); ++i) { - mCardCounters->addSeparator(); - mCardCounters->addAction(aAddCounter[i]); - if (card->getCounters().contains(i)) { - mCardCounters->addAction(aRemoveCounter[i]); - } - mCardCounters->addAction(aSetCounter[i]); - } - cardMenu->addSeparator(); - cardMenu->addMenu(mCardCounters); - } else if (card->getZone()->getName() == "stack") { - // Card is on the stack - if (canModifyCard) { - cardMenu->addAction(aAttach); - cardMenu->addAction(aDrawArrow); - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addMenu(createMoveMenu()); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - } else { - cardMenu->addAction(aDrawArrow); - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - } - - addRelatedCardView(card, cardMenu); - addRelatedCardActions(card, cardMenu); - } else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") { - // Card is in the graveyard or exile - if (canModifyCard) { - cardMenu->addAction(aPlay); - cardMenu->addAction(aPlayFacedown); - - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addMenu(createMoveMenu()); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - cardMenu->addAction(aSelectColumn); - - cardMenu->addSeparator(); - cardMenu->addAction(aAttach); - cardMenu->addAction(aDrawArrow); - } else { - cardMenu->addAction(aClone); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - cardMenu->addAction(aSelectColumn); - cardMenu->addSeparator(); - cardMenu->addAction(aDrawArrow); - } - - addRelatedCardView(card, cardMenu); - addRelatedCardActions(card, cardMenu); - } else { - // Card is in hand or a custom zone specified by server - cardMenu->addAction(aPlay); - cardMenu->addAction(aPlayFacedown); - - QMenu *revealMenu = cardMenu->addMenu(tr("Re&veal to...")); - initContextualPlayersMenu(revealMenu); - - connect(revealMenu, &QMenu::triggered, this, &Player::actReveal); - - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addMenu(createMoveMenu()); - - // actions that are really wonky when done from deck or sideboard - if (card->getZone()->getName() == "hand") { - cardMenu->addSeparator(); - cardMenu->addAction(aAttach); - cardMenu->addAction(aDrawArrow); - } - - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - if (qobject_cast(card->getZone())) { - cardMenu->addAction(aSelectColumn); - } - - addRelatedCardView(card, cardMenu); - if (card->getZone()->getName() == "hand") { - addRelatedCardActions(card, cardMenu); - } - } - } else { - cardMenu->addMenu(createMoveMenu()); - } - } else { - if (card->getZone() && card->getZone()->getName() != "hand") { - cardMenu->addAction(aDrawArrow); - cardMenu->addSeparator(); - addRelatedCardView(card, cardMenu); - addRelatedCardActions(card, cardMenu); - cardMenu->addSeparator(); - cardMenu->addAction(aClone); - cardMenu->addSeparator(); - cardMenu->addAction(aSelectAll); - } - } - - return cardMenu; -} - -QMenu *Player::createMoveMenu() const -{ - QMenu *moveMenu = new QMenu("Move to"); - moveMenu->addAction(aMoveToTopLibrary); - moveMenu->addAction(aMoveToXfromTopOfLibrary); - moveMenu->addAction(aMoveToBottomLibrary); - moveMenu->addSeparator(); - moveMenu->addAction(aMoveToHand); - moveMenu->addSeparator(); - moveMenu->addAction(aMoveToGraveyard); - moveMenu->addSeparator(); - moveMenu->addAction(aMoveToExile); - return moveMenu; -} - -QMenu *Player::createPtMenu() const -{ - QMenu *ptMenu = new QMenu("Power / toughness"); - ptMenu->addAction(aIncP); - ptMenu->addAction(aDecP); - ptMenu->addAction(aFlowP); - ptMenu->addSeparator(); - ptMenu->addAction(aIncT); - ptMenu->addAction(aDecT); - ptMenu->addAction(aFlowT); - ptMenu->addSeparator(); - ptMenu->addAction(aIncPT); - ptMenu->addAction(aDecPT); - ptMenu->addSeparator(); - ptMenu->addAction(aSetPT); - ptMenu->addAction(aResetPT); - return ptMenu; -} - -void Player::addRelatedCardView(const CardItem *card, QMenu *cardMenu) -{ - if (!card || !cardMenu) { - return; - } - auto exactCard = card->getCard(); - if (!exactCard) { - return; - } - - bool atLeastOneGoodRelationFound = false; - QList relatedCards = exactCard.getInfo().getAllRelatedCards(); - for (const CardRelation *cardRelation : relatedCards) { - CardInfoPtr relatedCard = CardDatabaseManager::getInstance()->getCardInfo(cardRelation->getName()); - if (relatedCard != nullptr) { - atLeastOneGoodRelationFound = true; - break; - } - } - - if (!atLeastOneGoodRelationFound) { - return; - } - - cardMenu->addSeparator(); - auto viewRelatedCards = new QMenu(tr("View related cards")); - cardMenu->addMenu(viewRelatedCards); - for (const CardRelation *relatedCard : relatedCards) { - QString relatedCardName = relatedCard->getName(); - CardRef cardRef = {relatedCardName, exactCard.getPrinting().getUuid()}; - QAction *viewCard = viewRelatedCards->addAction(relatedCardName); - connect(viewCard, &QAction::triggered, game, [this, cardRef] { game->viewCardInfo(cardRef); }); - } -} - -void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu) -{ - if (!card || !cardMenu) { - return; - } - auto exactCard = card->getCard(); - if (!exactCard) { - return; - } - - QList relatedCards = exactCard.getInfo().getAllRelatedCards(); - if (relatedCards.isEmpty()) { - return; - } - - cardMenu->addSeparator(); - int index = 0; - QAction *createRelatedCards = nullptr; - for (const CardRelation *cardRelation : relatedCards) { - ExactCard relatedCard = CardDatabaseManager::getInstance()->getCardFromSameSet(cardRelation->getName(), - card->getCard().getPrinting()); - - if (!relatedCard) { - continue; - } - - QString relatedCardName; - if (relatedCard.getInfo().getPowTough().size() > 0) { - relatedCardName = relatedCard.getInfo().getPowTough() + " " + relatedCard.getName(); // "n/n name" - } else { - relatedCardName = relatedCard.getName(); // "name" - } - - QString text = tr("Token: "); - if (cardRelation->getDoesAttach()) { - text += - tr(cardRelation->getDoesTransform() ? "Transform into " : "Attach to ") + "\"" + relatedCardName + "\""; - } else if (cardRelation->getIsVariable()) { - text += "X " + relatedCardName; - } else if (cardRelation->getDefaultCount() != 1) { - text += QString::number(cardRelation->getDefaultCount()) + "x " + relatedCardName; - } else { - text += relatedCardName; - } - - if (createRelatedCards == nullptr) { - if (relatedCards.length() == 1) { - createRelatedCards = new QAction(text, this); // set actCreateAllRelatedCards with this text - break; // do not set an individual entry as there is only one entry - } else { - createRelatedCards = new QAction(tr("All tokens"), this); - } - } - - auto *createRelated = new QAction(text, this); - createRelated->setData(QVariant(index++)); - connect(createRelated, &QAction::triggered, this, &Player::actCreateRelatedCard); - cardMenu->addAction(createRelated); - } - - if (createRelatedCards) { - if (shortcutsActive) { - createRelatedCards->setShortcuts( - SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens")); - } - connect(createRelatedCards, &QAction::triggered, this, &Player::actCreateAllRelatedCards); - cardMenu->addAction(createRelatedCards); - } -} - /** * Creates a card menu from the given card and sets it as the currently active card menu. * Will first check if the card should have a card menu, and no-ops if not. @@ -4240,99 +445,32 @@ void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu) * @param card The card to create the menu for. Pass nullptr to disable the card menu. * @return The new card menu, or nullptr if failed. */ -QMenu *Player::updateCardMenu(const CardItem *card) + +void Player::setActive(bool _active) { - if (!card) { - emit cardMenuUpdated(nullptr); - return nullptr; - } - - // If is spectator (as spectators don't need card menus), return - // only update the menu if the card is actually selected - if ((game->getGameState()->isSpectator() && !judge) || game->getActiveCard() != card) { - return nullptr; - } - - QMenu *menu = createCardMenu(card); - emit cardMenuUpdated(menu); - - return menu; + active = _active; + emit activeChanged(active); } -QString Player::getName() const +void Player::updateZones() { - return QString::fromStdString(userInfo->name()); + getTableZone()->reorganizeCards(); } -qreal Player::getMinimumWidth() const +PlayerGraphicsItem *Player::getGraphicsItem() { - qreal result = table->getMinimumWidth() + CARD_HEIGHT + 15 + counterAreaWidth + stack->boundingRect().width(); - if (!SettingsCache::instance().getHorizontalHand()) { - result += hand->boundingRect().width(); - } - return result; + return graphicsItem; +} + +GameScene *Player::getGameScene() +{ + return getGraphicsItem()->getGameScene(); } void Player::setGameStarted() { - if (local) { - aAlwaysRevealTopCard->setChecked(false); - aAlwaysLookAtTopCard->setChecked(false); + if (playerInfo->local) { + emit resetTopCardMenuActions(); } setConceded(false); } - -void Player::setConceded(bool _conceded) -{ - conceded = _conceded; - setVisible(!conceded); - if (conceded) { - clear(); - } - emit playerCountChanged(); -} - -void Player::setZoneId(int _zoneId) -{ - zoneId = _zoneId; - playerArea->setPlayerZoneId(_zoneId); -} - -void Player::setMirrored(bool _mirrored) -{ - if (mirrored != _mirrored) { - mirrored = _mirrored; - rearrangeZones(); - } -} - -void Player::processSceneSizeChange(int newPlayerWidth) -{ - // Extend table (and hand, if horizontal) to accommodate the new player width. - qreal tableWidth = newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stack->boundingRect().width(); - if (!SettingsCache::instance().getHorizontalHand()) { - tableWidth -= hand->boundingRect().width(); - } - - table->setWidth(tableWidth); - hand->setWidth(tableWidth + stack->boundingRect().width()); -} - -void Player::setLastToken(CardInfoPtr cardInfo) -{ - if (cardInfo == nullptr || aCreateAnotherToken == nullptr) { - return; - } - - lastTokenInfo = {.name = cardInfo->getName(), - .color = cardInfo->getColors().isEmpty() ? QString() : cardInfo->getColors().left(1).toLower(), - .pt = cardInfo->getPowTough(), - .annotation = SettingsCache::instance().getAnnotateTokens() ? cardInfo->getText() : "", - .destroy = true, - .providerId = - SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardInfo->getName())}; - - lastTokenTableRow = TableZone::clampValidTableRow(2 - cardInfo->getTableRow()); - aCreateAnotherToken->setText(tr("C&reate another %1 token").arg(lastTokenInfo.name)); - aCreateAnotherToken->setEnabled(true); -} diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index b4f901329..cd7d3f818 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -8,6 +8,12 @@ #include "../filters/filter_string.h" #include "pb/card_attributes.pb.h" #include "pb/game_event.pb.h" +#include "player_actions.h" +#include "player_area.h" +#include "player_event_handler.h" +#include "player_graphics_item.h" +#include "player_info.h" +#include "player_menu.h" #include #include @@ -26,476 +32,176 @@ class Message; } // namespace google class AbstractCardItem; class AbstractCounter; +class AbstractGame; class ArrowItem; class ArrowTarget; class CardDatabase; -class CardItem; class CardZone; class CommandContainer; -class Command_MoveCard; class DeckLoader; -class Event_AttachCard; -class Event_ChangeZoneProperties; -class Event_CreateArrow; -class Event_CreateCounter; -class Event_CreateToken; -class Event_DelCounter; -class Event_DeleteArrow; -class Event_DestroyCard; -class Event_DrawCards; -class Event_DumpZone; -class Event_FlipCard; -class Event_GameSay; -class Event_MoveCard; -class Event_RevealCards; -class Event_RollDie; -class Event_SetCardAttr; -class Event_SetCardCounter; -class Event_SetCounter; -class Event_Shuffle; class GameCommand; class GameEvent; -class GameEventContext; -class HandZone; -class PendingCommand; -class PlayerTarget; +class PlayerInfo; +class PlayerEventHandler; +class PlayerActions; +class PlayerMenu; class QAction; class QMenu; class ServerInfo_Arrow; class ServerInfo_Counter; class ServerInfo_Player; class ServerInfo_User; -class StackZone; class TabGame; -class TableZone; -class ZoneViewZone; const int MAX_TOKENS_PER_DIALOG = 99; -/** - * The entire graphical area belonging to a single player. - */ -class PlayerArea : public QObject, public QGraphicsItem +class Player : public QObject { Q_OBJECT - Q_INTERFACES(QGraphicsItem) -private: - QRectF bRect; - int playerZoneId; -private slots: - void updateBg(); -public: - enum - { - Type = typeOther - }; - int type() const override - { - return Type; - } - - explicit PlayerArea(QGraphicsItem *parent = nullptr); - QRectF boundingRect() const override - { - return bRect; - } - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - - void setSize(qreal width, qreal height); - - void setPlayerZoneId(int _playerZoneId); - int getPlayerZoneId() const - { - return playerZoneId; - } -}; - -class Player : public QObject, public QGraphicsItem -{ - Q_OBJECT - Q_INTERFACES(QGraphicsItem) signals: void openDeckEditor(const DeckLoader *deck); + void deckChanged(); void newCardAdded(AbstractCardItem *card); - // Log events - void logSay(Player *player, QString message); - void logShuffle(Player *player, CardZone *zone, int start, int end); - void logRollDie(Player *player, int sides, const QList &rolls); - void logCreateArrow(Player *player, - Player *startPlayer, - QString startCard, - Player *targetPlayer, - QString targetCard, - bool _playerTarget); - void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown); - void logDrawCards(Player *player, int number, bool deckIsEmpty); - void logUndoDraw(Player *player, QString cardName); - void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX); - void logFlipCard(Player *player, QString cardName, bool faceDown); - void logDestroyCard(Player *player, QString cardName); - void logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName); - void logUnattachCard(Player *player, QString cardName); - void logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue); - void logSetTapped(Player *player, CardItem *card, bool tapped); - void logSetCounter(Player *player, QString counterName, int value, int oldValue); - void logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap); - void logSetPT(Player *player, CardItem *card, QString newPT); - void logSetAnnotation(Player *player, CardItem *card, QString newAnnotation); - void logDumpZone(Player *player, CardZone *zone, int numberCards, bool isReversed = false); - void logRevealCards(Player *player, - CardZone *zone, - int cardId, - QString cardName, - Player *otherPlayer, - bool faceDown, - int amount, - bool isLentToAnotherPlayer = false); - void logAlwaysRevealTopCard(Player *player, CardZone *zone, bool reveal); - void logAlwaysLookAtTopCard(Player *player, CardZone *zone, bool reveal); - - void sizeChanged(); - void playerCountChanged(); - void cardMenuUpdated(QMenu *cardMenu); -public slots: - void actUntapAll(); - void actRollDie(); - void actCreateToken(); - void actCreateAnotherToken(); - void actShuffle(); - void actShuffleTop(); - void actShuffleBottom(); - void actDrawCard(); - void actDrawCards(); - void actUndoDraw(); - void actMulligan(); - - void actPlay(); - void actPlayFacedown(); - void actHide(); - - void actMoveTopCardToPlay(); - void actMoveTopCardToPlayFaceDown(); - void actMoveTopCardToGrave(); - void actMoveTopCardToExile(); - void actMoveTopCardsToGrave(); - void actMoveTopCardsToExile(); - void actMoveTopCardsUntil(); - void actMoveTopCardToBottom(); - void actDrawBottomCard(); - void actDrawBottomCards(); - void actMoveBottomCardToPlay(); - void actMoveBottomCardToPlayFaceDown(); - void actMoveBottomCardToGrave(); - void actMoveBottomCardToExile(); - void actMoveBottomCardsToGrave(); - void actMoveBottomCardsToExile(); - void actMoveBottomCardToTop(); - - void actSelectAll(); - void actSelectRow(); - void actSelectColumn(); - - void actViewLibrary(); - void actViewHand(); - void actViewTopCards(); - void actViewBottomCards(); - void actAlwaysRevealTopCard(); - void actAlwaysLookAtTopCard(); - void actViewGraveyard(); - void actRevealRandomGraveyardCard(); - void actViewRfg(); - void actViewSideboard(); - - void actSayMessage(); -private slots: - void addPlayer(Player *player); - void removePlayer(Player *player); - void playerListActionTriggered(); - - void updateBoundingRect(); - void rearrangeZones(); - - void actOpenDeckInDeckEditor(); - void actCreatePredefinedToken(); - void actCreateRelatedCard(); - void actCreateAllRelatedCards(); - void cardMenuAction(); - void actMoveCardXCardsFromTop(); - void actCardCounterTrigger(); - void actAttach(); - void actUnattach(); - 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 actSetAnnotation(); - void actReveal(QAction *action); - void refreshShortcuts(); - void actSortHand(); - void initSayMenu(); - -public: - enum EventProcessingOption - { - SKIP_REVEAL_WINDOW = 0x0001, - SKIP_TAP_ANIMATION = 0x0002 - }; - Q_DECLARE_FLAGS(EventProcessingOptions, EventProcessingOption) - -private: - TabGame *game; - QMenu *sbMenu, *countersMenu, *sayMenu, *createPredefinedTokenMenu, *mRevealLibrary, *mLendLibrary, *mRevealTopCard, - *mRevealHand, *mRevealRandomHandCard, *mRevealRandomGraveyardCard, *mCustomZones, *mCardCounters; - TearOffMenu *moveGraveMenu, *moveRfgMenu, *graveMenu, *moveHandMenu, *handMenu, *libraryMenu, *topLibraryMenu, - *bottomLibraryMenu, *rfgMenu, *playerMenu; - QList playerLists; - QList singlePlayerLists; - QList allPlayersActions; - QList> playersInfo; - QAction *aMoveHandToTopLibrary, *aMoveHandToBottomLibrary, *aMoveHandToGrave, *aMoveHandToRfg, - *aMoveGraveToTopLibrary, *aMoveGraveToBottomLibrary, *aMoveGraveToHand, *aMoveGraveToRfg, *aMoveRfgToTopLibrary, - *aMoveRfgToBottomLibrary, *aMoveRfgToHand, *aMoveRfgToGrave, *aViewHand, *aViewLibrary, *aViewTopCards, - *aViewBottomCards, *aAlwaysRevealTopCard, *aAlwaysLookAtTopCard, *aOpenDeckInDeckEditor, - *aMoveTopCardToGraveyard, *aMoveTopCardToExile, *aMoveTopCardsToGraveyard, *aMoveTopCardsToExile, - *aMoveTopCardsUntil, *aMoveTopCardToBottom, *aViewGraveyard, *aViewRfg, *aViewSideboard, *aDrawCard, - *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aShuffleTopCards, *aShuffleBottomCards, *aMoveTopToPlay, - *aMoveTopToPlayFaceDown, *aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken, *aMoveBottomToPlay, - *aMoveBottomToPlayFaceDown, *aMoveBottomCardToTop, *aMoveBottomCardToGraveyard, *aMoveBottomCardToExile, - *aMoveBottomCardsToGraveyard, *aMoveBottomCardsToExile, *aDrawBottomCard, *aDrawBottomCards; - - QList aAddCounter, aSetCounter, aRemoveCounter; - QAction *aPlay, *aPlayFacedown, *aHide, *aTap, *aDoesntUntap, *aAttach, *aUnattach, *aDrawArrow, *aSetPT, *aResetPT, - *aIncP, *aDecP, *aIncT, *aDecT, *aIncPT, *aDecPT, *aFlowP, *aFlowT, *aSetAnnotation, *aFlip, *aPeek, *aClone, - *aMoveToTopLibrary, *aMoveToBottomLibrary, *aMoveToHand, *aMoveToGraveyard, *aMoveToExile, - *aMoveToXfromTopOfLibrary, *aSelectAll, *aSelectRow, *aSelectColumn, *aSortHand, *aIncrementAllCardCounters; - - bool movingCardsUntil; - QTimer *moveTopCardTimer; - QStringList movingCardsUntilExprs = {}; - int movingCardsUntilNumberOfHits = 1; - bool movingCardsUntilAutoPlay = false; - FilterString movingCardsUntilFilter; - int movingCardsUntilCounter = 0; - void stopMoveTopCardsUntil(); - - bool shortcutsActive; - int defaultNumberTopCards = 1; - int defaultNumberTopCardsToPlaceBelow = 1; - int defaultNumberBottomCards = 1; - int defaultNumberDieRoll = 20; - - TokenInfo lastTokenInfo; - int lastTokenTableRow; - - ServerInfo_User *userInfo; - int id; - bool active; - bool local; - bool judge; - bool mirrored; - bool handVisible; - bool conceded; - int zoneId; - - bool dialogSemaphore; - bool clearCardsToDelete(); - QList cardsToDelete; - - DeckLoader *deck; - QStringList predefinedTokens; - - PlayerArea *playerArea; - QMap zones; - StackZone *stack; - TableZone *table; - HandZone *hand; - PlayerTarget *playerTarget; - - void setCardAttrHelper(const GameEventContext &context, - CardItem *card, - CardAttribute attribute, - const QString &avalue, - bool allCards, - EventProcessingOptions options); - QMenu *createMoveMenu() const; - QMenu *createPtMenu() const; - void addRelatedCardActions(const CardItem *card, QMenu *cardMenu); - void addRelatedCardView(const CardItem *card, QMenu *cardMenu); - void createCard(const CardItem *sourceCard, - const QString &dbCardName, - CardRelation::AttachType attach = CardRelation::DoesNotAttach, - bool persistent = false); - bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); - void moveOneCardUntil(CardItem *card); - void addPlayerToList(QMenu *playerList, Player *player); - static void removePlayerFromList(QMenu *playerList, Player *player); - - void playSelectedCards(bool faceDown = false); - - QRectF bRect; - - QMap counters; - QMap arrows; void rearrangeCounters(); + void activeChanged(bool active); + void concededChanged(int playerId, bool conceded); + void clearCustomZonesMenu(); + void addViewCustomZoneActionToCustomZoneMenu(QString zoneName); + void resetTopCardMenuActions(); - void initContextualPlayersMenu(QMenu *menu); - - // void eventConnectionStateChanged(const Event_ConnectionStateChanged &event); - void eventGameSay(const Event_GameSay &event); - void eventShuffle(const Event_Shuffle &event); - void eventRollDie(const Event_RollDie &event); - void eventCreateArrow(const Event_CreateArrow &event); - void eventDeleteArrow(const Event_DeleteArrow &event); - void eventCreateToken(const Event_CreateToken &event); - void - eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context, EventProcessingOptions options); - void eventSetCardCounter(const Event_SetCardCounter &event); - void eventCreateCounter(const Event_CreateCounter &event); - void eventSetCounter(const Event_SetCounter &event); - void eventDelCounter(const Event_DelCounter &event); - void eventDumpZone(const Event_DumpZone &event); - void eventMoveCard(const Event_MoveCard &event, const GameEventContext &context); - void eventFlipCard(const Event_FlipCard &event); - void eventDestroyCard(const Event_DestroyCard &event); - void eventAttachCard(const Event_AttachCard &event); - void eventDrawCards(const Event_DrawCards &event); - void eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options); - void eventChangeZoneProperties(const Event_ChangeZoneProperties &event); - void cmdSetTopCard(Command_MoveCard &cmd); - void cmdSetBottomCard(Command_MoveCard &cmd); - - QVariantList parsePT(const QString &pt); +public slots: + void setActive(bool _active); public: - static const int counterAreaWidth = 55; - enum CardMenuActionType - { - cmTap, - cmUntap, - cmDoesntUntap, - cmFlip, - cmPeek, - cmClone, - cmMoveToTopLibrary, - cmMoveToBottomLibrary, - cmMoveToHand, - cmMoveToGraveyard, - cmMoveToExile - }; - enum CardsToReveal - { - RANDOM_CARD_FROM_ZONE = -2 - }; + Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent); + ~Player() override; - enum - { - Type = typeOther - }; - int type() const override - { - return Type; - } - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void initializeZones(); + void updateZones(); + void clear(); + + void processPlayerInfo(const ServerInfo_Player &info); + void processCardAttachment(const ServerInfo_Player &info); - void playCard(CardItem *c, bool faceDown); - void playCardToTable(const CardItem *c, bool faceDown); void addCard(CardItem *c); void deleteCard(CardItem *c); + bool clearCardsToDelete(); + + bool getActive() const + { + return active; + } + + AbstractGame *getGame() const + { + return game; + } + + GameScene *getGameScene(); + + [[nodiscard]] PlayerGraphicsItem *getGraphicsItem(); + + [[nodiscard]] PlayerActions *getPlayerActions() const + { + return playerActions; + }; + + [[nodiscard]] PlayerEventHandler *getPlayerEventHandler() const + { + return playerEventHandler; + } + + [[nodiscard]] PlayerInfo *getPlayerInfo() const + { + return playerInfo; + }; + + [[nodiscard]] PlayerMenu *getPlayerMenu() const + { + return playerMenu; + } + + void setDeck(const DeckLoader &_deck); + + [[nodiscard]] DeckLoader *getDeck() const + { + return deck; + } + template T *addZone(T *zone) { zones.insert(zone->getName(), zone); return zone; } + CardZoneLogic *getZone(const QString zoneName) + { + return zones.value(zoneName); + } + + const QMap &getZones() const + { + return zones; + } + + PileZoneLogic *getDeckZone() + { + return qobject_cast(zones.value("deck")); + } + + PileZoneLogic *getGraveZone() + { + return qobject_cast(zones.value("grave")); + } + + PileZoneLogic *getRfgZone() + { + return qobject_cast(zones.value("rfg")); + } + + PileZoneLogic *getSideboardZone() + { + return qobject_cast(zones.value("sb")); + } + + TableZoneLogic *getTableZone() + { + return qobject_cast(zones.value("table")); + } + + StackZoneLogic *getStackZone() + { + return qobject_cast(zones.value("stack")); + } + + HandZoneLogic *getHandZone() + { + return qobject_cast(zones.value("hand")); + } + AbstractCounter *addCounter(const ServerInfo_Counter &counter); AbstractCounter *addCounter(int counterId, const QString &name, QColor color, int radius, int value); void delCounter(int counterId); void clearCounters(); void incrementAllCardCounters(); + QMap getCounters() + { + return counters; + } + ArrowItem *addArrow(const ServerInfo_Arrow &arrow); ArrowItem *addArrow(int arrowId, CardItem *startCard, ArrowTarget *targetItem, const QColor &color); void delArrow(int arrowId); void removeArrow(ArrowItem *arrow); void clearArrows(); - PlayerTarget *getPlayerTarget() const - { - return playerTarget; - } - Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, TabGame *_parent); - ~Player() override; - - void retranslateUi(); - void clear(); - TabGame *getGame() const - { - return game; - } - void setDeck(const DeckLoader &_deck); - QMenu *getPlayerMenu() const - { - return playerMenu; - } - int getId() const - { - return id; - } - QString getName() const; - ServerInfo_User *getUserInfo() const - { - return userInfo; - } - bool getLocal() const - { - return local; - } - bool getLocalOrJudge() const - { - return local || judge; - } - bool getJudge() const - { - return judge; - } - bool getMirrored() const - { - return mirrored; - } - int getZoneId() const - { - return zoneId; - } - void setZoneId(int _zoneId); - const QMap &getZones() const - { - return zones; - } const QMap &getArrows() const { return arrows; } - QMenu *updateCardMenu(const CardItem *card); - QMenu *createCardMenu(const CardItem *card); - bool getActive() const - { - return active; - } - void setActive(bool _active); - void setShortcutsActive(); - void setShortcutsInactive(); - void updateZones(); void setConceded(bool _conceded); bool getConceded() const @@ -505,35 +211,49 @@ public: void setGameStarted(); - qreal getMinimumWidth() const; - void setMirrored(bool _mirrored); - void processSceneSizeChange(int newPlayerWidth); + void setDialogSemaphore(const bool _active) + { + dialogSemaphore = _active; + } - void processPlayerInfo(const ServerInfo_Player &info); - void processCardAttachment(const ServerInfo_Player &info); + int getZoneId() const + { + return zoneId; + } - void processGameEvent(GameEvent::GameEventType type, - const GameEvent &event, - const GameEventContext &context, - EventProcessingOptions options); + void setZoneId(int _zoneId); - PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd); - PendingCommand *prepareGameCommand(const QList &cmdList); - void sendGameCommand(PendingCommand *pend); - void sendGameCommand(const google::protobuf::Message &command); +private: + AbstractGame *game; + PlayerInfo *playerInfo; + PlayerEventHandler *playerEventHandler; + PlayerActions *playerActions; + PlayerMenu *playerMenu; + PlayerGraphicsItem *graphicsItem; - void setLastToken(CardInfoPtr cardInfo); + bool active; + bool conceded; + + DeckLoader *deck; + + int zoneId; + QMap zones; + QMap counters; + QMap arrows; + + bool dialogSemaphore; + QList cardsToDelete; + + // void eventConnectionStateChanged(const Event_ConnectionStateChanged &event); }; -Q_DECLARE_OPERATORS_FOR_FLAGS(Player::EventProcessingOptions) - class AnnotationDialog : public QInputDialog { Q_OBJECT void keyPressEvent(QKeyEvent *e) override; public: - explicit AnnotationDialog(QWidget *parent) : QInputDialog(parent) + explicit AnnotationDialog(QWidget *parent = nullptr) : QInputDialog(parent) { } }; diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp new file mode 100644 index 000000000..bb96ecb67 --- /dev/null +++ b/cockatrice/src/game/player/player_actions.cpp @@ -0,0 +1,1895 @@ +#include "player_actions.h" + +#include "../../../common/pb/context_move_card.pb.h" +#include "../../client/get_text_with_max.h" +#include "../../client/tabs/tab_game.h" +#include "../../dialogs/dlg_move_top_cards_until.h" +#include "../../dialogs/dlg_roll_dice.h" +#include "../board/card_item.h" +#include "../cards/card_database_manager.h" +#include "../zones/logic/view_zone_logic.h" +#include "card_menu_action_type.h" +#include "pb/command_attach_card.pb.h" +#include "pb/command_change_zone_properties.pb.h" +#include "pb/command_concede.pb.h" +#include "pb/command_create_token.pb.h" +#include "pb/command_draw_cards.pb.h" +#include "pb/command_flip_card.pb.h" +#include "pb/command_game_say.pb.h" +#include "pb/command_move_card.pb.h" +#include "pb/command_mulligan.pb.h" +#include "pb/command_reveal_cards.pb.h" +#include "pb/command_roll_die.pb.h" +#include "pb/command_set_card_attr.pb.h" +#include "pb/command_set_card_counter.pb.h" +#include "pb/command_shuffle.pb.h" +#include "pb/command_undo_draw.pb.h" +#include "trice_limits.h" + +// milliseconds in between triggers of the move top cards until action +static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; + +PlayerActions::PlayerActions(Player *_player) : player(_player), lastTokenTableRow(0), movingCardsUntil(false) +{ + moveTopCardTimer = new QTimer(this); + moveTopCardTimer->setInterval(MOVE_TOP_CARD_UNTIL_INTERVAL); + moveTopCardTimer->setSingleShot(true); + connect(moveTopCardTimer, &QTimer::timeout, [this]() { actMoveTopCardToPlay(); }); +} + +void PlayerActions::playCard(CardItem *card, bool faceDown) +{ + if (card == nullptr) { + return; + } + + Command_MoveCard cmd; + cmd.set_start_player_id(card->getZone()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_start_zone(card->getZone()->getName().toStdString()); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(card->getId()); + + ExactCard exactCard = card->getCard(); + if (!exactCard) { + return; + } + + const CardInfo &info = exactCard.getInfo(); + + int tableRow = info.getTableRow(); + bool playToStack = SettingsCache::instance().getPlayToStack(); + QString currentZone = card->getZone()->getName(); + if (currentZone == "stack" && tableRow == 3) { + cmd.set_target_zone("grave"); + cmd.set_x(0); + cmd.set_y(0); + } else if (!faceDown && + ((!playToStack && tableRow == 3) || ((playToStack && tableRow != 0) && currentZone != "stack"))) { + cmd.set_target_zone("stack"); + cmd.set_x(-1); + cmd.set_y(0); + } else { + tableRow = faceDown ? 2 : info.getTableRow(); + QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow)); + cardToMove->set_face_down(faceDown); + if (!faceDown) { + cardToMove->set_pt(info.getPowTough().toStdString()); + } + cardToMove->set_tapped(!faceDown && info.getCipt()); + if (tableRow != 3) + cmd.set_target_zone("table"); + cmd.set_x(gridPoint.x()); + cmd.set_y(gridPoint.y()); + } + sendGameCommand(cmd); +} + +/** + * Like {@link Player::playCard}, but forces the card to be played to the table zone. + * Cards with tablerow 3 (the stack) will be played to tablerow 1 (the noncreatures row). + */ +void PlayerActions::playCardToTable(const CardItem *card, bool faceDown) +{ + if (card == nullptr) { + return; + } + + Command_MoveCard cmd; + cmd.set_start_player_id(card->getZone()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_start_zone(card->getZone()->getName().toStdString()); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(card->getId()); + + ExactCard exactCard = card->getCard(); + if (!exactCard) { + return; + } + + const CardInfo &info = exactCard.getInfo(); + + int tableRow = faceDown ? 2 : info.getTableRow(); + // default instant/sorcery cards to the noncreatures row + if (tableRow > 2) { + tableRow = 1; + } + + QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow)); + cardToMove->set_face_down(faceDown); + if (!faceDown) { + cardToMove->set_pt(info.getPowTough().toStdString()); + } + cardToMove->set_tapped(!faceDown && info.getCipt()); + cmd.set_target_zone("table"); + cmd.set_x(gridPoint.x()); + cmd.set_y(gridPoint.y()); + sendGameCommand(cmd); +} + +void PlayerActions::actViewLibrary() +{ + player->getGameScene()->toggleZoneView(player, "deck", -1); +} + +void PlayerActions::actViewHand() +{ + player->getGameScene()->toggleZoneView(player, "hand", -1); +} + +void PlayerActions::actSortHand() +{ + player->getGraphicsItem()->getHandZoneGraphicsItem()->sortHand(); +} + +void PlayerActions::actViewTopCards() +{ + 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; + player->getGameScene()->toggleZoneView(player, "deck", number); + } +} + +void PlayerActions::actViewBottomCards() +{ + 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; + player->getGameScene()->toggleZoneView(player, "deck", number, true); + } +} + +void PlayerActions::actAlwaysRevealTopCard() +{ + Command_ChangeZoneProperties cmd; + cmd.set_zone_name("deck"); + cmd.set_always_reveal_top_card(player->getPlayerMenu()->isAlwaysRevealTopCardChecked()); + + sendGameCommand(cmd); +} + +void PlayerActions::actAlwaysLookAtTopCard() +{ + Command_ChangeZoneProperties cmd; + cmd.set_zone_name("deck"); + cmd.set_always_look_at_top_card(player->getPlayerMenu()->isAlwaysLookAtTopCardChecked()); + + sendGameCommand(cmd); +} + +void PlayerActions::actOpenDeckInDeckEditor() +{ + emit player->openDeckEditor(player->getDeck()); +} + +void PlayerActions::actViewGraveyard() +{ + player->getGameScene()->toggleZoneView(player, "grave", -1); +} + +void PlayerActions::actRevealRandomGraveyardCard() +{ + Command_RevealCards cmd; + auto *action = dynamic_cast(sender()); + const int otherPlayerId = action->data().toInt(); + if (otherPlayerId != -1) { + cmd.set_player_id(otherPlayerId); + } + cmd.set_zone_name("grave"); + cmd.add_card_id(RANDOM_CARD_FROM_ZONE); + sendGameCommand(cmd); +} + +void PlayerActions::actViewRfg() +{ + player->getGameScene()->toggleZoneView(player, "rfg", -1); +} + +void PlayerActions::actViewSideboard() +{ + player->getGameScene()->toggleZoneView(player, "sb", -1); +} + +void PlayerActions::actShuffle() +{ + sendGameCommand(Command_Shuffle()); +} + +void PlayerActions::actShuffleTop() +{ + 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) { + return; + } + + if (number > maxCards) { + number = maxCards; + } + + defaultNumberTopCards = number; + + Command_Shuffle cmd; + cmd.set_zone_name("deck"); + cmd.set_start(0); + cmd.set_end(number - 1); // inclusive, the indexed card at end will be shuffled + + sendGameCommand(cmd); +} + +void PlayerActions::actShuffleBottom() +{ + 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) { + return; + } + + if (number > maxCards) { + number = maxCards; + } + + defaultNumberBottomCards = number; + + Command_Shuffle cmd; + cmd.set_zone_name("deck"); + cmd.set_start(-number); + cmd.set_end(-1); + + sendGameCommand(cmd); +} + +void PlayerActions::actDrawCard() +{ + Command_DrawCards cmd; + cmd.set_number(1); + sendGameCommand(cmd); +} + +void PlayerActions::actMulligan() +{ + int startSize = SettingsCache::instance().getStartingHandSize(); + int handSize = player->getHandZone()->getCards().size(); + int deckSize = player->getDeckZone()->getCards().size() + handSize; // hand is shuffled back into the deck + 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); + if (!ok) { + return; + } + Command_Mulligan cmd; + if (number < 1) { + if (handSize == 0) { + return; + } + cmd.set_number(handSize + number); + } else { + cmd.set_number(number); + } + sendGameCommand(cmd); + if (startSize != number) { + SettingsCache::instance().setStartingHandSize(number); + } +} + +void PlayerActions::actDrawCards() +{ + 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); + } +} + +void PlayerActions::actUndoDraw() +{ + sendGameCommand(Command_UndoDraw()); +} + +void PlayerActions::cmdSetTopCard(Command_MoveCard &cmd) +{ + cmd.set_start_zone("deck"); + auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(0); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); +} + +void PlayerActions::cmdSetBottomCard(Command_MoveCard &cmd) +{ + CardZoneLogic *zone = player->getDeckZone(); + int lastCard = zone->getCards().size() - 1; + cmd.set_start_zone("deck"); + auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(lastCard); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); +} + +void PlayerActions::actMoveTopCardToGrave() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetTopCard(cmd); + cmd.set_target_zone("grave"); + cmd.set_x(0); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveTopCardToExile() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetTopCard(cmd); + cmd.set_target_zone("rfg"); + cmd.set_x(0); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveTopCardsToGrave() +{ + 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 grave"), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, + maxCards, 1, &ok); + if (!ok) { + return; + } else if (number > maxCards) { + number = maxCards; + } + defaultNumberTopCards = number; + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("grave"); + cmd.set_x(0); + cmd.set_y(0); + + for (int i = number - 1; i >= 0; --i) { + cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + } + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveTopCardsToExile() +{ + 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 exile"), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, + maxCards, 1, &ok); + if (!ok) { + return; + } else if (number > maxCards) { + number = maxCards; + } + defaultNumberTopCards = number; + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("rfg"); + cmd.set_x(0); + cmd.set_y(0); + + for (int i = number - 1; i >= 0; --i) { + cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + } + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveTopCardsUntil() +{ + stopMoveTopCardsUntil(); + + DlgMoveTopCardsUntil dlg(player->getGame()->getTab(), movingCardsUntilExprs, movingCardsUntilNumberOfHits, + movingCardsUntilAutoPlay); + if (!dlg.exec()) { + return; + } + + auto expr = dlg.getExpr(); + movingCardsUntilExprs = dlg.getExprs(); + movingCardsUntilNumberOfHits = dlg.getNumberOfHits(); + movingCardsUntilAutoPlay = dlg.isAutoPlay(); + + if (player->getDeckZone()->getCards().empty()) { + stopMoveTopCardsUntil(); + } else { + movingCardsUntilFilter = FilterString(expr); + movingCardsUntilCounter = movingCardsUntilNumberOfHits; + movingCardsUntil = true; + actMoveTopCardToPlay(); + } +} + +void PlayerActions::moveOneCardUntil(CardItem *card) +{ + moveTopCardTimer->stop(); + + const bool isMatch = card && movingCardsUntilFilter.check(card->getCard().getCardPtr()); + + if (isMatch && movingCardsUntilAutoPlay) { + // Directly calling playCard will deadlock, since we are already in the middle of processing an event. + // Use QTimer::singleShot to queue up the playCard on the event loop. + QTimer::singleShot(0, this, [card, this] { playCard(card, false); }); + } + + if (player->getDeckZone()->getCards().empty() || !card) { + stopMoveTopCardsUntil(); + } else if (isMatch) { + --movingCardsUntilCounter; + if (movingCardsUntilCounter > 0) { + moveTopCardTimer->start(); + } else { + stopMoveTopCardsUntil(); + } + } else { + moveTopCardTimer->start(); + } +} + +/** + * @brief Immediately stops any ongoing `play top card to stack until...` process, resetting all variables involved. + */ +void PlayerActions::stopMoveTopCardsUntil() +{ + moveTopCardTimer->stop(); + movingCardsUntilCounter = 0; + movingCardsUntil = false; +} + +void PlayerActions::actMoveTopCardToBottom() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetTopCard(cmd); + cmd.set_target_zone("deck"); + cmd.set_x(-1); // bottom of deck + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveTopCardToPlay() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetTopCard(cmd); + cmd.set_target_zone("stack"); + cmd.set_x(-1); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveTopCardToPlayFaceDown() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(0); + cardToMove->set_face_down(true); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("table"); + cmd.set_x(-1); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardToGrave() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetBottomCard(cmd); + cmd.set_target_zone("grave"); + cmd.set_x(0); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardToExile() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetBottomCard(cmd); + cmd.set_target_zone("rfg"); + cmd.set_x(0); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardsToGrave() +{ + 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 grave"), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, + maxCards, 1, &ok); + if (!ok) { + return; + } else if (number > maxCards) { + number = maxCards; + } + defaultNumberBottomCards = number; + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("grave"); + cmd.set_x(0); + cmd.set_y(0); + + for (int i = maxCards - number; i < maxCards; ++i) { + cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + } + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardsToExile() +{ + 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 exile"), + tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, + maxCards, 1, &ok); + if (!ok) { + return; + } else if (number > maxCards) { + number = maxCards; + } + defaultNumberBottomCards = number; + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("rfg"); + cmd.set_x(0); + cmd.set_y(0); + + for (int i = maxCards - number; i < maxCards; ++i) { + cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + } + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardToTop() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetBottomCard(cmd); + cmd.set_target_zone("deck"); + cmd.set_x(0); // top of deck + cmd.set_y(0); + + sendGameCommand(cmd); +} + +/** + * Selects all cards in the given zone. + * + * @param zone The zone to select from + * @param filter A predicate to filter which cards are selected. Defaults to always returning true. + */ +static void selectCardsInZone( + const CardZoneLogic *zone, + std::function filter = [](const CardItem *) { return true; }) +{ + if (!zone) { + return; + } + + for (auto &cardItem : zone->getCards()) { + if (cardItem && filter(cardItem)) { + cardItem->setSelected(true); + } + } +} + +void PlayerActions::actSelectAll() +{ + const CardItem *card = player->getGame()->getActiveCard(); + if (!card) { + return; + } + + selectCardsInZone(card->getZone()); +} + +void PlayerActions::actSelectRow() +{ + const CardItem *card = player->getGame()->getActiveCard(); + if (!card) { + return; + } + + auto isSameRow = [card](const CardItem *cardItem) { + return qAbs(card->scenePos().y() - cardItem->scenePos().y()) < 50; + }; + selectCardsInZone(card->getZone(), isSameRow); +} + +void PlayerActions::actSelectColumn() +{ + const CardItem *card = player->getGame()->getActiveCard(); + if (!card) { + return; + } + + auto isSameColumn = [card](const CardItem *cardItem) { return cardItem->x() == card->x(); }; + selectCardsInZone(card->getZone(), isSameColumn); +} + +void PlayerActions::actDrawBottomCard() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetBottomCard(cmd); + cmd.set_target_zone("hand"); + cmd.set_x(0); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actDrawBottomCards() +{ + 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) { + return; + } else if (number > maxCards) { + number = maxCards; + } + defaultNumberBottomCards = number; + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("hand"); + cmd.set_x(0); + cmd.set_y(0); + + for (int i = maxCards - number; i < maxCards; ++i) { + cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + } + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardToPlay() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + Command_MoveCard cmd; + cmdSetBottomCard(cmd); + cmd.set_target_zone("stack"); + cmd.set_x(-1); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actMoveBottomCardToPlayFaceDown() +{ + if (player->getDeckZone()->getCards().empty()) { + return; + } + + CardZoneLogic *zone = player->getDeckZone(); + int lastCard = zone->getCards().size() - 1; + + Command_MoveCard cmd; + cmd.set_start_zone("deck"); + auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(lastCard); + cardToMove->set_face_down(true); + + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone("table"); + cmd.set_x(-1); + cmd.set_y(0); + + sendGameCommand(cmd); +} + +void PlayerActions::actUntapAll() +{ + Command_SetCardAttr cmd; + cmd.set_zone("table"); + cmd.set_attribute(AttrTapped); + cmd.set_attr_value("0"); + + sendGameCommand(cmd); +} + +void PlayerActions::actRollDie() +{ + DlgRollDice dlg(player->getGame()->getTab()); + if (!dlg.exec()) { + return; + } + + Command_RollDie cmd; + cmd.set_sides(dlg.getDieSideCount()); + cmd.set_count(dlg.getDiceToRollCount()); + sendGameCommand(cmd); +} + +void PlayerActions::actCreateToken() +{ + DlgCreateToken dlg(player->getPlayerMenu()->getPredefinedTokens(), player->getGame()->getTab()); + if (!dlg.exec()) { + return; + } + + lastTokenInfo = dlg.getTokenInfo(); + + ExactCard correctedCard = + CardDatabaseManager::getInstance()->guessCard({lastTokenInfo.name, lastTokenInfo.providerId}); + if (correctedCard) { + lastTokenInfo.name = correctedCard.getName(); + lastTokenTableRow = TableZone::clampValidTableRow(2 - correctedCard.getInfo().getTableRow()); + if (lastTokenInfo.pt.isEmpty()) { + lastTokenInfo.pt = correctedCard.getInfo().getPowTough(); + } + } + + player->getPlayerMenu()->setAndEnableCreateAnotherTokenAction( + tr("C&reate another %1 token").arg(lastTokenInfo.name)); + actCreateAnotherToken(); +} + +void PlayerActions::actCreateAnotherToken() +{ + if (lastTokenInfo.name.isEmpty()) { + return; + } + + Command_CreateToken cmd; + cmd.set_zone("table"); + cmd.set_card_name(lastTokenInfo.name.toStdString()); + cmd.set_card_provider_id(lastTokenInfo.providerId.toStdString()); + cmd.set_color(lastTokenInfo.color.toStdString()); + cmd.set_pt(lastTokenInfo.pt.toStdString()); + cmd.set_annotation(lastTokenInfo.annotation.toStdString()); + cmd.set_destroy_on_zone_change(lastTokenInfo.destroy); + cmd.set_face_down(lastTokenInfo.faceDown); + cmd.set_x(-1); + cmd.set_y(lastTokenTableRow); + + sendGameCommand(cmd); +} + +void PlayerActions::setLastToken(CardInfoPtr cardInfo) +{ + if (cardInfo == nullptr || !player->getPlayerMenu()->createAnotherTokenActionExists()) { + return; + } + + lastTokenInfo = {.name = cardInfo->getName(), + .color = cardInfo->getColors().isEmpty() ? QString() : cardInfo->getColors().left(1).toLower(), + .pt = cardInfo->getPowTough(), + .annotation = SettingsCache::instance().getAnnotateTokens() ? cardInfo->getText() : "", + .destroy = true, + .providerId = + SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardInfo->getName())}; + + lastTokenTableRow = TableZone::clampValidTableRow(2 - cardInfo->getTableRow()); + + player->getPlayerMenu()->setAndEnableCreateAnotherTokenAction( + tr("C&reate another %1 token").arg(lastTokenInfo.name)); +} + +void PlayerActions::actCreatePredefinedToken() +{ + auto *action = static_cast(sender()); + CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardInfo(action->text()); + if (!cardInfo) { + return; + } + + setLastToken(cardInfo); + + actCreateAnotherToken(); +} + +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::getInstance()->getCardFromSameSet( + cardRelation->getName(), sourceCard->getCard().getPrinting()); + setLastToken(relatedCard.getCardPtr()); + } +} + +void PlayerActions::actCreateAllRelatedCards() +{ + const CardItem *sourceCard = player->getGame()->getActiveCard(); + if (!sourceCard) { + return; + } + + auto relatedCards = sourceCard->getCardInfo().getAllRelatedCards(); + if (relatedCards.isEmpty()) { + return; + } + + CardRelation *cardRelation = nullptr; + int tokensTypesCreated = 0; + + if (relatedCards.length() == 1) { + cardRelation = relatedCards.at(0); + if (createRelatedFromRelation(sourceCard, cardRelation)) { + ++tokensTypesCreated; + } + } else { + QList nonExcludedRelatedCards; + QString dbName; + for (CardRelation *cardRelationTemp : relatedCards) { + if (!cardRelationTemp->getIsCreateAllExclusion() && !cardRelationTemp->getDoesAttach()) { + nonExcludedRelatedCards.append(cardRelationTemp); + } + } + switch (nonExcludedRelatedCards.length()) { + case 1: // if nonExcludedRelatedCards == 1 + cardRelation = nonExcludedRelatedCards.at(0); + if (createRelatedFromRelation(sourceCard, cardRelation)) { + ++tokensTypesCreated; + } + break; + // If all are marked "Exclude", then treat the situation as if none of them are. + // We won't accept "garbage in, garbage out", here. + case 0: // else if nonExcludedRelatedCards == 0 + for (CardRelation *cardRelationAll : relatedCards) { + if (!cardRelationAll->getDoesAttach() && !cardRelationAll->getIsVariable()) { + dbName = cardRelationAll->getName(); + bool persistent = cardRelationAll->getIsPersistent(); + for (int i = 0; i < cardRelationAll->getDefaultCount(); ++i) { + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); + } + ++tokensTypesCreated; + if (tokensTypesCreated == 1) { + cardRelation = cardRelationAll; + } + } + } + break; + default: // else + for (CardRelation *cardRelationNotExcluded : nonExcludedRelatedCards) { + if (!cardRelationNotExcluded->getDoesAttach() && !cardRelationNotExcluded->getIsVariable()) { + dbName = cardRelationNotExcluded->getName(); + bool persistent = cardRelationNotExcluded->getIsPersistent(); + for (int i = 0; i < cardRelationNotExcluded->getDefaultCount(); ++i) { + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); + } + ++tokensTypesCreated; + if (tokensTypesCreated == 1) { + cardRelation = cardRelationNotExcluded; + } + } + } + break; + } + } + + /* + * If we made at least one token via "Create All Tokens" + * then assign the first to the "Create another" shortcut. + */ + if (cardRelation != nullptr && cardRelation->getCanCreateAnother()) { + CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardInfo(cardRelation->getName()); + setLastToken(cardInfo); + } +} + +bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation) +{ + if (sourceCard == nullptr || cardRelation == nullptr) { + return false; + } + QString dbName = cardRelation->getName(); + bool persistent = cardRelation->getIsPersistent(); + if (cardRelation->getIsVariable()) { + bool ok; + 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) { + return false; + } + for (int i = 0; i < count; ++i) { + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); + } + } else if (cardRelation->getDefaultCount() > 1) { + for (int i = 0; i < cardRelation->getDefaultCount(); ++i) { + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); + } + } else { + auto attachType = cardRelation->getAttachType(); + + // 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 == CardRelation::AttachTo && sourceCard->getZone()->getName() != "table") { + playCardToTable(sourceCard, false); + } + + createCard(sourceCard, dbName, attachType, persistent); + } + return true; +} + +void PlayerActions::createCard(const CardItem *sourceCard, + const QString &dbCardName, + CardRelation::AttachType attachType, + bool persistent) +{ + CardInfoPtr cardInfo = CardDatabaseManager::getInstance()->getCardInfo(dbCardName); + + if (cardInfo == nullptr || sourceCard == nullptr) { + return; + } + + // get the target token's location + // TODO: Define this QPoint into its own function along with the one below + QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - cardInfo->getTableRow())); + + // create the token for the related card + Command_CreateToken cmd; + cmd.set_zone("table"); + cmd.set_card_name(cardInfo->getName().toStdString()); + switch (cardInfo->getColors().size()) { + case 0: + cmd.set_color(""); + break; + case 1: + cmd.set_color("m"); + break; + default: + cmd.set_color(cardInfo->getColors().left(1).toLower().toStdString()); + break; + } + + cmd.set_pt(cardInfo->getPowTough().toStdString()); + if (SettingsCache::instance().getAnnotateTokens()) { + cmd.set_annotation(cardInfo->getText().toStdString()); + } else { + cmd.set_annotation(""); + } + cmd.set_destroy_on_zone_change(!persistent); + cmd.set_x(gridPoint.x()); + cmd.set_y(gridPoint.y()); + + ExactCard relatedCard = CardDatabaseManager::getInstance()->getCardFromSameSet(cardInfo->getName(), + sourceCard->getCard().getPrinting()); + + switch (attachType) { + case CardRelation::DoesNotAttach: + cmd.set_target_zone("table"); + cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); + break; + + case CardRelation::AttachTo: + cmd.set_target_zone("table"); // We currently only support creating tokens on the table + cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); + cmd.set_target_card_id(sourceCard->getId()); + cmd.set_target_mode(Command_CreateToken::ATTACH_TO); + break; + + case CardRelation::TransformInto: + // allow cards to directly transform on stack + cmd.set_zone(sourceCard->getZone()->getName() == "stack" ? "stack" : "table"); + // Transform card zone changes are handled server-side + cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); + cmd.set_target_card_id(sourceCard->getId()); + cmd.set_target_mode(Command_CreateToken::TRANSFORM_INTO); + cmd.set_card_provider_id(sourceCard->getProviderId().toStdString()); + break; + } + + sendGameCommand(cmd); +} + +void PlayerActions::actSayMessage() +{ + auto *a = qobject_cast(sender()); + Command_GameSay cmd; + cmd.set_message(a->text().toStdString()); + sendGameCommand(cmd); +} + +void PlayerActions::setCardAttrHelper(const GameEventContext &context, + CardItem *card, + CardAttribute attribute, + const QString &avalue, + bool allCards, + EventProcessingOptions options) +{ + if (card == nullptr) { + return; + } + + bool moveCardContext = context.HasExtension(Context_MoveCard::ext); + switch (attribute) { + case AttrTapped: { + bool tapped = avalue == "1"; + if (!(!tapped && card->getDoesntUntap() && allCards)) { + if (!allCards) { + emit logSetTapped(player, card, tapped); + } + bool canAnimate = !options.testFlag(SKIP_TAP_ANIMATION) && !moveCardContext; + card->setTapped(tapped, canAnimate); + } + break; + } + case AttrAttacking: { + card->setAttacking(avalue == "1"); + break; + } + case AttrFaceDown: { + card->setFaceDown(avalue == "1"); + break; + } + case AttrColor: { + card->setColor(avalue); + break; + } + case AttrAnnotation: { + emit logSetAnnotation(player, card, avalue); + card->setAnnotation(avalue); + break; + } + case AttrDoesntUntap: { + bool value = (avalue == "1"); + emit logSetDoesntUntap(player, card, value); + card->setDoesntUntap(value); + break; + } + case AttrPT: { + emit logSetPT(player, card, avalue); + card->setPT(avalue); + break; + } + } +} + +void PlayerActions::actMoveCardXCardsFromTop() +{ + 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; + } + + defaultNumberTopCardsToPlaceBelow = number; + + QList sel = player->getGameScene()->selectedItems(); + if (sel.isEmpty()) { + return; + } + + QList cardList; + while (!sel.isEmpty()) { + cardList.append(qgraphicsitem_cast(sel.takeFirst())); + } + + QList commandList; + ListOfCardsToMove idList; + for (const auto &i : cardList) { + idList.add_card()->set_card_id(i->getId()); + } + + int startPlayerId = cardList[0]->getZone()->getPlayer()->getPlayerInfo()->getId(); + QString startZone = cardList[0]->getZone()->getName(); + + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + cmd->set_target_zone("deck"); + cmd->set_x(number); + cmd->set_y(0); + commandList.append(cmd); + + if (player->getPlayerInfo()->local) { + sendGameCommand(prepareGameCommand(commandList)); + } else { + player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList)); + } +} + +void PlayerActions::actIncPT(int deltaP, int deltaT) +{ + int playerid = player->getPlayerInfo()->getId(); + + QList commandList; + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + QString pt = card->getPT(); + const auto ptList = parsePT(pt); + QString newpt; + if (ptList.isEmpty()) { + newpt = QString::number(deltaP) + (deltaT ? "/" + QString::number(deltaT) : ""); + } else if (ptList.size() == 1) { + newpt = QString::number(ptList.at(0).toInt() + deltaP) + (deltaT ? "/" + QString::number(deltaT) : ""); + } else { + newpt = + QString::number(ptList.at(0).toInt() + deltaP) + "/" + QString::number(ptList.at(1).toInt() + deltaT); + } + + auto *cmd = new Command_SetCardAttr; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_attribute(AttrPT); + cmd->set_attr_value(newpt.toStdString()); + commandList.append(cmd); + + if (player->getPlayerInfo()->getLocal()) { + playerid = card->getZone()->getPlayer()->getPlayerInfo()->getId(); + } + } + + player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); +} + +void PlayerActions::actResetPT() +{ + int playerid = player->getPlayerInfo()->getId(); + QList commandList; + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + QString ptString; + if (!card->getFaceDown()) { // leave the pt empty if the card is face down + ExactCard ec = card->getCard(); + if (ec) { + ptString = ec.getInfo().getPowTough(); + } + } + if (ptString == card->getPT()) { + continue; + } + QString zoneName = card->getZone()->getName(); + auto *cmd = new Command_SetCardAttr; + cmd->set_zone(zoneName.toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_attribute(AttrPT); + cmd->set_attr_value(ptString.toStdString()); + commandList.append(cmd); + + if (player->getPlayerInfo()->getLocal()) { + playerid = card->getZone()->getPlayer()->getPlayerInfo()->getId(); + } + } + + if (!commandList.empty()) { + player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); + } +} + +QVariantList PlayerActions::parsePT(const QString &pt) +{ + QVariantList ptList = QVariantList(); + if (!pt.isEmpty()) { + int sep = pt.indexOf('/'); + if (sep == 0) { + ptList.append(QVariant(pt.mid(1))); // cut off starting '/' and take full string + } else { + int start = 0; + for (;;) { + QString item = pt.mid(start, sep - start); + if (item.isEmpty()) { + ptList.append(QVariant(QString())); + } else if (item[0] == '+') { + ptList.append(QVariant(item.mid(1).toInt())); // add as int + } else if (item[0] == '-') { + ptList.append(QVariant(item.toInt())); // add as int + } else { + ptList.append(QVariant(item)); // add as qstring + } + if (sep == -1) { + break; + } + start = sep + 1; + sep = pt.indexOf('/', start); + } + } + } + return ptList; +} + +void PlayerActions::actSetPT() +{ + QString oldPT; + int playerid = player->getPlayerInfo()->getId(); + + auto sel = player->getGameScene()->selectedItems(); + for (const auto &item : sel) { + auto *card = static_cast(item); + 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; + } + + const auto ptList = parsePT(pt); + bool empty = ptList.isEmpty(); + + QList commandList; + for (const auto &item : sel) { + auto *card = static_cast(item); + auto *cmd = new Command_SetCardAttr; + QString newpt = QString(); + if (!empty) { + const auto oldpt = parsePT(card->getPT()); + int ptIter = 0; + for (const auto &_item : ptList) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (_item.typeId() == QMetaType::Type::Int) { +#else + if (_item.type() == QVariant::Int) { +#endif + int oldItem = ptIter < oldpt.size() ? oldpt.at(ptIter).toInt() : 0; + newpt += '/' + QString::number(oldItem + _item.toInt()); + } else { + newpt += '/' + _item.toString(); + } + ++ptIter; + } + newpt = newpt.mid(1); + } + + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_attribute(AttrPT); + cmd->set_attr_value(newpt.toStdString()); + commandList.append(cmd); + + if (player->getPlayerInfo()->local) { + playerid = card->getZone()->getPlayer()->getPlayerInfo()->getId(); + } + } + + player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); +} + +void PlayerActions::actDrawArrow() +{ + auto *card = player->getGame()->getActiveCard(); + if (card) { + card->drawArrow(Qt::red); + } +} + +void PlayerActions::actIncP() +{ + actIncPT(1, 0); +} + +void PlayerActions::actDecP() +{ + actIncPT(-1, 0); +} + +void PlayerActions::actIncT() +{ + actIncPT(0, 1); +} + +void PlayerActions::actDecT() +{ + actIncPT(0, -1); +} + +void PlayerActions::actIncPT() +{ + actIncPT(1, 1); +} + +void PlayerActions::actDecPT() +{ + actIncPT(-1, -1); +} + +void PlayerActions::actFlowP() +{ + actIncPT(1, -1); +} + +void PlayerActions::actFlowT() +{ + actIncPT(-1, 1); +} + +void AnnotationDialog::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) { + event->accept(); + accept(); + return; + } + QInputDialog::keyPressEvent(event); +} + +void PlayerActions::actSetAnnotation() +{ + QString oldAnnotation; + auto sel = player->getGameScene()->selectedItems(); + for (const auto &item : sel) { + auto *card = static_cast(item); + if (!card->getAnnotation().isEmpty()) { + oldAnnotation = card->getAnnotation(); + } + } + + 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); + + QList commandList; + for (const auto &item : sel) { + auto *card = static_cast(item); + auto *cmd = new Command_SetCardAttr; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_attribute(AttrAnnotation); + cmd->set_attr_value(annotation.toStdString()); + commandList.append(cmd); + } + sendGameCommand(prepareGameCommand(commandList)); +} + +void PlayerActions::actAttach() +{ + auto *card = player->getGame()->getActiveCard(); + if (!card) { + return; + } + + card->drawAttachArrow(); +} + +void PlayerActions::actUnattach() +{ + QList commandList; + for (QGraphicsItem *item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + + if (!card->getAttachedTo()) { + continue; + } + + auto *cmd = new Command_AttachCard; + cmd->set_start_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + commandList.append(cmd); + } + sendGameCommand(prepareGameCommand(commandList)); +} + +void PlayerActions::actCardCounterTrigger() +{ + auto *action = static_cast(sender()); + int counterId = action->data().toInt() / 1000; + QList commandList; + switch (action->data().toInt() % 1000) { + case 9: { // increment counter + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + if (card->getCounters().value(counterId, 0) < MAX_COUNTERS_ON_CARD) { + auto *cmd = new Command_SetCardCounter; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_counter_id(counterId); + cmd->set_counter_value(card->getCounters().value(counterId, 0) + 1); + commandList.append(cmd); + } + } + break; + } + case 10: { // decrement counter + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + if (card->getCounters().value(counterId, 0)) { + auto *cmd = new Command_SetCardCounter; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_counter_id(counterId); + cmd->set_counter_value(card->getCounters().value(counterId, 0) - 1); + commandList.append(cmd); + } + } + break; + } + case 11: { // set counter with dialog + bool ok; + player->setDialogSemaphore(true); + + int oldValue = 0; + if (player->getGameScene()->selectedItems().size() == 1) { + auto *card = static_cast(player->getGameScene()->selectedItems().first()); + oldValue = card->getCounters().value(counterId, 0); + } + int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Set counters"), tr("Number:"), oldValue, + 0, MAX_COUNTERS_ON_CARD, 1, &ok); + player->setDialogSemaphore(false); + if (player->clearCardsToDelete() || !ok) { + return; + } + + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + auto *cmd = new Command_SetCardCounter; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_counter_id(counterId); + cmd->set_counter_value(number); + commandList.append(cmd); + } + break; + } + default:; + } + sendGameCommand(prepareGameCommand(commandList)); +} + +/** + * @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone + * is nullptr. + */ +static bool isUnwritableRevealZone(CardZoneLogic *zone) +{ + if (auto *view = qobject_cast(zone)) { + return view->getRevealZone() && !view->getWriteableRevealZone(); + } + return false; +} + +void PlayerActions::playSelectedCards(const bool faceDown) +{ + QList selectedCards; + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + selectedCards.append(card); + } + + // 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. + std::sort(selectedCards.begin(), selectedCards.end(), + [](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); }); + + for (auto &card : selectedCards) { + if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != "table") { + playCard(card, faceDown); + } + } +} + +void PlayerActions::actPlay() +{ + playSelectedCards(false); +} + +void PlayerActions::actPlayFacedown() +{ + playSelectedCards(true); +} + +void PlayerActions::actHide() +{ + for (const auto &item : player->getGameScene()->selectedItems()) { + auto *card = static_cast(item); + if (card && isUnwritableRevealZone(card->getZone())) { + card->getZone()->removeCard(card); + } + } +} + +void PlayerActions::actReveal(QAction *action) +{ + const int otherPlayerId = action->data().toInt(); + + Command_RevealCards cmd; + if (otherPlayerId != -1) { + cmd.set_player_id(otherPlayerId); + } + + QList sel = player->getGameScene()->selectedItems(); + while (!sel.isEmpty()) { + const auto *card = qgraphicsitem_cast(sel.takeFirst()); + if (!cmd.has_zone_name()) { + cmd.set_zone_name(card->getZone()->getName().toStdString()); + } + cmd.add_card_id(card->getId()); + } + + sendGameCommand(cmd); +} + +void PlayerActions::cardMenuAction() +{ + auto *a = dynamic_cast(sender()); + QList sel = player->getGameScene()->selectedItems(); + QList cardList; + while (!sel.isEmpty()) { + cardList.append(qgraphicsitem_cast(sel.takeFirst())); + } + + QList commandList; + if (a->data().toInt() <= (int)cmClone) { + for (const auto &card : cardList) { + switch (static_cast(a->data().toInt())) { + // Leaving both for compatibility with server + case cmUntap: + // fallthrough + case cmTap: { + auto *cmd = new Command_SetCardAttr; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_attribute(AttrTapped); + cmd->set_attr_value(std::to_string(1 - static_cast(card->getTapped()))); + commandList.append(cmd); + break; + } + case cmDoesntUntap: { + auto *cmd = new Command_SetCardAttr; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_attribute(AttrDoesntUntap); + cmd->set_attr_value(card->getDoesntUntap() ? "0" : "1"); + commandList.append(cmd); + break; + } + case cmFlip: { + auto *cmd = new Command_FlipCard; + cmd->set_zone(card->getZone()->getName().toStdString()); + cmd->set_card_id(card->getId()); + cmd->set_face_down(!card->getFaceDown()); + if (card->getFaceDown()) { + ExactCard ec = card->getCard(); + if (ec) { + cmd->set_pt(ec.getInfo().getPowTough().toStdString()); + } + } + commandList.append(cmd); + break; + } + case cmPeek: { + auto *cmd = new Command_RevealCards; + cmd->set_zone_name(card->getZone()->getName().toStdString()); + cmd->add_card_id(card->getId()); + cmd->set_player_id(player->getPlayerInfo()->getId()); + commandList.append(cmd); + break; + } + case cmClone: { + auto *cmd = new Command_CreateToken; + cmd->set_zone("table"); + cmd->set_card_name(card->getName().toStdString()); + cmd->set_card_provider_id(card->getProviderId().toStdString()); + cmd->set_color(card->getColor().toStdString()); + cmd->set_pt(card->getPT().toStdString()); + cmd->set_annotation(card->getAnnotation().toStdString()); + cmd->set_destroy_on_zone_change(true); + cmd->set_x(-1); + cmd->set_y(card->getGridPoint().y()); + commandList.append(cmd); + break; + } + default: + break; + } + } + } else { + CardZoneLogic *zone = cardList[0]->getZone(); + if (!zone) { + return; + } + + Player *startPlayer = zone->getPlayer(); + if (!startPlayer) { + return; + } + + int startPlayerId = startPlayer->getPlayerInfo()->getId(); + QString startZone = zone->getName(); + + ListOfCardsToMove idList; + for (const auto &i : cardList) { + idList.add_card()->set_card_id(i->getId()); + } + + switch (static_cast(a->data().toInt())) { + case cmMoveToTopLibrary: { + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + cmd->set_target_zone("deck"); + cmd->set_x(0); + cmd->set_y(0); + + if (idList.card_size() > 1) { + auto *scmd = new Command_Shuffle; + scmd->set_zone_name("deck"); + scmd->set_start(0); + scmd->set_end(idList.card_size() - 1); // inclusive, the indexed card at end will be shuffled + // Server process events backwards, so... + commandList.append(scmd); + } + + commandList.append(cmd); + break; + } + case cmMoveToBottomLibrary: { + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + cmd->set_target_zone("deck"); + cmd->set_x(-1); + cmd->set_y(0); + + if (idList.card_size() > 1) { + auto *scmd = new Command_Shuffle; + scmd->set_zone_name("deck"); + scmd->set_start(-idList.card_size()); + scmd->set_end(-1); + // Server process events backwards, so... + commandList.append(scmd); + } + + commandList.append(cmd); + break; + } + case cmMoveToHand: { + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + cmd->set_target_zone("hand"); + cmd->set_x(0); + cmd->set_y(0); + commandList.append(cmd); + break; + } + case cmMoveToGraveyard: { + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + cmd->set_target_zone("grave"); + cmd->set_x(0); + cmd->set_y(0); + commandList.append(cmd); + break; + } + case cmMoveToExile: { + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + cmd->set_target_zone("rfg"); + cmd->set_x(0); + cmd->set_y(0); + commandList.append(cmd); + break; + } + default: + break; + } + } + + if (player->getPlayerInfo()->getLocal()) { + sendGameCommand(prepareGameCommand(commandList)); + } else { + player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList)); + } +} + +PendingCommand *PlayerActions::prepareGameCommand(const google::protobuf::Message &cmd) +{ + + if (player->getPlayerInfo()->getJudge() && !player->getPlayerInfo()->getLocal()) { + Command_Judge base; + GameCommand *c = base.add_game_command(); + base.set_target_id(player->getPlayerInfo()->getId()); + c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd); + return player->getGame()->getGameEventHandler()->prepareGameCommand(base); + } else { + return player->getGame()->getGameEventHandler()->prepareGameCommand(cmd); + } +} + +PendingCommand *PlayerActions::prepareGameCommand(const QList &cmdList) +{ + if (player->getPlayerInfo()->getJudge() && !player->getPlayerInfo()->getLocal()) { + Command_Judge base; + base.set_target_id(player->getPlayerInfo()->getId()); + for (int i = 0; i < cmdList.size(); ++i) { + GameCommand *c = base.add_game_command(); + c->GetReflection() + ->MutableMessage(c, cmdList[i]->GetDescriptor()->FindExtensionByName("ext")) + ->CopyFrom(*cmdList[i]); + delete cmdList[i]; + } + return player->getGame()->getGameEventHandler()->prepareGameCommand(base); + } else { + return player->getGame()->getGameEventHandler()->prepareGameCommand(cmdList); + } +} + +void PlayerActions::sendGameCommand(const google::protobuf::Message &command) +{ + if (player->getPlayerInfo()->getJudge() && !player->getPlayerInfo()->getLocal()) { + Command_Judge base; + GameCommand *c = base.add_game_command(); + base.set_target_id(player->getPlayerInfo()->getId()); + c->GetReflection()->MutableMessage(c, command.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(command); + player->getGame()->getGameEventHandler()->sendGameCommand(base, player->getPlayerInfo()->getId()); + } else { + player->getGame()->getGameEventHandler()->sendGameCommand(command, player->getPlayerInfo()->getId()); + } +} + +void PlayerActions::sendGameCommand(PendingCommand *pend) +{ + player->getGame()->getGameEventHandler()->sendGameCommand(pend, player->getPlayerInfo()->getId()); +} diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h new file mode 100644 index 000000000..a694ff5f8 --- /dev/null +++ b/cockatrice/src/game/player/player_actions.h @@ -0,0 +1,178 @@ +#ifndef COCKATRICE_PLAYER_ACTIONS_H +#define COCKATRICE_PLAYER_ACTIONS_H +#include "event_processing_options.h" +#include "player.h" + +#include +#include + +namespace google +{ +namespace protobuf +{ +class Message; +} +} // namespace google + +class CardItem; +class Command_MoveCard; +class GameEventContext; +class PendingCommand; +class Player; +class PlayerActions : public QObject +{ + Q_OBJECT + +signals: + void logSetTapped(Player *player, CardItem *card, bool tapped); + void logSetAnnotation(Player *player, CardItem *card, QString newAnnotation); + void logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap); + void logSetPT(Player *player, CardItem *card, QString newPT); + +public: + enum CardsToReveal + { + RANDOM_CARD_FROM_ZONE = -2 + }; + + explicit PlayerActions(Player *player); + + void sendGameCommand(PendingCommand *pend); + void sendGameCommand(const google::protobuf::Message &command); + + PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd); + PendingCommand *prepareGameCommand(const QList &cmdList); + + void setCardAttrHelper(const GameEventContext &context, + CardItem *card, + CardAttribute attribute, + const QString &avalue, + bool allCards, + EventProcessingOptions options); + + void moveOneCardUntil(CardItem *card); + void stopMoveTopCardsUntil(); + + bool isMovingCardsUntil() const + { + return movingCardsUntil; + } + +public slots: + void setLastToken(CardInfoPtr cardInfo); + void playCard(CardItem *c, bool faceDown); + void playCardToTable(const CardItem *c, bool faceDown); + + void actUntapAll(); + void actRollDie(); + void actCreateToken(); + void actCreateAnotherToken(); + void actShuffle(); + void actShuffleTop(); + void actShuffleBottom(); + void actDrawCard(); + void actDrawCards(); + void actUndoDraw(); + void actMulligan(); + + void actPlay(); + void actPlayFacedown(); + void actHide(); + + void actMoveTopCardToPlay(); + void actMoveTopCardToPlayFaceDown(); + void actMoveTopCardToGrave(); + void actMoveTopCardToExile(); + void actMoveTopCardsToGrave(); + void actMoveTopCardsToExile(); + void actMoveTopCardsUntil(); + void actMoveTopCardToBottom(); + void actDrawBottomCard(); + void actDrawBottomCards(); + void actMoveBottomCardToPlay(); + void actMoveBottomCardToPlayFaceDown(); + void actMoveBottomCardToGrave(); + void actMoveBottomCardToExile(); + void actMoveBottomCardsToGrave(); + void actMoveBottomCardsToExile(); + void actMoveBottomCardToTop(); + + void actSelectAll(); + void actSelectRow(); + void actSelectColumn(); + + void actViewLibrary(); + void actViewHand(); + void actViewTopCards(); + void actViewBottomCards(); + void actAlwaysRevealTopCard(); + void actAlwaysLookAtTopCard(); + void actViewGraveyard(); + void actRevealRandomGraveyardCard(); + void actViewRfg(); + void actViewSideboard(); + + void actSayMessage(); + + void actOpenDeckInDeckEditor(); + void actCreatePredefinedToken(); + void actCreateRelatedCard(); + void actCreateAllRelatedCards(); + + void actMoveCardXCardsFromTop(); + void actCardCounterTrigger(); + void actAttach(); + void actUnattach(); + 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 actSetAnnotation(); + void actReveal(QAction *action); + + void actSortHand(); + + void cardMenuAction(); + +private: + Player *player; + + int defaultNumberTopCards = 1; + int defaultNumberTopCardsToPlaceBelow = 1; + int defaultNumberBottomCards = 1; + int defaultNumberDieRoll = 20; + + TokenInfo lastTokenInfo; + int lastTokenTableRow; + + bool movingCardsUntil; + QTimer *moveTopCardTimer; + QStringList movingCardsUntilExprs = {}; + int movingCardsUntilNumberOfHits = 1; + bool movingCardsUntilAutoPlay = false; + FilterString movingCardsUntilFilter; + int movingCardsUntilCounter = 0; + + void createCard(const CardItem *sourceCard, + const QString &dbCardName, + CardRelation::AttachType attach = CardRelation::DoesNotAttach, + bool persistent = false); + bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); + + void playSelectedCards(bool faceDown = false); + + void cmdSetTopCard(Command_MoveCard &cmd); + void cmdSetBottomCard(Command_MoveCard &cmd); + + QVariantList parsePT(const QString &pt); +}; + +#endif // COCKATRICE_PLAYER_ACTIONS_H diff --git a/cockatrice/src/game/player/player_area.cpp b/cockatrice/src/game/player/player_area.cpp new file mode 100644 index 000000000..2ac6821f2 --- /dev/null +++ b/cockatrice/src/game/player/player_area.cpp @@ -0,0 +1,34 @@ +#include "player_area.h" + +#include "../../client/ui/theme_manager.h" + +#include + +PlayerArea::PlayerArea(QGraphicsItem *parentItem) : QObject(), QGraphicsItem(parentItem) +{ + setCacheMode(DeviceCoordinateCache); + connect(themeManager, &ThemeManager::themeChanged, this, &PlayerArea::updateBg); + updateBg(); +} + +void PlayerArea::updateBg() +{ + update(); +} + +void PlayerArea::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) +{ + QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Player, playerZoneId); + painter->fillRect(boundingRect(), brush); +} + +void PlayerArea::setSize(qreal width, qreal height) +{ + prepareGeometryChange(); + bRect = QRectF(0, 0, width, height); +} + +void PlayerArea::setPlayerZoneId(int _playerZoneId) +{ + playerZoneId = _playerZoneId; +} \ No newline at end of file diff --git a/cockatrice/src/game/player/player_area.h b/cockatrice/src/game/player/player_area.h new file mode 100644 index 000000000..c0b689713 --- /dev/null +++ b/cockatrice/src/game/player/player_area.h @@ -0,0 +1,46 @@ +#ifndef COCKATRICE_PLAYER_AREA_H +#define COCKATRICE_PLAYER_AREA_H + +#include "../board/graphics_item_type.h" +#include "QGraphicsItem" + +/** + * The entire graphical area belonging to a single player. + */ +class PlayerArea : public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsItem) +private: + QRectF bRect; + int playerZoneId; +private slots: + void updateBg(); + +public: + enum + { + Type = typeOther + }; + int type() const override + { + return Type; + } + + explicit PlayerArea(QGraphicsItem *parent = nullptr); + QRectF boundingRect() const override + { + return bRect; + } + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + + void setSize(qreal width, qreal height); + + void setPlayerZoneId(int _playerZoneId); + int getPlayerZoneId() const + { + return playerZoneId; + } +}; + +#endif // COCKATRICE_PLAYER_AREA_H diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp new file mode 100644 index 000000000..88697f010 --- /dev/null +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -0,0 +1,602 @@ +#include "player_event_handler.h" + +#include "../../client/tabs/tab_game.h" +#include "../board/arrow_item.h" +#include "../board/card_item.h" +#include "../board/card_list.h" +#include "../zones/view_zone.h" +#include "pb/command_set_card_attr.pb.h" +#include "pb/context_move_card.pb.h" +#include "pb/context_undo_draw.pb.h" +#include "pb/event_attach_card.pb.h" +#include "pb/event_change_zone_properties.pb.h" +#include "pb/event_create_arrow.pb.h" +#include "pb/event_create_counter.pb.h" +#include "pb/event_create_token.pb.h" +#include "pb/event_del_counter.pb.h" +#include "pb/event_delete_arrow.pb.h" +#include "pb/event_destroy_card.pb.h" +#include "pb/event_draw_cards.pb.h" +#include "pb/event_dump_zone.pb.h" +#include "pb/event_flip_card.pb.h" +#include "pb/event_game_say.pb.h" +#include "pb/event_move_card.pb.h" +#include "pb/event_reveal_cards.pb.h" +#include "pb/event_roll_die.pb.h" +#include "pb/event_set_card_attr.pb.h" +#include "pb/event_set_card_counter.pb.h" +#include "pb/event_set_counter.pb.h" +#include "pb/event_shuffle.pb.h" +#include "player.h" + +PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player) +{ +} + +void PlayerEventHandler::eventGameSay(const Event_GameSay &event) +{ + emit logSay(player, QString::fromStdString(event.message())); +} + +void PlayerEventHandler::eventShuffle(const Event_Shuffle &event) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + auto &cardList = zone->getCards(); + int absStart = event.start(); + if (absStart < 0) { // negative indexes start from the end + absStart += cardList.length(); + } + + // close all views that contain shuffled cards + for (ZoneViewZone *view : zone->getViews()) { + if (view != nullptr) { + int length = view->getLogic()->getCards().length(); + // we want to close empty views as well + if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library + view->close(); + break; + } + } else { + qWarning() << zone->getName() << "of" << player->getPlayerInfo()->getName() << "holds empty zoneview!"; + } + } + + // remove revealed card name on top of decks + if (absStart == 0 && !cardList.isEmpty()) { + cardList.first()->setCardRef({}); + emit zone->updateGraphics(); + } + + emit logShuffle(player, zone, event.start(), event.end()); +} + +void PlayerEventHandler::eventRollDie(const Event_RollDie &event) +{ + if (!event.values().empty()) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QList rolls(event.values().begin(), event.values().end()); +#else + QList rolls; + for (const auto &value : event.values()) { + rolls.append(value); + } +#endif + std::sort(rolls.begin(), rolls.end()); + emit logRollDie(player, static_cast(event.sides()), rolls); + } else if (event.value()) { + // Backwards compatibility for old clients + emit logRollDie(player, static_cast(event.sides()), {event.value()}); + } +} + +void PlayerEventHandler::eventCreateArrow(const Event_CreateArrow &event) +{ + ArrowItem *arrow = player->addArrow(event.arrow_info()); + if (!arrow) { + return; + } + + auto *startCard = static_cast(arrow->getStartItem()); + auto *targetCard = qgraphicsitem_cast(arrow->getTargetItem()); + if (targetCard) { + emit logCreateArrow(player, startCard->getOwner(), startCard->getName(), targetCard->getOwner(), + targetCard->getName(), false); + } else { + emit logCreateArrow(player, startCard->getOwner(), startCard->getName(), arrow->getTargetItem()->getOwner(), + QString(), true); + } +} + +void PlayerEventHandler::eventDeleteArrow(const Event_DeleteArrow &event) +{ + player->delArrow(event.arrow_id()); +} + +void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + + CardRef cardRef = {QString::fromStdString(event.card_name()), QString::fromStdString(event.card_provider_id())}; + CardItem *card = new CardItem(player, nullptr, cardRef, event.card_id()); + // use db PT if not provided in event and not face-down + if (!QString::fromStdString(event.pt()).isEmpty()) { + card->setPT(QString::fromStdString(event.pt())); + } else if (!event.face_down()) { + ExactCard dbCard = card->getCard(); + if (dbCard) { + card->setPT(dbCard.getInfo().getPowTough()); + } + } + card->setColor(QString::fromStdString(event.color())); + card->setAnnotation(QString::fromStdString(event.annotation())); + card->setDestroyOnZoneChange(event.destroy_on_zone_change()); + card->setFaceDown(event.face_down()); + + emit logCreateToken(player, card->getName(), card->getPT(), card->getFaceDown()); + zone->addCard(card, true, event.x(), event.y()); +} + +void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event, + const GameEventContext &context, + EventProcessingOptions options) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + + if (!event.has_card_id()) { + const CardList &cards = zone->getCards(); + for (int i = 0; i < cards.size(); ++i) { + player->getPlayerActions()->setCardAttrHelper(context, cards.at(i), event.attribute(), + QString::fromStdString(event.attr_value()), true, options); + } + if (event.attribute() == AttrTapped) { + emit logSetTapped(player, nullptr, event.attr_value() == "1"); + } + } else { + CardItem *card = zone->getCard(event.card_id()); + if (!card) { + qWarning() << "PlayerEventHandler::eventSetCardAttr: card id=" << event.card_id() << "not found"; + return; + } + player->getPlayerActions()->setCardAttrHelper(context, card, event.attribute(), + QString::fromStdString(event.attr_value()), false, options); + } +} + +void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + + CardItem *card = zone->getCard(event.card_id()); + if (!card) { + return; + } + + int oldValue = card->getCounters().value(event.counter_id(), 0); + card->setCounter(event.counter_id(), event.counter_value()); + player->getPlayerMenu()->updateCardMenu(card); + emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue); +} + +void PlayerEventHandler::eventCreateCounter(const Event_CreateCounter &event) +{ + player->addCounter(event.counter_info()); +} + +void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event) +{ + AbstractCounter *ctr = player->getCounters().value(event.counter_id(), 0); + if (!ctr) { + return; + } + int oldValue = ctr->getValue(); + ctr->setValue(event.value()); + emit logSetCounter(player, ctr->getName(), event.value(), oldValue); +} + +void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event) +{ + player->delCounter(event.counter_id()); +} + +void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event) +{ + Player *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0); + if (!zoneOwner) { + return; + } + CardZoneLogic *zone = zoneOwner->getZones().value(QString::fromStdString(event.zone_name()), 0); + if (!zone) { + return; + } + emit logDumpZone(player, zone, event.number_cards(), event.is_reversed()); +} + +void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context) +{ + Player *startPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.start_player_id()); + if (!startPlayer) { + return; + } + QString startZoneString = QString::fromStdString(event.start_zone()); + CardZoneLogic *startZone = startPlayer->getZones().value(startZoneString, 0); + Player *targetPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.target_player_id()); + if (!targetPlayer) { + return; + } + CardZoneLogic *targetZone; + if (event.has_target_zone()) { + targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0); + } else { + targetZone = startZone; + } + if (!startZone || !targetZone) { + return; + } + + int position = event.position(); + int x = event.x(); + int y = event.y(); + + int logPosition = position; + int logX = x; + if (x == -1) { + x = 0; + } + CardItem *card = startZone->takeCard(position, event.card_id(), startZone != targetZone); + if (card == nullptr) { + return; + } + if (startZone != targetZone) { + card->deleteCardInfoPopup(); + } + if (event.has_card_name()) { + QString name = QString::fromStdString(event.card_name()); + QString providerId = + event.has_new_card_provider_id() ? QString::fromStdString(event.new_card_provider_id()) : ""; + card->setCardRef({name, providerId}); + } + + if (card->getAttachedTo() && (startZone != targetZone)) { + CardItem *parentCard = card->getAttachedTo(); + card->setAttachedTo(nullptr); + parentCard->getZone()->reorganizeCards(); + } + + card->deleteDragItem(); + + card->setId(event.new_card_id()); + card->setFaceDown(event.face_down()); + if (startZone != targetZone) { + card->setBeingPointedAt(false); + card->setHovered(false); + + const QList &attachedCards = card->getAttachedCards(); + for (auto attachedCard : attachedCards) { + emit targetZone->cardAdded(attachedCard); + } + + if (startZone->getPlayer() != targetZone->getPlayer()) { + card->setOwner(targetZone->getPlayer()); + } + } + + // The log event has to be sent before the card is added to the target zone + // because the addCard function can modify the card object. + if (context.HasExtension(Context_UndoDraw::ext)) { + emit logUndoDraw(player, card->getName()); + } else { + emit logMoveCard(player, card, startZone, logPosition, targetZone, logX); + } + + targetZone->addCard(card, true, x, y); + + // Look at all arrows from and to the card. + // If the card was moved to another zone, delete the arrows, otherwise update them. + QMapIterator playerIterator(player->getGame()->getPlayerManager()->getPlayers()); + while (playerIterator.hasNext()) { + Player *p = playerIterator.next().value(); + + QList arrowsToDelete; + QMapIterator arrowIterator(p->getArrows()); + while (arrowIterator.hasNext()) { + ArrowItem *arrow = arrowIterator.next().value(); + if ((arrow->getStartItem() == card) || (arrow->getTargetItem() == card)) { + if (startZone == targetZone) { + arrow->updatePath(); + } else { + arrowsToDelete.append(arrow); + } + } + } + for (auto &i : arrowsToDelete) { + i->delArrow(); + } + } + player->getPlayerMenu()->updateCardMenu(card); + + if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == "deck" && + targetZone->getName() == "stack") { + player->getPlayerActions()->moveOneCardUntil(card); + } +} + +void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + CardItem *card = zone->getCard(event.card_id()); + if (!card) { + return; + } + + if (!event.face_down()) { + QString cardName = QString::fromStdString(event.card_name()); + QString providerId = QString::fromStdString(event.card_provider_id()); + card->setCardRef({cardName, providerId}); + } + + emit logFlipCard(player, card->getName(), event.face_down()); + card->setFaceDown(event.face_down()); + player->getPlayerMenu()->updateCardMenu(card); +} + +void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + + CardItem *card = zone->getCard(event.card_id()); + if (!card) { + return; + } + + QList attachedCards = card->getAttachedCards(); + // This list is always empty except for buggy server implementations. + for (auto &attachedCard : attachedCards) { + attachedCard->setAttachedTo(nullptr); + } + + emit logDestroyCard(player, card->getName()); + zone->takeCard(-1, event.card_id(), true); + card->deleteLater(); +} + +void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event) +{ + const QMap &playerList = player->getGame()->getPlayerManager()->getPlayers(); + Player *targetPlayer = nullptr; + CardZoneLogic *targetZone = nullptr; + CardItem *targetCard = nullptr; + if (event.has_target_player_id()) { + targetPlayer = playerList.value(event.target_player_id(), 0); + if (targetPlayer) { + targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0); + if (targetZone) { + targetCard = targetZone->getCard(event.target_card_id()); + } + } + } + + CardZoneLogic *startZone = player->getZone(QString::fromStdString(event.start_zone())); + if (!startZone) { + return; + } + + CardItem *startCard = startZone->getCard(event.card_id()); + if (!startCard) { + return; + } + + CardItem *oldParent = startCard->getAttachedTo(); + + startCard->setAttachedTo(targetCard); + + startZone->reorganizeCards(); + if ((startZone != targetZone) && targetZone) { + targetZone->reorganizeCards(); + } + if (oldParent) { + oldParent->getZone()->reorganizeCards(); + } + + if (targetCard) { + emit logAttachCard(player, startCard->getName(), targetPlayer, targetCard->getName()); + } else { + emit logUnattachCard(player, startCard->getName()); + } + player->getPlayerMenu()->updateCardMenu(startCard); +} + +void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event) +{ + CardZoneLogic *_deck = player->getDeckZone(); + CardZoneLogic *_hand = player->getHandZone(); + + const int listSize = event.cards_size(); + if (listSize) { + for (int i = 0; i < listSize; ++i) { + const ServerInfo_Card &cardInfo = event.cards(i); + CardItem *card = _deck->takeCard(0, cardInfo.id()); + QString cardName = QString::fromStdString(cardInfo.name()); + QString providerId = QString::fromStdString(cardInfo.provider_id()); + card->setCardRef({cardName, providerId}); + _hand->addCard(card, false, -1); + } + } else { + const int number = event.number(); + for (int i = 0; i < number; ++i) { + _hand->addCard(_deck->takeCard(0, -1), false, -1); + } + } + + _hand->reorganizeCards(); + _deck->reorganizeCards(); + emit logDrawCards(player, event.number(), _deck->getCards().size() == 0); +} + +void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options) +{ + Q_UNUSED(options); + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + Player *otherPlayer = nullptr; + if (event.has_other_player_id()) { + otherPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.other_player_id()); + if (!otherPlayer) { + return; + } + } + + bool peeking = false; + QList cardList; + const int cardListSize = event.cards_size(); + for (int i = 0; i < cardListSize; ++i) { + const ServerInfo_Card *temp = &event.cards(i); + if (temp->face_down()) { + peeking = true; + } + cardList.append(temp); + } + + if (peeking) { + for (const auto &card : cardList) { + QString cardName = QString::fromStdString(card->name()); + QString providerId = QString::fromStdString(card->provider_id()); + CardItem *cardItem = zone->getCard(card->id()); + if (!cardItem) { + continue; + } + cardItem->setCardRef({cardName, providerId}); + emit logRevealCards(player, zone, card->id(), cardName, player, true, 1); + } + } else { + bool showZoneView = true; + QString cardName; + auto cardId = event.card_id_size() == 0 ? -1 : event.card_id(0); + if (cardList.size() == 1) { + cardName = QString::fromStdString(cardList.first()->name()); + + // Handle case of revealing top card of library in-place + if (cardId == 0 && dynamic_cast(zone)) { + auto card = zone->getCards().first(); + QString providerId = QString::fromStdString(cardList.first()->provider_id()); + card->setCardRef({cardName, providerId}); + + emit zone->updateGraphics(); + showZoneView = false; + } + } + + if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) { + player->getGameScene()->addRevealedZoneView(player, zone, cardList, event.grant_write_access()); + } + + emit logRevealCards(player, zone, cardId, cardName, otherPlayer, false, + event.has_number_of_cards() ? event.number_of_cards() : cardList.size(), + event.grant_write_access()); + } +} + +void PlayerEventHandler::eventChangeZoneProperties(const Event_ChangeZoneProperties &event) +{ + CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name())); + if (!zone) { + return; + } + + if (event.has_always_reveal_top_card()) { + zone->setAlwaysRevealTopCard(event.always_reveal_top_card()); + emit logAlwaysRevealTopCard(player, zone, event.always_reveal_top_card()); + } + if (event.has_always_look_at_top_card()) { + zone->setAlwaysRevealTopCard(event.always_look_at_top_card()); + emit logAlwaysLookAtTopCard(player, zone, event.always_look_at_top_card()); + } +} + +void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type, + const GameEvent &event, + const GameEventContext &context, + EventProcessingOptions options) +{ + switch (type) { + case GameEvent::GAME_SAY: + eventGameSay(event.GetExtension(Event_GameSay::ext)); + break; + case GameEvent::SHUFFLE: + eventShuffle(event.GetExtension(Event_Shuffle::ext)); + break; + case GameEvent::ROLL_DIE: + eventRollDie(event.GetExtension(Event_RollDie::ext)); + break; + case GameEvent::CREATE_ARROW: + eventCreateArrow(event.GetExtension(Event_CreateArrow::ext)); + break; + case GameEvent::DELETE_ARROW: + eventDeleteArrow(event.GetExtension(Event_DeleteArrow::ext)); + break; + case GameEvent::CREATE_TOKEN: + eventCreateToken(event.GetExtension(Event_CreateToken::ext)); + break; + case GameEvent::SET_CARD_ATTR: + eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context, options); + break; + case GameEvent::SET_CARD_COUNTER: + eventSetCardCounter(event.GetExtension(Event_SetCardCounter::ext)); + break; + case GameEvent::CREATE_COUNTER: + eventCreateCounter(event.GetExtension(Event_CreateCounter::ext)); + break; + case GameEvent::SET_COUNTER: + eventSetCounter(event.GetExtension(Event_SetCounter::ext)); + break; + case GameEvent::DEL_COUNTER: + eventDelCounter(event.GetExtension(Event_DelCounter::ext)); + break; + case GameEvent::DUMP_ZONE: + eventDumpZone(event.GetExtension(Event_DumpZone::ext)); + break; + case GameEvent::MOVE_CARD: + eventMoveCard(event.GetExtension(Event_MoveCard::ext), context); + break; + case GameEvent::FLIP_CARD: + eventFlipCard(event.GetExtension(Event_FlipCard::ext)); + break; + case GameEvent::DESTROY_CARD: + eventDestroyCard(event.GetExtension(Event_DestroyCard::ext)); + break; + case GameEvent::ATTACH_CARD: + eventAttachCard(event.GetExtension(Event_AttachCard::ext)); + break; + case GameEvent::DRAW_CARDS: + eventDrawCards(event.GetExtension(Event_DrawCards::ext)); + break; + case GameEvent::REVEAL_CARDS: + eventRevealCards(event.GetExtension(Event_RevealCards::ext), options); + break; + case GameEvent::CHANGE_ZONE_PROPERTIES: + eventChangeZoneProperties(event.GetExtension(Event_ChangeZoneProperties::ext)); + break; + default: { + qWarning() << "unhandled game event" << type; + } + } +} \ No newline at end of file diff --git a/cockatrice/src/game/player/player_event_handler.h b/cockatrice/src/game/player/player_event_handler.h new file mode 100644 index 000000000..8df6cf299 --- /dev/null +++ b/cockatrice/src/game/player/player_event_handler.h @@ -0,0 +1,109 @@ +#ifndef COCKATRICE_PLAYER_EVENT_HANDLER_H +#define COCKATRICE_PLAYER_EVENT_HANDLER_H +#include "event_processing_options.h" + +#include +#include +#include + +class CardItem; +class CardZoneLogic; +class Player; +class Event_AttachCard; +class Event_ChangeZoneProperties; +class Event_CreateArrow; +class Event_CreateCounter; +class Event_CreateToken; +class Event_DelCounter; +class Event_DeleteArrow; +class Event_DestroyCard; +class Event_DrawCards; +class Event_DumpZone; +class Event_FlipCard; +class Event_GameSay; +class Event_MoveCard; +class Event_RevealCards; +class Event_RollDie; +class Event_SetCardAttr; +class Event_SetCardCounter; +class Event_SetCounter; +class Event_Shuffle; +class PlayerEventHandler : public QObject +{ + + Q_OBJECT +signals: + void logSay(Player *player, QString message); + void logShuffle(Player *player, CardZoneLogic *zone, int start, int end); + void logRollDie(Player *player, int sides, const QList &rolls); + void logCreateArrow(Player *player, + Player *startPlayer, + QString startCard, + Player *targetPlayer, + QString targetCard, + bool _playerTarget); + void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown); + void logDrawCards(Player *player, int number, bool deckIsEmpty); + void logUndoDraw(Player *player, QString cardName); + void logMoveCard(Player *player, + CardItem *card, + CardZoneLogic *startZone, + int oldX, + CardZoneLogic *targetZone, + int newX); + void logFlipCard(Player *player, QString cardName, bool faceDown); + void logDestroyCard(Player *player, QString cardName); + void logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName); + void logUnattachCard(Player *player, QString cardName); + void logSetCardCounter(Player *player, QString cardName, int counterId, int value, int oldValue); + void logSetTapped(Player *player, CardItem *card, bool tapped); + void logSetCounter(Player *player, QString counterName, int value, int oldValue); + void logSetDoesntUntap(Player *player, CardItem *card, bool doesntUntap); + void logSetPT(Player *player, CardItem *card, QString newPT); + void logSetAnnotation(Player *player, CardItem *card, QString newAnnotation); + void logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed = false); + void logRevealCards(Player *player, + CardZoneLogic *zone, + int cardId, + QString cardName, + Player *otherPlayer, + bool faceDown, + int amount, + bool isLentToAnotherPlayer = false); + void logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal); + void logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal); + +public: + PlayerEventHandler(Player *player); + + void processGameEvent(GameEvent::GameEventType type, + const GameEvent &event, + const GameEventContext &context, + EventProcessingOptions options); + + void eventGameSay(const Event_GameSay &event); + void eventShuffle(const Event_Shuffle &event); + void eventRollDie(const Event_RollDie &event); + void eventCreateArrow(const Event_CreateArrow &event); + void eventDeleteArrow(const Event_DeleteArrow &event); + void eventCreateToken(const Event_CreateToken &event); + void + eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context, EventProcessingOptions options); + void eventSetCardCounter(const Event_SetCardCounter &event); + void eventCreateCounter(const Event_CreateCounter &event); + void eventSetCounter(const Event_SetCounter &event); + void eventDelCounter(const Event_DelCounter &event); + void eventDumpZone(const Event_DumpZone &event); + void eventMoveCard(const Event_MoveCard &event, const GameEventContext &context); + void eventFlipCard(const Event_FlipCard &event); + void eventDestroyCard(const Event_DestroyCard &event); + void eventAttachCard(const Event_AttachCard &event); + void eventDrawCards(const Event_DrawCards &event); + void eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options); + void eventChangeZoneProperties(const Event_ChangeZoneProperties &event); + +private: + Player *player; +}; + +#endif // COCKATRICE_PLAYER_EVENT_HANDLER_H diff --git a/cockatrice/src/game/player/player_graphics_item.cpp b/cockatrice/src/game/player/player_graphics_item.cpp new file mode 100644 index 000000000..d4f7d47e3 --- /dev/null +++ b/cockatrice/src/game/player/player_graphics_item.cpp @@ -0,0 +1,215 @@ +#include "player_graphics_item.h" + +#include "../../client/tabs/tab_game.h" +#include "../hand_counter.h" + +PlayerGraphicsItem::PlayerGraphicsItem(Player *_player) : player(_player) +{ + connect(&SettingsCache::instance(), &SettingsCache::horizontalHandChanged, this, + &PlayerGraphicsItem::rearrangeZones); + connect(&SettingsCache::instance(), &SettingsCache::handJustificationChanged, this, + &PlayerGraphicsItem::rearrangeZones); + connect(player, &Player::rearrangeCounters, this, &PlayerGraphicsItem::rearrangeCounters); + + playerArea = new PlayerArea(this); + + playerTarget = new PlayerTarget(player, playerArea); + qreal avatarMargin = (counterAreaWidth + CARD_HEIGHT + 15 - playerTarget->boundingRect().width()) / 2.0; + playerTarget->setPos(QPointF(avatarMargin, avatarMargin)); + + initializeZones(); + + connect(tableZoneGraphicsItem, &TableZone::sizeChanged, this, &PlayerGraphicsItem::updateBoundingRect); + + updateBoundingRect(); + + rearrangeZones(); + retranslateUi(); +} + +void PlayerGraphicsItem::retranslateUi() +{ + player->getPlayerMenu()->retranslateUi(); + + QMapIterator zoneIterator(player->getZones()); + while (zoneIterator.hasNext()) { + emit zoneIterator.next().value()->retranslateUi(); + } + + QMapIterator counterIterator(player->getCounters()); + while (counterIterator.hasNext()) { + counterIterator.next().value()->retranslateUi(); + } +} + +void PlayerGraphicsItem::onPlayerActiveChanged(bool _active) +{ + tableZoneGraphicsItem->setActive(_active); +} + +void PlayerGraphicsItem::initializeZones() +{ + deckZoneGraphicsItem = new PileZone(player->getDeckZone(), this); + QPointF base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0, + 10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0); + deckZoneGraphicsItem->setPos(base); + + qreal h = deckZoneGraphicsItem->boundingRect().width() + 5; + + sideboardGraphicsItem = new PileZone(player->getSideboardZone(), this); + player->getSideboardZone()->setGraphicsVisibility(false); + + auto *handCounter = new HandCounter(playerArea); + handCounter->setPos(base + QPointF(0, h + 10)); + qreal h2 = handCounter->boundingRect().height(); + + graveyardZoneGraphicsItem = new PileZone(player->getGraveZone(), this); + graveyardZoneGraphicsItem->setPos(base + QPointF(0, h + h2 + 10)); + + rfgZoneGraphicsItem = new PileZone(player->getRfgZone(), this); + rfgZoneGraphicsItem->setPos(base + QPointF(0, 2 * h + h2 + 10)); + + tableZoneGraphicsItem = new TableZone(player->getTableZone(), this); + connect(tableZoneGraphicsItem, &TableZone::sizeChanged, this, &PlayerGraphicsItem::updateBoundingRect); + + stackZoneGraphicsItem = + new StackZone(player->getStackZone(), static_cast(tableZoneGraphicsItem->boundingRect().height()), this); + + handZoneGraphicsItem = + new HandZone(player->getHandZone(), static_cast(tableZoneGraphicsItem->boundingRect().height()), this); + + connect(handZoneGraphicsItem->getLogic(), &HandZoneLogic::cardCountChanged, handCounter, + &HandCounter::updateNumber); + connect(handCounter, &HandCounter::showContextMenu, handZoneGraphicsItem, &HandZone::showContextMenu); +} + +QRectF PlayerGraphicsItem::boundingRect() const +{ + return bRect; +} + +qreal PlayerGraphicsItem::getMinimumWidth() const +{ + qreal result = tableZoneGraphicsItem->getMinimumWidth() + CARD_HEIGHT + 15 + counterAreaWidth + + stackZoneGraphicsItem->boundingRect().width(); + if (!SettingsCache::instance().getHorizontalHand()) { + result += handZoneGraphicsItem->boundingRect().width(); + } + return result; +} + +void PlayerGraphicsItem::paint(QPainter * /*painter*/, + const QStyleOptionGraphicsItem * /*option*/, + QWidget * /*widget*/) +{ +} + +void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth) +{ + // Extend table (and hand, if horizontal) to accommodate the new player width. + qreal tableWidth = + newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stackZoneGraphicsItem->boundingRect().width(); + if (!SettingsCache::instance().getHorizontalHand()) { + tableWidth -= handZoneGraphicsItem->boundingRect().width(); + } + + tableZoneGraphicsItem->setWidth(tableWidth); + handZoneGraphicsItem->setWidth(tableWidth + stackZoneGraphicsItem->boundingRect().width()); +} + +void PlayerGraphicsItem::setMirrored(bool _mirrored) +{ + if (mirrored != _mirrored) { + mirrored = _mirrored; + rearrangeZones(); + } +} + +void PlayerGraphicsItem::rearrangeCounters() +{ + qreal marginTop = 80; + const qreal padding = 5; + qreal ySize = boundingRect().y() + marginTop; + + // Place objects + for (const auto &counter : player->getCounters()) { + AbstractCounter *ctr = counter; + + if (!ctr->getShownInCounterArea()) { + continue; + } + + QRectF br = ctr->boundingRect(); + ctr->setPos((counterAreaWidth - br.width()) / 2, ySize); + ySize += br.height() + padding; + } +} + +void PlayerGraphicsItem::rearrangeZones() +{ + QPointF base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0); + if (SettingsCache::instance().getHorizontalHand()) { + if (mirrored) { + if (player->getHandZone()->contentsKnown()) { + player->getPlayerInfo()->setHandVisible(true); + handZoneGraphicsItem->setPos(base); + base += QPointF(0, handZoneGraphicsItem->boundingRect().height()); + } else { + player->getPlayerInfo()->setHandVisible(false); + } + + stackZoneGraphicsItem->setPos(base); + base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0); + + tableZoneGraphicsItem->setPos(base); + } else { + stackZoneGraphicsItem->setPos(base); + + tableZoneGraphicsItem->setPos(base.x() + stackZoneGraphicsItem->boundingRect().width(), 0); + base += QPointF(0, tableZoneGraphicsItem->boundingRect().height()); + + if (player->getHandZone()->contentsKnown()) { + player->getPlayerInfo()->setHandVisible(true); + handZoneGraphicsItem->setPos(base); + } else { + player->getPlayerInfo()->setHandVisible(false); + } + } + handZoneGraphicsItem->setWidth(tableZoneGraphicsItem->getWidth() + + stackZoneGraphicsItem->boundingRect().width()); + } else { + player->getPlayerInfo()->setHandVisible(true); + + handZoneGraphicsItem->setPos(base); + base += QPointF(handZoneGraphicsItem->boundingRect().width(), 0); + + stackZoneGraphicsItem->setPos(base); + base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0); + + tableZoneGraphicsItem->setPos(base); + } + handZoneGraphicsItem->setVisible(player->getPlayerInfo()->getHandVisible()); + handZoneGraphicsItem->updateOrientation(); + tableZoneGraphicsItem->reorganizeCards(); + updateBoundingRect(); + rearrangeCounters(); +} + +void PlayerGraphicsItem::updateBoundingRect() +{ + prepareGeometryChange(); + qreal width = CARD_HEIGHT + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); + if (SettingsCache::instance().getHorizontalHand()) { + qreal handHeight = + player->getPlayerInfo()->getHandVisible() ? handZoneGraphicsItem->boundingRect().height() : 0; + bRect = QRectF(0, 0, width + tableZoneGraphicsItem->boundingRect().width(), + tableZoneGraphicsItem->boundingRect().height() + handHeight); + } else { + bRect = QRectF( + 0, 0, width + handZoneGraphicsItem->boundingRect().width() + tableZoneGraphicsItem->boundingRect().width(), + tableZoneGraphicsItem->boundingRect().height()); + } + playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height()); + + emit sizeChanged(); +} \ No newline at end of file diff --git a/cockatrice/src/game/player/player_graphics_item.h b/cockatrice/src/game/player/player_graphics_item.h new file mode 100644 index 000000000..70f42ba79 --- /dev/null +++ b/cockatrice/src/game/player/player_graphics_item.h @@ -0,0 +1,125 @@ +#ifndef COCKATRICE_PLAYER_GRAPHICS_ITEM_H +#define COCKATRICE_PLAYER_GRAPHICS_ITEM_H +#include "../game_scene.h" +#include "player.h" + +#include + +class HandZone; +class PileZone; +class PlayerTarget; +class StackZone; +class TableZone; +class ZoneViewZone; + +class PlayerGraphicsItem : public QGraphicsObject +{ + Q_OBJECT + +public: + enum + { + Type = typeOther + }; + int type() const override + { + return Type; + } + + static constexpr int counterAreaWidth = 55; + + explicit PlayerGraphicsItem(Player *player); + void initializeZones(); + + [[nodiscard]] QRectF boundingRect() const override; + qreal getMinimumWidth() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void processSceneSizeChange(int newPlayerWidth); + + void setMirrored(bool _mirrored); + + bool getMirrored() const + { + return mirrored; + } + + GameScene *getGameScene() const + { + return static_cast(scene()); + } + + Player *getPlayer() const + { + return player; + } + + PlayerArea *getPlayerArea() const + { + return playerArea; + } + + PlayerTarget *getPlayerTarget() const + { + return playerTarget; + } + + [[nodiscard]] PileZone *getDeckZoneGraphicsItem() const + { + return deckZoneGraphicsItem; + } + + [[nodiscard]] PileZone *getSideboardZoneGraphicsItem() const + { + return sideboardGraphicsItem; + } + + [[nodiscard]] PileZone *getGraveyardZoneGraphicsItem() const + { + return graveyardZoneGraphicsItem; + } + [[nodiscard]] PileZone *getRfgZoneGraphicsItem() const + { + return rfgZoneGraphicsItem; + } + [[nodiscard]] TableZone *getTableZoneGraphicsItem() const + { + return tableZoneGraphicsItem; + } + [[nodiscard]] StackZone *getStackZoneGraphicsItem() const + { + return stackZoneGraphicsItem; + } + [[nodiscard]] HandZone *getHandZoneGraphicsItem() const + { + return handZoneGraphicsItem; + } + +public slots: + void onPlayerActiveChanged(bool _active); + void retranslateUi(); + +signals: + void sizeChanged(); + void playerCountChanged(); + +private: + Player *player; + PlayerArea *playerArea; + PlayerTarget *playerTarget; + PileZone *deckZoneGraphicsItem; + PileZone *sideboardGraphicsItem; + PileZone *graveyardZoneGraphicsItem; + PileZone *rfgZoneGraphicsItem; + TableZone *tableZoneGraphicsItem; + StackZone *stackZoneGraphicsItem; + HandZone *handZoneGraphicsItem; + QRectF bRect; + bool mirrored; + +private slots: + void updateBoundingRect(); + void rearrangeZones(); + void rearrangeCounters(); +}; + +#endif // COCKATRICE_PLAYER_GRAPHICS_ITEM_H diff --git a/cockatrice/src/game/player/player_info.cpp b/cockatrice/src/game/player/player_info.cpp new file mode 100644 index 000000000..8507f75eb --- /dev/null +++ b/cockatrice/src/game/player/player_info.cpp @@ -0,0 +1,8 @@ +#include "player_info.h" + +PlayerInfo::PlayerInfo(const ServerInfo_User &info, int _id, bool _local, bool _judge) + : id(_id), local(_local), judge(_judge), handVisible(false) +{ + userInfo = new ServerInfo_User; + userInfo->CopyFrom(info); +} \ No newline at end of file diff --git a/cockatrice/src/game/player/player_info.h b/cockatrice/src/game/player/player_info.h new file mode 100644 index 000000000..a8759b151 --- /dev/null +++ b/cockatrice/src/game/player/player_info.h @@ -0,0 +1,69 @@ +#ifndef COCKATRICE_PLAYER_INFO_H +#define COCKATRICE_PLAYER_INFO_H + +#include "../../../common/pb/serverinfo_user.pb.h" +#include "../../deck/deck_loader.h" +#include "../zones/hand_zone.h" +#include "../zones/pile_zone.h" +#include "../zones/stack_zone.h" +#include "../zones/table_zone.h" +#include "player_target.h" + +#include + +class PlayerInfo : public QObject +{ + Q_OBJECT +public: + PlayerInfo(const ServerInfo_User &info, int id, bool local, bool judge); + + ServerInfo_User *userInfo; + int id; + bool local; + bool judge; + bool handVisible; + + int getId() const + { + return id; + } + ServerInfo_User *getUserInfo() const + { + return userInfo; + } + + void setLocal(bool _local) + { + local = _local; + } + + bool getLocal() const + { + return local; + } + bool getLocalOrJudge() const + { + return local || judge; + } + bool getJudge() const + { + return judge; + } + + void setHandVisible(bool _handVisible) + { + handVisible = _handVisible; + } + + bool getHandVisible() const + { + return handVisible; + } + + QString getName() const + { + return QString::fromStdString(userInfo->name()); + } +}; + +#endif // COCKATRICE_PLAYER_INFO_H diff --git a/cockatrice/src/game/player/player_list_widget.cpp b/cockatrice/src/game/player/player_list_widget.cpp index dc7489dce..72bed247d 100644 --- a/cockatrice/src/game/player/player_list_widget.cpp +++ b/cockatrice/src/game/player/player_list_widget.cpp @@ -56,7 +56,7 @@ bool PlayerListTWI::operator<(const QTreeWidgetItem &other) const PlayerListWidget::PlayerListWidget(TabSupervisor *_tabSupervisor, AbstractClient *_client, - TabGame *_game, + AbstractGame *_game, QWidget *parent) : QTreeWidget(parent), tabSupervisor(_tabSupervisor), client(_client), game(_game), gameStarted(false) { diff --git a/cockatrice/src/game/player/player_list_widget.h b/cockatrice/src/game/player/player_list_widget.h index 4cef51998..ef5ceb212 100644 --- a/cockatrice/src/game/player/player_list_widget.h +++ b/cockatrice/src/game/player/player_list_widget.h @@ -11,7 +11,7 @@ class ServerInfo_PlayerProperties; class TabSupervisor; class AbstractClient; -class TabGame; +class AbstractGame; class UserContextMenu; class PlayerListItemDelegate : public QStyledItemDelegate @@ -39,7 +39,7 @@ private: QMap players; TabSupervisor *tabSupervisor; AbstractClient *client; - TabGame *game; + AbstractGame *game; UserContextMenu *userContextMenu; QIcon readyIcon, notReadyIcon, concededIcon, playerIcon, judgeIcon, spectatorIcon, lockIcon; bool gameStarted; @@ -47,7 +47,10 @@ signals: void openMessageDialog(const QString &userName, bool focus); public: - PlayerListWidget(TabSupervisor *_tabSupervisor, AbstractClient *_client, TabGame *_game, QWidget *parent = nullptr); + PlayerListWidget(TabSupervisor *_tabSupervisor, + AbstractClient *_client, + AbstractGame *_game, + QWidget *parent = nullptr); void retranslateUi(); void setActivePlayer(int playerId); void setGameStarted(bool _gameStarted, bool resuming); diff --git a/cockatrice/src/game/player/player_manager.cpp b/cockatrice/src/game/player/player_manager.cpp new file mode 100644 index 000000000..eb86fcace --- /dev/null +++ b/cockatrice/src/game/player/player_manager.cpp @@ -0,0 +1,84 @@ +#include "player_manager.h" + +#include "../abstract_game.h" +#include "player.h" + +PlayerManager::PlayerManager(AbstractGame *_game, + int _localPlayerId, + bool _localPlayerIsJudge, + bool localPlayerIsSpectator) + : QObject(_game), game(_game), players(QMap()), localPlayerId(_localPlayerId), + localPlayerIsJudge(_localPlayerIsJudge), localPlayerIsSpectator(localPlayerIsSpectator) +{ +} + +bool PlayerManager::isMainPlayerConceded() const +{ + Player *player = players.value(localPlayerId, nullptr); + return player && player->getConceded(); +} + +Player *PlayerManager::getActiveLocalPlayer(int activePlayer) const +{ + Player *active = players.value(activePlayer, 0); + if (active) + if (active->getPlayerInfo()->getLocal()) + return active; + + QMapIterator playerIterator(players); + while (playerIterator.hasNext()) { + Player *temp = playerIterator.next().value(); + if (temp->getPlayerInfo()->getLocal()) + return temp; + } + + return nullptr; +} + +Player *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info) +{ + auto *newPlayer = new Player(info, playerId, isLocalPlayer(playerId) || game->getGameState()->getIsLocalGame(), + isJudge(), getGame()); + connect(newPlayer, &Player::concededChanged, this, &PlayerManager::playerConceded); + players.insert(playerId, newPlayer); + emit playerAdded(newPlayer); + emit playerCountChanged(); + return newPlayer; +} + +void PlayerManager::removePlayer(int playerId) +{ + Player *player = getPlayer(playerId); + if (!player) { + return; + } + players.remove(playerId); + emit playerCountChanged(); + emit playerRemoved(player); +} + +Player *PlayerManager::getPlayer(int playerId) const +{ + Player *player = players.value(playerId, 0); + if (!player) + return nullptr; + return player; +} + +void PlayerManager::onPlayerConceded(int playerId, bool conceded) +{ + // GameEventHandler cares about this for sending the concede/unconcede commands + if (playerId == getActiveLocalPlayer(playerId)->getPlayerInfo()->getId()) { + if (conceded) { + emit activeLocalPlayerConceded(); + } else { + emit activeLocalPlayerUnconceded(); + } + } + // Everything else cares about this + if (conceded) { + emit playerConceded(playerId); + } else { + emit playerUnconceded(playerId); + } +} \ No newline at end of file diff --git a/cockatrice/src/game/player/player_manager.h b/cockatrice/src/game/player/player_manager.h new file mode 100644 index 000000000..92e492510 --- /dev/null +++ b/cockatrice/src/game/player/player_manager.h @@ -0,0 +1,114 @@ +#ifndef COCKATRICE_PLAYER_MANAGER_H +#define COCKATRICE_PLAYER_MANAGER_H + +#include "pb/serverinfo_playerproperties.pb.h" + +#include +#include + +class AbstractGame; +class Player; +class PlayerManager : public QObject +{ + Q_OBJECT + +public: + PlayerManager(AbstractGame *_game, int _localPlayerId, bool _localPlayerIsJudge, bool localPlayerIsSpectator); + + AbstractGame *game; + QMap players; + int localPlayerId; + bool localPlayerIsJudge; + bool localPlayerIsSpectator; + QMap spectators; + + bool isSpectator() const + { + return localPlayerIsSpectator; + } + + bool isJudge() const + { + return localPlayerIsJudge; + } + + int getLocalPlayerId() const + { + return localPlayerId; + } + + const QMap &getPlayers() const + { + return players; + } + + int getPlayerCount() const + { + return players.size(); + } + + Player *getActiveLocalPlayer(int activePlayer) const; + + Player *addPlayer(int playerId, const ServerInfo_User &info); + + void removePlayer(int playerId); + + Player *getPlayer(int playerId) const; + + void onPlayerConceded(int playerId, bool conceded); + + [[nodiscard]] bool isMainPlayerConceded() const; + + [[nodiscard]] bool isLocalPlayer(int playerId) const + { + return playerId == getLocalPlayerId(); + } + + const QMap &getSpectators() const + { + return spectators; + } + + ServerInfo_User getSpectator(int playerId) const + { + return spectators.value(playerId); + } + + QString getSpectatorName(int spectatorId) const + { + return QString::fromStdString(spectators.value(spectatorId).name()); + } + + void addSpectator(int spectatorId, const ServerInfo_PlayerProperties &prop) + { + if (!spectators.contains(spectatorId)) { + spectators.insert(spectatorId, prop.user_info()); + emit spectatorAdded(prop); + } + } + + void removeSpectator(int spectatorId) + { + ServerInfo_User spectatorInfo = spectators.value(spectatorId); + spectators.remove(spectatorId); + emit spectatorRemoved(spectatorId, spectatorInfo); + } + + AbstractGame *getGame() const + { + return game; + } + +signals: + void playerAdded(Player *player); + void playerRemoved(Player *player); + void activeLocalPlayerConceded(); + void activeLocalPlayerUnconceded(); + void playerConceded(int playerId); + void playerUnconceded(int playerId); + void playerCountChanged(); + void spectatorAdded(ServerInfo_PlayerProperties spectator); + void spectatorRemoved(int spectatorId, ServerInfo_User spectator); +}; + +#endif // COCKATRICE_PLAYER_MANAGER_H diff --git a/cockatrice/src/game/player/player_menu.cpp b/cockatrice/src/game/player/player_menu.cpp new file mode 100644 index 000000000..f91b421c5 --- /dev/null +++ b/cockatrice/src/game/player/player_menu.cpp @@ -0,0 +1,1386 @@ +#include "player_menu.h" + +#include "../../../common/pb/command_reveal_cards.pb.h" +#include "../../client/tabs/tab_game.h" +#include "../../settings/card_counter_settings.h" +#include "../board/card_item.h" +#include "../cards/card_database_manager.h" +#include "../zones/hand_zone.h" +#include "../zones/logic/view_zone_logic.h" +#include "card_menu_action_type.h" +#include "player_actions.h" + +PlayerMenu::PlayerMenu(Player *_player) : player(_player) +{ + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + connect(player->getGame()->getPlayerManager(), &PlayerManager::playerAdded, this, &PlayerMenu::addPlayer); + connect(player->getGame()->getPlayerManager(), &PlayerManager::playerRemoved, this, &PlayerMenu::removePlayer); + } + + const QList &players = player->getGame()->getPlayerManager()->getPlayers().values(); + for (const auto playerToAdd : players) { + addPlayer(playerToAdd); + } + + PlayerActions *playerActions = player->getPlayerActions(); + + createDrawActions(); + createShuffleActions(); + createMoveActions(); + createViewActions(); + + playerMenu = new TearOffMenu(); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + handMenu = playerMenu->addTearOffMenu(QString()); + handMenu->addAction(aViewHand); + handMenu->addAction(aSortHand); + playerLists.append(mRevealHand = handMenu->addMenu(QString())); + playerLists.append(mRevealRandomHandCard = handMenu->addMenu(QString())); + handMenu->addSeparator(); + handMenu->addAction(aMulligan); + handMenu->addSeparator(); + moveHandMenu = handMenu->addTearOffMenu(QString()); + moveHandMenu->addAction(aMoveHandToTopLibrary); + moveHandMenu->addAction(aMoveHandToBottomLibrary); + moveHandMenu->addSeparator(); + moveHandMenu->addAction(aMoveHandToGrave); + moveHandMenu->addSeparator(); + moveHandMenu->addAction(aMoveHandToRfg); + + libraryMenu = playerMenu->addTearOffMenu(QString()); + libraryMenu->addAction(aDrawCard); + libraryMenu->addAction(aDrawCards); + libraryMenu->addAction(aUndoDraw); + libraryMenu->addSeparator(); + libraryMenu->addAction(aShuffle); + libraryMenu->addSeparator(); + libraryMenu->addAction(aViewLibrary); + libraryMenu->addAction(aViewTopCards); + libraryMenu->addAction(aViewBottomCards); + libraryMenu->addSeparator(); + playerLists.append(mRevealLibrary = libraryMenu->addMenu(QString())); + singlePlayerLists.append(mLendLibrary = libraryMenu->addMenu(QString())); + playerLists.append(mRevealTopCard = libraryMenu->addMenu(QString())); + libraryMenu->addAction(aAlwaysRevealTopCard); + libraryMenu->addAction(aAlwaysLookAtTopCard); + libraryMenu->addSeparator(); + topLibraryMenu = libraryMenu->addTearOffMenu(QString()); + bottomLibraryMenu = libraryMenu->addTearOffMenu(QString()); + libraryMenu->addSeparator(); + libraryMenu->addAction(aOpenDeckInDeckEditor); + topLibraryMenu->addAction(aMoveTopToPlay); + topLibraryMenu->addAction(aMoveTopToPlayFaceDown); + topLibraryMenu->addAction(aMoveTopCardToBottom); + topLibraryMenu->addSeparator(); + topLibraryMenu->addAction(aMoveTopCardToGraveyard); + topLibraryMenu->addAction(aMoveTopCardsToGraveyard); + topLibraryMenu->addAction(aMoveTopCardToExile); + topLibraryMenu->addAction(aMoveTopCardsToExile); + topLibraryMenu->addAction(aMoveTopCardsUntil); + topLibraryMenu->addSeparator(); + topLibraryMenu->addAction(aShuffleTopCards); + + bottomLibraryMenu->addAction(aDrawBottomCard); + bottomLibraryMenu->addAction(aDrawBottomCards); + bottomLibraryMenu->addSeparator(); + bottomLibraryMenu->addAction(aMoveBottomToPlay); + bottomLibraryMenu->addAction(aMoveBottomToPlayFaceDown); + bottomLibraryMenu->addAction(aMoveBottomCardToTop); + bottomLibraryMenu->addSeparator(); + bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard); + bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard); + bottomLibraryMenu->addAction(aMoveBottomCardToExile); + bottomLibraryMenu->addAction(aMoveBottomCardsToExile); + bottomLibraryMenu->addSeparator(); + bottomLibraryMenu->addAction(aShuffleBottomCards); + } + + graveMenu = playerMenu->addTearOffMenu(QString()); + graveMenu->addAction(aViewGraveyard); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + mRevealRandomGraveyardCard = graveMenu->addMenu(QString()); + QAction *newAction = mRevealRandomGraveyardCard->addAction(QString()); + newAction->setData(-1); + connect(newAction, &QAction::triggered, playerActions, &PlayerActions::actRevealRandomGraveyardCard); + allPlayersActions.append(newAction); + mRevealRandomGraveyardCard->addSeparator(); + } + + rfgMenu = playerMenu->addTearOffMenu(QString()); + rfgMenu->addAction(aViewRfg); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + graveMenu->addSeparator(); + moveGraveMenu = graveMenu->addTearOffMenu(QString()); + moveGraveMenu->addAction(aMoveGraveToTopLibrary); + moveGraveMenu->addAction(aMoveGraveToBottomLibrary); + moveGraveMenu->addSeparator(); + moveGraveMenu->addAction(aMoveGraveToHand); + moveGraveMenu->addSeparator(); + moveGraveMenu->addAction(aMoveGraveToRfg); + + rfgMenu->addSeparator(); + moveRfgMenu = rfgMenu->addTearOffMenu(QString()); + moveRfgMenu->addAction(aMoveRfgToTopLibrary); + moveRfgMenu->addAction(aMoveRfgToBottomLibrary); + moveRfgMenu->addSeparator(); + moveRfgMenu->addAction(aMoveRfgToHand); + moveRfgMenu->addSeparator(); + moveRfgMenu->addAction(aMoveRfgToGrave); + + sbMenu = playerMenu->addMenu(QString()); + sbMenu->addAction(aViewSideboard); + + mCustomZones = playerMenu->addMenu(QString()); + mCustomZones->menuAction()->setVisible(false); + + aUntapAll = new QAction(this); + connect(aUntapAll, &QAction::triggered, playerActions, &PlayerActions::actUntapAll); + + aRollDie = new QAction(this); + connect(aRollDie, &QAction::triggered, playerActions, &PlayerActions::actRollDie); + + aCreateToken = new QAction(this); + connect(aCreateToken, &QAction::triggered, playerActions, &PlayerActions::actCreateToken); + + aCreateAnotherToken = new QAction(this); + connect(aCreateAnotherToken, &QAction::triggered, playerActions, &PlayerActions::actCreateAnotherToken); + aCreateAnotherToken->setEnabled(false); + + aIncrementAllCardCounters = new QAction(this); + connect(aIncrementAllCardCounters, &QAction::triggered, player, &Player::incrementAllCardCounters); + + createPredefinedTokenMenu = new QMenu(QString()); + createPredefinedTokenMenu->setEnabled(false); + + mCardCounters = new QMenu; + + playerMenu->addSeparator(); + countersMenu = playerMenu->addMenu(QString()); + playerMenu->addAction(aIncrementAllCardCounters); + playerMenu->addSeparator(); + playerMenu->addAction(aUntapAll); + playerMenu->addSeparator(); + playerMenu->addAction(aRollDie); + playerMenu->addSeparator(); + playerMenu->addAction(aCreateToken); + playerMenu->addAction(aCreateAnotherToken); + playerMenu->addMenu(createPredefinedTokenMenu); + playerMenu->addSeparator(); + } + + if (player->getPlayerInfo()->local) { + sayMenu = playerMenu->addMenu(QString()); + connect(&SettingsCache::instance().messages(), &MessageSettings::messageMacrosChanged, this, + &PlayerMenu::initSayMenu); + initSayMenu(); + } + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + + for (auto &playerList : playerLists) { + QAction *newAction = playerList->addAction(QString()); + newAction->setData(-1); + connect(newAction, &QAction::triggered, this, &PlayerMenu::playerListActionTriggered); + allPlayersActions.append(newAction); + playerList->addSeparator(); + } + } + + // We have to explicitly not-instantiate a bunch of things if we are not local or a judge or else we have to + // consider it everywhere instead of just null-checking + if (!player->getPlayerInfo()->local && !player->getPlayerInfo()->judge) { + countersMenu = nullptr; + sbMenu = nullptr; + mCustomZones = nullptr; + aCreateAnotherToken = nullptr; + createPredefinedTokenMenu = nullptr; + mCardCounters = nullptr; + moveHandMenu = nullptr; + aMoveHandToTopLibrary = nullptr; + aMoveHandToBottomLibrary = nullptr; + aMoveHandToGrave = nullptr; + aMoveHandToRfg = nullptr; + + moveGraveMenu = nullptr; + aMoveGraveToTopLibrary = nullptr; + aMoveGraveToBottomLibrary = nullptr; + aMoveGraveToHand = nullptr; + aMoveGraveToRfg = nullptr; + + moveRfgMenu = nullptr; + aMoveRfgToTopLibrary = nullptr; + aMoveRfgToBottomLibrary = nullptr; + aMoveRfgToHand = nullptr; + aMoveRfgToGrave = nullptr; + + aViewLibrary = nullptr; + aViewHand = nullptr; + aSortHand = nullptr; + aViewTopCards = nullptr; + aViewBottomCards = nullptr; + mRevealLibrary = nullptr; + mLendLibrary = nullptr; + mRevealTopCard = nullptr; + topLibraryMenu = nullptr; + bottomLibraryMenu = nullptr; + aAlwaysRevealTopCard = nullptr; + aAlwaysLookAtTopCard = nullptr; + aOpenDeckInDeckEditor = nullptr; + aViewSideboard = nullptr; + aDrawCard = nullptr; + aDrawCards = nullptr; + aUndoDraw = nullptr; + aMulligan = nullptr; + + aShuffle = nullptr; + aMoveTopToPlay = nullptr; + aMoveTopToPlayFaceDown = nullptr; + aMoveTopCardToBottom = nullptr; + aMoveTopCardToGraveyard = nullptr; + aMoveTopCardToExile = nullptr; + aMoveTopCardsToGraveyard = nullptr; + aMoveTopCardsToExile = nullptr; + aMoveTopCardsUntil = nullptr; + aShuffleTopCards = nullptr; + + aDrawBottomCard = nullptr; + aDrawBottomCards = nullptr; + aMoveBottomToPlay = nullptr; + aMoveBottomToPlayFaceDown = nullptr; + aMoveBottomCardToGraveyard = nullptr; + aMoveBottomCardToExile = nullptr; + aMoveBottomCardsToGraveyard = nullptr; + aMoveBottomCardsToExile = nullptr; + aMoveBottomCardToTop = nullptr; + aShuffleBottomCards = nullptr; + + handMenu = nullptr; + mRevealHand = nullptr; + mRevealRandomHandCard = nullptr; + mRevealRandomGraveyardCard = nullptr; + sbMenu = nullptr; + libraryMenu = nullptr; + countersMenu = nullptr; + mCustomZones = nullptr; + } + + aTap = new QAction(this); + aTap->setData(cmTap); + connect(aTap, &QAction::triggered, playerActions, &PlayerActions::cardMenuAction); + aDoesntUntap = new QAction(this); + aDoesntUntap->setData(cmDoesntUntap); + connect(aDoesntUntap, &QAction::triggered, playerActions, &PlayerActions::cardMenuAction); + 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); + aIncP = new QAction(this); + connect(aIncP, &QAction::triggered, playerActions, &PlayerActions::actIncP); + aDecP = new QAction(this); + connect(aDecP, &QAction::triggered, playerActions, &PlayerActions::actDecP); + aIncT = new QAction(this); + connect(aIncT, &QAction::triggered, playerActions, &PlayerActions::actIncT); + aDecT = new QAction(this); + connect(aDecT, &QAction::triggered, playerActions, &PlayerActions::actDecT); + aIncPT = new QAction(this); + connect(aIncPT, &QAction::triggered, playerActions, [playerActions] { playerActions->actIncPT(); }); + aDecPT = new QAction(this); + connect(aDecPT, &QAction::triggered, playerActions, &PlayerActions::actDecPT); + aFlowP = new QAction(this); + connect(aFlowP, &QAction::triggered, playerActions, &PlayerActions::actFlowP); + aFlowT = new QAction(this); + connect(aFlowT, &QAction::triggered, playerActions, &PlayerActions::actFlowT); + aSetPT = new QAction(this); + connect(aSetPT, &QAction::triggered, playerActions, &PlayerActions::actSetPT); + aResetPT = new QAction(this); + connect(aResetPT, &QAction::triggered, playerActions, &PlayerActions::actResetPT); + 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); + aMoveToTopLibrary = new QAction(this); + aMoveToTopLibrary->setData(cmMoveToTopLibrary); + aMoveToBottomLibrary = new QAction(this); + aMoveToBottomLibrary->setData(cmMoveToBottomLibrary); + aMoveToXfromTopOfLibrary = new QAction(this); + aMoveToGraveyard = new QAction(this); + aMoveToHand = new QAction(this); + aMoveToHand->setData(cmMoveToHand); + aMoveToGraveyard->setData(cmMoveToGraveyard); + aMoveToExile = new QAction(this); + aMoveToExile->setData(cmMoveToExile); + connect(aMoveToTopLibrary, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); + connect(aMoveToBottomLibrary, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); + connect(aMoveToXfromTopOfLibrary, &QAction::triggered, playerActions, &PlayerActions::actMoveCardXCardsFromTop); + connect(aMoveToHand, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); + connect(aMoveToGraveyard, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction); + connect(aMoveToExile, &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); + + 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); + + for (int i = 0; i < 6; ++i) { + auto *tempAddCounter = new QAction(this); + tempAddCounter->setData(9 + i * 1000); + auto *tempRemoveCounter = new QAction(this); + tempRemoveCounter->setData(10 + i * 1000); + auto *tempSetCounter = new QAction(this); + tempSetCounter->setData(11 + i * 1000); + aAddCounter.append(tempAddCounter); + aRemoveCounter.append(tempRemoveCounter); + aSetCounter.append(tempSetCounter); + connect(tempAddCounter, &QAction::triggered, playerActions, &PlayerActions::actCardCounterTrigger); + connect(tempRemoveCounter, &QAction::triggered, playerActions, &PlayerActions::actCardCounterTrigger); + connect(tempSetCounter, &QAction::triggered, playerActions, &PlayerActions::actCardCounterTrigger); + } + + connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, + &PlayerMenu::refreshShortcuts); + refreshShortcuts(); + + connect(player, &Player::clearCustomZonesMenu, this, &PlayerMenu::clearCustomZonesMenu); + connect(player, &Player::addViewCustomZoneActionToCustomZoneMenu, this, + &PlayerMenu::addViewCustomZoneActionToCustomZoneMenu); + connect(player, &Player::resetTopCardMenuActions, this, &PlayerMenu::resetTopCardMenuActions); + + retranslateUi(); +} + +void PlayerMenu::setMenusForGraphicItems() +{ + player->getGraphicsItem()->getTableZoneGraphicsItem()->setMenu(playerMenu); + player->getGraphicsItem()->getGraveyardZoneGraphicsItem()->setMenu(graveMenu, aViewGraveyard); + player->getGraphicsItem()->getRfgZoneGraphicsItem()->setMenu(rfgMenu, aViewRfg); + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + player->getGraphicsItem()->getHandZoneGraphicsItem()->setMenu(handMenu); + player->getGraphicsItem()->getDeckZoneGraphicsItem()->setMenu(libraryMenu, aDrawCard); + player->getGraphicsItem()->getSideboardZoneGraphicsItem()->setMenu(sbMenu); + } +} + +void PlayerMenu::createDrawActions() +{ + PlayerActions *playerActions = player->getPlayerActions(); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + aDrawCard = new QAction(this); + connect(aDrawCard, &QAction::triggered, playerActions, &PlayerActions::actDrawCard); + aDrawCards = new QAction(this); + connect(aDrawCards, &QAction::triggered, playerActions, &PlayerActions::actDrawCards); + aUndoDraw = new QAction(this); + connect(aUndoDraw, &QAction::triggered, playerActions, &PlayerActions::actUndoDraw); + aMulligan = new QAction(this); + connect(aMulligan, &QAction::triggered, playerActions, &PlayerActions::actMulligan); + aDrawBottomCard = new QAction(this); + connect(aDrawBottomCard, &QAction::triggered, playerActions, &PlayerActions::actDrawBottomCard); + aDrawBottomCards = new QAction(this); + connect(aDrawBottomCards, &QAction::triggered, playerActions, &PlayerActions::actDrawBottomCards); + } +} + +void PlayerMenu::createShuffleActions() +{ + PlayerActions *playerActions = player->getPlayerActions(); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + aShuffle = new QAction(this); + connect(aShuffle, &QAction::triggered, playerActions, &PlayerActions::actShuffle); + aShuffleTopCards = new QAction(this); + connect(aShuffleTopCards, &QAction::triggered, playerActions, &PlayerActions::actShuffleTop); + aShuffleBottomCards = new QAction(this); + connect(aShuffleBottomCards, &QAction::triggered, playerActions, &PlayerActions::actShuffleBottom); + } +} + +void PlayerMenu::createMoveActions() +{ + PlayerActions *playerActions = player->getPlayerActions(); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + aMoveHandToTopLibrary = new QAction(this); + aMoveHandToTopLibrary->setData(QList() << "deck" << 0); + aMoveHandToBottomLibrary = new QAction(this); + aMoveHandToBottomLibrary->setData(QList() << "deck" << -1); + aMoveHandToGrave = new QAction(this); + aMoveHandToGrave->setData(QList() << "grave" << 0); + aMoveHandToRfg = new QAction(this); + aMoveHandToRfg->setData(QList() << "rfg" << 0); + + auto hand = player->getHandZone(); + auto grave = player->getGraveZone(); + auto rfg = player->getRfgZone(); + + connect(aMoveHandToTopLibrary, &QAction::triggered, hand, &HandZoneLogic::moveAllToZone); + connect(aMoveHandToBottomLibrary, &QAction::triggered, hand, &HandZoneLogic::moveAllToZone); + connect(aMoveHandToGrave, &QAction::triggered, hand, &HandZoneLogic::moveAllToZone); + connect(aMoveHandToRfg, &QAction::triggered, hand, &HandZoneLogic::moveAllToZone); + + aMoveGraveToTopLibrary = new QAction(this); + aMoveGraveToTopLibrary->setData(QList() << "deck" << 0); + aMoveGraveToBottomLibrary = new QAction(this); + aMoveGraveToBottomLibrary->setData(QList() << "deck" << -1); + aMoveGraveToHand = new QAction(this); + aMoveGraveToHand->setData(QList() << "hand" << 0); + aMoveGraveToRfg = new QAction(this); + aMoveGraveToRfg->setData(QList() << "rfg" << 0); + + connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); + connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); + connect(aMoveGraveToHand, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); + connect(aMoveGraveToRfg, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); + + aMoveRfgToTopLibrary = new QAction(this); + aMoveRfgToTopLibrary->setData(QList() << "deck" << 0); + aMoveRfgToBottomLibrary = new QAction(this); + aMoveRfgToBottomLibrary->setData(QList() << "deck" << -1); + aMoveRfgToHand = new QAction(this); + aMoveRfgToHand->setData(QList() << "hand" << 0); + aMoveRfgToGrave = new QAction(this); + aMoveRfgToGrave->setData(QList() << "grave" << 0); + + connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); + connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); + connect(aMoveRfgToHand, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); + connect(aMoveRfgToGrave, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); + + aMoveTopToPlay = new QAction(this); + connect(aMoveTopToPlay, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToPlay); + aMoveTopToPlayFaceDown = new QAction(this); + connect(aMoveTopToPlayFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveTopCardToPlayFaceDown); + aMoveTopCardToGraveyard = new QAction(this); + connect(aMoveTopCardToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToGrave); + aMoveTopCardToExile = new QAction(this); + connect(aMoveTopCardToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToExile); + aMoveTopCardsToGraveyard = new QAction(this); + connect(aMoveTopCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToGrave); + aMoveTopCardsToExile = new QAction(this); + connect(aMoveTopCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToExile); + aMoveTopCardsUntil = new QAction(this); + connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsUntil); + aMoveTopCardToBottom = new QAction(this); + connect(aMoveTopCardToBottom, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToBottom); + + aMoveBottomToPlay = new QAction(this); + connect(aMoveBottomToPlay, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToPlay); + aMoveBottomToPlayFaceDown = new QAction(this); + connect(aMoveBottomToPlayFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardToPlayFaceDown); + aMoveBottomCardToGraveyard = new QAction(this); + connect(aMoveBottomCardToGraveyard, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardToGrave); + aMoveBottomCardToExile = new QAction(this); + connect(aMoveBottomCardToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToExile); + aMoveBottomCardsToGraveyard = new QAction(this); + connect(aMoveBottomCardsToGraveyard, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardsToGrave); + aMoveBottomCardsToExile = new QAction(this); + connect(aMoveBottomCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToExile); + aMoveBottomCardToTop = new QAction(this); + connect(aMoveBottomCardToTop, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToTop); + } +} + +void PlayerMenu::createViewActions() +{ + PlayerActions *playerActions = player->getPlayerActions(); + + aViewGraveyard = new QAction(this); + connect(aViewGraveyard, &QAction::triggered, playerActions, &PlayerActions::actViewGraveyard); + + aViewRfg = new QAction(this); + connect(aViewRfg, &QAction::triggered, playerActions, &PlayerActions::actViewRfg); + + if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { + aViewLibrary = new QAction(this); + connect(aViewLibrary, &QAction::triggered, playerActions, &PlayerActions::actViewLibrary); + aViewSideboard = new QAction(this); + connect(aViewSideboard, &QAction::triggered, playerActions, &PlayerActions::actViewSideboard); + + aViewHand = new QAction(this); + connect(aViewHand, &QAction::triggered, playerActions, &PlayerActions::actViewHand); + aSortHand = new QAction(this); + connect(aSortHand, &QAction::triggered, playerActions, &PlayerActions::actSortHand); + + aViewTopCards = new QAction(this); + connect(aViewTopCards, &QAction::triggered, playerActions, &PlayerActions::actViewTopCards); + aViewBottomCards = new QAction(this); + connect(aViewBottomCards, &QAction::triggered, playerActions, &PlayerActions::actViewBottomCards); + aAlwaysRevealTopCard = new QAction(this); + aAlwaysRevealTopCard->setCheckable(true); + connect(aAlwaysRevealTopCard, &QAction::triggered, playerActions, &PlayerActions::actAlwaysRevealTopCard); + aAlwaysLookAtTopCard = new QAction(this); + aAlwaysLookAtTopCard->setCheckable(true); + connect(aAlwaysLookAtTopCard, &QAction::triggered, playerActions, &PlayerActions::actAlwaysLookAtTopCard); + aOpenDeckInDeckEditor = new QAction(this); + aOpenDeckInDeckEditor->setEnabled(false); + connect(aOpenDeckInDeckEditor, &QAction::triggered, playerActions, &PlayerActions::actOpenDeckInDeckEditor); + } +} + +void PlayerMenu::refreshShortcuts() +{ + if (shortcutsActive) { + setShortcutsActive(); + } +} + +void PlayerMenu::addPlayer(Player *playerToAdd) +{ + if (playerToAdd == nullptr || playerToAdd == player) { + return; + } + + for (auto &playerList : playerLists) { + addPlayerToList(playerList, playerToAdd); + } + for (auto &playerList : singlePlayerLists) { + addPlayerToList(playerList, playerToAdd); + } + + playersInfo.append(qMakePair(playerToAdd->getPlayerInfo()->getName(), playerToAdd->getPlayerInfo()->getId())); +} + +void PlayerMenu::addPlayerToList(QMenu *playerList, Player *playerToAdd) +{ + QAction *newAction = playerList->addAction(playerToAdd->getPlayerInfo()->getName()); + newAction->setData(playerToAdd->getPlayerInfo()->getId()); + connect(newAction, &QAction::triggered, this, &PlayerMenu::playerListActionTriggered); +} + +void PlayerMenu::removePlayer(Player *playerToRemove) +{ + if (playerToRemove == nullptr) { + return; + } + + for (auto &playerList : playerLists) { + removePlayerFromList(playerList, playerToRemove); + } + for (auto &playerList : singlePlayerLists) { + removePlayerFromList(playerList, playerToRemove); + } + + for (auto it = playersInfo.begin(); it != playersInfo.end();) { + if (it->second == playerToRemove->getPlayerInfo()->getId()) { + it = playersInfo.erase(it); + } else { + ++it; + } + } +} + +void PlayerMenu::removePlayerFromList(QMenu *playerList, Player *player) +{ + QList actionList = playerList->actions(); + for (auto &j : actionList) + if (j->data().toInt() == player->getPlayerInfo()->getId()) { + playerList->removeAction(j); + j->deleteLater(); + } +} + +void PlayerMenu::playerListActionTriggered() +{ + auto *action = static_cast(sender()); + auto *menu = static_cast(action->parent()); + + Command_RevealCards cmd; + const int otherPlayerId = action->data().toInt(); + if (otherPlayerId != -1) { + cmd.set_player_id(otherPlayerId); + } + + if (menu == mRevealLibrary || menu == mLendLibrary) { + cmd.set_zone_name("deck"); + cmd.set_grant_write_access(menu == mLendLibrary); + } else if (menu == mRevealTopCard) { + int deckSize = player->getDeckZone()->getCards().size(); + bool ok; + int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Reveal top cards of library"), + tr("Number of cards: (max. %1)").arg(deckSize), /* defaultNumberTopCards */ 1, + 1, deckSize, 1, &ok); + if (ok) { + cmd.set_zone_name("deck"); + cmd.set_top_cards(number); + // backward compatibility: servers before #1051 only permits to reveal the first card + cmd.add_card_id(0); + // defaultNumberTopCards = number; + } + } else if (menu == mRevealHand) { + cmd.set_zone_name("hand"); + } else if (menu == mRevealRandomHandCard) { + cmd.set_zone_name("hand"); + cmd.add_card_id(PlayerActions::RANDOM_CARD_FROM_ZONE); + } else { + return; + } + + player->getPlayerActions()->sendGameCommand(cmd); +} + +void PlayerMenu::initContextualPlayersMenu(QMenu *menu) +{ + menu->addAction(tr("&All players"))->setData(-1); + menu->addSeparator(); + + for (const auto &playerInfo : playersInfo) { + menu->addAction(playerInfo.first)->setData(playerInfo.second); + } +} + +QMenu *PlayerMenu::createCardMenu(const CardItem *card) +{ + if (card == nullptr) { + return nullptr; + } + + bool revealedCard = false; + bool writeableCard = player->getPlayerInfo()->getLocalOrJudge(); + if (auto *view = qobject_cast(card->getZone())) { + if (view->getRevealZone()) { + if (view->getWriteableRevealZone()) { + writeableCard = true; + } else { + revealedCard = true; + } + } + } + + QMenu *cardMenu = new QMenu; + + if (revealedCard) { + cardMenu->addAction(aHide); + cardMenu->addAction(aClone); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + cardMenu->addAction(aSelectColumn); + addRelatedCardView(card, cardMenu); + } else if (writeableCard) { + bool canModifyCard = player->getPlayerInfo()->judge || card->getOwner() == player; + + if (card->getZone()) { + if (card->getZone()->getName() == "table") { + // Card is on the battlefield + + if (!canModifyCard) { + addRelatedCardView(card, cardMenu); + addRelatedCardActions(card, cardMenu); + + cardMenu->addSeparator(); + cardMenu->addAction(aDrawArrow); + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + cardMenu->addAction(aSelectRow); + return cardMenu; + } + + cardMenu->addAction(aTap); + cardMenu->addAction(aDoesntUntap); + cardMenu->addAction(aFlip); + if (card->getFaceDown()) { + cardMenu->addAction(aPeek); + } + + addRelatedCardView(card, cardMenu); + addRelatedCardActions(card, cardMenu); + + cardMenu->addSeparator(); + cardMenu->addAction(aAttach); + if (card->getAttachedTo()) { + cardMenu->addAction(aUnattach); + } + cardMenu->addAction(aDrawArrow); + cardMenu->addSeparator(); + cardMenu->addMenu(createPtMenu()); + cardMenu->addAction(aSetAnnotation); + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addMenu(createMoveMenu()); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + cardMenu->addAction(aSelectRow); + + cardMenu->addSeparator(); + mCardCounters->clear(); + for (int i = 0; i < aAddCounter.size(); ++i) { + mCardCounters->addSeparator(); + mCardCounters->addAction(aAddCounter[i]); + if (card->getCounters().contains(i)) { + mCardCounters->addAction(aRemoveCounter[i]); + } + mCardCounters->addAction(aSetCounter[i]); + } + cardMenu->addSeparator(); + cardMenu->addMenu(mCardCounters); + } else if (card->getZone()->getName() == "stack") { + // Card is on the stack + if (canModifyCard) { + cardMenu->addAction(aAttach); + cardMenu->addAction(aDrawArrow); + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addMenu(createMoveMenu()); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + } else { + cardMenu->addAction(aDrawArrow); + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + } + + addRelatedCardView(card, cardMenu); + addRelatedCardActions(card, cardMenu); + } else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") { + // Card is in the graveyard or exile + if (canModifyCard) { + cardMenu->addAction(aPlay); + cardMenu->addAction(aPlayFacedown); + + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addMenu(createMoveMenu()); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + cardMenu->addAction(aSelectColumn); + + cardMenu->addSeparator(); + cardMenu->addAction(aAttach); + cardMenu->addAction(aDrawArrow); + } else { + cardMenu->addAction(aClone); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + cardMenu->addAction(aSelectColumn); + cardMenu->addSeparator(); + cardMenu->addAction(aDrawArrow); + } + + addRelatedCardView(card, cardMenu); + addRelatedCardActions(card, cardMenu); + } else { + // Card is in hand or a custom zone specified by server + cardMenu->addAction(aPlay); + cardMenu->addAction(aPlayFacedown); + + QMenu *revealMenu = cardMenu->addMenu(tr("Re&veal to...")); + initContextualPlayersMenu(revealMenu); + + connect(revealMenu, &QMenu::triggered, player->getPlayerActions(), &PlayerActions::actReveal); + + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addMenu(createMoveMenu()); + + // actions that are really wonky when done from deck or sideboard + if (card->getZone()->getName() == "hand") { + cardMenu->addSeparator(); + cardMenu->addAction(aAttach); + cardMenu->addAction(aDrawArrow); + } + + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + if (qobject_cast(card->getZone())) { + cardMenu->addAction(aSelectColumn); + } + + addRelatedCardView(card, cardMenu); + if (card->getZone()->getName() == "hand") { + addRelatedCardActions(card, cardMenu); + } + } + } else { + cardMenu->addMenu(createMoveMenu()); + } + } else { + if (card->getZone() && card->getZone()->getName() != "hand") { + cardMenu->addAction(aDrawArrow); + cardMenu->addSeparator(); + addRelatedCardView(card, cardMenu); + addRelatedCardActions(card, cardMenu); + cardMenu->addSeparator(); + cardMenu->addAction(aClone); + cardMenu->addSeparator(); + cardMenu->addAction(aSelectAll); + } + } + + return cardMenu; +} + +QMenu *PlayerMenu::updateCardMenu(const CardItem *card) +{ + if (!card) { + emit cardMenuUpdated(nullptr); + return nullptr; + } + + // 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) { + return nullptr; + } + + QMenu *menu = createCardMenu(card); + emit cardMenuUpdated(menu); + + return menu; +} + +QMenu *PlayerMenu::createPtMenu() const +{ + QMenu *ptMenu = new QMenu(tr("Power / toughness")); + ptMenu->addAction(aIncP); + ptMenu->addAction(aDecP); + ptMenu->addAction(aFlowP); + ptMenu->addSeparator(); + ptMenu->addAction(aIncT); + ptMenu->addAction(aDecT); + ptMenu->addAction(aFlowT); + ptMenu->addSeparator(); + ptMenu->addAction(aIncPT); + ptMenu->addAction(aDecPT); + ptMenu->addSeparator(); + ptMenu->addAction(aSetPT); + ptMenu->addAction(aResetPT); + return ptMenu; +} + +QMenu *PlayerMenu::createMoveMenu() const +{ + QMenu *moveMenu = new QMenu(tr("Move to")); + moveMenu->addAction(aMoveToTopLibrary); + moveMenu->addAction(aMoveToXfromTopOfLibrary); + moveMenu->addAction(aMoveToBottomLibrary); + moveMenu->addSeparator(); + moveMenu->addAction(aMoveToHand); + moveMenu->addSeparator(); + moveMenu->addAction(aMoveToGraveyard); + moveMenu->addSeparator(); + moveMenu->addAction(aMoveToExile); + return moveMenu; +} + +void PlayerMenu::addRelatedCardView(const CardItem *card, QMenu *cardMenu) +{ + if (!card || !cardMenu) { + return; + } + auto exactCard = card->getCard(); + if (!exactCard) { + return; + } + + bool atLeastOneGoodRelationFound = false; + QList relatedCards = exactCard.getInfo().getAllRelatedCards(); + for (const CardRelation *cardRelation : relatedCards) { + CardInfoPtr relatedCard = CardDatabaseManager::getInstance()->getCardInfo(cardRelation->getName()); + if (relatedCard != nullptr) { + atLeastOneGoodRelationFound = true; + break; + } + } + + if (!atLeastOneGoodRelationFound) { + return; + } + + cardMenu->addSeparator(); + auto viewRelatedCards = new QMenu(tr("View related cards")); + cardMenu->addMenu(viewRelatedCards); + for (const CardRelation *relatedCard : relatedCards) { + QString relatedCardName = relatedCard->getName(); + CardRef cardRef = {relatedCardName, exactCard.getPrinting().getUuid()}; + QAction *viewCard = viewRelatedCards->addAction(relatedCardName); + Q_UNUSED(viewCard); + + connect(viewCard, &QAction::triggered, player->getGame(), + [this, cardRef] { player->getGame()->getTab()->viewCardInfo(cardRef); }); + } +} + +void PlayerMenu::addRelatedCardActions(const CardItem *card, QMenu *cardMenu) +{ + if (!card || !cardMenu) { + return; + } + auto exactCard = card->getCard(); + if (!exactCard) { + return; + } + + QList relatedCards = exactCard.getInfo().getAllRelatedCards(); + if (relatedCards.isEmpty()) { + return; + } + + cardMenu->addSeparator(); + int index = 0; + QAction *createRelatedCards = nullptr; + for (const CardRelation *cardRelation : relatedCards) { + ExactCard relatedCard = CardDatabaseManager::getInstance()->getCardFromSameSet(cardRelation->getName(), + card->getCard().getPrinting()); + if (!relatedCard) { + relatedCard = CardDatabaseManager::getInstance()->getCard({cardRelation->getName()}); + } + if (!relatedCard) { + continue; + } + + QString relatedCardName; + if (relatedCard.getInfo().getPowTough().size() > 0) { + relatedCardName = relatedCard.getInfo().getPowTough() + " " + relatedCard.getName(); // "n/n name" + } else { + relatedCardName = relatedCard.getName(); // "name" + } + + QString text = tr("Token: "); + if (cardRelation->getDoesAttach()) { + text += + tr(cardRelation->getDoesTransform() ? "Transform into " : "Attach to ") + "\"" + relatedCardName + "\""; + } else if (cardRelation->getIsVariable()) { + text += "X " + relatedCardName; + } else if (cardRelation->getDefaultCount() != 1) { + text += QString::number(cardRelation->getDefaultCount()) + "x " + relatedCardName; + } else { + text += relatedCardName; + } + + if (createRelatedCards == nullptr) { + if (relatedCards.length() == 1) { + createRelatedCards = new QAction(text, this); // set actCreateAllRelatedCards with this text + break; // do not set an individual entry as there is only one entry + } else { + createRelatedCards = new QAction(tr("All tokens"), this); + } + } + + auto *createRelated = new QAction(text, this); + createRelated->setData(QVariant(index++)); + connect(createRelated, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actCreateRelatedCard); + cardMenu->addAction(createRelated); + } + + if (createRelatedCards) { + if (shortcutsActive) { + createRelatedCards->setShortcuts( + SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens")); + } + connect(createRelatedCards, &QAction::triggered, player->getPlayerActions(), + &PlayerActions::actCreateAllRelatedCards); + cardMenu->addAction(createRelatedCards); + } +} + +void PlayerMenu::clearCustomZonesMenu() +{ + // Can be null if we are not the local player! + if (mCustomZones) { + mCustomZones->clear(); + mCustomZones->menuAction()->setVisible(false); + } +} + +void PlayerMenu::addViewCustomZoneActionToCustomZoneMenu(QString zoneName) +{ + if (mCustomZones) { + mCustomZones->menuAction()->setVisible(true); + QAction *aViewZone = mCustomZones->addAction(tr("View custom zone '%1'").arg(zoneName)); + aViewZone->setData(zoneName); + connect(aViewZone, &QAction::triggered, this, + [zoneName, this]() { player->getGameScene()->toggleZoneView(player, zoneName, -1); }); + } +} + +void PlayerMenu::populatePredefinedTokensMenu() +{ + DeckLoader *_deck = player->getDeck(); + createPredefinedTokenMenu->clear(); + createPredefinedTokenMenu->setEnabled(false); + predefinedTokens.clear(); + InnerDecklistNode *tokenZone = dynamic_cast(_deck->getRoot()->findChild(DECK_ZONE_TOKENS)); + + if (tokenZone) { + if (!tokenZone->empty()) + createPredefinedTokenMenu->setEnabled(true); + + for (int i = 0; i < tokenZone->size(); ++i) { + const QString tokenName = tokenZone->at(i)->getName(); + predefinedTokens.append(tokenName); + QAction *a = createPredefinedTokenMenu->addAction(tokenName); + if (i < 10) { + a->setShortcut(QKeySequence("Alt+" + QString::number((i + 1) % 10))); + } + connect(a, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actCreatePredefinedToken); + } + } +} + +void PlayerMenu::enableOpenInDeckEditorAction() const +{ + aOpenDeckInDeckEditor->setEnabled(true); +} + +void PlayerMenu::resetTopCardMenuActions() +{ + aAlwaysRevealTopCard->setChecked(false); + aAlwaysLookAtTopCard->setChecked(false); +} + +void PlayerMenu::retranslateUi() +{ + aViewGraveyard->setText(tr("&View graveyard")); + aViewRfg->setText(tr("&View exile")); + + playerMenu->setTitle(tr("Player \"%1\"").arg(player->getPlayerInfo()->getName())); + graveMenu->setTitle(tr("&Graveyard")); + rfgMenu->setTitle(tr("&Exile")); + + if (player->getPlayerInfo()->getLocalOrJudge()) { + moveHandMenu->setTitle(tr("&Move hand to...")); + aMoveHandToTopLibrary->setText(tr("&Top of library")); + aMoveHandToBottomLibrary->setText(tr("&Bottom of library")); + aMoveHandToGrave->setText(tr("&Graveyard")); + aMoveHandToRfg->setText(tr("&Exile")); + + moveGraveMenu->setTitle(tr("&Move graveyard to...")); + aMoveGraveToTopLibrary->setText(tr("&Top of library")); + aMoveGraveToBottomLibrary->setText(tr("&Bottom of library")); + aMoveGraveToHand->setText(tr("&Hand")); + aMoveGraveToRfg->setText(tr("&Exile")); + + moveRfgMenu->setTitle(tr("&Move exile to...")); + aMoveRfgToTopLibrary->setText(tr("&Top of library")); + aMoveRfgToBottomLibrary->setText(tr("&Bottom of library")); + aMoveRfgToHand->setText(tr("&Hand")); + aMoveRfgToGrave->setText(tr("&Graveyard")); + + aViewLibrary->setText(tr("&View library")); + aViewHand->setText(tr("&View hand")); + aSortHand->setText(tr("&Sort hand")); + aViewTopCards->setText(tr("View &top cards of library...")); + aViewBottomCards->setText(tr("View bottom cards of library...")); + mRevealLibrary->setTitle(tr("Reveal &library to...")); + mLendLibrary->setTitle(tr("Lend library to...")); + mRevealTopCard->setTitle(tr("Reveal &top cards to...")); + topLibraryMenu->setTitle(tr("&Top of library...")); + bottomLibraryMenu->setTitle(tr("&Bottom of library...")); + aAlwaysRevealTopCard->setText(tr("&Always reveal top card")); + aAlwaysLookAtTopCard->setText(tr("&Always look at top card")); + aOpenDeckInDeckEditor->setText(tr("&Open deck in deck editor")); + aViewSideboard->setText(tr("&View sideboard")); + aDrawCard->setText(tr("&Draw card")); + aDrawCards->setText(tr("D&raw cards...")); + aUndoDraw->setText(tr("&Undo last draw")); + aMulligan->setText(tr("Take &mulligan")); + + aShuffle->setText(tr("Shuffle")); + + aMoveTopToPlay->setText(tr("&Play top card")); + aMoveTopToPlayFaceDown->setText(tr("Play top card &face down")); + aMoveTopCardToBottom->setText(tr("Put top card on &bottom")); + aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard")); + aMoveTopCardToExile->setText(tr("Move top card to e&xile")); + aMoveTopCardsToGraveyard->setText(tr("Move top cards to &graveyard...")); + aMoveTopCardsToExile->setText(tr("Move top cards to &exile...")); + aMoveTopCardsUntil->setText(tr("Put top cards on stack &until...")); + aShuffleTopCards->setText(tr("Shuffle top cards...")); + + aDrawBottomCard->setText(tr("&Draw bottom card")); + aDrawBottomCards->setText(tr("D&raw bottom cards...")); + aMoveBottomToPlay->setText(tr("&Play bottom card")); + aMoveBottomToPlayFaceDown->setText(tr("Play bottom card &face down")); + aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard")); + aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile")); + aMoveBottomCardsToGraveyard->setText(tr("Move bottom cards to &graveyard...")); + aMoveBottomCardsToExile->setText(tr("Move bottom cards to &exile...")); + aMoveBottomCardToTop->setText(tr("Put bottom card on &top")); + aShuffleBottomCards->setText(tr("Shuffle bottom cards...")); + + handMenu->setTitle(tr("&Hand")); + mRevealHand->setTitle(tr("&Reveal hand to...")); + mRevealRandomHandCard->setTitle(tr("Reveal r&andom card to...")); + mRevealRandomGraveyardCard->setTitle(tr("Reveal random card to...")); + sbMenu->setTitle(tr("&Sideboard")); + libraryMenu->setTitle(tr("&Library")); + countersMenu->setTitle(tr("&Counters")); + mCustomZones->setTitle(tr("C&ustom Zones")); + + for (auto aViewZone : mCustomZones->actions()) { + aViewZone->setText(tr("View custom zone '%1'").arg(aViewZone->data().toString())); + } + + aIncrementAllCardCounters->setText(tr("Increment all card counters")); + aUntapAll->setText(tr("&Untap all permanents")); + aRollDie->setText(tr("R&oll die...")); + aCreateToken->setText(tr("&Create token...")); + aCreateAnotherToken->setText(tr("C&reate another token")); + createPredefinedTokenMenu->setTitle(tr("Cr&eate predefined token")); + + mCardCounters->setTitle(tr("Ca&rd counters")); + + for (auto &allPlayersAction : allPlayersActions) { + allPlayersAction->setText(tr("&All players")); + } + } + + if (player->getPlayerInfo()->getLocal()) { + sayMenu->setTitle(tr("S&ay")); + } + + aSelectAll->setText(tr("&Select All")); + aSelectRow->setText(tr("S&elect Row")); + aSelectColumn->setText(tr("S&elect Column")); + + aPlay->setText(tr("&Play")); + aHide->setText(tr("&Hide")); + aPlayFacedown->setText(tr("Play &Face Down")); + //: Turn sideways or back again + aTap->setText(tr("&Tap / Untap")); + aDoesntUntap->setText(tr("Toggle &normal untapping")); + //: Turn face up/face down + aFlip->setText(tr("T&urn Over")); // Only the user facing names in client got renamed to "turn over" + // All code and proto bits are still unchanged (flip) for compatibility reasons + // A protocol rewrite with v3 could incorporate that, see #3100 + aPeek->setText(tr("&Peek at card face")); + aClone->setText(tr("&Clone")); + aAttach->setText(tr("Attac&h to card...")); + aUnattach->setText(tr("Unattac&h")); + aDrawArrow->setText(tr("&Draw arrow...")); + aIncP->setText(tr("&Increase power")); + aDecP->setText(tr("&Decrease power")); + aIncT->setText(tr("I&ncrease toughness")); + aDecT->setText(tr("D&ecrease toughness")); + aIncPT->setText(tr("In&crease power and toughness")); + aDecPT->setText(tr("Dec&rease power and toughness")); + aFlowP->setText(tr("Increase power and decrease toughness")); + aFlowT->setText(tr("Decrease power and increase toughness")); + aSetPT->setText(tr("Set &power and toughness...")); + aResetPT->setText(tr("Reset p&ower and toughness")); + aSetAnnotation->setText(tr("&Set annotation...")); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + + for (int i = 0; i < aAddCounter.size(); ++i) { + aAddCounter[i]->setText(tr("&Add counter (%1)").arg(cardCounterSettings.displayName(i))); + } + for (int i = 0; i < aRemoveCounter.size(); ++i) { + aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(cardCounterSettings.displayName(i))); + } + for (int i = 0; i < aSetCounter.size(); ++i) { + aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(cardCounterSettings.displayName(i))); + } + + aMoveToTopLibrary->setText(tr("&Top of library in random order")); + aMoveToXfromTopOfLibrary->setText(tr("X cards from the top of library...")); + aMoveToBottomLibrary->setText(tr("&Bottom of library in random order")); + aMoveToHand->setText(tr("&Hand")); + aMoveToGraveyard->setText(tr("&Graveyard")); + aMoveToExile->setText(tr("&Exile")); +} + +void PlayerMenu::setShortcutIfItExists(QAction *action, ShortcutKey shortcut) +{ + if (action) { + action->setShortcuts(shortcut); + } +} + +void PlayerMenu::clearShortcutIfItExists(QAction *action) +{ + if (action) { + action->setShortcut(QKeySequence()); + } +} + +void PlayerMenu::setShortcutsActive() +{ + shortcutsActive = true; + ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); + + setShortcutIfItExists(aHide, shortcuts.getShortcut("Player/aHide")); + setShortcutIfItExists(aPlay, shortcuts.getShortcut("Player/aPlay")); + setShortcutIfItExists(aTap, shortcuts.getShortcut("Player/aTap")); + setShortcutIfItExists(aDoesntUntap, shortcuts.getShortcut("Player/aDoesntUntap")); + setShortcutIfItExists(aFlip, shortcuts.getShortcut("Player/aFlip")); + setShortcutIfItExists(aPeek, shortcuts.getShortcut("Player/aPeek")); + setShortcutIfItExists(aClone, shortcuts.getShortcut("Player/aClone")); + setShortcutIfItExists(aAttach, shortcuts.getShortcut("Player/aAttach")); + setShortcutIfItExists(aUnattach, shortcuts.getShortcut("Player/aUnattach")); + setShortcutIfItExists(aDrawArrow, shortcuts.getShortcut("Player/aDrawArrow")); + setShortcutIfItExists(aIncP, shortcuts.getShortcut("Player/aIncP")); + setShortcutIfItExists(aDecP, shortcuts.getShortcut("Player/aDecP")); + setShortcutIfItExists(aIncT, shortcuts.getShortcut("Player/aIncT")); + setShortcutIfItExists(aDecT, shortcuts.getShortcut("Player/aDecT")); + setShortcutIfItExists(aIncPT, shortcuts.getShortcut("Player/aIncPT")); + setShortcutIfItExists(aDecPT, shortcuts.getShortcut("Player/aDecPT")); + setShortcutIfItExists(aFlowP, shortcuts.getShortcut("Player/aFlowP")); + setShortcutIfItExists(aFlowT, shortcuts.getShortcut("Player/aFlowT")); + setShortcutIfItExists(aSetPT, shortcuts.getShortcut("Player/aSetPT")); + setShortcutIfItExists(aResetPT, shortcuts.getShortcut("Player/aResetPT")); + setShortcutIfItExists(aSetAnnotation, shortcuts.getShortcut("Player/aSetAnnotation")); + setShortcutIfItExists(aMoveToTopLibrary, shortcuts.getShortcut("Player/aMoveToTopLibrary")); + setShortcutIfItExists(aMoveToBottomLibrary, shortcuts.getShortcut("Player/aMoveToBottomLibrary")); + setShortcutIfItExists(aMoveToHand, shortcuts.getShortcut("Player/aMoveToHand")); + setShortcutIfItExists(aMoveToGraveyard, shortcuts.getShortcut("Player/aMoveToGraveyard")); + setShortcutIfItExists(aMoveToExile, shortcuts.getShortcut("Player/aMoveToExile")); + setShortcutIfItExists(aSortHand, shortcuts.getShortcut("Player/aSortHand")); + + setShortcutIfItExists(aSelectAll, shortcuts.getShortcut("Player/aSelectAll")); + setShortcutIfItExists(aSelectRow, shortcuts.getShortcut("Player/aSelectRow")); + setShortcutIfItExists(aSelectColumn, shortcuts.getShortcut("Player/aSelectColumn")); + + static const QStringList colorWords = {"Red", "Yellow", "Green", "Cyan", "Purple", "Magenta"}; + for (int i = 0; i < aAddCounter.size(); i++) { + setShortcutIfItExists(aAddCounter[i], shortcuts.getShortcut("Player/aCC" + colorWords[i])); + setShortcutIfItExists(aRemoveCounter[i], shortcuts.getShortcut("Player/aRC" + colorWords[i])); + setShortcutIfItExists(aSetCounter[i], shortcuts.getShortcut("Player/aSC" + colorWords[i])); + } + + QMapIterator counterIterator(player->getCounters()); + while (counterIterator.hasNext()) { + counterIterator.next().value()->setShortcutsActive(); + } + + setShortcutIfItExists(aIncrementAllCardCounters, shortcuts.getShortcut("Player/aIncrementAllCardCounters")); + setShortcutIfItExists(aViewSideboard, shortcuts.getShortcut("Player/aViewSideboard")); + setShortcutIfItExists(aViewLibrary, shortcuts.getShortcut("Player/aViewLibrary")); + setShortcutIfItExists(aViewHand, shortcuts.getShortcut("Player/aViewHand")); + setShortcutIfItExists(aViewTopCards, shortcuts.getShortcut("Player/aViewTopCards")); + setShortcutIfItExists(aViewBottomCards, shortcuts.getShortcut("Player/aViewBottomCards")); + setShortcutIfItExists(aViewGraveyard, shortcuts.getShortcut("Player/aViewGraveyard")); + setShortcutIfItExists(aDrawCard, shortcuts.getShortcut("Player/aDrawCard")); + setShortcutIfItExists(aDrawCards, shortcuts.getShortcut("Player/aDrawCards")); + setShortcutIfItExists(aUndoDraw, shortcuts.getShortcut("Player/aUndoDraw")); + setShortcutIfItExists(aMulligan, shortcuts.getShortcut("Player/aMulligan")); + setShortcutIfItExists(aShuffle, shortcuts.getShortcut("Player/aShuffle")); + setShortcutIfItExists(aShuffleTopCards, shortcuts.getShortcut("Player/aShuffleTopCards")); + setShortcutIfItExists(aShuffleBottomCards, shortcuts.getShortcut("Player/aShuffleBottomCards")); + setShortcutIfItExists(aUntapAll, shortcuts.getShortcut("Player/aUntapAll")); + setShortcutIfItExists(aRollDie, shortcuts.getShortcut("Player/aRollDie")); + setShortcutIfItExists(aCreateToken, shortcuts.getShortcut("Player/aCreateToken")); + setShortcutIfItExists(aCreateAnotherToken, shortcuts.getShortcut("Player/aCreateAnotherToken")); + setShortcutIfItExists(aAlwaysRevealTopCard, shortcuts.getShortcut("Player/aAlwaysRevealTopCard")); + setShortcutIfItExists(aAlwaysLookAtTopCard, shortcuts.getShortcut("Player/aAlwaysLookAtTopCard")); + setShortcutIfItExists(aMoveTopToPlay, shortcuts.getShortcut("Player/aMoveTopToPlay")); + setShortcutIfItExists(aMoveTopToPlayFaceDown, shortcuts.getShortcut("Player/aMoveTopToPlayFaceDown")); + setShortcutIfItExists(aMoveTopCardToGraveyard, shortcuts.getShortcut("Player/aMoveTopCardToGraveyard")); + setShortcutIfItExists(aMoveTopCardsToGraveyard, shortcuts.getShortcut("Player/aMoveTopCardsToGraveyard")); + setShortcutIfItExists(aMoveTopCardToExile, shortcuts.getShortcut("Player/aMoveTopCardToExile")); + setShortcutIfItExists(aMoveTopCardsToExile, shortcuts.getShortcut("Player/aMoveTopCardsToExile")); + setShortcutIfItExists(aMoveTopCardsUntil, shortcuts.getShortcut("Player/aMoveTopCardsUntil")); + setShortcutIfItExists(aMoveTopCardToBottom, shortcuts.getShortcut("Player/aMoveTopCardToBottom")); + setShortcutIfItExists(aDrawBottomCard, shortcuts.getShortcut("Player/aDrawBottomCard")); + setShortcutIfItExists(aDrawBottomCards, shortcuts.getShortcut("Player/aDrawBottomCards")); + setShortcutIfItExists(aMoveBottomToPlay, shortcuts.getShortcut("Player/aMoveBottomToPlay")); + setShortcutIfItExists(aMoveBottomToPlayFaceDown, shortcuts.getShortcut("Player/aMoveBottomToPlayFaceDown")); + setShortcutIfItExists(aMoveBottomCardToGraveyard, shortcuts.getShortcut("Player/aMoveBottomCardToGrave")); + setShortcutIfItExists(aMoveBottomCardsToGraveyard, shortcuts.getShortcut("Player/aMoveBottomCardsToGrave")); + setShortcutIfItExists(aMoveBottomCardToExile, shortcuts.getShortcut("Player/aMoveBottomCardToExile")); + setShortcutIfItExists(aMoveBottomCardsToExile, shortcuts.getShortcut("Player/aMoveBottomCardsToExile")); + setShortcutIfItExists(aMoveBottomCardToTop, shortcuts.getShortcut("Player/aMoveBottomCardToTop")); + setShortcutIfItExists(aPlayFacedown, shortcuts.getShortcut("Player/aPlayFacedown")); + setShortcutIfItExists(aPlay, shortcuts.getShortcut("Player/aPlay")); + + // Don't enable always-active shortcuts in local games, since it causes keyboard shortcuts to work inconsistently + // when there are more than 1 player. + if (!player->getGame()->getGameState()->getIsLocalGame()) { + // unattach action is only active in card menu if the active card is attached. + // make unattach shortcut always active so that it consistently works when multiple cards are selected. + player->getGame()->getTab()->addAction(aUnattach); + } +} + +void PlayerMenu::setShortcutsInactive() +{ + shortcutsActive = false; + + clearShortcutIfItExists(aViewSideboard); + clearShortcutIfItExists(aViewLibrary); + clearShortcutIfItExists(aViewHand); + clearShortcutIfItExists(aViewTopCards); + clearShortcutIfItExists(aViewBottomCards); + clearShortcutIfItExists(aViewGraveyard); + clearShortcutIfItExists(aDrawCard); + clearShortcutIfItExists(aDrawCards); + clearShortcutIfItExists(aUndoDraw); + clearShortcutIfItExists(aMulligan); + clearShortcutIfItExists(aShuffle); + clearShortcutIfItExists(aShuffleTopCards); + clearShortcutIfItExists(aShuffleBottomCards); + clearShortcutIfItExists(aUntapAll); + clearShortcutIfItExists(aRollDie); + clearShortcutIfItExists(aCreateToken); + clearShortcutIfItExists(aCreateAnotherToken); + clearShortcutIfItExists(aAlwaysRevealTopCard); + clearShortcutIfItExists(aAlwaysLookAtTopCard); + clearShortcutIfItExists(aMoveTopToPlay); + clearShortcutIfItExists(aMoveTopToPlayFaceDown); + clearShortcutIfItExists(aMoveTopCardToGraveyard); + clearShortcutIfItExists(aMoveTopCardsToGraveyard); + clearShortcutIfItExists(aMoveTopCardToExile); + clearShortcutIfItExists(aMoveTopCardsToExile); + clearShortcutIfItExists(aMoveTopCardsUntil); + clearShortcutIfItExists(aDrawBottomCard); + clearShortcutIfItExists(aDrawBottomCards); + clearShortcutIfItExists(aMoveBottomToPlay); + clearShortcutIfItExists(aMoveBottomToPlayFaceDown); + clearShortcutIfItExists(aMoveBottomCardToGraveyard); + clearShortcutIfItExists(aMoveBottomCardsToGraveyard); + clearShortcutIfItExists(aMoveBottomCardToExile); + clearShortcutIfItExists(aMoveBottomCardsToExile); + clearShortcutIfItExists(aIncrementAllCardCounters); + clearShortcutIfItExists(aSortHand); + + QMapIterator counterIterator(player->getCounters()); + while (counterIterator.hasNext()) { + counterIterator.next().value()->setShortcutsInactive(); + } +} + +void PlayerMenu::initSayMenu() +{ + sayMenu->clear(); + + int count = SettingsCache::instance().messages().getCount(); + sayMenu->setEnabled(count > 0); + + for (int i = 0; i < count; ++i) { + auto *newAction = new QAction(SettingsCache::instance().messages().getMessageAt(i), sayMenu); + if (i < 10) { + newAction->setShortcut(QKeySequence("Ctrl+" + QString::number((i + 1) % 10))); + } + connect(newAction, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actSayMessage); + sayMenu->addAction(newAction); + } +} diff --git a/cockatrice/src/game/player/player_menu.h b/cockatrice/src/game/player/player_menu.h new file mode 100644 index 000000000..644fbc76e --- /dev/null +++ b/cockatrice/src/game/player/player_menu.h @@ -0,0 +1,131 @@ +#ifndef COCKATRICE_PLAYER_MENU_H +#define COCKATRICE_PLAYER_MENU_H + +#include "../../client/tearoff_menu.h" +#include "player.h" + +#include +#include + +class PlayerMenu : public QObject +{ + Q_OBJECT + +signals: + void cardMenuUpdated(QMenu *cardMenu); + +public slots: + QMenu *createPtMenu() const; + QMenu *createMoveMenu() const; + void enableOpenInDeckEditorAction() const; + void populatePredefinedTokensMenu(); + void setMenusForGraphicItems(); + +private slots: + void addPlayer(Player *playerToAdd); + void removePlayer(Player *playerToRemove); + void playerListActionTriggered(); + void refreshShortcuts(); + void clearCustomZonesMenu(); + void addViewCustomZoneActionToCustomZoneMenu(QString zoneName); + void resetTopCardMenuActions(); + +public: + PlayerMenu(Player *player); + void createDrawActions(); + void createShuffleActions(); + void createMoveActions(); + void createViewActions(); + void retranslateUi(); + + void addPlayerToList(QMenu *playerList, Player *playerToAdd); + static void removePlayerFromList(QMenu *playerList, Player *player); + + QMenu *updateCardMenu(const CardItem *card); + + [[nodiscard]] bool createAnotherTokenActionExists() const + { + return aCreateAnotherToken != nullptr; + } + + void setAndEnableCreateAnotherTokenAction(QString text) + { + aCreateAnotherToken->setText(text); + aCreateAnotherToken->setEnabled(true); + } + + QStringList getPredefinedTokens() const + { + return predefinedTokens; + } + + [[nodiscard]] bool isAlwaysRevealTopCardChecked() + { + return aAlwaysRevealTopCard->isChecked(); + } + + [[nodiscard]] bool isAlwaysLookAtTopCardChecked() + { + return aAlwaysLookAtTopCard->isChecked(); + } + + [[nodiscard]] QMenu *getPlayerMenu() const + { + return playerMenu; + } + + [[nodiscard]] QMenu *getCountersMenu() + { + return countersMenu; + } + + bool getShortcutsActive() const + { + return shortcutsActive; + } + + void setShortcutsActive(); + void setShortcutIfItExists(QAction *action, ShortcutKey shortcut); + void clearShortcutIfItExists(QAction *action); + void setShortcutsInactive(); + +private: + Player *player; + QMenu *sbMenu, *countersMenu, *sayMenu, *createPredefinedTokenMenu, *mRevealLibrary, *mLendLibrary, *mRevealTopCard, + *mRevealHand, *mRevealRandomHandCard, *mRevealRandomGraveyardCard, *mCustomZones, *mCardCounters; + TearOffMenu *moveGraveMenu, *moveRfgMenu, *graveMenu, *moveHandMenu, *handMenu, *libraryMenu, *topLibraryMenu, + *bottomLibraryMenu, *rfgMenu, *playerMenu; + QList playerLists; + QList singlePlayerLists; + QList allPlayersActions; + QList> playersInfo; + QAction *aMoveHandToTopLibrary, *aMoveHandToBottomLibrary, *aMoveHandToGrave, *aMoveHandToRfg, + *aMoveGraveToTopLibrary, *aMoveGraveToBottomLibrary, *aMoveGraveToHand, *aMoveGraveToRfg, *aMoveRfgToTopLibrary, + *aMoveRfgToBottomLibrary, *aMoveRfgToHand, *aMoveRfgToGrave, *aViewHand, *aViewLibrary, *aViewTopCards, + *aViewBottomCards, *aAlwaysRevealTopCard, *aAlwaysLookAtTopCard, *aOpenDeckInDeckEditor, + *aMoveTopCardToGraveyard, *aMoveTopCardToExile, *aMoveTopCardsToGraveyard, *aMoveTopCardsToExile, + *aMoveTopCardsUntil, *aMoveTopCardToBottom, *aViewGraveyard, *aViewRfg, *aViewSideboard, *aDrawCard, + *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aShuffleTopCards, *aShuffleBottomCards, *aMoveTopToPlay, + *aMoveTopToPlayFaceDown, *aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken, *aMoveBottomToPlay, + *aMoveBottomToPlayFaceDown, *aMoveBottomCardToTop, *aMoveBottomCardToGraveyard, *aMoveBottomCardToExile, + *aMoveBottomCardsToGraveyard, *aMoveBottomCardsToExile, *aDrawBottomCard, *aDrawBottomCards; + + QList aAddCounter, aSetCounter, aRemoveCounter; + QAction *aPlay, *aPlayFacedown, *aHide, *aTap, *aDoesntUntap, *aAttach, *aUnattach, *aDrawArrow, *aSetPT, *aResetPT, + *aIncP, *aDecP, *aIncT, *aDecT, *aIncPT, *aDecPT, *aFlowP, *aFlowT, *aSetAnnotation, *aFlip, *aPeek, *aClone, + *aMoveToTopLibrary, *aMoveToBottomLibrary, *aMoveToHand, *aMoveToGraveyard, *aMoveToExile, + *aMoveToXfromTopOfLibrary, *aSelectAll, *aSelectRow, *aSelectColumn, *aSortHand, *aIncrementAllCardCounters; + + bool shortcutsActive; + QStringList predefinedTokens; + + QMenu *createCardMenu(const CardItem *card); + + void addRelatedCardActions(const CardItem *card, QMenu *cardMenu); + void addRelatedCardView(const CardItem *card, QMenu *cardMenu); + + void initSayMenu(); + void initContextualPlayersMenu(QMenu *menu); +}; + +#endif // COCKATRICE_PLAYER_MENU_H diff --git a/cockatrice/src/game/player/player_target.cpp b/cockatrice/src/game/player/player_target.cpp index becf605ab..ffc934822 100644 --- a/cockatrice/src/game/player/player_target.cpp +++ b/cockatrice/src/game/player/player_target.cpp @@ -9,13 +9,8 @@ #include #include -PlayerCounter::PlayerCounter(Player *_player, - int _id, - const QString &_name, - int _value, - QGraphicsItem *parent, - QWidget *game) - : AbstractCounter(_player, _id, _name, false, _value, false, parent, game) +PlayerCounter::PlayerCounter(Player *_player, int _id, const QString &_name, int _value, QGraphicsItem *parent) + : AbstractCounter(_player, _id, _name, false, _value, false, parent) { } @@ -52,12 +47,12 @@ void PlayerCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /* painter->drawText(translatedRect, Qt::AlignCenter, QString::number(value)); } -PlayerTarget::PlayerTarget(Player *_owner, QGraphicsItem *parentItem, QWidget *_game) - : ArrowTarget(_owner, parentItem), playerCounter(nullptr), game(_game) +PlayerTarget::PlayerTarget(Player *_owner, QGraphicsItem *parentItem) + : ArrowTarget(_owner, parentItem), playerCounter(nullptr) { setCacheMode(DeviceCoordinateCache); - const std::string &bmp = _owner->getUserInfo()->avatar_bmp(); + const std::string &bmp = _owner->getPlayerInfo()->getUserInfo()->avatar_bmp(); if (!fullPixmap.loadFromData((const uchar *)bmp.data(), static_cast(bmp.size()))) { fullPixmap = QPixmap(); } @@ -77,7 +72,7 @@ QRectF PlayerTarget::boundingRect() const void PlayerTarget::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - const ServerInfo_User *const info = owner->getUserInfo(); + const ServerInfo_User *const info = owner->getPlayerInfo()->getUserInfo(); const qreal border = 2; @@ -160,7 +155,7 @@ AbstractCounter *PlayerTarget::addCounter(int _counterId, const QString &_name, playerCounter->delCounter(); } - playerCounter = new PlayerCounter(owner, _counterId, _name, _value, this, game); + playerCounter = new PlayerCounter(owner, _counterId, _name, _value, this); playerCounter->setPos(boundingRect().width() - playerCounter->boundingRect().width(), boundingRect().height() - playerCounter->boundingRect().height()); connect(playerCounter, &PlayerCounter::destroyed, this, &PlayerTarget::counterDeleted); diff --git a/cockatrice/src/game/player/player_target.h b/cockatrice/src/game/player/player_target.h index f02f3bcea..7c6f61020 100644 --- a/cockatrice/src/game/player/player_target.h +++ b/cockatrice/src/game/player/player_target.h @@ -3,6 +3,7 @@ #include "../board/abstract_counter.h" #include "../board/arrow_target.h" +#include "../board/graphics_item_type.h" #include #include @@ -13,12 +14,7 @@ class PlayerCounter : public AbstractCounter { Q_OBJECT public: - PlayerCounter(Player *_player, - int _id, - const QString &_name, - int _value, - QGraphicsItem *parent = nullptr, - QWidget *game = nullptr); + PlayerCounter(Player *_player, int _id, const QString &_name, int _value, QGraphicsItem *parent = nullptr); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; }; @@ -29,7 +25,6 @@ class PlayerTarget : public ArrowTarget private: QPixmap fullPixmap; PlayerCounter *playerCounter; - QWidget *game; public slots: void counterDeleted(); @@ -43,7 +38,7 @@ public: return Type; } - explicit PlayerTarget(Player *_player = nullptr, QGraphicsItem *parentItem = nullptr, QWidget *_game = nullptr); + explicit PlayerTarget(Player *_player = nullptr, QGraphicsItem *parentItem = nullptr); ~PlayerTarget() override; QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; diff --git a/cockatrice/src/game/replay.cpp b/cockatrice/src/game/replay.cpp new file mode 100644 index 000000000..e52ed8a7c --- /dev/null +++ b/cockatrice/src/game/replay.cpp @@ -0,0 +1,11 @@ +#include "replay.h" + +#include "../client/tabs/tab_game.h" + +Replay::Replay(TabGame *_tab, GameReplay *_replay) : AbstractGame(_tab) +{ + gameState = new GameState(this, 0, -1, tab->getTabSupervisor()->getIsLocalGame(), {}, false, false, -1, false); + connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); + playerManager = new PlayerManager(this, -1, false, true); + loadReplay(_replay); +} diff --git a/cockatrice/src/game/replay.h b/cockatrice/src/game/replay.h new file mode 100644 index 000000000..bfad6731b --- /dev/null +++ b/cockatrice/src/game/replay.h @@ -0,0 +1,14 @@ +#ifndef COCKATRICE_REPLAY_H +#define COCKATRICE_REPLAY_H + +#include "abstract_game.h" + +class Replay : public AbstractGame +{ + Q_OBJECT + +public: + explicit Replay(TabGame *_tab, GameReplay *_replay); +}; + +#endif // COCKATRICE_REPLAY_H diff --git a/cockatrice/src/game/zones/card_zone.cpp b/cockatrice/src/game/zones/card_zone.cpp index 43cbc16c0..0c189cd2b 100644 --- a/cockatrice/src/game/zones/card_zone.cpp +++ b/cockatrice/src/game/zones/card_zone.cpp @@ -1,102 +1,31 @@ #include "card_zone.h" #include "../board/card_item.h" -#include "../cards/card_database_manager.h" -#include "../player/player.h" -#include "pb/command_move_card.pb.h" -#include "pb/serverinfo_user.pb.h" -#include "pile_zone.h" #include "view_zone.h" -#include -#include #include #include -/** - * @param _p the player that the zone belongs to - * @param _name internal name of the zone - * @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it - * @param _contentsKnown whether the cards in the zone are known to the client - * @param parent the parent graphics object. - */ -CardZone::CardZone(Player *_p, - const QString &_name, - bool _hasCardAttr, - bool _isShufflable, - bool _contentsKnown, - QGraphicsItem *parent) - : AbstractGraphicsItem(parent), player(_p), name(_name), cards(_contentsKnown), views{}, menu(nullptr), - doubleClickAction(0), hasCardAttr(_hasCardAttr), isShufflable(_isShufflable) +CardZone::CardZone(CardZoneLogic *_logic, QGraphicsItem *parent) + : AbstractGraphicsItem(parent), menu(nullptr), doubleClickAction(0), logic(_logic) { - // If we join a game before the card db finishes loading, the cards might have the wrong printings. - // Force refresh all cards in the zone when db finishes loading to fix that. - connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this, - &CardZone::refreshCardInfos); + connect(logic, &CardZoneLogic::retranslateUi, this, &CardZone::retranslateUi); + connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded); + connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); }); + connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); }); + connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards); +} + +void CardZone::onCardAdded(CardItem *addedCard) +{ + addedCard->setParentItem(this); + addedCard->update(); } void CardZone::retranslateUi() { - for (int i = 0; i < cards.size(); ++i) - cards[i]->retranslateUi(); -} - -void CardZone::clearContents() -{ - for (int i = 0; i < cards.size(); i++) { - // If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a - // player, we have to return them to avoid a crash. - const QList &attachedCards = cards[i]->getAttachedCards(); - for (auto attachedCard : attachedCards) - attachedCard->setParentItem(attachedCard->getZone()); - - player->deleteCard(cards.at(i)); - } - cards.clear(); - emit cardCountChanged(); -} - -QString CardZone::getTranslatedName(bool theirOwn, GrammaticalCase gc) const -{ - QString ownerName = player->getName(); - if (name == "hand") - return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName)); - else if (name == "deck") - switch (gc) { - case CaseLookAtZone: - return (theirOwn ? tr("their library", "look at zone") - : tr("%1's library", "look at zone").arg(ownerName)); - case CaseTopCardsOfZone: - return (theirOwn ? tr("of their library", "top cards of zone,") - : tr("of %1's library", "top cards of zone").arg(ownerName)); - case CaseRevealZone: - return (theirOwn ? tr("their library", "reveal zone") - : tr("%1's library", "reveal zone").arg(ownerName)); - case CaseShuffleZone: - return (theirOwn ? tr("their library", "shuffle") : tr("%1's library", "shuffle").arg(ownerName)); - default: - return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName)); - } - else if (name == "grave") - return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName)); - else if (name == "rfg") - return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName)); - else if (name == "sb") - switch (gc) { - case CaseLookAtZone: - return (theirOwn ? tr("their sideboard", "look at zone") - : tr("%1's sideboard", "look at zone").arg(ownerName)); - case CaseNominative: - return (theirOwn ? tr("their sideboard", "nominative") - : tr("%1's sideboard", "nominative").arg(ownerName)); - default: - break; - } - else { - return (theirOwn ? tr("their custom zone '%1'", "nominative").arg(name) - : tr("%1's custom zone '%2'", "nominative").arg(ownerName).arg(name)); - } - return QString(); + for (int i = 0; i < getLogic()->getCards().size(); ++i) + getLogic()->getCards()[i]->retranslateUi(); } void CardZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * /*event*/) @@ -114,13 +43,6 @@ bool CardZone::showContextMenu(const QPoint &screenPos) return false; } -void CardZone::refreshCardInfos() -{ - for (const auto &cardItem : cards) { - cardItem->refreshCardInfo(); - } -} - void CardZone::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::RightButton) { @@ -132,104 +54,6 @@ void CardZone::mousePressEvent(QGraphicsSceneMouseEvent *event) event->ignore(); } -void CardZone::addCard(CardItem *card, const bool reorganize, const int x, const int y) -{ - if (!card) { - qCWarning(CardZoneLog) << "CardZone::addCard() card is null; this shouldn't normally happen"; - return; - } - - for (auto *view : views) { - if (view->prepareAddCard(x)) { - view->addCard(new CardItem(player, nullptr, card->getCardRef(), card->getId()), reorganize, x, y); - } - } - - card->setZone(this); - addCardImpl(card, x, y); - - if (reorganize) - reorganizeCards(); - - emit cardCountChanged(); -} - -CardItem *CardZone::getCard(int cardId) -{ - CardItem *c = cards.findCard(cardId); - if (!c) { - qCWarning(CardZoneLog) << "CardZone::getCard: card id=" << cardId << "not found"; - return nullptr; - } - // If the card's id is -1, this zone is invisible, - // so we need to give the card an id as it comes out. - // It can be assumed that in an invisible zone, all cards are equal. - if (c->getId() == -1) { - c->setId(cardId); - } - return c; -} - -CardItem *CardZone::takeCard(int position, int cardId, bool toNewZone) -{ - if (position == -1) { - // position == -1 means either that the zone is indexed by card id - // or that it doesn't matter which card you take. - for (int i = 0; i < cards.size(); ++i) - if (cards[i]->getId() == cardId) { - position = i; - break; - } - if (position == -1) - position = 0; - } - if (position >= cards.size()) - return nullptr; - - for (auto *view : views) { - view->removeCard(position, toNewZone); - } - - CardItem *c = cards.takeAt(position); - - c->setId(cardId); - - reorganizeCards(); - emit cardCountChanged(); - return c; -} - -void CardZone::removeCard(CardItem *card) -{ - if (!card) { - qCWarning(CardZoneLog) << "CardZone::removeCard: card is null, this shouldn't normally happen"; - return; - } - - cards.removeOne(card); - reorganizeCards(); - emit cardCountChanged(); - player->deleteCard(card); -} - -void CardZone::moveAllToZone() -{ - QList data = static_cast(sender())->data().toList(); - QString targetZone = data[0].toString(); - int targetX = data[1].toInt(); - - Command_MoveCard cmd; - cmd.set_start_zone(getName().toStdString()); - cmd.set_target_player_id(player->getId()); - cmd.set_target_zone(targetZone.toStdString()); - cmd.set_x(targetX); - - for (int i = 0; i < cards.size(); ++i) - cmd.mutable_cards_to_move()->add_card()->set_card_id(cards[i]->getId()); - - player->sendGameCommand(cmd); -} - QPointF CardZone::closestGridPoint(const QPointF &point) { return point; diff --git a/cockatrice/src/game/zones/card_zone.h b/cockatrice/src/game/zones/card_zone.h index 08a2e1fa5..1034c4668 100644 --- a/cockatrice/src/game/zones/card_zone.h +++ b/cockatrice/src/game/zones/card_zone.h @@ -1,22 +1,15 @@ #ifndef CARDZONE_H #define CARDZONE_H -#include "../../client/translation.h" #include "../board/abstract_graphics_item.h" -#include "../board/card_list.h" +#include "../board/graphics_item_type.h" +#include "logic/card_zone_logic.h" #include #include inline Q_LOGGING_CATEGORY(CardZoneLog, "card_zone"); -class Player; -class ZoneViewZone; -class QMenu; -class QAction; -class QPainter; -class CardDragItem; - /** * A zone in the game that can contain cards. * This class contains methods to get and modify the cards that are contained inside this zone. @@ -27,26 +20,21 @@ class CardZone : public AbstractGraphicsItem { Q_OBJECT protected: - Player *player; - QString name; - CardList cards; - QList views; QMenu *menu; QAction *doubleClickAction; - bool hasCardAttr; - bool isShufflable; - bool alwaysRevealTopCard; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - virtual void addCardImpl(CardItem *card, int x, int y) = 0; -signals: - void cardCountChanged(); -public slots: - void moveAllToZone(); - bool showContextMenu(const QPoint &screenPos); + virtual void reorganizeCards() = 0; + virtual QPointF closestGridPoint(const QPointF &point); -private slots: - void refreshCardInfos(); + QMenu *getMenu() const + { + return menu; + } +public slots: + bool showContextMenu(const QPoint &screenPos); + void onCardAdded(CardItem *addedCard); public: enum @@ -58,69 +46,23 @@ public: return Type; } virtual void - handleDropEvent(const QList &dragItem, CardZone *startZone, const QPoint &dropPoint) = 0; - CardZone(Player *_player, - const QString &_name, - bool _hasCardAttr, - bool _isShufflable, - bool _contentsKnown, - QGraphicsItem *parent = nullptr); + handleDropEvent(const QList &dragItem, CardZoneLogic *startZone, const QPoint &dropPoint) = 0; + CardZone(CardZoneLogic *logic, QGraphicsItem *parent = nullptr); void retranslateUi(); - void clearContents(); - bool getHasCardAttr() const + + CardZoneLogic *getLogic() const { - return hasCardAttr; - } - bool getIsShufflable() const - { - return isShufflable; - } - QMenu *getMenu() const - { - return menu; + return logic; } + void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0) { menu = _menu; doubleClickAction = _doubleClickAction; } - QString getName() const - { - return name; - } - QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const; - Player *getPlayer() const - { - return player; - } - bool contentsKnown() const - { - return cards.getContentsKnown(); - } - const CardList &getCards() const - { - return cards; - } - void addCard(CardItem *card, bool reorganize, int x, int y = -1); - // getCard() finds a card by id. - CardItem *getCard(int cardId); - // takeCard() finds a card by position and removes it from the zone and from all of its views. - virtual CardItem *takeCard(int position, int cardId, bool canResize = true); - void removeCard(CardItem *card); - QList &getViews() - { - return views; - } - virtual void reorganizeCards() = 0; - virtual QPointF closestGridPoint(const QPointF &point); - bool getAlwaysRevealTopCard() const - { - return alwaysRevealTopCard; - } - void setAlwaysRevealTopCard(bool _alwaysRevealTopCard) - { - alwaysRevealTopCard = _alwaysRevealTopCard; - } + +private: + CardZoneLogic *logic; }; #endif diff --git a/cockatrice/src/game/zones/hand_zone.cpp b/cockatrice/src/game/zones/hand_zone.cpp index 2d6947c9d..298926ff5 100644 --- a/cockatrice/src/game/zones/hand_zone.cpp +++ b/cockatrice/src/game/zones/hand_zone.cpp @@ -9,8 +9,8 @@ #include -HandZone::HandZone(Player *_p, bool _contentsKnown, int _zoneHeight, QGraphicsItem *parent) - : SelectZone(_p, "hand", false, false, _contentsKnown, parent), zoneHeight(_zoneHeight) +HandZone::HandZone(HandZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent) + : SelectZone(_logic, parent), zoneHeight(_zoneHeight) { connect(themeManager, &ThemeManager::themeChanged, this, &HandZone::updateBg); updateBg(); @@ -22,50 +22,34 @@ void HandZone::updateBg() update(); } -void HandZone::addCardImpl(CardItem *card, int x, int /*y*/) -{ - // if x is negative set it to add at end - if (x < 0 || x >= cards.size()) { - x = cards.size(); - } - cards.insert(x, card); - - if (!cards.getContentsKnown()) { - card->setId(-1); - card->setCardRef({}); - } - card->setParentItem(this); - card->resetState(); - card->setVisible(true); - card->update(); -} - -void HandZone::handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) +void HandZone::handleDropEvent(const QList &dragItems, + CardZoneLogic *startZone, + const QPoint &dropPoint) { QPoint point = dropPoint + scenePos().toPoint(); int x = -1; if (SettingsCache::instance().getHorizontalHand()) { - for (x = 0; x < cards.size(); x++) - if (point.x() < static_cast(cards.at(x))->scenePos().x()) + for (x = 0; x < getLogic()->getCards().size(); x++) + if (point.x() < static_cast(getLogic()->getCards().at(x))->scenePos().x()) break; } else { - for (x = 0; x < cards.size(); x++) - if (point.y() < static_cast(cards.at(x))->scenePos().y()) + for (x = 0; x < getLogic()->getCards().size(); x++) + if (point.y() < static_cast(getLogic()->getCards().at(x))->scenePos().y()) break; } Command_MoveCard cmd; - cmd.set_start_player_id(startZone->getPlayer()->getId()); + cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_zone(startZone->getName().toStdString()); - cmd.set_target_player_id(player->getId()); - cmd.set_target_zone(getName().toStdString()); + cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_target_zone(getLogic()->getName().toStdString()); cmd.set_x(x); cmd.set_y(-1); for (int i = 0; i < dragItems.size(); ++i) cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); - player->sendGameCommand(cmd); + getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } QRectF HandZone::boundingRect() const @@ -78,23 +62,23 @@ QRectF HandZone::boundingRect() const void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Hand, player->getZoneId()); + QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Hand, getLogic()->getPlayer()->getZoneId()); painter->fillRect(boundingRect(), brush); } void HandZone::reorganizeCards() { - if (!cards.isEmpty()) { - const int cardCount = cards.size(); + if (!getLogic()->getCards().isEmpty()) { + const int cardCount = getLogic()->getCards().size(); if (SettingsCache::instance().getHorizontalHand()) { bool leftJustified = SettingsCache::instance().getLeftJustified(); - qreal cardWidth = cards.at(0)->boundingRect().width(); + qreal cardWidth = getLogic()->getCards().at(0)->boundingRect().width(); const int xPadding = leftJustified ? cardWidth * 1.4 : 5; qreal totalWidth = leftJustified ? boundingRect().width() - (1 * xPadding) - 5 : boundingRect().width() - 2 * xPadding; for (int i = 0; i < cardCount; i++) { - CardItem *c = cards.at(i); + CardItem *c = getLogic()->getCards().at(i); // If the total width of the cards is smaller than the available width, // the cards do not need to overlap and are displayed in the center of the area. if (cardWidth * cardCount > totalWidth) @@ -109,16 +93,16 @@ void HandZone::reorganizeCards() } } else { qreal totalWidth = boundingRect().width(); - qreal cardWidth = cards.at(0)->boundingRect().width(); + qreal cardWidth = getLogic()->getCards().at(0)->boundingRect().width(); qreal xspace = 5; qreal x1 = xspace; qreal x2 = totalWidth - xspace - cardWidth; for (int i = 0; i < cardCount; i++) { - CardItem *card = cards.at(i); + CardItem *card = getLogic()->getCards().at(i); qreal x = (i % 2) ? x2 : x1; - qreal y = - divideCardSpaceInZone(i, cardCount, boundingRect().height(), cards.at(0)->boundingRect().height()); + qreal y = divideCardSpaceInZone(i, cardCount, boundingRect().height(), + getLogic()->getCards().at(0)->boundingRect().height()); card->setPos(x, y); card->setRealZValue(i); } @@ -129,10 +113,10 @@ void HandZone::reorganizeCards() void HandZone::sortHand() { - if (cards.isEmpty()) { + if (getLogic()->getCards().isEmpty()) { return; } - cards.sortBy({CardList::SortByMainType, CardList::SortByManaValue, CardList::SortByColorGrouping}); + getLogic()->sortCards({CardList::SortByMainType, CardList::SortByManaValue, CardList::SortByColorGrouping}); reorganizeCards(); } diff --git a/cockatrice/src/game/zones/hand_zone.h b/cockatrice/src/game/zones/hand_zone.h index 3fd55b20b..a5e06819e 100644 --- a/cockatrice/src/game/zones/hand_zone.h +++ b/cockatrice/src/game/zones/hand_zone.h @@ -1,6 +1,7 @@ #ifndef HANDZONE_H #define HANDZONE_H +#include "logic/hand_zone_logic.h" #include "select_zone.h" class HandZone : public SelectZone @@ -14,16 +15,14 @@ public slots: void updateOrientation(); public: - HandZone(Player *_p, bool _contentsKnown, int _zoneHeight, QGraphicsItem *parent = nullptr); - void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) override; + HandZone(HandZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent = nullptr); + void + handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override; QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void reorganizeCards() override; void sortHand(); void setWidth(qreal _width); - -protected: - void addCardImpl(CardItem *card, int x, int y) override; }; #endif diff --git a/cockatrice/src/game/zones/logic/card_zone_logic.cpp b/cockatrice/src/game/zones/logic/card_zone_logic.cpp new file mode 100644 index 000000000..1b760fc81 --- /dev/null +++ b/cockatrice/src/game/zones/logic/card_zone_logic.cpp @@ -0,0 +1,206 @@ +#include "card_zone_logic.h" + +#include "../../board/card_item.h" +#include "../../cards/card_database_manager.h" +#include "../../player/player.h" +#include "../pile_zone.h" +#include "../view_zone.h" +#include "pb/command_move_card.pb.h" +#include "pb/serverinfo_user.pb.h" +#include "view_zone_logic.h" + +#include +#include + +/** + * @param _player the player that the zone belongs to + * @param _name internal name of the zone + * @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it + * @param _contentsKnown whether the cards in the zone are known to the client + * @param parent the parent QObject. + */ +CardZoneLogic::CardZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent) + : QObject(parent), player(_player), name(_name), cards(_contentsKnown), views{}, hasCardAttr(_hasCardAttr), + isShufflable(_isShufflable) +{ + // If we join a game before the card db finishes loading, the cards might have the wrong printings. + // Force refresh all cards in the zone when db finishes loading to fix that. + connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this, + &CardZoneLogic::refreshCardInfos); +} + +void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x, const int y) +{ + if (!card) { + qCWarning(CardZoneLog) << "CardZoneLogic::addCard() card is null; this shouldn't normally happen"; + return; + } + + for (auto *view : views) { + if (qobject_cast(view->getLogic())->prepareAddCard(x)) { + view->getLogic()->addCard(new CardItem(player, nullptr, card->getCardRef(), card->getId()), reorganize, x, + y); + } + } + + card->setZone(this); + emit cardAdded(card); + addCardImpl(card, x, y); + + if (reorganize) + emit reorganizeCards(); + + emit cardCountChanged(); +} + +CardItem *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone) +{ + if (position == -1) { + // position == -1 means either that the zone is indexed by card id + // or that it doesn't matter which card you take. + for (int i = 0; i < cards.size(); ++i) + if (cards[i]->getId() == cardId) { + position = i; + break; + } + if (position == -1) + position = 0; + } + if (position >= cards.size()) + return nullptr; + + for (auto *view : views) { + qobject_cast(view->getLogic())->removeCard(position, toNewZone); + } + + CardItem *c = cards.takeAt(position); + + c->setId(cardId); + + emit reorganizeCards(); + emit cardCountChanged(); + return c; +} + +CardItem *CardZoneLogic::getCard(int cardId) +{ + CardItem *c = cards.findCard(cardId); + if (!c) { + qCWarning(CardZoneLog) << "CardZoneLogic::getCard: card id=" << cardId << "not found"; + return nullptr; + } + // If the card's id is -1, this zone is invisible, + // so we need to give the card an id as it comes out. + // It can be assumed that in an invisible zone, all cards are equal. + if (c->getId() == -1) { + c->setId(cardId); + } + return c; +} + +void CardZoneLogic::removeCard(CardItem *card) +{ + if (!card) { + qCWarning(CardZoneLog) << "CardZoneLogic::removeCard: card is null, this shouldn't normally happen"; + return; + } + + cards.removeOne(card); + + emit reorganizeCards(); + emit cardCountChanged(); + player->deleteCard(card); +} + +void CardZoneLogic::refreshCardInfos() +{ + for (const auto &cardItem : cards) { + cardItem->refreshCardInfo(); + } +} + +void CardZoneLogic::moveAllToZone() +{ + QList data = static_cast(sender())->data().toList(); + if (data.length() < 2) { + return; + } + QString targetZone = data[0].toString(); + int targetX = data[1].toInt(); + + Command_MoveCard cmd; + cmd.set_start_zone(getName().toStdString()); + cmd.set_target_player_id(player->getPlayerInfo()->getId()); + cmd.set_target_zone(targetZone.toStdString()); + cmd.set_x(targetX); + + for (int i = 0; i < cards.size(); ++i) + cmd.mutable_cards_to_move()->add_card()->set_card_id(cards[i]->getId()); + + player->getPlayerActions()->sendGameCommand(cmd); +} + +void CardZoneLogic::clearContents() +{ + for (int i = 0; i < cards.size(); i++) { + // If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a + // player, we have to return them to avoid a crash. + + const QList &attachedCards = cards[i]->getAttachedCards(); + for (auto attachedCard : attachedCards) { + emit attachedCard->getZone()->cardAdded(attachedCard); + } + + player->deleteCard(cards.at(i)); + } + cards.clear(); + emit cardCountChanged(); +} + +QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) const +{ + QString ownerName = player->getPlayerInfo()->getName(); + if (name == "hand") + return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName)); + else if (name == "deck") + switch (gc) { + case CaseLookAtZone: + return (theirOwn ? tr("their library", "look at zone") + : tr("%1's library", "look at zone").arg(ownerName)); + case CaseTopCardsOfZone: + return (theirOwn ? tr("of their library", "top cards of zone,") + : tr("of %1's library", "top cards of zone").arg(ownerName)); + case CaseRevealZone: + return (theirOwn ? tr("their library", "reveal zone") + : tr("%1's library", "reveal zone").arg(ownerName)); + case CaseShuffleZone: + return (theirOwn ? tr("their library", "shuffle") : tr("%1's library", "shuffle").arg(ownerName)); + default: + return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName)); + } + else if (name == "grave") + return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName)); + else if (name == "rfg") + return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName)); + else if (name == "sb") + switch (gc) { + case CaseLookAtZone: + return (theirOwn ? tr("their sideboard", "look at zone") + : tr("%1's sideboard", "look at zone").arg(ownerName)); + case CaseNominative: + return (theirOwn ? tr("their sideboard", "nominative") + : tr("%1's sideboard", "nominative").arg(ownerName)); + default: + break; + } + else { + return (theirOwn ? tr("their custom zone '%1'", "nominative").arg(name) + : tr("%1's custom zone '%2'", "nominative").arg(ownerName).arg(name)); + } + return QString(); +} \ No newline at end of file diff --git a/cockatrice/src/game/zones/logic/card_zone_logic.h b/cockatrice/src/game/zones/logic/card_zone_logic.h new file mode 100644 index 000000000..166f63506 --- /dev/null +++ b/cockatrice/src/game/zones/logic/card_zone_logic.h @@ -0,0 +1,113 @@ +#ifndef COCKATRICE_CARD_ZONE_LOGIC_H +#define COCKATRICE_CARD_ZONE_LOGIC_H + +#include "../../../client/translation.h" +#include "../../board/card_list.h" + +#include +#include + +inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic"); + +class Player; +class ZoneViewZone; +class QMenu; +class QAction; +class QPainter; +class CardDragItem; + +class CardZoneLogic : public QObject +{ + Q_OBJECT + +signals: + void cardAdded(CardItem *addedCard); + void cardCountChanged(); + void reorganizeCards(); + void updateGraphics(); + void setGraphicsVisibility(bool visible); + void retranslateUi(); + +public: + explicit CardZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent = nullptr); + + void addCard(CardItem *card, bool reorganize, int x, int y = -1); + // getCard() finds a card by id. + CardItem *getCard(int cardId); + void removeCard(CardItem *card); + // takeCard() finds a card by position and removes it from the zone and from all of its views. + virtual CardItem *takeCard(int position, int cardId, bool canResize = true); + + void rawInsertCard(CardItem *card, int index) + { + cards.insert(index, card); + }; + + [[nodiscard]] const CardList &getCards() const + { + return cards; + } + + void sortCards(const QList &options) + { + cards.sortBy(options); + } + QString getName() const + { + return name; + } + QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const; + Player *getPlayer() const + { + return player; + } + bool contentsKnown() const + { + return cards.getContentsKnown(); + } + QList &getViews() + { + return views; + } + void setAlwaysRevealTopCard(bool _alwaysRevealTopCard) + { + alwaysRevealTopCard = _alwaysRevealTopCard; + } + bool getAlwaysRevealTopCard() const + { + return alwaysRevealTopCard; + } + bool getHasCardAttr() const + { + return hasCardAttr; + } + bool getIsShufflable() const + { + return isShufflable; + } + void clearContents(); + +public slots: + void moveAllToZone(); + +private slots: + void refreshCardInfos(); + +protected: + Player *player; + QString name; + CardList cards; + QList views; + bool hasCardAttr; + bool isShufflable; + bool alwaysRevealTopCard; + + virtual void addCardImpl(CardItem *card, int x, int y) = 0; +}; + +#endif // COCKATRICE_CARD_ZONE_LOGIC_H diff --git a/cockatrice/src/game/zones/logic/hand_zone_logic.cpp b/cockatrice/src/game/zones/logic/hand_zone_logic.cpp new file mode 100644 index 000000000..292b9f7e3 --- /dev/null +++ b/cockatrice/src/game/zones/logic/hand_zone_logic.cpp @@ -0,0 +1,29 @@ +#include "hand_zone_logic.h" + +#include "../../board/card_item.h" + +HandZoneLogic::HandZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent) + : CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent) +{ +} + +void HandZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/) +{ + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { + x = cards.size(); + } + cards.insert(x, card); + + if (!cards.getContentsKnown()) { + card->setId(-1); + card->setCardRef({}); + } + card->resetState(); + card->setVisible(true); +} \ No newline at end of file diff --git a/cockatrice/src/game/zones/logic/hand_zone_logic.h b/cockatrice/src/game/zones/logic/hand_zone_logic.h new file mode 100644 index 000000000..aebe6e03f --- /dev/null +++ b/cockatrice/src/game/zones/logic/hand_zone_logic.h @@ -0,0 +1,20 @@ +#ifndef COCKATRICE_HAND_ZONE_LOGIC_H +#define COCKATRICE_HAND_ZONE_LOGIC_H +#include "card_zone_logic.h" + +class HandZoneLogic : public CardZoneLogic +{ + Q_OBJECT +public: + HandZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent = nullptr); + +protected: + void addCardImpl(CardItem *card, int x, int y) override; +}; + +#endif // COCKATRICE_HAND_ZONE_LOGIC_H diff --git a/cockatrice/src/game/zones/logic/pile_zone_logic.cpp b/cockatrice/src/game/zones/logic/pile_zone_logic.cpp new file mode 100644 index 000000000..c5ffd5fd5 --- /dev/null +++ b/cockatrice/src/game/zones/logic/pile_zone_logic.cpp @@ -0,0 +1,33 @@ +#include "pile_zone_logic.h" + +#include "../../board/card_item.h" + +PileZoneLogic::PileZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent) + : CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent) +{ +} + +void PileZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/) +{ + connect(card, &CardItem::sigPixmapUpdated, this, &PileZoneLogic::callUpdate); + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { + x = cards.size(); + } + cards.insert(x, card); + card->setPos(0, 0); + if (!contentsKnown()) { + card->setCardRef({}); + card->setId(-1); + // If we obscure a previously revealed card, its name has to be forgotten + if (cards.size() > x + 1) + cards.at(x + 1)->setCardRef({}); + } + card->setVisible(false); + card->resetState(); +} \ No newline at end of file diff --git a/cockatrice/src/game/zones/logic/pile_zone_logic.h b/cockatrice/src/game/zones/logic/pile_zone_logic.h new file mode 100644 index 000000000..104ce42b5 --- /dev/null +++ b/cockatrice/src/game/zones/logic/pile_zone_logic.h @@ -0,0 +1,25 @@ +#ifndef COCKATRICE_PILE_ZONE_LOGIC_H +#define COCKATRICE_PILE_ZONE_LOGIC_H +#include "card_zone_logic.h" + +class PileZoneLogic : public CardZoneLogic +{ + + Q_OBJECT + +signals: + void callUpdate(); + +public: + PileZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent = nullptr); + +protected: + void addCardImpl(CardItem *card, int x, int y) override; +}; + +#endif // COCKATRICE_PILE_ZONE_LOGIC_H diff --git a/cockatrice/src/game/zones/logic/stack_zone_logic.cpp b/cockatrice/src/game/zones/logic/stack_zone_logic.cpp new file mode 100644 index 000000000..a477a8025 --- /dev/null +++ b/cockatrice/src/game/zones/logic/stack_zone_logic.cpp @@ -0,0 +1,29 @@ +#include "stack_zone_logic.h" + +#include "../../board/card_item.h" + +StackZoneLogic::StackZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent) + : CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent) +{ +} + +void StackZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/) +{ + // if x is negative set it to add at end + if (x < 0 || x >= cards.size()) { + x = static_cast(cards.size()); + } + cards.insert(x, card); + + if (!cards.getContentsKnown()) { + card->setId(-1); + card->setCardRef({}); + } + card->resetState(true); + card->setVisible(true); +} \ No newline at end of file diff --git a/cockatrice/src/game/zones/logic/stack_zone_logic.h b/cockatrice/src/game/zones/logic/stack_zone_logic.h new file mode 100644 index 000000000..709d0c87a --- /dev/null +++ b/cockatrice/src/game/zones/logic/stack_zone_logic.h @@ -0,0 +1,20 @@ +#ifndef COCKATRICE_STACK_ZONE_LOGIC_H +#define COCKATRICE_STACK_ZONE_LOGIC_H +#include "card_zone_logic.h" + +class StackZoneLogic : public CardZoneLogic +{ + Q_OBJECT +public: + StackZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent = nullptr); + +protected: + void addCardImpl(CardItem *card, int x, int y) override; +}; + +#endif // COCKATRICE_STACK_ZONE_LOGIC_H diff --git a/cockatrice/src/game/zones/logic/table_zone_logic.cpp b/cockatrice/src/game/zones/logic/table_zone_logic.cpp new file mode 100644 index 000000000..b154607f0 --- /dev/null +++ b/cockatrice/src/game/zones/logic/table_zone_logic.cpp @@ -0,0 +1,29 @@ +#include "table_zone_logic.h" + +#include "../../board/card_item.h" + +TableZoneLogic::TableZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent) + : CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent) +{ +} + +void TableZoneLogic::addCardImpl(CardItem *card, int _x, int _y) +{ + cards.append(card); + card->setGridPoint(QPoint(_x, _y)); + card->setVisible(true); +} + +CardItem *TableZoneLogic::takeCard(int position, int cardId, bool toNewZone) +{ + CardItem *result = CardZoneLogic::takeCard(position, cardId); + + if (toNewZone) + emit contentSizeChanged(); + return result; +} \ No newline at end of file diff --git a/cockatrice/src/game/zones/logic/table_zone_logic.h b/cockatrice/src/game/zones/logic/table_zone_logic.h new file mode 100644 index 000000000..a3ce3480b --- /dev/null +++ b/cockatrice/src/game/zones/logic/table_zone_logic.h @@ -0,0 +1,34 @@ +#ifndef COCKATRICE_TABLE_ZONE_LOGIC_H +#define COCKATRICE_TABLE_ZONE_LOGIC_H +#include "card_zone_logic.h" + +class TableZoneLogic : public CardZoneLogic +{ + Q_OBJECT +signals: + void contentSizeChanged(); + void toggleTapped(); + +public: + TableZoneLogic(Player *_player, + const QString &_name, + bool _hasCardAttr, + bool _isShufflable, + bool _contentsKnown, + QObject *parent = nullptr); + +protected: + void addCardImpl(CardItem *card, int x, int y) override; + + /** + * @brief Removes a card from view. + * + * @param position card position + * @param cardId id of card to take + * @param toNewZone Whether the destination of the card is not the same as the starting zone. Defaults to true + * @return CardItem that has been removed + */ + CardItem *takeCard(int position, int cardId, bool toNewZone = true) override; +}; + +#endif // COCKATRICE_TABLE_ZONE_LOGIC_H diff --git a/cockatrice/src/game/zones/logic/view_zone_logic.cpp b/cockatrice/src/game/zones/logic/view_zone_logic.cpp new file mode 100644 index 000000000..11fa0a27a --- /dev/null +++ b/cockatrice/src/game/zones/logic/view_zone_logic.cpp @@ -0,0 +1,159 @@ +#include "view_zone_logic.h" + +#include "../../../settings/cache_settings.h" +#include "../../board/card_item.h" + +ZoneViewZoneLogic::ZoneViewZoneLogic(Player *_player, + CardZoneLogic *_origZone, + int _numberCards, + bool _revealZone, + bool _writeableRevealZone, + bool _isReversed, + QObject *parent) + : CardZoneLogic(_player, _origZone->getName(), false, false, true, parent), origZone(_origZone), + numberCards(_numberCards), revealZone(_revealZone), writeableRevealZone(_writeableRevealZone), + isReversed(_isReversed) +{ +} + +/** + * Checks if inserting a card at the given position requires an actual new card to be created and added to the view. + * Also does any cardId updates that would be required if a card is inserted in that position. + * + * Note that this method can end up modifying the cardIds despite returning false. + * (for example, if the card is inserted into a hidden portion of the deck while the view is reversed) + * + * Make sure to call this method once before calling addCard(), so that you skip creating a new CardItem and calling + * addCard() if it's not required. + * + * @param x The position to insert the card at. + * @return Whether to proceed with calling addCard. + */ +bool ZoneViewZoneLogic::prepareAddCard(int x) +{ + bool doInsert = false; + if (!isReversed) { + if (x <= cards.size() || cards.size() == -1) { + doInsert = true; + } + } else { + // map x (which is in origZone indexes) to this viewZone's cardList index + int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId(); + int insertionIndex = x - firstId; + if (insertionIndex >= 0) { + // card was put into a portion of the deck that's in the view + doInsert = true; + } else { + // card was put into a portion of the deck that's not in the view; update ids but don't insert card + updateCardIds(ADD_CARD); + } + } + + // autoclose check is done both here and in removeCard + + if (cards.isEmpty() && !doInsert && SettingsCache::instance().getCloseEmptyCardView()) { + emit closeView(); + } + + return doInsert; +} + +/** + * Make sure prepareAddCard() was called before calling addCard(). + * This method assumes we already checked that the card is being inserted into the visible portion + */ +void ZoneViewZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/) +{ + if (!isReversed) { + // if x is negative set it to add at end + // if x is out-of-bounds then also set it to add at the end + if (x < 0 || x >= cards.size()) { + x = cards.size(); + } + cards.insert(x, card); + } else { + // map x (which is in origZone indexes) to this viewZone's cardList index + int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId(); + int insertionIndex = x - firstId; + // qMin to prevent out-of-bounds error when bottoming a card that is already in the view + cards.insert(qMin(insertionIndex, cards.size()), card); + } + + updateCardIds(ADD_CARD); + reorganizeCards(); +} + +void ZoneViewZoneLogic::updateCardIds(CardAction action) +{ + if (origZone->contentsKnown()) { + return; + } + + if (cards.isEmpty()) { + return; + } + + int cardCount = cards.size(); + + auto startId = 0; + + if (isReversed) { + // the card has not been added to origZone's cardList at this point + startId = origZone->getCards().size() - cardCount; + switch (action) { + case INITIALIZE: + break; + case ADD_CARD: + startId += 1; + break; + case REMOVE_CARD: + startId -= 1; + break; + } + } + + for (int i = 0; i < cardCount; ++i) { + cards[i]->setId(i + startId); + } +} + +void ZoneViewZoneLogic::removeCard(int position, bool toNewZone) +{ + if (isReversed) { + position -= cards.first()->getId(); + if (position < 0 || position >= cards.size()) { + updateCardIds(REMOVE_CARD); + return; + } + } + + if (position >= cards.size()) { + return; + } + + CardItem *card = cards.takeAt(position); + card->deleteLater(); + + // The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that + // card gets dragged within the view. + // Another autoclose check is done in prepareAddCard so that the view autocloses if the last card was moved to an + // unrevealed portion of the same zone. + if (cards.isEmpty() && SettingsCache::instance().getCloseEmptyCardView() && toNewZone) { + emit closeView(); + return; + } + + updateCardIds(REMOVE_CARD); + reorganizeCards(); +} + +void ZoneViewZoneLogic::setWriteableRevealZone(bool _writeableRevealZone) +{ + + if (writeableRevealZone && !_writeableRevealZone) { + emit addToViews(); + } else if (!writeableRevealZone && _writeableRevealZone) { + emit removeFromViews(); + } + writeableRevealZone = _writeableRevealZone; +} \ No newline at end of file diff --git a/cockatrice/src/game/zones/logic/view_zone_logic.h b/cockatrice/src/game/zones/logic/view_zone_logic.h new file mode 100644 index 000000000..f5139dfaf --- /dev/null +++ b/cockatrice/src/game/zones/logic/view_zone_logic.h @@ -0,0 +1,65 @@ +#ifndef COCKATRICE_VIEW_ZONE_LOGIC_H +#define COCKATRICE_VIEW_ZONE_LOGIC_H +#include "card_zone_logic.h" + +class ZoneViewZoneLogic : public CardZoneLogic +{ + Q_OBJECT +signals: + void addToViews(); + void removeFromViews(); + void closeView(); + +private: + CardZoneLogic *origZone; + int numberCards; + bool revealZone, writeableRevealZone; + bool isReversed; + +public: + enum CardAction + { + INITIALIZE, + ADD_CARD, + REMOVE_CARD + }; + + ZoneViewZoneLogic(Player *_player, + CardZoneLogic *_origZone, + int _numberCards, + bool _revealZone, + bool _writeableRevealZone, + bool _isReversed, + QObject *parent = nullptr); + + bool prepareAddCard(int x); + void removeCard(int position, bool toNewZone); + void updateCardIds(CardAction action); + int getNumberCards() const + { + return numberCards; + } + bool getRevealZone() const + { + return revealZone; + } + bool getWriteableRevealZone() const + { + return writeableRevealZone; + } + void setWriteableRevealZone(bool _writeableRevealZone); + bool getIsReversed() const + { + return isReversed; + } + + CardZoneLogic *getOriginalZone() const + { + return origZone; + } + +protected: + void addCardImpl(CardItem *card, int x, int y) override; +}; + +#endif // COCKATRICE_VIEW_ZONE_LOGIC_H diff --git a/cockatrice/src/game/zones/pile_zone.cpp b/cockatrice/src/game/zones/pile_zone.cpp index d1e97f05f..5df91a64d 100644 --- a/cockatrice/src/game/zones/pile_zone.cpp +++ b/cockatrice/src/game/zones/pile_zone.cpp @@ -3,6 +3,7 @@ #include "../board/card_drag_item.h" #include "../board/card_item.h" #include "../player/player.h" +#include "logic/pile_zone_logic.h" #include "pb/command_move_card.pb.h" #include "view_zone.h" @@ -10,8 +11,7 @@ #include #include -PileZone::PileZone(Player *_p, const QString &_name, bool _isShufflable, bool _contentsKnown, QGraphicsItem *parent) - : CardZone(_p, _name, false, _isShufflable, _contentsKnown, parent) +PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_logic, parent) { setCacheMode(DeviceCoordinateCache); // Do not move this line to the parent constructor! setAcceptHoverEvents(true); @@ -47,52 +47,30 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio { painter->drawPath(shape()); - if (!cards.isEmpty()) - cards.at(0)->paintPicture(painter, cards.at(0)->getTranslatedSize(painter), 90); + if (!getLogic()->getCards().isEmpty()) + getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter), + 90); painter->translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2); painter->rotate(-90); painter->translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2); - paintNumberEllipse(cards.size(), 28, Qt::white, -1, -1, painter); + paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter); } -void PileZone::addCardImpl(CardItem *card, int x, int /*y*/) -{ - connect(card, &CardItem::sigPixmapUpdated, this, &PileZone::callUpdate); - // if x is negative set it to add at end - if (x < 0 || x >= cards.size()) { - x = cards.size(); - } - cards.insert(x, card); - card->setPos(0, 0); - if (!contentsKnown()) { - card->setCardRef({}); - card->setId(-1); - // If we obscure a previously revealed card, its name has to be forgotten - if (cards.size() > x + 1) - cards.at(x + 1)->setCardRef({}); - } - card->setVisible(false); - card->resetState(); - card->setParentItem(this); -} - -void PileZone::handleDropEvent(const QList &dragItems, - CardZone *startZone, - const QPoint & /*dropPoint*/) +void PileZone::handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &) { Command_MoveCard cmd; - cmd.set_start_player_id(startZone->getPlayer()->getId()); + cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_zone(startZone->getName().toStdString()); - cmd.set_target_player_id(player->getId()); - cmd.set_target_zone(getName().toStdString()); + cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_target_zone(getLogic()->getName().toStdString()); cmd.set_x(0); cmd.set_y(0); for (int i = 0; i < dragItems.size(); ++i) cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); - player->sendGameCommand(cmd); + getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } void PileZone::reorganizeCards() @@ -119,13 +97,14 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event) QApplication::startDragDistance()) return; - if (cards.isEmpty()) + if (getLogic()->getCards().isEmpty()) return; bool faceDown = event->modifiers().testFlag(Qt::ShiftModifier); bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier); - CardItem *card = bottomCard ? cards.last() : cards.first(); - const int cardid = contentsKnown() ? card->getId() : (bottomCard ? cards.size() - 1 : 0); + CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first(); + const int cardid = + getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0); CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), faceDown); drag->grabMouse(); setCursor(Qt::OpenHandCursor); @@ -138,7 +117,7 @@ void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/) void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { - if (!cards.isEmpty()) - cards[0]->processHoverEvent(); + if (!getLogic()->getCards().isEmpty()) + getLogic()->getCards()[0]->processHoverEvent(); QGraphicsItem::hoverEnterEvent(event); } diff --git a/cockatrice/src/game/zones/pile_zone.h b/cockatrice/src/game/zones/pile_zone.h index a66543ab3..9ea05fa5d 100644 --- a/cockatrice/src/game/zones/pile_zone.h +++ b/cockatrice/src/game/zones/pile_zone.h @@ -2,6 +2,7 @@ #define PILEZONE_H #include "card_zone.h" +#include "logic/pile_zone_logic.h" /** * A CardZone where the cards are in a single pile instead of being laid out. @@ -17,23 +18,19 @@ private slots: } public: - PileZone(Player *_p, - const QString &_name, - bool _isShufflable, - bool _contentsKnown, - QGraphicsItem *parent = nullptr); - QRectF boundingRect() const override; - QPainterPath shape() const override; + PileZone(PileZoneLogic *_logic, QGraphicsItem *parent); + [[nodiscard]] QRectF boundingRect() const override; + [[nodiscard]] QPainterPath shape() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void reorganizeCards() override; - void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) override; + void + handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override; protected: void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; - void addCardImpl(CardItem *card, int x, int y) override; }; #endif diff --git a/cockatrice/src/game/zones/select_zone.cpp b/cockatrice/src/game/zones/select_zone.cpp index b26aa23ff..863edab48 100644 --- a/cockatrice/src/game/zones/select_zone.cpp +++ b/cockatrice/src/game/zones/select_zone.cpp @@ -32,13 +32,7 @@ qreal divideCardSpaceInZone(qreal index, int cardCount, qreal totalHeight, qreal return y; } -SelectZone::SelectZone(Player *_player, - const QString &_name, - bool _hasCardAttr, - bool _isShufflable, - bool _contentsKnown, - QGraphicsItem *parent) - : CardZone(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent) +SelectZone::SelectZone(CardZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_logic, parent) { } @@ -57,8 +51,8 @@ void SelectZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event) pos.setY(br.height()); QRectF selectionRect = QRectF(selectionOrigin, pos).normalized(); - for (auto card : cards) { - if (card->getAttachedTo() && card->getAttachedTo()->getZone() != this) { + for (auto card : getLogic()->getCards()) { + if (card->getAttachedTo() && card->getAttachedTo()->getZone() != getLogic()) { continue; } diff --git a/cockatrice/src/game/zones/select_zone.h b/cockatrice/src/game/zones/select_zone.h index 6aa56a1c6..eabd8b370 100644 --- a/cockatrice/src/game/zones/select_zone.h +++ b/cockatrice/src/game/zones/select_zone.h @@ -21,12 +21,7 @@ protected: void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; public: - SelectZone(Player *_player, - const QString &_name, - bool _hasCardAttr, - bool _isShufflable, - bool _contentsKnown, - QGraphicsItem *parent = nullptr); + SelectZone(CardZoneLogic *logic, QGraphicsItem *parent = nullptr); }; qreal divideCardSpaceInZone(qreal index, int cardCount, qreal totalHeight, qreal cardHeight, bool reverse = false); diff --git a/cockatrice/src/game/zones/stack_zone.cpp b/cockatrice/src/game/zones/stack_zone.cpp index 136e59ef1..3c940366b 100644 --- a/cockatrice/src/game/zones/stack_zone.cpp +++ b/cockatrice/src/game/zones/stack_zone.cpp @@ -6,13 +6,14 @@ #include "../board/card_drag_item.h" #include "../board/card_item.h" #include "../player/player.h" +#include "logic/stack_zone_logic.h" #include "pb/command_move_card.pb.h" #include #include -StackZone::StackZone(Player *_p, int _zoneHeight, QGraphicsItem *parent) - : SelectZone(_p, "stack", false, false, true, parent), zoneHeight(_zoneHeight) +StackZone::StackZone(StackZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent) + : SelectZone(_logic, parent), zoneHeight(_zoneHeight) { connect(themeManager, &ThemeManager::themeChanged, this, &StackZone::updateBg); updateBg(); @@ -24,24 +25,6 @@ void StackZone::updateBg() update(); } -void StackZone::addCardImpl(CardItem *card, int x, int /*y*/) -{ - // if x is negative set it to add at end - if (x < 0 || x >= cards.size()) { - x = static_cast(cards.size()); - } - cards.insert(x, card); - - if (!cards.getContentsKnown()) { - card->setId(-1); - card->setCardRef({}); - } - card->setParentItem(this); - card->resetState(true); - card->setVisible(true); - card->update(); -} - QRectF StackZone::boundingRect() const { return {0, 0, 100, zoneHeight}; @@ -49,27 +32,29 @@ QRectF StackZone::boundingRect() const void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, player->getZoneId()); + QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, getLogic()->getPlayer()->getZoneId()); painter->fillRect(boundingRect(), brush); } -void StackZone::handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) +void StackZone::handleDropEvent(const QList &dragItems, + CardZoneLogic *startZone, + const QPoint &dropPoint) { if (startZone == nullptr || startZone->getPlayer() == nullptr) { return; } Command_MoveCard cmd; - cmd.set_start_player_id(startZone->getPlayer()->getId()); + cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_zone(startZone->getName().toStdString()); - cmd.set_target_player_id(player->getId()); - cmd.set_target_zone(getName().toStdString()); + cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_target_zone(getLogic()->getName().toStdString()); int index = 0; - if (!cards.isEmpty()) { - const auto cardCount = static_cast(cards.size()); - const auto &card = cards.at(0); + if (!getLogic()->getCards().isEmpty()) { + const auto cardCount = static_cast(getLogic()->getCards().size()); + const auto &card = getLogic()->getCards().at(0); index = qRound(divideCardSpaceInZone(dropPoint.y(), cardCount, boundingRect().height(), card->boundingRect().height(), true)); @@ -78,9 +63,9 @@ void StackZone::handleDropEvent(const QList &dragItems, CardZone // currently, so clamp it to avoid crashes. index = qBound(0, index, cardCount - 1); - if (startZone == this) { + if (startZone == getLogic()) { const auto &dragItem = dragItems.at(0); - const auto &card = cards.at(index); + const auto &card = getLogic()->getCards().at(index); if (card->getId() == dragItem->getId()) { return; @@ -97,24 +82,24 @@ void StackZone::handleDropEvent(const QList &dragItems, CardZone } } - player->sendGameCommand(cmd); + getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } void StackZone::reorganizeCards() { - if (!cards.isEmpty()) { - const auto cardCount = static_cast(cards.size()); + if (!getLogic()->getCards().isEmpty()) { + const auto cardCount = static_cast(getLogic()->getCards().size()); qreal totalWidth = boundingRect().width(); - qreal cardWidth = cards.at(0)->boundingRect().width(); + qreal cardWidth = getLogic()->getCards().at(0)->boundingRect().width(); qreal xspace = 5; qreal x1 = xspace; qreal x2 = totalWidth - xspace - cardWidth; for (int i = 0; i < cardCount; i++) { - CardItem *card = cards.at(i); + CardItem *card = getLogic()->getCards().at(i); qreal x = (i % 2) ? x2 : x1; - qreal y = - divideCardSpaceInZone(i, cardCount, boundingRect().height(), cards.at(0)->boundingRect().height()); + qreal y = divideCardSpaceInZone(i, cardCount, boundingRect().height(), + getLogic()->getCards().at(0)->boundingRect().height()); card->setPos(x, y); card->setRealZValue(i); } diff --git a/cockatrice/src/game/zones/stack_zone.h b/cockatrice/src/game/zones/stack_zone.h index 38dee0aca..16dab22bb 100644 --- a/cockatrice/src/game/zones/stack_zone.h +++ b/cockatrice/src/game/zones/stack_zone.h @@ -1,6 +1,7 @@ #ifndef STACKZONE_H #define STACKZONE_H +#include "logic/stack_zone_logic.h" #include "select_zone.h" class StackZone : public SelectZone @@ -12,14 +13,12 @@ private slots: void updateBg(); public: - StackZone(Player *_p, int _zoneHeight, QGraphicsItem *parent = nullptr); - void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) override; + StackZone(StackZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent); + void + handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override; QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void reorganizeCards() override; - -protected: - void addCardImpl(CardItem *card, int x, int y) override; }; #endif diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index a42bcdb18..8505825f8 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -7,6 +7,7 @@ #include "../board/card_item.h" #include "../cards/card_info.h" #include "../player/player.h" +#include "logic/table_zone_logic.h" #include "pb/command_move_card.pb.h" #include "pb/command_set_card_attr.pb.h" @@ -19,9 +20,10 @@ 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(Player *_p, const QString &name, QGraphicsItem *parent) - : SelectZone(_p, name, true, false, true, parent), active(false) +TableZone::TableZone(TableZoneLogic *_logic, QGraphicsItem *parent) : SelectZone(_logic, parent), active(false) { + connect(_logic, &TableZoneLogic::contentSizeChanged, this, &TableZone::resizeToContents); + connect(_logic, &TableZoneLogic::toggleTapped, this, &TableZone::toggleTapped); connect(themeManager, &ThemeManager::themeChanged, this, &TableZone::updateBg); connect(&SettingsCache::instance(), &SettingsCache::invertVerticalCoordinateChanged, this, &TableZone::reorganizeCards); @@ -48,13 +50,15 @@ QRectF TableZone::boundingRect() const bool TableZone::isInverted() const { - return ((player->getMirrored() && !SettingsCache::instance().getInvertVerticalCoordinate()) || - (!player->getMirrored() && SettingsCache::instance().getInvertVerticalCoordinate())); + return ((getLogic()->getPlayer()->getGraphicsItem()->getMirrored() && + !SettingsCache::instance().getInvertVerticalCoordinate()) || + (!getLogic()->getPlayer()->getGraphicsItem()->getMirrored() && + SettingsCache::instance().getInvertVerticalCoordinate())); } void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) { - QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Table, player->getZoneId()); + QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Table, getLogic()->getPlayer()->getZoneId()); painter->fillRect(boundingRect(), brush); if (active) { @@ -108,30 +112,22 @@ void TableZone::paintLandDivider(QPainter *painter) painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY)); } -void TableZone::addCardImpl(CardItem *card, int _x, int _y) -{ - cards.append(card); - card->setGridPoint(QPoint(_x, _y)); - - card->setParentItem(this); - card->setVisible(true); - card->update(); -} - -void TableZone::handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) +void TableZone::handleDropEvent(const QList &dragItems, + CardZoneLogic *startZone, + const QPoint &dropPoint) { handleDropEventByGrid(dragItems, startZone, mapToGrid(dropPoint)); } void TableZone::handleDropEventByGrid(const QList &dragItems, - CardZone *startZone, + CardZoneLogic *startZone, const QPoint &gridPoint) { Command_MoveCard cmd; - cmd.set_start_player_id(startZone->getPlayer()->getId()); + cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_zone(startZone->getName().toStdString()); - cmd.set_target_player_id(player->getId()); - cmd.set_target_zone(getName().toStdString()); + cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_target_zone(getLogic()->getName().toStdString()); cmd.set_x(gridPoint.x()); cmd.set_y(gridPoint.y()); @@ -139,7 +135,7 @@ void TableZone::handleDropEventByGrid(const QList &dragItems, CardToMove *ctm = cmd.mutable_cards_to_move()->add_card(); ctm->set_card_id(item->getId()); ctm->set_face_down(item->getFaceDown()); - if (startZone->getName() != name && !item->getFaceDown()) { + if (startZone->getName() != getLogic()->getName() && !item->getFaceDown()) { const auto &card = item->getItem()->getCard(); if (card) { ctm->set_pt(card.getInfo().getPowTough().toStdString()); @@ -147,7 +143,7 @@ void TableZone::handleDropEventByGrid(const QList &dragItems, } } - startZone->getPlayer()->sendGameCommand(cmd); + startZone->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } void TableZone::reorganizeCards() @@ -155,8 +151,8 @@ void TableZone::reorganizeCards() // Calculate card stack widths so mapping functions work properly computeCardStackWidths(); - for (int i = 0; i < cards.size(); ++i) { - QPoint gridPoint = cards[i]->getGridPos(); + for (int i = 0; i < getLogic()->getCards().size(); ++i) { + QPoint gridPoint = getLogic()->getCards()[i]->getGridPos(); if (gridPoint.x() == -1) continue; @@ -164,16 +160,16 @@ void TableZone::reorganizeCards() qreal x = mapPoint.x(); qreal y = mapPoint.y(); - int numberAttachedCards = cards[i]->getAttachedCards().size(); + int numberAttachedCards = getLogic()->getCards()[i]->getAttachedCards().size(); qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X; qreal actualY = y; if (numberAttachedCards) actualY += 15; - cards[i]->setPos(actualX, actualY); - cards[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100); + getLogic()->getCards()[i]->setPos(actualX, actualY); + getLogic()->getCards()[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100); - QListIterator attachedCardIterator(cards[i]->getAttachedCards()); + QListIterator attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards()); int j = 0; while (attachedCardIterator.hasNext()) { ++j; @@ -211,22 +207,15 @@ void TableZone::toggleTapped() CardItem *temp = qgraphicsitem_cast(selectedItem); if (temp->getTapped() != tapAll) { Command_SetCardAttr *cmd = new Command_SetCardAttr; - cmd->set_zone(name.toStdString()); + cmd->set_zone(getLogic()->getName().toStdString()); cmd->set_card_id(temp->getId()); cmd->set_attribute(AttrTapped); cmd->set_attr_value(tapAll ? "1" : "0"); cmdList.append(cmd); } } - player->sendGameCommand(player->prepareGameCommand(cmdList)); -} - -CardItem *TableZone::takeCard(int position, int cardId, bool toNewZone) -{ - CardItem *result = CardZone::takeCard(position, cardId); - if (toNewZone) - resizeToContents(); - return result; + getLogic()->getPlayer()->getPlayerActions()->sendGameCommand( + getLogic()->getPlayer()->getPlayerActions()->prepareGameCommand(cmdList)); } void TableZone::resizeToContents() @@ -234,9 +223,9 @@ void TableZone::resizeToContents() int xMax = 0; // Find rightmost card position, which includes the left margin amount. - for (int i = 0; i < cards.size(); ++i) - if (cards[i]->pos().x() > xMax) - xMax = (int)cards[i]->pos().x(); + for (int i = 0; i < getLogic()->getCards().size(); ++i) + if (getLogic()->getCards()[i]->pos().x() > xMax) + xMax = (int)getLogic()->getCards()[i]->pos().x(); // Minimum width is the rightmost card position plus enough room for // another card with padding, then margin. @@ -254,9 +243,9 @@ void TableZone::resizeToContents() CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const { - for (int i = 0; i < cards.size(); i++) - if (cards.at(i)->getGridPoint() == gridPoint) - return cards.at(i); + for (int i = 0; i < getLogic()->getCards().size(); i++) + if (getLogic()->getCards().at(i)->getGridPoint() == gridPoint) + return getLogic()->getCards().at(i); return 0; } @@ -271,8 +260,8 @@ void TableZone::computeCardStackWidths() // Each card stack is three grid points worth of card locations. // First pass: compute the number of cards at each card stack. QMap cardStackCount; - for (int i = 0; i < cards.size(); ++i) { - const QPoint &gridPoint = cards[i]->getGridPos(); + for (int i = 0; i < getLogic()->getCards().size(); ++i) { + const QPoint &gridPoint = getLogic()->getCards()[i]->getGridPos(); if (gridPoint.x() == -1) continue; @@ -282,15 +271,16 @@ void TableZone::computeCardStackWidths() // Second pass: compute the width at each card stack. cardStackWidth.clear(); - for (int i = 0; i < cards.size(); ++i) { - const QPoint &gridPoint = cards[i]->getGridPos(); + for (int i = 0; i < getLogic()->getCards().size(); ++i) { + const QPoint &gridPoint = getLogic()->getCards()[i]->getGridPos(); if (gridPoint.x() == -1) continue; const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); const int stackCount = cardStackCount.value(key, 0); if (stackCount == 1) - cardStackWidth.insert(key, CARD_WIDTH + cards[i]->getAttachedCards().size() * STACKED_CARD_OFFSET_X); + cardStackWidth.insert(key, CARD_WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * + STACKED_CARD_OFFSET_X); else cardStackWidth.insert(key, CARD_WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); } diff --git a/cockatrice/src/game/zones/table_zone.h b/cockatrice/src/game/zones/table_zone.h index 3d464e6f3..155ac7ab3 100644 --- a/cockatrice/src/game/zones/table_zone.h +++ b/cockatrice/src/game/zones/table_zone.h @@ -2,6 +2,7 @@ #define TABLEZONE_H #include "../board/abstract_card_item.h" +#include "logic/table_zone_logic.h" #include "select_zone.h" /* @@ -75,7 +76,7 @@ private: /* If this TableZone is currently active */ - bool active; + bool active = false; bool isInverted() const; @@ -98,7 +99,7 @@ public: @param _p the Player @param parent defaults to null */ - explicit TableZone(Player *_p, const QString &name, QGraphicsItem *parent = nullptr); + explicit TableZone(TableZoneLogic *_logic, QGraphicsItem *parent = nullptr); /** @return a QRectF of the TableZone bounding box. @@ -121,12 +122,14 @@ public: /** See HandleDropEventByGrid */ - void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) override; + void + handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override; /** Handles the placement of cards */ - void handleDropEventByGrid(const QList &dragItems, CardZone *startZone, const QPoint &gridPoint); + void + handleDropEventByGrid(const QList &dragItems, CardZoneLogic *startZone, const QPoint &gridPoint); /** @return CardItem from grid location @@ -142,16 +145,6 @@ public: static int clampValidTableRow(const int row); - /** - Removes a card from view. - - @param position card position - @param cardId id of card to take - @param toNewZone Whether the destination of the card is not the same as the starting zone. Defaults to true - @return CardItem that has been removed - */ - CardItem *takeCard(int position, int cardId, bool toNewZone = true) override; - /** Resizes the TableZone in case CardItems are within or outside of the TableZone constraints. @@ -177,9 +170,6 @@ public: update(); } -protected: - void addCardImpl(CardItem *card, int x, int y) override; - private: void paintZoneOutline(QPainter *painter); void paintLandDivider(QPainter *painter); diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 213b60f2c..d96cbc28c 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -5,6 +5,7 @@ #include "../board/card_item.h" #include "../cards/card_info.h" #include "../player/player.h" +#include "logic/view_zone_logic.h" #include "pb/command_dump_zone.pb.h" #include "pb/command_move_card.pb.h" #include "pb/response_dump_zone.pb.h" @@ -23,21 +24,24 @@ * @param _writeableRevealZone whether the player can interact with the revealed cards. * @param parent the parent QGraphicsWidget containing the reveal zone */ -ZoneViewZone::ZoneViewZone(Player *_p, - CardZone *_origZone, - int _numberCards, - bool _revealZone, - bool _writeableRevealZone, - QGraphicsItem *parent, - bool _isReversed) - : SelectZone(_p, _origZone->getName(), false, false, true, parent), bRect(QRectF()), minRows(0), - numberCards(_numberCards), origZone(_origZone), revealZone(_revealZone), - writeableRevealZone(_writeableRevealZone), groupBy(CardList::NoSort), sortBy(CardList::NoSort), - isReversed(_isReversed) +ZoneViewZone::ZoneViewZone(ZoneViewZoneLogic *_logic, QGraphicsItem *parent) + : SelectZone(_logic, parent), bRect(QRectF()), minRows(0), groupBy(CardList::NoSort), sortBy(CardList::NoSort) { - if (!(revealZone && !writeableRevealZone)) { - origZone->getViews().append(this); + if (!(qobject_cast(getLogic())->getRevealZone() && + !qobject_cast(getLogic())->getWriteableRevealZone())) { + qobject_cast(getLogic())->getOriginalZone()->getViews().append(this); } + connect(_logic, &ZoneViewZoneLogic::closeView, this, &ZoneViewZone::close); +} + +void ZoneViewZone::addToViews() +{ + qobject_cast(getLogic())->getOriginalZone()->getViews().append(this); +} + +void ZoneViewZone::removeFromViews() +{ + qobject_cast(getLogic())->getOriginalZone()->getViews().removeOne(this); } /** @@ -47,8 +51,9 @@ ZoneViewZone::ZoneViewZone(Player *_p, void ZoneViewZone::close() { emit closed(); - if (!(revealZone && !writeableRevealZone)) { - origZone->getViews().removeOne(this); + if (!(qobject_cast(getLogic())->getRevealZone() && + !qobject_cast(getLogic())->getWriteableRevealZone())) { + qobject_cast(getLogic())->getOriginalZone()->getViews().removeOne(this); } deleteLater(); } @@ -67,29 +72,31 @@ void ZoneViewZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*o void ZoneViewZone::initializeCards(const QList &cardList) { + int numberCards = qobject_cast(getLogic())->getNumberCards(); if (!cardList.isEmpty()) { for (int i = 0; i < cardList.size(); ++i) { auto card = cardList[i]; CardRef cardRef = {QString::fromStdString(card->name()), QString::fromStdString(card->provider_id())}; - addCard(new CardItem(player, this, cardRef, card->id()), false, i); + getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, cardRef, card->id()), false, i); } reorganizeCards(); - } else if (!origZone->contentsKnown()) { + } else if (!qobject_cast(getLogic())->getOriginalZone()->contentsKnown()) { Command_DumpZone cmd; - cmd.set_player_id(player->getId()); - cmd.set_zone_name(name.toStdString()); + cmd.set_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_zone_name(getLogic()->getName().toStdString()); cmd.set_number_cards(numberCards); - cmd.set_is_reversed(isReversed); + cmd.set_is_reversed(qobject_cast(getLogic())->getIsReversed()); - PendingCommand *pend = player->prepareGameCommand(cmd); + PendingCommand *pend = getLogic()->getPlayer()->getPlayerActions()->prepareGameCommand(cmd); connect(pend, &PendingCommand::finished, this, &ZoneViewZone::zoneDumpReceived); - player->sendGameCommand(pend); + getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(pend); } else { - const CardList &c = origZone->getCards(); + const CardList &c = qobject_cast(getLogic())->getOriginalZone()->getCards(); int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size()); for (int i = 0; i < number; i++) { CardItem *card = c.at(i); - addCard(new CardItem(player, this, card->getCardRef(), card->getId()), false, i); + getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()), false, + i); } reorganizeCards(); } @@ -103,55 +110,21 @@ void ZoneViewZone::zoneDumpReceived(const Response &r) const ServerInfo_Card &cardInfo = resp.zone_info().card_list(i); auto cardName = QString::fromStdString(cardInfo.name()); auto cardProviderId = QString::fromStdString(cardInfo.provider_id()); - auto *card = new CardItem(player, this, {cardName, cardProviderId}, cardInfo.id(), this); - cards.insert(i, card); + auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic()); + getLogic()->rawInsertCard(card, i); } - updateCardIds(INITIALIZE); + qobject_cast(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE); reorganizeCards(); - emit cardCountChanged(); -} - -void ZoneViewZone::updateCardIds(CardAction action) -{ - if (origZone->contentsKnown()) { - return; - } - - if (cards.isEmpty()) { - return; - } - - int cardCount = cards.size(); - - auto startId = 0; - - if (isReversed) { - // the card has not been added to origZone's cardList at this point - startId = origZone->getCards().size() - cardCount; - switch (action) { - case INITIALIZE: - break; - case ADD_CARD: - startId += 1; - break; - case REMOVE_CARD: - startId -= 1; - break; - } - } - - for (int i = 0; i < cardCount; ++i) { - cards[i]->setId(i + startId); - } + emit getLogic()->cardCountChanged(); } // Because of boundingRect(), this function must not be called before the zone was added to a scene. void ZoneViewZone::reorganizeCards() { // filter cards - CardList cardsToDisplay = CardList(cards.getContentsKnown()); - for (auto card : cards) { + CardList cardsToDisplay = CardList(getLogic()->getCards().getContentsKnown()); + for (auto card : getLogic()->getCards()) { if (filterString.check(card->getCard().getCardPtr())) { card->show(); cardsToDisplay.append(card); @@ -297,122 +270,23 @@ void ZoneViewZone::setPileView(int _pileView) reorganizeCards(); } -/** - * Checks if inserting a card at the given position requires an actual new card to be created and added to the view. - * Also does any cardId updates that would be required if a card is inserted in that position. - * - * Note that this method can end up modifying the cardIds despite returning false. - * (for example, if the card is inserted into a hidden portion of the deck while the view is reversed) - * - * Make sure to call this method once before calling addCard(), so that you skip creating a new CardItem and calling - * addCard() if it's not required. - * - * @param x The position to insert the card at. - * @return Whether to proceed with calling addCard. - */ -bool ZoneViewZone::prepareAddCard(int x) -{ - bool doInsert = false; - if (!isReversed) { - if (x <= cards.size() || cards.size() == -1) { - doInsert = true; - } - } else { - // map x (which is in origZone indexes) to this viewZone's cardList index - int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId(); - int insertionIndex = x - firstId; - if (insertionIndex >= 0) { - // card was put into a portion of the deck that's in the view - doInsert = true; - } else { - // card was put into a portion of the deck that's not in the view; update ids but don't insert card - updateCardIds(ADD_CARD); - } - } - - // autoclose check is done both here and in removeCard - if (cards.isEmpty() && !doInsert && SettingsCache::instance().getCloseEmptyCardView()) { - close(); - } - - return doInsert; -} - -/** - * Make sure prepareAddCard() was called before calling addCard(). - * This method assumes we already checked that the card is being inserted into the visible portion - */ -void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/) -{ - if (!isReversed) { - // if x is negative set it to add at end - // if x is out-of-bounds then also set it to add at the end - if (x < 0 || x >= cards.size()) { - x = cards.size(); - } - cards.insert(x, card); - } else { - // map x (which is in origZone indexes) to this viewZone's cardList index - int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId(); - int insertionIndex = x - firstId; - // qMin to prevent out-of-bounds error when bottoming a card that is already in the view - cards.insert(qMin(insertionIndex, cards.size()), card); - } - - card->setParentItem(this); - card->update(); - - updateCardIds(ADD_CARD); - reorganizeCards(); -} - void ZoneViewZone::handleDropEvent(const QList &dragItems, - CardZone *startZone, + CardZoneLogic *startZone, const QPoint & /*dropPoint*/) { Command_MoveCard cmd; - cmd.set_start_player_id(startZone->getPlayer()->getId()); + cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_start_zone(startZone->getName().toStdString()); - cmd.set_target_player_id(player->getId()); - cmd.set_target_zone(getName().toStdString()); + cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); + cmd.set_target_zone(getLogic()->getName().toStdString()); cmd.set_x(0); cmd.set_y(0); - cmd.set_is_reversed(isReversed); + cmd.set_is_reversed(qobject_cast(getLogic())->getIsReversed()); for (int i = 0; i < dragItems.size(); ++i) cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); - player->sendGameCommand(cmd); -} - -void ZoneViewZone::removeCard(int position, bool toNewZone) -{ - if (isReversed) { - position -= cards.first()->getId(); - if (position < 0 || position >= cards.size()) { - updateCardIds(REMOVE_CARD); - return; - } - } - - if (position >= cards.size()) { - return; - } - - CardItem *card = cards.takeAt(position); - card->deleteLater(); - - // The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that - // card gets dragged within the view. - // Another autoclose check is done in prepareAddCard so that the view autocloses if the last card was moved to an - // unrevealed portion of the same zone. - if (cards.isEmpty() && SettingsCache::instance().getCloseEmptyCardView() && toNewZone) { - close(); - return; - } - - updateCardIds(REMOVE_CARD); - reorganizeCards(); + getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } void ZoneViewZone::setGeometry(const QRectF &rect) @@ -427,16 +301,6 @@ QSizeF ZoneViewZone::sizeHint(Qt::SizeHint /*which*/, const QSizeF & /*constrain return optimumRect.size(); } -void ZoneViewZone::setWriteableRevealZone(bool _writeableRevealZone) -{ - if (writeableRevealZone && !_writeableRevealZone) { - origZone->getViews().append(this); - } else if (!writeableRevealZone && _writeableRevealZone) { - origZone->getViews().removeOne(this); - } - writeableRevealZone = _writeableRevealZone; -} - void ZoneViewZone::wheelEvent(QGraphicsSceneWheelEvent *event) { emit wheelEventReceived(event); diff --git a/cockatrice/src/game/zones/view_zone.h b/cockatrice/src/game/zones/view_zone.h index 84fbe5ce6..f280e8794 100644 --- a/cockatrice/src/game/zones/view_zone.h +++ b/cockatrice/src/game/zones/view_zone.h @@ -2,6 +2,7 @@ #define ZONEVIEWERZONE_H #include "../filters/filter_string.h" +#include "logic/view_zone_logic.h" #include "select_zone.h" #include @@ -33,22 +34,10 @@ private: static constexpr int VERTICAL_PADDING = 5; QRectF bRect, optimumRect; - int minRows, numberCards; - CardZone *origZone; - bool revealZone, writeableRevealZone; + int minRows; FilterString filterString = FilterString(""); CardList::SortOption groupBy, sortBy; bool pileView; - bool isReversed; - - enum CardAction - { - INITIALIZE, - ADD_CARD, - REMOVE_CARD - }; - - void updateCardIds(CardAction action); struct GridSize { @@ -58,45 +47,23 @@ private: GridSize positionCardsForDisplay(CardList &cards, CardList::SortOption pileOption = CardList::NoSort); - void handleDropEvent(const QList &dragItems, CardZone *startZone, const QPoint &dropPoint) override; + void + handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override; public: - ZoneViewZone(Player *_p, - CardZone *_origZone, - int _numberCards = -1, - bool _revealZone = false, - bool _writeableRevealZone = false, - QGraphicsItem *parent = nullptr, - bool _isReversed = false); + ZoneViewZone(ZoneViewZoneLogic *_logic, QGraphicsItem *parent); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void reorganizeCards() override; void initializeCards(const QList &cardList = QList()); - bool prepareAddCard(int x); - void removeCard(int position, bool toNewZone); - int getNumberCards() const - { - return numberCards; - } void setGeometry(const QRectF &rect) override; QRectF getOptimumRect() const { return optimumRect; } - bool getRevealZone() const - { - return revealZone; - } - bool getWriteableRevealZone() const - { - return writeableRevealZone; - } - void setWriteableRevealZone(bool _writeableRevealZone); - bool getIsReversed() const - { - return isReversed; - } public slots: + void addToViews(); + void removeFromViews(); void close(); void setFilterString(const QString &_filterString); void setGroupBy(CardList::SortOption _groupBy); @@ -110,7 +77,6 @@ signals: void wheelEventReceived(QGraphicsSceneWheelEvent *event); protected: - void addCardImpl(CardItem *card, int x, int y) override; QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override; void wheelEvent(QGraphicsSceneWheelEvent *event) override; }; diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index f0430f549..dd014d8d9 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -29,7 +29,7 @@ * @param _writeableRevealZone whether the player can interact with the revealed cards. */ ZoneViewWidget::ZoneViewWidget(Player *_player, - CardZone *_origZone, + CardZoneLogic *_origZone, int numberCards, bool _revealZone, bool _writeableRevealZone, @@ -130,8 +130,9 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, vbox->addItem(zoneHBox); - zone = - new ZoneViewZone(player, _origZone, numberCards, _revealZone, _writeableRevealZone, zoneContainer, _isReversed); + zone = new ZoneViewZone(new ZoneViewZoneLogic(player, _origZone, numberCards, _revealZone, _writeableRevealZone, + _isReversed, zoneContainer), + zoneContainer); connect(zone, &ZoneViewZone::wheelEventReceived, scrollBarProxy, &ScrollableGraphicsProxyWidget::recieveWheelEvent); retranslateUi(); @@ -211,7 +212,7 @@ void ZoneViewWidget::processSetPileView(QT_STATE_CHANGED_T value) void ZoneViewWidget::retranslateUi() { - setWindowTitle(zone->getTranslatedName(false, CaseNominative)); + setWindowTitle(zone->getLogic()->getTranslatedName(false, CaseNominative)); { // We can't change the strings after they're put into the QComboBox, so this is our workaround int oldIndex = groupBySelector.currentIndex(); @@ -353,7 +354,7 @@ void ZoneViewWidget::closeEvent(QCloseEvent *event) // manually call zone->close in order to remove it from the origZones views zone->close(); if (shuffleCheckBox.isChecked()) - player->sendGameCommand(Command_Shuffle()); + player->getPlayerActions()->sendGameCommand(Command_Shuffle()); zoneDeleted(); event->accept(); } diff --git a/cockatrice/src/game/zones/view_zone_widget.h b/cockatrice/src/game/zones/view_zone_widget.h index 04662ebc9..ae282435d 100644 --- a/cockatrice/src/game/zones/view_zone_widget.h +++ b/cockatrice/src/game/zones/view_zone_widget.h @@ -2,6 +2,7 @@ #define ZONEVIEWWIDGET_H #include "../../utility/macros.h" +#include "logic/card_zone_logic.h" #include #include @@ -75,7 +76,7 @@ private slots: public: ZoneViewWidget(Player *_player, - CardZone *_origZone, + CardZoneLogic *_origZone, int numberCards = 0, bool _revealZone = false, bool _writeableRevealZone = false, diff --git a/cockatrice/src/server/chat_view/chat_view.cpp b/cockatrice/src/server/chat_view/chat_view.cpp index 069797926..2945127b7 100644 --- a/cockatrice/src/server/chat_view/chat_view.cpp +++ b/cockatrice/src/server/chat_view/chat_view.cpp @@ -23,7 +23,7 @@ UserMessagePosition::UserMessagePosition(QTextCursor &cursor) relativePosition = cursor.position() - block.position(); } -ChatView::ChatView(TabSupervisor *_tabSupervisor, TabGame *_game, bool _showTimestamps, QWidget *parent) +ChatView::ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent) : QTextBrowser(parent), tabSupervisor(_tabSupervisor), game(_game), userListProxy(_tabSupervisor->getUserListManager()), evenNumber(true), showTimestamps(_showTimestamps), hoveredItemType(HoveredNothing) diff --git a/cockatrice/src/server/chat_view/chat_view.h b/cockatrice/src/server/chat_view/chat_view.h index 02e73b789..fb601b4f1 100644 --- a/cockatrice/src/server/chat_view/chat_view.h +++ b/cockatrice/src/server/chat_view/chat_view.h @@ -12,11 +12,11 @@ #include #include +class AbstractGame; class QTextTable; class QMouseEvent; class UserContextMenu; class UserListProxy; -class TabGame; class UserMessagePosition { @@ -34,7 +34,7 @@ class ChatView : public QTextBrowser Q_OBJECT protected: TabSupervisor *const tabSupervisor; - TabGame *const game; + AbstractGame *const game; private: enum HoveredItemType @@ -83,7 +83,7 @@ private slots: void actMessageClicked(); public: - ChatView(TabSupervisor *_tabSupervisor, TabGame *_game, bool _showTimestamps, QWidget *parent = nullptr); + ChatView(TabSupervisor *_tabSupervisor, AbstractGame *_game, bool _showTimestamps, QWidget *parent = nullptr); void retranslateUi(); void appendHtml(const QString &html); void virtual appendHtmlServerMessage(const QString &html, diff --git a/cockatrice/src/server/message_log_widget.cpp b/cockatrice/src/server/message_log_widget.cpp index b29a68671..7cf8522d9 100644 --- a/cockatrice/src/server/message_log_widget.cpp +++ b/cockatrice/src/server/message_log_widget.cpp @@ -1,6 +1,7 @@ #include "message_log_widget.h" #include "../client/sound_engine.h" +#include "../client/tabs/tab_game.h" #include "../client/translate_counter_name.h" #include "../game/board/card_item.h" #include "../game/phase.h" @@ -31,7 +32,8 @@ static QString cardLink(const QString &cardName) return QString("%2").arg(cardName).arg(cardName); } -QPair MessageLogWidget::getFromStr(CardZone *zone, QString cardName, int position, bool ownerChange) +QPair +MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position, bool ownerChange) { bool cardNameContainsStartZone = false; QString fromStr; @@ -49,14 +51,14 @@ QPair MessageLogWidget::getFromStr(CardZone *zone, QString car if (position == 0) { if (cardName.isEmpty()) { if (ownerChange) { - cardName = tr("the top card of %1's library").arg(zone->getPlayer()->getName()); + cardName = tr("the top card of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName()); } else { cardName = tr("the top card of their library"); } cardNameContainsStartZone = true; } else { if (ownerChange) { - fromStr = tr(" from the top of %1's library").arg(zone->getPlayer()->getName()); + fromStr = tr(" from the top of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName()); } else { fromStr = tr(" from the top of their library"); } @@ -64,21 +66,21 @@ QPair MessageLogWidget::getFromStr(CardZone *zone, QString car } else if (position >= zone->getCards().size() - 1) { if (cardName.isEmpty()) { if (ownerChange) { - cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getName()); + cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName()); } else { cardName = tr("the bottom card of their library"); } cardNameContainsStartZone = true; } else { if (ownerChange) { - fromStr = tr(" from the bottom of %1's library").arg(zone->getPlayer()->getName()); + fromStr = tr(" from the bottom of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName()); } else { fromStr = tr(" from the bottom of their library"); } } } else { if (ownerChange) { - fromStr = tr(" from %1's library").arg(zone->getPlayer()->getName()); + fromStr = tr(" from %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName()); } else { fromStr = tr(" from their library"); } @@ -112,52 +114,59 @@ void MessageLogWidget::containerProcessingStarted(const GameEventContext &contex } } -void MessageLogWidget::logAlwaysRevealTopCard(Player *player, CardZone *zone, bool reveal) +void MessageLogWidget::logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal) { appendHtmlServerMessage((reveal ? tr("%1 is now keeping the top card %2 revealed.") : tr("%1 is not revealing the top card %2 any longer.")) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseTopCardsOfZone))); } -void MessageLogWidget::logAlwaysLookAtTopCard(Player *player, CardZone *zone, bool reveal) +void MessageLogWidget::logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal) { appendHtmlServerMessage((reveal ? tr("%1 can now look at top card %2 at any time.") : tr("%1 no longer can look at top card %2 at any time.")) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseTopCardsOfZone))); } void MessageLogWidget::logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName) { appendHtmlServerMessage(tr("%1 attaches %2 to %3's %4.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardLink(std::move(cardName))) - .arg(sanitizeHtml(targetPlayer->getName())) + .arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName())) .arg(cardLink(std::move(targetCardName)))); } -void MessageLogWidget::logConcede(Player *player) +void MessageLogWidget::logConcede(int playerId) { soundEngine->playSound("player_concede"); - appendHtmlServerMessage(tr("%1 has conceded the game.").arg(sanitizeHtml(player->getName())), true); + appendHtmlServerMessage( + tr("%1 has conceded the game.") + .arg(sanitizeHtml(game->getPlayerManager()->getPlayer(playerId)->getPlayerInfo()->getName())), + true); } -void MessageLogWidget::logUnconcede(Player *player) +void MessageLogWidget::logUnconcede(int playerId) { soundEngine->playSound("player_concede"); - appendHtmlServerMessage(tr("%1 has unconceded the game.").arg(sanitizeHtml(player->getName())), true); + appendHtmlServerMessage( + tr("%1 has unconceded the game.") + .arg(sanitizeHtml(game->getPlayerManager()->getPlayer(playerId)->getPlayerInfo()->getName())), + true); } void MessageLogWidget::logConnectionStateChanged(Player *player, bool connectionState) { if (connectionState) { soundEngine->playSound("player_reconnect"); - appendHtmlServerMessage(tr("%1 has restored connection to the game.").arg(sanitizeHtml(player->getName())), - true); + appendHtmlServerMessage( + tr("%1 has restored connection to the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName())), true); } else { soundEngine->playSound("player_disconnect"); - appendHtmlServerMessage(tr("%1 has lost connection to the game.").arg(sanitizeHtml(player->getName())), true); + appendHtmlServerMessage( + tr("%1 has lost connection to the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName())), true); } } @@ -174,44 +183,47 @@ void MessageLogWidget::logCreateArrow(Player *player, if (playerTarget) { if (player == startPlayer && player == targetPlayer) { str = tr("%1 points from their %2 to themselves."); - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())).arg(startCard)); + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(startCard)); } else if (player == startPlayer) { str = tr("%1 points from their %2 to %3."); - appendHtmlServerMessage( - str.arg(sanitizeHtml(player->getName())).arg(startCard).arg(sanitizeHtml(targetPlayer->getName()))); + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(startCard) + .arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName()))); } else if (player == targetPlayer) { str = tr("%1 points from %2's %3 to themselves."); - appendHtmlServerMessage( - str.arg(sanitizeHtml(player->getName())).arg(sanitizeHtml(startPlayer->getName())).arg(startCard)); + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName())) + .arg(startCard)); } else { str = tr("%1 points from %2's %3 to %4."); - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())) - .arg(sanitizeHtml(startPlayer->getName())) + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName())) .arg(startCard) - .arg(sanitizeHtml(targetPlayer->getName()))); + .arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName()))); } } else { if (player == startPlayer && player == targetPlayer) { str = tr("%1 points from their %2 to their %3."); - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())).arg(startCard).arg(targetCard)); + appendHtmlServerMessage( + str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(startCard).arg(targetCard)); } else if (player == startPlayer) { str = tr("%1 points from their %2 to %3's %4."); - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())) + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(startCard) - .arg(sanitizeHtml(targetPlayer->getName())) + .arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName())) .arg(targetCard)); } else if (player == targetPlayer) { str = tr("%1 points from %2's %3 to their own %4."); - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())) - .arg(sanitizeHtml(startPlayer->getName())) + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName())) .arg(startCard) .arg(targetCard)); } else { str = tr("%1 points from %2's %3 to %4's %5."); - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())) - .arg(sanitizeHtml(startPlayer->getName())) + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(sanitizeHtml(startPlayer->getPlayerInfo()->getName())) .arg(startCard) - .arg(sanitizeHtml(targetPlayer->getName())) + .arg(sanitizeHtml(targetPlayer->getPlayerInfo()->getName())) .arg(targetCard)); } } @@ -220,10 +232,11 @@ void MessageLogWidget::logCreateArrow(Player *player, void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt, bool faceDown) { if (faceDown) { - appendHtmlServerMessage(tr("%1 creates a face down token.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage( + tr("%1 creates a face down token.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } else { appendHtmlServerMessage(tr("%1 creates token: %2%3.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardLink(std::move(cardName))) .arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt)))); } @@ -232,10 +245,11 @@ void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize) { if (sideboardSize < 0) { - appendHtmlServerMessage(tr("%1 has loaded a deck (%2).").arg(sanitizeHtml(player->getName())).arg(deckHash)); + appendHtmlServerMessage( + tr("%1 has loaded a deck (%2).").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(deckHash)); } else { appendHtmlServerMessage(tr("%1 has loaded a deck with %2 sideboard cards (%3).") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + QString::number(sideboardSize) + "") .arg(deckHash)); } @@ -244,14 +258,14 @@ void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideb void MessageLogWidget::logDestroyCard(Player *player, QString cardName) { appendHtmlServerMessage( - tr("%1 destroys %2.").arg(sanitizeHtml(player->getName())).arg(cardLink(std::move(cardName)))); + tr("%1 destroys %2.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(std::move(cardName)))); } void MessageLogWidget::logMoveCard(Player *player, CardItem *card, - CardZone *startZone, + CardZoneLogic *startZone, int oldX, - CardZone *targetZone, + CardZoneLogic *targetZone, int newX) { if (currentContext == MessageContext_Mulligan) { @@ -286,8 +300,8 @@ void MessageLogWidget::logMoveCard(Player *player, if (ownerChanged && (startZone->getPlayer() == player)) { appendHtmlServerMessage(tr("%1 gives %2 control over %3.") - .arg(sanitizeHtml(player->getName())) - .arg(sanitizeHtml(targetZone->getPlayer()->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(sanitizeHtml(targetZone->getPlayer()->getPlayerInfo()->getName())) .arg(cardStr)); return; } @@ -329,7 +343,7 @@ void MessageLogWidget::logMoveCard(Player *player, finalStr = tr("%1 moves %2%3 to custom zone '%4'."); } - QString message = finalStr.arg(sanitizeHtml(player->getName()), cardStr, nameFrom.second); + QString message = finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()), cardStr, nameFrom.second); if (fourthArg.has_value()) { message = message.arg(fourthArg.value()); @@ -345,25 +359,26 @@ void MessageLogWidget::logDrawCards(Player *player, int number, bool deckIsEmpty logMulligan(player, number); } else { if (deckIsEmpty && number == 0) { - appendHtmlServerMessage(tr("%1 tries to draw from an empty library").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage( + tr("%1 tries to draw from an empty library").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } else { appendHtmlServerMessage(tr("%1 draws %2 card(s).", "", number) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + QString::number(number) + "")); } } } -void MessageLogWidget::logDumpZone(Player *player, CardZone *zone, int numberCards, bool isReversed) +void MessageLogWidget::logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed) { if (numberCards == -1) { appendHtmlServerMessage(tr("%1 is looking at %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(zone->getPlayer() == player, CaseLookAtZone))); } else { appendHtmlServerMessage( tr("%1 is looking at the %4 %3 card(s) %2.", "top card for singular, top %3 cards for plural", numberCards) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(zone->getPlayer() == player, CaseTopCardsOfZone)) .arg("" + QString::number(numberCards) + "") .arg(isReversed ? tr("bottom") : tr("top"))); @@ -374,10 +389,10 @@ void MessageLogWidget::logFlipCard(Player *player, QString cardName, bool faceDo { if (faceDown) { appendHtmlServerMessage( - tr("%1 turns %2 face-down.").arg(sanitizeHtml(player->getName())).arg(cardLink(cardName))); + tr("%1 turns %2 face-down.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(cardName))); } else { appendHtmlServerMessage( - tr("%1 turns %2 face-up.").arg(sanitizeHtml(player->getName())).arg(cardLink(cardName))); + tr("%1 turns %2 face-up.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(cardName))); } } @@ -399,7 +414,7 @@ void MessageLogWidget::logGameFlooded() void MessageLogWidget::logJoin(Player *player) { soundEngine->playSound("player_join"); - appendHtmlServerMessage(tr("%1 has joined the game.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage(tr("%1 has joined the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } void MessageLogWidget::logJoinSpectator(QString name) @@ -416,8 +431,9 @@ void MessageLogWidget::logKicked() void MessageLogWidget::logLeave(Player *player, QString reason) { soundEngine->playSound("player_leave"); - appendHtmlServerMessage( - tr("%1 has left the game (%2).").arg(sanitizeHtml(player->getName()), sanitizeHtml(std::move(reason))), true); + appendHtmlServerMessage(tr("%1 has left the game (%2).") + .arg(sanitizeHtml(player->getPlayerInfo()->getName()), sanitizeHtml(std::move(reason))), + true); } void MessageLogWidget::logLeaveSpectator(QString name, QString reason) @@ -429,7 +445,8 @@ void MessageLogWidget::logLeaveSpectator(QString name, QString reason) void MessageLogWidget::logNotReadyStart(Player *player) { - appendHtmlServerMessage(tr("%1 is not ready to start the game any more.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage( + tr("%1 is not ready to start the game any more.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } void MessageLogWidget::logMulligan(Player *player, int number) @@ -439,11 +456,11 @@ void MessageLogWidget::logMulligan(Player *player, int number) } if (number > 0) { appendHtmlServerMessage(tr("%1 shuffles their deck and draws a new hand of %2 card(s).", "", number) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(number)); } else { appendHtmlServerMessage( - tr("%1 shuffles their deck and draws a new hand.").arg(sanitizeHtml(player->getName()))); + tr("%1 shuffles their deck and draws a new hand.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } } @@ -454,11 +471,11 @@ void MessageLogWidget::logReplayStarted(int gameId) void MessageLogWidget::logReadyStart(Player *player) { - appendHtmlServerMessage(tr("%1 is ready to start the game.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage(tr("%1 is ready to start the game.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } void MessageLogWidget::logRevealCards(Player *player, - CardZone *zone, + CardZoneLogic *zone, int cardId, QString cardName, Player *otherPlayer, @@ -492,51 +509,54 @@ void MessageLogWidget::logRevealCards(Player *player, if (otherPlayer) { if (isLentToAnotherPlayer) { appendHtmlServerMessage(tr("%1 lends %2 to %3.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseRevealZone)) - .arg(sanitizeHtml(otherPlayer->getName()))); + .arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName()))); } else { appendHtmlServerMessage(tr("%1 reveals %2 to %3.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseRevealZone)) - .arg(sanitizeHtml(otherPlayer->getName()))); + .arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName()))); } } else { appendHtmlServerMessage(tr("%1 reveals %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseRevealZone))); } } else if (cardId == -2) { if (otherPlayer) { appendHtmlServerMessage(tr("%1 randomly reveals %2%3 to %4.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardStr) .arg(fromStr) - .arg(sanitizeHtml(otherPlayer->getName()))); + .arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName()))); } else { - appendHtmlServerMessage( - tr("%1 randomly reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr)); + appendHtmlServerMessage(tr("%1 randomly reveals %2%3.") + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(cardStr) + .arg(fromStr)); } } else { if (faceDown && player == otherPlayer) { if (cardName.isEmpty()) { - appendHtmlServerMessage( - tr("%1 peeks at face down card #%2.").arg(sanitizeHtml(player->getName())).arg(cardId)); + appendHtmlServerMessage(tr("%1 peeks at face down card #%2.") + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(cardId)); } else { appendHtmlServerMessage(tr("%1 peeks at face down card #%2: %3.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardId) .arg(cardStr)); } } else if (otherPlayer) { appendHtmlServerMessage(tr("%1 reveals %2%3 to %4.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardStr) .arg(fromStr) - .arg(sanitizeHtml(otherPlayer->getName()))); + .arg(sanitizeHtml(otherPlayer->getPlayerInfo()->getName()))); } else { appendHtmlServerMessage( - tr("%1 reveals %2%3.").arg(sanitizeHtml(player->getName())).arg(cardStr).arg(fromStr)); + tr("%1 reveals %2%3.").arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardStr).arg(fromStr)); } } } @@ -544,7 +564,7 @@ void MessageLogWidget::logRevealCards(Player *player, void MessageLogWidget::logReverseTurn(Player *player, bool reversed) { appendHtmlServerMessage(tr("%1 reversed turn order, now it's %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(reversed ? tr("reversed") : tr("normal"))); } @@ -555,18 +575,18 @@ void MessageLogWidget::logRollDie(Player *player, int sides, const QList & if (sides == 2) { QString coinOptions[2] = {tr("Heads") + " (1)", tr("Tails") + " (2)"}; appendHtmlServerMessage(tr("%1 flipped a coin. It landed as %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + coinOptions[roll - 1] + "")); } else { appendHtmlServerMessage(tr("%1 rolls a %2 with a %3-sided die.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + QString::number(roll) + "") .arg("" + QString::number(sides) + "")); } } else { if (sides == 2) { appendHtmlServerMessage(tr("%1 flips %2 coins. There are %3 heads and %4 tails.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + QString::number(rolls.length()) + "") .arg("" + QString::number(rolls.count(1)) + "") .arg("" + QString::number(rolls.count(2)) + "")); @@ -576,7 +596,7 @@ void MessageLogWidget::logRollDie(Player *player, int sides, const QList & rollsStrings.append(QString::number(roll)); } appendHtmlServerMessage(tr("%1 rolls a %2-sided dice %3 times: %4.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + QString::number(sides) + "") .arg("" + QString::number(rolls.length()) + "") .arg("" + rollsStrings.join(", ") + "")); @@ -587,7 +607,7 @@ void MessageLogWidget::logRollDie(Player *player, int sides, const QList & void MessageLogWidget::logSay(Player *player, QString message) { - appendMessage(std::move(message), {}, *player->getUserInfo(), true); + appendMessage(std::move(message), {}, *player->getPlayerInfo()->getUserInfo(), true); } void MessageLogWidget::logSetActivePhase(int phaseNumber) @@ -603,14 +623,14 @@ void MessageLogWidget::logSetActivePhase(int phaseNumber) void MessageLogWidget::logSetActivePlayer(Player *player) { appendHtml("
" + QDateTime::currentDateTime().toString("[hh:mm:ss] ") + - QString(tr("%1's turn.")).arg(player->getName()) + "
"); + QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "
"); } void MessageLogWidget::logSetAnnotation(Player *player, CardItem *card, QString newAnnotation) { appendHtmlServerMessage( QString(tr("%1 sets annotation of %2 to %3.")) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardLink(card->getName())) .arg(QString(""%1"").arg(sanitizeHtml(std::move(newAnnotation))))); } @@ -626,7 +646,7 @@ void MessageLogWidget::logSetCardCounter(Player *player, QString cardName, int c } auto &cardCounterSettings = SettingsCache::instance().cardCounters(); - appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getName())) + appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg("" + QString::number(delta) + "") .arg(cardCounterSettings.displayName(counterId)) .arg(cardLink(std::move(cardName))) @@ -641,7 +661,7 @@ void MessageLogWidget::logSetCounter(Player *player, QString counterName, int va QString counterDisplayName = TranslateCounterName::getDisplayName(counterName); appendHtmlServerMessage(tr("%1 sets counter %2 to %3 (%4%5).") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(QString("%1").arg(sanitizeHtml(counterDisplayName))) .arg(QString("%1").arg(value)) .arg(value > oldValue ? "+" : "") @@ -656,7 +676,7 @@ void MessageLogWidget::logSetDoesntUntap(Player *player, CardItem *card, bool do } else { str = tr("%1 sets %2 to untap normally."); } - appendHtmlServerMessage(str.arg(sanitizeHtml(player->getName())).arg(cardLink(card->getName()))); + appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName()))); } void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT) @@ -671,7 +691,7 @@ void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT) } else { name = cardLink(name); } - QString playerName = sanitizeHtml(player->getName()); + QString playerName = sanitizeHtml(player->getPlayerInfo()->getName()); if (newPT.isEmpty()) { appendHtmlServerMessage(tr("%1 removes the PT of %2.").arg(playerName).arg(name)); } else { @@ -689,9 +709,11 @@ void MessageLogWidget::logSetPT(Player *player, CardItem *card, QString newPT) void MessageLogWidget::logSetSideboardLock(Player *player, bool locked) { if (locked) { - appendHtmlServerMessage(tr("%1 has locked their sideboard.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage( + tr("%1 has locked their sideboard.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } else { - appendHtmlServerMessage(tr("%1 has unlocked their sideboard.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage( + tr("%1 has unlocked their sideboard.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } } @@ -710,15 +732,15 @@ void MessageLogWidget::logSetTapped(Player *player, CardItem *card, bool tapped) QString str; if (!card) { appendHtmlServerMessage((tapped ? tr("%1 taps their permanents.") : tr("%1 untaps their permanents.")) - .arg(sanitizeHtml(player->getName()))); + .arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } else { appendHtmlServerMessage((tapped ? tr("%1 taps %2.") : tr("%1 untaps %2.")) - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(cardLink(card->getName()))); } } -void MessageLogWidget::logShuffle(Player *player, CardZone *zone, int start, int end) +void MessageLogWidget::logShuffle(Player *player, CardZoneLogic *zone, int start, int end) { if (currentContext == MessageContext_Mulligan) { return; @@ -729,21 +751,21 @@ void MessageLogWidget::logShuffle(Player *player, CardZone *zone, int start, int // with negitive numbers counging from the bottom up. if (start == 0 && end == -1) { appendHtmlServerMessage(tr("%1 shuffles %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseShuffleZone))); } else if (start < 0 && end == -1) { appendHtmlServerMessage(tr("%1 shuffles the bottom %3 cards of %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseShuffleZone)) .arg(-start)); } else if (start < 0 && end > 0) { appendHtmlServerMessage(tr("%1 shuffles the top %3 cards of %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseShuffleZone)) .arg(end + 1)); } else { appendHtmlServerMessage(tr("%1 shuffles cards %3 - %4 of %2.") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(zone->getTranslatedName(true, CaseShuffleZone)) .arg(start) .arg(end)); @@ -757,18 +779,19 @@ void MessageLogWidget::logSpectatorSay(const ServerInfo_User &spectator, QString void MessageLogWidget::logUnattachCard(Player *player, QString cardName) { - appendHtmlServerMessage( - tr("%1 unattaches %2.").arg(sanitizeHtml(player->getName())).arg(cardLink(std::move(cardName)))); + appendHtmlServerMessage(tr("%1 unattaches %2.") + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) + .arg(cardLink(std::move(cardName)))); } void MessageLogWidget::logUndoDraw(Player *player, QString cardName) { if (cardName.isEmpty()) { - appendHtmlServerMessage(tr("%1 undoes their last draw.").arg(sanitizeHtml(player->getName()))); + appendHtmlServerMessage(tr("%1 undoes their last draw.").arg(sanitizeHtml(player->getPlayerInfo()->getName()))); } else { appendHtmlServerMessage( tr("%1 undoes their last draw (%2).") - .arg(sanitizeHtml(player->getName())) + .arg(sanitizeHtml(player->getPlayerInfo()->getName())) .arg(QString("%2").arg(sanitizeHtml(cardName)).arg(sanitizeHtml(cardName)))); } } @@ -785,34 +808,35 @@ void MessageLogWidget::appendHtmlServerMessage(const QString &html, bool optiona ChatView::appendHtmlServerMessage(messagePrefix + html + messageSuffix, optionalIsBold, optionalFontColor); } -void MessageLogWidget::connectToPlayer(Player *player) +void MessageLogWidget::connectToPlayerEventHandler(PlayerEventHandler *playerEventHandler) { - - connect(player, &Player::logSay, this, &MessageLogWidget::logSay); - connect(player, &Player::logShuffle, this, &MessageLogWidget::logShuffle); - connect(player, &Player::logRollDie, this, &MessageLogWidget::logRollDie); - connect(player, &Player::logCreateArrow, this, &MessageLogWidget::logCreateArrow); - connect(player, &Player::logCreateToken, this, &MessageLogWidget::logCreateToken); - connect(player, &Player::logSetCounter, this, &MessageLogWidget::logSetCounter); - connect(player, &Player::logSetCardCounter, this, &MessageLogWidget::logSetCardCounter); - connect(player, &Player::logSetTapped, this, &MessageLogWidget::logSetTapped); - connect(player, &Player::logSetDoesntUntap, this, &MessageLogWidget::logSetDoesntUntap); - connect(player, &Player::logSetPT, this, &MessageLogWidget::logSetPT); - connect(player, &Player::logSetAnnotation, this, &MessageLogWidget::logSetAnnotation); - connect(player, &Player::logMoveCard, this, &MessageLogWidget::logMoveCard); - connect(player, &Player::logFlipCard, this, &MessageLogWidget::logFlipCard); - connect(player, &Player::logDestroyCard, this, &MessageLogWidget::logDestroyCard); - connect(player, &Player::logAttachCard, this, &MessageLogWidget::logAttachCard); - connect(player, &Player::logUnattachCard, this, &MessageLogWidget::logUnattachCard); - connect(player, &Player::logDumpZone, this, &MessageLogWidget::logDumpZone); - connect(player, &Player::logDrawCards, this, &MessageLogWidget::logDrawCards); - connect(player, &Player::logUndoDraw, this, &MessageLogWidget::logUndoDraw); - connect(player, &Player::logRevealCards, this, &MessageLogWidget::logRevealCards); - connect(player, &Player::logAlwaysRevealTopCard, this, &MessageLogWidget::logAlwaysRevealTopCard); - connect(player, &Player::logAlwaysLookAtTopCard, this, &MessageLogWidget::logAlwaysLookAtTopCard); + connect(playerEventHandler, &PlayerEventHandler::logSay, this, &MessageLogWidget::logSay); + connect(playerEventHandler, &PlayerEventHandler::logShuffle, this, &MessageLogWidget::logShuffle); + connect(playerEventHandler, &PlayerEventHandler::logRollDie, this, &MessageLogWidget::logRollDie); + connect(playerEventHandler, &PlayerEventHandler::logCreateArrow, this, &MessageLogWidget::logCreateArrow); + connect(playerEventHandler, &PlayerEventHandler::logCreateToken, this, &MessageLogWidget::logCreateToken); + connect(playerEventHandler, &PlayerEventHandler::logSetCounter, this, &MessageLogWidget::logSetCounter); + connect(playerEventHandler, &PlayerEventHandler::logSetCardCounter, this, &MessageLogWidget::logSetCardCounter); + connect(playerEventHandler, &PlayerEventHandler::logSetTapped, this, &MessageLogWidget::logSetTapped); + connect(playerEventHandler, &PlayerEventHandler::logSetDoesntUntap, this, &MessageLogWidget::logSetDoesntUntap); + connect(playerEventHandler, &PlayerEventHandler::logSetPT, this, &MessageLogWidget::logSetPT); + connect(playerEventHandler, &PlayerEventHandler::logSetAnnotation, this, &MessageLogWidget::logSetAnnotation); + connect(playerEventHandler, &PlayerEventHandler::logMoveCard, this, &MessageLogWidget::logMoveCard); + connect(playerEventHandler, &PlayerEventHandler::logFlipCard, this, &MessageLogWidget::logFlipCard); + connect(playerEventHandler, &PlayerEventHandler::logDestroyCard, this, &MessageLogWidget::logDestroyCard); + connect(playerEventHandler, &PlayerEventHandler::logAttachCard, this, &MessageLogWidget::logAttachCard); + connect(playerEventHandler, &PlayerEventHandler::logUnattachCard, this, &MessageLogWidget::logUnattachCard); + connect(playerEventHandler, &PlayerEventHandler::logDumpZone, this, &MessageLogWidget::logDumpZone); + connect(playerEventHandler, &PlayerEventHandler::logDrawCards, this, &MessageLogWidget::logDrawCards); + connect(playerEventHandler, &PlayerEventHandler::logUndoDraw, this, &MessageLogWidget::logUndoDraw); + connect(playerEventHandler, &PlayerEventHandler::logRevealCards, this, &MessageLogWidget::logRevealCards); + connect(playerEventHandler, &PlayerEventHandler::logAlwaysRevealTopCard, this, + &MessageLogWidget::logAlwaysRevealTopCard); + connect(playerEventHandler, &PlayerEventHandler::logAlwaysLookAtTopCard, this, + &MessageLogWidget::logAlwaysLookAtTopCard); } -MessageLogWidget::MessageLogWidget(TabSupervisor *_tabSupervisor, TabGame *_game, QWidget *parent) +MessageLogWidget::MessageLogWidget(TabSupervisor *_tabSupervisor, AbstractGame *_game, QWidget *parent) : ChatView(_tabSupervisor, _game, true, parent), currentContext(MessageContext_None) { } diff --git a/cockatrice/src/server/message_log_widget.h b/cockatrice/src/server/message_log_widget.h index 12820abad..e496bb4e9 100644 --- a/cockatrice/src/server/message_log_widget.h +++ b/cockatrice/src/server/message_log_widget.h @@ -2,13 +2,15 @@ #define MESSAGELOGWIDGET_H #include "../client/translation.h" +#include "../game/zones/logic/card_zone_logic.h" #include "chat_view/chat_view.h" #include "user_level.h" -class Player; -class CardZone; -class GameEventContext; +class AbstractGame; class CardItem; +class GameEventContext; +class Player; +class PlayerEventHandler; class MessageLogWidget : public ChatView { @@ -24,16 +26,20 @@ private: MessageContext currentContext; QString messagePrefix, messageSuffix; - static QPair getFromStr(CardZone *zone, QString cardName, int position, bool ownerChange); + static QPair getFromStr(CardZoneLogic *zone, QString cardName, int position, bool ownerChange); + +public: + void connectToPlayerEventHandler(PlayerEventHandler *player); + MessageLogWidget(TabSupervisor *_tabSupervisor, AbstractGame *_game, QWidget *parent = nullptr); public slots: void containerProcessingDone(); void containerProcessingStarted(const GameEventContext &context); - void logAlwaysRevealTopCard(Player *player, CardZone *zone, bool reveal); - void logAlwaysLookAtTopCard(Player *player, CardZone *zone, bool reveal); + void logAlwaysRevealTopCard(Player *player, CardZoneLogic *zone, bool reveal); + void logAlwaysLookAtTopCard(Player *player, CardZoneLogic *zone, bool reveal); void logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName); - void logConcede(Player *player); - void logUnconcede(Player *player); + void logConcede(int playerId); + void logUnconcede(int playerId); void logConnectionStateChanged(Player *player, bool connectionState); void logCreateArrow(Player *player, Player *startPlayer, @@ -45,7 +51,7 @@ public slots: void logDeckSelect(Player *player, QString deckHash, int sideboardSize); void logDestroyCard(Player *player, QString cardName); void logDrawCards(Player *player, int number, bool deckIsEmpty); - void logDumpZone(Player *player, CardZone *zone, int numberCards, bool isReversed = false); + void logDumpZone(Player *player, CardZoneLogic *zone, int numberCards, bool isReversed = false); void logFlipCard(Player *player, QString cardName, bool faceDown); void logGameClosed(); void logGameStart(); @@ -56,12 +62,17 @@ public slots: void logLeave(Player *player, QString reason); void logLeaveSpectator(QString name, QString reason); void logNotReadyStart(Player *player); - void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX); + void logMoveCard(Player *player, + CardItem *card, + CardZoneLogic *startZone, + int oldX, + CardZoneLogic *targetZone, + int newX); void logMulligan(Player *player, int number); void logReplayStarted(int gameId); void logReadyStart(Player *player); void logRevealCards(Player *player, - CardZone *zone, + CardZoneLogic *zone, int cardId, QString cardName, Player *otherPlayer, @@ -80,7 +91,7 @@ public slots: void logSetPT(Player *player, CardItem *card, QString newPT); void logSetSideboardLock(Player *player, bool locked); void logSetTapped(Player *player, CardItem *card, bool tapped); - void logShuffle(Player *player, CardZone *zone, int start, int end); + void logShuffle(Player *player, CardZoneLogic *zone, int start, int end); void logSpectatorSay(const ServerInfo_User &spectator, QString message); void logUnattachCard(Player *player, QString cardName); void logUndoDraw(Player *player, QString cardName); @@ -88,10 +99,6 @@ public slots: void appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, QString optionalFontColor = QString()) override; - -public: - void connectToPlayer(Player *player); - MessageLogWidget(TabSupervisor *_tabSupervisor, TabGame *_game, QWidget *parent = nullptr); }; #endif diff --git a/cockatrice/src/server/user/user_context_menu.cpp b/cockatrice/src/server/user/user_context_menu.cpp index 457cc3dd5..e4df15fdf 100644 --- a/cockatrice/src/server/user/user_context_menu.cpp +++ b/cockatrice/src/server/user/user_context_menu.cpp @@ -29,7 +29,7 @@ #include #include -UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent, TabGame *_game) +UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent, AbstractGame *_game) : QObject(parent), client(_tabSupervisor->getClient()), tabSupervisor(_tabSupervisor), userListProxy(_tabSupervisor->getUserListManager()), game(_game) { @@ -380,7 +380,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos, aRemoveMessages = new QAction(tr("Remove this user's messages"), this); menu->addAction(aRemoveMessages); } - if (game && (game->getGameState()->isHost() || !tabSupervisor->getAdminLocked())) { + if (game && (game->isHost() || !tabSupervisor->getAdminLocked())) { menu->addSeparator(); menu->addAction(aKick); } diff --git a/cockatrice/src/server/user/user_context_menu.h b/cockatrice/src/server/user/user_context_menu.h index 66c8d28a2..730cfd185 100644 --- a/cockatrice/src/server/user/user_context_menu.h +++ b/cockatrice/src/server/user/user_context_menu.h @@ -5,6 +5,7 @@ #include +class AbstractGame; class UserListProxy; class AbstractClient; class ChatView; @@ -14,7 +15,6 @@ class QMenu; class QPoint; class Response; class ServerInfo_User; -class TabGame; class TabSupervisor; class UserContextMenu : public QObject @@ -24,7 +24,7 @@ private: AbstractClient *client; TabSupervisor *tabSupervisor; const UserListProxy *userListProxy; - TabGame *game; + AbstractGame *game; QAction *aUserName; QAction *aDetails; @@ -54,7 +54,7 @@ private slots: void gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer); public: - UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *_parent, TabGame *_game = 0); + UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *_parent, AbstractGame *_game = 0); void retranslateUi(); void showContextMenu(const QPoint &pos, const QString &userName,