mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-15 11:38:49 -07:00
Better support Double-Faced Cards (#4753)
* Better support Double-Faced Cards This patch allows cards to be (virtually) transformed into other cards while preserving their state, essentially implemeting the MTG mechanic of the same name. On the server side, this is implemented by allowing cards to be "stashed away". A card that is stashed away is not in any zone, but is instead owned by another card. When a token is destroyed due to a zone change, if it had a card stashed away, that card is placed in the target zone instead of the token. On the database side, `attach="transform"` is used on `<reverse>` and `<reverse-related>` to indicate that the created token should be transformed this way. Old servers ignore the new field in `Command_CreateToken` and will perform a regular attachment, as currently. * Address review comments * Prevent tokens from being stashed * format.sh
This commit is contained in:
parent
4558b1c7ef
commit
42e7a8b423
11 changed files with 300 additions and 83 deletions
|
|
@ -588,7 +588,7 @@ void CardDatabase::refreshCachedReverseRelatedCards()
|
|||
}
|
||||
|
||||
auto *newCardRelation = new CardRelation(
|
||||
card->getName(), cardRelation->getDoesAttach(), cardRelation->getIsCreateAllExclusion(),
|
||||
card->getName(), cardRelation->getAttachType(), cardRelation->getIsCreateAllExclusion(),
|
||||
cardRelation->getIsVariable(), cardRelation->getDefaultCount(), cardRelation->getIsPersistent());
|
||||
cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation);
|
||||
}
|
||||
|
|
@ -672,12 +672,12 @@ bool CardDatabase::saveCustomTokensToFile()
|
|||
}
|
||||
|
||||
CardRelation::CardRelation(const QString &_name,
|
||||
bool _doesAttach,
|
||||
AttachType _attachType,
|
||||
bool _isCreateAllExclusion,
|
||||
bool _isVariableCount,
|
||||
int _defaultCount,
|
||||
bool _isPersistent)
|
||||
: name(_name), doesAttach(_doesAttach), isCreateAllExclusion(_isCreateAllExclusion),
|
||||
: name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion),
|
||||
isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,9 +452,17 @@ signals:
|
|||
class CardRelation : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum AttachType
|
||||
{
|
||||
DoesNotAttach = 0,
|
||||
AttachTo = 1,
|
||||
TransformInto = 2,
|
||||
};
|
||||
|
||||
private:
|
||||
QString name;
|
||||
bool doesAttach;
|
||||
AttachType attachType;
|
||||
bool isCreateAllExclusion;
|
||||
bool isVariableCount;
|
||||
int defaultCount;
|
||||
|
|
@ -462,7 +470,7 @@ private:
|
|||
|
||||
public:
|
||||
explicit CardRelation(const QString &_name = QString(),
|
||||
bool _doesAttach = false,
|
||||
AttachType _attachType = DoesNotAttach,
|
||||
bool _isCreateAllExclusion = false,
|
||||
bool _isVariableCount = false,
|
||||
int _defaultCount = 1,
|
||||
|
|
@ -472,13 +480,32 @@ public:
|
|||
{
|
||||
return name;
|
||||
}
|
||||
AttachType getAttachType() const
|
||||
{
|
||||
return attachType;
|
||||
}
|
||||
bool getDoesAttach() const
|
||||
{
|
||||
return doesAttach;
|
||||
return attachType != DoesNotAttach;
|
||||
}
|
||||
bool getDoesTransform() const
|
||||
{
|
||||
return attachType == TransformInto;
|
||||
}
|
||||
QString getAttachTypeAsString() const
|
||||
{
|
||||
switch (attachType) {
|
||||
case AttachTo:
|
||||
return "attach";
|
||||
case TransformInto:
|
||||
return "transform";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
bool getCanCreateAnother() const
|
||||
{
|
||||
return !doesAttach;
|
||||
return !getDoesAttach();
|
||||
}
|
||||
bool getIsCreateAllExclusion() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
sets.insert(setName, setInfo);
|
||||
// related cards
|
||||
} else if (xmlName == "related" || xmlName == "reverse-related") {
|
||||
bool attach = false;
|
||||
CardRelation::AttachType attach = CardRelation::DoesNotAttach;
|
||||
bool exclude = false;
|
||||
bool variable = false;
|
||||
int count = 1;
|
||||
|
|
@ -246,7 +246,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
}
|
||||
|
||||
if (attrs.hasAttribute("attach")) {
|
||||
attach = true;
|
||||
attach = CardRelation::AttachTo;
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("exclude")) {
|
||||
|
|
@ -459,4 +459,4 @@ bool CockatriceXml3Parser::saveToFile(SetNameMap sets,
|
|||
xml.writeEndDocument();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
}
|
||||
// related cards
|
||||
} else if (xmlName == "related" || xmlName == "reverse-related") {
|
||||
bool attach = false;
|
||||
CardRelation::AttachType attachType = CardRelation::DoesNotAttach;
|
||||
bool exclude = false;
|
||||
bool variable = false;
|
||||
bool persistent = false;
|
||||
|
|
@ -205,7 +205,8 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
}
|
||||
|
||||
if (attrs.hasAttribute("attach")) {
|
||||
attach = true;
|
||||
attachType = attrs.value("attach").toString() == "transform" ? CardRelation::TransformInto
|
||||
: CardRelation::AttachTo;
|
||||
}
|
||||
|
||||
if (attrs.hasAttribute("exclude")) {
|
||||
|
|
@ -216,7 +217,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
persistent = true;
|
||||
}
|
||||
|
||||
auto *relation = new CardRelation(cardName, attach, exclude, variable, count, persistent);
|
||||
auto *relation = new CardRelation(cardName, attachType, exclude, variable, count, persistent);
|
||||
if (xmlName == "reverse-related") {
|
||||
reverseRelatedCards << relation;
|
||||
} else {
|
||||
|
|
@ -294,7 +295,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
|
|||
for (auto i : related) {
|
||||
xml.writeStartElement("related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
xml.writeAttribute("attach", i->getAttachTypeAsString());
|
||||
}
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
xml.writeAttribute("exclude", "exclude");
|
||||
|
|
@ -318,7 +319,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
|
|||
for (auto i : reverseRelated) {
|
||||
xml.writeStartElement("reverse-related");
|
||||
if (i->getDoesAttach()) {
|
||||
xml.writeAttribute("attach", "attach");
|
||||
xml.writeAttribute("attach", i->getAttachTypeAsString());
|
||||
}
|
||||
|
||||
if (i->getIsCreateAllExclusion()) {
|
||||
|
|
|
|||
|
|
@ -1677,7 +1677,7 @@ void Player::actCreateAllRelatedCards()
|
|||
dbName = cardRelationAll->getName();
|
||||
bool persistent = cardRelationAll->getIsPersistent();
|
||||
for (int i = 0; i < cardRelationAll->getDefaultCount(); ++i) {
|
||||
createCard(sourceCard, dbName, false, persistent);
|
||||
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
||||
}
|
||||
++tokensTypesCreated;
|
||||
if (tokensTypesCreated == 1) {
|
||||
|
|
@ -1692,7 +1692,7 @@ void Player::actCreateAllRelatedCards()
|
|||
dbName = cardRelationNotExcluded->getName();
|
||||
bool persistent = cardRelationNotExcluded->getIsPersistent();
|
||||
for (int i = 0; i < cardRelationNotExcluded->getDefaultCount(); ++i) {
|
||||
createCard(sourceCard, dbName, false, persistent);
|
||||
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
||||
}
|
||||
++tokensTypesCreated;
|
||||
if (tokensTypesCreated == 1) {
|
||||
|
|
@ -1731,23 +1731,22 @@ bool Player::createRelatedFromRelation(const CardItem *sourceCard, const CardRel
|
|||
return false;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
createCard(sourceCard, dbName, false, persistent);
|
||||
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
||||
}
|
||||
} else if (cardRelation->getDefaultCount() > 1) {
|
||||
for (int i = 0; i < cardRelation->getDefaultCount(); ++i) {
|
||||
createCard(sourceCard, dbName, false, persistent);
|
||||
createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent);
|
||||
}
|
||||
} else {
|
||||
if (cardRelation->getDoesAttach()) {
|
||||
createAttachedCard(sourceCard, dbName, persistent);
|
||||
} else {
|
||||
createCard(sourceCard, dbName, false, persistent);
|
||||
}
|
||||
createCard(sourceCard, dbName, cardRelation->getAttachType(), persistent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Player::createCard(const CardItem *sourceCard, const QString &dbCardName, bool attach, bool persistent)
|
||||
void Player::createCard(const CardItem *sourceCard,
|
||||
const QString &dbCardName,
|
||||
CardRelation::AttachType attachType,
|
||||
bool persistent)
|
||||
{
|
||||
CardInfoPtr cardInfo = db->getCard(dbCardName);
|
||||
|
||||
|
|
@ -1786,18 +1785,24 @@ void Player::createCard(const CardItem *sourceCard, const QString &dbCardName, b
|
|||
cmd.set_x(gridPoint.x());
|
||||
cmd.set_y(gridPoint.y());
|
||||
|
||||
if (attach) {
|
||||
cmd.set_target_card_id(sourceCard->getId());
|
||||
switch (attachType) {
|
||||
case CardRelation::DoesNotAttach:
|
||||
break;
|
||||
|
||||
case CardRelation::AttachTo:
|
||||
cmd.set_target_card_id(sourceCard->getId());
|
||||
cmd.set_target_mode(Command_CreateToken::ATTACH_TO);
|
||||
break;
|
||||
|
||||
case CardRelation::TransformInto:
|
||||
cmd.set_target_card_id(sourceCard->getId());
|
||||
cmd.set_target_mode(Command_CreateToken::TRANSFORM_INTO);
|
||||
break;
|
||||
}
|
||||
|
||||
sendGameCommand(cmd);
|
||||
}
|
||||
|
||||
void Player::createAttachedCard(const CardItem *sourceCard, const QString &dbCardName, bool persistent)
|
||||
{
|
||||
createCard(sourceCard, dbCardName, true, persistent);
|
||||
}
|
||||
|
||||
void Player::actSayMessage()
|
||||
{
|
||||
auto *a = qobject_cast<QAction *>(sender());
|
||||
|
|
@ -3550,6 +3555,7 @@ void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu)
|
|||
CardInfoPtr relatedCard = db->getCard(cardRelation->getName());
|
||||
if (relatedCard == nullptr)
|
||||
continue;
|
||||
|
||||
QString relatedCardName;
|
||||
if (relatedCard->getPowTough().size() > 0) {
|
||||
relatedCardName = relatedCard->getPowTough() + " " + relatedCard->getName(); // "n/n name"
|
||||
|
|
@ -3559,7 +3565,8 @@ void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu)
|
|||
|
||||
QString text = tr("Token: ");
|
||||
if (cardRelation->getDoesAttach()) {
|
||||
text += tr("Attach to ") + "\"" + relatedCardName + "\"";
|
||||
text +=
|
||||
tr(cardRelation->getDoesTransform() ? "Transform into " : "Attach to ") + "\"" + relatedCardName + "\"";
|
||||
} else if (cardRelation->getIsVariable()) {
|
||||
text += "X " + relatedCardName;
|
||||
} else if (cardRelation->getDefaultCount() != 1) {
|
||||
|
|
|
|||
|
|
@ -287,9 +287,10 @@ private:
|
|||
bool allCards);
|
||||
void addRelatedCardActions(const CardItem *card, QMenu *cardMenu);
|
||||
void addRelatedCardView(const CardItem *card, QMenu *cardMenu);
|
||||
void
|
||||
createCard(const CardItem *sourceCard, const QString &dbCardName, bool attach = false, bool persistent = false);
|
||||
void createAttachedCard(const CardItem *sourceCard, const QString &dbCardName, bool persistent = false);
|
||||
void createCard(const CardItem *sourceCard,
|
||||
const QString &dbCardName,
|
||||
CardRelation::AttachType attach = CardRelation::DoesNotAttach,
|
||||
bool persistent = false);
|
||||
bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation);
|
||||
|
||||
QRectF bRect;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue