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