62dd54c886
208.4. Effects that set a creature’s power and/or toughness to specific values may refer to that creature’s “base power,” “base toughness,” or “base power and toughness.” Other continuous effects may further modify the creature’s power and toughness. See rule 613, “Interaction of Continuous Effects.”
6004 lines
174 KiB
C++
6004 lines
174 KiB
C++
#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) :
|
||
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),nbcardsStr(nbcardsStr)
|
||
{
|
||
|
||
}
|
||
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)
|
||
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
const string AADepleter::getMenuText()
|
||
{
|
||
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)
|
||
{
|
||
source->copy(_target);
|
||
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());
|
||
}
|
||
|
||
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(¤tAbilities, 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(¤tAbilities, 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()));
|
||
|
||
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(_target->isToken)
|
||
{
|
||
spell->source->power = _target->origpower;
|
||
spell->source->toughness = _target->origtoughness;
|
||
spell->source->life = _target->origtoughness;
|
||
}
|
||
list<int>::iterator it;
|
||
for (it = awith.begin(); it != awith.end(); it++)
|
||
{
|
||
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);
|
||
}
|
||
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);
|
||
}
|
||
|
||
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 )
|
||
{//setting p/t only overrides base p/t as of M15 changes
|
||
WParsedInt * val = NEW WParsedInt(newpower,NULL, source);
|
||
oldpowerbonus = _target->power - _target->origpower;//keep bonus if any
|
||
_target->setPower(oldpowerbonus + val->getValue());
|
||
_target->power += reapplyCountersBonus(_target,false,true);
|
||
delete val;
|
||
}
|
||
if(newtoughnessfound )
|
||
{//setting p/t only overrides base p/t as of M15 changes
|
||
WParsedInt * val = NEW WParsedInt(newtoughness,NULL, source);
|
||
oldtoughnessbonus = _target->toughness - _target->origtoughness;// keep bonus if any
|
||
_target->setToughness(oldtoughnessbonus + val->getValue());
|
||
_target->addToToughness(reapplyCountersBonus(_target,true,false));
|
||
_target->life = _target->toughness;
|
||
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);
|
||
}
|
||
|
||
for (it = oldcolors.begin(); it != oldcolors.end(); it++)
|
||
{
|
||
_target->setColor(*it);
|
||
}
|
||
|
||
if(newpowerfound )
|
||
{
|
||
_target->setPower(oldpowerbonus + _target->origpower);
|
||
}
|
||
if(newtoughnessfound )
|
||
{
|
||
_target->setToughness(oldtoughnessbonus + _target->origtoughness);
|
||
}
|
||
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 can’t, 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();
|
||
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(¤tAbilities, 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) :
|
||
MTGAbility(observer, _id, _source),restricted(_restricted),asCopy(_copied),normal(asNormal),cardNamed(_namedCard),nameThis(_name),noEvent(_noEvent)
|
||
{
|
||
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 (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)))
|
||
{
|
||
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->stack,noEvent);
|
||
copy->changeController(source->controller(),true);
|
||
}
|
||
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();
|
||
return "Cast For Free";
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|