diff --git a/cockatrice/src/filter_string.cpp b/cockatrice/src/filter_string.cpp index e412ae5a..e45108cf 100644 --- a/cockatrice/src/filter_string.cpp +++ b/cockatrice/src/filter_string.cpp @@ -3,6 +3,7 @@ #include "../../common/lib/peglib.h" #include +#include #include #include @@ -334,6 +335,12 @@ static void setupParserRules() }; } +FilterString::FilterString() +{ + result = [](CardData) -> bool { return false; }; + _error = "Not initialized"; +} + FilterString::FilterString(const QString &expr) { QByteArray ba = expr.simplified().toUtf8(); @@ -352,7 +359,7 @@ FilterString::FilterString(const QString &expr) }; if (!search.parse(ba.data(), result)) { - std::cout << "Error!" << _error.toStdString() << std::endl; + qDebug() << "Filter string error" << _error; result = [](CardData) -> bool { return false; }; } } diff --git a/cockatrice/src/filter_string.h b/cockatrice/src/filter_string.h index 690ff2d7..2a29ed2a 100644 --- a/cockatrice/src/filter_string.h +++ b/cockatrice/src/filter_string.h @@ -24,6 +24,7 @@ typedef AstBase Ast; class FilterString { public: + FilterString(); explicit FilterString(const QString &exp); bool check(const CardData &card) { diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index cd64dadf..05918c30 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -66,10 +66,12 @@ #include #include +#include #include #include #include #include +#include PlayerArea::PlayerArea(QGraphicsItem *parentItem) : QObject(), QGraphicsItem(parentItem) { @@ -253,6 +255,8 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T connect(aMoveTopCardsToGraveyard, SIGNAL(triggered()), this, SLOT(actMoveTopCardsToGrave())); aMoveTopCardsToExile = new QAction(this); connect(aMoveTopCardsToExile, SIGNAL(triggered()), this, SLOT(actMoveTopCardsToExile())); + aMoveTopCardsUntil = new QAction(this); + connect(aMoveTopCardsUntil, SIGNAL(triggered()), this, SLOT(actMoveTopCardsUntil())); aMoveTopCardToBottom = new QAction(this); connect(aMoveTopCardToBottom, SIGNAL(triggered()), this, SLOT(actMoveTopCardToBottom())); @@ -325,6 +329,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T topLibraryMenu->addAction(aMoveTopCardsToGraveyard); topLibraryMenu->addAction(aMoveTopCardToExile); topLibraryMenu->addAction(aMoveTopCardsToExile); + topLibraryMenu->addAction(aMoveTopCardsUntil); bottomLibraryMenu->addAction(aDrawBottomCard); bottomLibraryMenu->addAction(aDrawBottomCards); @@ -754,11 +759,12 @@ void Player::retranslateUi() 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...")); - aMoveTopCardToBottom->setText(tr("Put top card on &bottom")); + aMoveTopCardsUntil->setText(tr("Take top cards &until...")); aDrawBottomCard->setText(tr("&Draw bottom card")); aDrawBottomCards->setText(tr("D&raw bottom cards...")); @@ -938,6 +944,7 @@ void Player::setShortcutsActive() aMoveTopCardsToGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardsToGraveyard")); aMoveTopCardToExile->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardToExile")); aMoveTopCardsToExile->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardsToExile")); + aMoveTopCardsUntil->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardsUntil")); aMoveTopCardToBottom->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardToBottom")); aDrawBottomCard->setShortcut(shortcuts.getSingleShortcut("Player/aDrawBottomCard")); aDrawBottomCards->setShortcut(shortcuts.getSingleShortcut("Player/aDrawBottomCards")); @@ -978,6 +985,7 @@ void Player::setShortcutsInactive() aMoveTopCardsToGraveyard->setShortcut(QKeySequence()); aMoveTopCardToExile->setShortcut(QKeySequence()); aMoveTopCardsToExile->setShortcut(QKeySequence()); + aMoveTopCardsUntil->setShortcut(QKeySequence()); aDrawBottomCard->setShortcut(QKeySequence()); aDrawBottomCards->setShortcut(QKeySequence()); aMoveBottomToPlay->setShortcut(QKeySequence()); @@ -1291,6 +1299,45 @@ void Player::actMoveTopCardsToExile() sendGameCommand(cmd); } +void Player::actMoveTopCardsUntil() +{ + QString expr = previousMovingCardsUntilExpr; + for (;;) { + bool ok; + expr = + QInputDialog::getText(game, "Take top cards until", "Select card (accepts search syntax)", {}, expr, &ok); + if (!ok) { + return; + } + movingCardsUntilFilter = FilterString(expr); + if (movingCardsUntilFilter.valid()) { + break; + } else { + auto button = QMessageBox::warning(game, "Invalid filter", movingCardsUntilFilter.error()); + if (button != QMessageBox::Ok) { + return; + } + } + } + previousMovingCardsUntilExpr = expr; + if (zones.value("deck")->getCards().empty()) { + movingCardsUntil = false; + } else { + movingCardsUntil = true; + actMoveTopCardToPlay(); + } +} + +void Player::moveOneCardUntil(const QString &cardName) +{ + auto card = db->getCard(cardName); + if (zones.value("deck")->getCards().empty() || card.isNull() || movingCardsUntilFilter.check(card)) { + movingCardsUntil = false; + } else { + QTimer::singleShot(100, [this]() { actMoveTopCardToPlay(); }); + } +} + void Player::actMoveTopCardToBottom() { if (zones.value("deck")->getCards().empty()) { @@ -2041,7 +2088,8 @@ void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext & if (!startPlayer) { return; } - CardZone *startZone = startPlayer->getZones().value(QString::fromStdString(event.start_zone()), 0); + QString startZoneString = QString::fromStdString(event.start_zone()); + CardZone *startZone = startPlayer->getZones().value(startZoneString, 0); Player *targetPlayer = game->getPlayers().value(event.target_player_id()); if (!targetPlayer) { return; @@ -2133,6 +2181,10 @@ void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext & } } updateCardMenu(card); + + if (movingCardsUntil && startZoneString == "deck" && targetZone->getName() == "stack") { + moveOneCardUntil(card->getName()); + } } void Player::eventFlipCard(const Event_FlipCard &event) diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index 7f14b7d5..96e2ba95 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -3,6 +3,7 @@ #include "abstractgraphicsitem.h" #include "carddatabase.h" +#include "filter_string.h" #include "pb/card_attributes.pb.h" #include "pb/game_event.pb.h" #include "tearoffmenu.h" @@ -162,6 +163,7 @@ public slots: void actMoveTopCardToExile(); void actMoveTopCardsToGrave(); void actMoveTopCardsToExile(); + void actMoveTopCardsUntil(); void actMoveTopCardToBottom(); void actDrawBottomCard(); void actDrawBottomCards(); @@ -233,12 +235,12 @@ private: *aMoveGraveToTopLibrary, *aMoveGraveToBottomLibrary, *aMoveGraveToHand, *aMoveGraveToRfg, *aMoveRfgToTopLibrary, *aMoveRfgToBottomLibrary, *aMoveRfgToHand, *aMoveRfgToGrave, *aViewHand, *aViewLibrary, *aViewTopCards, *aAlwaysRevealTopCard, *aAlwaysLookAtTopCard, *aOpenDeckInDeckEditor, *aMoveTopCardToGraveyard, - *aMoveTopCardToExile, *aMoveTopCardsToGraveyard, *aMoveTopCardsToExile, *aMoveTopCardToBottom, *aViewGraveyard, - *aViewRfg, *aViewSideboard, *aDrawCard, *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aMoveTopToPlay, - *aMoveTopToPlayFaceDown, *aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken, *aCardMenu, - *aMoveBottomToPlay, *aMoveBottomToPlayFaceDown, *aMoveBottomCardToTop, *aMoveBottomCardToGraveyard, - *aMoveBottomCardToExile, *aMoveBottomCardsToGraveyard, *aMoveBottomCardsToExile, *aDrawBottomCard, - *aDrawBottomCards; + *aMoveTopCardToExile, *aMoveTopCardsToGraveyard, *aMoveTopCardsToExile, *aMoveTopCardsUntil, + *aMoveTopCardToBottom, *aViewGraveyard, *aViewRfg, *aViewSideboard, *aDrawCard, *aDrawCards, *aUndoDraw, + *aMulligan, *aShuffle, *aMoveTopToPlay, *aMoveTopToPlayFaceDown, *aUntapAll, *aRollDie, *aCreateToken, + *aCreateAnotherToken, *aCardMenu, *aMoveBottomToPlay, *aMoveBottomToPlayFaceDown, *aMoveBottomCardToTop, + *aMoveBottomCardToGraveyard, *aMoveBottomCardToExile, *aMoveBottomCardsToGraveyard, *aMoveBottomCardsToExile, + *aDrawBottomCard, *aDrawBottomCards; QList aAddCounter, aSetCounter, aRemoveCounter; QAction *aPlay, *aPlayFacedown, *aHide, *aTap, *aDoesntUntap, *aAttach, *aUnattach, *aDrawArrow, *aSetPT, *aResetPT, @@ -246,6 +248,10 @@ private: *aMoveToTopLibrary, *aMoveToBottomLibrary, *aMoveToHand, *aMoveToGraveyard, *aMoveToExile, *aMoveToXfromTopOfLibrary; + bool movingCardsUntil; + QString previousMovingCardsUntilExpr = {}; + FilterString movingCardsUntilFilter; + bool shortcutsActive; int defaultNumberTopCards = 1; int defaultNumberTopCardsToPlaceBelow = 1; @@ -292,6 +298,7 @@ private: CardRelation::AttachType attach = CardRelation::DoesNotAttach, bool persistent = false); bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); + void moveOneCardUntil(const QString &cardName); QRectF bRect; diff --git a/cockatrice/src/shortcutssettings.h b/cockatrice/src/shortcutssettings.h index b7662394..e3a2e896 100644 --- a/cockatrice/src/shortcutssettings.h +++ b/cockatrice/src/shortcutssettings.h @@ -499,6 +499,9 @@ private: {"Player/aMoveTopCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), parseSequenceString(""), ShortcutGroup::Move_top)}, + {"Player/aMoveTopCardsUntil", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Stack"), + parseSequenceString("Ctrl+Shift+Y"), + ShortcutGroup::Move_top)}, {"Player/aMoveTopCardToBottom", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"), parseSequenceString(""), ShortcutGroup::Move_top)},