Always look at top card (#4238)

* Add option to always look at top card of deck

Similar to "always reveal", but reveals card only to the owner,
not all players.

* Add option to always look at top card of deck

Similar to "always reveal", but reveals card only to the owner,
not all players.

* Update bug_report.md (#4246)

* Update bug_report.md

* reproduction steps

* Update to address review comments

* Clangify

* set playerId on dumpEvent

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
Co-authored-by: Zach H <zahalpern+github@gmail.com>
This commit is contained in:
omegaula 2021-03-13 14:54:13 -05:00 committed by GitHub
parent 00ed5c370c
commit 073349fd05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 120 additions and 54 deletions

View file

@ -152,6 +152,14 @@ void MessageLogWidget::logAlwaysRevealTopCard(Player *player, CardZone *zone, bo
.arg(zone->getTranslatedName(true, CaseTopCardsOfZone)));
}
void MessageLogWidget::logAlwaysLookAtTopCard(Player *player, CardZone *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(zone->getTranslatedName(true, CaseTopCardsOfZone)));
}
void MessageLogWidget::logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName)
{
appendHtmlServerMessage(tr("%1 attaches %2 to %3's %4.")
@ -823,6 +831,8 @@ void MessageLogWidget::connectToPlayer(Player *player)
SLOT(logRevealCards(Player *, CardZone *, int, QString, Player *, bool, int)));
connect(player, SIGNAL(logAlwaysRevealTopCard(Player *, CardZone *, bool)), this,
SLOT(logAlwaysRevealTopCard(Player *, CardZone *, bool)));
connect(player, SIGNAL(logAlwaysLookAtTopCard(Player *, CardZone *, bool)), this,
SLOT(logAlwaysLookAtTopCard(Player *, CardZone *, bool)));
}
MessageLogWidget::MessageLogWidget(const TabSupervisor *_tabSupervisor,

View file

@ -42,6 +42,7 @@ 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 logAttachCard(Player *player, QString cardName, Player *targetPlayer, QString targetCardName);
void logConcede(Player *player);
void logUnconcede(Player *player);

View file

@ -211,6 +211,9 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
aAlwaysRevealTopCard = new QAction(this);
aAlwaysRevealTopCard->setCheckable(true);
connect(aAlwaysRevealTopCard, SIGNAL(triggered()), this, SLOT(actAlwaysRevealTopCard()));
aAlwaysLookAtTopCard = new QAction(this);
aAlwaysLookAtTopCard->setCheckable(true);
connect(aAlwaysLookAtTopCard, SIGNAL(triggered()), this, SLOT(actAlwaysLookAtTopCard()));
aOpenDeckInDeckEditor = new QAction(this);
aOpenDeckInDeckEditor->setEnabled(false);
connect(aOpenDeckInDeckEditor, SIGNAL(triggered()), this, SLOT(actOpenDeckInDeckEditor()));
@ -287,6 +290,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
playerLists.append(mRevealLibrary = libraryMenu->addMenu(QString()));
playerLists.append(mRevealTopCard = libraryMenu->addMenu(QString()));
libraryMenu->addAction(aAlwaysRevealTopCard);
libraryMenu->addAction(aAlwaysLookAtTopCard);
libraryMenu->addSeparator();
libraryMenu->addAction(aMoveTopToPlay);
libraryMenu->addAction(aMoveTopToPlayFaceDown);
@ -697,6 +701,7 @@ void Player::retranslateUi()
mRevealLibrary->setTitle(tr("Reveal &library to..."));
mRevealTopCard->setTitle(tr("Reveal t&op cards to..."));
aAlwaysRevealTopCard->setText(tr("&Always reveal top card"));
aAlwaysLookAtTopCard->setText(tr("Al&ways look at top card"));
aOpenDeckInDeckEditor->setText(tr("O&pen deck in deck editor"));
aViewSideboard->setText(tr("&View sideboard"));
aDrawCard->setText(tr("&Draw card"));
@ -874,6 +879,7 @@ void Player::setShortcutsActive()
aCreateToken->setShortcut(shortcuts.getSingleShortcut("Player/aCreateToken"));
aCreateAnotherToken->setShortcut(shortcuts.getSingleShortcut("Player/aCreateAnotherToken"));
aAlwaysRevealTopCard->setShortcut(shortcuts.getSingleShortcut("Player/aAlwaysRevealTopCard"));
aAlwaysLookAtTopCard->setShortcut(shortcuts.getSingleShortcut("Player/aAlwaysLookAtTopCard"));
aMoveTopToPlay->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopToPlay"));
aMoveTopToPlayFaceDown->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopToPlayFaceDown"));
aMoveTopCardToGraveyard->setShortcut(shortcuts.getSingleShortcut("Player/aMoveTopCardToGraveyard"));
@ -905,6 +911,7 @@ void Player::setShortcutsInactive()
aCreateToken->setShortcut(QKeySequence());
aCreateAnotherToken->setShortcut(QKeySequence());
aAlwaysRevealTopCard->setShortcut(QKeySequence());
aAlwaysLookAtTopCard->setShortcut(QKeySequence());
aMoveTopToPlay->setShortcut(QKeySequence());
aMoveTopToPlayFaceDown->setShortcut(QKeySequence());
aMoveTopCardToGraveyard->setShortcut(QKeySequence());
@ -991,6 +998,15 @@ void Player::actAlwaysRevealTopCard()
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);
@ -1976,6 +1992,10 @@ void Player::eventChangeZoneProperties(const Event_ChangeZoneProperties &event)
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)
@ -3271,6 +3291,7 @@ void Player::setGameStarted()
{
if (local) {
aAlwaysRevealTopCard->setChecked(false);
aAlwaysLookAtTopCard->setChecked(false);
}
setConceded(false);
}

View file

@ -144,6 +144,7 @@ signals:
bool faceDown,
int amount);
void logAlwaysRevealTopCard(Player *player, CardZone *zone, bool reveal);
void logAlwaysLookAtTopCard(Player *player, CardZone *zone, bool reveal);
void sizeChanged();
void playerCountChanged();
@ -170,6 +171,7 @@ public slots:
void actViewHand();
void actViewTopCards();
void actAlwaysRevealTopCard();
void actAlwaysLookAtTopCard();
void actViewGraveyard();
void actRevealRandomGraveyardCard();
void actViewRfg();
@ -221,9 +223,9 @@ private:
QAction *aMoveHandToTopLibrary, *aMoveHandToBottomLibrary, *aMoveHandToGrave, *aMoveHandToRfg,
*aMoveGraveToTopLibrary, *aMoveGraveToBottomLibrary, *aMoveGraveToHand, *aMoveGraveToRfg, *aMoveRfgToTopLibrary,
*aMoveRfgToBottomLibrary, *aMoveRfgToHand, *aMoveRfgToGrave, *aViewHand, *aViewLibrary, *aViewTopCards,
*aAlwaysRevealTopCard, *aOpenDeckInDeckEditor, *aMoveTopCardToGraveyard, *aMoveTopCardToExile,
*aMoveTopCardsToGraveyard, *aMoveTopCardsToExile, *aMoveTopCardToBottom, *aViewGraveyard, *aViewRfg,
*aViewSideboard, *aDrawCard, *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aMoveTopToPlay,
*aAlwaysRevealTopCard, *aAlwaysLookAtTopCard, *aOpenDeckInDeckEditor, *aMoveTopCardToGraveyard,
*aMoveTopCardToExile, *aMoveTopCardsToGraveyard, *aMoveTopCardsToExile, *aMoveTopCardToBottom, *aViewGraveyard,
*aViewRfg, *aViewSideboard, *aDrawCard, *aDrawCards, *aUndoDraw, *aMulligan, *aShuffle, *aMoveTopToPlay,
*aMoveTopToPlayFaceDown, *aUntapAll, *aRollDie, *aCreateToken, *aCreateAnotherToken, *aCardMenu,
*aMoveBottomCardToGrave;

View file

@ -537,6 +537,9 @@ private:
{"Player/aAlwaysRevealTopCard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Always Reveal Top Card"),
parseSequenceString("Ctrl+N"),
ShortcutGroup::Drawing)},
{"Player/aAlwaysLookAtTopCard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Always Look At Top Card"),
parseSequenceString("Ctrl+A"),
ShortcutGroup::Drawing)},
{"Player/aRotateViewCW", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Rotate View Clockwise"),
parseSequenceString(""),
ShortcutGroup::Gameplay)},

View file

@ -7,5 +7,8 @@ message Command_ChangeZoneProperties {
}
optional string zone_name = 1;
// Reveal top card to all players.
optional bool always_reveal_top_card = 10;
// reveal top card to the owner.
optional bool always_look_at_top_card = 11;
}

View file

@ -7,5 +7,8 @@ message Event_ChangeZoneProperties {
}
optional string zone_name = 1;
// Reveal top card to all players.
optional bool always_reveal_top_card = 10;
// reveal top card to the owner.
optional bool always_look_at_top_card = 11;
}

View file

@ -21,5 +21,8 @@ message ServerInfo_Zone {
optional bool with_coords = 3;
optional sint32 card_count = 4;
repeated ServerInfo_Card card_list = 5;
// Reveal top card to all players.
optional bool always_reveal_top_card = 10;
// reveal top card to the owner.
optional bool always_look_at_top_card = 11;
}

View file

@ -32,7 +32,7 @@ Server_CardZone::Server_CardZone(Server_Player *_player,
bool _has_coords,
ServerInfo_Zone::ZoneType _type)
: player(_player), name(_name), has_coords(_has_coords), type(_type), cardsBeingLookedAt(0),
alwaysRevealTopCard(false)
alwaysRevealTopCard(false), alwaysLookAtTopCard(false)
{
}
@ -305,6 +305,7 @@ void Server_CardZone::getInfo(ServerInfo_Zone *info, Server_Player *playerWhosAs
info->set_with_coords(has_coords);
info->set_card_count(cards.size());
info->set_always_reveal_top_card(alwaysRevealTopCard);
info->set_always_look_at_top_card(alwaysLookAtTopCard);
if ((((playerWhosAsking == player) || omniscient) && (type != ServerInfo_Zone::HiddenZone)) ||
((playerWhosAsking != player) && (type == ServerInfo_Zone::PublicZone))) {
QListIterator<Server_Card *> cardIterator(cards);

View file

@ -42,6 +42,7 @@ private:
int cardsBeingLookedAt;
QSet<int> playersWithWritePermission;
bool alwaysRevealTopCard;
bool alwaysLookAtTopCard;
QList<Server_Card *> cards;
QMap<int, QMap<int, Server_Card *>> coordinateMap; // y -> (x -> card)
QMap<int, QMultiMap<QString, int>> freePilesMap; // y -> (cardName -> x)
@ -108,6 +109,14 @@ public:
{
alwaysRevealTopCard = _alwaysRevealTopCard;
}
bool getAlwaysLookAtTopCard() const
{
return alwaysLookAtTopCard;
}
void setAlwaysLookAtTopCard(bool _alwaysLookAtTopCard)
{
alwaysLookAtTopCard = _alwaysLookAtTopCard;
}
};
#endif

View file

@ -318,18 +318,41 @@ Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int numbe
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(deckZone->getName().toStdString());
revealEvent.set_card_id(0);
deckZone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
revealTopCardIfNeeded(deckZone, ges);
return Response::RespOk;
}
void Server_Player::revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges)
{
if (zone->getCards().isEmpty()) {
return;
}
if (zone->getAlwaysRevealTopCard()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(zone->getName().toStdString());
revealEvent.set_card_id(0);
zone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
return;
}
if (zone->getAlwaysLookAtTopCard()) {
Event_DumpZone dumpEvent;
dumpEvent.set_zone_owner_id(playerId);
dumpEvent.set_zone_name(zone->getName().toStdString());
dumpEvent.set_number_cards(1);
ges.enqueueGameEvent(dumpEvent, playerId, GameEventStorageItem::SendToOthers);
Event_RevealCards revealEvent;
revealEvent.set_zone_name(zone->getName().toStdString());
revealEvent.set_number_of_cards(1);
revealEvent.set_card_id(0);
zone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId, GameEventStorageItem::SendToPrivate, playerId);
}
}
class Server_Player::MoveCardCompareFunctor
{
private:
@ -594,21 +617,11 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
setCardAttrHelper(ges, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), card->getId(),
AttrPT, ptString);
}
if (startzone->getAlwaysRevealTopCard() && !startzone->getCards().isEmpty() && (originalPosition == 0)) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(startzone->getName().toStdString());
revealEvent.set_card_id(0);
startzone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
if (originalPosition == 0) {
revealTopCardIfNeeded(startzone, ges);
}
if (targetzone->getAlwaysRevealTopCard() && !targetzone->getCards().isEmpty() && (newX == 0)) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(targetzone->getName().toStdString());
revealEvent.set_card_id(0);
targetzone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
if (newX == 0) {
revealTopCardIfNeeded(targetzone, ges);
}
}
}
@ -977,15 +990,7 @@ Server_Player::cmdShuffle(const Command_Shuffle &cmd, ResponseContainer & /*rc*/
event.set_start(cmd.start());
event.set_end(cmd.end());
ges.enqueueGameEvent(event, playerId);
if (zone->getAlwaysRevealTopCard() && !zone->getCards().isEmpty()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(zone->getName().toStdString());
revealEvent.set_card_id(0);
zone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
revealTopCardIfNeeded(zone, ges);
return Response::RespOk;
}
@ -1980,27 +1985,31 @@ Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_Chan
Event_ChangeZoneProperties event;
event.set_zone_name(cmd.zone_name());
if (cmd.has_always_reveal_top_card()) {
if (zone->getAlwaysRevealTopCard() == cmd.always_reveal_top_card()) {
// Neither value set -> error.
if (!cmd.has_always_look_at_top_card() && !cmd.has_always_reveal_top_card()) {
return Response::RespContextError;
}
// Neither value changed -> error.
bool alwaysRevealChanged =
cmd.has_always_reveal_top_card() && zone->getAlwaysRevealTopCard() != cmd.always_reveal_top_card();
bool alwaysLookAtTopChanged =
cmd.has_always_look_at_top_card() && zone->getAlwaysLookAtTopCard() != cmd.always_look_at_top_card();
if (!alwaysRevealChanged && !alwaysLookAtTopChanged) {
return Response::RespContextError;
}
if (cmd.has_always_reveal_top_card()) {
zone->setAlwaysRevealTopCard(cmd.always_reveal_top_card());
event.set_always_reveal_top_card(cmd.always_reveal_top_card());
}
if (cmd.has_always_look_at_top_card()) {
zone->setAlwaysLookAtTopCard(cmd.always_look_at_top_card());
event.set_always_look_at_top_card(cmd.always_look_at_top_card());
}
ges.enqueueGameEvent(event, playerId);
if (!zone->getCards().isEmpty() && cmd.always_reveal_top_card()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(zone->getName().toStdString());
revealEvent.set_card_id(0);
zone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
revealTopCardIfNeeded(zone, ges);
return Response::RespOk;
} else {
return Response::RespContextError;
}
}
Response::ResponseCode

View file

@ -84,6 +84,7 @@ private:
bool readyStart;
bool conceded;
bool sideboardLocked;
void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges);
public:
mutable QMutex playerMutex;