Files
wagic/projects/mtg/src/AllAbilities.cpp
2015-09-29 10:42:29 +08:00

6086 lines
177 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "PrecompiledHeader.h"
#include "AllAbilities.h"
#include "Translate.h"
//display a text animation, this is not a real ability.
MTGEventText::MTGEventText(GameObserver* observer, int _id, MTGCardInstance * card, string textToShow) :
MTGAbility(observer, _id,card)
{
textAlpha = 255;
text = textToShow;
}
void MTGEventText::Update(float dt)
{
if (textAlpha)
{
textAlpha -= static_cast<int> (200 * dt);
Render();
if (textAlpha < 0)
{
textAlpha = 0;
this->forceDestroy = 1;
}
}
MTGAbility::Update(dt);
}
void MTGEventText::Render()
{
if (!textAlpha)
return;
WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::OPTION_FONT);
float backup = mFont->GetScale();
mFont->SetScale(2 - (float) textAlpha / 130);
mFont->SetColor(ARGB(255,255,255,255));
mFont->DrawString(text.c_str(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, JGETEXT_CENTER);
mFont->SetScale(backup);
}
MTGEventText * MTGEventText::clone() const
{
return NEW MTGEventText(*this);
}
////////////////////////
//Activated Abilities
//Generic Activated Abilities
GenericActivatedAbility::GenericActivatedAbility(GameObserver* observer, string newName, string castRestriction, int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost,
string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest) :
ActivatedAbility(observer, _id, card, _cost, restrictions,limit,sideEffects,usesBeforeSideEffects,castRestriction), NestedAbility(a), activeZone(dest),newName(newName)
{
counters = 0;
target = ability->target;
}
GenericActivatedAbility::GenericActivatedAbility(const GenericActivatedAbility &other):
ActivatedAbility(other), NestedAbility(other), activeZone(other.activeZone), newName(other.newName)
{
}
int GenericActivatedAbility::resolve()
{
//Note: I've seen a similar block in some other MTGAbility, can this be refactored .
if (abilityCost)
{
source->X = 0;
ManaCost * diff = abilityCost->Diff(getCost());
source->X = diff->hasX();
SAFE_DELETE(diff);
}
ability->target = target; //may have been updated...
if (ability)
return ability->resolve();
return 0;
}
const string GenericActivatedAbility::getMenuText()
{
if(newName.size())
return newName.c_str();
if (ability)
return ability->getMenuText();
return "Error";
}
int GenericActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
if (dynamic_cast<AAMorph*> (ability) && !card->isMorphed && !card->morphed && card->turningOver)
return 0;
return ActivatedAbility::isReactingToClick(card, mana);
}
void GenericActivatedAbility::Update(float dt)
{
ActivatedAbility::Update(dt);
}
int GenericActivatedAbility::testDestroy()
{
if (!activeZone)
return ActivatedAbility::testDestroy();
if (activeZone->hasCard(source))
return 0;
return 1;
}
GenericActivatedAbility * GenericActivatedAbility::clone() const
{
GenericActivatedAbility * a = NEW GenericActivatedAbility(*this);
a->ability = ability->clone();
return a;
}
GenericActivatedAbility::~GenericActivatedAbility()
{
SAFE_DELETE(ability);
}
//AA Alter Poison
AAAlterPoison::AAAlterPoison(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int poison, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), poison(poison)
{
}
int AAAlterPoison::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
Player * pTarget = (Player*)_target;
if(!pTarget->inPlay()->hasAbility(Constants::POISONSHROUD) || poison < 0)
_target->poisonCount += poison;
}
return 0;
}
const string AAAlterPoison::getMenuText()
{
return "Poison";
}
AAAlterPoison * AAAlterPoison::clone() const
{
return NEW AAAlterPoison(*this);
}
AAAlterPoison::~AAAlterPoison()
{
}
//Damage Prevent
AADamagePrevent::AADamagePrevent(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int preventing, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), preventing(preventing)
{
aType = MTGAbility::STANDARD_PREVENT;
}
int AADamagePrevent::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
_target->preventable += preventing;
}
return 0;
}
const string AADamagePrevent::getMenuText()
{
return "Prevent Damage";
}
AADamagePrevent * AADamagePrevent::clone() const
{
return NEW AADamagePrevent(*this);
}
AADamagePrevent::~AADamagePrevent()
{
}
//AADamager
AADamager::AADamager(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, string d, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), d(d)
{
aType = MTGAbility::DAMAGER;
redirected = false;
}
int AADamager::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
if(_target == game->opponent() && game->opponent()->inPlay()->hasType("planeswalker") && !redirected)
{
vector<MTGAbility*>selection;
MTGCardInstance * check = NULL;
this->redirected = true;
MTGAbility * setPlayer = this->clone();
this->redirected = false;
selection.push_back(setPlayer);
int checkWalkers = ((Player*)_target)->game->battlefield->cards.size();
for(int i = 0; i < checkWalkers;++i)
{
check = ((Player*)_target)->game->battlefield->cards[i];
if(check->hasType(Subtypes::TYPE_PLANESWALKER))
{
this->redirected = true;
MTGAbility * setWalker = this->clone();
this->redirected = false;
setWalker->oneShot = true;
setWalker->target = check;
selection.push_back(setWalker);
}
}
if(selection.size())
{
MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), source, source,true,selection);
game->mLayers->actionLayer()->currentActionCard = source;
a1->resolve();
}
return 1;
}
game->mLayers->stackLayer()->addDamage(source, _target, damage.getValue());
game->mLayers->stackLayer()->resolve();
return 1;
}
return 0;
}
int AADamager::getDamage()
{
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
return damage.getValue();
}
const string AADamager::getMenuText()
{
MTGCardInstance * _target = dynamic_cast<MTGCardInstance*>(target);
if(_target && _target->hasType(Subtypes::TYPE_PLANESWALKER))
return _target->name.c_str();
if(redirected)
return "Damage Player";
return "Damage";
}
AADamager * AADamager::clone() const
{
return NEW AADamager(*this);
}
//AADepleter
AADepleter::AADepleter(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost, int who, bool toexile) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),nbcardsStr(nbcardsStr),toexile(toexile)
{
}
int AADepleter::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
WParsedInt numCards(nbcardsStr, NULL, source);
MTGLibrary * library = player->game->library;
for (int i = 0; i < numCards.getValue(); i++)
{
if (library->nb_cards)
{
if(toexile)
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->exile);
else
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
}
}
}
return 1;
}
const string AADepleter::getMenuText()
{
if(toexile)
return "Ingest";
return "Deplete";
}
AADepleter * AADepleter::clone() const
{
return NEW AADepleter(*this);
}
//take extra turns or skip turns, values in the negitive will make you skip.
AAModTurn::AAModTurn(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbTurnStr, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),nbTurnStr(nbTurnStr)
{
}
int AAModTurn::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
WParsedInt numTurns(nbTurnStr, NULL, source);
if(numTurns.getValue() > 0)
{
player->extraTurn += numTurns.getValue();
}
else
{
player->skippingTurn += abs(numTurns.getValue());
}
}
return 1;
}
const string AAModTurn::getMenuText()
{
WParsedInt numTurns(nbTurnStr, NULL, source);
if(numTurns.getValue() > 0)
return "Take Extra Turn(s)";
else
return "Skip A Turn(s)";
}
AAModTurn * AAModTurn::clone() const
{
return NEW AAModTurn(*this);
}
//move target to bottom of owners library
AALibraryBottom::AALibraryBottom(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AALibraryBottom::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
_target = _target->owner->game->putInLibrary(_target);
if (_target)
{
MTGLibrary * library = _target->owner->game->library;
vector<MTGCardInstance *>oldOrder = library->cards;
vector<MTGCardInstance *>newOrder;
newOrder.push_back(_target);
for(unsigned int k = 0;k < oldOrder.size();++k)
{
MTGCardInstance * rearranged = oldOrder[k];
if(rearranged != _target)
newOrder.push_back(rearranged);
}
library->cards = newOrder;
return 1;
}
return 0;
}
const string AALibraryBottom::getMenuText()
{
return "Bottom Of Library";
}
AALibraryBottom * AALibraryBottom::clone() const
{
return NEW AALibraryBottom(*this);
}
//AACopier
AACopier::AACopier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AACopier::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
if(_target->isACopier)
{
MTGCard* clone = MTGCollection()->getCardById(_target->copiedID);
MTGCardInstance * myClone = NEW MTGCardInstance(clone, source->controller()->game);
source->copy(myClone);
source->isACopier = true;
source->copiedID = _target->copiedID;
}
else
{
source->copy(_target);
source->isACopier = true;
source->copiedID = _target->getId();
}
return 1;
}
return 0;
}
const string AACopier::getMenuText()
{
return "Copy";
}
AACopier * AACopier::clone() const
{
return NEW AACopier(*this);
}
//phaser
AAPhaseOut::AAPhaseOut(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AAPhaseOut::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->isPhased = true;
_target->phasedTurn = game->turn;
if(_target->view)
_target->view->alpha = 50;
_target->initAttackersDefensers();
return 1;
}
return 0;
}
const string AAPhaseOut::getMenuText()
{
return "Phase Out";
}
AAPhaseOut * AAPhaseOut::clone() const
{
return NEW AAPhaseOut(*this);
}
//Counters
AACounter::AACounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness,
int nb,int maxNb, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0),counterstring(counterstring), nb(nb),maxNb(maxNb), power(power), toughness(toughness), name(_name)
{
this->target = target;
if (name.find("Level") != string::npos || name.find("level") != string::npos)
aType = MTGAbility::STANDARD_LEVELUP;
else
aType = MTGAbility::COUNTERS;
menu = "";
}
int AACounter::resolve()
{
if (target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
AbilityFactory af(game);
if(counterstring.size())
{
Counter * checkcounter = af.parseCounter(counterstring, source, NULL);
nb = checkcounter->nb;
delete checkcounter;
}
if (nb > 0)
{
for (int i = 0; i < nb; i++)
{
while (_target->next)
_target = _target->next;
Counter * targetCounter = NULL;
int currentAmount = 0;
if (_target->counters && _target->counters->hasCounter(name.c_str(), power, toughness))
{
targetCounter = _target->counters->hasCounter(name.c_str(), power, toughness);
currentAmount = targetCounter->nb;
}
if(!maxNb || (maxNb && currentAmount < maxNb))
{
_target->counters->addCounter(name.c_str(), power, toughness);
}
}
}
else
{
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;
}
return 0;
}
const string AACounter::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
char buffer[128];
if (name.size())
{
string s = name;
menu.append(s.c_str());
}
if (power != 0 || toughness != 0)
{
sprintf(buffer, " %i/%i", power, toughness);
menu.append(buffer);
}
menu.append(" Counter");
if (nb != 1 && !(nb < -1000))
{
sprintf(buffer, ": %i", nb);
menu.append(buffer);
}
sprintf(menuText, "%s", menu.c_str());
return menuText;
}
AACounter * AACounter::clone() const
{
return NEW AACounter(*this);
}
//shield a card from a certain type of counter.
ACounterShroud::ACounterShroud(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,TargetChooser * tc, Counter * counter) :
MTGAbility(observer, id, source),csTc(tc),counter(counter),re(NULL)
{
}
int ACounterShroud::addToGame()
{
SAFE_DELETE(re);
re = NEW RECountersPrevention(this,source,(MTGCardInstance*)target,csTc,counter);
if (re)
{
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
return 0;
}
int ACounterShroud::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
ACounterShroud * ACounterShroud::clone() const
{
ACounterShroud * a = NEW ACounterShroud(*this);
a->re = NULL;
return a;
}
ACounterShroud::~ACounterShroud()
{
SAFE_DELETE(re);
SAFE_DELETE(counter);
}
//track counters placed on a card
ACounterTracker::ACounterTracker(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string scounter) :
MTGAbility(observer, id, source, target),scounter(scounter)
{
removed = 0;
}
int ACounterTracker::addToGame()
{
MTGCardInstance * _target = (MTGCardInstance*)target;
AbilityFactory af(game);
Counter * counter = af.parseCounter(scounter, _target, NULL); //(Spell*)source);
if (!counter)
{
return 0;
}
if(_target && !removed)
{
if(_target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness) && _target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness)->nb >= counter->nb)
{
for(int nb = 0;nb < counter->nb;nb++)
{
_target->counters->removeCounter(counter->name.c_str(),counter->power,counter->toughness);
removed++;
}
}
SAFE_DELETE(counter);
return MTGAbility::addToGame();
}
SAFE_DELETE(counter);
return 0;
}
int ACounterTracker::destroy()
{
MTGCardInstance * _target = (MTGCardInstance*)target;
AbilityFactory af(game);
Counter * counter = af.parseCounter(scounter, _target, NULL); //(Spell*)source);
if (!counter)
{
return 0;
}
if(_target)
{
if(removed == counter->nb)
{
for(int nb = 0;nb < counter->nb;nb++)
{
_target->counters->addCounter(counter->name.c_str(),counter->power,counter->toughness);
}
}
}
SAFE_DELETE(counter);
return 1;
}
int ACounterTracker::testDestroy()
{
if(this->source->isInPlay(game))
return 0;
return 1;
}
ACounterTracker * ACounterTracker::clone() const
{
ACounterTracker * a = NEW ACounterTracker(*this);
return a;
}
ACounterTracker::~ACounterTracker()
{
}
//removeall counters of a certain type or all.
AARemoveAllCounter::AARemoveAllCounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness,
int nb,bool all, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0), nb(nb), power(power), toughness(toughness), name(_name),all(all)
{
this->target = target;
menu = "";
}
int AARemoveAllCounter::resolve()
{
if (!target)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (all )
{
for(int amount = 0;amount < _target->counters->mCount;amount++)
{
while(_target->counters->counters[amount]->nb > 0)
_target->counters->removeCounter(_target->counters->counters[amount]->name.c_str(),_target->counters->counters[amount]->power,_target->counters->counters[amount]->toughness);
}
}
Counter * targetCounter = NULL;
if (_target->counters && _target->counters->hasCounter(name.c_str(), power, toughness))
{
targetCounter = _target->counters->hasCounter(name.c_str(), power, toughness);
nb = targetCounter->nb;
}
for (int i = 0; i < nb; i++)
{
while (_target->next)
_target = _target->next;
_target->counters->removeCounter(name.c_str(), power, toughness);
}
return nb;
}
const string AARemoveAllCounter::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
char buffer[128];
if (name.size())
{
string s = name;
menu.append(s.c_str());
}
if (power != 0 || toughness != 0)
{
sprintf(buffer, " %i/%i", power, toughness);
menu.append(buffer);
}
menu.append(" Counter Removed");
if (nb != 1)
{
sprintf(buffer, ": %i", nb);
menu.append(buffer);
}
return menu.c_str();
}
AARemoveAllCounter * AARemoveAllCounter::clone() const
{
return NEW AARemoveAllCounter(*this);
}
//proliferate a target
AAProliferate::AAProliferate(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0)
{
this->GetId();
}
int AAProliferate::resolve()
{
if (!target)
return 0;
vector<MTGAbility*>pcounters;
Player * pTarget = dynamic_cast<Player *>(target);
MTGCardInstance * cTarget = dynamic_cast<MTGCardInstance *>(target);
if(pTarget && pTarget->poisonCount && pTarget != source->controller())
{
MTGAbility * a = NEW AAAlterPoison(game, game->mLayers->actionLayer()->getMaxId(), source, target, 1, NULL);
a->oneShot = true;
pcounters.push_back(a);
}
else if (cTarget && cTarget->counters)
{
Counters * counters = cTarget->counters;
for(size_t i = 0; i < counters->counters.size(); ++i)
{
Counter * counter = counters->counters[i];
MTGAbility * a = NEW AACounter(game, game->mLayers->actionLayer()->getMaxId(), source, cTarget,"", counter->name.c_str(), counter->power, counter->toughness, 1,0);
a->oneShot = true;
pcounters.push_back(a);
}
}
if(pcounters.size())
{
MTGAbility * a = NEW MenuAbility(game, this->GetId(), target, source,false,pcounters);
a->resolve();
}
return 1;
}
const string AAProliferate::getMenuText()
{
return "Proliferate";
}
AAProliferate * AAProliferate::clone() const
{
return NEW AAProliferate(*this);
}
AAProliferate::~AAProliferate()
{
}
//
//choosing a type or color
GenericChooseTypeColor::GenericChooseTypeColor(GameObserver* observer, int id, MTGCardInstance * source, Targetable *,string _toAdd,bool chooseColor,bool nonwall, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0), baseAbility(_toAdd),chooseColor(chooseColor),ANonWall(nonwall)
{
this->GetId();
setColor = NULL;
}
int GenericChooseTypeColor::resolve()
{
if (!target)
return 0;
vector<MTGAbility*>selection;
if(chooseColor)
{
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
setColor = NEW AASetColorChosen(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i, baseAbility);
MTGAbility * set = setColor->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(setColor);
}
}
else
{
vector<string> values = MTGAllCards::getCreatureValuesById();
for (size_t i = 0; i < values.size(); ++i)
{
string menu = values[i];
if (!ANonWall || (menu != "wall" && menu != "Wall"))
{
setType = NEW AASetTypeChosen(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i,menu,baseAbility);
MTGAbility * set = setType->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(setType);
}
}
}
if(selection.size())
{
MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source,true,selection);
game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target;
a1->resolve();
}
return 1;
}
const string GenericChooseTypeColor::getMenuText()
{
if(chooseColor)
return "Choose a color";
else
return "Choose a type";
}
GenericChooseTypeColor * GenericChooseTypeColor::clone() const
{
GenericChooseTypeColor * a = NEW GenericChooseTypeColor(*this);
return a;
}
GenericChooseTypeColor::~GenericChooseTypeColor()
{
}
//set color choosen
AASetColorChosen::AASetColorChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _color , string toAlter):
InstantAbility(observer, id, source),color(_color), abilityToAlter(toAlter)
{
this->target = _target;
abilityAltered = NULL;
}
int AASetColorChosen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->chooseacolor = color;
if(abilityToAlter.size())
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityToAlter, 0, NULL, _target);
if(!abilityAltered)
return 0;
abilityAltered->canBeInterrupted = false;
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->target = _target;
MayAbility * dontAdd = dynamic_cast<MayAbility*>(abilityAltered);
if (!dontAdd)
{
_target->cardsAbilities.push_back(abilityAltered);
for(unsigned int j = 0;j < _target->cardsAbilities.size();++j)
{
if(_target->cardsAbilities[j] == this)
_target->cardsAbilities.erase(_target->cardsAbilities.begin() + j);
}
}
abilityAltered->addToGame();
}
_target->skipDamageTestOnce = true;//some cards rely on this ability updating before damage test are run. otherwise they die before toughnes bonus applies.
}
return 1;
}
const string AASetColorChosen::getMenuText()
{
return Constants::MTGColorStrings[color];
}
AASetColorChosen * AASetColorChosen::clone() const
{
return NEW AASetColorChosen(*this);
}
AASetColorChosen::~AASetColorChosen()
{
}
//set type choosen
AASetTypeChosen::AASetTypeChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _type ,string _menu,string toAlter):
InstantAbility(observer, id, source),type(_type), abilityToAlter(toAlter), menutext(_menu)
{
this->target = _target;
abilityAltered = NULL;
}
int AASetTypeChosen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
string typeChoosen = menutext;
_target->chooseasubtype = typeChoosen;
if(abilityToAlter.size())
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityToAlter, 0, NULL, _target);
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->target = _target;
MayAbility * dontAdd = dynamic_cast<MayAbility*>(abilityAltered);
if (!dontAdd)
{
_target->cardsAbilities.push_back(abilityAltered);
for(unsigned int j = 0;j < _target->cardsAbilities.size();++j)
{
if(_target->cardsAbilities[j] == this)
_target->cardsAbilities.erase(_target->cardsAbilities.begin() + j);
}
}
abilityAltered->addToGame();
}
_target->skipDamageTestOnce = true;//some cards rely on this ability updating before damage test are run. otherwise they die before toughnes bonus applies.
}
return 1;
}
const string AASetTypeChosen::getMenuText()
{
return menutext.c_str();
}
AASetTypeChosen * AASetTypeChosen::clone() const
{
return NEW AASetTypeChosen(*this);
}
AASetTypeChosen::~AASetTypeChosen()
{
}
//
//choosing a type or color
GenericFlipACoin::GenericFlipACoin(GameObserver* observer, int id, MTGCardInstance * source, Targetable *,string _toAdd, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0), baseAbility(_toAdd)
{
this->GetId();
setCoin = NULL;
}
int GenericFlipACoin::resolve()
{
if (!target)
return 0;
vector<MTGAbility*>selection;
for (int i = 0; i <2; ++i)
{
setCoin = NEW AASetCoin(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i, baseAbility);
MTGAbility * set = setCoin->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(setCoin);
}
if(selection.size())
{
MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source,false,selection);
game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target;
a1->resolve();
}
return 1;
}
const string GenericFlipACoin::getMenuText()
{
return "Flip A Coin";
}
GenericFlipACoin * GenericFlipACoin::clone() const
{
GenericFlipACoin * a = NEW GenericFlipACoin(*this);
return a;
}
GenericFlipACoin::~GenericFlipACoin()
{
}
//set color choosen
AASetCoin::AASetCoin(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _side , string toAlter):
InstantAbility(observer, id, source),side(_side), abilityToAlter(toAlter)
{
this->target = _target;
abilityAltered = NULL;
}
int AASetCoin::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->coinSide = side;
int flip = game->getRandomGenerator()->random() % 2;
vector<string>Win = parseBetween(abilityToAlter,"winability "," winabilityend");
if(Win.size())
{
abilityWin = Win[1];
}
vector<string>Lose = parseBetween(abilityToAlter,"loseability "," loseabilityend");
if(Lose.size())
{
abilityLose = Lose[1];
}
if(abilityWin.size() && flip == side)
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityWin, 0, NULL, _target);
abilityAltered->canBeInterrupted = false;
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->addToGame();
}
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Won The Flip");
message->oneShot = true;
message->addToGame();
}
else if(abilityWin.size() && !abilityLose.size())
{
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Lost The Flip");
message->oneShot = true;
message->addToGame();
}
else if(abilityLose.size() && flip != side)
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityLose, 0, NULL, _target);
abilityAltered->canBeInterrupted = false;
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->addToGame();
}
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Lost The Flip");
message->oneShot = true;
message->addToGame();
}
else if(abilityLose.size())
{
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Won The Flip");
message->oneShot = true;
message->addToGame();
}
_target->skipDamageTestOnce = true;
return 1;
}
const string AASetCoin::getMenuText()
{
if(side == 1)
return "Tails";
return "Heads";
}
AASetCoin * AASetCoin::clone() const
{
return NEW AASetCoin(*this);
}
AASetCoin::~AASetCoin()
{
}
//paying for an ability as an effect but as a cost
GenericPaidAbility::GenericPaidAbility(GameObserver* observer, int id, MTGCardInstance * source,
Targetable * target, string _newName, string _castRestriction, string mayCost, string _toAdd, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0),
newName(_newName), restrictions(_castRestriction), baseCost(mayCost), baseAbilityStr(_toAdd)
{
this->GetId();
baseAbility = NULL;
optionalCost = NULL;
}
int GenericPaidAbility::resolve()
{
if (!target)
return 0;
if (restrictions.size())
{
AbilityFactory af(game);
int checkCond = af.parseCastRestrictions(source,source->controller(),restrictions);
if(!checkCond)
{
return 0;
}
}
AbilityFactory Af(game);
vector<string> baseAbilityStrSplit = split(baseAbilityStr,'?');
vector<MTGAbility*> selection;
if (baseAbilityStrSplit.size() > 1)
{
baseAbility = Af.parseMagicLine(baseAbilityStrSplit[0], this->GetId(), NULL, source);
baseAbility->target = target;
optionalCost = ManaCost::parseManaCost(baseCost, NULL, source);
/*// hacky way to produce better MenuText
AAFakeAbility* isFake = dynamic_cast< AAFakeAbility* >( baseAbility );
size_t findPayN = isFake->named.find(" {value} mana");
if (isFake && findPayN != string::npos) {
stringstream parseN;
parseN << optionalCost->getCost(Constants::MTG_COLOR_ARTIFACT);
isFake->named.replace(findPayN + 1, 7, parseN.str());
}//commented out, it crashes cards with recover ability*/
MTGAbility * set = baseAbility->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(baseAbility);
baseAbility = Af.parseMagicLine(baseAbilityStrSplit[1], this->GetId(), NULL, source);
baseAbility->target = target;
set = baseAbility->clone();
set->oneShot = true;
selection.push_back(set);
}
else
{
baseAbility = Af.parseMagicLine(baseAbilityStrSplit[0], this->GetId(), NULL, source);
baseAbility->target = target;
optionalCost = ManaCost::parseManaCost(baseCost, NULL, source);
MTGAbility * set = baseAbility->clone();
set->oneShot = true;
selection.push_back(set);
}
if (selection.size())
{
bool must = baseAbilityStrSplit.size() > 1 ? true : false;
MenuAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source, must, selection, NULL, newName);
a1->optionalCosts.push_back(NEW ManaCost(optionalCost));
game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target;
a1->resolve();
}
return 1;
}
const string GenericPaidAbility::getMenuText()
{
if (newName.size())
return newName.c_str();
return "Pay For Effect";
}
GenericPaidAbility * GenericPaidAbility::clone() const
{
GenericPaidAbility * a = NEW GenericPaidAbility(*this);
return a;
}
GenericPaidAbility::~GenericPaidAbility()
{
SAFE_DELETE(optionalCost);
SAFE_DELETE(baseAbility);
}
//saves a listed mana type until end of turn.
AManaPoolSaver::AManaPoolSaver(GameObserver* observer, int id, MTGCardInstance * source,string color, bool otherPlayer) :
MTGAbility(observer, id, source),Color(color),OtherPlayer(otherPlayer)
{
}
int AManaPoolSaver::addToGame()
{
int colorInt = Constants::GetColorStringIndex(Color.c_str());
source->controller()->poolDoesntEmpty->add(colorInt,1);
return 1;
}
int AManaPoolSaver::destroy()
{
int colorInt = Constants::GetColorStringIndex(Color.c_str());
source->controller()->poolDoesntEmpty->remove(colorInt,1);
return 1;
}
AManaPoolSaver * AManaPoolSaver::clone() const
{
AManaPoolSaver * a = NEW AManaPoolSaver(*this);
return a;
}
AManaPoolSaver::~AManaPoolSaver()
{
}
//replace drawing a card with activation of an ability
ADrawReplacer::ADrawReplacer(GameObserver* observer, int id, MTGCardInstance * source, MTGAbility * replace, bool otherPlayer) :
MTGAbility(observer, id, source),re(NULL),replacer(replace),OtherPlayer(otherPlayer)
{
}
int ADrawReplacer::addToGame()
{
SAFE_DELETE(re);
if(OtherPlayer)
re = NEW REDrawReplacement(this,source->controller()->opponent(),replacer);
else
re = NEW REDrawReplacement(this,source->controller(),replacer);
if (re)
{
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
return 0;
}
int ADrawReplacer::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
ADrawReplacer * ADrawReplacer::clone() const
{
ADrawReplacer * a = NEW ADrawReplacer(*this);
a->re = NULL;
return a;
}
ADrawReplacer::~ADrawReplacer()
{
SAFE_DELETE(re);
SAFE_DELETE(replacer);
}
//Reset Damage on creatures
AAResetDamage::AAResetDamage(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, ManaCost * cost):
ActivatedAbility(observer, id, source, cost, 0)
{
this->target = _target;
}
int AAResetDamage::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->life = _target->toughness;
return 1;
}
const string AAResetDamage::getMenuText()
{
return "Reset Damages";
}
AAResetDamage * AAResetDamage::clone() const
{
return NEW AAResetDamage(*this);
}
//ability that resolves to do nothing.
AAFakeAbility::AAFakeAbility(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, string _named,ManaCost * cost):
ActivatedAbility(observer, id, source, cost, 0),named(_named)
{
this->target = _target;
}
int AAFakeAbility::resolve()
{
return 1;
}
const string AAFakeAbility::getMenuText()
{
if(named.size())
return named.c_str();
return "Ability";
}
AAFakeAbility * AAFakeAbility::clone() const
{
return NEW AAFakeAbility(*this);
}
//EPIC
AAEPIC::AAEPIC(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, string _named,ManaCost * cost):
ActivatedAbility(observer, id, source, cost, 0),named(_named)
{
this->target = _target;
}
int AAEPIC::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->controller()->epic = 1;
return 1;
}
const string AAEPIC::getMenuText()
{
if(named.size())
return named.c_str();
return "EPIC";
}
AAEPIC * AAEPIC::clone() const
{
return NEW AAEPIC(*this);
}
// Fizzler
AAFizzler::AAFizzler(GameObserver* observer, int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, card, _cost, 0)
{
aType = MTGAbility::STANDARD_FIZZLER;
target = _target;
// by default we put the spell to graveyard after fizzling
fizzleMode = ActionStack::PUT_IN_GRAVEARD;
}
int AAFizzler::resolve()
{
ActionStack * stack = game->mLayers->stackLayer();
//the next section helps Ai correctly recieve its targets for this effect
if (!target && source->target)
{
//ai is casting a spell from its hand to fizzle.
target = stack->getActionElementFromCard(source->target);
}
else if(MTGCardInstance * cTarget = dynamic_cast<MTGCardInstance *>(target))
{
//ai targeted using an ability on a card to fizzle.
target = stack->getActionElementFromCard(cTarget);
}
Spell * sTarget = (Spell *) target;
MTGCardInstance* sCard = NULL;
if (sTarget)
sCard = sTarget->source;
if (!sCard || !sTarget || sCard->has(Constants::NOFIZZLE))
return 0;
if (source->alias == 111057 && sTarget)//Draining Whelk
{
for (int j = sTarget->cost->getConvertedCost(); j > 0; j--)
{
source->counters->addCounter(1,1);
}
}
stack->Fizzle(sTarget, fizzleMode);
return 1;
}
const string AAFizzler::getMenuText()
{
return "Fizzle";
}
AAFizzler* AAFizzler::clone() const
{
return NEW AAFizzler(*this);
}
// BanishCard implementations
// Bury
AABuryCard::AABuryCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AABuryCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->bury();
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AABuryCard::getMenuText()
{
if(menu.size())
return menu.c_str();
return "Bury";
}
AABuryCard * AABuryCard::clone() const
{
AABuryCard * a = NEW AABuryCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AABuryCard::~AABuryCard()
{
SAFE_DELETE(andAbility);
}
// Destroy
AADestroyCard::AADestroyCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AADestroyCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->destroy();
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AADestroyCard::getMenuText()
{
return "Destroy";
}
AADestroyCard * AADestroyCard::clone() const
{
AADestroyCard * a = NEW AADestroyCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AADestroyCard::~AADestroyCard()
{
SAFE_DELETE(andAbility);
}
// Sacrifice
AASacrificeCard::AASacrificeCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AASacrificeCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
MTGCardInstance * beforeCard = _target;
p->game->putInGraveyard(_target);
while(_target->next)
_target = _target->next;
WEvent * e = NEW WEventCardSacrifice(beforeCard,_target);
game->receiveEvent(e);
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AASacrificeCard::getMenuText()
{
return "Sacrifice";
}
AASacrificeCard * AASacrificeCard::clone() const
{
AASacrificeCard * a = NEW AASacrificeCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AASacrificeCard::~AASacrificeCard()
{
SAFE_DELETE(andAbility);
}
// Discard
AADiscardCard::AADiscardCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AADiscardCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
WEvent * e = NEW WEventCardDiscard(_target);
game->receiveEvent(e);
p->game->putInGraveyard(_target);
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AADiscardCard::getMenuText()
{
return "Discard";
}
AADiscardCard * AADiscardCard::clone() const
{
AADiscardCard * a = NEW AADiscardCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AADiscardCard::~AADiscardCard()
{
SAFE_DELETE(andAbility);
}
//
AADrawer::AADrawer(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, string nbcardsStr,
int who, bool noreplace) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who), nbcardsStr(nbcardsStr),noReplace(noreplace)
{
aType = MTGAbility::STANDARD_DRAW;
}
int AADrawer::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
WParsedInt numCards(nbcardsStr, NULL, source);
WEvent * e = NEW WEventDraw(player, numCards.getValue(),this);
if(!noReplace)
e = game->replacementEffects->replace(e);
if(e)
{
game->mLayers->stackLayer()->addDraw(player, numCards.getValue());
game->mLayers->stackLayer()->resolve();
for(int i = numCards.getValue(); i > 0;i--)
{
player->drawCounter += 1;
if ((game->turn < 1) && game->getCurrentGamePhase() == MTG_PHASE_FIRSTMAIN
&& game->currentPlayer->game->inPlay->nb_cards == 0 && game->currentPlayer->game->graveyard->nb_cards == 0
&& game->currentPlayer->game->exile->nb_cards == 0 && game->currentlyActing() == (Player*)game->currentPlayer) //1st Play Check
{
game->currentPlayer->drawCounter = 0;//Reset drawCounter for pre-game draw
}
WEvent * e = NEW WEventcardDraw(player, 1);
game->receiveEvent(e);
}
}
SAFE_DELETE(e);
}
return 1;
}
int AADrawer::getNumCards()
{
WParsedInt numCards(nbcardsStr, NULL, source);
return numCards.getValue();
}
const string AADrawer::getMenuText()
{
return "Draw";
}
AADrawer * AADrawer::clone() const
{
return NEW AADrawer(*this);
}
// AAFrozen: Prevent a card from untapping during next untap phase
AAFrozen::AAFrozen(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0)
{
target = _target;
}
int AAFrozen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->frozen += 1;
}
return 1;
}
const string AAFrozen::getMenuText()
{
return "Freeze";
}
AAFrozen * AAFrozen::clone() const
{
return NEW AAFrozen(*this);
}
// chose a new target for an aura or enchantment and equip it note: VERY basic right now.
AANewTarget::AANewTarget(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,bool retarget, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0),retarget(retarget)
{
target = _target;
}
int AANewTarget::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(retarget)
{
_target = source;
source = (MTGCardInstance *) target;
}
if (_target)
{
while (_target->next)
_target = _target->next;
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->exile);
_target = _target->next;
MTGCardInstance * refreshed = source->controller()->game->putInZone(_target,_target->currentZone,source->controller()->game->battlefield);
Spell * reUp = NEW Spell(game, refreshed);
if(reUp->source->hasSubtype(Subtypes::TYPE_AURA))
{
reUp->source->target = source;
reUp->resolve();
}
if(_target->hasSubtype(Subtypes::TYPE_EQUIPMENT))
{
reUp->resolve();
for (size_t i = 1; i < game->mLayers->actionLayer()->mObjects.size(); i++)
{
MTGAbility * a = ((MTGAbility *) game->mLayers->actionLayer()->mObjects[i]);
AEquip * eq = dynamic_cast<AEquip*> (a);
if (eq && eq->source == reUp->source)
{
((AEquip*)a)->unequip();
((AEquip*)a)->equip(source);
}
}
}
delete reUp;
if(retarget)
{
target = source;
source = _target;
}
}
return 1;
}
const string AANewTarget::getMenuText()
{
return "New Target";
}
AANewTarget * AANewTarget::clone() const
{
AANewTarget * a = NEW AANewTarget(*this);
a->oneShot = 1;
return a;
}
// morph a card
AAMorph::AAMorph(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, restrictions)
{
target = _target;
}
int AAMorph::resolve()
{
MTGCardInstance * Morpher = (MTGCardInstance*)source;
if(!Morpher->isMorphed && !Morpher->morphed && Morpher->turningOver)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
AbilityFactory af(game);
_target->morphed = false;
_target->isMorphed = false;
_target->turningOver = true;
af.getAbilities(&currentAbilities, NULL, _target, 0);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
a->source = (MTGCardInstance *) _target;
if( a && dynamic_cast<AAMorph *> (a))
{
a->removeFromGame();
game->removeObserver(a);
}
if (a)
{
if (a->oneShot)
{
a->resolve();
delete (a);
}
else
{
a->addToGame();
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
if(!dontAdd)
{
_target->cardsAbilities.push_back(a);
}
}
}
}
currentAbilities.clear();
testDestroy();
}
return 1;
}
int AAMorph::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target)
{
if(_target->turningOver && !_target->isMorphed && !_target->morphed)
{
game->removeObserver(this);
return 1;
}
}
return 0;
}
const string AAMorph::getMenuText()
{
return "Morph";
}
AAMorph * AAMorph::clone() const
{
AAMorph * a = NEW AAMorph(*this);
a->forceDestroy = 1;
return a;
}
// flip a card
AAFlip::AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats) :
InstantAbility(observer, id, card, _target),flipStats(flipStats)
{
target = _target;
}
int AAFlip::resolve()
{
MTGCardInstance * Flipper = (MTGCardInstance*)source;
this->oneShot = true;
if(Flipper->isFlipped)
{
game->removeObserver(this);
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
AbilityFactory af(game);
_target->isFlipped = true;
GameObserver * game = _target->getObserver();
if(flipStats.size())
{
MTGCard * fcard = MTGCollection()->getCardByName(flipStats);
if(!fcard) return 0;
MTGCardInstance * myFlip = NEW MTGCardInstance(fcard, _target->controller()->game);
_target->name = myFlip->name;
_target->setName(myFlip->name);
_target->colors = myFlip->colors;
_target->types = myFlip->types;
_target->text = myFlip->text;
_target->formattedText = myFlip->formattedText;
_target->basicAbilities = myFlip->basicAbilities;
for(unsigned int i = 0;i < _target->cardsAbilities.size();i++)
{
MTGAbility * a = dynamic_cast<MTGAbility *>(_target->cardsAbilities[i]);
if(a) game->removeObserver(a);
}
_target->cardsAbilities.clear();
_target->magicText = myFlip->magicText;
af.getAbilities(&currentAbilities, NULL, _target);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
a->source = (MTGCardInstance *) _target;
if (a)
{
if (a->oneShot)
{
a->resolve();
SAFE_DELETE(a);
}
else
{
a->addToGame();
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
if(!dontAdd)
{
_target->cardsAbilities.push_back(a);
}
}
}
}
//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;
}
currentAbilities.clear();
testDestroy();
}
return 1;
}
int AAFlip::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target)
{
if(_target->isFlipped)
{
this->forceDestroy = 1;
//_target->getObserver()->removeObserver(this);
//originally added as a safegaurd to insure the ability was removed
//it's been so long and so much has changed that it appears to do nothing but cause a crash now
_target->isFlipped = false;
return 1;
}
}
return 0;
}
const string AAFlip::getMenuText()
{
string s = flipStats;
sprintf(menuText, "Transform:%s", s.c_str());
return menuText;
}
AAFlip * AAFlip::clone() const
{
AAFlip * a = NEW AAFlip(*this);
a->forceDestroy = 1;
return a;
}
// AADYNAMIC: dynamic ability builder
AADynamic::AADynamic(GameObserver* observer, int id, MTGCardInstance * card, Damageable * _target,int type,int effect,int who,int amountsource,MTGAbility * storedAbility, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0),type(type),effect(effect),who(who),amountsource(amountsource),storedAbility(storedAbility)
{
target = _target;
sourceamount = 0;
targetamount = 0;
eachother = false;
tosrc = false;
menu = "";
OriginalSrc = source;
clonedStored = NULL;
mainAbility = NULL;
}
int AADynamic::resolve()
{
Damageable * _target = (Damageable *) target;
Damageable * secondaryTarget = NULL;
if(amountsource == 2)
source = (MTGCardInstance * )_target;
switch(who)
{
case DYNAMIC_ABILITY_WHO_EACHOTHER://each other, both take the effect
eachother = true;
break;
case DYNAMIC_ABILITY_WHO_ITSELF:
source = ((MTGCardInstance *) _target);
break;
case DYNAMIC_ABILITY_WHO_TARGETCONTROLLER:
secondaryTarget = ((MTGCardInstance *) _target)->controller();
break;
case DYNAMIC_ABILITY_WHO_TARGETOPPONENT:
secondaryTarget = ((MTGCardInstance *) _target)->controller()->opponent();
break;
case DYNAMIC_ABILITY_WHO_TOSOURCE:
tosrc = true;
break;
case DYNAMIC_ABILITY_WHO_SOURCECONTROLLER:
secondaryTarget = ((MTGCardInstance *) OriginalSrc)->controller();
break;
case DYNAMIC_ABILITY_WHO_SOURCEOPPONENT:
secondaryTarget = OriginalSrc->controller()->opponent();
break;
default:
break;
}
if(amountsource == DYNAMIC_MYSELF_AMOUNT)
_target = OriginalSrc->controller();//looking at controller for amount
if(amountsource == DYNAMIC_MYFOE_AMOUNT)
_target = OriginalSrc->controller()->opponent();//looking at controllers opponent for amount
if(!_target)
return 0;
while (dynamic_cast<MTGCardInstance *>(_target) && ((MTGCardInstance *)_target)->next)
_target = ((MTGCardInstance *)_target)->next;
//find the amount variables that will be used
sourceamount = 0;
targetamount = 0;
int colored = 0;
switch(type)
{
case DYNAMIC_ABILITY_TYPE_POWER:
sourceamount = ((MTGCardInstance *) source)->power;
targetamount = ((MTGCardInstance *) _target)->power;
if(eachother )
sourceamount = ((MTGCardInstance *) source)->power;
break;
case DYNAMIC_ABILITY_TYPE_TOUGHNESS:
sourceamount = ((MTGCardInstance *) source)->toughness;
targetamount = ((MTGCardInstance *) _target)->toughness;
if(eachother )
sourceamount = ((MTGCardInstance *) source)->toughness;
break;
case DYNAMIC_ABILITY_TYPE_MANACOST:
if(amountsource == 1)
sourceamount = ((MTGCardInstance *) source)->getManaCost()->getConvertedCost();
else
sourceamount = ((MTGCardInstance *) _target)->getManaCost()->getConvertedCost();
break;
case DYNAMIC_ABILITY_TYPE_COLORS:
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
if (amountsource == 1 && ((MTGCardInstance *)source)->hasColor(i))
++colored;
else
if (amountsource == 2 && ((MTGCardInstance *)_target)->hasColor(i))
++colored;
}
sourceamount = colored;
break;
case DYNAMIC_ABILITY_TYPE_AGE:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter("age", 0, 0))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter("age", 0, 0);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter("age", 0, 0))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter("age", 0, 0);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_CHARGE:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter("charge", 0, 0))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter("charge", 0, 0);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter("charge", 0, 0))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter("charge", 0, 0);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_ONEONECOUNTERS:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter(1, 1))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter(1,1);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter(1, 1))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter(1,1);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_THATMUCH:
{
sourceamount = _target->thatmuch;
break;
}
default:
break;
}
if(secondaryTarget != NULL)
_target = secondaryTarget;
if (_target)
{
while (dynamic_cast<MTGCardInstance *>(_target) && ((MTGCardInstance *)_target)->next)
_target = ((MTGCardInstance *)_target)->next;
if(sourceamount < 0)
sourceamount = 0;
if(targetamount < 0)
targetamount = 0;
std::stringstream out;
std::stringstream out2;
out << sourceamount;
string sourceamountstring = out.str();
out2 << targetamount;
string targetamountstring = out2.str();
//set values less then 0 to 0, it was reported that negitive numbers such as a creature who get -3/-3 having the power become
//negitive, if then used as the amount, would cuase weird side effects on resolves.
switch(effect)
{
case DYNAMIC_ABILITY_EFFECT_STRIKE://deal damage
{
mainAbility = NEW AADamager(game, this->GetId(), source,tosrc == true?(Targetable*)OriginalSrc:(Targetable*)_target,sourceamountstring);
activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
if(eachother)
{
mainAbility = NEW AADamager(game, this->GetId(), (MTGCardInstance*)_target,(Targetable*)OriginalSrc,targetamountstring);
activateMainAbility(mainAbility,source,OriginalSrc);
}
return 1;
break;
}
case DYNAMIC_ABILITY_EFFECT_DRAW://draw cards
{
mainAbility = NEW AADrawer(game, this->GetId(), source,_target,NULL, sourceamountstring);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_LIFEGAIN://gain life
{
mainAbility = NEW AALifer(game, this->GetId(), source,_target, sourceamountstring);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPPOWER://pump power
{
mainAbility = NEW PTInstant(game, this->GetId(), source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target,NEW WParsedPT(sourceamount,0));
return activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPTOUGHNESS://pump toughness
{
mainAbility = NEW PTInstant(game, this->GetId(), source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target,NEW WParsedPT(0,sourceamount));
return activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPBOTH://pump both
{
mainAbility = NEW PTInstant(game, this->GetId(), source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target,NEW WParsedPT(sourceamount,sourceamount));
return activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_LIFELOSS://lose life
{
string altered = "-";
altered.append(sourceamountstring);
mainAbility = NEW AALifer(game, this->GetId(), source,_target, altered);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_DEPLETE://deplete cards
{
mainAbility = NEW AADepleter(game, this->GetId(), source,_target, sourceamountstring);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_COUNTERSONEONE:
{
if(!dynamic_cast<MTGCardInstance *>(_target))
_target = OriginalSrc;
for(int j = 0;j < sourceamount;j++)
((MTGCardInstance*)_target)->counters->addCounter(1,1);
break;
}
default:
return 0;
}
}
return 0;
}
int AADynamic::activateMainAbility(MTGAbility * toActivate,MTGCardInstance * , Damageable *)
{
if(storedAbility)
activateStored();
if(!toActivate)
return 0;
if(PTInstant * a = dynamic_cast<PTInstant *>(toActivate))
{
a->addToGame();
return 1;
}
toActivate->oneShot = true;
toActivate->forceDestroy = 1;
toActivate->resolve();
SAFE_DELETE(toActivate);
return 1;
}
int AADynamic::activateStored()
{
clonedStored = storedAbility->clone();
clonedStored->target = target;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
return 1;
}
const string AADynamic::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
switch(type)
{
case 0:
menu.append("Power");
break;
case 1:
menu.append("Tough");
break;
case 2:
menu.append("Mana");
break;
case 3:
menu.append("color");
break;
case 4:
menu.append("Elder");
break;
case 5:
menu.append("Charged");
break;
case 6:
menu.append("Counter");
break;
case 7:
menu.append("That Many ");
break;
default:
break;
}
switch(effect)
{
case 0:
menu.append("Strike");
break;
case 1:
menu.append("Draw");
break;
case 2:
menu.append("Life");
break;
case 3:
menu.append("Pump");
break;
case 4:
menu.append("Fortify");
break;
case 5:
menu.append("Buff");
break;
case 6:
menu.append("Drain");
break;
case 7:
menu.append("Deplete!");
break;
case 8:
menu.append("Counters!");
break;
default:
break;
}
return menu.c_str();
}
AADynamic * AADynamic::clone() const
{
AADynamic * a = NEW AADynamic(*this);
a->storedAbility = storedAbility? storedAbility->clone() : NULL;
return a;
}
AADynamic::~AADynamic()
{
SAFE_DELETE(storedAbility);
}
//AALifer
AALifer::AALifer(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, string life_s, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),life_s(life_s)
{
aType = MTGAbility::LIFER;
}
int AALifer::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (!_target)
return 0;
WParsedInt life(life_s, NULL, source);
if (_target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
{
_target = ((MTGCardInstance *) _target)->controller();
}
Player *player = (Player*)_target;
player->gainOrLoseLife(life.getValue());
return 1;
}
int AALifer::getLife()
{
WParsedInt life(life_s, NULL, source);
return life.getValue();
}
const string AALifer::getMenuText()
{
if(getLife() < 0)
return "Life Loss";
return "Life";
}
AALifer * AALifer::clone() const
{
return NEW AALifer(*this);
}
//players max hand size
AASetHand::AASetHand(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int hand, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), hand(hand)
{
}
int AASetHand::resolve()
{
Damageable * _target = (Damageable *) getTarget();
Player * p = getPlayerFromDamageable(_target);
if (!p)
return 0;
p->handsize = hand;
return 1;
}
const string AASetHand::getMenuText()
{
return "Set Hand Size";
}
AASetHand * AASetHand::clone() const
{
return NEW AASetHand(*this);
}
//Lifeset
AALifeSet::AALifeSet(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * life, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), life(life)
{
}
int AALifeSet::resolve()
{
Damageable * _target = (Damageable *) getTarget();
Player * p = getPlayerFromDamageable(_target);
if (!p)
return 0;
int lifeDiff = life->getValue() - p->life ;
p->gainOrLoseLife(lifeDiff);
return 1;
}
const string AALifeSet::getMenuText()
{
return "Set Life";
}
AALifeSet * AALifeSet::clone() const
{
AALifeSet * a = NEW AALifeSet(*this);
a->life = NEW WParsedInt(*(a->life));
return a;
}
AALifeSet::~AALifeSet()
{
SAFE_DELETE(life);
}
//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 TypesList) :
ActivatedAbility(observer, _id, _source, _cost, 0), who(who)
{
aType = MTGAbility::CLONING;
target = _target;
source = _source;
if (abilitiesStringList.size() > 0)
{
PopulateAbilityIndexVector(awith, abilitiesStringList);
PopulateColorIndexVector(colors, abilitiesStringList);
}
if (TypesList.size())
{
PopulateSubtypesIndexVector(typesToAdd,TypesList);
}
}
int AACloner::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (!_target)
return 0;
// Use id of the card to have the same image as the original
MTGCard* clone = (_target->isToken ? _target: MTGCollection()->getCardById(_target->getId()));
// If its a copier then copy what it is
if(_target->isACopier)
clone = _target;
Player * targetPlayer = who == 1 ? source->controller()->opponent() : source->controller();
int tokenize = 1;//tokenizer support for cloning
if (targetPlayer->game->battlefield->hasAbility(Constants::TOKENIZER))
{
int nbcards = targetPlayer->game->battlefield->nb_cards;
for (int j = 0; j < nbcards; j++)
{
if (targetPlayer->game->battlefield->cards[j]->has(Constants::TOKENIZER))
tokenize *= 2;
}
}
for (int i = 0; i < tokenize; ++i)
{
MTGCardInstance * myClone = NEW MTGCardInstance(clone, targetPlayer->game);
targetPlayer->game->temp->addCard(myClone);
Spell * spell = NEW Spell(game, myClone);
spell->source->isToken = 1;
spell->resolve();
spell->source->fresh = 1;
spell->source->model = spell->source;
spell->source->model->data = spell->source;
//if the token doesn't have cda/dynamic pt then allow this...
if((_target->isToken) && (!_target->isCDA))
{
if(_target->pbonus > 0)
spell->source->power = _target->power - _target->pbonus;
else
spell->source->power = _target->power + _target->pbonus;
if(_target->tbonus > 0)
{
spell->source->toughness = _target->toughness - _target->tbonus;
spell->source->life = _target->toughness - _target->tbonus;
}
else
{
spell->source->toughness = _target->toughness + _target->tbonus;
spell->source->life = _target->toughness + _target->tbonus;
}
}
list<int>::iterator it;
for (it = awith.begin(); it != awith.end(); it++)
{//there must be a layer of temporary abilities and original abilities
spell->source->basicAbilities[*it] = 1;
}
for (it = colors.begin(); it != colors.end(); it++)
{
spell->source->setColor(*it);
}
for (it = typesToAdd.begin(); it != typesToAdd.end(); it++)
{
spell->source->addType(*it);
}
spell->source->modifiedbAbi = _target->modifiedbAbi;
spell->source->origbasicAbilities = _target->origbasicAbilities;
delete spell;
}
return 1;
}
const string AACloner::getMenuText()
{
if (who == 1)
return "Clone For Opponent";
return "Clone";
}
ostream& AACloner::toString(ostream& out) const
{
out << "AACloner ::: with : ?" // << abilities
<< " (";
return ActivatedAbility::toString(out) << ")";
}
AACloner * AACloner::clone() const
{
return NEW AACloner(*this);
}
AACloner::~AACloner()
{
}
// Cast/Play Restriction modifier
ACastRestriction::ACastRestriction(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
AbilityTP(observer, _id, card, _target, who), restrictionsScope(_restrictionsScope), value(_value), modifyExisting(_modifyExisting),zoneId(_zoneId)
{
existingRestriction = NULL;
targetPlayer = NULL;
}
int ACastRestriction::addToGame()
{
Targetable * _target = getTarget();
targetPlayer = getPlayerFromTarget(_target);
if (!targetPlayer)
return 0;
if (modifyExisting)
{
//For now the only modifying rule is the one for lands, so this is hardcoded here.
//This means that a modifying rule for anything lands will actually modify the lands rule.
//In the future, we need a way to "identify" rules that modify an existing restriction, probably by doing a comparison of the TargetChoosers
existingRestriction = targetPlayer->game->playRestrictions->getMaxPerTurnRestrictionByTargetChooser(restrictionsScope);
if(existingRestriction && existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX)
existingRestriction->maxPerTurn += value->getValue();
}
else
{
TargetChooser * _tc = restrictionsScope->clone();
existingRestriction = NEW MaxPerTurnRestriction(_tc, value->getValue(), MTGGameZone::intToZone(zoneId, targetPlayer));
targetPlayer->game->playRestrictions->addRestriction(existingRestriction);
}
AbilityTP::addToGame();
return 1;
}
int ACastRestriction::destroy()
{
if (!existingRestriction)
return 0;
if (modifyExisting)
{
if(existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX)
existingRestriction->maxPerTurn -= value->getValue();
}
else
{
targetPlayer->game->playRestrictions->removeRestriction(existingRestriction);
SAFE_DELETE(existingRestriction);
}
return 1;
}
const string ACastRestriction::getMenuText()
{
if (modifyExisting)
return "Additional Lands"; //hardoced because only the lands rule allows to modify existing rule for now
return "Cast Restriction";
}
ACastRestriction * ACastRestriction::clone() const
{
ACastRestriction * a = NEW ACastRestriction(*this);
a->value = NEW WParsedInt(*(a->value));
a->restrictionsScope = restrictionsScope->clone();
return a;
}
ACastRestriction::~ACastRestriction()
{
SAFE_DELETE(value);
SAFE_DELETE(restrictionsScope);
}
AInstantCastRestrictionUEOT::AInstantCastRestrictionUEOT(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
InstantAbilityTP(observer, _id, card, _target, who)
{
ability = NEW ACastRestriction(observer, _id, card, _target, _restrictionsScope, _value, _modifyExisting, _zoneId, who);
}
int AInstantCastRestrictionUEOT::resolve()
{
ACastRestriction * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const string AInstantCastRestrictionUEOT::getMenuText()
{
return ability->getMenuText();
}
AInstantCastRestrictionUEOT * AInstantCastRestrictionUEOT::clone() const
{
AInstantCastRestrictionUEOT * a = NEW AInstantCastRestrictionUEOT(*this);
a->ability = this->ability->clone();
return a;
}
AInstantCastRestrictionUEOT::~AInstantCastRestrictionUEOT()
{
SAFE_DELETE(ability);
}
//AAMover
AAMover::AAMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest,string newName, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0), destination(dest),named(newName)
{
if (_target)
target = _target;
andAbility = NULL;
}
MTGGameZone * AAMover::destinationZone(Targetable * target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
return MTGGameZone::stringToZone(game, destination, source, _target);
}
int AAMover::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (target)
{
Player* p = _target->controller();
if (p)
{
MTGGameZone * fromZone = _target->getCurrentZone();
MTGGameZone * destZone = destinationZone(target);
//inplay is a special zone !
for (int i = 0; i < 2; i++)
{
if (destZone == game->players[i]->game->inPlay && fromZone != game->players[i]->game->inPlay && fromZone
!= game->players[i]->opponent()->game->inPlay)
{
MTGCardInstance * copy = game->players[i]->game->putInZone(_target, fromZone, game->players[i]->game->temp);
Spell * spell = NEW Spell(game, copy);
spell->resolve();
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = spell->source;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
delete spell;
return 1;
}
}
p->game->putInZone(_target, fromZone, destZone);
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
}
return 0;
}
const string AAMover::getMenuText()
{
if(named.size())
return named.c_str();
return "Move";
}
const char* AAMover::getMenuText(TargetChooser * tc)
{
if(named.size())
return named.c_str();
MTGGameZone * dest = destinationZone();
for (int i = 0; i < 2; i++)
{
// Move card to hand
if (dest == game->players[i]->game->hand)
{
if (tc->targetsZone(game->players[i]->game->inPlay))
return "Bounce";
if (tc->targetsZone(game->players[i]->game->graveyard))
return "Reclaim";
if (tc->targetsZone(game->opponent()->game->hand))
return "Steal";
}
// Move card to graveyard
else if (dest == game->players[i]->game->graveyard)
{
if (tc->targetsZone(game->players[i]->game->inPlay))
return "Sacrifice";
if (tc->targetsZone(game->players[i]->game->hand))
return "Discard";
if (tc->targetsZone(game->opponent()->game->hand))
return "Opponent Discards";
}
// move card to library
else if (dest == game->players[i]->game->library)
{
if (tc->targetsZone(game->players[i]->game->graveyard))
return "Recycle";
return "Put in Library";
}
// move card to battlefield
else if (dest == game->players[i]->game->battlefield)
{
if (tc->targetsZone(game->players[i]->game->graveyard))
return "Reanimate";
return "Put in Play";
}
// move card into exile
else if (dest == game->players[i]->game->exile)
{
return "Exile";
}
// move card from Library
else if (tc->targetsZone(game->players[i]->game->library))
{
return "Fetch";
}
}
return "Move";
}
AAMover * AAMover::clone() const
{
AAMover * a = NEW AAMover(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AAMover::~AAMover()
{
SAFE_DELETE(andAbility);
}
//random movement of a card from zone to zone
AARandomMover::AARandomMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string _tcs, string _from, string _to) :
ActivatedAbility(observer, _id, _source, NULL, 0), abilityTC(_tcs),fromZone(_from),toZone(_to)
{
if (_target)
target = _target;
}
MTGGameZone * AARandomMover::destinationZone(Targetable * target,string zone)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
return MTGGameZone::stringToZone(game, zone, source, _target);
}
int AARandomMover::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (target)
{
Player* p = _target->controller();
if (p)
{
MTGGameZone * fromDest = destinationZone(target,fromZone);
MTGGameZone * toDest = destinationZone(target,toZone);
if (!fromDest->nb_cards)
return 0;
TargetChooserFactory tcf(game);
TargetChooser * rTc = tcf.createTargetChooser(abilityTC, source);
rTc->targetter = NULL;
rTc->setAllZones();
vector<MTGCardInstance*>selectedCards;
for(unsigned int i = 0; i < fromDest->cards.size();++i)
{
if(rTc->canTarget(fromDest->cards[i]))
selectedCards.push_back(fromDest->cards[i]);
}
SAFE_DELETE(rTc);
if(!selectedCards.size())
return 0;
int r = fromDest->owner->getObserver()->getRandomGenerator()->random() % (selectedCards.size());
MTGCardInstance * toMove = selectedCards[r];
//inplay is a special zone !
for (int i = 0; i < 2; i++)
{
if (toDest == game->players[i]->game->inPlay && fromDest != game->players[i]->game->inPlay && fromDest
!= game->players[i]->opponent()->game->inPlay)
{
MTGCardInstance * copy = game->players[i]->game->putInZone(toMove, fromDest, game->players[i]->game->temp);
Spell * spell = NEW Spell(game, copy);
spell->resolve();
delete spell;
return 1;
}
}
p->game->putInZone(toMove, fromDest, toDest);
return 1;
}
}
return 0;
}
const string AARandomMover::getMenuText()
{
return "Dig";
}
AARandomMover * AARandomMover::clone() const
{
AARandomMover * a = NEW AARandomMover(*this);
return a;
}
AARandomMover::~AARandomMover()
{
}
//Random Discard
AARandomDiscarder::AARandomDiscarder(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who), nbcardsStr(nbcardsStr)
{
}
int AARandomDiscarder::resolve()
{
Targetable * _target = getTarget();
Player * player = getPlayerFromTarget(_target);
if (player)
{
WParsedInt numCards(nbcardsStr, NULL, source);
for (int i = 0; i < numCards.intValue; i++)
{
player->game->discardRandom(player->game->hand, source);
}
}
return 1;
}
const string AARandomDiscarder::getMenuText()
{
return "Discard Random";
}
AARandomDiscarder * AARandomDiscarder::clone() const
{
return NEW AARandomDiscarder(*this);
}
// Shuffle
AAShuffle::AAShuffle(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who)
{
}
int AAShuffle::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
MTGLibrary * library = player->game->library;
library->shuffle();
}
return 1;
}
const string AAShuffle::getMenuText()
{
return "Shuffle";
}
AAShuffle * AAShuffle::clone() const
{
return NEW AAShuffle(*this);
}
// Mulligan
AAMulligan::AAMulligan(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who)
{
}
int AAMulligan::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
player->serumMulligan();
}
return 1;
}
const string AAMulligan::getMenuText()
{
return "Mulligan";
}
AAMulligan * AAMulligan::clone() const
{
return NEW AAMulligan(*this);
}
// Remove Mana From ManaPool
AARemoveMana::AARemoveMana(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, string manaDesc, int who) :
ActivatedAbilityTP(observer, _id, card, _target, NULL, who)
{
if (!manaDesc.size())
{
DebugTrace("ALL_ABILITIES: AARemoveMana ctor error");
return;
}
mRemoveAll = (manaDesc[0] == '*');
if (mRemoveAll)
manaDesc = manaDesc.substr(1);
mManaDesc = (manaDesc.size()) ? ManaCost::parseManaCost(manaDesc) : NULL;
}
int AARemoveMana::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
ManaPool * manaPool = player->getManaPool();
if (mRemoveAll)
{
if (mManaDesc) // Remove all mana Matching a description
{
for (int i = 0; i < Constants::NB_Colors; i++)
{
if (mManaDesc->hasColor(i))
manaPool->removeAll(i);
}
}
else //Remove all mana
{
if(game->getCurrentGamePhase() != MTG_PHASE_ENDOFTURN)
{
if (player->doesntEmpty->getConvertedCost() && !player->poolDoesntEmpty->getConvertedCost())
{
ManaCost * toRemove = manaPool->Diff(player->doesntEmpty);
player->getManaPool()->pay(toRemove);
delete(toRemove);
return 1;
}
else if(!player->doesntEmpty->getConvertedCost() && player->poolDoesntEmpty->getConvertedCost())
{
ManaCost * toSave = NEW ManaCost();
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
if(player->poolDoesntEmpty->getCost(k))
toSave->add(k,manaPool->getCost(k));
}
player->getManaPool()->pay(manaPool->Diff(toSave));
delete(toSave);
return 1;
}
else if(player->doesntEmpty->getConvertedCost() && player->poolDoesntEmpty->getConvertedCost())
{
ManaCost * toSave = NEW ManaCost();
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
if(player->poolDoesntEmpty->getCost(k))
{
toSave->add(k,manaPool->getCost(k));//save the whole amount of k;
}
else if(player->doesntEmpty->getCost(k))
{
toSave->add(k,player->doesntEmpty->getCost(k));//save the amount of doesnt empty
}
}
player->getManaPool()->pay(manaPool->Diff(toSave));//remove the manacost equal to the difference of toSave and the manapool.
delete(toSave);
return 1;
}
manaPool->Empty();
}
else
manaPool->Empty();
}
}
else //remove a "standard" mana Description
{
((ManaCost *)manaPool)->remove(mManaDesc); //why do I have to cast here?
}
}
return 1;
}
const string AARemoveMana::getMenuText()
{
if (mRemoveAll && !mManaDesc)
return "Empty Manapool";
return "Remove Mana";
}
AARemoveMana * AARemoveMana::clone() const
{
AARemoveMana * a = NEW AARemoveMana(*this);
a->mManaDesc = mManaDesc ? NEW ManaCost(mManaDesc) : NULL;
return a;
}
AARemoveMana::~AARemoveMana()
{
SAFE_DELETE(mManaDesc);
}
//Tapper
AATapper::AATapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0)
{
target = _target;
aType = MTGAbility::TAPPER;
}
int AATapper::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->tap();
}
return 1;
}
const string AATapper::getMenuText()
{
return "Tap";
}
AATapper * AATapper::clone() const
{
return NEW AATapper(*this);
}
//AA Untapper
AAUntapper::AAUntapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0)
{
target = _target;
aType = MTGAbility::UNTAPPER;
}
int AAUntapper::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->untap();
}
return 1;
}
const string AAUntapper::getMenuText()
{
return "Untap";
}
AAUntapper * AAUntapper::clone() const
{
return NEW AAUntapper(*this);
}
AAWhatsMax::AAWhatsMax(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance *, ManaCost * _cost, int value) :
ActivatedAbility(observer, id, card, _cost, 0), value(value)
{
}
int AAWhatsMax::resolve()
{
if (source)
{
source->MaxLevelUp = value;
source->isLeveler = 1;
}
return 1;
}
AAWhatsMax * AAWhatsMax::clone() const
{
return NEW AAWhatsMax(*this);
}
// Win Game
AAWinGame::AAWinGame(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who)
{
}
int AAWinGame::resolve()
{
Player * p = getPlayerFromDamageable((Damageable *) getTarget());
if (!p)
return 0;
bool canwin = true;
MTGGameZone * z = p->opponent()->game->inPlay;
int nbcards = z->nb_cards;
for (int i = 0; i < nbcards; i++)
{
MTGCardInstance * c = z->cards[i];
if (c->has(Constants::CANTLOSE))
{
canwin = false;
break;
}
}
if (canwin)
{
MTGGameZone * k = p->game->inPlay;
int onbcards = k->nb_cards;
for (int m = 0; m < onbcards; ++m)
{
MTGCardInstance * e = k->cards[m];
if (e->has(Constants::CANTWIN))
{
canwin = false;
break;
}
}
}
if (canwin)
{
game->setLoser(p->opponent());
}
return 1;
}
const string AAWinGame::getMenuText()
{
return "Win Game";
}
AAWinGame * AAWinGame::clone() const
{
return NEW AAWinGame(*this);
}
//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)
{
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, MTGAbility * delayedElseAbility, MTGCardInstance * _source, Targetable * _target, int type,string Cond) :
InstantAbility(observer, _id, _source),delayedAbility(delayedAbility),delayedElseAbility(delayedElseAbility), type(type),Cond(Cond)
{
target = _target;
}
int IfThenAbility::resolve()
{
MTGCardInstance * card = (MTGCardInstance*)source;
AbilityFactory af(game);
Targetable* aTarget = (Targetable*)target;
int checkCond = af.parseCastRestrictions(card,card->controller(),Cond);
if(Cond.find("cantargetcard(") != string::npos)
{
TargetChooser * condTc = NULL;
vector<string>splitTarget = parseBetween(Cond, "card(", ")");
if (splitTarget.size())
{
TargetChooserFactory tcf(game);
condTc = tcf.createTargetChooser(splitTarget[1], source);
condTc->targetter = NULL;
if(aTarget)
checkCond = condTc->canTarget(aTarget);
SAFE_DELETE(condTc);
}
}
MTGAbility * a1 = NULL;
if((checkCond && type == 1)||(!checkCond && type == 2))
{
a1 = delayedAbility->clone();
}
else if(delayedElseAbility)
{
a1 = delayedElseAbility->clone();
}
if (!a1)
return 0;
else
{
if(a1->target && !dynamic_cast<Player *>(a1->target))
a1->target = aTarget;
if(a1->oneShot)
{
a1->resolve();
SAFE_DELETE(a1);
}
else
a1->addToGame();
return 1;
}
return 0;
}
const string IfThenAbility::getMenuText()
{
return "";
}
IfThenAbility * IfThenAbility::clone() const
{
IfThenAbility * a = NEW IfThenAbility(*this);
a->delayedAbility = delayedAbility->clone();
return a;
}
IfThenAbility::~IfThenAbility()
{
SAFE_DELETE(delayedAbility);
SAFE_DELETE(delayedElseAbility);
}
//
//May Abilities
MayAbility::MayAbility(GameObserver* observer, int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must,string _cond) :
MTGAbility(observer, _id, _source), NestedAbility(_ability), must(must), Cond(_cond)
{
triggered = 0;
mClone = NULL;
}
void MayAbility::Update(float dt)
{
MTGAbility::Update(dt);
if (!triggered && !game->getCurrentTargetChooser() && (!game->mLayers->actionLayer()->menuObject||game->mLayers->actionLayer()->menuObject == source))
{
triggered = 1;
if(Cond.size())
{
AbilityFactory af(game);
int checkCond = af.parseCastRestrictions(source,source->controller(),Cond);
if(!checkCond)
{
return;
}
}
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability))
{
if (!ta->getActionTc()->validTargetsExist() || ta->getActionTc()->maxtargets == 0)
return;
}
game->mLayers->actionLayer()->setMenuObject(source, must);
previousInterrupter = game->isInterrupting;
game->mLayers->stackLayer()->setIsInterrupting(source->controller(), false);
}
}
const string MayAbility::getMenuText()
{
return ability->getMenuText();
}
int MayAbility::testDestroy()
{
if (!triggered)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(mClone) != -1)
return 0;
if(game->currentPlayer == source->controller() && game->isInterrupting == source->controller() && dynamic_cast<AManaProducer*>(AbilityFactory::getCoreAbility(ability)))
//if its my turn, and im interrupting myself(why?) then set interrupting to previous interrupter if the ability was a manaability
//special case since they don't use the stack.
game->mLayers->stackLayer()->setIsInterrupting(previousInterrupter, false);
return 1;
}
int MayAbility::isReactingToTargetClick(Targetable * card)
{
if (card == source)
{
if(Cond.size())
{
AbilityFactory af(game);
int checkCond = af.parseCastRestrictions(source,source->controller(),Cond);
if(!checkCond)
{
return 0;
}
}
return 1;
}
return 0;
}
int MayAbility::reactToTargetClick(Targetable * object)
{
mClone = ability->clone();
mClone->addToGame();
mClone->forceDestroy = 1;
return mClone->reactToTargetClick(object);
}
MayAbility * MayAbility::clone() const
{
MayAbility * a = NEW MayAbility(*this);
a->ability = ability->clone();
return a;
}
MayAbility::~MayAbility()
{
SAFE_DELETE(ability);
}
//Menu building ability Abilities
MenuAbility::MenuAbility(GameObserver* observer, int _id, Targetable * mtarget, MTGCardInstance * _source, bool must,vector<MTGAbility*>abilities,Player * who, string newName) :
MayAbility(observer, _id,NULL,_source,must), must(must),abilities(abilities),who(who),newNameString(newName)
{
triggered = 0;
mClone = NULL;
this->target = mtarget;
removeMenu = false;
vector<ManaCost*>optionalCost = vector<ManaCost*>();
toPay = NULL;
processed = false;
}
bool MenuAbility::CheckUserInput(JButton key)
{
if (game->mExtraPayment && key == JGE_BTN_SEC)
{
if(toPay && toPay->extraCosts == game->mExtraPayment)
{
//the user cancelled the paidability. fireAbility() on the second menu item.
//paidability will always occupy the abilities[0]; in the vector.
if(abilities.size() > 1)
{
abilities[1]->target = abilities[0]->target;
abilities[1]->fireAbility();
}
}
return false;
}
return false;
}
void MenuAbility::Update(float dt)
{
MTGAbility::Update(dt);
ActionLayer * object = game->mLayers->actionLayer();
if(toPay && game->mExtraPayment && !processed)
{
if(game->mExtraPayment->isPaymentSet() && game->mExtraPayment->canPay() )
{
game->mExtraPayment->doPay();
game->mLayers->actionLayer()->reactToClick(game->mExtraPayment->action, game->mExtraPayment->source);
game->mExtraPayment = NULL;
processAbility();
return;
}
}
if (!triggered && !object->menuObject && !object->getCurrentTargetChooser())
{
triggered = 1;
object->currentActionCard = (MTGCardInstance*)this->target;
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability))
{
if (!ta->getActionTc()->validTargetsExist())
return;
}
}
if(object->currentActionCard && this->target != object->currentActionCard)
{
triggered = 0;
}
if(triggered && !game->mExtraPayment && !processed)
{
game->mLayers->actionLayer()->setCustomMenuObject(source, must,abilities,newNameString.size()?newNameString.c_str():"");
previousInterrupter = game->isInterrupting;
game->mLayers->stackLayer()->setIsInterrupting(source->controller(), false);
}
}
int MenuAbility::resolve()
{
this->triggered = 1;
MTGAbility * a = this;
return a->addToGame();
}
const string MenuAbility::getMenuText()
{
if((abilities.size() > 1 && must)||(abilities.size() > 2 && !must))
return "choose one";
return "Action";
}
int MenuAbility::testDestroy()
{
if (game->mExtraPayment)
return 0;
if (!removeMenu)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(mClone) != -1)
return 0;
return 1;
}
int MenuAbility::isReactingToTargetClick(Targetable * card){return 0/*MayAbility::isReactingToTargetClick(card)*/;}
int MenuAbility::reactToTargetClick(Targetable * object){return 1;}
int MenuAbility::processAbility()
{
if(!mClone)
return 0;
if(processed)
return 0;
if(abilities[0])
mClone->target = abilities[0]->target;
if(MayAbility * toCheck = dynamic_cast<MayAbility*>(mClone))
{
toCheck->must = true;
mClone->addToGame();
}
else
{
mClone->oneShot = true;
mClone->forceDestroy = 1;
mClone->canBeInterrupted = false;
mClone->resolve();
SAFE_DELETE(mClone);
if (source->controller() == game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
}
processed = true;
this->forceDestroy = 1;
removeMenu = true;
return 1;
}
int MenuAbility::reactToChoiceClick(Targetable * object,int choice,int control)
{
ActionElement * currentAction = (ActionElement *) game->mLayers->actionLayer()->mObjects[control];
if(currentAction != (ActionElement*)this)
return 0;
if(!abilities.size()||!triggered)
return 0;
for(int i = 0;i < int(abilities.size());i++)
{
if(choice == i)
mClone = abilities[choice]->clone();
else if(!optionalCosts.size())
SAFE_DELETE(abilities[i]);
if (mClone && !toPay && optionalCosts.size() && i < int(optionalCosts.size()) && optionalCosts[i])//paidability only supports the first ability as paid for now.
{
toPay = NEW ManaCost();
if (optionalCosts[i]->extraCosts)
toPay->extraCosts = optionalCosts[i]->extraCosts->clone();
toPay->addExtraCost(NEW ExtraManaCost(NEW ManaCost(optionalCosts[i])));
toPay->setExtraCostsAction(this, source);
game->mExtraPayment = toPay->extraCosts;
return 0;
}
}
if(!mClone)
{
if (source->controller() == game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
return 0;
}
mClone->target = abilities[choice]->target;
processAbility();
return reactToTargetClick(object);
}
MenuAbility * MenuAbility::clone() const
{
MenuAbility * a = NEW MenuAbility(*this);
a->canBeInterrupted = false;
if(abilities.size())
{
for(int i = 0;i < int(abilities.size());i++)
{
a->abilities.push_back(abilities[i]->clone());
a->optionalCosts.push_back(NEW ManaCost(optionalCosts[i]));
a->abilities[i]->target = abilities[i]->target;
}
}
else
a->ability = ability->clone();
return a;
}
MenuAbility::~MenuAbility()
{
if(abilities.size())
{
for(int i = 0;i < int(abilities.size());i++)
{
if(abilities[i])
{
AASetColorChosen * chooseA = dynamic_cast<AASetColorChosen *>(abilities[i]);
if(chooseA && chooseA->abilityAltered)
SAFE_DELETE(chooseA->abilityAltered);
SAFE_DELETE(abilities[i]);
}
}
}
else
SAFE_DELETE(ability);
SAFE_DELETE(toPay);
SAFE_DELETE(mClone);
if(optionalCosts.size())
for(int i = 0;i < int(optionalCosts.size());i++)
{
if(optionalCosts[i])
{
SAFE_DELETE(optionalCosts[i]);
}
}
}
///
//MultiAbility : triggers several actions for a cost
MultiAbility::MultiAbility(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, card, _cost, 0)
{
if (_target)
target = _target;
}
int MultiAbility::Add(MTGAbility * ability)
{
abilities.push_back(ability);
return 1;
}
int MultiAbility::resolve()
{
Targetable * Phaseactiontarget = NULL;
for (size_t i = 0; i < abilities.size(); ++i)
{
if (abilities[i] == NULL)
continue;
Targetable * backup = abilities[i]->target;
if (target && target != source && abilities[i]->target == abilities[i]->source)
{
abilities[i]->target = target;
Phaseactiontarget = target;
}
abilities[i]->resolve();
abilities[i]->target = backup;
if(Phaseactiontarget && dynamic_cast<APhaseActionGeneric *> (abilities[i]))
abilities[i]->target = Phaseactiontarget;
}
return 1;
}
int MultiAbility::addToGame()
{
for (size_t i = 0; i < abilities.size(); ++i)
{
if (abilities[i] == NULL)
continue;
MTGAbility * a = abilities[i]->clone();
a->target = target;
a->addToGame();
clones.push_back(a);
}
MTGAbility::addToGame();
return 1;
}
int MultiAbility::destroy()
{
for (size_t i = 0; i < clones.size(); ++i)
{
//I'd like to call game->removeObserver here instead of using forceDestroy, but I get a weird crash after that, need to investigate a bit
clones[i]->forceDestroy = 1;
}
clones.clear();
return ActivatedAbility::destroy();
}
const string MultiAbility::getMenuText()
{
if (abilities.size() && abilities[0])
return abilities[0]->getMenuText();
return "";
}
MultiAbility * MultiAbility::clone() const
{
MultiAbility * a = NEW MultiAbility(*this);
a->abilities.clear();
for (size_t i = 0; i < abilities.size(); ++i)
{
a->abilities.push_back(abilities[i]->clone());
}
return a;
}
MultiAbility::~MultiAbility()
{
for (size_t i = 0; i < abilities.size(); ++i)
{
SAFE_DELETE(abilities[i]);
}
abilities.clear();
}
//Generic Target Ability
GenericTargetAbility::GenericTargetAbility(GameObserver* observer, string newName, string castRestriction, int _id, MTGCardInstance * _source, TargetChooser * _tc, MTGAbility * a,
ManaCost * _cost, string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest,string _tcString) :
TargetAbility(observer, _id, _source, _tc, _cost, restrictions, castRestriction), limit(limit), activeZone(dest),newName(newName),sideEffects(sideEffects),usesBeforeSideEffects(usesBeforeSideEffects),tcString(_tcString)
{
ability = a;
MTGAbility * core = AbilityFactory::getCoreAbility(a);
if (dynamic_cast<AACopier *> (core))
tc->other = true; //http://code.google.com/p/wagic/issues/detail?id=209 (avoid inifinite loop)
counters = 0;
}
const string GenericTargetAbility::getMenuText()
{
if (!ability)
return "Error";
if (newName.size())
return newName.c_str();
//Special case for move
MTGAbility * core = AbilityFactory::getCoreAbility(ability);
if (AAMover * move = dynamic_cast<AAMover *>(core))
return (move->getMenuText(tc));
return ability->getMenuText();
}
int GenericTargetAbility::resolve()
{
counters++;
tc->done = false;
if(sideEffects && usesBeforeSideEffects.size())
{
WParsedInt * use = NEW WParsedInt(usesBeforeSideEffects.c_str(),NULL,source);
uses = use->getValue();
delete use;
if(counters == uses)
{
sa = sideEffects->clone();
sa->target = this->target;
sa->source = this->source;
if(sa->oneShot)
{
sa->fireAbility();
}
else
{
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), sa);
wrapper->addToGame();
}
}
}
return TargetAbility::resolve();
}
int GenericTargetAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
limitPerTurn = 0;
if(limit.size())
{
WParsedInt value(limit.c_str(),NULL,source);
limitPerTurn = value.getValue();
}
if (limitPerTurn && counters >= limitPerTurn)
return 0;
if(tcString.size() && !tc->targetListSet())
{
TargetChooser * current = this->getActionTc();
TargetChooserFactory tcf(game);
TargetChooser *refreshed = tcf.createTargetChooser(tcString, source, this);
refreshed->setTargetsTo(current->getTargetsFrom());
this->setActionTC(refreshed);
SAFE_DELETE(current);
}
return TargetAbility::isReactingToClick(card, mana);
}
void GenericTargetAbility::Update(float dt)
{
if (newPhase != currentPhase && newPhase == MTG_PHASE_AFTER_EOT)
{
counters = 0;
}
TargetAbility::Update(dt);
}
int GenericTargetAbility::testDestroy()
{
if (!activeZone)
return TargetAbility::testDestroy();
if (activeZone->hasCard(source))
return 0;
return 1;
}
GenericTargetAbility * GenericTargetAbility::clone() const
{
GenericTargetAbility * a = NEW GenericTargetAbility(*this);
a->ability = ability->clone();
return a;
}
GenericTargetAbility::~GenericTargetAbility()
{
SAFE_DELETE(ability);
SAFE_DELETE(sideEffects);
}
//Alter Cost
AAlterCost::AAlterCost(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int amount, int type) :
MTGAbility(observer, id, source, target), amount(amount), type(type)
{
manaReducer = source;
}
int AAlterCost::addToGame()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(!_target || _target->hasType("land"))
{
this->forceDestroy = 1;
return MTGAbility::addToGame();
}
if (amount > 0)
{
if(!_target->getIncreasedManaCost()->getConvertedCost())
{
ManaCost * increased = NEW ManaCost();
_target->getIncreasedManaCost()->copy(increased);
delete increased;
}
_target->getIncreasedManaCost()->add(type,amount);
}
else
{
if(!_target->getReducedManaCost()->getConvertedCost())
{
ManaCost * reduced = NEW ManaCost();
_target->getReducedManaCost()->copy(reduced);
delete reduced;
}
_target->getReducedManaCost()->add(type,abs(amount));
}
return MTGAbility::addToGame();
}
int AAlterCost::destroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if(!this->manaReducer->isInPlay(game))
{
if (amount > 0)
{
_target->getIncreasedManaCost()->remove(type,amount);
refreshCost(_target);//special case for 0 cost.
}
else
{
_target->getReducedManaCost()->remove(type,abs(amount));
refreshCost(_target);//special case for 0 cost.
}
return MTGAbility::testDestroy();
}
return 0;
}
int AAlterCost::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if(!this->manaReducer->isInPlay(game))
{
if (amount > 0)
{
_target->getIncreasedManaCost()->remove(type,amount);
refreshCost(_target);//special case for 0 cost.
}
else
{
_target->getReducedManaCost()->remove(type,abs(amount));
refreshCost(_target);//special case for 0 cost.
}
return MTGAbility::testDestroy();
}
return 0;
}
void AAlterCost::refreshCost(MTGCardInstance * card)
{
ManaCost * original = NEW ManaCost();
original->copy(card->model->data->getManaCost());
original->add(card->getIncreasedManaCost());
original->remove(card->getReducedManaCost());
card->getManaCost()->copy(original);
delete original;
return;
}
void AAlterCost::increaseTheCost(MTGCardInstance * card)
{
if(card->getIncreasedManaCost()->getConvertedCost())
{
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
card->getManaCost()->add(k,card->getIncreasedManaCost()->getCost(k));
if (card->getManaCost()->getAlternative())
{
card->getManaCost()->getAlternative()->add(k,card->getIncreasedManaCost()->getCost(k));
}
if (card->getManaCost()->getBuyback())
{
card->getManaCost()->getBuyback()->add(k,card->getIncreasedManaCost()->getCost(k));
}
}
}
return;
}
void AAlterCost::decreaseTheCost(MTGCardInstance * card)
{
if(card->getReducedManaCost()->getConvertedCost())
{
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
card->getManaCost()->remove(k,card->getReducedManaCost()->getCost(k));
if (card->getManaCost()->getAlternative())
{
card->getManaCost()->getAlternative()->remove(k,card->getReducedManaCost()->getCost(k));
}
if (card->getManaCost()->getBuyback())
{
card->getManaCost()->getBuyback()->remove(k,card->getReducedManaCost()->getCost(k));
}
}
}
return;
}
AAlterCost * AAlterCost::clone() const
{
return NEW AAlterCost(*this);
}
AAlterCost::~AAlterCost()
{
}
// ATransformer
ATransformer::ATransformer(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, string sabilities,string newpower,bool newpowerfound,string newtoughness,bool newtoughnessfound,vector<string> newAbilitiesList,bool newAbilityFound,bool aForever, bool aUntilNext,string _menu) :
MTGAbility(observer, id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever),UYNT(aUntilNext),menutext(_menu)
{
if (target != source) {
target->storedSourceCard = source;
}
PopulateAbilityIndexVector(abilities, sabilities);
PopulateColorIndexVector(colors, sabilities);
if(sabilities.find("chosencolor") != string::npos)
{
colors.push_back(source->chooseacolor);
}
myCurrentTurn = 1000;
//this subkeyword adds a color without removing the existing colors.
addNewColors = (sabilities.find("newcolors") != string::npos);
remove = (stypes.find("removealltypes") != string::npos);
removeCreatureSubtypes = (stypes.find("removecreaturesubtypes") != string::npos);
removeTypes = (stypes.find("removetypes") != string::npos);
if (stypes.find("allsubtypes") != string::npos || stypes.find("removecreaturesubtypes") != string::npos)
{
const vector<string> values = MTGAllCards::getValuesById();
for (size_t i = 0; i <values.size(); ++i)
{
if (!MTGAllCards::isSubtypeOfType(i,Subtypes::TYPE_CREATURE))
continue;
types.push_back(i);
}
}
else
{
if(stypes.find("chosentype") != string::npos)
{
stypes = source->chooseasubtype;
}
PopulateSubtypesIndexVector(types, stypes);
}
menu = stypes;
}
int ATransformer::addToGame()
{
if(UYNT)
myCurrentTurn = game->turn;
MTGCardInstance * _target = NULL;
Interruptible * action = (Interruptible *) target;
if (action && action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
{
Spell * spell = (Spell *) action;
_target = spell->source;
aForever = true;
//when targeting the stack, set the effect to forever, incase the person does not use it
//otherwise we will end up with a null pointer on the destroy.
}
else
{
_target = (MTGCardInstance *) target;
}
if (!_target)
{
DebugTrace("ALL_ABILITIES: Target not set in ATransformer::addToGame\n");
return 0;
}
while (_target->next)
_target = _target->next;
for (int j = 0; j < Constants::NB_Colors; j++)
{
if (_target->hasColor(j))
oldcolors.push_back(j);
}
for (size_t j = 0; j < _target->types.size(); ++j)
oldtypes.push_back( _target->types[j]);
list<int>::iterator it;
for (it = colors.begin(); it != colors.end(); it++)
{
if(!addNewColors)
_target->setColor(0, 1);
}
if (removeTypes)
{
//remove the main types from a card, ie: hidden enchantment cycle.
for (int i = 0; i < Subtypes::LAST_TYPE; ++ i)
_target->removeType(i,1);
}
else if (remove)
{
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
{
_target->removeType(*it);
}
}
else
{
for (it = types.begin(); it != types.end(); it++)
{
if(removeCreatureSubtypes)
{
_target->removeType(*it);
}
else if(_target->hasSubtype(*it))
{
//we generally don't want to give a creature type creature again
//all it does is create a sloppy mess of the subtype line on alternative quads
//also creates instances where a card gained a type from an ability like this one
//then loses the type through another ability, when this effect is destroyed the creature regains
//the type, which is wrong.
dontremove.push_back(*it);
}
else
{
_target->addType(*it);
}
}
}
for (it = colors.begin(); it != colors.end(); it++)
{
_target->setColor(*it);
}
for (it = abilities.begin(); it != abilities.end(); it++)
{
_target->basicAbilities.set(*it);
_target->modifiedbAbi += 1;
}
if(newAbilityFound)
{
for (unsigned int k = 0 ; k < newAbilitiesList.size();k++)
{
AbilityFactory af(game);
MTGAbility * aNew = af.parseMagicLine(newAbilitiesList[k], 0, NULL, _target);
if(!aNew)
continue;
GenericTargetAbility * gta = dynamic_cast<GenericTargetAbility*> (aNew);
if (gta)
{
((GenericTargetAbility *)aNew)->source = _target;
((GenericTargetAbility *)aNew)->ability->source = _target;
}
GenericActivatedAbility * gaa = dynamic_cast<GenericActivatedAbility*> (aNew);
if (gaa)
{
((GenericActivatedAbility *)aNew)->source = _target;
((GenericActivatedAbility *)aNew)->ability->source = _target;
}
MultiAbility * abi = dynamic_cast<MultiAbility*>(aNew);
if (abi)
{
((MultiAbility *)aNew)->source = _target;
((MultiAbility *)aNew)->abilities[0]->source = _target;
}
aNew->target = _target;
aNew->source = (MTGCardInstance *) _target;
if(aNew->oneShot)
{
aNew->resolve();
delete aNew;
}
else
{
aNew->addToGame();
newAbilities[_target].push_back(aNew);
}
}
}
if(newpowerfound || newtoughnessfound)
_target->isSettingBase += 1;
if(newpowerfound )
{
WParsedInt * val = NEW WParsedInt(newpower,NULL, source);
if(_target->isSwitchedPT)
{
_target->switchPT(false);
_target->addbaseP(val->getValue());
_target->switchPT(true);
}
else
_target->addbaseP(val->getValue());
delete val;
}
if(newtoughnessfound )
{//we should consider the damage if there is, if you have a 5/5 creature with 1 damage,
//and you turn it into 1/1, the 1 damage is still there and the creature must die...
//the toughness is intact but what we see in the game is the life...
WParsedInt * val = NEW WParsedInt(newtoughness,NULL, source);
if(_target->isSwitchedPT)
{
_target->switchPT(false);
_target->addbaseT(val->getValue());
_target->switchPT(true);
}
else
_target->addbaseT(val->getValue());
delete val;
}
return MTGAbility::addToGame();
}
int ATransformer::reapplyCountersBonus(MTGCardInstance * rtarget,bool , bool toughnessapplied)
{
if(!rtarget->counters || !rtarget->counters->counters.size())
return 0;
Counter * c = rtarget->counters->counters[0];
int rNewPower = 0;
int rNewToughness = 0;
for (int t = 0; t < rtarget->counters->mCount; t++)
{
if (c)
{
for(int i = 0;i < c->nb;i++)
{
rNewPower += c->power;
rNewToughness += c->toughness;
}
}
c = rtarget->counters->getNext(c);
}
if(toughnessapplied)
return rNewToughness;
return rNewPower;
}
int ATransformer::testDestroy()
{
if(UYNT)
{
if(myCurrentTurn != 1000 && game->turn > myCurrentTurn && source->controller()->getId() == game->currentPlayer->getId())
{
return 1;
}
}
return MTGAbility::testDestroy();
}
int ATransformer::destroy()
{
if(aForever)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
list<int>::iterator it;
if (!remove)
{
for (it = types.begin(); it != types.end(); it++)
{
bool removing = true;
for(unsigned int k = 0;k < dontremove.size();k++)
{
if(dontremove[k] == *it)
removing = false;
}
if(removing)
_target->removeType(*it);
}
//iterators annoy me :/
}
for (it = colors.begin(); it != colors.end(); it++)
{
_target->removeColor(*it);
}
for (it = abilities.begin(); it != abilities.end(); it++)
{
_target->basicAbilities.reset(*it);
_target->modifiedbAbi -= 1;
}
for (it = oldcolors.begin(); it != oldcolors.end(); it++)
{
_target->setColor(*it);
}
if(newpowerfound || newtoughnessfound)
_target->isSettingBase -= 1;
if(newpowerfound )
{
_target->revertbaseP();
}
if(newtoughnessfound )
{
_target->revertbaseT();
}
if(newAbilityFound)
{
for (unsigned int i = 0;i < newAbilities[_target].size(); i++)
{
if(newAbilities[_target].at(i))
{
newAbilities[_target].at(i)->forceDestroy = 1;
newAbilities[_target].at(i)->removeFromGame();
}
}
if (newAbilities.find(_target) != newAbilities.end())
{
newAbilities.erase(_target);
}
}
if (remove || removeCreatureSubtypes)
{
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
{
if(!_target->hasSubtype(*it))
_target->addType(*it);
}
}
////in the case that we removed or added types to a card, so that it retains its original name when the effect is removed.
//if(_target->model->data->name.size())//tokens don't have a model name.
// _target->setName(_target->model->data->name.c_str());
//edit: this ability shouldn't have to reset the name on a card becuase removing a subtype changes the name of a land.
//that should be handled in addType...not here.
//im sure commenting this out will reintroduce a bug somewhere but it needs to be handled correctly. furthermore, why does adding and removing a type touch the name of a card?
}
return 1;
}
const string ATransformer::getMenuText()
{
if(menutext.size())
return menutext.c_str();
string s = menu;
sprintf(menuText, "Becomes %s", s.c_str());
return menuText;
}
ATransformer * ATransformer::clone() const
{
return NEW ATransformer(*this);
}
ATransformer::~ATransformer()
{
}
//ATransformerInstant
ATransformerInstant::ATransformerInstant(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities,string newpower,bool newpowerfound,string newtoughness,bool newtoughnessfound,vector<string>newAbilitiesList,bool newAbilityFound,bool aForever,bool aUntilNext,string _menu) :
InstantAbility(observer, id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever),UYNT(aUntilNext),menu(_menu)
{
ability = NEW ATransformer(game, id, source, target, types, abilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,aForever,aUntilNext,_menu);
aType = MTGAbility::STANDARD_BECOMES;
}
int ATransformerInstant::resolve()
{
ATransformer * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const string ATransformerInstant::getMenuText()
{
if(menu.size())
return menu.c_str();
return ability->getMenuText();
}
ATransformerInstant * ATransformerInstant::clone() const
{
ATransformerInstant * a = NEW ATransformerInstant(*this);
a->ability = this->ability->clone();
return a;
}
ATransformerInstant::~ATransformerInstant()
{
SAFE_DELETE(ability);
}
//P/t ueot
PTInstant::PTInstant(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, WParsedPT * wppt,string s,bool nonstatic) :
InstantAbility(observer, id, source, target), wppt(wppt),s(s),nonstatic(nonstatic)
{
ability = NEW APowerToughnessModifier(game, id, source, target, wppt,s,nonstatic);
aType = MTGAbility::STANDARD_PUMP;
}
int PTInstant::resolve()
{
APowerToughnessModifier * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
((Damageable *) (this->target))->afterDamage();//additional check the negative pt after resolving..
return 1;
}
const string PTInstant::getMenuText()
{
return ability->getMenuText();
}
PTInstant * PTInstant::clone() const
{
PTInstant * a = NEW PTInstant(*this);
a->ability = this->ability->clone();
return a;
}
PTInstant::~PTInstant()
{
SAFE_DELETE(ability);
}
// ASwapPTUEOT
ASwapPTUEOT::ASwapPTUEOT(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target) :
InstantAbility(observer, id, source, target)
{
ability = NEW ASwapPT(observer, id, source, target);
}
int ASwapPTUEOT::resolve()
{
ASwapPT * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const string ASwapPTUEOT::getMenuText()
{
return ability->getMenuText();
}
ASwapPTUEOT * ASwapPTUEOT::clone() const
{
ASwapPTUEOT * a = NEW ASwapPTUEOT(*this);
a->ability = this->ability->clone();
return a;
}
ASwapPTUEOT::~ASwapPTUEOT()
{
SAFE_DELETE(ability);
}
//exhange life with targetchooser
AAExchangeLife::AAExchangeLife(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who)
{
}
int AAExchangeLife::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
Player *player = source->controller();
int oldlife = player->getLife();
int targetOldLife = _target->getLife();
int modifier = oldlife > targetOldLife? oldlife - targetOldLife:targetOldLife - oldlife;
if (_target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
{
int increaser = 0;
MTGCardInstance * card = ((MTGCardInstance*)_target);
int toughMod = 0;
targetOldLife <= card->origtoughness?toughMod = card->origtoughness - targetOldLife: toughMod = targetOldLife - card->origtoughness;
if(oldlife > targetOldLife)
{
increaser = oldlife - targetOldLife;
player->gainOrLoseLife(modifier * -1);
card->addToToughness(increaser+toughMod);
}
else
{
_target->life = oldlife;
card->toughness = oldlife;
player->gainOrLoseLife(modifier);
}
return 1;
}
Player * opponent = (Player*)_target;
if(oldlife > targetOldLife)
{
player->gainOrLoseLife(modifier * -1);
opponent->gainOrLoseLife(modifier);
}
else
{
player->gainOrLoseLife(modifier);
opponent->gainOrLoseLife(modifier * -1);
}
return 1;
}
return 0;
}
const string AAExchangeLife::getMenuText()
{
return "Exchange life";
}
AAExchangeLife * AAExchangeLife::clone() const
{
return NEW AAExchangeLife(*this);
}
//ALoseAbilities
ALoseAbilities::ALoseAbilities(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target) :
MTGAbility(observer, id, source)
{
target = _target;
}
int ALoseAbilities::addToGame()
{
if (storedAbilities.size())
{
DebugTrace("FATAL:storedAbilities shouldn't be already set inALoseAbilitie\n");
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *)target;
ActionLayer * al = game->mLayers->actionLayer();
//Build a list of Lords in game, this is a hack mostly for lands, see below
vector <ALord *> lordsInGame;
for (int i = (int)(al->mObjects.size()) - 1; i > 0; i--) //0 is not a mtgability...hackish
{
if (al->mObjects[i])
{
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
ALord * l = dynamic_cast<ALord*> (currentAction);
if(l)
lordsInGame.push_back(l);
}
}
for (int i = (int)(al->mObjects.size()) - 1; i > 0; i--) //0 is not a mtgability...hackish
{
if (al->mObjects[i])
{
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
ALoseAbilities * la = dynamic_cast<ALoseAbilities*> (currentAction);
if(la)
continue;
if (currentAction->source == _target)
{
bool canRemove = true;
//Hack: we don't remove abilities on the card if they are provided by an external lord ability.
//This is partly to solve the following issues:
// http://code.google.com/p/wagic/issues/detail?id=647
// http://code.google.com/p/wagic/issues/detail?id=700
// But also because "most" abilities granted by lords will actually go away by themselves,
// based on the fact that we usually remove abilities AND change the type of the card
//Also in a general way we don't want to remove the card's abilities if it is provided by a Lord,
//although there is also a problem with us not handling the P/T layer correctly
for (size_t i = 0; i < lordsInGame.size(); ++i)
{
if (lordsInGame[i]->isParentOf(_target, currentAction))
{
canRemove = false;
break;
}
}
if (canRemove)
{
storedAbilities.push_back(currentAction);
al->removeFromGame(currentAction);
}
}
}
}
return MTGAbility::addToGame();
}
int ALoseAbilities::destroy()
{
for (size_t i = 0; i < storedAbilities.size(); ++i)
{
MTGAbility * a = storedAbilities[i];
//OneShot abilities are not supposed to stay in the game for long.
// If we copied one, something wrong probably happened
if (a->oneShot)
{
DebugTrace("ALLABILITIES: Ability should not be one shot");
continue;
}
//Avoid inifinite loop of removing/putting back abilities
if (dynamic_cast<ALoseAbilities*> (a))
{
DebugTrace("ALLABILITIES: loseability won't be put in the loseability list");
continue;
}
a->addToGame();
}
storedAbilities.clear();
return 1;
}
ALoseAbilities * ALoseAbilities::clone() const
{
return NEW ALoseAbilities(*this);
}
//ALoseSubtypes
ALoseSubtypes::ALoseSubtypes(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, int parentType) :
MTGAbility(observer, id, source), parentType(parentType)
{
target = _target;
}
int ALoseSubtypes::addToGame()
{
if (storedSubtypes.size())
{
DebugTrace("FATAL:storedSubtypes shouldn't be already set inALoseSubtypes\n");
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *)target;
for (int i = ((int)_target->types.size())-1; i >= 0; --i)
{
int subtype = _target->types[i];
if (MTGAllCards::isSubtypeOfType(subtype, parentType))
{
storedSubtypes.push_back(subtype);
_target->removeType(subtype);
}
}
return MTGAbility::addToGame();
}
int ALoseSubtypes::destroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
for (size_t i = 0; i < storedSubtypes.size(); ++i)
_target->addType(storedSubtypes[i]);
storedSubtypes.clear();
return 1;
}
ALoseSubtypes * ALoseSubtypes::clone() const
{
return NEW ALoseSubtypes(*this);
}
//APreventDamageTypes
APreventDamageTypes::APreventDamageTypes(GameObserver* observer, int id, MTGCardInstance * source, string to, string from, int type) :
MTGAbility(observer, id, source), to(to), from(from), type(type)
{
re = NULL;
}
int APreventDamageTypes::addToGame()
{
if (re)
{
DebugTrace("FATAL:re shouldn't be already set in APreventDamageTypes\n");
return 0;
}
TargetChooserFactory tcf(game);
TargetChooser *toTc = tcf.createTargetChooser(to, source, this);
if (toTc)
toTc->targetter = NULL;
TargetChooser *fromTc = tcf.createTargetChooser(from, source, this);
if (fromTc)
fromTc->targetter = NULL;
if (type != 1 && type != 2)
{//not adding this creates a memory leak.
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, Damage::DAMAGE_COMBAT);
}
else if (type == 1)
{
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, Damage::DAMAGE_ALL_TYPES);
}
else if (type == 2)
{
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, Damage::DAMAGE_OTHER);
}
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
int APreventDamageTypes::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
APreventDamageTypes * APreventDamageTypes::clone() const
{
APreventDamageTypes * a = NEW APreventDamageTypes(*this);
a->re = NULL;
return a;
}
APreventDamageTypes::~APreventDamageTypes()
{
SAFE_DELETE(re);
}
//APreventDamageTypesUEOT
APreventDamageTypesUEOT::APreventDamageTypesUEOT(GameObserver* observer, int id, MTGCardInstance * source, string to, string from, int type) :
InstantAbility(observer, id, source)
{
ability = NEW APreventDamageTypes(observer, id, source, to, from, type);
}
int APreventDamageTypesUEOT::resolve()
{
APreventDamageTypes * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
int APreventDamageTypesUEOT::destroy()
{
for (size_t i = 0; i < clones.size(); ++i)
{
clones[i]->forceDestroy = 0;
}
clones.clear();
return 1;
}
const string APreventDamageTypesUEOT::getMenuText()
{
return ability->getMenuText();
}
APreventDamageTypesUEOT * APreventDamageTypesUEOT::clone() const
{
APreventDamageTypesUEOT * a = NEW APreventDamageTypesUEOT(*this);
a->ability = this->ability->clone();
return a;
}
APreventDamageTypesUEOT::~APreventDamageTypesUEOT()
{
SAFE_DELETE(ability);
}
//AVanishing creature also fading
// Comprehensive 702.31a:
// Fading is a keyword that represents two abilities.
// “Fading N” means “This permanent comes into play with N fade counters on it” and
// “At the beginning of your upkeep, remove a fade counter from this permanent.
// If you cant, sacrifice the permanent.”
// Comprehensive 702.62a:
// Vanishing is a keyword that represents three abilities.
// "Vanishing N" means "This permanent comes into play with N time counters on it,"
// "At the beginning of your upkeep, if this permanent has a time counter on it, remove a time counter from it," and
// "When the last time counter is removed from this permanent, sacrifice it."
AVanishing::AVanishing(GameObserver* observer, int _id, MTGCardInstance * card, ManaCost *, int, int amount, string counterName) :
MTGAbility(observer, _id, source, target),amount(amount),counterName(counterName)
{
target = card;
source = card;
for (int i = 0; i < amount; i++)
source->counters->addCounter(counterName.c_str(), 0, 0);
}
void AVanishing::Update(float dt)
{
if (newPhase != currentPhase && source->controller() == game->currentPlayer)
{
if (newPhase == MTG_PHASE_UPKEEP)
{
bool hasCounters = (source->counters && source->counters->hasCounter(counterName.c_str(), 0, 0));
if (hasCounters)
{
// sacrifice of card with time counter is handled in removeCounter
source->counters->removeCounter(counterName.c_str(), 0, 0);
} else
{
if (counterName == "fade")
{
MTGCardInstance * beforeCard = source;
source->controller()->game->putInGraveyard(source);
WEvent * e = NEW WEventCardSacrifice(beforeCard,source);
game->receiveEvent(e);
}
}
}
}
MTGAbility::Update(dt);
}
int AVanishing::resolve()
{
return 1;
}
const string AVanishing::getMenuText()
{
if (counterName.find("fade") != string::npos)
{
return "Fading";
}
return "Vanishing";
}
AVanishing * AVanishing::clone() const
{
return NEW AVanishing(*this);
}
AVanishing::~AVanishing()
{
}
//AUpkeep
AUpkeep::AUpkeep(GameObserver* observer, int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int restrictions, int _phase,
int _once,bool Cumulative) :
ActivatedAbility(observer, _id, card, _cost, restrictions), NestedAbility(a), phase(_phase), once(_once),Cumulative(Cumulative)
{
paidThisTurn = 0;
aType = MTGAbility::UPCOST;
if(Cumulative)
{
backupMana = NEW ManaCost();
backupMana->copy(this->getCost());
backupMana->addExtraCosts(this->getCost()->extraCosts);
}
}
int AUpkeep::receiveEvent(WEvent * event)
{
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
{
if (MTG_PHASE_DRAW == pe->to->id && MTG_PHASE_UPKEEP == pe->from->id)
{
if (source->controller() == game->currentPlayer && once < 2 && paidThisTurn < 1)
{
ability->resolve();
}
}
}
return 1;
}
void AUpkeep::Update(float dt)
{
// once: 0 means always go off, 1 means go off only once, 2 means go off only once and already has.
if (newPhase != currentPhase && source->controller() == game->currentPlayer && once < 2)
{
if (newPhase == MTG_PHASE_BEFORE_BEGIN)
{
paidThisTurn = 0;
}
else if(newPhase == MTG_PHASE_UPKEEP && Cumulative )
{
source->counters->addCounter("age",0,0);
Counter * targetCounter = NULL;
currentage = 0;
if (source->counters && source->counters->hasCounter("age", 0, 0))
{
targetCounter = source->counters->hasCounter("age", 0, 0);
currentage = targetCounter->nb - 1;
}
if(currentage)
{
paidThisTurn = 0;
this->getCost()->copy(backupMana);
for(int age = 0;age < currentage;age++)
{
this->getCost()->add(backupMana);
this->getCost()->addExtraCosts(backupMana->extraCosts);
}
}
}
if (newPhase == phase + 1 && once)
once = 2;
}
ActivatedAbility::Update(dt);
}
int AUpkeep::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
if (currentPhase != phase || paidThisTurn > 0 || once >= 2)
return 0;
return ActivatedAbility::isReactingToClick(card, mana);
}
int AUpkeep::resolve()
{
paidThisTurn += 1;
return 1;
}
const string AUpkeep::getMenuText()
{
return "Upkeep";
}
ostream& AUpkeep::toString(ostream& out) const
{
out << "AUpkeep ::: paidThisTurn : " << paidThisTurn << " (";
return ActivatedAbility::toString(out) << ")";
}
AUpkeep * AUpkeep::clone() const
{
AUpkeep * a = NEW AUpkeep(*this);
a->ability = ability->clone();
return a;
}
AUpkeep::~AUpkeep()
{
if(Cumulative)
{
SAFE_DELETE(backupMana);
}
SAFE_DELETE(ability);
}
//A Phase based Action
APhaseAction::APhaseAction(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance *, string sAbility, int, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once) :
MTGAbility(observer, _id, card),sAbility(sAbility), phase(_phase),forcedestroy(forcedestroy),next(next),myturn(myturn),opponentturn(opponentturn),once(once)
{
abilityId = _id;
abilityOwner = card->controller();
psMenuText = "";
AbilityFactory af(game);
ability = af.parseMagicLine(sAbility, abilityId, NULL, NULL);
if(ability)
psMenuText = ability->getMenuText();
else
psMenuText = sAbility.c_str();
delete (ability);
}
void APhaseAction::Update(float dt)
{
if (newPhase != currentPhase)
{
if((myturn && game->currentPlayer == source->controller())||
(opponentturn && game->currentPlayer != source->controller())/*||*/
/*(myturn && opponentturn)*/)
{
if(newPhase == phase && next )
{
MTGCardInstance * _target = NULL;
bool isTargetable = false;
if(target)
{
_target = static_cast<MTGCardInstance *>(target);
isTargetable = (_target && !_target->currentZone && _target != this->source);
}
if(!sAbility.size() || (!target || isTargetable))
{
this->forceDestroy = 1;
return;
}
else
{
while(_target && _target->next)
_target = _target->next;
}
AbilityFactory af(game);
MTGAbility * ability = af.parseMagicLine(sAbility, abilityId, NULL, _target);
MTGAbility * a = ability->clone();
a->target = _target;
a->resolve();
delete (a);
delete (ability);
if(this->oneShot || once)
{
this->forceDestroy = 1;
}
}
else if(newPhase == phase && next == false)
next = true;
}
}
MTGAbility::Update(dt);
}
int APhaseAction::resolve()
{
return 0;
}
const string APhaseAction::getMenuText()
{
if(psMenuText.size())
return psMenuText.c_str();
else
return "Phase Based Action";
}
APhaseAction * APhaseAction::clone() const
{
APhaseAction * a = NEW APhaseAction(*this);
if(forcedestroy == false)
a->forceDestroy = -1;// we want this ability to stay alive until it resolves.
return a;
}
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, card, target)
{
MTGCardInstance * _target = target;
ability = NEW APhaseAction(game, _id, card,_target, sAbility, restrictions, _phase,forcedestroy,next,myturn,opponentturn,once);
}
int APhaseActionGeneric::resolve()
{
APhaseAction * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const string APhaseActionGeneric::getMenuText()
{
return ability->getMenuText();
}
APhaseActionGeneric * APhaseActionGeneric::clone() const
{
APhaseActionGeneric * a = NEW APhaseActionGeneric(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
return a;
}
APhaseActionGeneric::~APhaseActionGeneric()
{
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)
{
target = _target;
Blinked = NULL;
resolved = false;
}
void ABlink::Update(float dt)
{
if (resolved == false)
{
resolved = true;
resolveBlink();
}
if ((blinkueot && currentPhase == MTG_PHASE_ENDOFTURN) || (blinkForSource && !source->isInPlay(game)))
{
if (Blinked == NULL)
MTGAbility::Update(dt);
MTGCardInstance * _target = Blinked;
returnCardIntoPlay(_target);
}
MTGAbility::Update(dt);
}
void ABlink::resolveBlink()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
//going to comment this condiational out, i can't remember if i added this to fix some kind of bug
//which is why i plan on leaving it here. seems to work fine though without it.
//if(blinkhand && !_target->controller()->game->isInZone(_target,_target->controller()->game->hand))
//{
// this->forceDestroy = 1;
// return;
//}
//else if(!blinkhand && !_target->controller()->game->isInZone(_target,_target->controller()->game->battlefield))
//{
// this->forceDestroy = 1;
// return;
//}
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->exile);
if(_target->isToken)
{
//if our target is a token, we're done as soon as its sent to exile.
this->forceDestroy = 1;
return;
}
_target = _target->next;
Blinked = _target;
if(!blinkueot && !blinkForSource)
{
returnCardIntoPlay(_target);
}
}
}
void ABlink::returnCardIntoPlay(MTGCardInstance* _target) {
MTGCardInstance * Blinker = NULL;
if (!blinkhand)
Blinker = _target->controller()->game->putInZone(
_target,
_target->currentZone,
_target->owner->game->battlefield);
if (blinkhand)
{
_target->controller()->game->putInZone(
_target,
_target->currentZone,
_target->owner->game->hand);
return;
}
Spell * spell = NEW Spell(game, Blinker);
spell->source->counters->init();
if (spell->source->hasSubtype(Subtypes::TYPE_AURA) && !blinkhand)
{
TargetChooserFactory tcf(game);
TargetChooser * tc = tcf.createTargetChooser(
spell->source->spellTargetType,
spell->source);
if (!tc->validTargetsExist())
{
spell->source->owner->game->putInExile(spell->source);
SAFE_DELETE(spell);
SAFE_DELETE(tc);
this->forceDestroy = 1;
return;
}
MTGGameZone * inplay = spell->source->owner->game->inPlay;
spell->source->target = NULL;
for (int i = game->getRandomGenerator()->random()%inplay->nb_cards;;i = game->getRandomGenerator()->random()%inplay->nb_cards)
{
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
{
spell->source->target = inplay->cards[i];
spell->getNextCardTarget();
spell->resolve();
SAFE_DELETE(spell);
SAFE_DELETE(tc);
this->forceDestroy = 1;
return;
}
}
}
spell->source->power = spell->source->origpower;
spell->source->toughness = spell->source->origtoughness;
spell->source->X = 0;
if (!spell->source->hasSubtype(Subtypes::TYPE_AURA)) {
spell->resolve();
if (stored)
{
MTGAbility * clonedStored = stored->clone();
clonedStored->target = spell->source;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
}
}
SAFE_DELETE(spell);
SAFE_DELETE(tc);
SAFE_DELETE(stored);
this->forceDestroy = 1;
Blinked = NULL;
}
int ABlink::resolve()
{
return 0;
}
const string ABlink::getMenuText()
{
return "Blink";
}
ABlink * ABlink::clone() const
{
ABlink * a = NEW ABlink(*this);
a->stored = stored ? stored->clone() : NULL;
a->forceDestroy = -1;
return a;
};
ABlink::~ABlink()
{
SAFE_DELETE(stored);
}
ABlinkGeneric::ABlinkGeneric(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot,bool blinkForSource,bool blinkhand,MTGAbility * stored) :
InstantAbility(observer, _id, source, _target)
{
ability = NEW ABlink(observer, _id,card,_target,blinkueot,blinkForSource,blinkhand,stored);
}
int ABlinkGeneric::resolve()
{
ABlink * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const string ABlinkGeneric::getMenuText()
{
return "Blink";
}
ABlinkGeneric * ABlinkGeneric::clone() const
{
ABlinkGeneric * a = NEW ABlinkGeneric(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
return a;
}
ABlinkGeneric::~ABlinkGeneric()
{
SAFE_DELETE(ability);
}
// target becomes blocked by source
AABlock::AABlock(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
}
int AABlock::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
source = (MTGCardInstance*)source;
if (_target && source->canBlock(_target))
{
source->toggleDefenser(_target);
source->getObserver()->isInterrupting = NULL;
}
return 1;
}
AABlock * AABlock::clone() const
{
return NEW AABlock(*this);
}
// target becomes pair of source
PairCard::PairCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
oneShot = true;
forceDestroy = 1;
}
int PairCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
source = (MTGCardInstance*)source;
if (_target && !_target->myPair && source)
{
source->myPair = _target;
_target->myPair = source;
}
return 1;
}
PairCard * PairCard::clone() const
{
return NEW PairCard(*this);
}
//target is dredged
dredgeCard::dredgeCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
oneShot = true;
forceDestroy = 1;
}
int dredgeCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(_target)
{
for(int j = 0; j < _target->data->dredge();j++)
{
_target->controller()->game->putInZone(
_target->controller()->game->library->cards[_target->controller()->game->library->nb_cards - 1],
_target->controller()->game->library, _target->controller()->game->graveyard);
}
_target->controller()->game->putInZone(_target,_target->currentZone,_target->controller()->game->hand);
}
return 1;
}
dredgeCard * dredgeCard::clone() const
{
return NEW dredgeCard(*this);
}
// target becomes a parent of card(source)
AAConnect::AAConnect(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
}
int AAConnect::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
_target->childrenCards.push_back(source);
source->parentCards.push_back(_target);
//weapon
if(source->hasSubtype(Subtypes::TYPE_EQUIPMENT))
{
for (size_t i = 1; i < game->mLayers->actionLayer()->mObjects.size(); i++)
{
MTGAbility * a = ((MTGAbility *) game->mLayers->actionLayer()->mObjects[i]);
AEquip * eq = dynamic_cast<AEquip*> (a);
if (eq && eq->source == source)
{
((AEquip*)a)->unequip();
((AEquip*)a)->equip(_target);
}
}
}
else
{
if(source->target)
source->target = NULL;
//clearing the source target allows us to use target= line
//without creating side effects on any other abilities a card has
//connect has to be the first ability in the cards lines unless you want it to do effects to the targeted card!!!
}
}
return 1;
}
AAConnect * AAConnect::clone() const
{
return NEW AAConnect(*this);
}
AEquip::AEquip(GameObserver* observer, int _id, MTGCardInstance * _source, ManaCost * _cost, int restrictions) :
TargetAbility(observer, _id, _source, NULL, _cost, restrictions)
{
aType = MTGAbility::STANDARD_EQUIP;
isAttach = restrictions != ActivatedAbility::AS_SORCERY;
}
int AEquip::unequip()
{
if (source->target)
{
source->target->equipment -= 1;
source->parentCards.clear();
for (unsigned int w = 0; w < source->target->childrenCards.size(); w++)
{
MTGCardInstance * child = source->target->childrenCards[w];
if (child == source)
source->target->childrenCards.erase(source->target->childrenCards.begin() + w);
}
}
source->target = NULL;
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
if (dynamic_cast<AEquip *> (a) || dynamic_cast<ATeach *> (a) || dynamic_cast<AAConnect *> (a)
|| dynamic_cast<AANewTarget *> (AbilityFactory::getCoreAbility(a))
|| (a->aType == MTGAbility::STANDARD_TOKENCREATOR && a->oneShot))
{
SAFE_DELETE(a);
continue;
}
game->removeObserver(currentAbilities[i]);
}
currentAbilities.clear();
WEvent * e = NEW WEventCardUnattached(source);
game->receiveEvent(e);
return 1;
}
int AEquip::equip(MTGCardInstance * equipped)
{
source->target = equipped;
source->target->equipment += 1;
source->parentCards.push_back(equipped);
source->target->childrenCards.push_back((MTGCardInstance*)source);
AbilityFactory af(game);
af.getAbilities(&currentAbilities, NULL, source);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
if (dynamic_cast<AEquip *> (a)) continue;
if (dynamic_cast<ATeach *> (a)) continue;
if (dynamic_cast<AAConnect *> (a)) continue;
if (dynamic_cast<AANewTarget *> (af.getCoreAbility(a))) continue;
if (a->aType == MTGAbility::STANDARD_TOKENCREATOR && a->oneShot)
{
a->forceDestroy = 1;
continue;
}
if (dynamic_cast<AACopier *> (af.getCoreAbility(a)))
{
a->forceDestroy = 1;
continue;
}
//we generally dont want to pass oneShot tokencreators to the cards
//we equip...
a->addToGame();
}
return 1;
}
int AEquip::resolve()
{
MTGCardInstance * mTarget = tc->getNextCardTarget();
if (!mTarget) return 0;
if (mTarget == source) return 0;
unequip();
equip(mTarget);
return 1;
}
const string AEquip::getMenuText()
{
if (isAttach)
return "Attach";
else
return "Equip";
}
int AEquip::testDestroy()
{
if (source->target && !game->isInPlay(source->target))
unequip();
if (!game->connectRule)
{
if (source->target && TargetAbility::tc && !TargetAbility::tc->canTarget((Targetable *)source->target,true))
unequip();
}
return TargetAbility::testDestroy();
}
int AEquip::destroy()
{
unequip();
return TargetAbility::destroy();
}
AEquip * AEquip::clone() const
{
return NEW AEquip(*this);
}
// casting a card for free, or casting a copy of a card.
AACastCard::AACastCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target,bool _restricted,bool _copied,bool asNormal,string _namedCard,string _name,bool _noEvent,bool putinplay) :
MTGAbility(observer, _id, _source),restricted(_restricted),asCopy(_copied),normal(asNormal),cardNamed(_namedCard),nameThis(_name),noEvent(_noEvent),putinplay(putinplay)
{
target = _target;
andAbility = NULL;
processed = false;
theNamedCard = NULL;
}
void AACastCard::Update(float dt)
{
MTGAbility::Update(dt);
if (processed)
return;
if(cardNamed.size() && !theNamedCard)
{
theNamedCard = makeCard();
}
if(putinplay)
{
MTGCardInstance * toCheck = (MTGCardInstance*)target;
toCheck->target = NULL;
toCheck->playerTarget = NULL;
toCheck->bypassTC = true;
TargetChooserFactory tcf(game);
TargetChooser * atc = tcf.createTargetChooser(toCheck->spellTargetType,toCheck);
if (toCheck->hasType(Subtypes::TYPE_AURA) && !atc->validTargetsExist())
{
processed = true;
this->forceDestroy = 1;
return ;
}
SAFE_DELETE(atc);
}
if (restricted)
{
MTGCardInstance * toCheck = (MTGCardInstance*)target;
if(theNamedCard)
toCheck = theNamedCard;
if (game->currentActionPlayer->game->playRestrictions->canPutIntoZone(toCheck, source->controller()->game->stack) == PlayRestriction::CANT_PLAY)
{
processed = true;
this->forceDestroy = 1;
return ;
}
if(!allowedToCast(toCheck,source->controller()))
{
processed = true;
this->forceDestroy = 1;
return;
}
if(!toCheck->hasType(Subtypes::TYPE_INSTANT) && !(game->getCurrentGamePhase() == MTG_PHASE_FIRSTMAIN || game->getCurrentGamePhase() == MTG_PHASE_SECONDMAIN))
{
processed = true;
this->forceDestroy = 1;
return;
}
}
MTGCardInstance * toCheck = (MTGCardInstance*)target;
if(theNamedCard)
toCheck = theNamedCard;
if (Spell * checkSpell = dynamic_cast<Spell*>(target))
{
toCheck = checkSpell->source;
}
if (!game->targetListIsSet(toCheck))
{
if(game->targetChooser)
game->targetChooser->Owner = source->controller();//sources controller is the caster
return;
}
resolveSpell();
this->forceDestroy = 1;
return;
}
int AACastCard::isReactingToTargetClick(Targetable * card){return 0;}
int AACastCard::reactToTargetClick(Targetable * object)
{
if (MTGCardInstance * cObject = dynamic_cast<MTGCardInstance *>(object))
return reactToClick(cObject);
if (waitingForAnswer)
{
if (tc->toggleTarget(object) == TARGET_OK_FULL)
{
waitingForAnswer = 0;
game->mLayers->actionLayer()->setCurrentWaitingAction(NULL);
return MTGAbility::reactToClick(source);
}
return 1;
}
return 0;
}
MTGCardInstance * AACastCard::makeCard()
{
MTGCardInstance * card = NULL;
MTGCard * cardData = MTGCollection()->getCardByName(cardNamed);
card = NEW MTGCardInstance(cardData, source->controller()->game);
source->controller()->game->temp->addCard(card);
return card;
}
int AACastCard::resolveSpell()
{
if (processed)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if(theNamedCard)
_target = theNamedCard;
if (Spell * checkSpell = dynamic_cast<Spell*>(target))
{
_target = checkSpell->source;
}
if(asCopy)
{
MTGCard * cardToCopy = MTGCollection()->getCardById(_target->getId());
MTGCardInstance * myDummy = NULL;
myDummy = NEW MTGCardInstance(cardToCopy, source->controller()->game);
source->controller()->game->garbage->addCard(myDummy);
_target = myDummy;
_target->isToken = 1;
_target->changeController(source->controller(),true);
}
if (_target)
{
if (_target->isLand())
{
MTGCardInstance * copy = _target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->temp,noEvent);
copy->changeController(source->controller(),true);
Spell * spell = NEW Spell(game, 0,copy,NULL,NULL, 1);
spell->resolve();
delete spell;
}
else
{
Spell * spell = NULL;
MTGCardInstance * copy = NULL;
if (normal ||(!_target->hasType(Subtypes::TYPE_INSTANT) && !_target->hasType(Subtypes::TYPE_SORCERY)))
{
if (putinplay && (_target->hasType(Subtypes::TYPE_ARTIFACT)||_target->hasType(Subtypes::TYPE_CREATURE)||_target->hasType(Subtypes::TYPE_ENCHANTMENT)||_target->hasType(Subtypes::TYPE_PLANESWALKER)))
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->battlefield,noEvent);
else
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->stack,noEvent);
copy->changeController(source->controller(),true);
}
else
{
if (putinplay && (_target->hasType(Subtypes::TYPE_ARTIFACT)||_target->hasType(Subtypes::TYPE_CREATURE)||_target->hasType(Subtypes::TYPE_ENCHANTMENT)||_target->hasType(Subtypes::TYPE_PLANESWALKER)))
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->battlefield,noEvent);
else
copy =_target->controller()->game->putInZone(_target, _target->currentZone, _target->controller()->game->stack,noEvent);
copy->changeController(source->controller(),true);
}
if (game->targetChooser)
{
game->targetChooser->Owner = source->controller();
spell = game->mLayers->stackLayer()->addSpell(copy, game->targetChooser, NULL, 1, 0);
game->targetChooser = NULL;
}
else
{
spell = game->mLayers->stackLayer()->addSpell(copy, NULL, NULL, 1, 0);
}
if (copy->has(Constants::STORM))
{
int storm = _target->controller()->game->stack->seenThisTurn("*", Constants::CAST_ALL) + source->controller()->opponent()->game->stack->seenThisTurn("*", Constants::CAST_ALL);
for (int i = storm; i > 1; i--)
{
spell = game->mLayers->stackLayer()->addSpell(copy, NULL, 0, 1, 1);
}
}
if (!copy->has(Constants::STORM))
{
copy->X = _target->X;
copy->castX = copy->X;
}
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = copy;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
}
this->forceDestroy = true;
processed = true;
return 1;
}
return 0;
}
const string AACastCard::getMenuText()
{
if(nameThis.size())
return nameThis.c_str();
if(putinplay)
return "Put Into Play";
return "Cast Card";
}
AACastCard * AACastCard::clone() const
{
AACastCard * a = NEW AACastCard(*this);
if(tc)
a->tc = tc->clone();
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AACastCard::~AACastCard()
{
SAFE_DELETE(tc);
SAFE_DELETE(andAbility);
}
//Tutorial Messaging
ATutorialMessage::ATutorialMessage(GameObserver* observer, MTGCardInstance * source, string message, int limit)
: MTGAbility(observer, 0, source), IconButtonsController(observer->getInput(), 0, 0), mLimit(limit)
{
mBgTex = NULL;
mElapsed = 0;
mIsImage = false;
for (int i = 0; i < 9; i++)
mBg[i] = NULL;
if(game->getResourceManager())
{
string gfx = game->getResourceManager()->graphicsFile(message);
if (fileExists(gfx.c_str()))
{
mIsImage = true;
mMessage = message;
}
else
{
mMessage = _(message); //translate directly here, remove this and translate at rendering time if it bites us
ReplaceString(mMessage, "\\n", "\n");
}
}
if (mIsImage)
{
mX = SCREEN_WIDTH_F / 2;
mY = SCREEN_HEIGHT_F / 2;
}
else
{
mX = 0;
mY = -SCREEN_HEIGHT_F - 0.1f; //Offscreen
}
mDontShow = mUserCloseRequest = (mLimit > 0) && (alreadyShown() >= mLimit);
if(mDontShow)
forceDestroy = 1;
}
string ATutorialMessage::getOptionName()
{
std::stringstream out;
out << "tuto_";
out << hash_djb2(mMessage.c_str());
return out.str();
}
int ATutorialMessage::alreadyShown()
{
return options[getOptionName()].number;
}
bool ATutorialMessage::CheckUserInput(JButton key)
{
if (mUserCloseRequest) return false;
if(key == JGE_BTN_SEC || key == JGE_BTN_OK)
{
ButtonPressed(0, 1);
return true;
}
//Required for Mouse/touch input
IconButtonsController::CheckUserInput(key);
return true; //this ability is modal, so it catches all key events until it gets closed
}
void ATutorialMessage::Update(float dt)
{
if (!game->mLayers->stackLayer()->getCurrentTutorial() && !mDontShow)
game->mLayers->stackLayer()->setCurrentTutorial(this);
if (game->mLayers->stackLayer()->getCurrentTutorial() != this)
return;
if (mUserCloseRequest && mY < -SCREEN_HEIGHT)
mDontShow = true;
if (mDontShow)
{
game->mLayers->stackLayer()->setCurrentTutorial(0);
forceDestroy = 1;
return;
}
mElapsed += dt;
if(!mUserCloseRequest)
IconButtonsController::Update(dt);
if (mIsImage)
return;
//Below this only affects "text" mode
if (!mUserCloseRequest && mY < 0)
{
mY = -SCREEN_HEIGHT + (SCREEN_HEIGHT * mElapsed / 0.75f); //Todo: more physical drop-in.
if (mY >= 0)
mY = 0;
}
else if (mUserCloseRequest && mY > -SCREEN_HEIGHT)
{
mY = -(SCREEN_HEIGHT * mElapsed / 0.75f);
}
}
void ATutorialMessage::ButtonPressed(int, int)
{
//TODO : cancel ALL tips/tutorials for JGE_BTN_SEC?
if (mLimit)
{
string optionName = getOptionName();
options[optionName].number = options[optionName].number + 1;
options.save(); //TODO: if we experience I/O slowness in tutorials, move this save at the end of a turn, or at the end of the game.
}
mElapsed = 0;
mUserCloseRequest = true;
}
void ATutorialMessage::Render()
{
if (mDontShow)
return;
if (mY < -SCREEN_HEIGHT)
return;
if (!mBgTex)
{
if (mIsImage)
{
mBgTex = game->getResourceManager()->RetrieveTexture(mMessage, RETRIEVE_LOCK);
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, (float) mBgTex->mWidth, (float) mBgTex->mHeight);
mBg[0]->SetHotSpot(mBg[0]->mWidth / 2, mBg[0]->mHeight / 2);
//Continue Button
JQuadPtr quad = game->getResourceManager()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), 0, mBg[0]->mHeight / 2, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
}
if (options[Options::SFXVOLUME].number > 0)
{
game->getResourceManager()->PlaySample("tutorial.wav");
}
}
else
{
mBgTex = game->getResourceManager()->RetrieveTexture("taskboard.png", RETRIEVE_LOCK);
float unitH = static_cast<float> (mBgTex->mHeight / 4);
float unitW = static_cast<float> (mBgTex->mWidth / 4);
if (unitH == 0 || unitW == 0) return;
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, unitW, unitH);
mBg[1] = NEW JQuad(mBgTex, unitW, 0, unitW * 2, unitH);
mBg[2] = NEW JQuad(mBgTex, unitW * 3, 0, unitW, unitH);
mBg[3] = NEW JQuad(mBgTex, 0, unitH, unitW, unitH * 2);
mBg[4] = NEW JQuad(mBgTex, unitW, unitH, unitW * 2, unitH * 2);
mBg[5] = NEW JQuad(mBgTex, unitW * 3, unitH, unitW, unitH * 2);
mBg[6] = NEW JQuad(mBgTex, 0, unitH * 3, unitW, unitH);
mBg[7] = NEW JQuad(mBgTex, unitW, unitH * 3, unitW * 2, unitH);
mBg[8] = NEW JQuad(mBgTex, unitW * 3, unitH * 3, unitW, unitH);
}
//Continue Button
JQuadPtr quad = game->getResourceManager()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), SCREEN_WIDTH_F / 2, SCREEN_HEIGHT_F - 60, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
mSH = 64 / unitH;
mSW = 64 / unitW;
if (options[Options::SFXVOLUME].number > 0)
{
game->getResourceManager()->PlaySample("chain.wav");
}
}
}
JRenderer * r = JRenderer::GetInstance();
//Render background board
if (mBgTex)
{
if (mIsImage)
{
int alpha = mUserCloseRequest ? MAX(0, 255 - (int)(mElapsed * 500)) : MIN(255, (int)(mElapsed * 500)) ;
if (mUserCloseRequest && alpha == 0)
mDontShow = true;
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(alpha / 2,0,0,0));
mBg[0]->SetColor(ARGB(alpha,255,255,255));
r->RenderQuad(mBg[0], SCREEN_WIDTH_F /2 , SCREEN_HEIGHT_F / 2 , 0);
IconButtonsController::SetColor(ARGB(alpha,255,255,255));
}
else
{
//Setup fonts.
WFont * f2 = game->getResourceManager()->GetWFont(Fonts::MAGIC_FONT);
f2->SetColor(ARGB(255, 205, 237, 240));
r->FillRect(0, mY, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->RenderQuad(mBg[0], 0, mY, 0, mSW, mSH); //TL
r->RenderQuad(mBg[2], SCREEN_WIDTH - 64, mY, 0, mSW, mSH); //TR
r->RenderQuad(mBg[6], 0, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BL
r->RenderQuad(mBg[8], SCREEN_WIDTH - 64, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BR
//Stretch the sides
float stretchV = (144.0f / 128.0f) * mSH;
float stretchH = (176.0f / 128.0f) * mSW;
r->RenderQuad(mBg[3], 0, mY + 64, 0, mSW, stretchV); //L
r->RenderQuad(mBg[5], SCREEN_WIDTH - 64, mY + 64, 0, mSW, stretchV); //R
r->RenderQuad(mBg[1], 64, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[1], 240, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[7], 64, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[7], 240, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[4], 64, mY + 64, 0, stretchH, stretchV); //Center1
r->RenderQuad(mBg[4], 240, mY + 64, 0, stretchH, stretchV); //Center2
}
}
else
{
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->FillRect(10, 10 + mY, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 10, ARGB(128,0,0,0));
}
if (!mBgTex || !mIsImage)
{
float posX = 40, posY = mY + 20;
string title = _("Help");
WFont * f = game->getResourceManager()->GetWFont(Fonts::MAGIC_FONT);
WFont * f3 = game->getResourceManager()->GetWFont(Fonts::MENU_FONT); //OPTION_FONT
f->SetColor(ARGB(255, 55, 46, 34));
f3->SetColor(ARGB(255, 219, 206, 151));
f3->DrawString(title.c_str(), static_cast<float> ((SCREEN_WIDTH - 20) / 2 - title.length() * 4), posY);
posY += 30;
f->DrawString(_(mMessage).c_str(), posX, posY);
f->SetScale(1);
}
IconButtonsController::Render();
}
ATutorialMessage * ATutorialMessage::clone() const
{
ATutorialMessage * copy = NEW ATutorialMessage(*this);
copy->mUserCloseRequest = (copy->alreadyShown() > 0);
return copy;
}
ATutorialMessage::~ATutorialMessage()
{
if (mBgTex)
{
game->getResourceManager()->Release(mBgTex);
for (int i = 0; i < 9; i++)
SAFE_DELETE(mBg[i]);
}
}
// utility functions
// Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities
void PopulateAbilityIndexVector(list<int>& abilities, const string& abilityStringList, char delimiter)
{
vector<string> abilitiesList = split(abilityStringList, delimiter);
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
{
int abilityIndex = Constants::GetBasicAbilityIndex(*iter);
if (abilityIndex != -1)
abilities.push_back(abilityIndex);
}
}
void PopulateColorIndexVector(list<int>& colors, const string& colorStringList, char delimiter)
{
vector<string> abilitiesList = split(colorStringList, delimiter);
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
{
for (int colorIndex = Constants::MTG_COLOR_ARTIFACT; colorIndex < Constants::NB_Colors; ++colorIndex)
{
// if the text is not a basic ability but contains a valid color add it to the color vector
if ((Constants::GetBasicAbilityIndex(*iter) == -1)
&& ((*iter).find(Constants::MTGColorStrings[colorIndex]) != string::npos))
colors.push_back(colorIndex);
}
}
}
void PopulateSubtypesIndexVector(list<int>& types, const string& subTypesStringList, char delimiter)
{
vector<string> subTypesList = split(subTypesStringList, delimiter);
for (vector<string>::iterator it = subTypesList.begin(); it != subTypesList.end(); ++it)
{
string subtype = *it;
size_t id = MTGAllCards::findType(subtype);
if (id != string::npos)
types.push_back(id);
}
}