52b83a135c
-- Tutorial Messages are an ability like any other, except it can only be displayed once. Subsequent calls are ignored, the ability is removed from the game as soon as it is added -- This allows to add event triggered messages ingame. Messages are either text, or images (I don't have an image sample, but rules/classic.txt has a few examples that might help) -- only tested on Windows, although I made sure the PSP version compiles. Hopefully I also made the necessary for it to work in the touch version (touching the screen should be enough to close the tuto message) -- Room for improvement: possibility to choose a title in text mode, possibility to have some messages depending on others (e.g.: don't show message X until message Y has been shown), improve some of the abilities and triggers to give more flexibility, add events outside of game, to allow tuto messages in deck creator, etc...
3898 lines
107 KiB
C++
3898 lines
107 KiB
C++
#include "PrecompiledHeader.h"
|
|
#include "AllAbilities.h"
|
|
#include "Translate.h"
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
//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)
|
|
{
|
|
aType = MTGAbility::STANDARD_FIZZLER;
|
|
target = _target;
|
|
}
|
|
|
|
int AAFizzler::resolve()
|
|
{
|
|
ActionStack * stack = game->mLayers->stackLayer();
|
|
//the next section helps Ai correctly recieve its targets for this effect
|
|
if(!target && source->target)
|
|
{
|
|
//ai is casting a spell from it's hand to fizzle.
|
|
target = stack->getAt(stack->getActionElementFromCard(source->target));
|
|
}
|
|
else if(target->typeAsTarget() == TARGET_CARD)
|
|
{
|
|
//ai targeted using an ability on a card to fizzle.
|
|
target = stack->getAt(stack->getActionElementFromCard((MTGCardInstance*)target));
|
|
}
|
|
Spell * sTarget = (Spell *) target;
|
|
MTGCardInstance* sCard = (MTGCardInstance*)sTarget->source;
|
|
if(!sCard || !sTarget || sCard->has(Constants::NOFIZZLE))
|
|
return 0;
|
|
stack->Fizzle(sTarget);
|
|
return 1;
|
|
}
|
|
|
|
const char * AAFizzler::getMenuText()
|
|
{
|
|
return "Fizzle";
|
|
}
|
|
|
|
AAFizzler* AAFizzler::clone() const
|
|
{
|
|
AAFizzler * a = NEW AAFizzler(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
// BanishCard implementations
|
|
// Bury
|
|
|
|
AABuryCard::AABuryCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
ActivatedAbility(_id, _source)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
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) :
|
|
ActivatedAbility(_id, _source)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
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) :
|
|
ActivatedAbility(_id, _source)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
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) :
|
|
ActivatedAbility(_id, _source)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
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 (size_t i = 1; i < g->mLayers->actionLayer()->mObjects.size(); 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;
|
|
}
|
|
|
|
|
|
//players max hand size
|
|
AASetHand::AASetHand(int _id, MTGCardInstance * _source, Targetable * _target, int hand, ManaCost * _cost,
|
|
int who) :
|
|
ActivatedAbilityTP(_id, _source, _target, _cost, who), hand(hand)
|
|
{
|
|
}
|
|
|
|
int AASetHand::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;
|
|
}
|
|
|
|
p->handsize = hand;
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char * AASetHand::getMenuText()
|
|
{
|
|
return "Set Hand Size";
|
|
}
|
|
|
|
AASetHand * AASetHand::clone() const
|
|
{
|
|
AASetHand * a = NEW AASetHand(*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)
|
|
{
|
|
const vector<string> values = Subtypes::subtypesList->getValuesById();
|
|
for (size_t i = 0; i <values.size(); ++i)
|
|
{
|
|
if (!Subtypes::subtypesList->isSubtypeOfType(i,Subtypes::TYPE_CREATURE))
|
|
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);
|
|
}
|
|
}
|
|
//in the case that we removed or added types to a card, so that it retains its original name when the effect is removed.
|
|
if(_target->model->data->name.size())//tokens don't have a model name.
|
|
_target->setName(_target->model->data->name.c_str());
|
|
}
|
|
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 = (int)(al->mObjects.size()) - 1; i > 0; i--) //0 is not a mtgability...hackish
|
|
{
|
|
if (al->mObjects[i])
|
|
{
|
|
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
|
|
ALoseAbilities * la = dynamic_cast<ALoseAbilities*> (currentAction);
|
|
if(la)
|
|
continue;
|
|
if (currentAction->source == _target)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
//Tutorial Messaging
|
|
|
|
ATutorialMessage * ATutorialMessage::Current = NULL;
|
|
|
|
ATutorialMessage::ATutorialMessage(MTGCardInstance * source, string message) : MTGAbility(0, source), IconButtonsController(0, 0)
|
|
{
|
|
mBgTex = NULL;
|
|
|
|
mElapsed = 0;
|
|
mIsImage = false;
|
|
|
|
for (int i = 0; i < 9; i++)
|
|
mBg[i] = NULL;
|
|
|
|
string gfx = WResourceManager::Instance()->graphicsFile(message);
|
|
if (fileExists(gfx.c_str()))
|
|
{
|
|
mIsImage = true;
|
|
mMessage = message;
|
|
}
|
|
else
|
|
{
|
|
mMessage = _(message); //translate directly here, remove this and translate at rendering time if it bites us
|
|
boost::replace_all(mMessage, "\\n", "\n");
|
|
}
|
|
|
|
if (mIsImage)
|
|
{
|
|
mX = SCREEN_WIDTH_F / 2;
|
|
mY = SCREEN_HEIGHT_F / 2;
|
|
|
|
}
|
|
else
|
|
{
|
|
mX = 0;
|
|
mY = -SCREEN_HEIGHT_F - 0.1f; //Offscreen
|
|
}
|
|
mDontShow = mUserCloseRequest = alreadyShown();
|
|
|
|
if(mDontShow)
|
|
forceDestroy = 1;
|
|
}
|
|
|
|
|
|
string ATutorialMessage::getOptionName()
|
|
{
|
|
std::stringstream out;
|
|
out << "tuto_";
|
|
out << hash_djb2(mMessage.c_str());
|
|
return out.str();
|
|
}
|
|
|
|
bool ATutorialMessage::alreadyShown()
|
|
{
|
|
return options[getOptionName()].number ? true : false;
|
|
}
|
|
|
|
bool ATutorialMessage::CheckUserInput(JButton key)
|
|
{
|
|
if (mUserCloseRequest) return false;
|
|
|
|
if(key == JGE_BTN_SEC || key == JGE_BTN_OK)
|
|
{
|
|
ButtonPressed(0, 1);
|
|
return true;
|
|
}
|
|
|
|
//Required for Mouse/touch input
|
|
IconButtonsController::CheckUserInput(key);
|
|
|
|
return true; //this ability is modal, so it catches all key events until it gets closed
|
|
}
|
|
|
|
void ATutorialMessage::Update(float dt)
|
|
{
|
|
if (!Current && !mDontShow)
|
|
Current = this;
|
|
|
|
if (Current != this)
|
|
return;
|
|
|
|
if (mUserCloseRequest && mY < -SCREEN_HEIGHT)
|
|
mDontShow = true;
|
|
|
|
if (mDontShow)
|
|
{
|
|
Current = NULL;
|
|
forceDestroy = 1;
|
|
return;
|
|
}
|
|
|
|
mElapsed += dt;
|
|
|
|
IconButtonsController::Update(dt);
|
|
|
|
if (mIsImage)
|
|
return;
|
|
|
|
//Below this only affects "text" mode
|
|
if (!mUserCloseRequest && mY < 0)
|
|
{
|
|
mY = -SCREEN_HEIGHT + (SCREEN_HEIGHT * mElapsed / 0.75f); //Todo: more physical drop-in.
|
|
if (mY >= 0)
|
|
mY = 0;
|
|
}
|
|
else if (mUserCloseRequest && mY > -SCREEN_HEIGHT)
|
|
{
|
|
mY = -(SCREEN_HEIGHT * mElapsed / 0.75f);
|
|
}
|
|
}
|
|
|
|
void ATutorialMessage::ButtonPressed(int controllerId, int controlId)
|
|
{
|
|
//TODO : cancel ALL tips/tutorials for JGE_BTN_SEC?
|
|
options[getOptionName()].number = 1;
|
|
options.save(); //TODO: if we experience I/O slowness in tutorials, move this save at the end of a turn, or at the end of the game.
|
|
mElapsed = 0;
|
|
mUserCloseRequest = true;
|
|
}
|
|
|
|
void ATutorialMessage::Render()
|
|
{
|
|
if (mDontShow)
|
|
return;
|
|
|
|
if (mY < -SCREEN_HEIGHT)
|
|
return;
|
|
|
|
if (!mBgTex)
|
|
{
|
|
if (mIsImage)
|
|
{
|
|
mBgTex = WResourceManager::Instance()->RetrieveTexture(mMessage, RETRIEVE_LOCK);
|
|
if (mBgTex)
|
|
{
|
|
mBg[0] = NEW JQuad(mBgTex, 0, 0, (float) mBgTex->mWidth, (float) mBgTex->mHeight);
|
|
mBg[0]->SetHotSpot(mBg[0]->mWidth / 2, mBg[0]->mHeight / 2);
|
|
|
|
//Continue Button
|
|
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
|
|
quad->SetHotSpot(16, 16);
|
|
IconButton * iconButton = NEW IconButton(1, this, quad.get(), 0, mBg[0]->mHeight / 2, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
|
|
Add(iconButton);
|
|
}
|
|
|
|
if (options[Options::SFXVOLUME].number > 0)
|
|
{
|
|
JSample * sample = WResourceManager::Instance()->RetrieveSample("tutorial.wav");
|
|
if (sample)
|
|
JSoundSystem::GetInstance()->PlaySample(sample);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mBgTex = WResourceManager::Instance()->RetrieveTexture("taskboard.png", RETRIEVE_LOCK);
|
|
|
|
float unitH = static_cast<float> (mBgTex->mHeight / 4);
|
|
float unitW = static_cast<float> (mBgTex->mWidth / 4);
|
|
if (unitH == 0 || unitW == 0) return;
|
|
|
|
if (mBgTex)
|
|
{
|
|
mBg[0] = NEW JQuad(mBgTex, 0, 0, unitW, unitH);
|
|
mBg[1] = NEW JQuad(mBgTex, unitW, 0, unitW * 2, unitH);
|
|
mBg[2] = NEW JQuad(mBgTex, unitW * 3, 0, unitW, unitH);
|
|
mBg[3] = NEW JQuad(mBgTex, 0, unitH, unitW, unitH * 2);
|
|
mBg[4] = NEW JQuad(mBgTex, unitW, unitH, unitW * 2, unitH * 2);
|
|
mBg[5] = NEW JQuad(mBgTex, unitW * 3, unitH, unitW, unitH * 2);
|
|
mBg[6] = NEW JQuad(mBgTex, 0, unitH * 3, unitW, unitH);
|
|
mBg[7] = NEW JQuad(mBgTex, unitW, unitH * 3, unitW * 2, unitH);
|
|
mBg[8] = NEW JQuad(mBgTex, unitW * 3, unitH * 3, unitW, unitH);
|
|
}
|
|
|
|
//Continue Button
|
|
JQuadPtr quad = WResourceManager::Instance()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
|
|
quad->SetHotSpot(16, 16);
|
|
IconButton * iconButton = NEW IconButton(1, this, quad.get(), SCREEN_WIDTH_F / 2, SCREEN_HEIGHT_F - 60, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
|
|
Add(iconButton);
|
|
|
|
mSH = 64 / unitH;
|
|
mSW = 64 / unitW;
|
|
|
|
if (options[Options::SFXVOLUME].number > 0)
|
|
{
|
|
JSample * sample = WResourceManager::Instance()->RetrieveSample("chain.wav");
|
|
if (sample)
|
|
JSoundSystem::GetInstance()->PlaySample(sample);
|
|
}
|
|
}
|
|
}
|
|
|
|
JRenderer * r = JRenderer::GetInstance();
|
|
|
|
//Render background board
|
|
if (mBgTex)
|
|
{
|
|
if (mIsImage)
|
|
{
|
|
int alpha = mUserCloseRequest ? MAX(0, 255 - (int)(mElapsed * 500)) : MIN(255, (int)(mElapsed * 500)) ;
|
|
if (mUserCloseRequest && alpha == 0)
|
|
mDontShow = true;
|
|
|
|
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(alpha / 2,0,0,0));
|
|
mBg[0]->SetColor(ARGB(alpha,255,255,255));
|
|
r->RenderQuad(mBg[0], SCREEN_WIDTH_F /2 , SCREEN_HEIGHT_F / 2 , 0);
|
|
IconButtonsController::SetColor(ARGB(alpha,255,255,255));
|
|
}
|
|
else
|
|
{
|
|
//Setup fonts.
|
|
WFont * f2 = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
|
|
f2->SetColor(ARGB(255, 205, 237, 240));
|
|
|
|
r->FillRect(0, mY, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
|
|
r->RenderQuad(mBg[0], 0, mY, 0, mSW, mSH); //TL
|
|
r->RenderQuad(mBg[2], SCREEN_WIDTH - 64, mY, 0, mSW, mSH); //TR
|
|
r->RenderQuad(mBg[6], 0, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BL
|
|
r->RenderQuad(mBg[8], SCREEN_WIDTH - 64, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BR
|
|
|
|
//Stretch the sides
|
|
float stretchV = (144.0f / 128.0f) * mSH;
|
|
float stretchH = (176.0f / 128.0f) * mSW;
|
|
r->RenderQuad(mBg[3], 0, mY + 64, 0, mSW, stretchV); //L
|
|
r->RenderQuad(mBg[5], SCREEN_WIDTH - 64, mY + 64, 0, mSW, stretchV); //R
|
|
r->RenderQuad(mBg[1], 64, mY, 0, stretchH, mSH); //T1
|
|
r->RenderQuad(mBg[1], 240, mY, 0, stretchH, mSH); //T1
|
|
r->RenderQuad(mBg[7], 64, mY + 208, 0, stretchH, mSH); //B1
|
|
r->RenderQuad(mBg[7], 240, mY + 208, 0, stretchH, mSH); //B1
|
|
r->RenderQuad(mBg[4], 64, mY + 64, 0, stretchH, stretchV); //Center1
|
|
r->RenderQuad(mBg[4], 240, mY + 64, 0, stretchH, stretchV); //Center2
|
|
}
|
|
}
|
|
else
|
|
{
|
|
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
|
|
r->FillRect(10, 10 + mY, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 10, ARGB(128,0,0,0));
|
|
}
|
|
|
|
if (!mBgTex || !mIsImage)
|
|
{
|
|
float posX = 40, posY = mY + 20;
|
|
string title = _("Help");
|
|
|
|
WFont * f = WResourceManager::Instance()->GetWFont(Fonts::MAGIC_FONT);
|
|
WFont * f3 = WResourceManager::Instance()->GetWFont(Fonts::MENU_FONT); //OPTION_FONT
|
|
f->SetColor(ARGB(255, 55, 46, 34));
|
|
f3->SetColor(ARGB(255, 219, 206, 151));
|
|
|
|
f3->DrawString(title.c_str(), static_cast<float> ((SCREEN_WIDTH - 20) / 2 - title.length() * 4), posY);
|
|
posY += 30;
|
|
|
|
f->DrawString(_(mMessage).c_str(), posX, posY);
|
|
posY += 20;
|
|
|
|
f->SetScale(1);
|
|
}
|
|
|
|
IconButtonsController::Render();
|
|
|
|
}
|
|
|
|
ATutorialMessage * ATutorialMessage::clone() const
|
|
{
|
|
ATutorialMessage * copy = NEW ATutorialMessage(*this);
|
|
copy->mUserCloseRequest = copy->alreadyShown();
|
|
return copy;
|
|
}
|
|
|
|
ATutorialMessage::~ATutorialMessage()
|
|
{
|
|
if (mBgTex)
|
|
{
|
|
WResourceManager::Instance()->Release(mBgTex);
|
|
for (int i = 0; i < 9; i++)
|
|
SAFE_DELETE(mBg[i]);
|
|
}
|
|
}
|
|
|
|
// utility functions
|
|
|
|
// Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities
|
|
void PopulateAbilityIndexVector(list<int>& abilities, const string& abilityStringList, char delimiter)
|
|
{
|
|
vector<string> abilitiesList = split(abilityStringList, delimiter);
|
|
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
|
|
{
|
|
int abilityIndex = Constants::GetBasicAbilityIndex(*iter);
|
|
|
|
if (abilityIndex != -1)
|
|
abilities.push_back(abilityIndex);
|
|
}
|
|
}
|
|
|
|
void PopulateColorIndexVector(list<int>& colors, const string& colorStringList, char delimiter)
|
|
{
|
|
vector<string> abilitiesList = split(colorStringList, delimiter);
|
|
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
|
|
{
|
|
for (int colorIndex = Constants::MTG_COLOR_ARTIFACT; colorIndex < Constants::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);
|
|
}
|
|
}
|