diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index 599c06209..45832aff8 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -392,6 +392,7 @@ Mimics#2.txt mirri_the_cursed.txt mirri_the_cursed2_i284.txt misc01.txt +misty_rainforest_i604.txt moat.txt mobile_fort.txt Morph#1.txt diff --git a/projects/mtg/bin/Res/test/misty_rainforest_i604.txt b/projects/mtg/bin/Res/test/misty_rainforest_i604.txt new file mode 100644 index 000000000..7261334a8 --- /dev/null +++ b/projects/mtg/bin/Res/test/misty_rainforest_i604.txt @@ -0,0 +1,21 @@ +#Bug: Misty Rainforst triggers the "one land per turn" limit +# see http://code.google.com/p/wagic/issues/detail?id=604 +[INIT] +FIRSTMAIN +[PLAYER1] +inplay:misty rainforest +library:island +hand:forest +[PLAYER2] +[DO] +misty rainforest +island +forest +[ASSERT] +FIRSTMAIN +[PLAYER1] +life:19 +graveyard:misty rainforest +inplay:island,forest +[PLAYER2] +[END] \ No newline at end of file diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 9e8bddd18..bbbbbe428 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -4746,7 +4746,7 @@ public: { counter = NEW TypeTargetChooser("land"); - landsPlayedThisTurn = source->controller()->game->inPlay->seenThisTurn(counter); + landsPlayedThisTurn = source->controller()->game->inPlay->seenThisTurn(counter, Constants::CAST_ALL); PlayRestrictions * restrictions = source->controller()->game->playRestrictions; landsRestriction = restrictions->getMaxPerTurnRestrictionByTargetChooser(counter); restrictions->removeRestriction(landsRestriction); @@ -4764,7 +4764,7 @@ public: int trigger() { - int landsPlayedThisTurnUpdated = source->controller()->game->inPlay->seenThisTurn(counter); + int landsPlayedThisTurnUpdated = source->controller()->game->inPlay->seenThisTurn(counter, Constants::CAST_ALL); if (landsPlayedThisTurnUpdated > 1 && landsPlayedThisTurnUpdated > landsPlayedThisTurn) { landsPlayedThisTurn = landsPlayedThisTurnUpdated; diff --git a/projects/mtg/include/MTGCardInstance.h b/projects/mtg/include/MTGCardInstance.h index 5156e489c..4dfa934e3 100644 --- a/projects/mtg/include/MTGCardInstance.h +++ b/projects/mtg/include/MTGCardInstance.h @@ -46,6 +46,7 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable { int XX; int alternateCostPaid[ManaCost::MANA_PAID_WITH_RETRACE + 1]; int paymenttype; + int castMethod; /* Tells if the card reached its current zone by being cast or not (brought into the zone by an effect). non 0 == cast, 0 == not cast */ int frozen; int sunburst; int equipment; @@ -94,6 +95,9 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable { MTGCardInstance * next; int doDamageTest; int summoningSickness; + + bool matchesCastFilter(int castMethod); + // The recommended method to test for summoning Sickness ! int hasSummoningSickness(); MTGCardInstance * changeController(Player * newcontroller); diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index 8429645c4..05bc2623b 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -228,6 +228,23 @@ SNOWSWAMPWALK = 90, }; + enum{ + NOT_CAST = 0, + CAST_NORMALLY = 1, + CAST_WITH_KICKER = 2, + CAST_WITH_ALTERNATIVE = 3, + CAST_WITH_BUYBACK = 4, + CAST_WITH_FLASHBACK = 5, + CAST_WITH_RETRACE = 6, + CAST_WITH_MORPH = 7, + CAST_WITH_SUSPEND = 8, + + CAST_ALTERNATE = -1, //matches all alternate costs, including itself + CAST_ALL = -2, // matches everything except NOT_CAST + CAST_DONT_CARE = -3 //matches everything + + }; + static char MTGColorChars[]; static const char* MTGColorStrings[]; static int _r[], _g[], _b[]; diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index 32fb74c66..d92fd11cf 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -99,8 +99,8 @@ class MTGGameZone { int hasX(); //How many cards matching a TargetChooser have been put in this zone during the turn - int seenThisTurn(TargetChooser * tc); - int seenThisTurn(string s); + int seenThisTurn(TargetChooser * tc, int castFilter = Constants::CAST_DONT_CARE); + int seenThisTurn(string s, int castFilter = Constants::CAST_DONT_CARE); void setOwner(Player * player); MTGCardInstance * lastCardDrawn; diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index 310dba2fd..27bbb2e65 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -201,11 +201,14 @@ Interruptible(0) cost = NEW ManaCost(); tc = NULL; from = _source->getCurrentZone(); + payResult = ManaCost::MANA_UNPAID; + source->castMethod = Constants::NOT_CAST; } Spell::Spell(int id, MTGCardInstance * _source, TargetChooser * tc, ManaCost * _cost, int payResult) : Interruptible(id), tc(tc), cost(_cost), payResult(payResult) { + if (!cost) cost = NEW ManaCost(); source = _source; mHeight = 40; type = ACTION_SPELL; @@ -215,6 +218,20 @@ Interruptible(id), tc(tc), cost(_cost), payResult(payResult) if(tc && tc->targets[i] != NULL) _source->backupTargets[i] = tc->targets[i]; } + + // fill information on how the card came into this zone. Right now the quickest way is to do it here, based on how the mana was paid... + switch(payResult) { + case ManaCost::MANA_UNPAID: + source->castMethod = Constants::NOT_CAST; + break; + case ManaCost::MANA_PAID: + case ManaCost::MANA_PAID_WITH_KICKER: + source->castMethod = Constants::CAST_NORMALLY; + break; + default: + source->castMethod = Constants::CAST_ALTERNATE; + break; + } } int Spell::computeX(MTGCardInstance * card) @@ -275,7 +292,13 @@ int Spell::resolve() if (!source->hasType("instant") && !source->hasType("sorcery")) { Player * p = source->controller(); + int castMethod = source->castMethod; source = p->game->putInZone(source, from, p->game->battlefield); + + // We need to get the information about the cast method on both the card in the stack AND the card in play, + //so we copy it from the previous card (in the stack) to the new one (in play). + source->castMethod = castMethod; + from = p->game->battlefield; } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 8d6de9f5c..bee45b62e 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -65,7 +65,7 @@ int MTGAbility::allowedToCast(MTGCardInstance * card,Player * player) return 0; break; case CASTED_A_SPELL: - if(player->game->stack->seenThisTurn("*") < 1) + if(player->game->stack->seenThisTurn("*", Constants::CAST_ALL) < 1) return 0; break; case ONE_OF_AKIND: diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index 791333333..bfe6e7abc 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -43,6 +43,7 @@ MTGCardInstance::MTGCardInstance(MTGCard * card, MTGPlayerCards * arg_belongs_to life = toughness; preventable = 0; flanked = 0; + castMethod = Constants::NOT_CAST; } void MTGCardInstance::copy(MTGCardInstance * card) @@ -146,6 +147,8 @@ void MTGCardInstance::initMTGCI() damageToController = false; wasDealtDamage = false; suspended = false; + castMethod = Constants::NOT_CAST; + for (int i = 0; i < ManaCost::MANA_PAID_WITH_RETRACE +1; i++) alternateCostPaid[i] = 0; @@ -883,7 +886,7 @@ int MTGCardInstance::toggleDefenser(MTGCardInstance * opponent) { if(opponent->view != NULL) { - //todo: qoute wololo "change this into a cool blinking effects when opposing creature has cursor focus." + //todo: quote wololo "change this into a cool blinking effects when opposing creature has cursor focus." opponent->view->actZ += .8f; opponent->view->actT -= .2f; } @@ -896,6 +899,16 @@ int MTGCardInstance::toggleDefenser(MTGCardInstance * opponent) return 0; } +bool MTGCardInstance::matchesCastFilter(int castFilter) { + if(castFilter == Constants::CAST_DONT_CARE) + return true; //everything + if(castFilter == Constants::CAST_ALL) + return (castMethod != Constants::NOT_CAST); //everything except "not cast" + if (castFilter == Constants::CAST_ALTERNATE && castMethod > Constants::CAST_NORMALLY) + return true; //all alternate casts + return (castFilter == castMethod); +}; + int MTGCardInstance::addProtection(TargetChooser * tc) { tc->targetter = NULL; diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 5c15934b5..34d83eebb 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -608,7 +608,7 @@ int MTGGameZone::hasAbility(int ability) return 0; } -int MTGGameZone::seenThisTurn(TargetChooser * tc) +int MTGGameZone::seenThisTurn(TargetChooser * tc, int castMethod) { //The following 2 lines modify the passed TargetChooser. Call this function with care :/ tc->setAllZones(); // This is to allow targetting cards without caring about the actual zone @@ -617,17 +617,18 @@ int MTGGameZone::seenThisTurn(TargetChooser * tc) int count = 0; for (vector::iterator iter = cardsSeenThisTurn.begin(); iter != cardsSeenThisTurn.end(); ++iter) { - if (tc->canTarget(*iter)) + MTGCardInstance * c = (*iter); + if (c->matchesCastFilter(castMethod) && tc->canTarget(c)) count++; } return count; } -int MTGGameZone::seenThisTurn(string targetChooserDefinition) +int MTGGameZone::seenThisTurn(string targetChooserDefinition, int castMethod) { TargetChooserFactory tcf; TargetChooser *tc = tcf.createTargetChooser(targetChooserDefinition, NULL); - int result = seenThisTurn(tc); + int result = seenThisTurn(tc, castMethod); delete(tc); return result; } diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index e8668035f..a515631a7 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -130,7 +130,7 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) if (card->hasType("land")) { MTGCardInstance * copy = player->game->putInZone(card, player->game->hand, player->game->temp); - Spell * spell = NEW Spell(copy); + Spell * spell = NEW Spell(0,copy,NULL,NULL, payResult); spell->resolve(); delete spellCost; delete spell; @@ -151,7 +151,7 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) if (card->has(Constants::STORM)) { - int storm = player->game->stack->seenThisTurn("*") + player->opponent()->game->stack->seenThisTurn("*"); + int storm = player->game->stack->seenThisTurn("*", Constants::CAST_ALL) + player->opponent()->game->stack->seenThisTurn("*", Constants::CAST_ALL); ManaCost * spellCost = player->getManaPool(); for (int i = storm; i > 1; i--) { @@ -298,7 +298,7 @@ int MTGAlternativeCostRule::reactToClick(MTGCardInstance * card, ManaCost *alter if (card->hasType("land")) { MTGCardInstance * copy = player->game->putInZone(card, card->currentZone, player->game->temp); - Spell * spell = NEW Spell(copy); + Spell * spell = NEW Spell(0,copy,NULL,NULL, alternateCostType); copy->alternateCostPaid[alternateCostType] = 1; spell->resolve(); SAFE_DELETE(spell); @@ -316,7 +316,7 @@ int MTGAlternativeCostRule::reactToClick(MTGCardInstance * card, ManaCost *alter if (card->has(Constants::STORM)) { - int storm = player->game->stack->seenThisTurn("*") + player->opponent()->game->stack->seenThisTurn("*"); + int storm = player->game->stack->seenThisTurn("*", Constants::CAST_ALL) + player->opponent()->game->stack->seenThisTurn("*", Constants::CAST_ALL); for (int i = storm; i > 1; i--) { game->mLayers->stackLayer()->addSpell(copy, NULL, playerMana, alternateCostType, 1); diff --git a/projects/mtg/src/PlayRestrictions.cpp b/projects/mtg/src/PlayRestrictions.cpp index 63beb3d17..fd9f113ac 100644 --- a/projects/mtg/src/PlayRestrictions.cpp +++ b/projects/mtg/src/PlayRestrictions.cpp @@ -31,7 +31,7 @@ int MaxPerTurnRestriction::canPutIntoZone(MTGCardInstance * card, MTGGameZone * if (maxPerTurn == NO_MAX) return PlayRestriction::CAN_PLAY; - if (zone->seenThisTurn(tc) >= maxPerTurn) + if (zone->seenThisTurn(tc, Constants::CAST_ALL) >= maxPerTurn) return PlayRestriction::CANT_PLAY; return PlayRestriction::CAN_PLAY;