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.
This commit is contained in:
zethfoxster
2016-07-01 21:29:51 -04:00
parent 9841ce32ae
commit f76c28fa64
13 changed files with 467 additions and 22 deletions

View File

@@ -166,8 +166,11 @@ private:
{ {
if(!s.size()) if(!s.size())
return; return;
if(!card) if (!card)
return; {
intValue = atoi(s.c_str());//if there is no card, try parsing a number.
return;
}
MTGCardInstance * target = card->target; MTGCardInstance * target = card->target;
if(!card->storedCard) if(!card->storedCard)
card->storedCard = card->storedSourceCard; card->storedCard = card->storedSourceCard;
@@ -5166,7 +5169,38 @@ public:
~AShackleWrapper(); ~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 //ABlink
class ABlink: public MTGAbility class ABlink: public MTGAbility
{ {

View File

@@ -274,7 +274,27 @@ public:
virtual int doPay(); virtual int doPay();
virtual Ninja * clone() const; 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 //offering cost
class Offering : public ExtraCost class Offering : public ExtraCost
{ {

View File

@@ -251,7 +251,8 @@ class Constants
MENACE = 129, MENACE = 129,
NOSOLO = 130,//cant attack alone NOSOLO = 130,//cant attack alone
MUSTBLOCK = 131,//blocks each turn MUSTBLOCK = 131,//blocks each turn
NB_BASIC_ABILITIES = 132, DETHRONE = 132,
NB_BASIC_ABILITIES = 133,
RARITY_S = 'S', //Special Rarity RARITY_S = 'S', //Special Rarity
RARITY_M = 'M', //Mythics RARITY_M = 'M', //Mythics

View File

@@ -105,6 +105,11 @@ bool ActionLayer::CheckUserInput(JButton key)
//being cancelled. currently only menuability and paidability will care. //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; observer->mExtraPayment = NULL;
return 1; return 1;
} }

View File

@@ -6581,6 +6581,113 @@ AShackleWrapper::~AShackleWrapper()
SAFE_DELETE(ability); 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 //a blink
ABlink::ABlink(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, bool blinkueot, bool blinkForSource, bool blinkhand, MTGAbility * stored) : 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) MTGAbility(observer, _id, card),blinkueot(blinkueot),blinkForSource(blinkForSource),blinkhand(blinkhand),stored(stored)

View File

@@ -72,6 +72,10 @@ int ExtraCost::setPayment(MTGCardInstance * card)
if (tc) if (tc)
{ {
result = tc->addTarget(card); 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) if (result)
{ {
target = card; target = card;
@@ -748,7 +752,7 @@ int TapTargetCost::isPaymentSet()
target = NULL; target = NULL;
return 0; return 0;
} }
if (target) if (target && (tc->getNbTargets() == tc->maxtargets || tc->done))
return 1; return 1;
return 0; return 0;
} }
@@ -922,6 +926,193 @@ int Ninja::doPay()
//endbouncetargetcostforninja //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())
{
vector<Targetable*>targetlist = tc->getTargetsFrom();
for (vector<Targetable*>::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<MTGCardInstance*>(*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<Targetable*>::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<MTGCardInstance*>(*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);
vector<Targetable*>targetlist = tc->getTargetsFrom();
for (vector<Targetable*>::iterator it = targetlist.begin(); it != targetlist.end(); it++)
{
MTGCardInstance * targetCard = dynamic_cast<MTGCardInstance*>(*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);
vector<Targetable*>targetlist = tc->getTargetsFrom();
for (vector<Targetable*>::iterator it = targetlist.begin(); it != targetlist.end(); it++)
{
MTGCardInstance * targetCard = dynamic_cast<MTGCardInstance*>(*it);
source->storedCard = targetCard->createSnapShot();
targetCard->controller()->game->putInExile(targetCard);
}
if (tc)
tc->initTargets();
return 1;
}
return 0;
}
///////////////
//Sacrifice target as cost for Offering //Sacrifice target as cost for Offering
Offering * Offering::clone() const Offering * Offering::clone() const
{ {
@@ -1214,7 +1405,7 @@ int ExtraCosts::tryToSetPayment(MTGCardInstance * card)
} }
if (int result = costs[i]->setPayment(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; return result;
} }
} }
@@ -1251,10 +1442,20 @@ int ExtraCosts::doPay()
int result = 0; int result = 0;
for (size_t i = 0; i < costs.size(); i++) 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; costs[i]->target->isExtraCostTarget = false;
} }
if (costs[i]->tc)
{
vector<Targetable*>targetlist = costs[i]->tc->getTargetsFrom();
for (vector<Targetable*>::iterator it = targetlist.begin(); it != targetlist.end(); it++)
{
costs[i]->target = dynamic_cast<MTGCardInstance*>(*it);
costs[i]->doPay();
}
}
else
result += costs[i]->doPay(); result += costs[i]->doPay();
} }
return result; return result;

View File

@@ -715,6 +715,27 @@ void GameObserver::gameStateBasedEffects()
} }
} }
card->bypassTC = false; //turn off bypass 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)
{
vector<Targetable*>targetlist = mExtraPayment->costs[ec]->tc->getTargetsFrom();
for (vector<Targetable*>::iterator it = targetlist.begin(); it != targetlist.end(); it++)
{
Targetable * cardMasked = *it;
dynamic_cast<MTGCardInstance*>(cardMasked)->isExtraCostTarget = true;
}
}
}
}
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
//Unattach Equipments that dont have valid targets// //Unattach Equipments that dont have valid targets//
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
@@ -733,6 +754,7 @@ void GameObserver::gameStateBasedEffects()
} }
} }
} }
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
//Remove auras that don't have a valid target anymore// //Remove auras that don't have a valid target anymore//
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
@@ -773,18 +795,7 @@ void GameObserver::gameStateBasedEffects()
{ {
card->playerTarget->curses.push_back(card); 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// //reset morph hiding//
////////////////////// //////////////////////
@@ -1046,6 +1057,28 @@ void GameObserver::Affinity()
if (!card) if (!card)
continue; 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)
{
vector<Targetable*>targetlist = mExtraPayment->costs[ec]->tc->getTargetsFrom();
for (vector<Targetable*>::iterator it = targetlist.begin(); it != targetlist.end(); it++)
{
Targetable * cardMasked = *it;
dynamic_cast<MTGCardInstance*>(cardMasked)->isExtraCostTarget = true;
}
}
}
}
////////////////////////////
bool NewAffinityFound = false; bool NewAffinityFound = false;
for (unsigned int na = 0; na < card->cardsAbilities.size(); na++) for (unsigned int na = 0; na < card->cardsAbilities.size(); na++)
{ {

View File

@@ -1374,6 +1374,15 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
} }
} }
vector<string> 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<string> splitRevealx = parseBetween(s, "reveal:", " revealend", false); vector<string> splitRevealx = parseBetween(s, "reveal:", " revealend", false);
if (!abilfound.size() && !transfound.size() && splitRevealx.size() && storedAbilityString.empty()) 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; 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 //momentary blink
found = s.find("(blink)"); found = s.find("(blink)");
if (found != string::npos) if (found != string::npos)

View File

@@ -972,6 +972,7 @@ ManaCost * MTGCardInstance::computeNewCost(MTGCardInstance * card,ManaCost * new
string type = ""; string type = "";
ManaCost * original = NEW ManaCost(); ManaCost * original = NEW ManaCost();
original->copy(newCost); original->copy(newCost);
int reducem = 0; int reducem = 0;
bool resetCost = false; bool resetCost = false;
for(unsigned int na = 0; na < card->cardsAbilities.size();na++) for(unsigned int na = 0; na < card->cardsAbilities.size();na++)

View File

@@ -162,7 +162,8 @@ const char* Constants::MTGBasicAbilities[] = {
"skulk", "skulk",
"menace", "menace",
"nosolo", "nosolo",
"mustblock" "mustblock",
"dethrone",
}; };
map<string,int> Constants::MTGBasicAbilitiesMap; map<string,int> Constants::MTGBasicAbilitiesMap;

View File

@@ -1605,6 +1605,11 @@ int MTGAttackRule::receiveEvent(WEvent *e)
if (Check <2) if (Check <2)
card->initAttackersDefensers(); 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. 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); reactToClick(card);
if (!card->isAttacker() && card->has(Constants::TREASON) && p->isAI()) if (!card->isAttacker() && card->has(Constants::TREASON) && p->isAI())

View File

@@ -179,7 +179,13 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan
} }
break; break;
case 'd': //DiscardRandom cost 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)); manaCost->addExtraCost(NEW DiscardRandomCost(tc));
} }
@@ -253,7 +259,13 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan
break; break;
case 'c': //Counters or cycle 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) if(c)
manaCost->add(c->chooseacolor, 1); manaCost->add(c->chooseacolor, 1);

View File

@@ -20,9 +20,15 @@ int TargetsList::addTarget(Targetable * target)
{ {
if (!alreadyHasTarget(target)) if (!alreadyHasTarget(target))
{ {
TargetChooser * tc = target->getObserver()->getCurrentTargetChooser(); TargetChooser * tc = target->getObserver()->getCurrentTargetChooser();
if(!tc || (tc && tc->maxtargets == 1)) if(!tc || (tc && tc->maxtargets == 1))
{ {
if (dynamic_cast<TargetChooser*>(this)->maxtargets > int(getNbTargets()))
{
targets.push_back(target);
return 1;
}
//because this was originally coded with targets as an array //because this was originally coded with targets as an array
//we have to add this conditional to insure that cards with single target effects //we have to add this conditional to insure that cards with single target effects
//and abilities that seek the nextcardtarget still work correctly. //and abilities that seek the nextcardtarget still work correctly.