removing the types, removing all types, or adding a type...not ever doing them all in a single transforms.
3599 lines
98 KiB
C++
3599 lines
98 KiB
C++
#include "PrecompiledHeader.h"
|
|
#include "AllAbilities.h"
|
|
|
|
//Activated Abilities
|
|
|
|
//Generic Activated Abilities
|
|
GenericActivatedAbility::GenericActivatedAbility(string newName,int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost,
|
|
string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest) :
|
|
ActivatedAbility(_id, card, _cost, restrictions,limit,sideEffects,usesBeforeSideEffects), NestedAbility(a), activeZone(dest),newName(newName)
|
|
{
|
|
counters = 0;
|
|
target = ability->target;
|
|
}
|
|
|
|
int GenericActivatedAbility::resolve()
|
|
{
|
|
ManaCost * diff = abilityCost->Diff(cost);
|
|
source->X = diff->hasX();
|
|
SAFE_DELETE(diff);
|
|
//SAFE_DELETE(abilityCost); this line has been reported as a bug. removing it doesn't seem to break anything, although I didn't get any error in the test suite by leaving it either, so... leaving it for now as a comment, in case.
|
|
ability->target = target; //may have been updated...
|
|
if (ability)
|
|
return ability->resolve();
|
|
return 0;
|
|
}
|
|
|
|
const char * 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->cost = NEW ManaCost();
|
|
a->cost->copy(cost);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
GenericActivatedAbility::~GenericActivatedAbility()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
//AA Alter Poison
|
|
AAAlterPoison::AAAlterPoison(int _id, MTGCardInstance * _source, Targetable * _target, int poison, ManaCost * _cost,
|
|
int who) :
|
|
ActivatedAbilityTP(_id, _source, _target, _cost, who), poison(poison)
|
|
{
|
|
}
|
|
|
|
int AAAlterPoison::resolve()
|
|
{
|
|
Damageable * _target = (Damageable *) getTarget();
|
|
if (_target)
|
|
{
|
|
_target->poisonCount += poison;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AAAlterPoison::getMenuText()
|
|
{
|
|
return "Poison";
|
|
}
|
|
|
|
AAAlterPoison * AAAlterPoison::clone() const
|
|
{
|
|
AAAlterPoison * a = NEW AAAlterPoison(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AAAlterPoison::~AAAlterPoison()
|
|
{
|
|
}
|
|
|
|
//Damage Prevent
|
|
AADamagePrevent::AADamagePrevent(int _id, MTGCardInstance * _source, Targetable * _target, int preventing, ManaCost * _cost,
|
|
int who) :
|
|
ActivatedAbilityTP(_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 char * AADamagePrevent::getMenuText()
|
|
{
|
|
return "Prevent Damage";
|
|
}
|
|
|
|
AADamagePrevent * AADamagePrevent::clone() const
|
|
{
|
|
AADamagePrevent * a = NEW AADamagePrevent(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AADamagePrevent::~AADamagePrevent()
|
|
{
|
|
}
|
|
|
|
//AADamager
|
|
AADamager::AADamager(int _id, MTGCardInstance * _source, Targetable * _target, string d, ManaCost * _cost,
|
|
int who) :
|
|
ActivatedAbilityTP(_id, _source, _target, _cost, who), d(d)
|
|
{
|
|
aType = MTGAbility::DAMAGER;
|
|
}
|
|
|
|
int AADamager::resolve()
|
|
{
|
|
Damageable * _target = (Damageable *) getTarget();
|
|
if (_target)
|
|
{
|
|
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
|
|
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 char * AADamager::getMenuText()
|
|
{
|
|
return "Damage";
|
|
}
|
|
|
|
AADamager * AADamager::clone() const
|
|
{
|
|
AADamager * a = NEW AADamager(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
|
|
//AADepleter
|
|
AADepleter::AADepleter(int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost, int who) :
|
|
ActivatedAbilityTP(_id, card, _target, _cost, who),nbcardsStr(nbcardsStr)
|
|
{
|
|
|
|
}
|
|
int AADepleter::resolve()
|
|
{
|
|
|
|
Targetable * _target = getTarget();
|
|
Player * player;
|
|
if (_target)
|
|
{
|
|
WParsedInt numCards(nbcardsStr, NULL, source);
|
|
if (_target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
player = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
player = (Player *) _target;
|
|
}
|
|
MTGLibrary * library = player->game->library;
|
|
for (int i = 0; i < numCards.getValue(); i++)
|
|
{
|
|
if (library->nb_cards)
|
|
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * AADepleter::getMenuText()
|
|
{
|
|
return "Deplete";
|
|
}
|
|
|
|
AADepleter * AADepleter::clone() const
|
|
{
|
|
AADepleter * a = NEW AADepleter(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//AACopier
|
|
AACopier::AACopier(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
|
|
ActivatedAbility(_id, _source, _cost, 0)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int AACopier::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
source->copy(_target);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AACopier::getMenuText()
|
|
{
|
|
return "Copy";
|
|
}
|
|
|
|
AACopier * AACopier::clone() const
|
|
{
|
|
AACopier * a = NEW AACopier(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//phaser
|
|
AAPhaseOut::AAPhaseOut(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
|
|
ActivatedAbility(_id, _source, _cost, 0)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int AAPhaseOut::resolve()
|
|
{GameObserver * g = g->GetInstance();
|
|
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 char * AAPhaseOut::getMenuText()
|
|
{
|
|
return "Phase Out";
|
|
}
|
|
|
|
AAPhaseOut * AAPhaseOut::clone() const
|
|
{
|
|
AAPhaseOut * a = NEW AAPhaseOut(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//Counters
|
|
AACounter::AACounter(int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness,
|
|
int nb,int maxNb, ManaCost * cost) :
|
|
ActivatedAbility(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;
|
|
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);
|
|
}
|
|
}
|
|
//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 char* 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
|
|
{
|
|
AACounter * a = NEW AACounter(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//Counters
|
|
AARemoveAllCounter::AARemoveAllCounter(int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness,
|
|
int nb,bool all, ManaCost * cost) :
|
|
ActivatedAbility(id, source, cost, 0), nb(nb), power(power), toughness(toughness), name(_name),all(all)
|
|
{
|
|
this->target = target;
|
|
menu = "";
|
|
}
|
|
|
|
int AARemoveAllCounter::resolve()
|
|
{
|
|
if (target)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (nb > 0)
|
|
{
|
|
for (int i = 0; i < nb; i++)
|
|
{
|
|
while (_target->next)
|
|
_target = _target->next;
|
|
_target->counters->removeCounter(name.c_str(), power, toughness);
|
|
}
|
|
}
|
|
return nb;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char* 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);
|
|
}
|
|
|
|
sprintf(menuText, "%s", menu.c_str());
|
|
return menuText;
|
|
}
|
|
|
|
AARemoveAllCounter * AARemoveAllCounter::clone() const
|
|
{
|
|
AARemoveAllCounter * a = NEW AARemoveAllCounter(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//Reset Damage on creatures
|
|
AAResetDamage::AAResetDamage(int id, MTGCardInstance * source, MTGCardInstance * _target, ManaCost * cost):
|
|
ActivatedAbility(id, source, cost, 0)
|
|
{
|
|
this->target = _target;
|
|
}
|
|
int AAResetDamage::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
_target->life = _target->toughness;
|
|
return 1;
|
|
}
|
|
|
|
const char* AAResetDamage::getMenuText()
|
|
{
|
|
return "Reset Damages";
|
|
}
|
|
|
|
AAResetDamage * AAResetDamage::clone() const
|
|
{
|
|
AAResetDamage * a = NEW AAResetDamage(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
|
|
// Fizzler
|
|
AAFizzler::AAFizzler(int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost) :
|
|
ActivatedAbility(_id, card, _cost, 0)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int AAFizzler::resolve()
|
|
{
|
|
Spell * _target = (Spell *) target;
|
|
if(!target && !_target)
|
|
{
|
|
//if we hit this condiational its because Ai was targetting.
|
|
Interruptible * targetCard = game->mLayers->stackLayer()->getAt(game->mLayers->stackLayer()->getActionElementFromCard(source->target));
|
|
if (source->target && source->target->has(Constants::NOFIZZLE))
|
|
return 0;
|
|
_target = (Spell *) targetCard;
|
|
game->mLayers->stackLayer()->Fizzle(_target);
|
|
return 1;
|
|
}
|
|
if (target && _target->source->has(Constants::NOFIZZLE))
|
|
return 0;
|
|
game->mLayers->stackLayer()->Fizzle(_target);
|
|
return 1;
|
|
}
|
|
|
|
const char * AAFizzler::getMenuText()
|
|
{
|
|
return "Fizzle";
|
|
}
|
|
|
|
AAFizzler* AAFizzler::clone() const
|
|
{
|
|
AAFizzler * a = NEW AAFizzler(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
// BanishCard implementations
|
|
|
|
AABanishCard::AABanishCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType) :
|
|
ActivatedAbility(_id, _source, NULL), banishmentType(_banishmentType)
|
|
{
|
|
if (_target)
|
|
target = _target;
|
|
}
|
|
|
|
const char * AABanishCard::getMenuText()
|
|
{
|
|
return "Send to graveyard";
|
|
}
|
|
|
|
int AABanishCard::resolve()
|
|
{
|
|
DebugTrace("This is not implemented!");
|
|
return 0;
|
|
}
|
|
|
|
AABanishCard * AABanishCard::clone() const
|
|
{
|
|
AABanishCard * a = NEW AABanishCard(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Bury
|
|
|
|
AABuryCard::AABuryCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType) :
|
|
AABanishCard(_id, _source, _target, AABanishCard::BURY)
|
|
{
|
|
}
|
|
|
|
int AABuryCard::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
return _target->bury();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AABuryCard::getMenuText()
|
|
{
|
|
return "Bury";
|
|
}
|
|
|
|
AABuryCard * AABuryCard::clone() const
|
|
{
|
|
AABuryCard * a = NEW AABuryCard(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Destroy
|
|
|
|
AADestroyCard::AADestroyCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType) :
|
|
AABanishCard(_id, _source, _target, AABanishCard::DESTROY)
|
|
{
|
|
}
|
|
|
|
int AADestroyCard::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
return _target->destroy();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AADestroyCard::getMenuText()
|
|
{
|
|
return "Destroy";
|
|
}
|
|
|
|
AADestroyCard * AADestroyCard::clone() const
|
|
{
|
|
AADestroyCard * a = NEW AADestroyCard(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Sacrifice
|
|
AASacrificeCard::AASacrificeCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType) :
|
|
AABanishCard(_id, _source, _target, AABanishCard::SACRIFICE)
|
|
{
|
|
}
|
|
|
|
int AASacrificeCard::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
Player * p = _target->controller();
|
|
WEvent * e = NEW WEventCardSacrifice(_target);
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
game->receiveEvent(e);
|
|
p->game->putInGraveyard(_target);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AASacrificeCard::getMenuText()
|
|
{
|
|
return "Sacrifice";
|
|
}
|
|
|
|
AASacrificeCard * AASacrificeCard::clone() const
|
|
{
|
|
AASacrificeCard * a = NEW AASacrificeCard(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Discard
|
|
|
|
AADiscardCard::AADiscardCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType) :
|
|
AABanishCard(_id, _source, _target, AABanishCard::DISCARD)
|
|
{
|
|
}
|
|
|
|
int AADiscardCard::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
Player * p = _target->controller();
|
|
WEvent * e = NEW WEventCardDiscard(_target);
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
game->receiveEvent(e);
|
|
p->game->putInGraveyard(_target);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AADiscardCard::getMenuText()
|
|
{
|
|
return "Discard";
|
|
}
|
|
|
|
AADiscardCard * AADiscardCard::clone() const
|
|
{
|
|
AADiscardCard * a = NEW AADiscardCard(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AADrawer::AADrawer(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, string nbcardsStr,
|
|
int who) :
|
|
ActivatedAbilityTP(_id, card, _target, _cost, who), nbcardsStr(nbcardsStr)
|
|
{
|
|
aType = MTGAbility::STANDARD_DRAW;
|
|
}
|
|
|
|
int AADrawer::resolve()
|
|
{
|
|
Targetable * _target = getTarget();
|
|
Player * player;
|
|
if (_target)
|
|
{
|
|
WParsedInt numCards(nbcardsStr, NULL, source);
|
|
if (_target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
player = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
player = (Player *) _target;
|
|
}
|
|
game->mLayers->stackLayer()->addDraw(player, numCards.getValue());
|
|
game->mLayers->stackLayer()->resolve();
|
|
GameObserver *g = GameObserver::GetInstance();
|
|
for(int i = numCards.getValue(); i > 0;i--)
|
|
{
|
|
WEvent * e = NEW WEventcardDraw(player, 1);
|
|
g->receiveEvent(e);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int AADrawer::getNumCards()
|
|
{
|
|
WParsedInt numCards(nbcardsStr, NULL, source);
|
|
return numCards.getValue();
|
|
}
|
|
|
|
const char * AADrawer::getMenuText()
|
|
{
|
|
return "Draw";
|
|
}
|
|
|
|
AADrawer * AADrawer::clone() const
|
|
{
|
|
AADrawer * a = NEW AADrawer(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// AAFrozen: Prevent a card from untapping during next untap phase
|
|
AAFrozen::AAFrozen(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
|
ActivatedAbility(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 char * AAFrozen::getMenuText()
|
|
{
|
|
return "Freeze";
|
|
}
|
|
|
|
AAFrozen * AAFrozen::clone() const
|
|
{
|
|
AAFrozen * a = NEW AAFrozen(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// chose a new target for an aura or enchantment and equip it note: VERY basic right now.
|
|
AANewTarget::AANewTarget(int id, MTGCardInstance * card, MTGCardInstance * _target,bool retarget, ManaCost * _cost) :
|
|
ActivatedAbility(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(refreshed);
|
|
if(reUp->source->hasSubtype(Subtypes::TYPE_AURA))
|
|
{
|
|
reUp->source->target = source;
|
|
reUp->resolve();
|
|
}
|
|
if(_target->hasSubtype(Subtypes::TYPE_EQUIPMENT))
|
|
{
|
|
reUp->resolve();
|
|
GameObserver * g = g->GetInstance();
|
|
for (int i = 1; i < g->mLayers->actionLayer()->mCount; i++)
|
|
{
|
|
MTGAbility * a = ((MTGAbility *) g->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 char * AANewTarget::getMenuText()
|
|
{
|
|
return "New Target";
|
|
}
|
|
|
|
AANewTarget * AANewTarget::clone() const
|
|
{
|
|
AANewTarget * a = NEW AANewTarget(*this);
|
|
a->isClone = 1;
|
|
a->oneShot = 1;
|
|
return a;
|
|
}
|
|
// morph a card
|
|
AAMorph::AAMorph(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
|
ActivatedAbility(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;
|
|
_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();
|
|
GameObserver * g = g->GetInstance();
|
|
g->removeObserver(a);
|
|
}
|
|
if (a)
|
|
{
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
}
|
|
}
|
|
}
|
|
currentAbilities.clear();
|
|
testDestroy();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int AAMorph::testDestroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if(target)
|
|
{
|
|
if(_target->turningOver && !_target->isMorphed && !_target->morphed)
|
|
{
|
|
GameObserver * g = g->GetInstance();
|
|
g->removeObserver(this);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AAMorph::getMenuText()
|
|
{
|
|
return "Morph";
|
|
}
|
|
|
|
AAMorph * AAMorph::clone() const
|
|
{
|
|
AAMorph * a = NEW AAMorph(*this);
|
|
a->isClone = 1;
|
|
a->forceDestroy = 1;
|
|
return a;
|
|
}
|
|
// AADYNAMIC: dynamic ability builder
|
|
AADynamic::AADynamic(int id, MTGCardInstance * card, Damageable * _target,int type,int effect,int who,int amountsource,MTGAbility * storedAbility, ManaCost * _cost) :
|
|
ActivatedAbility(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;
|
|
}
|
|
|
|
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);
|
|
_target = _target;
|
|
break;
|
|
case DYNAMIC_ABILITY_WHO_TARGETCONTROLLER:
|
|
_target = _target;
|
|
secondaryTarget = ((MTGCardInstance *) _target)->controller();
|
|
break;
|
|
case DYNAMIC_ABILITY_WHO_TARGETOPPONENT:
|
|
_target = _target;
|
|
secondaryTarget = ((MTGCardInstance *) _target)->controller()->opponent();
|
|
break;
|
|
case DYNAMIC_ABILITY_WHO_TOSOURCE:
|
|
tosrc = true;
|
|
break;
|
|
case DYNAMIC_ABILITY_WHO_SOURCECONTROLLER:
|
|
_target = _target;
|
|
secondaryTarget = ((MTGCardInstance *) OriginalSrc)->controller();
|
|
break;
|
|
case DYNAMIC_ABILITY_WHO_SOURCEOPPONENT:
|
|
secondaryTarget = OriginalSrc->controller()->opponent();
|
|
break;
|
|
default:
|
|
_target = _target;
|
|
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 (_target->typeAsTarget() == TARGET_CARD && ((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 (_target->typeAsTarget() == TARGET_CARD && ((MTGCardInstance *)_target)->next)
|
|
_target = ((MTGCardInstance *)_target)->next;
|
|
if(sourceamount < 0)
|
|
sourceamount = 0;
|
|
if(targetamount < 0)
|
|
targetamount = 0;
|
|
//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
|
|
if(storedAbility)
|
|
activateStored();
|
|
if(tosrc == false)
|
|
{
|
|
game->mLayers->stackLayer()->addDamage((MTGCardInstance *)source, _target, sourceamount);
|
|
game->mLayers->stackLayer()->resolve();
|
|
}
|
|
else
|
|
{
|
|
game->mLayers->stackLayer()->addDamage((MTGCardInstance *)source, OriginalSrc, sourceamount);
|
|
game->mLayers->stackLayer()->resolve();
|
|
}
|
|
if(eachother )
|
|
{
|
|
game->mLayers->stackLayer()->addDamage((MTGCardInstance *)_target, source, targetamount);
|
|
game->mLayers->stackLayer()->resolve();
|
|
}
|
|
return 1;
|
|
break;
|
|
case DYNAMIC_ABILITY_EFFECT_DRAW://draw cards
|
|
if(storedAbility)
|
|
activateStored();
|
|
game->mLayers->stackLayer()->addDraw((Player *)_target,sourceamount);
|
|
game->mLayers->stackLayer()->resolve();
|
|
return 1;
|
|
break;
|
|
case DYNAMIC_ABILITY_EFFECT_LIFEGAIN://gain life
|
|
if(storedAbility)
|
|
activateStored();
|
|
game->mLayers->stackLayer()->addLife(_target,sourceamount);
|
|
game->mLayers->stackLayer()->resolve();
|
|
return 1;
|
|
break;
|
|
case DYNAMIC_ABILITY_EFFECT_PUMPPOWER://pump power
|
|
{
|
|
if(storedAbility)
|
|
activateStored();
|
|
if(tosrc == false)
|
|
{
|
|
PTInstant * a = NEW PTInstant(this->GetId(), source, (MTGCardInstance*)_target,NEW WParsedPT(sourceamount,0));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,(MTGCardInstance*)_target, a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
PTInstant * a = NEW PTInstant(this->GetId(), source, OriginalSrc,NEW WParsedPT(sourceamount,0));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,OriginalSrc, a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case DYNAMIC_ABILITY_EFFECT_PUMPTOUGHNESS://pump toughness
|
|
{
|
|
if(storedAbility)
|
|
activateStored();
|
|
if(tosrc == false)
|
|
{
|
|
PTInstant * a = NEW PTInstant(this->GetId(), source, (MTGCardInstance*)_target,NEW WParsedPT(0,sourceamount));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,(MTGCardInstance*)_target, a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
PTInstant * a = NEW PTInstant(this->GetId(), source, OriginalSrc,NEW WParsedPT(0,sourceamount));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,OriginalSrc, a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case DYNAMIC_ABILITY_EFFECT_PUMPBOTH://pump both
|
|
{
|
|
if(storedAbility)
|
|
activateStored();
|
|
if(tosrc == false)
|
|
{
|
|
PTInstant * a = NEW PTInstant(this->GetId(), source, (MTGCardInstance*)_target,NEW WParsedPT(sourceamount,sourceamount));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,(MTGCardInstance*)_target, a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
PTInstant * a = NEW PTInstant(this->GetId(), source, OriginalSrc,NEW WParsedPT(sourceamount,sourceamount));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source,OriginalSrc, a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case DYNAMIC_ABILITY_EFFECT_LIFELOSS://lose life
|
|
if(storedAbility)
|
|
activateStored();
|
|
game->mLayers->stackLayer()->addLife(_target,(sourceamount * -1));
|
|
game->mLayers->stackLayer()->resolve();
|
|
|
|
return 1;
|
|
break;
|
|
case DYNAMIC_ABILITY_EFFECT_DEPLETE://deplete cards
|
|
{
|
|
if(storedAbility)
|
|
activateStored();
|
|
Player * player = (Player *)_target;
|
|
MTGLibrary * library = player->game->library;
|
|
for (int i = 0; i < sourceamount; i++)
|
|
{
|
|
if (library->nb_cards)
|
|
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
|
|
}
|
|
return 1;
|
|
break;
|
|
}
|
|
case DYNAMIC_ABILITY_EFFECT_COUNTERSONEONE:
|
|
{
|
|
if(_target->typeAsTarget() != TARGET_CARD)
|
|
_target = OriginalSrc;
|
|
for(int j = 0;j < sourceamount;j++)
|
|
((MTGCardInstance*)_target)->counters->addCounter(1,1);
|
|
break;
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int AADynamic::activateStored()
|
|
{
|
|
clonedStored = storedAbility->clone();
|
|
clonedStored->target = target;
|
|
if (clonedStored->oneShot)
|
|
{
|
|
clonedStored->resolve();
|
|
delete (clonedStored);
|
|
}
|
|
else
|
|
{
|
|
clonedStored->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * 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;
|
|
}
|
|
sprintf(menuText, "%s", menu.c_str());
|
|
return menuText;
|
|
}
|
|
|
|
AADynamic * AADynamic::clone() const
|
|
{
|
|
AADynamic * a = NEW AADynamic(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AADynamic::~AADynamic()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(storedAbility);
|
|
}
|
|
|
|
//AALifer
|
|
AALifer::AALifer(int _id, MTGCardInstance * card, Targetable * _target, string life_s, ManaCost * _cost, int who) :
|
|
ActivatedAbilityTP(_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_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 char * AALifer::getMenuText()
|
|
{
|
|
if(getLife() < 0)
|
|
return "Life Loss";
|
|
return "Life";
|
|
}
|
|
|
|
AALifer * AALifer::clone() const
|
|
{
|
|
AALifer * a = NEW AALifer(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
|
|
|
|
//Lifeset
|
|
AALifeSet::AALifeSet(int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * life, ManaCost * _cost,
|
|
int who) :
|
|
ActivatedAbilityTP(_id, _source, _target, _cost, who), life(life)
|
|
{
|
|
}
|
|
|
|
int AALifeSet::resolve()
|
|
{
|
|
Damageable * _target = (Damageable *) getTarget();
|
|
if (!_target)
|
|
return 0;
|
|
|
|
Player * p = NULL;
|
|
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
p = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
p = (Player*)_target;
|
|
}
|
|
|
|
int lifeDiff = life->getValue() - p->life ;
|
|
p->gainOrLoseLife(lifeDiff);
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char * AALifeSet::getMenuText()
|
|
{
|
|
return "Set Life";
|
|
}
|
|
|
|
AALifeSet * AALifeSet::clone() const
|
|
{
|
|
AALifeSet * a = NEW AALifeSet(*this);
|
|
a->life = NEW WParsedInt(*(a->life));
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AALifeSet::~AALifeSet()
|
|
{
|
|
SAFE_DELETE(life);
|
|
}
|
|
|
|
//AACloner
|
|
//cloning...this makes a token thats a copy of the target.
|
|
AACloner::AACloner(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int who,
|
|
string abilitiesStringList) :
|
|
ActivatedAbility(_id, _source, _cost, 0), who(who)
|
|
{
|
|
aType = MTGAbility::CLONING;
|
|
target = _target;
|
|
source = _source;
|
|
if (abilitiesStringList.size() > 0)
|
|
{
|
|
PopulateAbilityIndexVector(awith, abilitiesStringList);
|
|
PopulateColorIndexVector(colors, abilitiesStringList);
|
|
}
|
|
|
|
}
|
|
|
|
int AACloner::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
MTGCardInstance * myClone = NULL;
|
|
MTGCard* clone = (_target->isToken ? _target: MTGCollection()->getCardByName(_target->name));
|
|
if (who != 1)
|
|
{
|
|
myClone = NEW MTGCardInstance(clone, source->controller()->game);
|
|
}
|
|
if (who == 1)
|
|
{
|
|
myClone = NEW MTGCardInstance(clone, source->controller()->opponent()->game);
|
|
}
|
|
if (who != 1)
|
|
source->controller()->game->temp->addCard(myClone);
|
|
else
|
|
source->controller()->opponent()->game->temp->addCard(myClone);
|
|
Spell * spell = NEW Spell(myClone);
|
|
spell->resolve();
|
|
spell->source->isToken = 1;
|
|
spell->source->fresh = 1;
|
|
if(_target->isToken)
|
|
{
|
|
spell->source->power = _target->origpower;
|
|
spell->source->toughness = _target->origtoughness;
|
|
spell->source->life = _target->origtoughness;
|
|
}
|
|
list<int>::iterator it;
|
|
for (it = awith.begin(); it != awith.end(); it++)
|
|
{
|
|
spell->source->basicAbilities[*it] = 1;
|
|
}
|
|
for (it = colors.begin(); it != colors.end(); it++)
|
|
{
|
|
spell->source->setColor(*it);
|
|
}
|
|
delete spell;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * 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
|
|
{
|
|
AACloner * a = NEW AACloner(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
AACloner::~AACloner()
|
|
{
|
|
}
|
|
|
|
// Cast/Play Restriction modifier
|
|
ACastRestriction::ACastRestriction(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
|
|
AbilityTP(_id, card, _target, who), restrictionsScope(_restrictionsScope), value(_value), modifyExisting(_modifyExisting),zoneId(_zoneId)
|
|
{
|
|
existingRestriction = NULL;
|
|
targetPlayer = NULL;
|
|
}
|
|
|
|
int ACastRestriction::addToGame()
|
|
{
|
|
Targetable * _target = getTarget();
|
|
|
|
if (_target)
|
|
{
|
|
if (_target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
targetPlayer = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
targetPlayer = (Player *) _target;
|
|
}
|
|
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 0;
|
|
}
|
|
|
|
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 char * 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();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
ACastRestriction::~ACastRestriction()
|
|
{
|
|
SAFE_DELETE(value);
|
|
SAFE_DELETE(restrictionsScope);
|
|
}
|
|
|
|
|
|
AInstantCastRestrictionUEOT::AInstantCastRestrictionUEOT(int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
|
|
InstantAbilityTP(_id, card, _target, who)
|
|
{
|
|
ability = NEW ACastRestriction(_id, card, _target, _restrictionsScope, _value, _modifyExisting, _zoneId, who);
|
|
}
|
|
|
|
int AInstantCastRestrictionUEOT::resolve()
|
|
{
|
|
ACastRestriction * a = ability->clone();
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
const char * AInstantCastRestrictionUEOT::getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
AInstantCastRestrictionUEOT * AInstantCastRestrictionUEOT::clone() const
|
|
{
|
|
AInstantCastRestrictionUEOT * a = NEW AInstantCastRestrictionUEOT(*this);
|
|
a->ability = this->ability->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AInstantCastRestrictionUEOT::~AInstantCastRestrictionUEOT()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
|
|
//AAMover
|
|
AAMover::AAMover(int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest, ManaCost * _cost) :
|
|
ActivatedAbility(_id, _source, _cost, 0), destination(dest)
|
|
{
|
|
if (_target)
|
|
target = _target;
|
|
}
|
|
|
|
MTGGameZone * AAMover::destinationZone(Targetable * target)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
return MTGGameZone::stringToZone(destination, source, _target);
|
|
}
|
|
|
|
int AAMover::resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (target)
|
|
{
|
|
Player* p = _target->controller();
|
|
if (p)
|
|
{
|
|
GameObserver * g = GameObserver::GetInstance();
|
|
MTGGameZone * fromZone = _target->getCurrentZone();
|
|
MTGGameZone * destZone = destinationZone(target);
|
|
|
|
//inplay is a special zone !
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
if (destZone == g->players[i]->game->inPlay && fromZone != g->players[i]->game->inPlay && fromZone
|
|
!= g->players[i]->opponent()->game->inPlay)
|
|
{
|
|
MTGCardInstance * copy = g->players[i]->game->putInZone(_target, fromZone, g->players[i]->game->temp);
|
|
Spell * spell = NEW Spell(copy);
|
|
spell->resolve();
|
|
delete spell;
|
|
return 1;
|
|
}
|
|
}
|
|
p->game->putInZone(_target, fromZone, destZone);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * AAMover::getMenuText()
|
|
{
|
|
return "Move";
|
|
}
|
|
|
|
const char * AAMover::getMenuText(TargetChooser * tc)
|
|
{
|
|
GameObserver * g = GameObserver::GetInstance();
|
|
MTGGameZone * dest = destinationZone();
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
// Move card to hand
|
|
if (dest == g->players[i]->game->hand)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->inPlay))
|
|
return "Bounce";
|
|
if (tc->targetsZone(g->players[i]->game->graveyard))
|
|
return "Reclaim";
|
|
if (tc->targetsZone(g->opponent()->game->hand))
|
|
return "Steal";
|
|
}
|
|
|
|
// Move card to graveyard
|
|
else if (dest == g->players[i]->game->graveyard)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->inPlay))
|
|
return "Sacrifice";
|
|
if (tc->targetsZone(g->players[i]->game->hand))
|
|
return "Discard";
|
|
if (tc->targetsZone(g->opponent()->game->hand))
|
|
return "Opponent Discards";
|
|
}
|
|
|
|
// move card to library
|
|
else if (dest == g->players[i]->game->library)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->graveyard))
|
|
return "Recycle";
|
|
return "Put in Library";
|
|
}
|
|
|
|
// move card to battlefield
|
|
else if (dest == g->players[i]->game->battlefield)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->graveyard))
|
|
return "Reanimate";
|
|
return "Put in Play";
|
|
}
|
|
|
|
// move card into exile
|
|
else if (dest == g->players[i]->game->exile)
|
|
{
|
|
return "Exile";
|
|
}
|
|
|
|
// move card from Library
|
|
else if (tc->targetsZone(g->players[i]->game->library))
|
|
{
|
|
return "Fetch";
|
|
}
|
|
}
|
|
|
|
return "Move";
|
|
}
|
|
|
|
AAMover * AAMover::clone() const
|
|
{
|
|
AAMover * a = NEW AAMover(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//Random Discard
|
|
AARandomDiscarder::AARandomDiscarder(int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost,
|
|
int who) :
|
|
ActivatedAbilityTP(_id, card, _target, _cost, who), nbcardsStr(nbcardsStr)
|
|
{
|
|
}
|
|
|
|
int AARandomDiscarder::resolve()
|
|
{
|
|
Targetable * _target = getTarget();
|
|
Player * player;
|
|
if (_target)
|
|
{
|
|
if (_target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
player = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
player = (Player *) _target;
|
|
}
|
|
|
|
|
|
WParsedInt numCards(nbcardsStr, NULL, source);
|
|
for (int i = 0; i < numCards.intValue; i++)
|
|
{
|
|
player->game->discardRandom(player->game->hand, source);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char * AARandomDiscarder::getMenuText()
|
|
{
|
|
return "Discard Random";
|
|
}
|
|
|
|
AARandomDiscarder * AARandomDiscarder::clone() const
|
|
{
|
|
AARandomDiscarder * a = NEW AARandomDiscarder(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Shuffle
|
|
AAShuffle::AAShuffle(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
|
|
ActivatedAbilityTP(_id, card, _target, _cost, who)
|
|
{
|
|
}
|
|
|
|
int AAShuffle::resolve()
|
|
{
|
|
Targetable * _target = getTarget();
|
|
Player * player;
|
|
if (_target)
|
|
{
|
|
if (_target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
player = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
player = (Player *) _target;
|
|
}
|
|
MTGLibrary * library = player->game->library;
|
|
library->shuffle();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * AAShuffle::getMenuText()
|
|
{
|
|
return "Shuffle";
|
|
}
|
|
|
|
AAShuffle * AAShuffle::clone() const
|
|
{
|
|
AAShuffle * a = NEW AAShuffle(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Remove Mana From ManaPool
|
|
AARemoveMana::AARemoveMana(int _id, MTGCardInstance * card, Targetable * _target, string manaDesc, int who) :
|
|
ActivatedAbilityTP(_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()
|
|
{
|
|
Targetable * _target = getTarget();
|
|
Player * player;
|
|
if (_target)
|
|
{
|
|
if (_target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
player = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
else
|
|
{
|
|
player = (Player *) _target;
|
|
}
|
|
ManaPool * manaPool = player->getManaPool();
|
|
if (mRemoveAll)
|
|
{
|
|
if (mManaDesc) // Remove all mana Matching a description
|
|
{
|
|
for (unsigned int i = 0; i < Constants::MTG_NB_COLORS; i++)
|
|
{
|
|
if (mManaDesc->hasColor(i))
|
|
manaPool->removeAll(i);
|
|
}
|
|
}
|
|
else //Remove all mana
|
|
{
|
|
manaPool->init();
|
|
}
|
|
}
|
|
else //remove a "standard" mana Description
|
|
{
|
|
((ManaCost *)manaPool)->remove(mManaDesc); //why do I have to cast here?
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * 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;
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AARemoveMana::~AARemoveMana()
|
|
{
|
|
SAFE_DELETE(mManaDesc);
|
|
}
|
|
|
|
//Tapper
|
|
AATapper::AATapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
|
ActivatedAbility(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 char * AATapper::getMenuText()
|
|
{
|
|
return "Tap";
|
|
}
|
|
|
|
AATapper * AATapper::clone() const
|
|
{
|
|
AATapper * a = NEW AATapper(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//AA Untapper
|
|
AAUntapper::AAUntapper(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
|
|
ActivatedAbility(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 char * AAUntapper::getMenuText()
|
|
{
|
|
return "Untap";
|
|
}
|
|
|
|
AAUntapper * AAUntapper::clone() const
|
|
{
|
|
AAUntapper * a = NEW AAUntapper(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AAWhatsMax::AAWhatsMax(int id, MTGCardInstance * card, MTGCardInstance * source, ManaCost * _cost, int value) :
|
|
ActivatedAbility(id, card, _cost, 0), value(value)
|
|
{
|
|
}
|
|
|
|
int AAWhatsMax::resolve()
|
|
{
|
|
|
|
if (source)
|
|
{
|
|
source->MaxLevelUp = value;
|
|
source->isLeveler = 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
AAWhatsMax * AAWhatsMax::clone() const
|
|
{
|
|
AAWhatsMax * a = NEW AAWhatsMax(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
// Win Game
|
|
AAWinGame::AAWinGame(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
|
|
ActivatedAbilityTP(_id, card, _target, _cost, who)
|
|
{
|
|
}
|
|
|
|
int AAWinGame::resolve()
|
|
{
|
|
Damageable * _target = (Damageable *) getTarget();
|
|
if (_target)
|
|
{
|
|
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
_target = ((MTGCardInstance *) _target)->controller();
|
|
}
|
|
int cantlosers = 0;
|
|
MTGGameZone * z = ((Player *) _target)->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))
|
|
{
|
|
cantlosers++;
|
|
}
|
|
}
|
|
MTGGameZone * k = ((Player *) _target)->game->inPlay;
|
|
int onbcards = k->nb_cards;
|
|
for (int m = 0; m < onbcards; ++m)
|
|
{
|
|
MTGCardInstance * e = k->cards[m];
|
|
if (e->has(Constants::CANTWIN))
|
|
{
|
|
cantlosers++;
|
|
}
|
|
}
|
|
if (cantlosers < 1)
|
|
{
|
|
game->gameOver = ((Player *) _target)->opponent();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * AAWinGame::getMenuText()
|
|
{
|
|
return "Win Game";
|
|
}
|
|
|
|
AAWinGame * AAWinGame::clone() const
|
|
{
|
|
AAWinGame * a = NEW AAWinGame(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//Generic Abilities
|
|
|
|
//May Abilities
|
|
MayAbility::MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must) :
|
|
MTGAbility(_id, _source), NestedAbility(_ability), must(must)
|
|
{
|
|
triggered = 0;
|
|
mClone = NULL;
|
|
}
|
|
|
|
void MayAbility::Update(float dt)
|
|
{
|
|
MTGAbility::Update(dt);
|
|
if (!triggered)
|
|
{
|
|
triggered = 1;
|
|
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability))
|
|
{
|
|
if (!ta->tc->validTargetsExist())
|
|
return;
|
|
}
|
|
game->mLayers->actionLayer()->setMenuObject(source, must);
|
|
previousInterrupter = game->isInterrupting;
|
|
game->mLayers->stackLayer()->setIsInterrupting(source->controller());
|
|
}
|
|
}
|
|
|
|
const char * 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);
|
|
return 1;
|
|
}
|
|
|
|
int MayAbility::isReactingToTargetClick(Targetable * card)
|
|
{
|
|
if (card == source)
|
|
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();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
MayAbility::~MayAbility()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
//MultiAbility : triggers several actions for a cost
|
|
MultiAbility::MultiAbility(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost) :
|
|
ActivatedAbility(_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;
|
|
vector<int>::size_type sz = abilities.size();
|
|
for (unsigned int i = 0; i < sz; 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 (unsigned int 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 char * MultiAbility::getMenuText()
|
|
{
|
|
if (abilities.size())
|
|
return abilities[0]->getMenuText();
|
|
return "";
|
|
}
|
|
|
|
MultiAbility * MultiAbility::clone() const
|
|
{
|
|
MultiAbility * a = NEW MultiAbility(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
MultiAbility::~MultiAbility()
|
|
{
|
|
if (!isClone)
|
|
{
|
|
vector<int>::size_type sz = abilities.size();
|
|
for (size_t i = 0; i < sz; i++)
|
|
{
|
|
SAFE_DELETE(abilities[i]);
|
|
}
|
|
}
|
|
abilities.clear();
|
|
}
|
|
|
|
//Generic Target Ability
|
|
GenericTargetAbility::GenericTargetAbility(string newName,int _id, MTGCardInstance * _source, TargetChooser * _tc, MTGAbility * a,
|
|
ManaCost * _cost, string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest) :
|
|
TargetAbility(_id, _source, _tc, _cost, restrictions), limit(limit), activeZone(dest),newName(newName)
|
|
{
|
|
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 char * 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++;
|
|
return TargetAbility::resolve();
|
|
}
|
|
|
|
int GenericTargetAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
|
|
{
|
|
limitPerTurn = 0;
|
|
if(limit.size())
|
|
{
|
|
WParsedInt * value = NEW WParsedInt(limit.c_str(),NULL,source);
|
|
limitPerTurn = value->getValue();
|
|
delete value;
|
|
}
|
|
if (limitPerTurn && counters >= limitPerTurn)
|
|
return 0;
|
|
return TargetAbility::isReactingToClick(card, mana);
|
|
}
|
|
|
|
void GenericTargetAbility::Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::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();
|
|
a->cost = NEW ManaCost();
|
|
a->cost->copy(cost);
|
|
if (tc)
|
|
a->tc = tc->clone();
|
|
return a;
|
|
}
|
|
|
|
GenericTargetAbility::~GenericTargetAbility()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
//Alter Cost
|
|
AAlterCost::AAlterCost(int id, MTGCardInstance * source, MTGCardInstance * target, int amount, int type) :
|
|
MTGAbility(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();
|
|
increased->init();
|
|
_target->getIncreasedManaCost()->copy(increased);
|
|
delete increased;
|
|
|
|
}
|
|
_target->getIncreasedManaCost()->add(type,amount);
|
|
}
|
|
else
|
|
{
|
|
if(!_target->getReducedManaCost()->getConvertedCost())
|
|
{
|
|
ManaCost * reduced = NEW ManaCost();
|
|
reduced->init();
|
|
_target->getReducedManaCost()->copy(reduced);
|
|
delete reduced;
|
|
}
|
|
_target->getReducedManaCost()->add(type,abs(amount));
|
|
}
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int AAlterCost::testDestroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *)target;
|
|
if(!this->manaReducer->isInPlay())
|
|
{
|
|
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::MTG_NB_COLORS;k++)
|
|
{
|
|
card->getManaCost()->add(k,card->getIncreasedManaCost()->getCost(k));
|
|
if (card->getManaCost()->alternative)
|
|
{
|
|
card->getManaCost()->alternative->add(k,card->getIncreasedManaCost()->getCost(k));
|
|
}
|
|
if (card->getManaCost()->BuyBack)
|
|
{
|
|
card->getManaCost()->BuyBack->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::MTG_NB_COLORS;k++)
|
|
{
|
|
card->getManaCost()->remove(k,card->getReducedManaCost()->getCost(k));
|
|
if (card->getManaCost()->alternative)
|
|
{
|
|
card->getManaCost()->alternative->remove(k,card->getReducedManaCost()->getCost(k));
|
|
}
|
|
if (card->getManaCost()->BuyBack)
|
|
{
|
|
card->getManaCost()->BuyBack->remove(k,card->getReducedManaCost()->getCost(k));
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
AAlterCost * AAlterCost::clone() const
|
|
{
|
|
AAlterCost * a = NEW AAlterCost(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AAlterCost::~AAlterCost()
|
|
{
|
|
}
|
|
|
|
// ATransformer
|
|
ATransformer::ATransformer(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) :
|
|
MTGAbility(id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever)
|
|
{
|
|
|
|
PopulateAbilityIndexVector(abilities, sabilities);
|
|
PopulateColorIndexVector(colors, sabilities);
|
|
|
|
//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)
|
|
{
|
|
for (size_t i = 0; i <Subtypes::subtypesList->getValuesById().size(); ++i)
|
|
{
|
|
if (!Subtypes::subtypesList->isSubtypeOfType(i,Subtypes::TYPE_CREATURE))
|
|
continue;
|
|
|
|
//Erwan 2011/5/6 String comparison is expensive. Any way to do this in a cleaner way?
|
|
//this check is related to targetchooser instances of cards dynamically loaded subtypes.
|
|
//example(foreach(arbor elf)) adds this as a subtype for list ment.
|
|
//TODO find cheaper method
|
|
string s = Subtypes::subtypesList->find(i);
|
|
if (s.find(" ") != string::npos)
|
|
continue;
|
|
types.push_back(i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PopulateSubtypesIndexVector(types, stypes);
|
|
}
|
|
|
|
menu = stypes;
|
|
}
|
|
|
|
int ATransformer::addToGame()
|
|
{
|
|
MTGCardInstance * _target = NULL;
|
|
Interruptible * action = (Interruptible *) target;
|
|
if (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::MTG_NB_COLORS; j++)
|
|
{
|
|
if (_target->hasColor(j))
|
|
oldcolors.push_back(j);
|
|
}
|
|
for (size_t j = 0; j < _target->types.size(); ++j)
|
|
oldtypes.push_back( _target->types[j]);
|
|
|
|
list<int>::iterator it;
|
|
for (it = colors.begin(); it != colors.end(); it++)
|
|
{
|
|
if(!addNewColors)
|
|
_target->setColor(0, 1);
|
|
}
|
|
|
|
if (removeTypes)
|
|
{
|
|
//remove the main types from a card, ie: hidden enchantment cycle.
|
|
for (int i = 0; i < Subtypes::LAST_TYPE; ++ i)
|
|
_target->removeType(i,1);
|
|
}
|
|
else if (remove)
|
|
{
|
|
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
|
|
{
|
|
_target->removeType(*it);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (it = types.begin(); it != types.end(); it++)
|
|
{
|
|
if(removeCreatureSubtypes)
|
|
{
|
|
_target->removeType(*it);
|
|
}
|
|
else if(_target->hasSubtype(*it))
|
|
{
|
|
//we generally don't want to give a creature type creature again
|
|
//all it does is create a sloppy mess of the subtype line on alternative quads
|
|
//also creates instances where a card gained a type from an ability like this one
|
|
//then loses the type through another ability, when this effect is destroyed the creature regains
|
|
//the type, which is wrong.
|
|
dontremove.push_back(*it);
|
|
}
|
|
else
|
|
{
|
|
_target->addType(*it);
|
|
}
|
|
}
|
|
}
|
|
for (it = colors.begin(); it != colors.end(); it++)
|
|
{
|
|
_target->setColor(*it);
|
|
}
|
|
|
|
for (it = abilities.begin(); it != abilities.end(); it++)
|
|
{
|
|
_target->basicAbilities.set(*it);
|
|
}
|
|
|
|
if(newAbilityFound)
|
|
{
|
|
for (unsigned int k = 0 ; k < newAbilitiesList.size();k++)
|
|
{
|
|
AbilityFactory af;
|
|
MTGAbility * aNew = af.parseMagicLine(newAbilitiesList[k], 0, NULL, _target);
|
|
aNew->isClone = 1;
|
|
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 )
|
|
{
|
|
WParsedInt * val = NEW WParsedInt(newpower,NULL, source);
|
|
oldpower = _target->power;
|
|
_target->power += val->getValue();
|
|
_target->power -= oldpower;
|
|
_target->power += reapplyCountersBonus(_target,false,true);
|
|
delete val;
|
|
}
|
|
if(newtoughnessfound )
|
|
{
|
|
WParsedInt * val = NEW WParsedInt(newtoughness,NULL, source);
|
|
oldtoughness = _target->toughness;
|
|
_target->addToToughness(val->getValue());
|
|
_target->addToToughness(-oldtoughness);
|
|
_target->addToToughness(reapplyCountersBonus(_target,true,false));
|
|
_target->life = _target->toughness;
|
|
delete val;
|
|
}
|
|
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int ATransformer::reapplyCountersBonus(MTGCardInstance * rtarget,bool powerapplied,bool toughnessapplied)
|
|
{
|
|
if(!rtarget->counters)
|
|
return 0;
|
|
Counter * c = NULL;
|
|
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::destroy()
|
|
{
|
|
if(aForever)
|
|
return 0;
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
while (_target->next)
|
|
_target = _target->next;
|
|
list<int>::iterator it;
|
|
|
|
if (!remove)
|
|
{
|
|
for (it = types.begin(); it != types.end(); it++)
|
|
{
|
|
bool removing = true;
|
|
for(unsigned int k = 0;k < dontremove.size();k++)
|
|
{
|
|
if(dontremove[k] == *it)
|
|
removing = false;
|
|
}
|
|
if(removing)
|
|
_target->removeType(*it);
|
|
}
|
|
//iterators annoy me :/
|
|
}
|
|
|
|
for (it = colors.begin(); it != colors.end(); it++)
|
|
{
|
|
_target->removeColor(*it);
|
|
}
|
|
|
|
for (it = abilities.begin(); it != abilities.end(); it++)
|
|
{
|
|
_target->basicAbilities.reset(*it);
|
|
}
|
|
|
|
for (it = oldcolors.begin(); it != oldcolors.end(); it++)
|
|
{
|
|
_target->setColor(*it);
|
|
}
|
|
|
|
if(newpowerfound )
|
|
{
|
|
_target->power = oldpower;
|
|
}
|
|
if(newtoughnessfound )
|
|
{
|
|
_target->toughness = oldtoughness;
|
|
}
|
|
if(newAbilityFound)
|
|
{
|
|
for (unsigned int i = 0;i < newAbilities[_target].size(); i++)
|
|
{
|
|
newAbilities[_target].at(i)->forceDestroy = 1;
|
|
}
|
|
if (newAbilities.find(_target) != newAbilities.end())
|
|
{
|
|
newAbilities.erase(_target);
|
|
}
|
|
}
|
|
if (remove)
|
|
{
|
|
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
|
|
{
|
|
_target->addType(*it);
|
|
}
|
|
}
|
|
//n the case that we removed or added types to a card, so that it retains its original name when the effect is removed.
|
|
_target->name.clear();
|
|
_target->setName(_target->model->data->name.c_str());
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * ATransformer::getMenuText()
|
|
{
|
|
string s = menu;
|
|
sprintf(menuText, "Becomes %s", s.c_str());
|
|
return menuText;
|
|
}
|
|
|
|
ATransformer * ATransformer::clone() const
|
|
{
|
|
ATransformer * a = NEW ATransformer(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
ATransformer::~ATransformer()
|
|
{
|
|
}
|
|
|
|
//ATransformerInstant
|
|
ATransformerInstant::ATransformerInstant(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) :
|
|
InstantAbility(id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever)
|
|
{
|
|
ability = NEW ATransformer(id, source, target, types, abilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,aForever);
|
|
aType = MTGAbility::STANDARD_BECOMES;
|
|
}
|
|
|
|
int ATransformerInstant::resolve()
|
|
{
|
|
ATransformer * a = ability->clone();
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
const char * ATransformerInstant::getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
ATransformerInstant * ATransformerInstant::clone() const
|
|
{
|
|
ATransformerInstant * a = NEW ATransformerInstant(*this);
|
|
a->ability = this->ability->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
ATransformerInstant::~ATransformerInstant()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
//P/t ueot
|
|
PTInstant::PTInstant(int id, MTGCardInstance * source, MTGCardInstance * target, WParsedPT * wppt,string s,bool nonstatic) :
|
|
InstantAbility(id, source, target), wppt(wppt),s(s),nonstatic(nonstatic)
|
|
{
|
|
ability = NEW APowerToughnessModifier(id, source, target, wppt,s,nonstatic);
|
|
aType = MTGAbility::STANDARD_PUMP;
|
|
}
|
|
|
|
int PTInstant::resolve()
|
|
{
|
|
APowerToughnessModifier * a = ability->clone();
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
const char * PTInstant::getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
PTInstant * PTInstant::clone() const
|
|
{
|
|
PTInstant * a = NEW PTInstant(*this);
|
|
a->ability = this->ability->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
PTInstant::~PTInstant()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
// ASwapPTUEOT
|
|
ASwapPTUEOT::ASwapPTUEOT(int id, MTGCardInstance * source, MTGCardInstance * target) :
|
|
InstantAbility(id, source, target)
|
|
{
|
|
ability = NEW ASwapPT(id, source, target);
|
|
}
|
|
|
|
int ASwapPTUEOT::resolve()
|
|
{
|
|
ASwapPT * a = ability->clone();
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1, source, (Damageable *) (this->target), a);
|
|
wrapper->addToGame();
|
|
return 1;
|
|
}
|
|
|
|
const char * ASwapPTUEOT::getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
ASwapPTUEOT * ASwapPTUEOT::clone() const
|
|
{
|
|
ASwapPTUEOT * a = NEW ASwapPTUEOT(*this);
|
|
a->ability = this->ability->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
ASwapPTUEOT::~ASwapPTUEOT()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
|
|
//ALoseAbilities
|
|
ALoseAbilities::ALoseAbilities(int id, MTGCardInstance * source, MTGCardInstance * _target) :
|
|
MTGAbility(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();
|
|
|
|
for (int i = al->mCount - 1; i > 0; i--) //0 is not a mtgability...hackish
|
|
{
|
|
if (al->mObjects[i])
|
|
{
|
|
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
|
|
if (currentAction->source == _target)
|
|
{
|
|
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
|
|
ALoseAbilities * la = dynamic_cast<ALoseAbilities*> (a);
|
|
if (a->oneShot||la)
|
|
{
|
|
if(la)
|
|
{
|
|
DebugTrace("Dangerous chance of infinate loop avoided!");
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace("ALLABILITIES: Ability should not be one shot");
|
|
continue;
|
|
}
|
|
}
|
|
a->addToGame();
|
|
}
|
|
storedAbilities.clear();
|
|
return 1;
|
|
}
|
|
|
|
ALoseAbilities * ALoseAbilities::clone() const
|
|
{
|
|
ALoseAbilities * a = NEW ALoseAbilities(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//ALoseSubtypes
|
|
ALoseSubtypes::ALoseSubtypes(int id, MTGCardInstance * source, MTGCardInstance * _target, int parentType) :
|
|
MTGAbility(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 (Subtypes::subtypesList->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
|
|
{
|
|
ALoseSubtypes * a = NEW ALoseSubtypes(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
//APreventDamageTypes
|
|
APreventDamageTypes::APreventDamageTypes(int id, MTGCardInstance * source, string to, string from, int type) :
|
|
MTGAbility(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;
|
|
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_COMBAT);
|
|
}
|
|
else if (type == 1)
|
|
{
|
|
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, DAMAGE_ALL_TYPES);
|
|
}
|
|
else if (type == 2)
|
|
{
|
|
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, 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->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
APreventDamageTypes::~APreventDamageTypes()
|
|
{
|
|
SAFE_DELETE(re);
|
|
}
|
|
|
|
//APreventDamageTypesUEOT
|
|
APreventDamageTypesUEOT::APreventDamageTypesUEOT(int id, MTGCardInstance * source, string to, string from, int type) :
|
|
InstantAbility(id, source)
|
|
{
|
|
ability = NEW APreventDamageTypes(id, source, to, from, type);
|
|
}
|
|
|
|
int APreventDamageTypesUEOT::resolve()
|
|
{
|
|
APreventDamageTypes * a = ability->clone();
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(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 char * APreventDamageTypesUEOT::getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
APreventDamageTypesUEOT * APreventDamageTypesUEOT::clone() const
|
|
{
|
|
APreventDamageTypesUEOT * a = NEW APreventDamageTypesUEOT(*this);
|
|
a->ability = this->ability->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
APreventDamageTypesUEOT::~APreventDamageTypesUEOT()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
//AVanishing creature also fading
|
|
AVanishing::AVanishing(int _id, MTGCardInstance * card, ManaCost * _cost, int restrictions, int amount, string counterName) :
|
|
MTGAbility(_id, source, target),amount(amount),counterName(counterName)
|
|
{
|
|
target = card;
|
|
source = card;
|
|
next = 0;
|
|
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 == Constants::MTG_PHASE_UPKEEP)
|
|
{
|
|
source->counters->removeCounter(counterName.c_str(),0,0);
|
|
Counter * targetCounter = NULL;
|
|
timeLeft = 0;
|
|
|
|
if (source->counters && source->counters->hasCounter(counterName.c_str(), 0, 0))
|
|
{
|
|
targetCounter = source->counters->hasCounter(counterName.c_str(), 0, 0);
|
|
timeLeft = targetCounter->nb;
|
|
}
|
|
else
|
|
{
|
|
timeLeft = 0;
|
|
if(counterName.find("fade") != string::npos && next == 0)
|
|
{
|
|
next = 1;
|
|
}
|
|
else
|
|
{
|
|
next = 0;
|
|
}
|
|
if (newPhase == Constants::MTG_PHASE_UPKEEP && timeLeft <= 0 && next == 0)
|
|
{
|
|
WEvent * e = NEW WEventCardSacrifice(source);
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
game->receiveEvent(e);
|
|
source->controller()->game->putInGraveyard(source);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MTGAbility::Update(dt);
|
|
}
|
|
|
|
int AVanishing::resolve()
|
|
{
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char * AVanishing::getMenuText()
|
|
{
|
|
if(counterName.find("fade") != string::npos)
|
|
return "Fading";
|
|
return "Vanishing";
|
|
}
|
|
|
|
AVanishing * AVanishing::clone() const
|
|
{
|
|
AVanishing * a = NEW AVanishing(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AVanishing::~AVanishing()
|
|
{
|
|
}
|
|
|
|
//AUpkeep
|
|
AUpkeep::AUpkeep(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int restrictions, int _phase,
|
|
int _once,bool Cumulative) :
|
|
ActivatedAbility(_id, card, _cost, restrictions), NestedAbility(a), phase(_phase), once(_once),Cumulative(Cumulative)
|
|
{
|
|
paidThisTurn = 0;
|
|
aType = MTGAbility::UPCOST;
|
|
}
|
|
|
|
int AUpkeep::receiveEvent(WEvent * event)
|
|
{
|
|
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
|
|
{
|
|
if (Constants::MTG_PHASE_DRAW == pe->to->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 == Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
paidThisTurn = 0;
|
|
}
|
|
else if(newPhase == Constants::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 -= currentage;
|
|
}
|
|
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 char * 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->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
AUpkeep::~AUpkeep()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
//A Phase based Action
|
|
APhaseAction::APhaseAction(int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once) :
|
|
MTGAbility(_id, card),sAbility(sAbility), phase(_phase),forcedestroy(forcedestroy),next(next),myturn(myturn),opponentturn(opponentturn),once(once)
|
|
{
|
|
abilityId = _id;
|
|
abilityOwner = card->controller();
|
|
psMenuText = "";
|
|
AbilityFactory af;
|
|
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;
|
|
if(target)
|
|
_target = (MTGCardInstance *) target;
|
|
if(!sAbility.size() || (!target||(!_target->currentZone && _target != this->source)))
|
|
{
|
|
this->forceDestroy = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
while(_target->next)
|
|
_target = _target->next;
|
|
}
|
|
AbilityFactory af;
|
|
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 char * 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.
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
APhaseAction::~APhaseAction()
|
|
{
|
|
|
|
}
|
|
|
|
// the main ability
|
|
APhaseActionGeneric::APhaseActionGeneric(int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once) :
|
|
InstantAbility(_id, source, target)
|
|
{
|
|
MTGCardInstance * _target = target;
|
|
ability = NEW APhaseAction(_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 char * APhaseActionGeneric::getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
APhaseActionGeneric * APhaseActionGeneric::clone() const
|
|
{
|
|
APhaseActionGeneric * a = NEW APhaseActionGeneric(*this);
|
|
a->ability = this->ability->clone();
|
|
a->oneShot = 1;
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
APhaseActionGeneric::~APhaseActionGeneric()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
//a blink
|
|
ABlink::ABlink(int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot,bool blinkForSource,bool blinkhand,MTGAbility * stored) :
|
|
MTGAbility(_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();
|
|
}
|
|
GameObserver * game = game->GetInstance();
|
|
if ((blinkueot && currentPhase == Constants::MTG_PHASE_ENDOFTURN)||(blinkForSource && !source->isInPlay()))
|
|
{
|
|
if(Blinked == NULL)
|
|
MTGAbility::Update(dt);
|
|
MTGCardInstance * _target = Blinked;
|
|
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(Blinker);
|
|
spell->source->counters->init();
|
|
if(spell->source->hasSubtype(Subtypes::TYPE_AURA) && !blinkhand)
|
|
{
|
|
TargetChooserFactory tcf;
|
|
TargetChooser * tc = tcf.createTargetChooser(spell->source->spellTargetType,spell->source);
|
|
if(!tc->validTargetsExist())
|
|
{
|
|
spell->source->owner->game->putInExile(spell->source);
|
|
delete spell;
|
|
delete tc;
|
|
this->forceDestroy = 1;
|
|
return;
|
|
}
|
|
|
|
MTGGameZone * inplay = spell->source->owner->game->inPlay;
|
|
spell->source->target = NULL;
|
|
for(int i = WRand()%inplay->nb_cards;;i = WRand()%inplay->nb_cards)
|
|
{
|
|
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
|
|
{
|
|
spell->source->target = inplay->cards[i];
|
|
spell->getNextCardTarget();
|
|
spell->resolve();
|
|
|
|
delete spell;
|
|
delete tc;
|
|
this->forceDestroy = 1;
|
|
return;
|
|
}
|
|
if(!tc->validTargetsExist())
|
|
return;
|
|
}
|
|
}
|
|
spell->source->power = spell->source->origpower;
|
|
spell->source->toughness = spell->source->origtoughness;
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
delete spell;
|
|
this->forceDestroy = 1;
|
|
Blinker = NULL;
|
|
return;
|
|
}
|
|
MTGAbility::Update(dt);
|
|
}
|
|
|
|
void ABlink::resolveBlink()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
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)
|
|
{
|
|
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(Blinker);
|
|
spell->source->counters->init();
|
|
if(spell->source->hasSubtype(Subtypes::TYPE_AURA) && !blinkhand)
|
|
{
|
|
TargetChooserFactory tcf;
|
|
TargetChooser * tc = tcf.createTargetChooser(spell->source->spellTargetType,spell->source);
|
|
if(!tc->validTargetsExist())
|
|
{
|
|
spell->source->owner->game->putInExile(spell->source);
|
|
delete spell;
|
|
delete tc;
|
|
this->forceDestroy = 1;
|
|
return;
|
|
}
|
|
|
|
MTGGameZone * inplay = spell->source->owner->game->inPlay;
|
|
spell->source->target = NULL;
|
|
for(int i = WRand()%inplay->nb_cards;;i = WRand()%inplay->nb_cards)
|
|
{
|
|
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
|
|
{
|
|
spell->source->target = inplay->cards[i];
|
|
spell->getNextCardTarget();
|
|
spell->resolve();
|
|
delete spell;
|
|
delete tc;
|
|
this->forceDestroy = 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
spell->source->power = spell->source->origpower;
|
|
spell->source->toughness = spell->source->origtoughness;
|
|
spell->resolve();
|
|
if(stored)
|
|
{
|
|
MTGAbility * clonedStored = stored->clone();
|
|
clonedStored->target = spell->source;
|
|
if (clonedStored->oneShot)
|
|
{
|
|
clonedStored->resolve();
|
|
delete (clonedStored);
|
|
}
|
|
else
|
|
{
|
|
clonedStored->addToGame();
|
|
}
|
|
}
|
|
delete tc;
|
|
delete spell;
|
|
this->forceDestroy = 1;
|
|
if(stored)
|
|
delete(stored);
|
|
Blinked = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ABlink::resolve()
|
|
{
|
|
return 0;
|
|
}
|
|
const char * ABlink::getMenuText()
|
|
{
|
|
return "Blink";
|
|
}
|
|
|
|
ABlink * ABlink::clone() const
|
|
{
|
|
ABlink * a = NEW ABlink(*this);
|
|
a->isClone = 1;
|
|
a->forceDestroy = -1;
|
|
return a;
|
|
};
|
|
ABlink::~ABlink()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(stored);
|
|
}
|
|
|
|
ABlinkGeneric::ABlinkGeneric(int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot,bool blinkForSource,bool blinkhand,MTGAbility * stored) :
|
|
InstantAbility(_id, source, _target)
|
|
{
|
|
ability = NEW ABlink(_id,card,_target,blinkueot,blinkForSource,blinkhand,stored);
|
|
}
|
|
|
|
int ABlinkGeneric::resolve()
|
|
{
|
|
ABlink * a = ability->clone();
|
|
a->target = target;
|
|
a->addToGame();
|
|
return 1;
|
|
}
|
|
|
|
const char * ABlinkGeneric::getMenuText()
|
|
{
|
|
return "Blink";
|
|
}
|
|
|
|
ABlinkGeneric * ABlinkGeneric::clone() const
|
|
{
|
|
ABlinkGeneric * a = NEW ABlinkGeneric(*this);
|
|
a->ability = this->ability->clone();
|
|
a->oneShot = 1;
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
ABlinkGeneric::~ABlinkGeneric()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
// 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::MTG_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 = Subtypes::subtypesList->find(subtype);
|
|
if (id != string::npos)
|
|
types.push_back(id);
|
|
}
|
|
}
|