mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-22 06:43:54 -07:00
Support creating face-down tokens (#5800)
* add new fields to proto * update token dlg * send facedown in command * update server to get it to work * disable certain edits when face down * update client event processing * log face-down token creation * Don't support colors on face-down tokens The other client doesn't know about the color, so it causes a desync * Update wording Co-authored-by: Basile Clement <Elarnon@users.noreply.github.com> * Allow annotations on face-down tokens --------- Co-authored-by: Basile Clement <Elarnon@users.noreply.github.com>
This commit is contained in:
parent
e3465be8c1
commit
bb8213deb5
10 changed files with 97 additions and 19 deletions
|
|
@ -63,6 +63,9 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
||||||
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
|
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
|
||||||
destroyCheckBox->setChecked(true);
|
destroyCheckBox->setChecked(true);
|
||||||
|
|
||||||
|
faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
|
||||||
|
connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
|
||||||
|
|
||||||
QGridLayout *grid = new QGridLayout;
|
QGridLayout *grid = new QGridLayout;
|
||||||
grid->addWidget(nameLabel, 0, 0);
|
grid->addWidget(nameLabel, 0, 0);
|
||||||
grid->addWidget(nameEdit, 0, 1);
|
grid->addWidget(nameEdit, 0, 1);
|
||||||
|
|
@ -73,6 +76,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
|
||||||
grid->addWidget(annotationLabel, 3, 0);
|
grid->addWidget(annotationLabel, 3, 0);
|
||||||
grid->addWidget(annotationEdit, 3, 1);
|
grid->addWidget(annotationEdit, 3, 1);
|
||||||
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
|
grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
|
||||||
|
grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
|
||||||
|
|
||||||
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
|
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
|
||||||
tokenDataGroupBox->setLayout(grid);
|
tokenDataGroupBox->setLayout(grid);
|
||||||
|
|
@ -155,6 +159,21 @@ void DlgCreateToken::closeEvent(QCloseEvent *event)
|
||||||
SettingsCache::instance().setTokenDialogGeometry(saveGeometry());
|
SettingsCache::instance().setTokenDialogGeometry(saveGeometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DlgCreateToken::faceDownCheckBoxToggled(bool checked)
|
||||||
|
{
|
||||||
|
if (checked) {
|
||||||
|
colorEdit->setCurrentIndex(6);
|
||||||
|
colorEdit->setEnabled(false);
|
||||||
|
ptEdit->clear();
|
||||||
|
ptEdit->clearFocus();
|
||||||
|
ptEdit->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
colorEdit->setEnabled(true);
|
||||||
|
ptEdit->setEnabled(true);
|
||||||
|
annotationEdit->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/)
|
||||||
{
|
{
|
||||||
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
|
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
|
||||||
|
|
@ -230,5 +249,6 @@ TokenInfo DlgCreateToken::getTokenInfo() const
|
||||||
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
|
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
|
||||||
.pt = ptEdit->text(),
|
.pt = ptEdit->text(),
|
||||||
.annotation = annotationEdit->text(),
|
.annotation = annotationEdit->text(),
|
||||||
.destroy = destroyCheckBox->isChecked()};
|
.destroy = destroyCheckBox->isChecked(),
|
||||||
|
.faceDown = faceDownCheckBox->isChecked()};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ struct TokenInfo
|
||||||
QString pt;
|
QString pt;
|
||||||
QString annotation;
|
QString annotation;
|
||||||
bool destroy = true;
|
bool destroy = true;
|
||||||
|
bool faceDown = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DlgCreateToken : public QDialog
|
class DlgCreateToken : public QDialog
|
||||||
|
|
@ -36,6 +37,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
private slots:
|
private slots:
|
||||||
|
void faceDownCheckBoxToggled(bool checked);
|
||||||
void tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
void tokenSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
void updateSearch(const QString &search);
|
void updateSearch(const QString &search);
|
||||||
void actChooseTokenFromAll(bool checked);
|
void actChooseTokenFromAll(bool checked);
|
||||||
|
|
@ -51,6 +53,7 @@ private:
|
||||||
QComboBox *colorEdit;
|
QComboBox *colorEdit;
|
||||||
QLineEdit *nameEdit, *ptEdit, *annotationEdit;
|
QLineEdit *nameEdit, *ptEdit, *annotationEdit;
|
||||||
QCheckBox *destroyCheckBox;
|
QCheckBox *destroyCheckBox;
|
||||||
|
QCheckBox *faceDownCheckBox;
|
||||||
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
|
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
|
||||||
CardInfoPictureWidget *pic;
|
CardInfoPictureWidget *pic;
|
||||||
QTreeView *chooseTokenView;
|
QTreeView *chooseTokenView;
|
||||||
|
|
|
||||||
|
|
@ -1855,6 +1855,7 @@ void Player::actCreateAnotherToken()
|
||||||
cmd.set_pt(lastTokenInfo.pt.toStdString());
|
cmd.set_pt(lastTokenInfo.pt.toStdString());
|
||||||
cmd.set_annotation(lastTokenInfo.annotation.toStdString());
|
cmd.set_annotation(lastTokenInfo.annotation.toStdString());
|
||||||
cmd.set_destroy_on_zone_change(lastTokenInfo.destroy);
|
cmd.set_destroy_on_zone_change(lastTokenInfo.destroy);
|
||||||
|
cmd.set_face_down(lastTokenInfo.faceDown);
|
||||||
cmd.set_x(-1);
|
cmd.set_x(-1);
|
||||||
cmd.set_y(lastTokenTableRow);
|
cmd.set_y(lastTokenTableRow);
|
||||||
|
|
||||||
|
|
@ -2233,10 +2234,10 @@ void Player::eventCreateToken(const Event_CreateToken &event)
|
||||||
|
|
||||||
CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()),
|
CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()),
|
||||||
QString::fromStdString(event.card_provider_id()), event.card_id());
|
QString::fromStdString(event.card_provider_id()), event.card_id());
|
||||||
// use db PT if not provided in event
|
// use db PT if not provided in event and not face-down
|
||||||
if (!QString::fromStdString(event.pt()).isEmpty()) {
|
if (!QString::fromStdString(event.pt()).isEmpty()) {
|
||||||
card->setPT(QString::fromStdString(event.pt()));
|
card->setPT(QString::fromStdString(event.pt()));
|
||||||
} else {
|
} else if (!event.face_down()) {
|
||||||
CardInfoPtr dbCard = card->getInfo();
|
CardInfoPtr dbCard = card->getInfo();
|
||||||
if (dbCard) {
|
if (dbCard) {
|
||||||
card->setPT(dbCard->getPowTough());
|
card->setPT(dbCard->getPowTough());
|
||||||
|
|
@ -2245,8 +2246,9 @@ void Player::eventCreateToken(const Event_CreateToken &event)
|
||||||
card->setColor(QString::fromStdString(event.color()));
|
card->setColor(QString::fromStdString(event.color()));
|
||||||
card->setAnnotation(QString::fromStdString(event.annotation()));
|
card->setAnnotation(QString::fromStdString(event.annotation()));
|
||||||
card->setDestroyOnZoneChange(event.destroy_on_zone_change());
|
card->setDestroyOnZoneChange(event.destroy_on_zone_change());
|
||||||
|
card->setFaceDown(event.face_down());
|
||||||
|
|
||||||
emit logCreateToken(this, card->getName(), card->getPT());
|
emit logCreateToken(this, card->getName(), card->getPT(), card->getFaceDown());
|
||||||
zone->addCard(card, true, event.x(), event.y());
|
zone->addCard(card, true, event.x(), event.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ signals:
|
||||||
Player *targetPlayer,
|
Player *targetPlayer,
|
||||||
QString targetCard,
|
QString targetCard,
|
||||||
bool _playerTarget);
|
bool _playerTarget);
|
||||||
void logCreateToken(Player *player, QString cardName, QString pt);
|
void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
|
||||||
void logDrawCards(Player *player, int number, bool deckIsEmpty);
|
void logDrawCards(Player *player, int number, bool deckIsEmpty);
|
||||||
void logUndoDraw(Player *player, QString cardName);
|
void logUndoDraw(Player *player, QString cardName);
|
||||||
void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX);
|
void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX);
|
||||||
|
|
|
||||||
|
|
@ -214,12 +214,16 @@ void MessageLogWidget::logCreateArrow(Player *player,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt)
|
void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt, bool faceDown)
|
||||||
{
|
{
|
||||||
appendHtmlServerMessage(tr("%1 creates token: %2%3.")
|
if (faceDown) {
|
||||||
.arg(sanitizeHtml(player->getName()))
|
appendHtmlServerMessage(tr("%1 creates a face down token.").arg(sanitizeHtml(player->getName())));
|
||||||
.arg(cardLink(std::move(cardName)))
|
} else {
|
||||||
.arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
|
appendHtmlServerMessage(tr("%1 creates token: %2%3.")
|
||||||
|
.arg(sanitizeHtml(player->getName()))
|
||||||
|
.arg(cardLink(std::move(cardName)))
|
||||||
|
.arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize)
|
void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ public slots:
|
||||||
Player *targetPlayer,
|
Player *targetPlayer,
|
||||||
QString targetCard,
|
QString targetCard,
|
||||||
bool playerTarget);
|
bool playerTarget);
|
||||||
void logCreateToken(Player *player, QString cardName, QString pt);
|
void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
|
||||||
void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
|
void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
|
||||||
void logDestroyCard(Player *player, QString cardName);
|
void logDestroyCard(Player *player, QString cardName);
|
||||||
void logDrawCards(Player *player, int number, bool deckIsEmpty);
|
void logDrawCards(Player *player, int number, bool deckIsEmpty);
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,5 @@ message Command_CreateToken {
|
||||||
optional TargetMode target_mode = 11;
|
optional TargetMode target_mode = 11;
|
||||||
|
|
||||||
optional string card_provider_id = 12;
|
optional string card_provider_id = 12;
|
||||||
|
optional bool face_down = 13;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,5 @@ message Event_CreateToken {
|
||||||
optional sint32 x = 8;
|
optional sint32 x = 8;
|
||||||
optional sint32 y = 9;
|
optional sint32 y = 9;
|
||||||
optional string card_provider_id = 10;
|
optional string card_provider_id = 10;
|
||||||
|
optional bool face_down = 11;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -386,13 +386,23 @@ void Server_Player::revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord)
|
/**
|
||||||
|
* Creates the create token event.
|
||||||
|
* By default, will set event's name and color fields to empty if the token is face-down
|
||||||
|
*/
|
||||||
|
static Event_CreateToken
|
||||||
|
makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, bool revealFacedownInfo = false)
|
||||||
{
|
{
|
||||||
Event_CreateToken event;
|
Event_CreateToken event;
|
||||||
event.set_zone_name(zone->getName().toStdString());
|
event.set_zone_name(zone->getName().toStdString());
|
||||||
event.set_card_id(card->getId());
|
event.set_card_id(card->getId());
|
||||||
event.set_card_name(card->getName().toStdString());
|
event.set_face_down(card->getFaceDown());
|
||||||
event.set_card_provider_id(card->getProviderId().toStdString());
|
|
||||||
|
if (!card->getFaceDown() || revealFacedownInfo) {
|
||||||
|
event.set_card_name(card->getName().toStdString());
|
||||||
|
event.set_card_provider_id(card->getProviderId().toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
event.set_color(card->getColor().toStdString());
|
event.set_color(card->getColor().toStdString());
|
||||||
event.set_pt(card->getPT().toStdString());
|
event.set_pt(card->getPT().toStdString());
|
||||||
event.set_annotation(card->getAnnotation().toStdString());
|
event.set_annotation(card->getAnnotation().toStdString());
|
||||||
|
|
@ -401,7 +411,6 @@ static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card
|
||||||
event.set_y(yCoord);
|
event.set_y(yCoord);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr)
|
static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr)
|
||||||
{
|
{
|
||||||
Event_AttachCard event;
|
Event_AttachCard event;
|
||||||
|
|
@ -1494,7 +1503,8 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
|
||||||
const QString cardName = nameFromStdString(cmd.card_name());
|
const QString cardName = nameFromStdString(cmd.card_name());
|
||||||
const QString cardProviderId = nameFromStdString(cmd.card_provider_id());
|
const QString cardProviderId = nameFromStdString(cmd.card_provider_id());
|
||||||
if (zone->hasCoords()) {
|
if (zone->hasCoords()) {
|
||||||
xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, false);
|
bool dontStackSameName = cmd.face_down();
|
||||||
|
xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, dontStackSameName);
|
||||||
}
|
}
|
||||||
if (xCoord < 0) {
|
if (xCoord < 0) {
|
||||||
xCoord = 0;
|
xCoord = 0;
|
||||||
|
|
@ -1505,13 +1515,17 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
|
||||||
|
|
||||||
auto *card = new Server_Card(cardName, cardProviderId, newCardId(), xCoord, yCoord);
|
auto *card = new Server_Card(cardName, cardProviderId, newCardId(), xCoord, yCoord);
|
||||||
card->moveToThread(thread());
|
card->moveToThread(thread());
|
||||||
card->setPT(nameFromStdString(cmd.pt()));
|
// Client should already prevent face-down tokens from having attributes; this just an extra server-side check
|
||||||
card->setColor(nameFromStdString(cmd.color()));
|
if (!cmd.face_down()) {
|
||||||
|
card->setColor(nameFromStdString(cmd.color()));
|
||||||
|
card->setPT(nameFromStdString(cmd.pt()));
|
||||||
|
}
|
||||||
card->setAnnotation(nameFromStdString(cmd.annotation()));
|
card->setAnnotation(nameFromStdString(cmd.annotation()));
|
||||||
card->setDestroyOnZoneChange(cmd.destroy_on_zone_change());
|
card->setDestroyOnZoneChange(cmd.destroy_on_zone_change());
|
||||||
|
card->setFaceDown(cmd.face_down());
|
||||||
|
|
||||||
zone->insertCard(card, xCoord, yCoord);
|
zone->insertCard(card, xCoord, yCoord);
|
||||||
ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId);
|
sendCreateTokenEvents(zone, card, xCoord, yCoord, ges);
|
||||||
|
|
||||||
// check if the token is a replacement for an existing card
|
// check if the token is a replacement for an existing card
|
||||||
if (!targetCard) {
|
if (!targetCard) {
|
||||||
|
|
@ -1642,6 +1656,38 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and sends the events required to properly communicate the given token creation.
|
||||||
|
* Primarily written to handle creating face-down tokens.
|
||||||
|
*/
|
||||||
|
void Server_Player::sendCreateTokenEvents(Server_CardZone *zone,
|
||||||
|
Server_Card *card,
|
||||||
|
int xCoord,
|
||||||
|
int yCoord,
|
||||||
|
GameEventStorage &ges)
|
||||||
|
{
|
||||||
|
// Token is not face-down; things are easy
|
||||||
|
if (!card->getFaceDown()) {
|
||||||
|
ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is face-down. We have to send different info to each player
|
||||||
|
auto eventOthers = makeCreateTokenEvent(zone, card, xCoord, yCoord, false);
|
||||||
|
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
|
||||||
|
|
||||||
|
auto eventPrivate = makeCreateTokenEvent(zone, card, xCoord, yCoord, true);
|
||||||
|
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
|
||||||
|
|
||||||
|
// Event_CreateToken didn't use to have face_down field; send attribute event afterward for backwards compatibility
|
||||||
|
Event_SetCardAttr event;
|
||||||
|
event.set_zone_name(zone->getName().toStdString());
|
||||||
|
event.set_card_id(card->getId());
|
||||||
|
event.set_attribute(AttrFaceDown);
|
||||||
|
event.set_attr_value("1");
|
||||||
|
ges.enqueueGameEvent(event, playerId);
|
||||||
|
}
|
||||||
|
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ private:
|
||||||
bool conceded;
|
bool conceded;
|
||||||
bool sideboardLocked;
|
bool sideboardLocked;
|
||||||
void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges);
|
void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges);
|
||||||
|
void sendCreateTokenEvents(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, GameEventStorage &ges);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mutable QMutex playerMutex;
|
mutable QMutex playerMutex;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue