diff --git a/projects/mtg/include/AIHints.h b/projects/mtg/include/AIHints.h index 4bd4dd788..bc4123877 100644 --- a/projects/mtg/include/AIHints.h +++ b/projects/mtg/include/AIHints.h @@ -16,6 +16,7 @@ class AIHint public: string mCondition; string mAction; + string mCombatAttackTip; int mSourceId; AIHint(string line); }; @@ -36,6 +37,7 @@ protected: public: AIHints (AIPlayerBaka * player); AIAction * suggestAbility(ManaCost * potentialMana); + bool HintSaysDontAttack(GameObserver* observer,MTGCardInstance * card = NULL); void add(string line); ~AIHints(); }; diff --git a/projects/mtg/include/AIPlayerBaka.h b/projects/mtg/include/AIPlayerBaka.h index ca20c5e90..b3e003d16 100644 --- a/projects/mtg/include/AIPlayerBaka.h +++ b/projects/mtg/include/AIPlayerBaka.h @@ -81,7 +81,7 @@ class AIPlayerBaka: public AIPlayer{ //Tries to play an ability recommended by the deck creator virtual int selectHintAbility(); - virtual vector canPayMana(MTGCardInstance * card = NULL,ManaCost * mCost = NULL); + virtual vector canPayMana(MTGCardInstance * card = NULL,ManaCost * mCost = NULL, mapusedCards = map()); virtual vector canPaySunBurst(ManaCost * mCost = NULL); virtual MTGCardInstance * chooseCard(TargetChooser * tc, MTGCardInstance * source, int random = 0); diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index bad435579..484591b05 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -54,6 +54,11 @@ private: s = s.substr(1); multiplier = -1; } + if(s[0] == '+') + { + //ignore "+" signs.... + s = s.substr(1); + } //rounding values, the words can be written anywhere in the line, //they are erased after parsing. if(s.find("halfup") != string::npos) @@ -858,6 +863,18 @@ public: /* Generic classes */ + +class ANewAffinity: public MTGAbility +{ +public: + string tcString; + string manaString; + ANewAffinity(GameObserver* observer, int _id, MTGCardInstance * _source,string Tc = "", string mana =""); + void Update(float dt); + int testDestroy(); + ANewAffinity * clone() const; +}; + //if/ifnot Cond then EFFECT class IfThenAbility: public ActivatedAbility { @@ -993,11 +1010,13 @@ class AACloner: public ActivatedAbility public: int who; string with; + string types; list awith; list colors; + list typesToAdd; AACloner(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL, int who = 0, - string abilitiesStringList = ""); + string abilitiesStringList = "",string typeslist = ""); int resolve(); const char * getMenuText(); virtual ostream& toString(ostream& out) const; @@ -1650,6 +1669,11 @@ public: int addToGame() { MTGCardInstance * _target = (MTGCardInstance *) target; + if(PT.size()) + { + SAFE_DELETE(wppt); + wppt = NEW WParsedPT(PT,NULL,(MTGCardInstance *) source); + } _target->power += wppt->power.getValue(); _target->addToToughness(wppt->toughness.getValue()); if(_target->has(Constants::INDESTRUCTIBLE) && wppt->toughness.getValue() < 0 && _target->toughness <= 0) @@ -4906,50 +4930,25 @@ public: class ABushidoAbility: public MTGAbility { public: - MTGCardInstance * opponents[20]; - int nbOpponents; - int PowerModifier; - int ToughnessModifier; + string PowerToughnessModifier; - ABushidoAbility(GameObserver* observer, int _id, MTGCardInstance * _source, int _PowerModifier, int _ToughnessModifier) : + ABushidoAbility(GameObserver* observer, int _id, MTGCardInstance * _source, string _PowerToughnessModifier) : MTGAbility(observer, _id, _source) { - PowerModifier = _PowerModifier; - ToughnessModifier = _ToughnessModifier; - nbOpponents = 0; + PowerToughnessModifier = _PowerToughnessModifier; } - int receiveEvent(WEvent * event) - { - if (dynamic_cast (event)) + int receiveEvent(WEvent * event) { - MTGCardInstance * opponent = source->getNextOpponent(); - if (!opponent) return 0; - source->power += PowerModifier; - source->addToToughness(ToughnessModifier); - while (opponent) + if (dynamic_cast (event)) { - opponents[nbOpponents] = opponent; - nbOpponents++; - opponent = source->getNextOpponent(opponent); + MTGCardInstance * opponent = source->getNextOpponent(); + if (!opponent) return 0; + PTInstant * a = NEW PTInstant(game, this->GetId(), source, source,NEW WParsedPT(PowerToughnessModifier,NULL,source)); + GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source,source, a); + wrapper->addToGame(); } + return 1; } - else if (WEventPhaseChange* pe = dynamic_cast(event)) - { - if (MTG_PHASE_AFTER_EOT == pe->to->id && nbOpponents) - { - source->power -= PowerModifier; - source->addToToughness(-ToughnessModifier); - nbOpponents = 0; - } - } - return 1; - } - - virtual ostream& toString(ostream& out) const - { - out << "ABushidoAbility ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " ("; - return MTGAbility::toString(out) << ")"; - } ABushidoAbility * clone() const { @@ -4999,14 +4998,24 @@ public: AInstantControlSteal(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) : InstantAbility(observer, _id, _source, _target) { - TrueController = _target->controller(); - TheftController = source->controller(); - MTGCardInstance * copy = _target->changeController(TheftController); - target = copy; - source->target = copy; - copy->summoningSickness = 0; + } + int resolve() + { + MTGCardInstance * _theftTarget = (MTGCardInstance*)target; + if(_theftTarget) + { + TrueController = _theftTarget->controller(); + TheftController = source->controller(); + MTGCardInstance * copy = _theftTarget->changeController(TheftController); + target = copy; + source->target = copy; + copy->summoningSickness = 0; + } + return 1; + } + int destroy() { MTGCardInstance * _target = (MTGCardInstance *) target; diff --git a/projects/mtg/include/Counters.h b/projects/mtg/include/Counters.h index a4d9b04ae..92ea68e7f 100644 --- a/projects/mtg/include/Counters.h +++ b/projects/mtg/include/Counters.h @@ -19,6 +19,7 @@ public: int init(MTGCardInstance * _target, const char * _name, int _power, int _toughness); bool sameAs(const char * _name, int _power, int _toughness); bool cancels(int _power, int _toughness); + int cancelCounter(int power, int toughness); int added(); int removed(); }; diff --git a/projects/mtg/include/ExtraCost.h b/projects/mtg/include/ExtraCost.h index a3eb18347..c7e9b0b7a 100644 --- a/projects/mtg/include/ExtraCost.h +++ b/projects/mtg/include/ExtraCost.h @@ -197,6 +197,7 @@ class Ninja : public ExtraCost { public: Ninja(TargetChooser *_tc = NULL); + virtual int canPay(); virtual int isPaymentSet(); virtual int doPay(); virtual Ninja * clone() const; diff --git a/projects/mtg/include/MTGCardInstance.h b/projects/mtg/include/MTGCardInstance.h index 67cd6fc18..c8c5e37e5 100644 --- a/projects/mtg/include/MTGCardInstance.h +++ b/projects/mtg/include/MTGCardInstance.h @@ -47,6 +47,7 @@ protected: public: vectorparentCards; vectorchildrenCards; + vectorcardsAbilities; int setAttacker(int value); int setDefenser(MTGCardInstance * c); @@ -95,6 +96,7 @@ public: int MaxLevelUp; int kicked; bool isDualWielding; + bool stillNeeded; Player * lastController; MTGGameZone * getCurrentZone(); MTGGameZone * previousZone; diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index cce334dda..90c9a29b7 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -210,8 +210,9 @@ class Constants SNOWSWAMPWALK = 90, CANATTACK = 91, HYDRA = 92, + UNDYING = 93, - NB_BASIC_ABILITIES = 93, + NB_BASIC_ABILITIES = 94, RARITY_S = 'S', //Special Rarity diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index b95a43e5c..4f8647dcb 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -75,6 +75,8 @@ class MTGGameZone { //list of cards that have been through this zone in the current turn vector cardsSeenThisTurn; + //list of cards that have been through this zone in the last turn + vector cardsSeenLastTurn; int nb_cards; MTGGameZone(); ~MTGGameZone(); @@ -104,8 +106,9 @@ class MTGGameZone { bool hasX(); //How many cards matching a TargetChooser have been put in this zone during the turn - int seenThisTurn(TargetChooser * tc, int castFilter = Constants::CAST_DONT_CARE); - int seenThisTurn(string s, int castFilter = Constants::CAST_DONT_CARE); + int seenThisTurn(TargetChooser * tc, int castFilter = Constants::CAST_DONT_CARE,bool lastTurn = false); + int seenThisTurn(string s, int castFilter = Constants::CAST_DONT_CARE); + int seenLastTurn(string s, int castFilter = Constants::CAST_DONT_CARE); void setOwner(Player * player); MTGCardInstance * lastCardDrawn; @@ -175,6 +178,7 @@ public: MTGRemovedFromGame * removedFromGame; MTGRemovedFromGame * exile; //alias to removedFromZone MTGGameZone * garbage; + MTGGameZone * garbageLastTurn; MTGGameZone * temp; MTGPlayerCards(); diff --git a/projects/mtg/include/ManaCost.h b/projects/mtg/include/ManaCost.h index 756cd5b5e..7f229b19d 100644 --- a/projects/mtg/include/ManaCost.h +++ b/projects/mtg/include/ManaCost.h @@ -56,6 +56,7 @@ public: virtual void reinit(); void x(); int hasX(); + int hasAnotherCost(); ManaCost(std::vector& _cost, int nb_elems = 1); ManaCost(); ~ManaCost(); diff --git a/projects/mtg/src/AIHints.cpp b/projects/mtg/src/AIHints.cpp index ac5380149..4501cdd1a 100644 --- a/projects/mtg/src/AIHints.cpp +++ b/projects/mtg/src/AIHints.cpp @@ -31,6 +31,12 @@ AIHint::AIHint(string _line) mAction = action; mSourceId = 0; } + + vector splitDontAttack = parseBetween(action, "dontattackwith(", ")"); + if(splitDontAttack.size()) + { + mCombatAttackTip = splitDontAttack[1]; + } } AIHints::AIHints(AIPlayerBaka * player): mPlayer(player) @@ -62,6 +68,27 @@ AIHint * AIHints::getByCondition (string condition) return NULL; } +bool AIHints::HintSaysDontAttack(GameObserver* observer,MTGCardInstance * card) +{ + int count = 0; + TargetChooserFactory tfc(observer); + TargetChooser * hintTc = NULL; + for(unsigned int i = 0; i < hints.size();i++) + { + if (hints[i]->mCombatAttackTip.size()) + { + hintTc = tfc.createTargetChooser(hints[i]->mCombatAttackTip,card); + if(hintTc && hintTc->canTarget(card,true)) + { + SAFE_DELETE(hintTc); + return true; + } + SAFE_DELETE(hintTc); + } + } + return false; +} + //return true if a given ability matches a hint's description //Eventually this will look awfully similar to the parser...any way to merge them somehow ? bool AIHints::abilityMatches(MTGAbility * ability, AIHint * hint) @@ -159,6 +186,11 @@ string AIHints::constraintsNotFulfilled(AIAction * action, AIHint * hint, ManaCo if (!action) { + if (hint->mCombatAttackTip.size()) + { + out << "to see if this can attack[" << hint->mCombatAttackTip << "]"; + return out.str(); + } if (hint->mSourceId && !findSource(hint->mSourceId)) { out << "needcardinplay[" << hint->mSourceId << "]"; @@ -220,6 +252,8 @@ AIAction * AIHints::findAbilityRecursive(AIHint * hint, ManaCost * potentialMana } string s = constraintsNotFulfilled(a, hint, potentialMana); + if (hint->mCombatAttackTip.size()) + return NULL; if (s.size()) { SAFE_DELETE(a); diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 4fcb15b27..e7ef337cd 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -8,6 +8,7 @@ #include "GuiCombat.h" #include "AIHints.h" #include "ManaCostHybrid.h" +#include "MTGRules.h" // // AIAction @@ -602,7 +603,10 @@ int OrderedAIAction::getEfficiency() //Decrease chance of using ability if there is an extra cost to use the ability, ignore tap } } - + if (MTGPutInPlayRule * pip = dynamic_cast(a)) + { + efficiency += 65; + } return efficiency; } @@ -714,7 +718,7 @@ bool AIPlayerBaka::payTheManaCost(ManaCost * cost, MTGCardInstance * target,vect } if(k == gotPayments.size()-1)//only add it once, and at the end. paid->add(this->getManaPool());//incase some of our payments were mana already in the pool/. - if(paid->canAfford(cost) && (!cost->hasX() || k == gotPayments.size()-1)) + if(paid->canAfford(cost) && (!cost->hasX() && !cost->hasAnotherCost()) || k == gotPayments.size()-1) { SAFE_DELETE(paid); for(size_t clicking = 0; clicking < clicks.size(); ++clicking) @@ -830,12 +834,12 @@ ManaCost * AIPlayerBaka::getPotentialMana(MTGCardInstance * target) return result; } -vector AIPlayerBaka::canPayMana(MTGCardInstance * target,ManaCost * cost) +vector AIPlayerBaka::canPayMana(MTGCardInstance * target,ManaCost * cost, mapusedCards ) { if(!cost || (cost && !cost->getConvertedCost())) return vector(); ManaCost * result = NEW ManaCost(); - map used; + map used = usedCards; vectorpayments = vector(); if (this->getManaPool()->getConvertedCost()) { @@ -1003,6 +1007,34 @@ vector AIPlayerBaka::canPayMana(MTGCardInstance * target,ManaCost * return payments;//we didn't meet one of the color cost requirements. } } + if(cost->kicker && !usedCards.size()) + { + + ManaCost * withKickerCost= NEW ManaCost(cost->kicker); + int canKick = 0; + vectorkickerPayment; + bool keepLooking = true; + while(keepLooking) + { + kickerPayment = canPayMana(target,withKickerCost,used); + if(kickerPayment.size()) + { + for(unsigned int w = 0;w < kickerPayment.size();++w) + { + if(!used[kickerPayment[w]->source]) + { + payments.push_back(kickerPayment[w]); + used[kickerPayment[w]->source] = true; + } + } + canKick += 1; + keepLooking = cost->kicker->isMulti; + } + else + keepLooking = false; + } + SAFE_DELETE(withKickerCost); + } SAFE_DELETE(check); SAFE_DELETE(checkResult); } @@ -1612,7 +1644,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty int currentCost = card->getManaCost()->getConvertedCost(); int hasX = card->getManaCost()->hasX(); gotPayments.clear(); - if(!pMana->canAfford(card->getManaCost())) + if((!pMana->canAfford(card->getManaCost()) || card->getManaCost()->kicker)) gotPayments = canPayMana(card,card->getManaCost()); //for preformence reason we only look for specific mana if the payment couldn't be made with pmana. if ((currentCost > maxCost || hasX) && (gotPayments.size() || pMana->canAfford(card->getManaCost()))) @@ -1671,8 +1703,6 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty // shouldPlay == baka_effect_bad giving it a 1 for odd ball lottery chance. shouldPlayPercentage = 1; } - DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl - <<"shouldPlayPercentage = "<< shouldPlayPercentage); } //Reduce the chances of playing a spell with X cost if available mana is low @@ -1685,18 +1715,12 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty } if(card->getManaCost() && card->getManaCost()->kicker && card->getManaCost()->kicker->isMulti) { - - ManaCost * withKickerCost= NEW ManaCost(card->getManaCost()); - withKickerCost->add(withKickerCost->kicker); - int canKick = 0; - while(pMana->canAfford(withKickerCost)) - { - withKickerCost->add(withKickerCost->kicker); - canKick += 1; - } - SAFE_DELETE(withKickerCost); - shouldPlayPercentage = 10*canKick; + shouldPlayPercentage = 10* size_t(gotPayments.size())/int(1+(card->getManaCost()->getConvertedCost()+card->getManaCost()->kicker->getConvertedCost())); + if(shouldPlayPercentage < 40) + shouldPlayPercentage = shouldPlayPercentage/3; } + DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl + <<"shouldPlayPercentage = "<< shouldPlayPercentage); if(card->getRestrictions().size()) { AbilityFactory af(observer); @@ -1708,7 +1732,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty int chance = randomChance % 100; if (chance > shouldPlayPercentage) continue; - if(shouldPlayPercentage < 10) + if(shouldPlayPercentage <= 10) { DebugTrace("shouldPlayPercentage was less than 10 this was a lottery roll on RNG"); } @@ -1720,7 +1744,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty } if(nextCardToPlay) { - if(!pMana->canAfford(nextCardToPlay->getManaCost())) + if(!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->kicker) gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost()); DebugTrace(" AI wants to play card." << endl << "- Next card to play: " << (nextCardToPlay ? nextCardToPlay->name : "None" ) << endl ); @@ -2002,6 +2026,8 @@ int AIPlayerBaka::chooseAttackers() MTGCardInstance * card = NULL; while ((card = cd.nextmatch(game->inPlay, card))) { + if(hints && hints->HintSaysDontAttack(observer,card)) + continue; observer->cardClick(card, MTGAbility::MTG_ATTACK_RULE); } } diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index 1e63178bd..86cd321cd 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -200,6 +200,7 @@ Interruptible(observer, 0) mHeight = 40; type = ACTION_SPELL; cost = NEW ManaCost(); + cost->extraCosts = NULL; tc = NULL; from = _source->getCurrentZone(); payResult = ManaCost::MANA_UNPAID; @@ -209,7 +210,11 @@ Interruptible(observer, 0) Spell::Spell(GameObserver* observer, int id, MTGCardInstance * _source, TargetChooser * tc, ManaCost * _cost, int payResult) : Interruptible(observer, id), tc(tc), cost(_cost), payResult(payResult) { - if (!cost) cost = NEW ManaCost(); + if (!cost) + { + cost = NEW ManaCost(); + cost->extraCosts = NULL; + } source = _source; mHeight = 40; type = ACTION_SPELL; @@ -291,12 +296,13 @@ int Spell::resolve() { Player * p = source->controller(); int castMethod = source->castMethod; + vectorbackupTgt = source->backupTargets; 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; - + source->backupTargets = backupTgt; from = p->game->battlefield; } @@ -309,7 +315,6 @@ int Spell::resolve() JSoundSystem::GetInstance()->PlaySample(sample); } } - AbilityFactory af(observer); af.addAbilities(observer->mLayers->actionLayer()->getMaxId(), this); return 1; diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 15e748475..f3a96245d 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -290,9 +290,9 @@ AACounter::AACounter(GameObserver* observer, int id, MTGCardInstance * source, M AbilityFactory af(game); if(counterstring.size()) { - Counter * checkcounter = af.parseCounter(counterstring, source, NULL); - nb = checkcounter->nb; - delete checkcounter; + Counter * checkcounter = af.parseCounter(counterstring, source, NULL); + nb = checkcounter->nb; + delete checkcounter; } if (nb > 0) { @@ -314,22 +314,40 @@ AACounter::AACounter(GameObserver* observer, int id, MTGCardInstance * source, M } } } - else - { - for (int i = 0; i < -nb; i++) + else { - while (_target->next) - _target = _target->next; - _target->counters->removeCounter(name.c_str(), power, toughness); + for (int i = 0; i < -nb; i++) + { + while (_target->next) + _target = _target->next; + _target->counters->removeCounter(name.c_str(), power, toughness); + } } + + _target->doDamageTest = 1; + if(!_target->afterDamage()) + { + //If a creature with +1/+1 counters on it gets enough -1/-1 counters to kill it, + //it dies before the two counters have the chance to cancel out. For example, + //if your Strangleroot Geist with a +1/+1 counter on it got three -1/-1 counters + //from Skinrender's "enters the battlefield" ability, the Geist would die with // + //one +1/+1 counter and three -1/-1 counters and wouldn't return to the battlefield. + for (int i = 0; i < _target->counters->mCount; i++) + { + if (_target->counters->counters[i]->cancels(power, toughness) && !name.size() && _target->counters->counters[i]->nb > 0) + { + _target->counters->counters[i]->cancelCounter(power,toughness); + } + } + } + + //specail cases, indestructible creatures which recieve enough counters to kill it are destroyed as a state based effect + if(_target->toughness <= 0 && _target->has(Constants::INDESTRUCTIBLE) && toughness < 0) + _target->controller()->game->putInGraveyard(_target); + return nb; } - //specail cases, indestructible creatures which recieve enough counters to kill it are destroyed as a state based effect - if(_target->toughness <= 0 && _target->has(Constants::INDESTRUCTIBLE) && toughness < 0) - _target->controller()->game->putInGraveyard(_target); - return nb; + return 0; } - return 0; -} const char* AACounter::getMenuText() { @@ -975,6 +993,7 @@ int AAMorph::resolve() else { a->addToGame(); + _target->cardsAbilities.push_back(a); } } } @@ -1040,23 +1059,16 @@ int AAFlip::resolve() MTGCardInstance * myFlip = NEW MTGCardInstance(fcard, _target->controller()->game); _target->name = myFlip->name; _target->colors = myFlip->colors; - _target->power = (myFlip->power + _target->power) - _target->origpower; - _target->addToToughness(myFlip->toughness - _target->origtoughness); _target->types = myFlip->types; _target->text = myFlip->text; _target->formattedText = myFlip->formattedText; ActionLayer * al = game->mLayers->actionLayer(); - for (int k = (int)(al->mObjects.size()) - 1; k > 0; k--) + for(unsigned int i = 0;i < _target->cardsAbilities.size();i++) { - MTGAbility * a = dynamic_cast(game->mLayers->actionLayer()->mObjects[k]); - if(a && a->source == _target) - { - a->forceDestroy = 1; - a->destroy(); - a->removeFromGame(); - al->removeFromGame(a); - } + MTGAbility * a = dynamic_cast(_target->cardsAbilities[i]); + if(a) game->removeObserver(a); } + _target->cardsAbilities.clear(); _target->magicText = myFlip->magicText; af.getAbilities(¤tAbilities, NULL, _target); for (size_t i = 0; i < currentAbilities.size(); ++i) @@ -1073,12 +1085,43 @@ int AAFlip::resolve() else { a->addToGame(); + _target->cardsAbilities.push_back(a); } } } - SAFE_DELETE(myFlip); + //power + int powerMod = 0; + int toughMod = 0; + bool powerlessThanOriginal = false; + bool toughLessThanOriginal = false; + if(_target->power < _target->origpower) + { + powerMod = _target->origpower - _target->power; + powerlessThanOriginal = true; + } + else + { + powerMod =_target->power - _target->origpower; + } + //toughness + if(_target->toughness <= _target->origtoughness) + { + toughMod = _target->origtoughness - _target->toughness; + toughLessThanOriginal = true; + } + else + { + toughMod =_target->toughness - _target->origtoughness; + } + _target->power = powerlessThanOriginal?myFlip->power - powerMod:myFlip->power + powerMod; + _target->life = toughLessThanOriginal?myFlip->toughness - toughMod:myFlip->toughness + toughMod; + _target->toughness = toughLessThanOriginal?myFlip->toughness - toughMod:myFlip->toughness + toughMod; + _target->origpower = myFlip->origpower; + _target->origtoughness = myFlip->origtoughness; + SAFE_DELETE(myFlip); + _target->mPropertiesChangedSinceLastUpdate = true; } - _target->mPropertiesChangedSinceLastUpdate = true; + currentAbilities.clear(); testDestroy(); } @@ -1642,7 +1685,7 @@ AALifeSet::~AALifeSet() //AACloner //cloning...this makes a token thats a copy of the target. AACloner::AACloner(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int who, - string abilitiesStringList) : + string abilitiesStringList,string TypesList) : ActivatedAbility(observer, _id, _source, _cost, 0), who(who) { aType = MTGAbility::CLONING; @@ -1653,6 +1696,10 @@ AACloner::AACloner(GameObserver* observer, int _id, MTGCardInstance * _source, M PopulateAbilityIndexVector(awith, abilitiesStringList); PopulateColorIndexVector(colors, abilitiesStringList); } + if (TypesList.size()) + { + PopulateSubtypesIndexVector(typesToAdd,TypesList); + } } @@ -1688,6 +1735,10 @@ int AACloner::resolve() { spell->source->setColor(*it); } + for (it = typesToAdd.begin(); it != typesToAdd.end(); it++) + { + spell->source->addType(*it); + } delete spell; return 1; @@ -2241,6 +2292,28 @@ AAWinGame * AAWinGame::clone() const //Generic Abilities +//a new affinity +ANewAffinity::ANewAffinity(GameObserver* observer, int _id, MTGCardInstance * _source, string Tc, string mana) : +MTGAbility(observer, _id, _source), tcString(Tc), manaString(mana) +{ +} + +void ANewAffinity::Update(float dt) +{ + testDestroy(); + return; +} + +int ANewAffinity::testDestroy() +{ + if(this->source->isInPlay(game)) + return 1; + return 0; +} +ANewAffinity * ANewAffinity::clone() const +{ + return NEW ANewAffinity(*this); +} //IfThenEffect IfThenAbility::IfThenAbility(GameObserver* observer, int _id, MTGAbility * delayedAbility, MTGCardInstance * _source, Targetable * _target, int type,string Cond) : @@ -3713,7 +3786,7 @@ APhaseAction::~APhaseAction() // the main ability APhaseActionGeneric::APhaseActionGeneric(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once) : - InstantAbility(observer, _id, source, target) + InstantAbility(observer, _id, card, target) { MTGCardInstance * _target = target; ability = NEW APhaseAction(game, _id, card,_target, sAbility, restrictions, _phase,forcedestroy,next,myturn,opponentturn,once); diff --git a/projects/mtg/src/Counters.cpp b/projects/mtg/src/Counters.cpp index e1f996b1d..f0c3a08af 100644 --- a/projects/mtg/src/Counters.cpp +++ b/projects/mtg/src/Counters.cpp @@ -37,6 +37,21 @@ bool Counter::cancels(int _power, int _toughness) return (power == -_power && toughness == -_toughness); } +int Counter::cancelCounter(int power, int toughness) +{ + while(this->target->counters->hasCounter(power,toughness) && this->target->counters->hasCounter(power*-1,toughness*-1)) + { + GameObserver *g = this->target->getObserver(); + this->removed(); + this->nb--; + WEvent * t = NEW WEventCounters(NULL,"",power*-1,toughness*-1,false,true); + dynamic_cast(t)->targetCard = this->target; + g->receiveEvent(t); + this->target->counters->removeCounter(power,toughness); + } + return 1; +} + int Counter::added() { if (power != 0 || toughness != 0) @@ -78,19 +93,6 @@ int Counters::addCounter(const char * _name, int _power, int _toughness) dynamic_cast(e)->targetCard = this->target; if (e == g->replacementEffects->replace(e)) { - for (int i = 0; i < mCount; i++) - { - if (counters[i]->cancels(_power, _toughness) && !counters[i]->name.size() && counters[i]->nb > 0) - { - counters[i]->removed(); - counters[i]->nb--; - WEvent * t = NEW WEventCounters(this,_name,_power*-1,_toughness*-1,false,true); - dynamic_cast(t)->targetCard = this->target; - g->receiveEvent(t); - delete(e); - return mCount; - } - } for (int i = 0; i < mCount; i++) { if (counters[i]->sameAs(_name, _power, _toughness)) @@ -111,6 +113,8 @@ int Counters::addCounter(const char * _name, int _power, int _toughness) dynamic_cast(w)->targetCard = this->target; g->receiveEvent(w); mCount++; + this->target->doDamageTest = 1; + this->target->afterDamage(); } delete(e); return mCount; diff --git a/projects/mtg/src/ExtraCost.cpp b/projects/mtg/src/ExtraCost.cpp index 4c10376ef..e50cf4438 100644 --- a/projects/mtg/src/ExtraCost.cpp +++ b/projects/mtg/src/ExtraCost.cpp @@ -533,6 +533,13 @@ ExtraCost("Select unblocked attacker", _tc) { } +int Ninja::canPay() +{ + if(source->getObserver()->getCurrentGamePhase() != MTG_PHASE_COMBATBLOCKERS) + return 0; + return 1; +} + int Ninja::isPaymentSet() { if (target && ((target->isAttacker() && target->isBlocked()) || diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index bfaa99135..87a07fa60 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -893,6 +893,48 @@ void GameObserver::Affinity() } } } + int reducem = 0; + bool resetCost = false; + for(unsigned int na = 0; na < card->cardsAbilities.size();na++) + { + ANewAffinity * newAff = dynamic_cast(card->cardsAbilities[na]); + if(newAff) + { + if(!resetCost) + { + resetCost = true; + card->getManaCost()->copy(original); + if(card->getManaCost()->extraCosts) + { + for(unsigned int i = 0; i < card->getManaCost()->extraCosts->costs.size();i++) + { + card->getManaCost()->extraCosts->costs[i]->setSource(card); + } + } + } + TargetChooserFactory tf(this); + TargetChooser * tcn = tf.createTargetChooser(newAff->tcString,card,NULL); + + for (int w = 0; w < 2; ++w) + { + Player *p = this->players[w]; + MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->stack, p->game->exile }; + for (int k = 0; k < 6; k++) + { + MTGGameZone * z = zones[k]; + if (tcn->targetsZone(z)) + { + reducem += z->countByCanTarget(tcn); + } + } + } + SAFE_DELETE(tcn); + ManaCost * removingCost = ManaCost::parseManaCost(newAff->manaString); + for(int j = 0; j < reducem; j++) + card->getManaCost()->remove(removingCost); + SAFE_DELETE(removingCost); + } + } if(card->has(Constants::AFFINITYARTIFACTS)|| card->has(Constants::AFFINITYFOREST)|| card->has(Constants::AFFINITYGREENCREATURES)|| @@ -930,6 +972,13 @@ void GameObserver::Affinity() type = "creature"; } card->getManaCost()->copy(original); + if(card->getManaCost()->extraCosts) + { + for(unsigned int i = 0; i < card->getManaCost()->extraCosts->costs.size();i++) + { + card->getManaCost()->extraCosts->costs[i]->setSource(card); + } + } int reduce = 0; if(card->has(Constants::AFFINITYGREENCREATURES)) { @@ -947,6 +996,7 @@ void GameObserver::Affinity() if(card->getManaCost()->getCost(color) > 0) card->getManaCost()->remove(color,1); } + } SAFE_DELETE(original); } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index e422452e4..cab3b2e2d 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -118,13 +118,19 @@ int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * playe } size_t typeRelated = restriction[i].find("type("); + size_t seenType = restriction[i].find("lastturn("); + size_t seenRelated = restriction[i].find("lastturn("); + if(seenRelated == string::npos) + seenRelated = restriction[i].find("thisturn("); + size_t compRelated = restriction[i].find("compare("); + size_t check = 0; - if(typeRelated != string::npos) + if(typeRelated != string::npos || seenRelated != string::npos || compRelated != string::npos) { int firstAmount = 0; int secondAmount = 0; int mod=0; - string type; + string rtc; vector comparasion = split(restriction[i],'~'); if(comparasion.size() != 3) return 0;//it was incorrectly coded, user should proofread card code. @@ -134,16 +140,26 @@ int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * playe for(unsigned int i = 0; i < comparasion.size(); i++) { check = comparasion[i].find("type("); + if(check == string::npos) + check = comparasion[i].find("lastturn("); + if(check == string::npos) + check = comparasion[i].find("thisturn("); + if(check == string::npos) + check = comparasion[i].find("compare("); if( check != string::npos) { size_t end = 0; - size_t found = comparasion[i].find("type("); - if (found != string::npos) + size_t foundType = comparasion[i].find("type("); + size_t foundComp = comparasion[i].find("compare("); + size_t foundSeen = comparasion[i].find("lastturn("); + if(foundSeen == string::npos) + foundSeen = comparasion[i].find("thisturn("); + if (foundType != string::npos) { - end = comparasion[i].find(")", found); - type = comparasion[i].substr(found + 5, end - found - 5).c_str(); + end = comparasion[i].find(")", foundType); + rtc = comparasion[i].substr(foundType + 5, end - foundType - 5).c_str(); TargetChooserFactory tcf(observer); - TargetChooser * ttc = tcf.createTargetChooser(type,card); + TargetChooser * ttc = tcf.createTargetChooser(rtc,card); mod = atoi(comparasion[i].substr(end+1).c_str()); if(i == 2) { @@ -158,6 +174,59 @@ int AbilityFactory::parseCastRestrictions(MTGCardInstance * card, Player * playe SAFE_DELETE(ttc); } + if (foundComp != string::npos) + { + end = comparasion[i].find(")", foundComp); + rtc = comparasion[i].substr(foundComp + 8, end - foundComp - 8).c_str(); + mod = atoi(comparasion[i].substr(end+1).c_str()); + if(i == 2) + { + WParsedInt * newAmount = NEW WParsedInt(rtc,card); + secondAmount = newAmount->getValue(); + secondAmount += mod; + SAFE_DELETE(newAmount); + } + else + { + WParsedInt * newAmount = NEW WParsedInt(rtc,card); + firstAmount = newAmount->getValue(); + firstAmount += mod; + SAFE_DELETE(newAmount); + } + } + if (foundSeen != string::npos) + { + end = comparasion[i].find(")", foundSeen); + rtc = comparasion[i].substr(foundSeen + 9, end - foundSeen - 9).c_str(); + mod = atoi(comparasion[i].substr(end+1).c_str()); + + TargetChooserFactory tcf(observer); + TargetChooser * stc = tcf.createTargetChooser(rtc,card); + for (int w = 0; w < 2; ++w) + { + Player *p = observer->players[w]; + MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->stack, p->game->exile }; + for (int k = 0; k < 6; k++) + { + MTGGameZone * z = zones[k]; + if (stc->targetsZone(z)) + { + if(i == 2) + { + secondAmount += seenType != string::npos ? z->seenLastTurn(rtc,Constants::CAST_ALL):z->seenThisTurn(rtc,Constants::CAST_ALL); + + } + else + { + firstAmount += seenType != string::npos ? z->seenLastTurn(rtc,Constants::CAST_ALL):z->seenThisTurn(rtc,Constants::CAST_ALL); + + } + } + } + } + i == 2 ? secondAmount += mod:firstAmount += mod; + SAFE_DELETE(stc); + } } else if (i == 2) { @@ -1094,8 +1163,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return parseMagicLine(s.substr(kAlternateCostKeywords[i].length()), id, spell, card); } } - - //if/ifnot COND then DO EFFECT. const string ifKeywords[] = {"if ", "ifnot "}; int checkIf[] = { 1, 2 }; @@ -1175,24 +1242,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { return parsePhaseActionAbility(s,card,spell,target,restrictions,id); } - //Multiple abilities for ONE cost - found = s.find("&&"); - if (found != string::npos) - { - SAFE_DELETE(tc); - vector multiEffects = split(s,'&'); - MultiAbility * multi = NEW MultiAbility(observer, id, card, target, NULL); - for(unsigned int i = 0;i < multiEffects.size();i++) - { - if(!multiEffects[i].empty()) - { - MTGAbility * addAbility = parseMagicLine(multiEffects[i], id, spell, card, activated); - multi->Add(addAbility); - } - } - multi->oneShot = 1; - return multi; - } int forcedalive = 0; //force an ability to ignore destroy while source is still valid. @@ -1300,6 +1349,26 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return NULL; } + //Multiple abilities for ONE cost + found = s.find("&&"); + if (found != string::npos) + { + SAFE_DELETE(tc); + vector multiEffects = split(s,'&'); + MultiAbility * multi = NEW MultiAbility(observer, id, card, target, NULL); + for(unsigned int i = 0;i < multiEffects.size();i++) + { + if(!multiEffects[i].empty()) + { + MTGAbility * addAbility = parseMagicLine(multiEffects[i], id, spell, card, activated); + multi->Add(addAbility); + } + } + multi->oneShot = 1; + return multi; + } + + //Lord, foreach, aslongas found = string::npos; @@ -1710,12 +1779,18 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (found != string::npos) { string with = ""; + string types = ""; vector splitWith = parseBetween(s, "with(", ")"); if (splitWith.size()) { with = splitWith[1]; } - MTGAbility * a = NEW AACloner(observer, id, card, target, 0, who, with); + vector splitTypes = parseBetween(s, "addtype(", ")"); + if (splitTypes.size()) + { + types = splitTypes[1]; + } + MTGAbility * a = NEW AACloner(observer, id, card, target, 0, who, with,types); a->oneShot = 1; return a; } @@ -1988,7 +2063,11 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG //combat damage spirit link if (s.find("spiritlink") != string::npos) { - bool combatOnly = (s.find("combatspiritlink") != string::npos); + bool combatOnly = false; + if(s.find("combatspiritlink") != string::npos) + { + combatOnly = true; + } return NEW ASpiritLinkAbility(observer, id, card, combatOnly); } @@ -1996,13 +2075,11 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG vector splitBushido = parseBetween(s, "bushido(", ")"); if (splitBushido.size()) { - int power, toughness; - if (!parsePowerToughness(splitBushido[1], &power, &toughness)) - { - DebugTrace("MTGAbility Parse error in bushido" << s); + string power, toughness; + vectorsplitPT = split(splitBushido[1],'/'); + if(!splitPT.size()) return NULL; - } - return NEW ABushidoAbility(observer, id, card, power, toughness); + return NEW ABushidoAbility(observer, id, card,splitBushido[1]); } //loseAbilities @@ -2271,7 +2348,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { if (card->hasType(Subtypes::TYPE_INSTANT) || card->hasType(Subtypes::TYPE_SORCERY) || forceUEOT) { - return NULL; //TODO + MTGAbility * aPF = NEW AProtectionFrom(observer, id, card, target, fromTc, splitProtection[1]); + return NEW GenericInstantAbility(observer, 1, card, (Damageable *) target, aPF); } return NEW AProtectionFrom(observer, id, card, target, fromTc, splitProtection[1]); } @@ -2317,6 +2395,20 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG } return NULL; //TODO } + + //affinity based on targetchooser + vector splitNewAffinity = parseBetween(s, "affinity(", ")"); + if (splitNewAffinity.size()) + { + string tcString = splitNewAffinity[1]; + string manaString = ""; + vector splitNewAffinityMana = parseBetween(splitNewAffinity[2], "reduce(", ")"); + if(splitNewAffinityMana.size()) + manaString = splitNewAffinityMana[1]; + if(!manaString.size()) + return NULL; + return NEW ANewAffinity(observer, id, card, tcString, manaString); + } //proliferate found = s.find("proliferate"); @@ -2444,6 +2536,15 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG a->oneShot = 1; return a; } + + found = s.find("steal"); + if (found != string::npos) + { + MTGAbility * a = NEW AInstantControlSteal(observer, id, card, target); + a->oneShot = 1; + return a; + } + DebugTrace(" no matching ability found. " << s); return NULL; } @@ -2910,6 +3011,13 @@ int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int else { a->addToGame(); + if (a->source) + a->source->cardsAbilities.push_back(a); + else if(spell && spell->source) + spell->source->cardsAbilities.push_back(a); + + //keep track of abilities being added to the game on each card it belongs to, this ignores p/t bonuses given + //from other cards, or ability bonuses, making it generally easier to strip a card of it's abilities. } } } diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index 7b2e40b08..71e55fc07 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -88,16 +88,17 @@ void MTGCardInstance::copy(MTGCardInstance * card) delete spell; mtgid = backupid; castMethod = castMethodBackUP; + backupTargets = this->backupTargets; } MTGCardInstance::~MTGCardInstance() { SAFE_DELETE(counters); - if (previous != NULL) - { - //DebugTrace("MTGCardInstance::~MTGCardInstance(): deleting " << ToHex(previous)); - SAFE_DELETE(previous); - } + if (previous != NULL) + { + //DebugTrace("MTGCardInstance::~MTGCardInstance(): deleting " << ToHex(previous)); + SAFE_DELETE(previous); + } } int MTGCardInstance::init() @@ -147,6 +148,7 @@ void MTGCardInstance::initMTGCI() suspended = false; castMethod = Constants::NOT_CAST; mPropertiesChangedSinceLastUpdate = false; + stillNeeded = true; kicked = 0; @@ -171,6 +173,7 @@ void MTGCardInstance::initMTGCI() regenerateTokens = 0; blocked = false; currentZone = NULL; + cardsAbilities = vector(); data = this; //an MTGCardInstance point to itself for data, allows to update it without killing the underlying database item if (basicAbilities[(int)Constants::CHANGELING]) @@ -487,7 +490,11 @@ int MTGCardInstance::stillInUse() return 1; if (!previous) return 0; - + if (previous->stillNeeded) + { + previous->stillNeeded = false; + return 1; + } return previous->stillInUse(); } diff --git a/projects/mtg/src/MTGDefinitions.cpp b/projects/mtg/src/MTGDefinitions.cpp index 6422722f5..e243b49b5 100644 --- a/projects/mtg/src/MTGDefinitions.cpp +++ b/projects/mtg/src/MTGDefinitions.cpp @@ -121,7 +121,8 @@ const char* Constants::MTGBasicAbilities[] = { "snowislandlandwalk", "snowswamplandwalk", "canattack", - "hydra" + "hydra", + "undying" }; map Constants::MTGBasicAbilitiesMap; diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 5da7f3365..1a5be155c 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -80,8 +80,8 @@ MTGPlayerCards::~MTGPlayerCards() void MTGPlayerCards::beforeBeginPhase() { - delete garbage; - garbage = NEW MTGGameZone(); + SAFE_DELETE(garbageLastTurn); + garbageLastTurn = garbage = NEW MTGGameZone(); garbage->setOwner(this->owner); library->beforeBeginPhase(); @@ -104,6 +104,7 @@ void MTGPlayerCards::setOwner(Player * player) removedFromGame->setOwner(player); stack->setOwner(player); garbage->setOwner(player); + garbageLastTurn->setOwner(player); temp->setOwner(player); } @@ -243,7 +244,8 @@ void MTGPlayerCards::drawFromLibrary() } } - putInZone(toMove, library, hand); + if(putInZone(toMove, library, hand)) + toMove->currentZone = hand; } void MTGPlayerCards::resetLibrary() @@ -264,6 +266,7 @@ void MTGPlayerCards::init() removedFromGame = NEW MTGRemovedFromGame(); exile = removedFromGame; garbage = NEW MTGGameZone(); + garbageLastTurn = garbage; temp = NEW MTGGameZone(); playRestrictions = NEW PlayRestrictions(); @@ -298,7 +301,6 @@ MTGCardInstance * MTGPlayerCards::putInHand(MTGCardInstance * card) return putInZone(card, card->currentZone, card->owner->game->hand); } - // Moves a card from one zone to another // If the card is not actually in the expected "from" zone, does nothing and returns null MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone * from, MTGGameZone * to) @@ -410,6 +412,8 @@ MTGGameZone::~MTGGameZone() { for (size_t i = 0; i < cards.size(); i++) { + cards[i]->stillNeeded = false; + SAFE_DELETE(cards[i]->previous); SAFE_DELETE( cards[i] ); } cards.clear(); @@ -419,6 +423,11 @@ MTGGameZone::~MTGGameZone() void MTGGameZone::beforeBeginPhase() { + cardsSeenLastTurn.clear(); + for(size_t k = 0; k < cardsSeenThisTurn.size(); k++) + { + cardsSeenLastTurn.push_back(cardsSeenThisTurn[k]); + } cardsSeenThisTurn.clear(); }; @@ -631,18 +640,30 @@ bool MTGGameZone::hasAbility(int ability) return false; } -int MTGGameZone::seenThisTurn(TargetChooser * tc, int castMethod) +int MTGGameZone::seenThisTurn(TargetChooser * tc, int castMethod, bool lastTurn) { //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 tc->targetter = NULL; int count = 0; - for (vector::iterator iter = cardsSeenThisTurn.begin(); iter != cardsSeenThisTurn.end(); ++iter) + if (lastTurn) { - MTGCardInstance * c = (*iter); - if (c->matchesCastFilter(castMethod) && tc->canTarget(c)) - count++; + for (vector::iterator iter = cardsSeenLastTurn.begin(); iter != cardsSeenLastTurn.end(); ++iter) + { + MTGCardInstance * c = (*iter); + if (c && c->matchesCastFilter(castMethod) && tc->canTarget(c)) + count++; + } + } + else + { + for (vector::iterator iter = cardsSeenThisTurn.begin(); iter != cardsSeenThisTurn.end(); ++iter) + { + MTGCardInstance * c = (*iter); + if (c->matchesCastFilter(castMethod) && tc->canTarget(c)) + count++; + } } return count; } @@ -651,11 +672,19 @@ int MTGGameZone::seenThisTurn(string targetChooserDefinition, int castMethod) { TargetChooserFactory tcf(owner->getObserver()); TargetChooser *tc = tcf.createTargetChooser(targetChooserDefinition, NULL); - int result = seenThisTurn(tc, castMethod); - delete(tc); + int result = seenThisTurn(tc, castMethod,false); + SAFE_DELETE(tc); return result; } +int MTGGameZone::seenLastTurn(string targetChooserDefinition, int castMethod) +{ + TargetChooserFactory tcf(owner->getObserver()); + TargetChooser *tc = tcf.createTargetChooser(targetChooserDefinition, NULL); + int result = seenThisTurn(tc, castMethod,true); + SAFE_DELETE(tc); + return result; +} void MTGGameZone::cleanupPhase() { diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 1072f72ee..5b4aa9901 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -1822,9 +1822,10 @@ int MTGPersistRule::receiveEvent(WEvent * event) { WEventZoneChange * e = (WEventZoneChange *) event; MTGCardInstance * card = e->card->previous; - if (card && card->basicAbilities[(int)Constants::PERSIST] && !card->counters->hasCounter(-1, -1)) + if (!card) return 0; + int ok = 0; + if((card->basicAbilities[(int)Constants::PERSIST] && !card->counters->hasCounter(-1, -1))||(card->basicAbilities[(int)Constants::UNDYING] && !card->counters->hasCounter(1, 1))) { - int ok = 0; for (int i = 0; i < 2; i++) { Player * p = game->players[i]; @@ -1833,20 +1834,24 @@ int MTGPersistRule::receiveEvent(WEvent * event) } if (!ok) return 0; + for (int i = 0; i < 2; i++) { Player * p = game->players[i]; if (e->to == p->game->graveyard) { - MTGCardInstance * copy = p->game->putInZone(e->card, p->game->graveyard, e->card->owner->game->temp); + MTGCardInstance * copy = p->game->putInZone(e->card, p->game->graveyard, e->card->owner->game->temp); if (!copy) { - DebugTrace("MTGRULES: couldn't move card for persist"); + DebugTrace("MTGRULES: couldn't move card for persist/undying"); return 0; } Spell * spell = NEW Spell(game, copy); spell->resolve(); - spell->source->counters->addCounter(-1, -1); + if(card->basicAbilities[(int)Constants::PERSIST]) + spell->source->counters->addCounter(-1, -1); + else + spell->source->counters->addCounter(1,1); delete spell; return 1; } diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index 3a06af2fd..fa75fd581 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -371,6 +371,20 @@ int ManaCost::hasX() return cost[Constants::NB_Colors]; } +int ManaCost::hasAnotherCost() +{ + if (cost.size() <= (size_t)Constants::NB_Colors) + { + DebugTrace("Seems ManaCost was not properly initialized"); + return 0; + } + int result = 0; + if(kicker) + result = 1; + //kicker is the only one ai knows for now, later hasAnotherCost() can be used to determine other cost types. + return result; +} + void ManaCost::init() { int i; diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index 0d62784f7..b03852b9c 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -26,6 +26,19 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta return NEW CardTargetChooser(observer, target, card); }; + found = s.find("targetedplayer"); + if (found == 0) + { + if(card && card->backupTargets.size()) + { + Player * pTarget = dynamic_cast(card->backupTargets[0]); + if (ability) + pTarget = dynamic_cast(ability->target); + if(pTarget) + return NEW PlayerTargetChooser(observer, card, 1, pTarget); + } + }; + found = s.find("opponent"); if (found == 0) {