diff --git a/projects/mtg/bin/Res/sets/primitives/mtg.txt b/projects/mtg/bin/Res/sets/primitives/mtg.txt index 1646f64f3..24a7e247a 100644 --- a/projects/mtg/bin/Res/sets/primitives/mtg.txt +++ b/projects/mtg/bin/Res/sets/primitives/mtg.txt @@ -1902,7 +1902,7 @@ type=Artifact name=Angelic Arbiter abilities=flying auto=@movedTo(*|opponentstack):all(creature|opponentBattlefield) cantattack -auto=@attacking(creature|opponentBattlefield):nospell opponent +auto=@attacking(creature|opponentBattlefield):maxCast(*)0 opponent ueot text=Flying -- Each opponent who cast a spell this turn can't attack with creatures. -- Each opponent who attacked with creatures this turn can't cast spells. mana={5}{W}{W} type=Creature @@ -4109,8 +4109,7 @@ toughness=4 [/card] [card] name=Azusa, Lost but Seeking -auto=land:2 -auto=@each my upkeep:land:2 controller +auto=maxPlay(land)+2 text=You may play two additional lands on each of your turns. mana={2}{G} type=Legendary Creature @@ -9712,7 +9711,7 @@ type=Sorcery [card] name=Cease-Fire target=player -auto=nocreatures +auto=maxCast(creature)0 auto=draw:1 controller text=Target player can't cast creature spells this turn. -- Draw a card. mana={2}{W} @@ -20094,15 +20093,14 @@ toughness=1 [/card] [card] name=Exploration -auto=land:1 -auto=@each my upkeep:land:1 controller +auto=maxPlay(land)+1 text=You may play an additional land on each of your turns. mana={G} type=Enchantment [/card] [card] name=Explore -auto=land:1 +auto=maxPlay(land)+1 auto=draw:1 text=You may play an additional land this turn. -- Draw a card. mana={1}{G} @@ -44353,7 +44351,7 @@ toughness=6 [card] name=Orim's Chant target=player -auto=nospells +auto=maxCast(*)0 kicker={W} auto=kicker cantattack all(creature) text=Kicker {W} (You may pay an additional {W} as you cast this spell.) -- Target player can't cast spells this turn. -- If Orim's Chant was kicked, creatures can't attack this turn. @@ -44400,8 +44398,8 @@ toughness=3 [card] name=Oriss Samite Guardian auto={T}:prevent:999 target(creature) -auto={discard(oriss samite guardian|myhand)}:name(opponent can't cast spells and his creatures cannot attack ueot) && nospell opponent && cantattack all(creature|opponentBattlefield) -auto={discard(oriss samite guardian|myhand)}:name(you can't cast spells and your creatures cannot attack ueot) && nospell control && cantattack all(creature|myBattlefield) +auto={discard(oriss samite guardian|myhand)}:name(opponent can't cast spells and his creatures cannot attack ueot) && maxCast(*)0 opponent ueot && cantattack all(creature|opponentBattlefield) +auto={discard(oriss samite guardian|myhand)}:name(you can't cast spells and your creatures cannot attack ueot) && maxCast(*)0 control ueot && cantattack all(creature|myBattlefield) text={T}: Prevent all damage that would be dealt to target creature this turn. -- Grandeur - Discard another card named Oriss, Samite Guardian: Target player can't cast spells this turn, and creatures that player controls can't attack this turn. mana={1}{W}{W} type=Legendary Creature @@ -56424,7 +56422,7 @@ type=Sorcery [/card] [card] name=Silence -auto=nospells opponent +auto=maxCast(*)0 opponent text=Your opponents can't cast spells this turn. (Spells cast before this resolves are unaffected.) mana={W} type=Instant @@ -62151,7 +62149,7 @@ type=Land [/card] [card] name=Summer Bloom -auto=land:3 +auto=maxPlay(land)+3 text=You may play up to three additional lands this turn. mana={1}{G} type=Sorcery diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index d5dc866e0..0651c7caa 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -472,6 +472,7 @@ selesnya_guildmage.txt setpower_settoughness.txt shard_volley.txt sigil_captain_i467.txt +silence.txt silver_seraph_i300.txt skullcage0_i239.txt skullcage1_i239.txt diff --git a/projects/mtg/bin/Res/test/silence.txt b/projects/mtg/bin/Res/test/silence.txt new file mode 100644 index 000000000..8163fd860 --- /dev/null +++ b/projects/mtg/bin/Res/test/silence.txt @@ -0,0 +1,28 @@ +#Slience: Opponent cannot cast spells this turn +[init] +firstmain +[player1] +manapool:{G}{G} +inplay:mountain +hand:grizzly bears,raging golblin +[player2] +inplay:plains +hand:Silence +[do] +Grizzly Bears +no +yes +plains +Silence +endofinterruption +mountain +Raging goblin +[assert] +firstmain +[player1] +inplay:mountain,Grizzly bears +manapool:{R} +[player2] +inplay:plains +graveyard:Silence +[end] \ No newline at end of file diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index b27f61cb3..b0784efc4 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -1280,22 +1280,41 @@ public: int getNumCards(); }; -//lands, allows to play more land during a turn: -class AMoreLandPlzUEOT: public InstantAbilityTP + + +class ACastRestriction: public AbilityTP { public: - WParsedInt *additional; - MaxPerTurnRestriction * landsRestriction; + WParsedInt *value; //"maxPerTurn" value + MaxPerTurnRestriction * existingRestriction; // a pointer to the restriction that is being modified or that has been created (for restriction deletion purpose) + TargetChooser * restrictionsScope; //a minimalist TargetChooser object describing the cards impacted by the restriction (for example: lands) + bool modifyExisting; //if set to true, means we want to modify an existing restriction, otherwise we create a new one + int zoneId; // identifier of the zone id impacted by the restriction + Player * targetPlayer; // Reference to the player impacted by the restriction (for restriction deletion purpose) - AMoreLandPlzUEOT(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * _additional, int who = TargetChooser::UNSET); + ACastRestriction(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who = TargetChooser::UNSET); int addToGame(); int destroy(); const char * getMenuText(); - AMoreLandPlzUEOT * clone() const; - ~AMoreLandPlzUEOT(); + ACastRestriction * clone() const; + ~ACastRestriction(); }; + +class AInstantCastRestrictionUEOT: public InstantAbilityTP +{ +public: + ACastRestriction * ability; + + + AInstantCastRestrictionUEOT(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who = TargetChooser::UNSET); + int resolve(); + const char * getMenuText(); + AInstantCastRestrictionUEOT * clone() const; + ~AInstantCastRestrictionUEOT(); +}; + /*Gives life to target controller*/ class AALifer: public ActivatedAbilityTP { @@ -2052,7 +2071,6 @@ public: a->isClone = 1; return a; } - }; //Circle of Protections @@ -5552,38 +5570,6 @@ public: AAShuffle * clone() const; }; -//only 1 spell -class AAOnlyOne: public ActivatedAbilityTP -{ -public: - AAOnlyOne(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who = - TargetChooser::UNSET); - int resolve(); - const char * getMenuText(); - AAOnlyOne * clone() const; -}; - -//nospells -class AANoSpells: public ActivatedAbilityTP -{ -public: - AANoSpells(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who = - TargetChooser::UNSET); - int resolve(); - const char * getMenuText(); - AANoSpells * clone() const; -}; - -//NoCreature -class AANoCreatures: public ActivatedAbilityTP -{ -public: - AANoCreatures(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who = - TargetChooser::UNSET); - int resolve(); - const char * getMenuText(); - AANoCreatures * clone() const; -}; //Random Discard class AARandomDiscarder: public ActivatedAbilityTP diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index ee3d7c1c2..52d45e3f8 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -406,6 +406,14 @@ public: Targetable * getTarget(); }; +class AbilityTP:public MTGAbility{ +public: + int who; + AbilityTP(int id, MTGCardInstance * card, Targetable * _target = NULL, int who = TargetChooser::UNSET); + Targetable * getTarget(); + virtual ~AbilityTP(){}; +}; + class AManaProducer: public ActivatedAbilityTP{ protected: diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index e2e95491f..a3f6bb7ae 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -103,6 +103,7 @@ class MTGGameZone { static MTGGameZone * stringToZone(string zoneName, MTGCardInstance * source, MTGCardInstance * target); static int zoneStringToId(string zoneName); static MTGGameZone *intToZone(int zoneId, MTGCardInstance * source = NULL,MTGCardInstance * target = NULL); + static MTGGameZone *intToZone(int zoneId, Player * source, Player * target); bool needShuffle; virtual const char * getName(){return "zone";}; virtual ostream& toString(ostream&) const; diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index e8f9f2245..bc25d55dc 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -31,9 +31,6 @@ public: int castedspellsthisturn; bool onlyonecast; int castcount; - bool nocreatureinstant; - bool nospellinstant; - bool onlyoneinstant; bool castrestrictedcreature; bool castrestrictedspell; bool onlyoneboth; diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 8f77dab32..7bb398d47 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -1330,13 +1330,15 @@ int AIPlayerBaka::computeActions() ipotential = true; } //look for an instant of ability to interupt with - if((castrestrictedspell == false && nospellinstant == false)&& - (onlyonecast == false || castcount < 2) && (onlyoneinstant == false || castcount < 2)) + if((castrestrictedspell == false)&& + (onlyonecast == false || castcount < 2)) { if (!nextCardToPlay) { nextCardToPlay = FindCardToPlay(icurrentMana, "instant"); + if (game->playRestrictions->canPutIntoZone(nextCardToPlay, game->stack) == PlayRestriction::CANT_PLAY) + nextCardToPlay = NULL; } if (!nextCardToPlay) { @@ -1379,33 +1381,20 @@ int AIPlayerBaka::computeActions() nextCardToPlay = FindCardToPlay(currentMana, "land"); selectAbility(); //look for the most expensive creature we can afford - if((castrestrictedspell == false && nospellinstant == false)&& - (onlyonecast == false || castcount < 2)&&(onlyoneinstant == false || castcount < 2)) + if((castrestrictedspell == false)&& + (onlyonecast == false || castcount < 2)) { - if (castrestrictedcreature == false && nocreatureinstant == false) + + const char* types[] = {"creature", "enchantment", "artifact", "sorcery", "instant"}; + int count = 0; + while (!nextCardToPlay && count < 5) { - if (!nextCardToPlay) - { - nextCardToPlay = FindCardToPlay(currentMana, "creature"); - } - } - //Let's Try an enchantment maybe ? - if (!nextCardToPlay) - { - nextCardToPlay = FindCardToPlay(currentMana, "enchantment"); - } - if (!nextCardToPlay) - { - nextCardToPlay = FindCardToPlay(currentMana, "artifact"); - } - if (!nextCardToPlay) - { - nextCardToPlay = FindCardToPlay(currentMana, "sorcery"); - } - if (!nextCardToPlay) - { - nextCardToPlay = FindCardToPlay(currentMana, "instant"); + nextCardToPlay = FindCardToPlay(currentMana, types[count]); + if (game->playRestrictions->canPutIntoZone(nextCardToPlay, game->stack) == PlayRestriction::CANT_PLAY) + nextCardToPlay = NULL; + count++; } + if (!nextCardToPlay) { selectAbility(); diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 9c028599e..f1886a039 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -1455,63 +1455,124 @@ AACloner::~AACloner() { } -// More Land - allow more lands to be played on a turn -AMoreLandPlzUEOT::AMoreLandPlzUEOT(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * _additional, int who) : - InstantAbilityTP(_id, card, _target, who), additional(_additional) + + +// Cast/Play Restriction modifier +ACastRestriction::ACastRestriction(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) : + AbilityTP(_id, card, _target, who), restrictionsScope(_restrictionsScope), value(_value), modifyExisting(_modifyExisting),zoneId(_zoneId) { - landsRestriction = NULL; + existingRestriction = NULL; + targetPlayer = NULL; } -int AMoreLandPlzUEOT::addToGame() +int ACastRestriction::addToGame() { Targetable * _target = getTarget(); - Player * player; + if (_target) { if (_target->typeAsTarget() == TARGET_CARD) { - player = ((MTGCardInstance *) _target)->controller(); + targetPlayer = ((MTGCardInstance *) _target)->controller(); } else { - player = (Player *) _target; + targetPlayer = (Player *) _target; } - landsRestriction = (MaxPerTurnRestriction *) (player->game->playRestrictions->getRestrictionById(PlayRestriction::LANDS_RULE_ID)); - if(landsRestriction && landsRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX) - landsRestriction->maxPerTurn += additional->getValue(); - return InstantAbility::addToGame(); + if (modifyExisting) + { + //For now the only modifying rule is the one for lands, so this is hardcoded here. + //This means that a modifying rule for anything lands will actually modify the lands rule. + //In the future, we need a way to "identify" rules that modify an existing restriction, probably by doing a comparison of the TargetChoosers + existingRestriction = (MaxPerTurnRestriction *) (targetPlayer->game->playRestrictions->getRestrictionById(PlayRestriction::LANDS_RULE_ID)); + if(existingRestriction && existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX) + existingRestriction->maxPerTurn += value->getValue(); + } + else + { + TargetChooser * _tc = restrictionsScope->clone(); + existingRestriction = NEW MaxPerTurnRestriction(PlayRestriction::UNDEF_ID, _tc, value->getValue(), MTGGameZone::intToZone(zoneId, source->controller(), targetPlayer)); + targetPlayer->game->playRestrictions->addRestriction(existingRestriction); + + } + AbilityTP::addToGame(); } return 0; } -int AMoreLandPlzUEOT::destroy() +int ACastRestriction::destroy() { - if (!landsRestriction) + if (!existingRestriction) return 0; - if(landsRestriction && landsRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX) - landsRestriction->maxPerTurn -= additional->getValue(); + if (modifyExisting) + { + if(existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX) + existingRestriction->maxPerTurn -= value->getValue(); + } + else + { + targetPlayer->game->playRestrictions->removeRestriction(existingRestriction); + SAFE_DELETE(existingRestriction); + } return 1; } -const char * AMoreLandPlzUEOT::getMenuText() +const char * ACastRestriction::getMenuText() { - return "Additional Lands"; + if (modifyExisting) + return "Additional Lands"; //hardoced because only the lands rule allows to modify existing rule for now + return "Cast Restriction"; } -AMoreLandPlzUEOT * AMoreLandPlzUEOT::clone() const +ACastRestriction * ACastRestriction::clone() const { - AMoreLandPlzUEOT * a = NEW AMoreLandPlzUEOT(*this); - a->additional = NEW WParsedInt(*(a->additional)); + ACastRestriction * a = NEW ACastRestriction(*this); + a->value = NEW WParsedInt(*(a->value)); + a->restrictionsScope = restrictionsScope->clone(); a->isClone = 1; return a; } -AMoreLandPlzUEOT::~AMoreLandPlzUEOT() +ACastRestriction::~ACastRestriction() { - SAFE_DELETE(additional); + SAFE_DELETE(value); + SAFE_DELETE(restrictionsScope); } + +AInstantCastRestrictionUEOT::AInstantCastRestrictionUEOT(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) : + InstantAbilityTP(_id, card, _target, who) +{ + ability = NEW ACastRestriction(_id, card, _target, _restrictionsScope, _value, _modifyExisting, _zoneId, who); +} + +int AInstantCastRestrictionUEOT::resolve() +{ + ACastRestriction * a = ability->clone(); + GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a); + wrapper->addToGame(); + return 1; +} +const char * AInstantCastRestrictionUEOT::getMenuText() +{ + return ability->getMenuText(); +} + +AInstantCastRestrictionUEOT * AInstantCastRestrictionUEOT::clone() const +{ + AInstantCastRestrictionUEOT * a = NEW AInstantCastRestrictionUEOT(*this); + a->ability = this->ability->clone(); + a->isClone = 1; + return a; +} + +AInstantCastRestrictionUEOT::~AInstantCastRestrictionUEOT() +{ + SAFE_DELETE(ability); +} + + //AAMover AAMover::AAMover(int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest, ManaCost * _cost, int doTap) : ActivatedAbility(_id, _source, _cost, 0, doTap), destination(dest) @@ -1570,116 +1631,6 @@ AAMover * AAMover::clone() const return a; } -// No Creatures -AANoCreatures::AANoCreatures(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int _tap, int who) : - ActivatedAbilityTP(_id, card, _target, _cost, _tap, who) -{ -} - -int AANoCreatures::resolve() -{ - Targetable * _target = getTarget(); - Player * player; - if (_target) - { - if (_target->typeAsTarget() == TARGET_CARD) - { - player = ((MTGCardInstance *) _target)->controller(); - } - else - { - player = (Player *) _target; - } - player->nocreatureinstant = true; - } - return 1; -} - -const char * AANoCreatures::getMenuText() -{ - return "No Creatures!"; -} - -AANoCreatures * AANoCreatures::clone() const -{ - AANoCreatures * a = NEW AANoCreatures(*this); - a->isClone = 1; - return a; -} - -// AA No Spells -AANoSpells::AANoSpells(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int _tap, int who) : - ActivatedAbilityTP(_id, card, _target, _cost, _tap, who) -{ -} -int AANoSpells::resolve() -{ - Targetable * _target = getTarget(); - Player * player; - if (_target) - { - if (_target->typeAsTarget() == TARGET_CARD) - { - player = ((MTGCardInstance *) _target)->controller(); - } - else - { - player = (Player *) _target; - } - player->nospellinstant = true; - } - return 1; -} - -const char * AANoSpells::getMenuText() -{ - return "No Spells!"; -} - -AANoSpells * AANoSpells::clone() const -{ - AANoSpells * a = NEW AANoSpells(*this); - a->isClone = 1; - return a; -} - -//OnlyOne -AAOnlyOne::AAOnlyOne(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int _tap, int who) : - ActivatedAbilityTP(_id, card, _target, _cost, _tap, who) -{ -} - -int AAOnlyOne::resolve() -{ - Targetable * _target = getTarget(); - Player * player; - if (_target) - { - if (_target->typeAsTarget() == TARGET_CARD) - { - player = ((MTGCardInstance *) _target)->controller(); - } - else - { - player = (Player *) _target; - } - player->onlyoneinstant = true; - } - return 1; -} - -const char * AAOnlyOne::getMenuText() -{ - return "Only One Spell!"; -} - -AAOnlyOne * AAOnlyOne::clone() const -{ - AAOnlyOne * a = NEW AAOnlyOne(*this); - a->isClone = 1; - return a; -} - //Random Discard AARandomDiscarder::AARandomDiscarder(int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost, int _tap, int who) : diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index 1ba21be7e..98e34fc14 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -111,9 +111,6 @@ void GameObserver::nextGamePhase() currentPlayer->castedspellsthisturn = 0; currentPlayer->opponent()->castedspellsthisturn = 0; currentPlayer->castcount = 0; - currentPlayer->nocreatureinstant = false; - currentPlayer->nospellinstant = false; - currentPlayer->onlyoneinstant = false; currentPlayer->damageCount = 0; currentPlayer->preventable = 0; currentPlayer->isPoisoned = false; diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 3f6568682..2ce97c8ae 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -18,6 +18,10 @@ const size_t kLordKeywordsCount = 5; const string kThisKeywords[] = { "this(", "thisforeach(" }; const size_t kThisKeywordsCount = 2; +const string kMaxCastKeywords[] = { "maxplay(", "maxcost("}; +const int kMaxCastZones[] = { MTGGameZone::BATTLEFIELD, MTGGameZone::STACK}; +const size_t kMaxCastKeywordsCount = 2; + int MTGAbility::allowedToCast(MTGCardInstance * card,Player * player) { int cPhase = game->getCurrentGamePhase(); @@ -699,7 +703,8 @@ TriggeredAbility * AbilityFactory::parseTrigger(string s, string magicText, int } return NEW TrVampired(id, card, tc, fromTc, 0); } - //when card becomes the target of a spell or ability + + //when card becomes the target of a spell or ability found = s.find("targeted("); if (found != string::npos) { @@ -796,6 +801,7 @@ int AbilityFactory::parseRestriction(string s) return ActivatedAbility::NO_RESTRICTION; } +// When abilities encapsulate each other, gets the deepest one (it is the one likely to have the most relevant information) MTGAbility * AbilityFactory::getCoreAbility(MTGAbility * a) { GenericTargetAbility * gta = dynamic_cast (a); @@ -2187,27 +2193,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG a->oneShot = 1; return a; } - //additional lands per turn - found = s.find("land:"); - if (found != string::npos) - { - size_t start = s.find(":", found); - size_t end = s.find(" ", start); - string additionalStr; - if (end != string::npos) - { - additionalStr = s.substr(start + 1, end - start - 1); - } - else - { - additionalStr = s.substr(start + 1); - } - WParsedInt * additional = NEW WParsedInt(additionalStr, spell, card); - Targetable * t = NULL; - if (spell) - t = spell->getNextTarget(); - return NEW AMoreLandPlzUEOT(id, card, t, additional, who); - } + //Deplete found = s.find("deplete:"); @@ -2244,38 +2230,45 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } - //cantcastspells - found = s.find("onlyonespell"); - if (found != string::npos) + //Cast/Play Restrictions + for (size_t i = 0; i < kMaxCastKeywordsCount; ++i) { - Targetable * t = NULL; - if (spell) - t = spell->getNextTarget(); - MTGAbility * a = NEW AAOnlyOne(id, card, t, NULL, 0, who); - a->oneShot = 1; - return a; - } - //cantcastspells - found = s.find("nospells"); - if (found != string::npos) - { - Targetable * t = NULL; - if (spell) - t = spell->getNextTarget(); - MTGAbility * a = NEW AANoSpells(id, card, t, NULL, 0, who); - a->oneShot = 1; - return a; - } - //cantcastcreature - found = s.find("nocreatures"); - if (found != string::npos) - { - Targetable * t = NULL; - if (spell) - t = spell->getNextTarget(); - MTGAbility * a = NEW AANoCreatures(id, card, t, NULL, 0, who); - a->oneShot = 1; - return a; + found = s.find(kMaxCastKeywords[i]); + if (found != string::npos) + { + size_t header = kMaxCastKeywords[i].size(); + size_t end = s.find(")"); + string targetsString = s.substr(found + header, end - found - header); + TargetChooserFactory tcf; + TargetChooser * castTargets = tcf.createTargetChooser(targetsString, card); + + size_t space = s.find(" ", end); + string valueStr; + if (space!= string::npos) + { + valueStr = s.substr(end + 1, space - end - 1); + } + else + { + valueStr = s.substr(end + 1); + } + + bool modifyExisting = (valueStr.find("+") != string::npos || valueStr.find("-") != string::npos); + + WParsedInt * value = NEW WParsedInt(valueStr, spell, card); + Targetable * t = NULL; + if (spell) + t = spell->getNextTarget(); + if (!activated) + { + if (card->hasType("instant") || card->hasType("sorcery") || forceUEOT) + { + return NEW AInstantCastRestrictionUEOT(id, card, t, castTargets, value, modifyExisting, kMaxCastZones[i], who); + } + return NEW ACastRestriction(id, card, t, castTargets, value, modifyExisting, kMaxCastZones[i], who); + } + return NULL; //TODO NEW ACastRestrictionUntilEndOfTurn(id, card, t, value, modifyExisting, kMaxCastZones[i], who); + } } //Discard @@ -4752,6 +4745,44 @@ AManaProducer * AManaProducer::clone() const return a; } + +AbilityTP::AbilityTP(int id, MTGCardInstance * card, Targetable * _target, int who) : + MTGAbility(id, card), who(who) +{ + if (_target) + target = _target; +} + +Targetable * AbilityTP::getTarget() +{ + switch (who) + { + case TargetChooser::TARGET_CONTROLLER: + if (target) + { + switch (target->typeAsTarget()) + { + case TARGET_CARD: + return ((MTGCardInstance *) target)->controller(); + case TARGET_STACKACTION: + return ((Interruptible *) target)->source->controller(); + default: + return (Player *) target; + } + } + return NULL; + case TargetChooser::CONTROLLER: + return source->controller(); + case TargetChooser::OPPONENT: + return source->controller()->opponent(); + case TargetChooser::OWNER: + return source->owner; + default: + return target; + } + return NULL; +} + ActivatedAbilityTP::ActivatedAbilityTP(int id, MTGCardInstance * card, Targetable * _target, ManaCost * cost, int doTap, int who) : ActivatedAbility(id, card, cost, 0, doTap), who(who) { diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index ccc1ab117..b29680538 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -731,6 +731,63 @@ void MTGLibrary::shuffleTopToBottom(int nbcards) } } +MTGGameZone * MTGGameZone::intToZone(int zoneId, Player * p, Player * p2) +{ + + switch (zoneId) + { + case MY_GRAVEYARD: + return p->game->graveyard; + case OPPONENT_GRAVEYARD: + return p->opponent()->game->graveyard; + case TARGET_CONTROLLER_GRAVEYARD: + return p2->game->graveyard; + + case MY_BATTLEFIELD: + return p->game->inPlay; + case OPPONENT_BATTLEFIELD: + return p->opponent()->game->inPlay; + case TARGET_CONTROLLER_BATTLEFIELD: + return p2->game->inPlay; + case BATTLEFIELD: + return p->game->inPlay; + + case MY_HAND: + return p->game->hand; + case OPPONENT_HAND: + return p->opponent()->game->hand; + case TARGET_CONTROLLER_HAND: + return p2->game->hand; + + case MY_EXILE: + return p->game->removedFromGame; + case OPPONENT_EXILE: + return p->opponent()->game->removedFromGame; + case TARGET_CONTROLLER_EXILE: + return p2->game->removedFromGame; + + case MY_LIBRARY: + return p->game->library; + case OPPONENT_LIBRARY: + return p->opponent()->game->library; + case TARGET_CONTROLLER_LIBRARY: + return p2->game->library; + case LIBRARY: + return p->game->library; + + case MY_STACK: + return p->game->stack; + case OPPONENT_STACK: + return p->opponent()->game->stack; + case TARGET_CONTROLLER_STACK: + return p2->game->stack; + case STACK: + return p->game->stack; + default: + return NULL; + } +} + MTGGameZone * MTGGameZone::intToZone(int zoneId, MTGCardInstance * source, MTGCardInstance * target) { Player *p, *p2; @@ -758,88 +815,51 @@ MTGGameZone * MTGGameZone::intToZone(int zoneId, MTGCardInstance * source, MTGCa else p2 = target->controller(); + + MTGGameZone * result = intToZone(zoneId, p, p2); + if (result) return result; + switch (zoneId) { - case MY_GRAVEYARD: - return p->game->graveyard; - case OPPONENT_GRAVEYARD: - return p->opponent()->game->graveyard; case TARGET_OWNER_GRAVEYARD: return target->owner->game->graveyard; - case TARGET_CONTROLLER_GRAVEYARD: - return p2->game->graveyard; case GRAVEYARD: return target->owner->game->graveyard; case OWNER_GRAVEYARD: return target->owner->game->graveyard; - case MY_BATTLEFIELD: - return p->game->inPlay; - case OPPONENT_BATTLEFIELD: - return p->opponent()->game->inPlay; case TARGET_OWNER_BATTLEFIELD: return target->owner->game->inPlay; - case TARGET_CONTROLLER_BATTLEFIELD: - return p2->game->inPlay; - case BATTLEFIELD: - return p->game->inPlay; case OWNER_BATTLEFIELD: return target->owner->game->inPlay; - case MY_HAND: - return p->game->hand; - case OPPONENT_HAND: - return p->opponent()->game->hand; case TARGET_OWNER_HAND: return target->owner->game->hand; - case TARGET_CONTROLLER_HAND: - return p2->game->hand; case HAND: return target->owner->game->hand; case OWNER_HAND: return target->owner->game->hand; - case MY_EXILE: - return p->game->removedFromGame; - case OPPONENT_EXILE: - return p->opponent()->game->removedFromGame; case TARGET_OWNER_EXILE: return target->owner->game->removedFromGame; - case TARGET_CONTROLLER_EXILE: - return p2->game->removedFromGame; case EXILE: return target->owner->game->removedFromGame; case OWNER_EXILE: return target->owner->game->removedFromGame; - case MY_LIBRARY: - return p->game->library; - case OPPONENT_LIBRARY: - return p->opponent()->game->library; case TARGET_OWNER_LIBRARY: return target->owner->game->library; - case TARGET_CONTROLLER_LIBRARY: - return p2->game->library; - case LIBRARY: - return p->game->library; case OWNER_LIBRARY: return target->owner->game->library; - case MY_STACK: - return p->game->stack; - case OPPONENT_STACK: - return p->opponent()->game->stack; case TARGET_OWNER_STACK: return target->owner->game->stack; - case TARGET_CONTROLLER_STACK: - return p2->game->stack; - case STACK: - return p->game->stack; case OWNER_STACK: return target->owner->game->stack; default: return NULL; } + } int MTGGameZone::zoneStringToId(string zoneName) diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 5a12709e1..e2b8e808b 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -55,6 +55,9 @@ int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN)) ) { + + if (currentPlayer->game->playRestrictions->canPutIntoZone(card, currentPlayer->game->stack) == PlayRestriction::CANT_PLAY) + return 0; ManaCost * playerMana = player->getManaPool(); ManaCost * cost = card->getManaCost(); @@ -69,21 +72,7 @@ int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) { return 0; } - if (player->nospellinstant) - { - return 0; - } - if (player->onlyoneinstant) - { - if (player->castcount >= 1) - { - return 0; - } - } - if (player->nocreatureinstant && card->hasType("creature")) - { - return 0; - } + if (player->castrestrictedcreature && card->hasType("creature")) { return 0; @@ -168,7 +157,7 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) game->targetChooser = NULL; player->castedspellsthisturn += 1; player->opponent()->castedspellsthisturn += 1; - if (player->onlyonecast || player->onlyoneinstant) + if (player->onlyonecast) { player->castcount += 1; } @@ -178,7 +167,7 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) spell = game->mLayers->stackLayer()->addSpell(copy, NULL, spellCost, payResult, 0); player->castedspellsthisturn += 1; player->opponent()->castedspellsthisturn += 1; - if (player->onlyonecast || player->onlyoneinstant) + if (player->onlyonecast) { player->castcount += 1; } @@ -275,6 +264,8 @@ int MTGAlternativeCostRule::isReactingToClick(MTGCardInstance * card, ManaCost * || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN)) ) { + if (currentPlayer->game->playRestrictions->canPutIntoZone(card, currentPlayer->game->stack) == PlayRestriction::CANT_PLAY) + return 0; ManaCost * playerMana = player->getManaPool(); #ifdef WIN32 @@ -289,21 +280,7 @@ int MTGAlternativeCostRule::isReactingToClick(MTGCardInstance * card, ManaCost * { return 0; } - if (player->nospellinstant ) - { - return 0; - } - if (player->onlyoneinstant ) - { - if (player->castcount >= 1) - { - return 0; - } - } - if (player->nocreatureinstant && card->hasType("creature")) - { - return 0; - } + if (player->castrestrictedcreature && card->hasType("creature")) { return 0; @@ -375,7 +352,7 @@ int MTGAlternativeCostRule::reactToClick(MTGCardInstance * card, ManaCost *alter game->targetChooser = NULL; player->castedspellsthisturn += 1; player->opponent()->castedspellsthisturn += 1; - if (player->onlyonecast || player->onlyoneinstant) + if (player->onlyonecast ) player->castcount += 1; if (card->has(Constants::STORM)) @@ -610,6 +587,8 @@ int MTGMorphCostRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) || game->currentGamePhase == Constants::MTG_PHASE_SECONDMAIN)) ) { + if (currentPlayer->game->playRestrictions->canPutIntoZone(card, currentPlayer->game->stack) == PlayRestriction::CANT_PLAY) + return 0; ManaCost * playerMana = player->getManaPool(); ManaCost * cost = card->getManaCost(); ManaCost * morph = card->getManaCost()->morph; @@ -624,21 +603,7 @@ int MTGMorphCostRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) { return 0; } - if (player->nospellinstant ) - { - return 0; - } - if (player->onlyoneinstant ) - { - if (player->castcount >= 1) - { - return 0; - } - } - if (player->nocreatureinstant && card->hasType("creature")) - { - return 0; - } + if (player->castrestrictedcreature && card->hasType("creature")) { return 0; @@ -709,7 +674,7 @@ int MTGMorphCostRule::reactToClick(MTGCardInstance * card) copy->toughness = 2; player->castedspellsthisturn += 1; player->opponent()->castedspellsthisturn += 1; - if (player->onlyonecast || player->onlyoneinstant) + if (player->onlyonecast) { player->castcount += 1; diff --git a/projects/mtg/src/Player.cpp b/projects/mtg/src/Player.cpp index 4ad722e34..ba8aa834c 100644 --- a/projects/mtg/src/Player.cpp +++ b/projects/mtg/src/Player.cpp @@ -21,9 +21,6 @@ Damageable(20) onlyoneboth = false; onlyonecast = false; castcount = 0; - nocreatureinstant = false; - nospellinstant = false; - onlyoneinstant = false; poisonCount = 0; damageCount = 0; preventable = 0;