From 79716a45339db9d5d525b9c99ae997e48e675e73 Mon Sep 17 00:00:00 2001 From: Vittorio Alfieri Date: Wed, 28 Apr 2021 17:03:29 +0200 Subject: [PATCH] Added a new rule to flip back modal dual face card on each phase and after each action, fixed "Aladdin's Lamp" and "Turntimber Symbiosis" primitives, allowed the AI to play back side of modal dual face cards, improved the "doubleside" keyword to flip modal dual face cards, improved filters to target flipped cards using the "isflipped" keyword, fixed a crash when zone pointer was null in GameObserver::logAction method. --- projects/mtg/bin/Res/rules/Commander.txt | 18 ++++++++++++- projects/mtg/bin/Res/rules/mtg.txt | 19 ++++++++++++-- .../bin/Res/sets/primitives/borderline.txt | 6 ++--- projects/mtg/src/AIPlayerBaka.cpp | 6 +++-- projects/mtg/src/AllAbilities.cpp | 25 ++++++++++++++++--- projects/mtg/src/CardDescriptor.cpp | 5 ++++ projects/mtg/src/CardGui.cpp | 12 +++++++++ projects/mtg/src/GameObserver.cpp | 3 ++- projects/mtg/src/TargetChooser.cpp | 12 +++++++++ 9 files changed, 93 insertions(+), 13 deletions(-) diff --git a/projects/mtg/bin/Res/rules/Commander.txt b/projects/mtg/bin/Res/rules/Commander.txt index 8047057f6..e63d6210c 100644 --- a/projects/mtg/bin/Res/rules/Commander.txt +++ b/projects/mtg/bin/Res/rules/Commander.txt @@ -53,7 +53,6 @@ auto=lord(Swamp[-noactivatedability;-nomanaability;-notapability;land]|MyBattlef auto=lord(Mountain[-noactivatedability;-nomanaability;-notapability;land]|MyBattlefield) {T}:Add{R} auto=lord(Forest[-noactivatedability;-nomanaability;-notapability;land]|MyBattlefield) {T}:Add{G} - #Mana Empties from manapool at the end of each phase auto=@each untap:removeMana(*) auto=@each upkeep:removeMana(*) @@ -68,6 +67,23 @@ auto=@each secondmain:removeMana(*) auto=@each end:removeMana(*) auto=@each cleanup:removeMana(*) +#Modal dual face card flip back rule +auto=@each untap restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each upkeep restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each draw restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each firstmain restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each combatbegins restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each attackers restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each blockers restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each combatdamage restriction{type(*[isflipped]|nonbattlezone))~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each combatEnds restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each secondmain restriction{type(*[isflipped]|nonbattlezone))~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each end restriction{compare(type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each cleanup restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@movedto(other *|nonbattlezone) restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@movedto(other *|stack) restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@movedto(other *|battlefield) restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller + #Monarch rule auto=@each my endofturn restriction{compare(pmonarch)~morethan~0}:draw:1 diff --git a/projects/mtg/bin/Res/rules/mtg.txt b/projects/mtg/bin/Res/rules/mtg.txt index 163e3be06..5c79f4692 100644 --- a/projects/mtg/bin/Res/rules/mtg.txt +++ b/projects/mtg/bin/Res/rules/mtg.txt @@ -45,7 +45,6 @@ auto=sethand:7 auto=@each my draw:draw:1 auto=maxPlay(land)1 - #Lands Mana Rules auto=lord(Plains[-noactivatedability;-nomanaability;-notapability;land]|MyBattlefield) {T}:Add{W} auto=lord(Island[-noactivatedability;-nomanaability;-notapability;land]|MyBattlefield) {T}:Add{U} @@ -53,7 +52,6 @@ auto=lord(Swamp[-noactivatedability;-nomanaability;-notapability;land]|MyBattlef auto=lord(Mountain[-noactivatedability;-nomanaability;-notapability;land]|MyBattlefield) {T}:Add{R} auto=lord(Forest[-noactivatedability;-nomanaability;-notapability;land]|MyBattlefield) {T}:Add{G} - #Mana Empties from manapool at the end of each phase auto=@each untap:removeMana(*) auto=@each upkeep:removeMana(*) @@ -68,6 +66,23 @@ auto=@each secondmain:removeMana(*) auto=@each end:removeMana(*) auto=@each cleanup:removeMana(*) +#Modal dual face card flip back rule +auto=@each untap restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each upkeep restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each draw restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each firstmain restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each combatbegins restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each attackers restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each blockers restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each combatdamage restriction{type(*[isflipped]|nonbattlezone))~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each combatEnds restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each secondmain restriction{type(*[isflipped]|nonbattlezone))~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each end restriction{compare(type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@each cleanup restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@movedto(other *|nonbattlezone) restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@movedto(other *|stack) restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller +auto=@movedto(other *|battlefield) restriction{type(*[isflipped]|nonbattlezone)~morethan~0}:ability$!all(*[isflipped]|nonbattlezone) doubleside()!$ controller + #Monarch rule auto=@each my endofturn restriction{compare(pmonarch)~morethan~0}:draw:1 diff --git a/projects/mtg/bin/Res/sets/primitives/borderline.txt b/projects/mtg/bin/Res/sets/primitives/borderline.txt index 62f61042c..3b07790a3 100644 --- a/projects/mtg/bin/Res/sets/primitives/borderline.txt +++ b/projects/mtg/bin/Res/sets/primitives/borderline.txt @@ -974,7 +974,7 @@ auto={6}{T}:name(X=6) name(X=6) transforms((,newability[replacedraw reveal:6 opt auto={7}{T}:name(X=7) name(X=7) transforms((,newability[replacedraw reveal:7 optionone name(Choose a card) target(*|reveal) moveto(myhand) and!( all(*[zpos<=6]|mylibrary) moveto(myhand) )! optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend revealend])) ueot auto={8}{T}:name(X=8) name(X=8) transforms((,newability[replacedraw reveal:8 optionone name(Choose a card) target(*|reveal) moveto(myhand) and!( all(*[zpos<=7]|mylibrary) moveto(myhand) )! optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend revealend])) ueot auto={9}{T}:name(X=9) name(X=9) transforms((,newability[replacedraw reveal:9 optionone name(Choose a card) target(*|reveal) moveto(myhand) and!( all(*[zpos<=8]|mylibrary) moveto(myhand) )! optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend revealend])) ueot -auto={10}{T}:name(X=10) name(X=10) transforms((,newability[replacedraw reveal:10 optionone name(Choose a card) target(*|reveal) moveto(myhand) and!( all(*[zpos<=9]|mylibrary) moveto(myhand) )! optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend afterreveal all(*[zpos<=10]) moveto(myhand) afterrevealend revealend])) ueot +auto={10}{T}:name(X=10) name(X=10) transforms((,newability[replacedraw reveal:10 optionone name(Choose a card) target(*|reveal) moveto(myhand) and!( all(*[zpos<=9]|mylibrary) moveto(myhand) )! optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend afterrevealed all(*[zpos<=10]) moveto(myhand) afterrevealedend revealend])) ueot text={X}, {T}: The next time you would draw a card this turn, instead look at the top X cards of your library, put all but one of them on the bottom of your library in a random order, then draw a card. X can't be 0. mana={10} type=Artifact @@ -59937,8 +59937,8 @@ name=Turntimber Symbiosis restriction=compare(isflipped)~equalto~0 anyzone={0}:doubleside(Turntimber, Serpentine Wood) autohand={0}:restriction{can play land,compare(isflipped)~equalto~1} name(Turntimber, Serpentine Wood) name(Turntimber, Serpentine Wood) flip(Turntimber, Serpentine Wood) forcetype(land) -auto=choice name(Look seven and put creature with manacost 3 or less) name(Look seven and put creature with manacost 3 or less) reveal:7 optionone name(Get Creature) target(creature[manacost<=3]|reveal) moveTo(myBattlefield) and!( counter(1/1,3) )! optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend revealend restriction{type(creature[manacost<=3;zpos<=7]|myLibrary)~morethan~0} -auto=choice name(Look seven and put creature with manacost 4 or more) name(Look seven and put creature with manacost 4 or more) reveal:7 optionone name(Get Creature) target(creature[manacost>=4]|reveal) moveTo(myBattlefield) optiononeend optiontwo name(put on bottom) all(*|reveal) bottomoflibrary optiontwoend revealend restriction{type(creature[manacost>=4;zpos<=7]|myLibrary)~morethan~0} +aicode=activate target(*[zpos=1]|mylibrary) transforms((,newability[if type(creature[zpos<=7]|mylibrary)~equalto~0 then all(*[zpos<=7]|mylibrary) moveto(myreveal) and!( bottomoflibrary )!],newability[if type(creature[zpos<=7]|mylibrary)~morethan~0 then target(creature[zpos<=7]|mylibrary) moveto(myBattlefield) and!( transforms((,newability[all(*[zpos<=7]|mylibrary) moveto(myreveal) and!( bottomoflibrary )!],newability[if cantargetcard(creature[manacost<=3]|*) then counter(1/1.3)])) oneshot )! ])) oneshot +auto=name(Look seven and put creature in play) reveal:7 optionone name(Get Creature) target(creature|reveal) moveTo(mylibrary) and!( becomes(tobecast) ueot )! optiononeend optiontwo name(put on bottom) target(*|reveal) bottomoflibrary and!( all(*|reveal) bottomoflibrary )! optiontwoend afterrevealed all(tobecast|mylibrary) moveto(myBattlefield) and!( transforms((,newability[if cantargetcard(creature[manacost<=3]|*) then counter(1/1.3)])) oneshot )! afterrevealedend revealend text=Look at the top seven cards of your library. You may put a creature card from among them onto the battlefield. If that card has converted mana cost 3 or less, it enters with three additional +1/+1 counters on it. Put the rest on the bottom of your library in a random order. // Turntimber, Serpentine Wood mana={4}{G}{G}{G} type=Sorcery diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 0d01fc9b5..1229161b9 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -632,9 +632,11 @@ int OrderedAIAction::getEfficiency() efficiency = 90; } } - else if (dynamic_cast(a)) + else if (AATurnSide * ats = dynamic_cast(a)) { - efficiency = 0; // AI does not have to use the doubleside ability to avoid loops. + efficiency = 0; // AI does not have to use the doubleside ability to avoid loops but it can randomly choose to flip card and cast its back side. + if(std::rand() % 2) + ats->source->isFlipped = !ats->source->isFlipped; } else if (ATokenCreator * atc = dynamic_cast(a)) { diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index b9878e962..43f755e21 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -4349,12 +4349,15 @@ int AATurnSide::resolve() if(_target->mutation && _target->parentCards.size() > 0) return 0; // Mutated down cards cannot be turned, they will follow the fate of top-card MTGCard * fcard; MTGCardInstance* sideCard; + if(_target->controller()->isAI() && _target->isFlipped) _target->isFlipped = false; // If it's AI calling back we just have to reset isFLipped flag and then return. + if(!_target->isFlipped && _SideName == "") return 0; // No need to turn front if card has not been flipped before. if(!_target->isFlipped){ fcard = MTGCollection()->getCardByName(_SideName); if(!fcard) return 0; sideCard = NEW MTGCardInstance(fcard, _target->controller()->game); _target->nameOrig = _target->name; _target->name = sideCard->name; + _target->setName(sideCard->name); if(!sideCard) return 0; if(sideCard->getManaCost()){ if(_target->getManaCost()->getAlternative()){ @@ -4367,6 +4370,7 @@ int AATurnSide::resolve() fcard = MTGCollection()->getCardByName(_target->nameOrig); if(!fcard) return 0; _target->name = _target->nameOrig; + _target->setName(_target->nameOrig); _target->nameOrig = ""; sideCard = NEW MTGCardInstance(fcard, _target->controller()->game); if(!sideCard) return 0; @@ -4375,13 +4379,26 @@ int AATurnSide::resolve() _target->getManaCost()->copy(sideCard->getManaCost()); // Restore the original side cost mana symbols. } } - for (int i = ((int)_target->types.size())-1; i >= 0; --i) // Load all the types from the current side - _target->removeType(_target->types[i]); - for (int i = 0; i < ((int)sideCard->types.size()); i++) - _target->addType(sideCard->types[i]); + if(_target->owner->playMode != Player::MODE_TEST_SUITE) + { + _target->setMTGId(sideCard->getMTGId()); + _target->setId = sideCard->setId; + } + _target->power = sideCard->power; + _target->life = sideCard->life; + _target->toughness = sideCard->toughness; + _target->origpower = sideCard->origpower; + _target->origtoughness = sideCard->origtoughness; + _target->basepower = sideCard->basepower; + _target->basetoughness = sideCard->basetoughness; + _target->types = sideCard->types; _target->text = sideCard->text; _target->formattedText = sideCard->formattedText; + _target->magicText = sideCard->magicText; + _target->colors = sideCard->colors; + _target->basicAbilities = sideCard->basicAbilities; _target->isFlipped = !_target->isFlipped; + _target->mPropertiesChangedSinceLastUpdate = true; SAFE_DELETE(sideCard); return 1; } diff --git a/projects/mtg/src/CardDescriptor.cpp b/projects/mtg/src/CardDescriptor.cpp index 996df4074..2c086e71b 100644 --- a/projects/mtg/src/CardDescriptor.cpp +++ b/projects/mtg/src/CardDescriptor.cpp @@ -281,6 +281,11 @@ MTGCardInstance * CardDescriptor::match(MTGCardInstance * card) match = NULL; } + if (isFlipped != card->isFlipped) + { + match = NULL; + } + if ((tapped == -1 && card->isTapped()) || (tapped == 1 && !card->isTapped())) { match = NULL; diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index fc7937585..a92e1a5ae 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -1544,6 +1544,18 @@ bool CardGui::FilterCard(MTGCard * _card,string filter) cd.isToken = 1; } } + //Has been flipped + else if (attribute.find("isflipped") != string::npos) + { + if (minus) + { + cd.isFlipped = false; + } + else + { + cd.isFlipped = true; + } + } //Has x in cost else if (attribute.find("hasx") != string::npos) { diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index 6ed5e5d51..6c241ac8d 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -2125,8 +2125,9 @@ void GameObserver::logAction(Player* player, const string& s) { void GameObserver::logAction(MTGCardInstance* card, MTGGameZone* zone, size_t index, int result) { stringstream stream; if(zone == NULL) zone = card->currentZone; + string zoneName = (zone != NULL)?zone->getName():"UnknownZone"; // Fixed a crash when zone pointer was null. stream << "p" << ((card->controller()==players[0])?"1.":"2.") - << zone->getName()<< "[" << index << "] " + << zoneName << "[" << index << "] " << result << card->getLCName(); logAction(stream.str()); } diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index 3f7ba763d..480a90f24 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -590,6 +590,18 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta cd->isToken = 1; } } + //Has been flipped + else if (attribute.find("isflipped") != string::npos) + { + if (minus) + { + cd->isFlipped = false; + } + else + { + cd->isFlipped = true; + } + } //Has x in cost else if (attribute.find("hasx") != string::npos) {