From f7eded74176bfac3bed5a13a1ce0f7e8702bdf3d Mon Sep 17 00:00:00 2001 From: pankdm Date: Fri, 18 Oct 2013 06:37:09 +0000 Subject: [PATCH] Devotion mechanics (thanks to excessum for patch) + some refactoring: extrManaCost --> ExtraManaCost unattachCost --> UnattachCost --- projects/mtg/bin/Res/sets/primitives/mtg.txt | 10 +++ projects/mtg/bin/Res/test/_tests.txt | 1 + .../mtg/bin/Res/test/generic/devotion.txt | 30 ++++++++ projects/mtg/include/AllAbilities.h | 20 +++++- projects/mtg/include/ExtraCost.h | 21 +++--- projects/mtg/include/MTGGameZones.h | 1 + projects/mtg/include/ManaCost.h | 10 ++- projects/mtg/include/ManaCostHybrid.h | 1 + projects/mtg/src/AIPlayerBaka.cpp | 8 +-- projects/mtg/src/AllAbilities.cpp | 2 +- projects/mtg/src/ExtraCost.cpp | 58 ++++++++-------- projects/mtg/src/GameObserver.cpp | 4 +- projects/mtg/src/MTGGameZones.cpp | 24 ++++++- projects/mtg/src/ManaCost.cpp | 69 +++++++++++++++---- projects/mtg/src/ManaCostHybrid.cpp | 8 +++ projects/mtg/src/TargetChooser.cpp | 2 +- 16 files changed, 203 insertions(+), 66 deletions(-) create mode 100644 projects/mtg/bin/Res/test/generic/devotion.txt diff --git a/projects/mtg/bin/Res/sets/primitives/mtg.txt b/projects/mtg/bin/Res/sets/primitives/mtg.txt index d2172cb6e..042eb6180 100644 --- a/projects/mtg/bin/Res/sets/primitives/mtg.txt +++ b/projects/mtg/bin/Res/sets/primitives/mtg.txt @@ -37348,6 +37348,16 @@ mana={1}{G}{G} type=Enchantment [/card] [card] +name=Gray Merchant of Asphodel +auto=life:-type:manab:mybattlefield opponent && life:type:manab controller +text=When Gray Merchant of Asphodel enters the battlefield, each opponent loses X life, where X is your devotion to black. You gain life equal to the life lost this way. (Each {B} in the mana costs of permanents you control counts toward your devotion to black.) +mana={3}{B}{B} +type=Creature +subtype=Zombie +power=2 +toughness=4 +[/card] +[card] name=Gray Ogre mana={2}{R} type=Creature diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index 5930038a1..40466c7ca 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -14,6 +14,7 @@ generic/changeling_i501.txt generic/cycling.txt generic/cycling2.txt generic/deathtouch.txt +generic/devotion.txt generic/doesnotuntap.txt generic/doesnotuntap2.txt generic/double_strike.txt diff --git a/projects/mtg/bin/Res/test/generic/devotion.txt b/projects/mtg/bin/Res/test/generic/devotion.txt new file mode 100644 index 000000000..879acd18d --- /dev/null +++ b/projects/mtg/bin/Res/test/generic/devotion.txt @@ -0,0 +1,30 @@ +# Testing Devotion mechanic +# also checking that hybrid cost and phyrexian cost are handled correctly +# total devotion should be 2 + 1 + 1 = 4 + +# Gray Merchant of Asphodel +# When Gray Merchant of Asphodel enters the battlefield, each opponent loses X life, where X is your devotion to black. You gain life equal to the life lost this way. (Each {B} in the mana costs of permanents you control counts toward your devotion to black.) + +# Reaper King +# mana={2W}{2U}{2B}{2R}{2G} + +# Vault Skirge +# mana={1}{p(B)} + +[INIT] +firstmain +[PLAYER1] +hand:gray merchant of asphodel +inplay:Reaper King,Vault Skirge +manapool:{3}{B}{B} +[PLAYER2] +[DO] +gray merchant of asphodel +[ASSERT] +firstmain +[PLAYER1] +life:24 +inplay:gray merchant of asphodel,Reaper King,Vault Skirge +[PLAYER2] +life:16 +[END] \ No newline at end of file diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 4475ed7a0..569b3e9a5 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -183,7 +183,12 @@ private: } else { - replace(theType.begin(), theType.end(), ':', '|'); + replace(theType.begin(), theType.end(), ':', '|'); + } + int color = 0; + if (theType.find("mana") != string::npos) { + color = ManaCost::parseManaSymbol(theType[4]); + theType.replace(0, 5, "*"); } TargetChooserFactory tf(card->getObserver()); TargetChooser * tc = tf.createTargetChooser(theType.c_str(),NULL); @@ -194,8 +199,17 @@ private: for (int k = 0; k < 4; k++) { MTGGameZone * zone = zones[k]; - if(tc->targetsZone(zone,target)) - intValue += zone->countByCanTarget(tc); + if (tc->targetsZone(zone, target)) + { + if (color) + { + intValue += zone->countTotalManaSymbols(tc, color); + } + else + { + intValue += zone->countByCanTarget(tc); + } + } } } SAFE_DELETE(tc); diff --git a/projects/mtg/include/ExtraCost.h b/projects/mtg/include/ExtraCost.h index e08f5939f..01c54619a 100644 --- a/projects/mtg/include/ExtraCost.h +++ b/projects/mtg/include/ExtraCost.h @@ -61,15 +61,15 @@ public: }; //extraextra -class extraManaCost : public ExtraCost +class ExtraManaCost : public ExtraCost { public: - extraManaCost(ManaCost * cost = NULL); + ExtraManaCost(ManaCost * cost = NULL); virtual int tryToSetPayment(MTGCardInstance * card); virtual int isPaymentSet(); virtual int canPay(); virtual int doPay(); - virtual extraManaCost * clone() const; + virtual ExtraManaCost * clone() const; }; class SacrificeCost : public ExtraCost @@ -90,15 +90,18 @@ public: virtual LifeCost * clone() const; }; -//pyrhaixa mana +//phyrexian mana class LifeorManaCost : public ExtraCost { -public: - LifeorManaCost(TargetChooser *_tc = NULL,string manaType = ""); +private: string manaType; + +public: + LifeorManaCost(TargetChooser *_tc = NULL, string manaType = ""); virtual int canPay(); virtual int doPay(); virtual LifeorManaCost * clone() const; + ManaCost * getManaCost(); }; //Discard a random card cost @@ -157,15 +160,15 @@ public: }; //unattach cost -class unattachCost : public ExtraCost +class UnattachCost : public ExtraCost { public: - unattachCost(MTGCardInstance * realSource = NULL); + UnattachCost(MTGCardInstance * realSource = NULL); MTGCardInstance * rSource; virtual int isPaymentSet(); virtual int canPay(); virtual int doPay(); - virtual unattachCost * clone() const; + virtual UnattachCost * clone() const; }; //tap cost class TapCost : public ExtraCost diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index dbb7798b7..9970ff4d9 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -97,6 +97,7 @@ class MTGGameZone { unsigned int countByType(const char * value); unsigned int countByCanTarget(TargetChooser * tc); + unsigned int countTotalManaSymbols(TargetChooser * tc, int color); MTGCardInstance * findByName(string name); //returns true if one of the cards in the zone has the ability diff --git a/projects/mtg/include/ManaCost.h b/projects/mtg/include/ManaCost.h index 8934c8593..f714904ba 100644 --- a/projects/mtg/include/ManaCost.h +++ b/projects/mtg/include/ManaCost.h @@ -56,6 +56,8 @@ public: string alternativeName; bool isMulti; static ManaCost * parseManaCost(string value, ManaCost * _manacost = NULL, MTGCardInstance * c = NULL); + static int parseManaSymbol(char symbol); + virtual void resetCosts(); void x(); int hasX(); @@ -69,15 +71,17 @@ public: ManaCost(ManaCost * _manaCost); ManaCost(const ManaCost& manaCost); ManaCost& operator= (const ManaCost& manaCost); - void copy (ManaCost * _manaCost); + void copy(ManaCost * _manaCost); int isNull(); int getConvertedCost(); string toString(); int getCost(int color); + int getManaSymbols(int color); + //Returns NULL if i is greater than nbhybrids ManaCostHybrid * getHybridCost(unsigned int i); int hasColor(int color); - int remove (int color, int value); + int remove(int color, int value); int add(int color, int value); // @@ -92,7 +96,7 @@ public: ExtraCost * getExtraCost(unsigned int i); int addHybrid(int c1, int v1, int c2, int v2); - int tryToPayHybrids(std::vector& _hybrids, int _nbhybrids,std::vector& diff); + int tryToPayHybrids(const std::vector &_hybrids, int _nbhybrids, std::vector& diff); void randomDiffHybrids(ManaCost * _cost, std::vector& diff); int add(ManaCost * _cost); int remove(ManaCost * _cost); diff --git a/projects/mtg/include/ManaCostHybrid.h b/projects/mtg/include/ManaCostHybrid.h index 041d6b26a..5d7afff43 100644 --- a/projects/mtg/include/ManaCostHybrid.h +++ b/projects/mtg/include/ManaCostHybrid.h @@ -18,6 +18,7 @@ public: int hasColor(int color); string toString(); int getConvertedCost(); + int getManaSymbols(int color); friend std::ostream& operator<<(std::ostream& out, ManaCostHybrid& m); friend std::ostream& operator<<(std::ostream& out, ManaCostHybrid* m); diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index fe4ee88e8..2940e8ad8 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -1356,8 +1356,8 @@ int AIPlayerBaka::selectAbility() { if(observer->mExtraPayment && observer->mExtraPayment->source->controller() == this) { - extraManaCost * check = NULL; - check = dynamic_cast(observer->mExtraPayment->costs[0]); + ExtraManaCost * check = NULL; + check = dynamic_cast(observer->mExtraPayment->costs[0]); if(check) { vector CostToPay = canPayMana(observer->mExtraPayment->source,check->costToPay); @@ -2564,8 +2564,8 @@ int AIPlayerBaka::Act(float dt) { if(observer->mExtraPayment && observer->mExtraPayment->source->controller() == this) { - extraManaCost * check = NULL; - check = dynamic_cast(observer->mExtraPayment->costs[0]); + ExtraManaCost * check = NULL; + check = dynamic_cast(observer->mExtraPayment->costs[0]); if(check) { vector CostToPay = canPayMana(observer->mExtraPayment->source,check->costToPay); diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 0daf5b9a1..f7685cfe2 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -3516,7 +3516,7 @@ int MenuAbility::reactToChoiceClick(Targetable * object,int choice,int control) toPay = NEW ManaCost(); if(optionalCosts[i]->extraCosts) toPay->extraCosts = optionalCosts[i]->extraCosts->clone(); - toPay->addExtraCost(NEW extraManaCost(NEW ManaCost(optionalCosts[i]))); + toPay->addExtraCost(NEW ExtraManaCost(NEW ManaCost(optionalCosts[i]))); toPay->setExtraCostsAction(this,source); game->mExtraPayment = toPay->extraCosts; return 0; diff --git a/projects/mtg/src/ExtraCost.cpp b/projects/mtg/src/ExtraCost.cpp index f4267ee68..148067eb3 100644 --- a/projects/mtg/src/ExtraCost.cpp +++ b/projects/mtg/src/ExtraCost.cpp @@ -7,6 +7,7 @@ #include "Player.h" #include "Counters.h" #include "AllAbilities.h" +#include SUPPORT_OBJECT_ANALYTICS(ExtraCost) @@ -69,23 +70,23 @@ int ExtraCost::setPayment(MTGCardInstance * card) return result; } //extra added manacost, or add a manacost as the cost of extra -extraManaCost * extraManaCost::clone() const +ExtraManaCost * ExtraManaCost::clone() const { - extraManaCost * ec = NEW extraManaCost(*this); + ExtraManaCost * ec = NEW ExtraManaCost(*this); return ec; } -extraManaCost::extraManaCost(ManaCost * costToPay) +ExtraManaCost::ExtraManaCost(ManaCost * costToPay) : ExtraCost("Pay The Cost",NULL, costToPay) { } -int extraManaCost::tryToSetPayment(MTGCardInstance * card) +int ExtraManaCost::tryToSetPayment(MTGCardInstance * card) { return 1; } -int extraManaCost::isPaymentSet() +int ExtraManaCost::isPaymentSet() { if (!source->controller()->getManaPool()->canAfford(costToPay)) { @@ -94,7 +95,7 @@ int extraManaCost::isPaymentSet() return 1; } -int extraManaCost::canPay() +int ExtraManaCost::canPay() { if(!source->controller()->getManaPool()->canAfford(costToPay)) { @@ -103,7 +104,7 @@ int extraManaCost::canPay() return 1; } -int extraManaCost::doPay() +int ExtraManaCost::doPay() { if (!source->controller()->getManaPool()->canAfford(costToPay)) return 0; @@ -159,7 +160,15 @@ LifeorManaCost * LifeorManaCost::clone() const return ec; } -LifeorManaCost::LifeorManaCost(TargetChooser *_tc,string manaType) +ManaCost * LifeorManaCost::getManaCost() +{ + string buildType ="{"; + buildType.append(manaType); + buildType.append("}"); + return ManaCost::parseManaCost(buildType); +} + +LifeorManaCost::LifeorManaCost(TargetChooser *_tc, string manaType) : ExtraCost("Phyrexian Mana", _tc), manaType(manaType) { } @@ -167,16 +176,11 @@ LifeorManaCost::LifeorManaCost(TargetChooser *_tc,string manaType) int LifeorManaCost::canPay() { MTGCardInstance * _target = (MTGCardInstance *) target; - string buildType ="{"; - buildType.append(manaType); - buildType.append("}"); - ManaCost * newCost = ManaCost::parseManaCost(buildType); - if(_target->controller()->getManaPool()->canAfford(newCost) || _target->controller()->life > 1) + boost::scoped_ptr manaCost(getManaCost()); + if (_target->controller()->getManaPool()->canAfford(manaCost.get()) || _target->controller()->life > 1) { - SAFE_DELETE(newCost); return 1; } - SAFE_DELETE(newCost); return 0; } @@ -186,24 +190,22 @@ int LifeorManaCost::doPay() return 0; MTGCardInstance * _target = (MTGCardInstance *) target; - string buildType ="{"; - buildType.append(manaType); - buildType.append("}"); - ManaCost * newCost = ManaCost::parseManaCost(buildType); - if(_target->controller()->getManaPool()->canAfford(newCost)) + ManaCost * manaCost = getManaCost(); + if (_target->controller()->getManaPool()->canAfford(manaCost)) { - _target->controller()->getManaPool()->pay(newCost); + _target->controller()->getManaPool()->pay(manaCost); } else { _target->controller()->loseLife(2); } - SAFE_DELETE(newCost); + SAFE_DELETE(manaCost); target = NULL; if (tc) tc->initTargets(); return 1; } + //discard a card at random as a cost //DiscardRandom cost DiscardRandomCost * DiscardRandomCost::clone() const @@ -404,18 +406,18 @@ int MillExileCost::doPay() } //unattach cost -unattachCost * unattachCost::clone() const +UnattachCost * UnattachCost::clone() const { - unattachCost * ec = NEW unattachCost(*this); + UnattachCost * ec = NEW UnattachCost(*this); return ec; } -unattachCost::unattachCost(MTGCardInstance * realSource) +UnattachCost::UnattachCost(MTGCardInstance * realSource) : ExtraCost("Unattach"),rSource(realSource) { } -int unattachCost::isPaymentSet() +int UnattachCost::isPaymentSet() { if (rSource && !rSource->target) { @@ -424,12 +426,12 @@ int unattachCost::isPaymentSet() return 1; } -int unattachCost::canPay() +int UnattachCost::canPay() { return isPaymentSet(); } -int unattachCost::doPay() +int UnattachCost::doPay() { MTGCardInstance * _source = (MTGCardInstance *) source; if(_source != rSource) diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index d4708a4ff..8a851acc1 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -1275,9 +1275,9 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object, bool lo break; } } - extraManaCost * costType = NULL; + ExtraManaCost * costType = NULL; if( mExtraPayment && mExtraPayment->costs.size()) - costType = dynamic_cast(mExtraPayment->costs[0]); + costType = dynamic_cast(mExtraPayment->costs[0]); if (WaitForExtraPayment(card) && !costType) { diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index e204978f4..ea8da1258 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -549,17 +549,37 @@ unsigned int MTGGameZone::countByCanTarget(TargetChooser * tc) { if(!tc) return 0; - tc->targetter = NULL;//becuase we are counting what can be targeted by this TC, we don't care if cards have protection. + // we don't care if cards have protection. + bool withoutProtections = true; int result = 0; for (int i = 0; i < (nb_cards); i++) { - if (tc->canTarget(cards[i])) + if (tc->canTarget(cards[i]), withoutProtections) { result++; } } return result; } + +unsigned int MTGGameZone::countTotalManaSymbols(TargetChooser * tc, int color) +{ + if (!tc) { + return 0; + } + // we don't care if cards have protection. + bool withoutProtections = true; + int result = 0; + for (int i = 0; i < nb_cards; i++) + { + if (tc->canTarget(cards[i]), withoutProtections) + { + result += cards[i]->getManaCost()->getManaSymbols(color); + } + } + return result; +} + MTGCardInstance * MTGGameZone::findByName(string name) { for (int i = 0; i < (nb_cards); i++) diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index f88bdc363..d3e2e3b69 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -8,6 +8,7 @@ #include "WEvent.h" #include "MTGAbility.h" #include "iterator" +#include SUPPORT_OBJECT_ANALYTICS(ManaCost) @@ -238,7 +239,7 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan { if(value == "unattach") { - manaCost->addExtraCost(NEW unattachCost(c)); + manaCost->addExtraCost(NEW UnattachCost(c)); break; } int intvalue = atoi(value.c_str()); @@ -431,6 +432,7 @@ void ManaCost::specificX(int color) xColor = color; cost[Constants::NB_Colors] = 1; } + int ManaCost::hasSpecificX() { if (cost.size() <= (size_t)Constants::NB_Colors) @@ -578,6 +580,47 @@ int ManaCost::getCost(int color) return cost[color]; } +int ManaCost::getManaSymbols(int color) +{ + int result = cost[color]; + for (size_t i = 0; i < hybrids.size(); ++i) + { + result += hybrids[i].getManaSymbols(color); + } + if (extraCosts && extraCosts->costs.size()) + { + for (size_t i = 0; i < extraCosts->costs.size(); ++i) + { + LifeorManaCost * phyrexianMana = dynamic_cast(extraCosts->costs[i]); + if (phyrexianMana) + { + boost::scoped_ptr manaCost(phyrexianMana->getManaCost()); + result += manaCost->getManaSymbols(color); + } + } + } + return result; +} + +int ManaCost::parseManaSymbol(char symbol) +{ + switch (symbol) + { + case 'g': + return Constants::MTG_COLOR_GREEN; + case 'u': + return Constants::MTG_COLOR_BLUE; + case 'r': + return Constants::MTG_COLOR_RED; + case 'b': + return Constants::MTG_COLOR_BLACK; + case 'w': + return Constants::MTG_COLOR_WHITE; + } + DebugTrace( "Failed to parse mana symbol" ); + return -1; +} + ManaCostHybrid * ManaCost::getHybridCost(unsigned int i) { if (hybrids.size() <= i) @@ -620,7 +663,7 @@ int ManaCost::isNull() int ManaCost::getConvertedCost() { int result = 0; - for ( int i = 0; i < Constants::NB_Colors; i++) + for (int i = 0; i < Constants::NB_Colors; i++) { result += cost[i]; } @@ -628,15 +671,15 @@ int ManaCost::getConvertedCost() { result += hybrids[i].getConvertedCost(); } - if(extraCosts && extraCosts->costs.size()) - { - for(unsigned int i = 0; i < extraCosts->costs.size();i++) - { - ExtraCost * pMana = dynamic_cast(extraCosts->costs[i]); - if(pMana) - result++; - } - } + if (extraCosts && extraCosts->costs.size()) + { + for (unsigned int i = 0; i < extraCosts->costs.size(); i++) + { + ExtraCost * pMana = dynamic_cast(extraCosts->costs[i]); + if (pMana) + result++; + } + } return result; } @@ -801,12 +844,12 @@ void ManaCost::randomDiffHybrids(ManaCost * _cost, std::vector& diff) /** starting from the end of the array (diff) */ -int ManaCost::tryToPayHybrids(std::vector& _hybrids, int _nbhybrids, std::vector& diff) +int ManaCost::tryToPayHybrids(const std::vector& _hybrids, int _nbhybrids, std::vector& diff) { if (!_nbhybrids) return 1; int result = 0; - ManaCostHybrid& h = _hybrids[_nbhybrids - 1]; + const ManaCostHybrid& h = _hybrids[_nbhybrids - 1]; if (diff[h.color1 * 2 + 1] >= h.value1) { diff[h.color1 * 2 + 1] -= h.value1; diff --git a/projects/mtg/src/ManaCostHybrid.cpp b/projects/mtg/src/ManaCostHybrid.cpp index 6b4dea2a5..5d65df10e 100644 --- a/projects/mtg/src/ManaCostHybrid.cpp +++ b/projects/mtg/src/ManaCostHybrid.cpp @@ -50,6 +50,14 @@ int ManaCostHybrid::getConvertedCost() return value1; } +int ManaCostHybrid::getManaSymbols(int color) +{ + // we assume that color1 and color2 are different + if (color1 == color) return value1; + if (color2 == color) return value2; + return 0; +} + int ManaCostHybrid::hasColor(int color) { if (((color1 == color) && value1) || ((color2 == color) && value2)) diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index ef8d205b6..e48cbb911 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -813,7 +813,7 @@ TargetChooser::TargetChooser(GameObserver *observer, MTGCardInstance * card, int //Default targetter : every card can be targetted, unless it is protected from the targetter card // For spells that do not "target" a specific card, set targetter to NULL -bool TargetChooser::canTarget(Targetable * target,bool withoutProtections) +bool TargetChooser::canTarget(Targetable * target, bool withoutProtections) { if (!target) return false; if (MTGCardInstance * card = dynamic_cast(target))