From f76c28fa643fdd3b7644bd8fd4dc35bce76717de Mon Sep 17 00:00:00 2001 From: zethfoxster Date: Fri, 1 Jul 2016 21:29:51 -0400 Subject: [PATCH] convoke other={convoke} name(Convoke) delve other={delve} they might be able to be added directly to the real manacost. added an ability that grants an ability while the source remains tapped grant ability grantend... added dethrone abilities=dethrone added support of multitargeting to extra cost, it acts the same as normal multitargeting, repeats dopay() the effects for each. --- projects/mtg/include/AllAbilities.h | 38 ++++- projects/mtg/include/ExtraCost.h | 22 ++- projects/mtg/include/MTGDefinitions.h | 3 +- projects/mtg/src/ActionLayer.cpp | 5 + projects/mtg/src/AllAbilities.cpp | 107 +++++++++++++ projects/mtg/src/ExtraCost.cpp | 207 +++++++++++++++++++++++++- projects/mtg/src/GameObserver.cpp | 57 +++++-- projects/mtg/src/MTGAbility.cpp | 19 +++ projects/mtg/src/MTGCardInstance.cpp | 1 + projects/mtg/src/MTGDefinitions.cpp | 3 +- projects/mtg/src/MTGRules.cpp | 5 + projects/mtg/src/ManaCost.cpp | 16 +- projects/mtg/src/TargetsList.cpp | 6 + 13 files changed, 467 insertions(+), 22 deletions(-) diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 9be81630a..0f48ceeee 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -166,8 +166,11 @@ private: { if(!s.size()) return; - if(!card) - return; + if (!card) + { + intValue = atoi(s.c_str());//if there is no card, try parsing a number. + return; + } MTGCardInstance * target = card->target; if(!card->storedCard) card->storedCard = card->storedSourceCard; @@ -5166,7 +5169,38 @@ public: ~AShackleWrapper(); }; +//Grant +class AGrant : public MTGAbility +{ +public: + MTGCardInstance * Blessed; + bool resolved; + MTGAbility * Granted; + MTGAbility * toGrant; + AGrant(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * toGrant); + void Update(float dt); + void resolveGrant(); + int resolve(); + const string getMenuText(); + AGrant * clone() const; + ~AGrant(); +private: + void removeGranted(MTGCardInstance *_target); +}; +//GrantWrapper +class AGrantWrapper : public InstantAbility +{ +public: + AGrant * ability; + MTGAbility * Granted; + AGrantWrapper(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * toGrant); + int resolve(); + const string getMenuText(); + AGrantWrapper * clone() const; + ~AGrantWrapper(); + +}; //ABlink class ABlink: public MTGAbility { diff --git a/projects/mtg/include/ExtraCost.h b/projects/mtg/include/ExtraCost.h index a42a3f779..8d8e8f2b1 100644 --- a/projects/mtg/include/ExtraCost.h +++ b/projects/mtg/include/ExtraCost.h @@ -274,7 +274,27 @@ public: virtual int doPay(); virtual Ninja * clone() const; }; - +//Convoke +class Convoke : public ExtraCost +{ +public: + ManaCost * getReduction(); + Convoke(TargetChooser *_tc = NULL); + virtual int canPay(); + virtual int isPaymentSet(); + virtual int doPay(); + virtual Convoke * clone() const; +}; +//delve +class Delve : public ExtraCost +{ +public: + Delve(TargetChooser *_tc = NULL); + virtual int canPay(); + virtual int isPaymentSet(); + virtual int doPay(); + virtual Delve * clone() const; +}; //offering cost class Offering : public ExtraCost { diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index ff19c6e15..e1bdca9cb 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -251,7 +251,8 @@ class Constants MENACE = 129, NOSOLO = 130,//cant attack alone MUSTBLOCK = 131,//blocks each turn - NB_BASIC_ABILITIES = 132, + DETHRONE = 132, + NB_BASIC_ABILITIES = 133, RARITY_S = 'S', //Special Rarity RARITY_M = 'M', //Mythics diff --git a/projects/mtg/src/ActionLayer.cpp b/projects/mtg/src/ActionLayer.cpp index cc68deee9..1af691e8b 100644 --- a/projects/mtg/src/ActionLayer.cpp +++ b/projects/mtg/src/ActionLayer.cpp @@ -105,6 +105,11 @@ bool ActionLayer::CheckUserInput(JButton key) //being cancelled. currently only menuability and paidability will care. } } + if (observer->mExtraPayment->costs.size() && observer->mExtraPayment->costs[0]->tc) + { + //if we cancel, clear the targets list so that when you try again you dont already have targets from before. + observer->mExtraPayment->costs[0]->tc->initTargets(); + } observer->mExtraPayment = NULL; return 1; } diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index b4088a9f4..496d9c33c 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -6581,6 +6581,113 @@ AShackleWrapper::~AShackleWrapper() SAFE_DELETE(ability); } +//grant +AGrant::AGrant(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * _Grant) : + MTGAbility(observer, _id, card) +{ + Granted = _Grant; + target = _target; + Blessed = NULL; + resolved = false; + toGrant = NULL; +} + +void AGrant::Update(float dt) +{ + if (resolved == false) + { + resolved = true; + resolveGrant(); + } + + if (!source->isTapped() || !source->isInPlay(game)) + { + if (Blessed == NULL || !Blessed->isInPlay(game)) + MTGAbility::Update(dt); + MTGCardInstance * _target = Blessed; + removeGranted(_target); + } + else + resolveGrant(); + MTGAbility::Update(dt); +} + +void AGrant::resolveGrant() +{ + if (toGrant) return; + MTGCardInstance * _target = (MTGCardInstance *)target; + if (_target) + { + toGrant = Granted->clone(); + toGrant->target = _target; + toGrant->addToGame(); + Blessed = _target; + } +} + +void AGrant::removeGranted(MTGCardInstance* _target) +{ + if (!toGrant) return; + MTGCardInstance * cardToReturn = _target; + game->removeObserver(toGrant); + game->removeObserver(this); + Blessed = NULL; + return; +} + +int AGrant::resolve() +{ + return 0; +} +const string AGrant::getMenuText() +{ + return Granted->getMenuText(); +} + +AGrant * AGrant::clone() const +{ + AGrant * a = NEW AGrant(*this); + a->forceDestroy = -1; + a->Granted = Granted->clone(); + return a; +}; +AGrant::~AGrant() +{ + SAFE_DELETE(Granted); +} + +AGrantWrapper::AGrantWrapper(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * _Grant) : + InstantAbility(observer, _id, source, _target), Granted(_Grant) +{ + ability = NEW AGrant(observer, _id, card, _target,_Grant); +} + +int AGrantWrapper::resolve() +{ + AGrant * a = ability->clone(); + a->target = target; + a->addToGame(); + return 1; +} + +const string AGrantWrapper::getMenuText() +{ + return "Grant"; +} + +AGrantWrapper * AGrantWrapper::clone() const +{ + AGrantWrapper * a = NEW AGrantWrapper(*this); + a->ability = this->ability->clone(); + a->oneShot = 1; + return a; +} + +AGrantWrapper::~AGrantWrapper() +{ + SAFE_DELETE(ability); +} + //a blink ABlink::ABlink(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, bool blinkueot, bool blinkForSource, bool blinkhand, MTGAbility * stored) : MTGAbility(observer, _id, card),blinkueot(blinkueot),blinkForSource(blinkForSource),blinkhand(blinkhand),stored(stored) diff --git a/projects/mtg/src/ExtraCost.cpp b/projects/mtg/src/ExtraCost.cpp index a0c5ca1b8..b45732e70 100644 --- a/projects/mtg/src/ExtraCost.cpp +++ b/projects/mtg/src/ExtraCost.cpp @@ -72,6 +72,10 @@ int ExtraCost::setPayment(MTGCardInstance * card) if (tc) { result = tc->addTarget(card); + //this is flawed logic, we need to fix. if there is a target in list + //we return targetready instead, the card is not pushed back into list + //how ever, it is made the target becuase the result is 1 even if we couldnt + //target it with the targetchooser. if (result) { target = card; @@ -748,7 +752,7 @@ int TapTargetCost::isPaymentSet() target = NULL; return 0; } - if (target) + if (target && (tc->getNbTargets() == tc->maxtargets || tc->done)) return 1; return 0; } @@ -922,6 +926,193 @@ int Ninja::doPay() //endbouncetargetcostforninja +//Convoke +Convoke * Convoke::clone() const +{ + Convoke * ec = NEW Convoke(*this); + if (tc) + ec->tc = tc->clone(); + return ec; +} + +Convoke::Convoke(TargetChooser *_tc) : + ExtraCost("Select Cards To Tap", _tc) +{ +} + +int Convoke::canPay() +{ + return isPaymentSet(); +} + +int Convoke::isPaymentSet() +{ + if (target && target->isTapped()) + { + tc->removeTarget(target); + target->isExtraCostTarget = false; + target = NULL; + return 0; + } + ManaCost * toReduce = getReduction(); + if (target && (!source->controller()->getManaPool()->canAfford(toReduce))) + { + target = NULL; + SAFE_DELETE(toReduce); + return 0; + } + if (target && (source->controller()->getManaPool()->canAfford(toReduce))) + { + SAFE_DELETE(toReduce); + return 1; + } + return 0; +} + +ManaCost * Convoke::getReduction() +{ + ManaCost * toReduce = NEW ManaCost(source->getManaCost()); + tc->maxtargets = source->getManaCost()->getConvertedCost(); + if (tc->getNbTargets()) + { + vectortargetlist = tc->getTargetsFrom(); + for (vector::iterator it = targetlist.begin(); it != targetlist.end(); it++) + { + bool next = false; + for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i) + { + if (next == true) + break; + MTGCardInstance * targetCard = dynamic_cast(*it); + if ((targetCard->getManaCost()->hasColor(i) || targetCard->hasColor(i)) && toReduce->hasColor(i)) + { + toReduce->remove(i, 1); + next = true; + } + else + { + toReduce->remove(Constants::MTG_COLOR_ARTIFACT, 1); + next = true; + } + } + } + //if we didnt find it payable one way, lets try again backwards. + if (!source->controller()->getManaPool()->canAfford(toReduce)) + { + SAFE_DELETE(toReduce); + toReduce = NEW ManaCost(source->getManaCost()); + for (vector::reverse_iterator it = targetlist.rbegin(); it != targetlist.rend(); it++) + { + bool next = false; + for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i) + { + if (next == true) + break; + MTGCardInstance * targetCard = dynamic_cast(*it); + if ((targetCard->getManaCost()->hasColor(i) || targetCard->hasColor(i)) && toReduce->hasColor(i)) + { + toReduce->remove(i, 1); + next = true; + } + else + { + toReduce->remove(Constants::MTG_COLOR_ARTIFACT, 1); + next = true; + } + } + } + } + } + return toReduce; +} + +int Convoke::doPay() +{ + if (target && tc->getNbTargets()) + { + ManaCost * toReduce = getReduction(); + target->controller()->getManaPool()->pay(toReduce); + SAFE_DELETE(toReduce); + vectortargetlist = tc->getTargetsFrom(); + for (vector::iterator it = targetlist.begin(); it != targetlist.end(); it++) + { + MTGCardInstance * targetCard = dynamic_cast(*it); + source->storedCard = targetCard->createSnapShot(); + targetCard->tap(); + } + if (tc) + tc->initTargets(); + return 1; + } + return 0; +} + +//DELVE +Delve * Delve::clone() const +{ + Delve * ec = NEW Delve(*this); + if (tc) + ec->tc = tc->clone(); + return ec; +} + +Delve::Delve(TargetChooser *_tc) : + ExtraCost("Select Cards To Exile", _tc) +{ +} + +int Delve::canPay() +{ + return isPaymentSet(); +} + +int Delve::isPaymentSet() +{ + ManaCost * toReduce = NEW ManaCost(source->getManaCost()); + tc->maxtargets = source->getManaCost()->getCost(Constants::MTG_COLOR_ARTIFACT); + if (tc->getNbTargets()) + { + toReduce->remove(Constants::MTG_COLOR_ARTIFACT, tc->getNbTargets()); + } + if (target && (!source->controller()->getManaPool()->canAfford(toReduce))) + { + target = NULL; + SAFE_DELETE(toReduce); + return 0; + } + if (target && (source->controller()->getManaPool()->canAfford(toReduce))) + { + SAFE_DELETE(toReduce); + return 1; + } + return 0; +} + +int Delve::doPay() +{ + if (target && tc->getNbTargets()) + { + ManaCost * toReduce = NEW ManaCost(source->getManaCost()); + + toReduce->remove(Constants::MTG_COLOR_ARTIFACT, tc->getNbTargets()); + + target->controller()->getManaPool()->pay(toReduce); + SAFE_DELETE(toReduce); + vectortargetlist = tc->getTargetsFrom(); + for (vector::iterator it = targetlist.begin(); it != targetlist.end(); it++) + { + MTGCardInstance * targetCard = dynamic_cast(*it); + source->storedCard = targetCard->createSnapShot(); + targetCard->controller()->game->putInExile(targetCard); + } + if (tc) + tc->initTargets(); + return 1; + } + return 0; +} + +/////////////// //Sacrifice target as cost for Offering Offering * Offering::clone() const { @@ -1214,7 +1405,7 @@ int ExtraCosts::tryToSetPayment(MTGCardInstance * card) } if (int result = costs[i]->setPayment(card)) { - card->isExtraCostTarget = true; + //card->isExtraCostTarget = true;//moved to gameobserver, flawed logic was setting this to true even when it wasnt really a target return result; } } @@ -1251,10 +1442,20 @@ int ExtraCosts::doPay() int result = 0; for (size_t i = 0; i < costs.size(); i++) { - if(costs[i]->target) + if(costs[i]->target)//todo deprecate this let gameobserver control this. { costs[i]->target->isExtraCostTarget = false; } + if (costs[i]->tc) + { + vectortargetlist = costs[i]->tc->getTargetsFrom(); + for (vector::iterator it = targetlist.begin(); it != targetlist.end(); it++) + { + costs[i]->target = dynamic_cast(*it); + costs[i]->doPay(); + } + } + else result += costs[i]->doPay(); } return result; diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index f3c4bf2ab..693b232c2 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -715,6 +715,27 @@ void GameObserver::gameStateBasedEffects() } } card->bypassTC = false; //turn off bypass + /////////////////////////// + //reset extracost shadows// + /////////////////////////// + card->isExtraCostTarget = false; + if (mExtraPayment != NULL) + { + for (unsigned int ec = 0; ec < mExtraPayment->costs.size(); ec++) + { + + if (mExtraPayment->costs[ec]->tc) + { + vectortargetlist = mExtraPayment->costs[ec]->tc->getTargetsFrom(); + for (vector::iterator it = targetlist.begin(); it != targetlist.end(); it++) + { + Targetable * cardMasked = *it; + dynamic_cast(cardMasked)->isExtraCostTarget = true; + } + + } + } + } //////////////////////////////////////////////////// //Unattach Equipments that dont have valid targets// //////////////////////////////////////////////////// @@ -733,6 +754,7 @@ void GameObserver::gameStateBasedEffects() } } } + /////////////////////////////////////////////////////// //Remove auras that don't have a valid target anymore// /////////////////////////////////////////////////////// @@ -773,18 +795,7 @@ void GameObserver::gameStateBasedEffects() { card->playerTarget->curses.push_back(card); } - /////////////////////////// - //reset extracost shadows// - /////////////////////////// - card->isExtraCostTarget = false; - if(mExtraPayment != NULL) - { - for(unsigned int ec = 0;ec < mExtraPayment->costs.size();ec++) - { - if( mExtraPayment->costs[ec]->target) - mExtraPayment->costs[ec]->target->isExtraCostTarget = true; - } - } + ////////////////////// //reset morph hiding// ////////////////////// @@ -1046,6 +1057,28 @@ void GameObserver::Affinity() if (!card) continue; + /////////////////////////// + //reset extracost shadows// + /////////////////////////// + card->isExtraCostTarget = false; + if (mExtraPayment != NULL) + { + for (unsigned int ec = 0; ec < mExtraPayment->costs.size(); ec++) + { + + if (mExtraPayment->costs[ec]->tc) + { + vectortargetlist = mExtraPayment->costs[ec]->tc->getTargetsFrom(); + for (vector::iterator it = targetlist.begin(); it != targetlist.end(); it++) + { + Targetable * cardMasked = *it; + dynamic_cast(cardMasked)->isExtraCostTarget = true; + } + + } + } + } + //////////////////////////// bool NewAffinityFound = false; for (unsigned int na = 0; na < card->cardsAbilities.size(); na++) { diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index f6f147ae4..fffb8ae10 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -1374,6 +1374,15 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG } } + vector splitGrant = parseBetween(s, "grant ", " grantend", false); + if (splitGrant.size() && storedAbilityString.empty()) + { + storedAbilityString = splitGrant[1]; + s = splitGrant[0]; + s.append("grant "); + s.append(splitGrant[2]); + } + vector splitRevealx = parseBetween(s, "reveal:", " revealend", false); if (!abilfound.size() && !transfound.size() && splitRevealx.size() && storedAbilityString.empty()) { @@ -2242,6 +2251,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //grant ability until source is untapped or leaves battlefield + found = s.find("grant "); + if (found != string::npos) + { + MTGAbility * toGrant = parseMagicLine(storedAbilityString, id, spell, card); + MTGAbility * a = NEW AGrantWrapper(observer, id, card, target,toGrant); + a->oneShot = 1; + return a; + } + //momentary blink found = s.find("(blink)"); if (found != string::npos) diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index eb20e1bb3..b44c178b2 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -972,6 +972,7 @@ ManaCost * MTGCardInstance::computeNewCost(MTGCardInstance * card,ManaCost * new string type = ""; ManaCost * original = NEW ManaCost(); original->copy(newCost); + int reducem = 0; bool resetCost = false; for(unsigned int na = 0; na < card->cardsAbilities.size();na++) diff --git a/projects/mtg/src/MTGDefinitions.cpp b/projects/mtg/src/MTGDefinitions.cpp index d2c76c05f..7c3178dcc 100644 --- a/projects/mtg/src/MTGDefinitions.cpp +++ b/projects/mtg/src/MTGDefinitions.cpp @@ -162,7 +162,8 @@ const char* Constants::MTGBasicAbilities[] = { "skulk", "menace", "nosolo", - "mustblock" + "mustblock", + "dethrone", }; map Constants::MTGBasicAbilitiesMap; diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 37b609f06..fc67729fa 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -1605,6 +1605,11 @@ int MTGAttackRule::receiveEvent(WEvent *e) if (Check <2) card->initAttackersDefensers(); } + if (card->isAttacker() && card->has(Constants::DETHRONE)) + { + if (p->opponent()->life >= p->life) + card->counters->addCounter(1, 1); + } if (!card->isAttacker() && !event->from->isExtra && card->has(Constants::MUSTATTACK))//cards are only required to attack in the real attack phase of a turn. reactToClick(card); if (!card->isAttacker() && card->has(Constants::TREASON) && p->isAI()) diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index e9af2bd43..de9991d65 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -179,7 +179,13 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan } break; case 'd': //DiscardRandom cost - if (value == "d") + if (value.find("delve") != string::npos) + { + if(!tc) + tc = tcf.createTargetChooser("*|mygraveyard", c); + manaCost->addExtraCost(NEW Delve(tc)); + } + else if (value == "d") { manaCost->addExtraCost(NEW DiscardRandomCost(tc)); } @@ -253,7 +259,13 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan break; case 'c': //Counters or cycle { - if(value == "chosencolor") + if (value.find("convoke") != string::npos) + { + if (!tc) + tc = tcf.createTargetChooser("creature|mybattlefield", c); + manaCost->addExtraCost(NEW Convoke(tc)); + } + else if(value == "chosencolor") { if(c) manaCost->add(c->chooseacolor, 1); diff --git a/projects/mtg/src/TargetsList.cpp b/projects/mtg/src/TargetsList.cpp index 0a55009ec..54d4bad57 100644 --- a/projects/mtg/src/TargetsList.cpp +++ b/projects/mtg/src/TargetsList.cpp @@ -20,9 +20,15 @@ int TargetsList::addTarget(Targetable * target) { if (!alreadyHasTarget(target)) { + TargetChooser * tc = target->getObserver()->getCurrentTargetChooser(); if(!tc || (tc && tc->maxtargets == 1)) { + if (dynamic_cast(this)->maxtargets > int(getNbTargets())) + { + targets.push_back(target); + return 1; + } //because this was originally coded with targets as an array //we have to add this conditional to insure that cards with single target effects //and abilities that seek the nextcardtarget still work correctly.